Tag Archives: pi

Parsing LLAP packets using NodeRED on the Raspberry Pi

It’s taken a few weeks but here is the next instalment in my promised series of articles. Part of the scenario requires me to detect a particular scenario using a remote sensor and relay it on to a variety of other systems via my Raspberry Pi. The sensor itself is assembled using the rather brilliant Ciseco Wireless Inventor’s Kit which provides all you need for prototyping remote sensors and transmitting data over a short range radio link back to a waiting Pi.

It’s a pretty simple yet powerful set of kit. You build your sensor using the breadboard and XinoRF module supplied which communicates with a Slice of Radio transceiver module you attach to the Pi’s GPIO pins. Your applications then send and receive data to and from the remote sensor via the Pi’s serial port and you’re away. The protocol used for the serial communications is designed by Ciseco and is called Lightweight Logical Application Protocol (LLAP). It takes the form of a simple fixed-field text datagram and as such can be parsed by pretty well any programming language that can interrogate the serial port.

In my case I’m using NodeRED to parse the LLAP packets so I can invoke other services in response to an event triggered via a sensor. The event is detected by a switch (a pressure mat in fact) that sets the D03 pin on the XinoRF module to be “high”. The nice thing about how the XinoRF works is that it operates in an event-driven way — i.e. you don’t need to poll to check the state of the pin, it transmits when there is a change in the state from “low” to “high” or vice versa. The other XinoRF pins perform different functions in different ways, but I thought I’d show how to at least send and receive so others can tailor to their scenario.

I’m assuming here that folks are familiar with the NodeRED concepts, so will get straight into the meat of how it works.

Configuring A serial input node to Receive llap packets into a flow

The first thing to do is configure the serial input node in NodeRED. The settings for my node are shown below.

Screen Shot 2014-10-14 at 17.56.37

You can confirm the serial port name using the RasWIK documentation and tools. A particular point to note is the Split input settings which determines at which point the input node offers up data to the next node in the flow. This can be set to a delimiter, or set to a timeout, wherein if no data is received within a certain period, the transmission is considered complete. I discovered that the latter timeout approach worked with a setting of 250 milliseconds, though it does introduce the possibility of multiple LLAP packets being received in one go, which we now need to address within the next node in our flow.

Creating a function node to segment multiple llap packets into individual messages

For greatest efficiency want all subsequent nodes in the flow to be designed to ingest a single LLAP packet in a single message. To ensure this downstream behaviour is honoured we create a function node after our serial input node to check for multiple packets and segment them into individual messages if so. The way NodeRED is designed to work, we can return an array of messages and it will cycle through each message in turn, invoking the remainder of the flow as if triggered by a single message.

The first thing we do in our function node is test for the simple case of a single packet which we can test by the length of the payload, LLAP packets being 12 characters in length.

var instring = msg.payload;
if (instring.length<=12) {
   console.log("Single packet: "+instring);
   return msg;
} 

If the length of the payload is longer, it means we have multiple packets so we need to iterate over the payload, extracting each packet into an array of new messages to return at the end of the function.

else {
   var packets = [];
   console.log("Multiple packets: "+instring);
   while (instring.length>=12) {
      var packet = instring.substr(0, 12);
      console.log("Unspliced packet: "+packet);
      packets[packets.length] = {payload:packet};
      instring = instring.substr(12);
      console.log("instring is now length "+instring.length);
   }
   return [packets]; 
}
return null;

An important point to note (highlighted above) is that although we have created an array of payload objects, NodeRED requires that we in turn encase that array in another array when we return it for processing downstream.

Parsing the LLAP packet

In our subsequent function node, we can now parse the single LLAP packet to determine whether we have a “high” event for the D03 pin we’re interested in. The format of an LLAP packet in this case would be:

a--D03HIGH--

Our code simply needs to check the packet that is received, and parse it see if it corresponds to the above event. Note that in a simple scenario we could just do a direct string comparison between what we’ve received and a template packet such as that above, but if we have multiple stations (denoted by characters 1 and 2, “–” in the example) then we might have more complex flow control logic. My function node simply emits the word “HIGH” if we’ve got a D03 “high” event from the XinoRF.

var thePayload = msg.payload;
var pin = thePayload.substring(3,6);
if (pin == "D03") {
   // Now test if it was HIGH
   if (thePayload.substring(6,10) == "HIGH") {
      console.log("HIGH on D03");

Now in my scenario, the switch (pressure mat) could toggle on and off in quick succession which would technically send a number of LLAP packets back to the Pi. Within a certain time window, I’m only interested in a single “high” event, so have added some logic using NodeRED’s context to test when I last received a “high” event and only taking action if it was more than a minute ago.

      var timeNow = new Date();
      console.log("timeNow is "+timeNow);
		
      if (context.lastHigh) {
         console.log("context.lastHigh is "+context.lastHigh);
         var lastTimeMillis = context.lastHigh.getTime();
         var currentTimeMillis = timeNow.getTime();
         var delta = currentTimeMillis - lastTimeMillis;
         if (delta < 60000) { 
            console.log("Less than a minute since last HIGH: "+
               delta+" ms.");
            return null;
         }
         console.log("Over a minute, actually "+delta+" ms.");
      } else {
         console.log("Continuing.");
      }
      context.lastHigh = timeNow;
      return {payload: "HIGH"}
   }
}
return null;

So, if we get a “high” event and we haven’t seen one for at least a minute, the subsequent nodes in the flow will receive a packet with a payload of “HIGH”.

And with that we’re free to continue our processing, which could be as simple as a debug node. Here’s a snapshot of my NodeRED workspace — in this case I’m using the “high” event to trigger a Tweet.

Screen Shot 2014-10-14 at 18.33.17

 

 

Configuring the Raspberry Pi to share a Mac’s internet connection

One of the great things you can do with a Mac is share your internet connection, and I’ve found this particularly invaluable for doing development with my Raspberry Pi, and especially when I want to take it somewhere to show people. Normally it connects to my home router where I can configure the router to have a static IP address so I know where to find it and off I go, not so easy when you’re mobile.

There are a number of articles out there claiming to show you how to make it all work when you Google for it, but it still took me a fair bit of puzzling and piecing of the bits together to come up with something that worked. So, to save you that trouble, I’m recording my configuration here.

First set up your ethernet adapter

The mechanism I’m using to connect my Mac (Macbook Pro Retina running OS X Mavericks 10.9.5) is via good old Ethernet. For this you’ll need the (rather expensive) Thunderbolt to Ethernet adapter if your Macbook (like mine) doesn’t have an onboard Ethernet socket. You can get cheaper non-Apple alternatives but I’ve no experience of whether they work or not. Once plugged in, open up System Preferences, and then Network. You’ll notice your Ethernet port appears on the list of adapters on the left hand side. You can give the configuration a name of its own, I’ve imaginatively called mine “Raspberry Pi Ethernet (home)” as you can see below.

Screen Shot 2014-10-14 at 15.49.52Now of course DHCP would be fine, and whilst you can probably guess at which IP the Mac’s DHCP server will give you each time, it’s more predictable to have a static IP defined, particularly when running your Pi in “headless” mode. There are therefore some settings we need to set up to make this hang together, not least a static IP configuration for the Mac as it will appear to the Pi so we can use it as our IP gateway. As in the above picture, use the following settings:

  • Configure IPv4: Manually
  • IP Address: 192.168.2.1
  • Subnet mask: 255.255.255.0

Because we’re using a static configuration, we now need to set the DNS server settings ourselves. Without any DNS servers set, the Ethernet connection (and therefore the Pi) will not be able to resolve any of the domain names we throw at it.

Click the Advanced… button, then the DNS tab and then the + button to add a DNS server entry.

Screen Shot 2014-10-14 at 15.59.09

You can simply lift the DNS settings from your Wifi configuration (provided of course your wifi is already up and happily resolving!) or indeed use some public DNS server settings. I’ve got a mix of public DNS, a couple from work and my home router. Click OK when you’re done to save the settings.

NOW TURN ON INTERNET SHARING

Having set up the Ethernet port, we now need to tell the Mac we want to share the wifi internet connection with devices connected via Ethernet. It couldn’t be easier — back to System Preferences, select Sharing and tick Internet Sharing. Select Wi-fi and Thunderbolt Ethernet as the points where you want to share from and to as per the screenshot below.

Screen Shot 2014-10-14 at 16.06.29

 

NOW CONNECT AND CONFIGURE THE PI

I was surprised how resilient the local configuration between the Mac and the Pi was when I was fiddling with these next steps. Getting the Mac and the Pi to see each other is the easy bit, the harder bit was making the Pi be able to see the internet beyond. Apple have made things easier still by making the Ethernet adapter work out when you’re using it to connect to another machine and automatically flipping into “crossover” mode so you can connect the Pi with bog standard Ethernet cable. Connect the Pi to the Mac’s Ethernet adapter and start it up.

First time of asking the Pi will be assigned an IP of 192.168.2.2 by the Mac’s DHCP server (assuming your Pi by default is set up for DHCP from the Ethernet port). I’ve not tested the theory of whether that ever changes, but to make absolutely sure (and certainty is definitely preferred when doing demos) we will configure the Ethernet adapter with static settings based on the Mac’s configuration. Once the Pi is started, connect via SSH from the Mac and once logged in and on the command prompt, enter the following command:

sudo vim.tiny /etc/resolv.conf

This will open the Vim editor (same commands as Vi). You want a single line it as follows:

nameserver 192.168.2.1

This tells the Pi to use the Mac as its DNS server, rather like you might expect with a connection to a home router.
Having saved the changes, we now need to set the static configuration for the Ethernet port. We’ll use Vim again, this time enter the following command:

sudo vim.tiny /etc/network/interfaces

This time there are a few more lines to add. If you want to make sure you don’t forget your existing Ethernet settings, you can comment them out with a hash. Otherwise, your settings should look as follows:

auto eth0
iface eth0 inet static
address 192.168.2.2
netmask 255.255.255.0
gateway 192.168.2.1

This tells the Pi to adopt the 192.168.2.2 address permanently using the Mac as the IP gateway with corresponding subnet mask.

You’re now all set. For good measure, restart the Pi and all the changes should stick.

The Proof in the PUDDING (or PI)

In my case I’ve been using TightVNC to access the Pi’s desktop, so as a crude test that everything was hunky dory I simply open up my Midori browser and try and hit the public internet. For posterity, everything looks as it should when I try and hit this blog:

Screen Shot 2014-10-14 at 16.29.13