Raspberry Pi peer-to-peer communications

Lead Image © sergwsq, 123RF.com

Pi Play

MQTT messaging controls multiple Pi music players from a smartphone.

For Pi-to-Pi communication, you can choose among a number of different protocol options, and some excellent cross-platform messaging protocols meet simple data-passing requirements, including Advanced Message Queue Protocol (AMQP) [1], Constrained Application Protocol (CoAP) [2], Message Queuing Telemetry Transport (MQTT) [3], and Simple/Streaming Text-Oriented Messaging Protocol (STOMP) [4]. Moreover, some popular middleware solutions like RabbitMQ [5] layer on top of the messaging protocols and offer a generic interface with many added features.

For simple Pi data-passing projects, MQTT is something you should seriously consider. MQTT is a lightweight, well-documented solution that is easy to install. Many Internet of Things (IoT) options are available, and Arduino has a number of libraries that support MQTT.

MQTT comprises the following elements:

  • The MQTT broker manages and distributes messages.
  • The MQTT publisher is a source of data.
  • The MQTT subscriber receives the data.

Open source MQTT brokers exist for Windows, Linux, and Mac machines, and a number of public MQTT brokers can be used for testing or for IoT applications.

Python and MQTT

In a simple example, one Raspberry Pi (Rasp Pi) can be an MQTT publisher and a second Rasp Pi can be an MQTT subscriber. An MQTT public broker, such as the Eclipse IoT [6], manages the message handling and is referenced by both Rasp Pis. Figure 1 shows this basic layout.

Figure 1: Simple MQTT publisher, subscriber, and broker.

If you already have the Python pip package manager (included in "jessie," but not "wheezy") [7], you can install the Python Paho MQTT client [8] by entering:

sudo pip install paho-mqtt

The Paho library has some wrapper functions that make MQTT communications quite simple. To connect to an MQTT broker and send a message from a client to a broker use:

connect(<host>, port=1883, keepalive=60)
publish(<topic>, payload="None", qos=0)

The <topic> parameter is a unique name that links subscribers and publishers together, and payload is the message sent. Topics are case sensitive, and payloads are strings ("None" sends an empty payload). Using JSON, it is possible to dump complex data structures into a single payload string. Listing 1 is a simple publish program that uses the iot.eclipse.org public broker and sends the time as a message in the topic mydata.

Listing 1

mqtt_send_time.py

01 # Python Publisher
02 #   Send the time (hh:mm:ss) to MQTT queue (mydata) every 10 seconds
03 import paho.mqtt.client as mqtt
04 import time
05
06 client = mqtt.Client()
07 client.connect("iot.eclipse.org",1883,60)
08
09 while True:
10   mypayload = time.strftime("%I:%M:%S")
11   client.publish("mydata", mypayload  );
12   time.sleep(10)
13
14 client.disconnect();

To subscribe, or read, MQTT messages, use the following functions:

subscribe(<topic>, qos=0)
on_message(<client>, <userdata>, <message>)
loop_forever()

The subscribe() function topic needs to match the publisher topic; you can subscribe to multiple topics with the same client object. The quality of service (qos) option prioritizes messages. Listing 2 is a simple subscribe program that collects data from the mydata topic.

Listing 2

mqtt_read_mydata.py

01 # Python Subscriber
02 #   Get messages from the MQTT queue (mydata)
03 import paho.mqtt.client as mqtt
04
05 def on_message(client, userdata, msg):
06   print( msg.payload)
07
08 client = mqtt.Client()
09 client.connect("iot.eclipse.org",1883,60)
10 client.subscribe("mydata")
11
12 client.on_message = on_message
13
14 print ("Waiting for messages .....")
15 client.loop_forever()

Internet Radio Controller

In this example, a web page with a JavaScript MQTT provider sends Internet radio stations and volume settings to multiple Rasp Pis (Figure 2). On the Rasp Pis, a small Python MQTT subscriber program changes the Internet radio stations and volume levels as instructed. The goal in this example is to have the same music playing throughout the house.

Figure 2: Web page controlling three Rasp Pi music players.

To enable Internet radio stations, install the Music Player Daemon (mpd) and Music Player Client (mpc) with:

sudo apt-get install mpd mpc

The key commands that manage Internet radio stations include the following:

  • mpc add <radio station url> adds a radio station to a playlist.
  • mpc play <play list number> plays a selected playlist.
  • mpc volume <number> adjusts the volume between 0% and 100%.
  • mpc clear clears the playlists.

The Internet Radio website [9] lists a number of Internet radio stations. To discover a radio station's URL, right-click on any of the station links and select Copy link address (Figure 3).

Figure 3: Getting URLs for Internet radio stations.

On each Pi, a Python program subscribes to an MQTT topic called Radio. New Internet radio stations are passed as strings, and new volume settings are passed as numbers between 0 and 100. For this application, the MQTT payload could be either an integer or a string, so a decode function like

msg.payload.decode("utf-8")

converts from a byte array to a generic string.

When a new radio station is received, the current playlist is cleared (mpc clear), the new radio station is added to the playlist (mpc add), the volume is reset to its original value (mpc volume), and the radio station plays (mpc play). If the payload is numeric only, the volume changes. The full Rasp Pi Python program is shown in Listing 3.

Listing 3

mqtt_2_mpc.py

01 # mqtt_2_mpc.py - have MQTT change Internet Radio Stations
02 #
03 import paho.mqtt.client as mqtt
04 import os
05
06 thevolume = 75  # save the volume
07
08 # Subscribe to topic: Radio
09
10 def on_connect(client, userdata, flags, rc):
11   print("Connected with result code "+str(rc))
12   client.subscribe("Radio")
13
14 def on_message(client, userdata, msg):
15   global thevolume
16   themsg = msg.payload.decode("utf-8")
17   print("payload : " + themsg)
18   # if the message is a number it's the volume
19   if themsg.isnumeric():
20     thevolume = themsg
21     os.system("mpc volume " + thevolume)
22   # if the message is a string it's the station URL
23   else:
24     os.system("mpc clear")
25     os.system("mpc add '" + themsg + "'")
26     os.system("mpc volume " + thevolume)
27     os.system("mpc play")
28
29 client = mqtt.Client()
30 client.connect("iot.eclipse.org",1883,60)
31
32 client.on_connect = on_connect
33 client.on_message = on_message
34
35 print ("Waiting for messages .....")
36 client.loop_forever()

To control the Internet radio stations, I created a simple web page (Figure 4). The full HTML and JavaScript code is shown in Listing 4. The <option> tags (lines 30-34) set up a list of radio stations. One <button> tag (line 28) passes the selected radio station to the newStation() function, and another (line 37) passes the slider's value to the newVolume() function. The <input type="range"> in line 36 selects a volume between 0% and 100%.

Figure 4: Internet radio web page.

Listing 4

MQTT_Music.htm

01 <html>
02 <head>
03 <title>MQTT Publisher: Topic-Radio</title>
04 <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
05
06 <script>
07 // Create a client instance
08 client = new Paho.MQTT.Client("iot.eclipse.org", 80,"");
09 client.connect();
10
11 function newStation() {
12   var x = document.getElementById("mySelect");
13   var thestation = x.options[x.selectedIndex].value;
14   message = new Paho.MQTT.Message(thestation); //define the message text
15   message.destinationName = "Radio";          //define the topic
16   client.send(message);
17 }
18 function newVolume(theRange) {
19   var theVolume = document.getElementById(theRange).value;
20   message = new Paho.MQTT.Message(theVolume);
21   message.destinationName = "Radio";
22   client.send(message);
23 }
24
25 </script>
26 <body>
27 <h1>MQTT Music Control</h1>
28 <button onclick="newStation()">Update Radio Station</button><br>
29 <select id="mySelect" style="width:300px" size="5">
30     <option value='http://185.33.21.112:11029'>1.FM Amsterdam Trance Radio</option>
31     <option value='http://104.166.83.114:9918' selected >Radio Jamaica Brasileira</option>
32     <option value='http://66.85.88.2:7136'>Comedy104</option>
33     <option value='http://live.leanstream.co/CKNXFM'>The One - Wingham</option>
34     <option value='http://eu.radioboss.fm:8121'>Yoga</option>
35 </select><br>
36 <input type="range" style="width:300px;box-shadow: 1px 1px 1px #40FFB9;background: #3071A9;" id="Range1" value="50"><br>
37 <button onclick="newVolume('Range1')">Change Volume</button>
38 </body>
39 </html>

On the web page, the Paho JavaScript client [10] connects to an MQTT broker (line 4). It is important to note that the JavaScript interface uses WebSockets to communicate with the MQTT broker (typically on port 80; lines 8-9), rather than the native MQTT port 1883 (Figure 5). If you install your own MQTT broker, you need to check the documentation for WebSocket setup and support.

Figure 5: MQTT brokers support WebSockets.

The JavaScript function newStation() (lines 11-17) passes the selected radio station URL. Lines 14-16 publish an MQTT topic. The newVolume() function (lines 18-23) passes the volume to the Radio topic. If your phone or tablet cannot open web pages locally, you might have to use a web server. Included in the online code [11] is a Python application (web2mqtt.py) that runs a small standalone web server that will host this page.

Buy this article as PDF

Express-Checkout as PDF

Pages: 6

Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content