# See the License for the specific language governing permissions and
# limitations under the License.
-# This programs interprets the input of a physical interface thought the
-# GPIO pins of a Raspberry Pi to control an MPD server.
+# Backend to a hardware interface to control an MPD server from a Raspberry Pi
#
-# It suppot two inputs: a play/pause button and a rotary encoder to adjust
+# It supports two inputs: a play/pause button and a rotary encoder to adjust
# the volume.
#
# The each data output of the volume control are connected to the board
import mpd
import privileges
-redef class Object
- fun mpd: MPDConnection do return once new MPDConnection("localhost", 6600, "password")
-end
+class PhysicalInterface
+ var mpd = new MPDConnection(server, port, password)
+ protected fun password: String do return "password"
+ fun server: String do return "localhost"
+ fun port: Int do return 6600
-fun hit_play_stop
-do
- # get current status
- var status = mpd.status
- var playing = false
- if status != null then
- playing = status.playing
- else
- print "Cannot get state"
- return
- end
+ var but_play: Switch is noinit
+ var but_playlist_a: Switch is noinit
- if playing then
- # stop
- print "playing -> stop"
- mpd.pause
- else
- print "stopped -> play"
- mpd.play
- end
-end
+ var vol: RotaryEncoder is noinit
+ var vol_step = 2
-# commandline options for privileges drop
-var opts = new OptionContext
-var opt_ug = new OptionDropPrivileges
-#opt_ug.mandatory = true
-opts.add_option(opt_ug)
-
-# parse and check command line options
-opts.parse(args)
-if not opts.errors.is_empty then
- print opts.errors
- print "Usage: {program_name} [options]"
- opts.usage
- exit 1
-end
+ var lcd: HD44780 is noinit
+
+ var lcd_backlight: RPiPin is noinit
+ var lcd_backlight_delay = 1000
+
+ var buzzer: Buzzer is noinit
+
+ init
+ do
+ # commandline options for privileges drop
+ var opts = new OptionContext
+ var opt_ug = new OptionUserAndGroup.for_dropping_privileges
+ #opt_ug.mandatory = true
+ opts.add_option(opt_ug)
+
+ # parse and check command line options
+ opts.parse(args)
+ if not opts.errors.is_empty then
+ print opts.errors
+ print "Usage: {sys.program_name} [options]"
+ opts.usage
+ exit 1
+ end
+
+ assert bcm2835_init else print "Failed to init"
+
+ # drop privileges!
+ var user_group = opt_ug.value
+ if user_group != null then user_group.drop_privileges
+
+ # Play button
+ but_play = new Switch(new RPiPin.p1_13, new PUDControl.down)
-assert bcm2835_init else print "Failed to init"
-
-# drop privileges!
-var user_group = opt_ug.value
-if user_group != null then user_group.drop_privileges
-
-# Debug LED
-var out = new RPiPin.p1_11
-out.fsel = new FunctionSelect.outp
-out.write(false)
-
-# Play button
-var inp = new RPiPin.p1_13
-inp.fsel = new FunctionSelect.inpt
-inp.pud = new PUDControl.down
-
-# Vol +
-var vol3 = new RPiPin.p1_03
-vol3.fsel = new FunctionSelect.inpt
-vol3.pud = new PUDControl.up
-
-# Vol -
-var vol5 = new RPiPin.p1_05
-vol5.fsel = new FunctionSelect.inpt
-vol5.pud = new PUDControl.up
-
-var vol = new RotaryEncoder(vol3,vol5)
-var vol_step = 2
-
-# LCD
-var lcd_rs = new RPiPin.p1_23
-var lcd_en = new RPiPin.p1_21
-var lcd_d4 = new RPiPin.p1_19
-var lcd_d5 = new RPiPin.p1_26
-var lcd_d6 = new RPiPin.p1_24
-var lcd_d7 = new RPiPin.p1_22
-var lcd = new HD44780(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7)
-lcd.setup
-lcd.clear
-
-var last_in = false
-var led_on = false
-var tick = 0
-loop
- var force_lcd_update = false
-
- # play button
- var lev = inp.lev
- if lev != last_in then
- last_in = lev
- if lev then hit_play_stop
- force_lcd_update = true
+ # Playlist a button
+ but_playlist_a = new Switch(new RPiPin.p1_15, new PUDControl.down)
+
+ # Vol +
+ var vol3 = new RPiPin.p1_03
+ vol3.fsel = new FunctionSelect.inpt
+ vol3.pud = new PUDControl.up
+
+ # Vol -
+ var vol5 = new RPiPin.p1_05
+ vol5.fsel = new FunctionSelect.inpt
+ vol5.pud = new PUDControl.up
+
+ vol = new RotaryEncoder(vol3,vol5)
+
+ # LCD
+ var lcd_rs = new RPiPin.p1_23
+ var lcd_en = new RPiPin.p1_21
+ var lcd_d4 = new RPiPin.p1_19
+ var lcd_d5 = new RPiPin.p1_26
+ var lcd_d6 = new RPiPin.p1_24
+ var lcd_d7 = new RPiPin.p1_22
+ lcd = new HD44780(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7)
+ lcd.setup
+ lcd.clear
+
+ lcd_backlight = new RPiPin.p1_18
+ lcd_backlight.fsel = new FunctionSelect.outp
+
+ # Buzzer
+ var buzzer_pin = new RPiPin.p1_11
+ buzzer_pin.fsel = new FunctionSelect.outp
+ buzzer = new Buzzer(buzzer_pin)
end
- # volume
- var s = vol.update
- if s != null then
- if s == '<' then
- print "vol down"
- mpd.relative_volume = -vol_step
- else # >
- print "vol up"
- mpd.relative_volume = vol_step
+ fun run
+ do
+ var tick = 0
+ var last_event = 0
+ loop
+ var force_lcd_update = false
+
+ # play button
+ if but_play.changed and but_play.is_down then
+ print "but"
+ hit_play_stop
+ force_lcd_update = true
+ end
+
+ if but_playlist_a.changed and but_playlist_a.is_down then
+ play_playlist_a
+ force_lcd_update = true
+ end
+
+ # volume
+ var s = vol.update
+ if s != null then
+ if s == '<' then
+ print "vol down"
+ mpd.relative_volume = -vol_step
+ else # >
+ print "vol up"
+ mpd.relative_volume = vol_step
+ end
+ force_lcd_update = true
+ end
+
+ # update lcd
+ if tick % 100 == 0 or force_lcd_update then
+ var status = mpd.status
+ var song = mpd.current_song
+
+ var status_char
+ if status == null then
+ lcd.text = "Unknown status"
+ else if song == null then
+ lcd.text = "No song playing"
+ else
+ if status.playing then
+ last_event = tick
+ status_char = ">"
+ else status_char = "#"
+
+ var tr = status.time_ratio
+ var pos = "-"
+ if tr != null then pos = (status.time_ratio*10.0).to_i.to_s
+
+ lcd.text = "{status_char} {song.artist}\n{pos} {song.title}"
+ end
+ end
+
+ # manage backlight
+ if force_lcd_update then last_event = tick
+
+ var diff_with_last_event = tick - last_event
+ if diff_with_last_event == 0 then
+ lcd_backlight.write(true)
+ else if diff_with_last_event == lcd_backlight_delay then
+ lcd_backlight.write(false)
+ end
+
+ 10.bcm2835_delay
+ tick += 1
end
- force_lcd_update = true
end
- # update lcd
- if tick % 100 == 0 or force_lcd_update then
+ fun hit_play_stop
+ do
+ # get current status
var status = mpd.status
- var song = mpd.current_song
+ var playing = false
+ if status != null then
+ playing = status.playing
+ else
+ print "Cannot get state"
+ return
+ end
- var status_char
- if status == null then
- lcd.text = "Unknown status"
- else if song == null then
- lcd.text = "No song playing"
+ if playing then
+ # stop
+ print "playing -> stop"
+ mpd.pause
else
- if status.playing then
- status_char = ">"
- else status_char = "#"
+ print "stopped -> play"
+ mpd.play
+ end
- var tr = status.time_ratio
- var pos = "-"
- if tr != null then pos = (status.time_ratio*10.0).to_i.to_s
+ bell
+ end
- lcd.text = "{status_char} {song.artist}\n{pos} {song.title}"
- end
+ fun play_playlist_a
+ do
+ mpd.load_playlist("alexis")
end
- 10.bcm2835_delay
- tick += 1
+ fun bell do buzzer.buzz(1.5, 20)
end
+
+var phy = new PhysicalInterface
+phy.run