Saturday, November 30, 2013
Big Sound for small RaspberryPI with IR: Adding Favorites
As soon as I had created my music center with RaspberryPI controlled by an Onkyo remote, I've realized that I needed a few dedicated "favorites" buttons and a more complex configuration file for the remote to make sure that I could persist them and make available after a reboot.
Fortunately, Onkyo remote has a lot of buttons that I don't normally use, so I've dedicated the whole Deck B panel to favorites and used Group Preset Up/Down buttons to navigate between different favorites.
Now I have 7 pre-programmed buttons, which are sufficient to remember all my favorite Internet radio-stations and local MPD's playlists. Since I might want to re-program some favorites on fly, I've decided to use a combination of Stop+<FavoriteN> buttons to assign a new playlist to a button <FavoriteN>.
You can find all those new features in the new IR configuration file below. I think, it can be re-used by anyone who has the same model of Onkyo's remote - RC287S .
/etc/lirc/lircd.conf:
# Please make this file available to others
# by sending it to <lirc@bartelmus.de>
#
# this config file was automatically generated
# using lirc-0.9.0-pre1(default) on Sat Nov 16 03:37:07 2013
#
# contributed by
#
# brand: lircd.conf.conf
# model no. of remote control:
# devices being controlled by this remote:
#
begin remote
name ONKYO-RC-287S
flags RAW_CODES|CONST_LENGTH
eps 30
aeps 100
gap 108090
begin raw_codes
name KEY_POWER
9017 4480 624 509 579 1663
581 555 576 529 600 1670
577 530 600 1667 579 1662
583 1661 598 537 571 1670
599 1668 579 531 598 1678
569 1666 572 562 576 530
600 536 572 1669 598 536
581 555 574 531 600 534
582 554 576 1664 625 1618
598 536 584 1658 599 1675
576 1667 571 1672 592 1677
574
name KEY_LEFT
9054 4447 601 533 575 1667
601 533 625 509 579 1664
625 509 578 1672 616 1617
609 1659 579 538 604 1664
570 1676 622 501 574 1668
581 1661 607 527 581 1660
606 1673 565 1667 631 1610
606 530 577 556 574 1677
570 556 573 540 605 526
577 556 573 539 603 1666
569 1672 626 508 583 1652
631
name KEY_RIGHT
9040 4461 600 534 582 1658
601 535 572 562 578 1663
573 562 577 1663 588 1659
606 1660 576 531 609 1659
578 1665 591 543 576 1666
581 1661 608 526 581 553
576 1666 583 1660 606 1660
589 525 604 527 580 1667
600 540 569 1659 608 529
579 554 575 533 606 1662
575 1675 572 555 574 1667
579
name KEY_PLAY
9016 4485 628 505 581 1660
575 559 581 528 609 1657
583 527 601 1670 579 1666
570 1665 603 532 575 1665
604 1667 581 526 601 1666
582 1661 576 558 580 528
602 533 574 559 581 527
602 1667 580 527 603 1667
580 526 603 1672 581 1673
563 1666 603 1668 577 526
601 1673 572 527 602 1665
581
name KEY_PAUSE
9072 4439 569 555 574 1666
594 541 576 532 606 1664
574 532 606 1662 576 1677
570 1663 604 530 579 1661
607 1663 575 533 605 1672
565 1671 577 553 575 1668
579 555 574 1676 571 1663
606 529 579 554 575 1668
580 554 608 498 608 1669
567 534 605 530 579 1661
606 1662 576 532 616 1653
574
name KEY_LEFTALT
9015 4488 573 561 579 1660
575 560 579 528 601 1677
571 527 601 1669 578 1663
574 1665 603 533 583 1660
599 1668 579 528 601 1669
578 1665 574 562 575 530
599 1668 580 529 599 536
572 1670 598 536 571 1669
612 528 598 1649 600 526
581 1660 607 1672 565 532
608 1661 576 531 608 1659
578
name KEY_RIGHTALT
9044 4466 594 529 579 1664
603 529 579 556 573 1669
579 556 572 1669 587 1656
609 1667 567 538 600 1670
566 1676 583 546 572 1668
577 1665 601 533 573 1669
600 534 574 561 579 528
601 1668 577 531 600 1668
580 528 600 544 564 1668
598 1670 579 1664 573 561
578 1672 563 561 579 1664
572
name KEY_VOLUMEUP
9036 4464 597 537 579 1663
602 532 575 560 578 1662
573 567 573 1674 569 1663
599 1668 578 531 605 1665
571 1671 572 561 576 1667
579 1662 603 541 566 560
578 1661 573 563 575 534
603 530 576 558 580 1673
561 562 575 1668 577 556
572 1670 573 1668 597 1673
573 1669 574 560 577 1663
582
name KEY_VOLUMEDOWN
9032 4470 603 528 579 1666
605 529 574 559 580 1673
562 563 576 1615 627 1668
595 1677 567 533 603 1663
571 1672 573 575 563 1666
578 1662 603 533 574 1667
598 1679 565 534 603 538
569 559 579 528 598 1670
574 534 605 529 604 541
558 1674 572 1669 606 1662
572 1678 569 562 578 1661
579
name BTN_1
9009 4501 568 556 573 1668
579 557 572 536 603 1672
565 537 602 1662 574 1671
576 1666 601 533 584 1656
604 1666 580 528 602 1666
581 1679 567 549 580 529
599 1668 580 1665 571 571
567 1664 584 551 577 530
599 536 573 1669 597 538
571 566 576 1682 552 562
577 1665 570 1673 607 1661
576
name BTN_2
9018 4480 569 536 604 1666
571 537 602 542 565 1667
601 533 576 1666 599 1669
579 1671 566 560 579 1663
573 1668 602 537 572 1674
592 1668 577 531 600 535
572 562 578 529 600 1669
578 1671 566 560 577 530
598 537 572 1670 598 1670
577 1665 572 563 576 533
596 1671 576 1668 579 1663
614
name BTN_3
9058 4442 600 534 572 1670
599 536 572 562 576 1665
573 562 576 1666 572 1680
587 1672 573 535 606 1663
574 1666 581 562 566 1666
581 1663 605 529 578 556
573 1669 577 566 564 1669
578 1665 603 531 577 557
586 523 602 1674 562 537
602 1667 580 527 608 530
571 1667 601 1675 572 1662
574
name BTN_4
9015 4479 582 551 576 1666
581 553 577 532 607 1659
578 533 606 1662 574 1667
580 1661 607 528 579 1663
605 1668 575 529 602 1667
572 1669 578 556 582 1660
577 556 584 1660 576 568
571 1662 574 558 580 527
602 533 575 559 581 1662
583 552 579 1661 575 560
579 1663 574 1669 599 1668
579
name BTN_5
9019 4480 583 526 601 1668
589 518 603 533 573 1667
607 528 574 1668 600 1668
579 1662 574 561 579 1662
573 1669 599 535 572 1673
597 1670 577 531 604 1669
578 1659 572 576 563 531
597 1672 575 532 608 527
579 556 575 532 606 528
580 1669 598 1663 573 536
604 1663 573 1669 579 1664
602
name BTN_6
9021 4481 572 533 606 1664
581 527 603 532 576 1665
603 530 579 1665 602 1666
579 1662 576 557 572 1672
575 1666 603 532 575 1685
581 1669 580 528 601 1668
579 529 600 534 572 1678
593 1667 578 529 601 534
573 560 577 531 600 1668
580 1663 573 561 578 530
598 1671 577 1664 572 1679
590
name BTN_7
9013 4481 573 560 576 1666
573 561 577 530 599 1670
577 532 596 1671 576 1665
583 1663 606 527 580 1661
607 1661 575 534 606 1672
564 1667 580 554 573 535
603 1666 572 536 603 1665
585 524 603 531 576 1666
602 532 576 1666 602 532
586 1655 602 534 575 1667
601 1667 580 527 600 1677
572
name BTN_8
9024 4484 570 529 599 1680
566 530 598 538 570 1670
598 535 573 1672 596 1669
578 1666 571 563 575 1667
571 1670 608 526 591 1652
605 1664 573 534 606 533
574 582 547 560 578 556
556 554 573 560 578 556
553 562 565 1670 580 1661
605 1675 562 1670 578 1663
604 1665 572 1679 567 1665
603
name BTN_9
9043 4469 587 536 572 1670
598 536 570 565 576 1666
622 513 574 1666 570 1671
607 1668 590 521 598 1666
572 1666 579 555 573 1670
576 1665 603 532 579 1662
603 532 576 558 571 538
600 533 576 559 579 527
602 534 581 553 579 1663
626 1616 600 1667 580 1662
634 1608 599 1668 579 1665
624
end raw_codes
end remote
I've also changed a config file format for storing favorites and a current playlist in a JSON file and added support for playlists created at soundcloud.com. Internet radiostations are supported through traditional m3u files that MPD understands.
{
"current": "PlayList1",
"soundcloud": [
"soundcloud://playlist/xxxxxxxx"
],
"favorites": [
"soundcloud://playlist/xxxxxxxx",
"InternetRadio1",
"InternetRadio2",
"InternetRadio3",
"InternetRadio4",
"LocalPLayList1",
"LocalPLayList2"
]
}
where
"current" - the currently playing playlist
"soundcloud" - a list of soundclud URL's
"favorites" - a list of all favorites assigned to IR buttons
Finally, I had to change /etc/lirc/lircrc configuration file and Python script that is called when an IR button or combination of buttons are pressed.
/etc/lirc/lircrc:
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_POWER
config = sudo shutdown -h now
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_RIGHT
config = /usr/bin/mpc next
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_LEFT
config = /usr/bin/mpc prev
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_PLAY
config = /usr/bin/mpc toggle
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_PAUSE
button = BTN_1
config = /home/pi/pls_man.py -s 1
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_PAUSE
button = BTN_2
config = /home/pi/pls_man.py -s 2
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_PAUSE
button = BTN_3
config = /home/pi/pls_man.py -s 3
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_PAUSE
button = BTN_4
config = /home/pi/pls_man.py -s 4
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_PAUSE
button = BTN_5
config = /home/pi/pls_man.py -s 5
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_PAUSE
button = BTN_6
config = /home/pi/pls_man.py -s 6
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_PAUSE
button = BTN_7
config = /home/pi/pls_man.py -s 7
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_1
config = /home/pi/pls_man.py -f 1
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_2
config = /home/pi/pls_man.py -f 2
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_3
config = /home/pi/pls_man.py -f 3
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_4
config = /home/pi/pls_man.py -f 4
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_5
config = /home/pi/pls_man.py -f 5
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_6
config = /home/pi/pls_man.py -f 6
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_7
config = /home/pi/pls_man.py -f 7
flags = quit
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_LEFTALT
config = /home/pi/pls_man.py -p
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_RIGHTALT
config = /home/pi/pls_man.py -n
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_8
config = /home/pi/pls_man.py -P
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = BTN_9
config = /home/pi/pls_man.py -N
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_VOLUMEUP
repeat = 1
config = mpc volume +1
end
begin
remote = ONKYO-RC-287S
prog = irexec
button = KEY_VOLUMEDOWN
repeat = 1
config = mpc volume -1
end
pls_man.py script is provided below
#!/usr/bin/env python
# encoding: utf-8
'''
pls_man -- Manage Play List
pls_man is a module for controlling play lists through an IR remote
@author: ogryb
@copyright: 2013 Oleg Gryb. All rights reserved.
@license: GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with luna_mech. If not, see <http://www.gnu.org/licenses/>.
@contact: oleg@gryb.info
@deffield updated: 2013.11.18
'''
import sys
import os
import re
import json
from optparse import OptionParser
__all__ = []
__version__ = 0.1
__date__ = '2013-11-18'
__updated__ = '2013-11-18'
DEBUG = 0
TESTRUN = 0
PROFILE = 0
SOUND_CLOUD = ['soundcloud://playlist/12692664']
def main(argv=None):
'''Command line options.'''
program_name = os.path.basename(sys.argv[0])
program_version = "v0.1"
program_build_date = "%s" % __updated__
program_version_string = '%%prog %s (%s)' % (program_version, program_build_date)
#program_usage = '''usage: spam two eggs''' # optional - will be autogenerated by optparse
program_longdesc = '''''' # optional - give further explanation about what the program does
program_license = "Copyright 2013 Oleg Gryb \
Licensed under the GPL\nhttp://www.gnu.org/licenses/"
if argv is None:
argv = sys.argv[1:]
try:
# setup option parser
parser = OptionParser(version=program_version_string, epilog=program_longdesc, description=program_license)
parser.add_option("-c", "--config", dest="config", help="config json file [default: %default]", metavar="FILE")
parser.add_option("-v", "--verbose", dest="verbose", action="count", help="set verbosity level [default: %default]")
parser.add_option("-n", "--next", dest="nextpl",
action="store_true", default=False,
help="Next playlist")
parser.add_option("-p", "--prev", dest="prevpl",
action="store_true", default=False,
help="Previous playlist")
parser.add_option("-N", "--next-fav", dest="nextfav",
action="store_true", default=False,
help="Next favorites")
parser.add_option("-P", "--prev-fav", dest="prevfav",
action="store_true", default=False,
help="Previous favorites")
parser.add_option("-s", "--store-fav", dest="storenum",
help="Store favorite", default=0, type="int", metavar="NUMBER")
parser.add_option("-f", "--play-fav", dest="playnum",
help="Store favorite", default=0, type="int", metavar="NUMBER")
# set defaults
parser.set_defaults(config="~/.pls_man/config", verbosity=0)
# process options
(opts, args) = parser.parse_args(argv)
cf = os.path.expanduser(opts.config)
if not os.path.isfile(cf):
if not os.path.isdir(os.path.dirname(cf)):
os.system("mkdir -p %s" % os.path.dirname(cf))
cfj = dict()
cfj['current'] = ''
cfj['favorites'] = ["","","","","","",""]
cfj['soundcloud'] = SOUND_CLOUD
with open(cf, 'w') as out:
json.dump(cfj, out)
if not os.path.isfile(cf):
sys.exit("Can's create file %s" % cf)
io = open(cf)
cfj = json.load(io)
io.close()
if not cfj:
cfj = dict()
ind = 0
if (opts.nextpl or opts.prevpl):
if 'current' in cfj:
s = cfj['current']
lsa = []
if 'soundcloud' in cfj:
lsa = cfj['soundcloud']
ls = os.popen('mpc lsplaylists').read()
lsa = lsa + ls.split("\n")
del lsa [-1]
for i in range(0,len(lsa)-1):
if (s == lsa[i]):
if opts.nextpl:
ind = i+1
else:
ind = i-1
break
if ind < 0:
ind = len(lsa) - 1
if ind >= len(lsa):
ind = 0
if ind < 0:
ind = 0
if ind < len(lsa):
os.system("mpc clear")
loaded = os.system("mpc load %s" % lsa[ind])
if loaded == 0:
os.system("mpc play")
cfj['current'] = lsa[ind]
with open(cf, 'w') as out:
json.dump(cfj, out)
if (opts.nextfav or opts.prevfav):
if 'current' in cfj:
s = cfj['current']
lsa = []
if 'favorites' in cfj:
lsa = cfj['favorites']
for i in range(0,len(lsa)-1):
if (s == lsa[i]):
if opts.nextfav:
ind = i+1
while ind < len(lsa) and ((not lsa[ind]) or len(lsa[ind]) == 0):
ind += 1
else:
ind = i-1
while ind >= 0 and ((not lsa[ind]) or len(lsa[ind]) == 0):
ind -= 1
break
if ind < 0:
ind = len(lsa) - 1
while ind >= 0 and ((not lsa[ind]) or len(lsa[ind]) == 0):
ind -= 1
if ind >= len(lsa):
ind = 0
while ind < len(lsa) and ((not lsa[ind]) or len(lsa[ind]) == 0):
ind += 1
if 0 <= ind and ind < len(lsa):
os.system("mpc clear")
loaded = os.system("mpc load %s" % lsa[ind])
if loaded == 0:
os.system("mpc play")
cfj['current'] = lsa[ind]
with open(cf, 'w') as out:
json.dump(cfj, out)
if ( 0 < opts.storenum and opts.storenum <= 7 ):
if not 'favorites' in cfj:
cfj['favorites'] = ["","","","","","",""]
if 'current' in cfj:
s = cfj['current']
cfj['favorites'][opts.storenum-1] = s
with open(cf, 'w') as out:
json.dump(cfj, out)
if ( 0 < opts.playnum and opts.playnum <= 7 ):
ind = opts.playnum - 1
if 'favorites' in cfj and ind < len(cfj['favorites']):
fav = cfj['favorites'][ind]
if (len(fav) > 0):
os.system("mpc clear")
loaded = os.system("mpc load %s" % fav)
if loaded == 0:
os.system("mpc play")
cfj['current'] = fav
with open(cf, 'w') as out:
json.dump(cfj, out)
except Exception, e:
indent = len(program_name) * " "
sys.stderr.write(program_name + ": " + repr(e) + "\n")
sys.stderr.write(indent + " for help use --help")
return 2
if __name__ == "__main__":
if DEBUG:
sys.argv.append("-h")
if TESTRUN:
import doctest
doctest.testmod()
if PROFILE:
import cProfile
import pstats
profile_filename = 'pls_man_profile.txt'
cProfile.run('main()', profile_filename)
statsfile = open("profile_stats.txt", "wb")
p = pstats.Stats(profile_filename, stream=statsfile)
stats = p.strip_dirs().sort_stats('cumulative')
stats.print_stats()
statsfile.close()
sys.exit(0)
sys.exit(main())
Everything works as expected so far, switching between Internet radio-stations and local MPD's playlists stored on Raspberry's is very easy now.
Sunday, November 24, 2013
UDOO - RaspberryPI Talks to Arduino
Communication Schemes
There are two loosely coupled components in UDOO device - one is built around Atmel's SAM3X8E processor and the second one around ARM v7 Cortex A9, i.MX6 series processor. Since the first one resembles traditional Arduino's functionality and the second one is very similar to RaspberryPI, it would be fair to say that we're dealing with Arduino + RaspberryPI hybrid here.It would be interesting to learn, of course, how these two loosely coupled components can talk to each other. One of the obvious way of doing this would be by utilizing GPIO's available on both sides.
Possible connectivity scenarios are provided by UDOO in their "UDOO Starting Manual":
It's obvious from this diagram that I could take a signal from i.MX6's output GPIO and feed it to an SAM3X8E's input GPIO.
Since UDOO has only one green led, which simply stays on and never blinks, I've decided to add my own custom led on SAM3X8E that would monitor a status and network activity on i.MX6 side. If i.MX6 is running, it will send a pulse signal every few seconds to GPIO 134. Network activity signals will be sent to the same GPIO when a TCP packet received or send by i.MX6's wireless card. I monitor wireless interface because it's my primary one, but it can be easily changed to wired or any other network interface if necessary.
Solution Details
This is the final hardware solution:
GPIO 134 is connected to Arduino's A6, light diode's cathode is connected to the ground on J8 and anode to Arduino's DAC0.
The rest of that is coding. You'll need an activity monitor running on i.MX6 and a code for Arduino's controller that reads A6 and sends an analog signal to DAC0.
I've "demonized" my activity monitor by creating an init script and storing it in /etc/init.d/udoo-activity file:
#!/bin/bash
### BEGIN INIT INFO
# Provides: udoo-activity
# Required-Start: $remote_fs $all
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Sends TCP activity and udoo pulse to a GPIO 134
### END INIT INFO
. /lib/lsb/init-functions
PATH=/sbin:/usr/sbin:/bin:/usr/bin
ACTIVITYPID=/var/run/udoo-activity.pid
PULSEPID=/var/run/udoo-pulse.pid
GPIODIR=/sys/class/gpio/gpio134
[ -f ${GPIODIR}/direction ] || exit 0
[ -f ${GPIODIR}/value ] || exit 0
echo out >${GPIODIR}/directionu
echo 0 >${GPIODIR}/value
do_stop () {
log_daemon_msg "Stopping UDOO activity monitors" "udoo-activity"
if [ -f "$PULSEPID" ]; then
kill -9 `cat $PULSEPID` >/dev/null 2>&1
fi
if [ -f "$ACTIVITYPID" ]; then
kill -9 `cat $ACTIVITYPID` >/dev/null 2>&1
fi
log_end_msg $?
echo 0 >${GPIODIR}/value
}
do_start () {
# Pulse Loop
while true; do
sleep 5
echo 1 >${GPIODIR}/value
sleep 0.0015
echo 0 >${GPIODIR}/value
done &
echo $! >$PULSEPID
# TCP Dump Loop
while true; do
echo 0 >${GPIODIR}/value
tcpdump -q -i wlan6 -n tcp -c 1 >/dev/null 2>&1
echo 1 >${GPIODIR}/value
sleep 1
done &
echo $! >$ACTIVITYPID
}
case "$1" in
start)
do_start
;;
stop)
do_stop
;;
force-reload|restart)
$0 stop
$0 start
;;
*)
echo "Usage: /etc/init.d/udoo-activity {start|stop|restart|force-reload}"
exit 1
;;
esac
exit 0
To start the monitor at i.MX6's boot, you'll need to update your init.d scripts:
update-rc.d udoo-activity defaults
Finally, the code for controlling Arduino's part:
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
analogWriteResolution(12);
analogWrite(6, 0);
pinMode(6, INPUT);
analogWrite(6, 0);
pinMode(DAC0, OUTPUT);
analogWrite(DAC0, 0);
}
int BLINK_DURATION=50;
int IDLE_DURATION=2500;
int IDLE_DURATION_LIGHT=100;
int PULSE_DURATION=5500;
int LOOP_DURATION=10;
int idle_count = 0;
int light_on = 0;
int pulse_count = 0;
// the loop routine runs over and over again forever:
void loop() {
int level = analogRead(6);
if (level >= 900) {
blink(BLINK_DURATION);
pulse_count = 0;
idle_count = 0;
Serial.println(level);
}
else {
idle_count += 1;
if (pulse_count*LOOP_DURATION >= PULSE_DURATION) {
analogWrite(DAC0,0);
idle_count = 0;
light_on = 0;
}
else {
pulse_count += 1;
if (idle_count*LOOP_DURATION >= IDLE_DURATION && !light_on) {
analogWrite(DAC0,2048);
light_on = 1;
idle_count = 0;
}
else {
if (idle_count*LOOP_DURATION >= IDLE_DURATION_LIGHT && light_on) {
analogWrite(DAC0,0);
idle_count = 0;
light_on = 0;
}
}
}
}
// print out the value you read:
//Serial.println(sensorValue);
delay(LOOP_DURATION); // delay in between reads for stability
}
void blink(int duration) {
analogWrite(DAC0, 2048);
delay(duration);
analogWrite(DAC0, 0);
}
When there is no any network activity, the led will be slowly blinking, when the packets are received or sent by wlan6, it will blink fast. If i.MX6 is off or doesn't send a pulse, the led will be turned off. An apparent benefit of the last state (led is off) is that it can be used to determine when shutdown on i.MX6 is complete and when it's safe to turn the power off.
Yet another interesting thing that I've learned about UDOO is that even if you completely shut down i.MX6 9 (e.g. by running 'init 0') Arduino part of it will still be running.
Demo
At the video below you can see the led in two states: during the boot when the wireless interface is active trying to acquire a connection and after the boot is completed (starting @ 15th second) when there is no network activity and the led is blinking slowly because of the pulse signal sent by the i.MX6 board once in a few seconds.Saturday, November 16, 2013
Converting Raspberry PI to Music Center with Remote
Hooking New Stuff to Legacy Hardware
Long time ago I bought Onkyo TX-SV535 receiver/amplifier with a couple of decent speakers. I occasionally used it to listen radio or CD's spinning on another Onkyo device - DX-C320.I've realized lately that I could have much more fun with all that legacy hardware when got my first Raspberry PI device.
Installing Music Player Daemon (MPD) and Music Player Client (MPC) was not a problem. I've even managed to built MPD from sources to make sure that I can easily integrate it with Spotify, SoundCloud, last.fm and virtually any existing audio format.
It all worked well, but it was irritating a bit that I needed to use a native Onkyo's remote control for the receiver and MPC running on my mobile device or on a desktop to control Raspberry's MPD.
Then I came across the Adafruit's IR chip and realized that this is what I needed. Hooking up the chip to Raspberry's GPIO was easy - I used female to female connectors, drilled a hole in a Raspberry's clear case, used a bit of heat shrink tubing to make sure that the IR receiver can be fixed on the top of the case.
This is how everything looks together:
Making IR receiver to understand Onkyo's remote was a little bit harder, but eventually I've made it working.
Tuning IR to Onkyo's Remote
The first step was to install LIRC tools that have not been installed on my Raspberry OS by default:sudo apt-get install lirc
You'll also need to make sure that lirc_rpi driver is loaded before you can do anything:
sudo modprobe lirc_rpi
To make the module loaded at boot time add it to /etc/modules.
The next step is to record codes generated by remote's buttons that you want to use. Stop lircd daemon if it's running and use irrecord command line utility to record the codes:
sudo /etc/init.d/lircd stop
irrecord -d /dev/lirc0 ./lircd.conf
Follow instructions provided by irrecord and chose the buttons that you need.
This is how my lircd.conf file looks like:
begin remote name ONKYO-RC-287S flags RAW_CODES|CONST_LENGTH eps 30 aeps 100 gap 108090 begin raw_codes name KEY_POWER 9017 4480 624 509 579 1663 581 555 576 529 600 1670 577 530 600 1667 579 1662 583 1661 598 537 571 1670 599 1668 579 531 598 1678 569 1666 572 562 576 530 600 536 572 1669 598 536 581 555 574 531 600 534 582 554 576 1664 625 1618 598 536 584 1658 599 1675 576 1667 571 1672 592 1677 574 name KEY_LEFT 9054 4447 601 533 575 1667 601 533 625 509 579 1664 625 509 578 1672 616 1617 609 1659 579 538 604 1664 570 1676 622 501 574 1668 581 1661 607 527 581 1660 606 1673 565 1667 631 1610 606 530 577 556 574 1677 570 556 573 540 605 526 577 556 573 539 603 1666 569 1672 626 508 583 1652 631 name KEY_RIGHT 9040 4461 600 534 582 1658 601 535 572 562 578 1663 573 562 577 1663 588 1659 606 1660 576 531 609 1659 578 1665 591 543 576 1666 581 1661 608 526 581 553 576 1666 583 1660 606 1660 589 525 604 527 580 1667 600 540 569 1659 608 529 579 554 575 533 606 1662 575 1675 572 555 574 1667 579 name KEY_PLAY 9016 4485 628 505 581 1660 575 559 581 528 609 1657 583 527 601 1670 579 1666 570 1665 603 532 575 1665 604 1667 581 526 601 1666 582 1661 576 558 580 528 602 533 574 559 581 527 602 1667 580 527 603 1667 580 526 603 1672 581 1673 563 1666 603 1668 577 526 601 1673 572 527 602 1665 581 name KEY_PAUSE 9072 4439 569 555 574 1666 594 541 576 532 606 1664 574 532 606 1662 576 1677 570 1663 604 530 579 1661 607 1663 575 533 605 1672 565 1671 577 553 575 1668 579 555 574 1676 571 1663 606 529 579 554 575 1668 580 554 608 498 608 1669 567 534 605 530 579 1661 606 1662 576 532 616 1653 574 name KEY_LEFTALT 9015 4488 573 561 579 1660 575 560 579 528 601 1677 571 527 601 1669 578 1663 574 1665 603 533 583 1660 599 1668 579 528 601 1669 578 1665 574 562 575 530 599 1668 580 529 599 536 572 1670 598 536 571 1669 612 528 598 1649 600 526 581 1660 607 1672 565 532 608 1661 576 531 608 1659 578 name KEY_RIGHTALT 9044 4466 594 529 579 1664 603 529 579 556 573 1669 579 556 572 1669 587 1656 609 1667 567 538 600 1670 566 1676 583 546 572 1668 577 1665 601 533 573 1669 600 534 574 561 579 528 601 1668 577 531 600 1668 580 528 600 544 564 1668 598 1670 579 1664 573 561 578 1672 563 561 579 1664 572 name KEY_VOLUMEUP 9036 4464 597 537 579 1663 602 532 575 560 578 1662 573 567 573 1674 569 1663 599 1668 578 531 605 1665 571 1671 572 561 576 1667 579 1662 603 541 566 560 578 1661 573 563 575 534 603 530 576 558 580 1673 561 562 575 1668 577 556 572 1670 573 1668 597 1673 573 1669 574 560 577 1663 582 name KEY_VOLUMEDOWN 9032 4470 603 528 579 1666 605 529 574 559 580 1673 562 563 576 1615 627 1668 595 1677 567 533 603 1663 571 1672 573 575 563 1666 578 1662 603 533 574 1667 598 1679 565 534 603 538 569 559 579 528 598 1670 574 534 605 529 604 541 558 1674 572 1669 606 1662 572 1678 569 562 578 1661 579 end raw_codes end remote
Copy this file to /etc/lirc/lircd.conf to make it available for lircd daemon when it starts next time.
You'll also need to change /etc/lirc/hardware.conf to make it working with /dev/lirc0 device. The only two changes that I've made are below:
#DRIVER="UNCONFIGURED" DEVICE="/dev/lirc0"
'DRIVER' option was commented out and 'DEVICE' parameter was set to the IR device - /dev/lirc0.
If everything is done correctly, you can start lircd daemon now and check if IR receiver recognizes the configured keys using irw command:
sudo /etc/init.d/lirc start irw
Press configured buttons on remote sequentially and observe irw's output. If the irw shows all the keys that you've configured, proceed to binding the keys to actions using irexec daemon. You'll need to create a configuration file for irexec called lircrc and copy it it to /etc/lirc/lircrc. My lircrc file is provided below. You can find detailed description of the lircrc's file format here.
begin
remote = ONKYO-RC-287S prog = irexec button = KEY_POWER config = sudo shutdown -h now end begin remote = ONKYO-RC-287S prog = irexec button = KEY_RIGHT config = /usr/bin/mpc next end begin remote = ONKYO-RC-287S prog = irexec button = KEY_LEFT config = /usr/bin/mpc prev end begin remote = ONKYO-RC-287S prog = irexec button = KEY_PLAY config = /usr/bin/mpc toggle end begin remote = ONKYO-RC-287S prog = irexec button = KEY_PAUSE config = /usr/bin/mpc pause end begin remote = ONKYO-RC-287S prog = irexec button = KEY_LEFTALT config = /home/pi/load_playlist.py 0 end begin remote = ONKYO-RC-287S prog = irexec button = KEY_RIGHTALT config = /home/pi/load_playlist.py 1 end begin remote = ONKYO-RC-287S prog = irexec button = KEY_VOLUMEUP repeat = 1 config = mpc volume +1 end begin remote = ONKYO-RC-287S prog = irexec button = KEY_VOLUMEDOWN repeat = 1 config = mpc volume -1 end
KEY_POWER is used to shutdown Raspberry, for almost all other keys MPC command line utility is used to control MPD. I didn't find a simple way of loading a next or previous playlist from a server and had to write a small Python script called load_playlist.py to do that:
/home/pi/load_playlist.py
================================================================
#!/usr/bin/python # Load next/previous playlist from MPD server using MPC import os import sys import re CONFIG_FILE=os.path.expanduser("~/.load_playlist/current") if len(sys.argv) < 2: sys.exit("Usage: %s n [config_file]\n\tn 0 - prev, 1 - next\n\tconfig_file contains current playlist" % sys.argv[0]) cf = CONFIG_FILE next = True if len(sys.argv) > 2: cf = sys.argv[2] if sys.argv[1] == '0': next = False if not os.path.isfile(cf): if not os.path.isdir(os.path.dirname(cf)): os.system("mkdir -p %s" % os.path.dirname(cf)) os.system("echo '' >%s" % cf) if not os.path.isfile(cf): sys.exit("Can's create file %s" % cf) with file(cf) as f: s = f.read() s = re.sub(r'[\n\r]', "", s) print "S='%s'" % s ls = os.popen('mpc lsplaylists').read() # print ls lsa = ls.split("\n") del lsa [-1] # print lsa ind = 0 for i in range(0,len(lsa)-1): if (s == lsa[i]): if next: ind = i+1 else: ind = i-1 if ind < 0: ind = len(lsa) - 1 if ind >= len(lsa): ind = 0 if ind < 0: ind = 0 if ind < len(lsa): os.system("mpc clear") loaded = os.system("mpc load %s" % lsa[ind]) if loaded == 0: os.system("mpc play") os.system("echo %s >%s" % (lsa[ind], cf))
irexec daemon should start automatically when you restart lircd daemon.
That's very much it! Enjoy the quality of your old good HiFi improved by smartness of your PI integrated with IR and WiFi :)
Subscribe to:
Posts (Atom)