contrib: update the README of many contribs
[nit.git] / contrib / physical_interface_for_mpd_on_rpi / physical_interface_for_mpd_on_rpi.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Backend to a hardware interface to control an MPD server from a Raspberry Pi
18 #
19 # It supports two inputs: a play/pause button and a rotary encoder to adjust
20 # the volume.
21 #
22 # The each data output of the volume control are connected to the board
23 # pins #3 and #5.
24 module physical_interface_for_mpd_on_rpi
25
26 import bcm2835
27 import mpd
28 import privileges
29
30 class PhysicalInterface
31 var mpd = new MPDConnection(server, port, password)
32 protected fun password: String do return "password"
33 fun server: String do return "localhost"
34 fun port: Int do return 6600
35
36 var but_play: Switch is noinit
37 var but_playlist_a: Switch is noinit
38
39 var vol: RotaryEncoder is noinit
40 var vol_step = 2
41
42 var lcd: HD44780 is noinit
43
44 var lcd_backlight: RPiPin is noinit
45 var lcd_backlight_delay = 1000
46
47 var buzzer: Buzzer is noinit
48
49 init
50 do
51 # commandline options for privileges drop
52 var opts = new OptionContext
53 var opt_ug = new OptionUserAndGroup.for_dropping_privileges
54 #opt_ug.mandatory = true
55 opts.add_option(opt_ug)
56
57 # parse and check command line options
58 opts.parse(args)
59 if not opts.errors.is_empty then
60 print opts.errors
61 print "Usage: {sys.program_name} [options]"
62 opts.usage
63 exit 1
64 end
65
66 assert bcm2835_init else print "Failed to init"
67
68 # drop privileges!
69 var user_group = opt_ug.value
70 if user_group != null then user_group.drop_privileges
71
72 # Play button
73 but_play = new Switch(new RPiPin.p1_13, new PUDControl.down)
74
75 # Playlist a button
76 but_playlist_a = new Switch(new RPiPin.p1_15, new PUDControl.down)
77
78 # Vol +
79 var vol3 = new RPiPin.p1_03
80 vol3.fsel = new FunctionSelect.inpt
81 vol3.pud = new PUDControl.up
82
83 # Vol -
84 var vol5 = new RPiPin.p1_05
85 vol5.fsel = new FunctionSelect.inpt
86 vol5.pud = new PUDControl.up
87
88 vol = new RotaryEncoder(vol3,vol5)
89
90 # LCD
91 var lcd_rs = new RPiPin.p1_23
92 var lcd_en = new RPiPin.p1_21
93 var lcd_d4 = new RPiPin.p1_19
94 var lcd_d5 = new RPiPin.p1_26
95 var lcd_d6 = new RPiPin.p1_24
96 var lcd_d7 = new RPiPin.p1_22
97 lcd = new HD44780(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7)
98 lcd.setup
99 lcd.clear
100
101 lcd_backlight = new RPiPin.p1_18
102 lcd_backlight.fsel = new FunctionSelect.outp
103
104 # Buzzer
105 var buzzer_pin = new RPiPin.p1_11
106 buzzer_pin.fsel = new FunctionSelect.outp
107 buzzer = new Buzzer(buzzer_pin)
108 end
109
110 fun run
111 do
112 var tick = 0
113 var last_event = 0
114 loop
115 var force_lcd_update = false
116
117 # play button
118 if but_play.changed and but_play.is_down then
119 print "but"
120 hit_play_stop
121 force_lcd_update = true
122 end
123
124 if but_playlist_a.changed and but_playlist_a.is_down then
125 play_playlist_a
126 force_lcd_update = true
127 end
128
129 # volume
130 var s = vol.update
131 if s != null then
132 if s == '<' then
133 print "vol down"
134 mpd.relative_volume = -vol_step
135 else # >
136 print "vol up"
137 mpd.relative_volume = vol_step
138 end
139 force_lcd_update = true
140 end
141
142 # update lcd
143 if tick % 100 == 0 or force_lcd_update then
144 var status = mpd.status
145 var song = mpd.current_song
146
147 var status_char
148 if status == null then
149 lcd.text = "Unknown status"
150 else if song == null then
151 lcd.text = "No song playing"
152 else
153 if status.playing then
154 last_event = tick
155 status_char = ">"
156 else status_char = "#"
157
158 var tr = status.time_ratio
159 var pos = "-"
160 if tr != null then pos = (status.time_ratio*10.0).to_i.to_s
161
162 lcd.text = "{status_char} {song.artist}\n{pos} {song.title}"
163 end
164 end
165
166 # manage backlight
167 if force_lcd_update then last_event = tick
168
169 var diff_with_last_event = tick - last_event
170 if diff_with_last_event == 0 then
171 lcd_backlight.write(true)
172 else if diff_with_last_event == lcd_backlight_delay then
173 lcd_backlight.write(false)
174 end
175
176 10.bcm2835_delay
177 tick += 1
178 end
179 end
180
181 fun hit_play_stop
182 do
183 # get current status
184 var status = mpd.status
185 var playing = false
186 if status != null then
187 playing = status.playing
188 else
189 print "Cannot get state"
190 return
191 end
192
193 if playing then
194 # stop
195 print "playing -> stop"
196 mpd.pause
197 else
198 print "stopped -> play"
199 mpd.play
200 end
201
202 bell
203 end
204
205 fun play_playlist_a
206 do
207 mpd.load_playlist("alexis")
208 end
209
210 fun bell do buzzer.buzz(1.5, 20)
211 end
212
213 var phy = new PhysicalInterface
214 phy.run