contrib/gpio-mpd-rpi: adds backlight control with delay
[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 # This programs interprets the input of a physical interface thought the
18 # GPIO pins of a Raspberry Pi to control an MPD server.
19 #
20 # It suppot two inputs: a play/pause button and a rotary encoder to adjust
21 # the volume.
22 #
23 # The each data output of the volume control are connected to the board
24 # pins #3 and #5.
25 module physical_interface_for_mpd_on_rpi
26
27 import bcm2835
28 import mpd
29 import privileges
30
31 class PhysicalInterface
32 var mpd = new MPDConnection("localhost", 6600, "password")
33
34 var debug_led: RPiPin
35
36 var but_play: Switch
37 var but_playlist_a: Switch
38
39 var vol: RotaryEncoder
40 var vol_step = 2
41
42 var lcd: HD44780
43
44 var lcd_backlight: RPiPin
45 var lcd_backlight_delay = 1000
46
47 init
48 do
49 # commandline options for privileges drop
50 var opts = new OptionContext
51 var opt_ug = new OptionDropPrivileges
52 #opt_ug.mandatory = true
53 opts.add_option(opt_ug)
54
55 # parse and check command line options
56 opts.parse(args)
57 if not opts.errors.is_empty then
58 print opts.errors
59 print "Usage: {sys.program_name} [options]"
60 opts.usage
61 exit 1
62 end
63
64 assert bcm2835_init else print "Failed to init"
65
66 # drop privileges!
67 var user_group = opt_ug.value
68 if user_group != null then user_group.drop_privileges
69
70 # Debug LED
71 debug_led = new RPiPin.p1_11
72 debug_led.fsel = new FunctionSelect.outp
73 debug_led.write(false)
74
75 # Play button
76 but_play = new Switch(new RPiPin.p1_13, new PUDControl.down)
77
78 # Playlist a button
79 but_playlist_a = new Switch(new RPiPin.p1_15, new PUDControl.down)
80
81 # Vol +
82 var vol3 = new RPiPin.p1_03
83 vol3.fsel = new FunctionSelect.inpt
84 vol3.pud = new PUDControl.up
85
86 # Vol -
87 var vol5 = new RPiPin.p1_05
88 vol5.fsel = new FunctionSelect.inpt
89 vol5.pud = new PUDControl.up
90
91 vol = new RotaryEncoder(vol3,vol5)
92
93 # LCD
94 var lcd_rs = new RPiPin.p1_23
95 var lcd_en = new RPiPin.p1_21
96 var lcd_d4 = new RPiPin.p1_19
97 var lcd_d5 = new RPiPin.p1_26
98 var lcd_d6 = new RPiPin.p1_24
99 var lcd_d7 = new RPiPin.p1_22
100 lcd = new HD44780(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7)
101 lcd.setup
102 lcd.clear
103
104 lcd_backlight = new RPiPin.p1_18
105 lcd_backlight.fsel = new FunctionSelect.outp
106 end
107
108 fun run
109 do
110 var led_on = false
111 var tick = 0
112 var last_event = 0
113 loop
114 var force_lcd_update = false
115
116 # play button
117 if but_play.changed and but_play.is_down then
118 print "but"
119 hit_play_stop
120 force_lcd_update = true
121 end
122
123 if but_playlist_a.changed and but_playlist_a.is_down then
124 play_playlist_a
125 force_lcd_update = true
126 end
127
128 # volume
129 var s = vol.update
130 if s != null then
131 if s == '<' then
132 print "vol down"
133 mpd.relative_volume = -vol_step
134 else # >
135 print "vol up"
136 mpd.relative_volume = vol_step
137 end
138 force_lcd_update = true
139 end
140
141 # update lcd
142 if tick % 100 == 0 or force_lcd_update then
143 var status = mpd.status
144 var song = mpd.current_song
145
146 var status_char
147 if status == null then
148 lcd.text = "Unknown status"
149 else if song == null then
150 lcd.text = "No song playing"
151 else
152 if status.playing then
153 last_event = tick
154 status_char = ">"
155 else status_char = "#"
156
157 var tr = status.time_ratio
158 var pos = "-"
159 if tr != null then pos = (status.time_ratio*10.0).to_i.to_s
160
161 lcd.text = "{status_char} {song.artist}\n{pos} {song.title}"
162 end
163 end
164
165 # manage backlight
166 if force_lcd_update then last_event = tick
167
168 var diff_with_last_event = tick - last_event
169 if diff_with_last_event == 0 then
170 lcd_backlight.write(true)
171 else if diff_with_last_event == lcd_backlight_delay then
172 lcd_backlight.write(false)
173 end
174
175 10.bcm2835_delay
176 tick += 1
177 end
178 end
179
180 fun hit_play_stop
181 do
182 # get current status
183 var status = mpd.status
184 var playing = false
185 if status != null then
186 playing = status.playing
187 else
188 print "Cannot get state"
189 return
190 end
191
192 if playing then
193 # stop
194 print "playing -> stop"
195 mpd.pause
196 else
197 print "stopped -> play"
198 mpd.play
199 end
200 end
201
202 fun play_playlist_a
203 do
204 mpd.load_playlist("alexis")
205 end
206 end
207
208 var phy = new PhysicalInterface
209 phy.run