Sending multiple values Feb 2017

The previous article showed how to send a value wirelessly from one node to another, as text.

While it’s a good example to start off with due to the simplicity, this doesn’t really scale. It’s tedious to build up text packets, they lead to larger packet payloads, and this all adds to the complexity on both ends. It’s better to send binary data, using an encoding which compresses small values a little (which also happens with ASCII: small integers take fewer bytes).

Also, we really need to be able to send multiple values per packet, not just one.

Sending individual bytes would be easy: just store them one after another in a buffer and send the buffer as payload. But not all values are in the range -128..+127 (or 0..255 if unsigned). And using 4 bytes per value is a bit wasteful for values which do happen to be relatively small, even though this could transfer values from -2,197,815,296 .. +2,197,815,295.

There are a few ways around it. One approach been used in various projects at JeeLabs is “varint-coding” - a loosely defined term which means that you store a variable number of bytes for each value, depending on its magnitude.

The varint format was described a while ago on the weblog, see this article. Unfortunately, it suffers from being limited to non-negative integers. Not so great when sending temperatures.

A slightly extended version of varints has now been implemented in Forth, which adds support for negative values and is nevertheless quite easy to encode and decode in other programming languages. See the new Varint documentation page for details.

This provides us with a tool to send multiple values, so let’s extend the previous example to send four ADC readings every second, measured on pins PA0..PA3.

Let’s start off with a quick interactive check that the ADC works:

adc-init  ok.
PA0 adc . 11  ok.
PA1 adc . 641  ok.
PA2 adc . 640  ok.
PA3 adc . 677  ok.

The varint package convention is that the first value is a “packet type ID” - this lets us verify that we’re decoding the proper data, and allows us to deal with different packet formats later.

The first task is to make sure that we have the varint package loaded - and while we’re at it, let’s store that code in flash memory (the usual approach would be to add this to core.fs):

compiletoflash  ok.
!s ../flib/any/varint.fs
compiletoram  ok.

If you get a bunch of “Redefine” warnings, then varint was already present. That’s fine.

Now we can “assemble” a packet. Let’s use ID 6. To try this out, we need to have rf-listen running on the receiving F103 node. Then we can enter the following on the sending JNZ:

adc-init rf-init
6 <pkt  pa0 adc +pkt pa1 adc +pkt pa2 adc +pkt pa3 adc +pkt  pkt>rf

The result of repeating this a few times shows up as follows on the F103:

RF69 21EB2AB701FDE2803D08 8C900A8C0A9C0BB0
RF69 21EB2ABB01FD26803D08 8C800A920AA00BB4
RF69 21EB2AB901FEC2803D08 8C800A920A9A0BAE

Only 8 bytes of payload, but nearly undecypherable. We’re going to have to decode this stuff. Luckily, that’s trivial - all we need to do is replace type with var. from the varint package:

: rxtestv ( -- )
    rf-recv ?dup if
      cr  rf.buf 2+  swap 2-  var.
  again ;

As before, we can save this to file (”rxtestv.fs”) and start up the receiver as follows:

!s rxtestv.fs

Then, after sending a few more packets from the JeeNode Zero, we’ll get something like:

6 8 641 654 742
6 57 652 660 716
6 0 646 668 732

As expected: a packet format ID of 6, and then 4 values from the ADC channels. Success!

Weblog © Jean-Claude Wippler. Generated by Hugo.