Skip to main content

Prototyping LoRa Wireless Monitoring with XinaBox Part 2: Sending Data

Main23_2430ca1117f911b38f5c17bec9f5ea6d2f8e4ca1.jpg

Rapid prototyping a long-range wireless sensor system with the modular electronics platform.

In part 1 we outlined our use case and took a look at LoRa wireless modulation, giving our rationale for using this, before then exploring some of the XinaBox hardware options available.

In this post, we move on to programming a CR02 module or “☒CHIP” — which features an integrated microcontroller and radio — and then sending and receiving data across a LoRa wireless link.

Setting up

IP01CR02_9d110cd278346f520f09bc46ca9b111221d226ba.jpg

First, we need to connect the IP01 (174-3703) programmer to the CR02 (174-3699) using an xBUS connector (174-4977) . We’ll also need to have a suitable antenna attached before running any LoRa examples, such as an SMA whip tuned for the 868MHz band (054-2563) .

AntennaDetail_f69ce59a3cdfbb14fffb614346c6fbf42399335c.jpg

Of course, we will actually require two sets of the aforementioned parts if we are to test both sending and receiving!

We will also need to have the Arduino IDE installed, or some other means of programming Arduino compatible boards. For example, the PlatformIO IDE should equally work in principle.

PLWM-P2_RadioHeadDownload_e3ee16139a43e1a6071a238d2815e4fa1fcf4e55.jpg

We’ll be using a library called RadioHead that provides the capability for sending and receiving packetised messages via a variety of common data radios, including the RFM95W that is integrated into the CR02 CHIP we’re using. At this point in time, it appears as though we will need to use the RadioHead fork hosted under the XinaBox GitHub organisation.

There are different ways of installing new libraries in the Arduino IDE, but the easiest is to download a ZIP archive and then in the IDE select Sketch → Include Library → Add .ZIP Library.

Under the Tools → Board menu, we need to select “Arduino Pro or Pro Mini”, ensuring that the appropriate port is selected via Tools → Port. Under Linux, the IP01 should enumerate as /dev/ttyUSB0 (or perhaps a higher number if there is another FTDI-based device connected).

Hello, World

CR02Blink_6c920636bc6433a7565640d72aa1ce2fe5f5518f.jpg

With setup out of the way, let’s move on to the “Hello, World of hardware” — LED blink!

The CR02 actually has a tri-colour LED and so the provided blink example is a little bigger than the one that ships with the Arduino IDE, but essentially the same and just cycles through flashing each colour in turn. Note that when uploading the sketch the LED on the IP01 should flash rapidly for a brief period and if it does not, this suggests a hardware error or port permissions issue.

Now let’s take a look at sending and receiving data via the LoRa radio.

LoRa Server

CR02Server_9ddb90a28c83e8b7c44c435c711fda650038b46c.jpg

So let’s start with the “server” sketch example. First, this includes the RadioHead library for our wireless module, before going on to define some constants, such as the operating frequency, which with the CR02 will be in the 868MHz band.

#include <RH_RF95.h>

#define LED_BUILTIN 16

#define CR02_FREQUENCY 868.0

uint8_t tempdata[30];

Next in setup() we configure an LED pin and serial port for debugging, before then going on to initialise the radio module and configure its frequency and transmit power.

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(115200);

  if (!CR02.init()) {
    Serial.println("init failed");
  }
  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
  // you can set transmitter powers from 5 to 23 dBm:
  // Failure to do that will result in extremely low transmit powers.

  //CR02.setModemConfig(CR02.Bw31_25Cr48Sf512);
  CR02.setFrequency(CR02_FREQUENCY);
  CR02.setTxPower(23, false);
}

There is also a line commented out that allows setting the link bandwidth and LoRa spreading factor (SF) to non-default values. In short, a higher spreading factor means more link budget and hence longer range, but at the cost of reduced bandwidth and more time on air transmitting.

The default spreading factor is fine for development purposes, but this is something that could be optimised for the particular environment, so as to get the best combination of range and energy consumption, plus use of spectrum. Generally, the less time on air, the better. However, if you wanted to really ensure your message was getting through, with bandwidth and battery life less of a concern — for example, with an alarm/alerting system — you might always use the highest SF.

For details of how to use the setModemConfig() call to configure the CR02 LoRa radio, see the RadioHead documentation.

Now on to the main loop. Here we look for an incoming message and when received, turn on the LED, then write the message out to the serial port, along with the received signal strength indication (RSSI), before then turning the LED off and finally transmitting a reply. Simple!

void loop()
{
  if (CR02.available())
  {
    // Should be a message for us now
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);
    if (CR02.recv(buf, &len))
    {
      digitalWrite(LED_BUILTIN, HIGH);
      Serial.print("got request: ");
      Serial.println((char*)buf);
      Serial.print("RSSI: ");
      Serial.println(CR02.lastRssi(), DEC);

      // Send a reply
      sprintf(tempdata, "%s", "Hello Client");
      CR02.send(tempdata, sizeof(tempdata));
      CR02.waitPacketSent();
      Serial.println("Sent a reply");
      digitalWrite(LED_BUILTIN, LOW);
    }
    else
    {
      Serial.println("recv failed");
    }
  }
}

LoRa Client

CR02Client_2eb95c9cef63f0eef1740afda4520cb7b3b2e950.jpg

The CRO2 Client example is very similar and as you might imagine, this instead transmits a message, then waits for a response. If we take a look at its main loop:

void loop()
{
  Serial.println("Sending to CR02_server");
  // Send a message to CR02_server

  sprintf(tempdata, "%s", "Hello Server");

  CR02.send(tempdata, sizeof(tempdata));

  CR02.waitPacketSent();
  // Now wait for a reply
  uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
  uint8_t len = sizeof(buf);

  if (CR02.waitAvailableTimeout(3000))
  {
    // Should be a reply message for us now
    if (CR02.recv(buf, &len))
    {
      digitalWrite(LED_BUILTIN, HIGH);
      Serial.print("got reply: ");
      Serial.println((char*)buf);
      Serial.print("RSSI: ");
      Serial.println(CR02.lastRssi(), DEC);
    }
    else
    {
      Serial.println("recv failed");
    }
  }
  else
  {
    Serial.println("No reply, is CR02_server running?");
  }
  digitalWrite(LED_BUILTIN, LOW);
  delay(400);
}

Once again we can see that it is quite simple and with this we get a message response printed out, along with the RSSI of the signal from the server.

More advanced options

The simple examples shown above do not provide encryption/message authentication or any sort of addressing. For many systems, this may be fine, but if much more advanced capabilities are required, there are also RadioHead examples that make use of encryption, and that provide a reliable datagram service complete with simple node addressing support. However, these have not been tested with the CR02 and it is possible that some modification may be required.

Coming up

In the next post in this series, we’ll add peripherals into the mix and our wireless monitoring system will start to come together.

Andrew Back

Open source (hardware and software!) advocate, Treasurer and Director of the Free and Open Source Silicon Foundation, organiser of Wuthering Bytes technology festival and founder of the Open Source Hardware User Group.
DesignSpark Electrical Logolinkedin