Firmware v1/v2

Overview

Firmware for the ATTNode is developed using PlatformIO, which can be used with the Atom Editor or Visual Studio Code on all major platforms. The code of the reference firmware can be found in my git repository. The firmware code uses PlatformIOs Arduino Framework for the Atmel Platform.

The reference firmware does supports the I²C sensors SHT21 (Temperature, Humidity) and BME280 (Temperature, Humidity, Atmospheric Pressure) as well as a beacon mode without any sensor. Selecting the sensor type and adjusting LoRaWAN keys and other parameters is done via defines and variables and is described in the next section.

Please see this post for additional information about running the AttNode v1 and v2 on The Things Stack (TTN v3)

Configuration for ATTNode

All configuration is done in the file src/secconfig.h via preprocessor defines and variables. Here is an example:

#define RF_LORA
#define HAS_SHT21
#define LED_PIN PIN_A7
#define LED_ON_SEND
#define SLEEP_TIME 544
unsigned char NwkSkey[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
unsigned char AppSkey[16] = { 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
unsigned char DevAddr[4] = { 0xAA, 0xBB, 0xCC, 0xDD };

Here are the meanings of the individual lines:


#define RF_LORA

Configures the firmware for the RFM95W LoRa transceiver chip. The firmware can also be used with a TinyTX Transmitter using the RFM69W transceiver, which can be done with #define RF_RFM69


#define HAS_SHT21

Chooses the SHT21 sensor. Instead of this there are also

  • #define HAS_BME280 - configure for a BME280 sensor
  • #define HAS_SHT21_BRIGHTNESS - configure a SHT21 Sensor + Brightness Measurement via LED
  • #define HAS_BME280_BRIGHTNESS - configure a BME280 Sensor + Brightness Measurement via LED
  • #define HAS_ALARM - configure as an Alarm Sensor using a Pin-Interrupt (See description of ALARM_PIN for details)
  • #define HAS_SHT21_ALARM - configure a combined Sensor with SHT21 and Alarm functionality
  • #define HAS_BME280_ALARM - configure a combined Sensor with SHT21 and Alarm functionality
  • #define HAS_NO_SENSOR - enable beacon mode, which will send the battery voltage on all LoRa Spreading Factors for gateway monitoring.

#define LED_PIN PIN_A7

Enable the use of the LED. PIN_A7 is connected to the LED on ATTNode v1 and v2. If this line is left out the LED will not be used at all (e.g. for energy saving purposes)


#define LED_ON_SEND

If this define is enabled, the LED (set with LED_PIN above) will be on during sending. If this is not set, but LED_PIN is set, there will be a brief blink on powerup to indicate that the device is ready, but the LED will stay off during send to save energy. This is the recommended setup for production. If neither of the two LED defines is set the LED will not be used at all.


#define SLEEP_TIME 544

The amount of time in seconds to sleep between sensor readings and transmits. The time will be divided by 8 for the sleep method, since the deepsleep interval is 8 seconds at a time. Because of this limit its a good idea to choose a value divisible by 8 here (Other values are accepted, but will be rounded to the next multiple of 8). Typical values I use are:

  • 528 - for BME280 sensor, 10 minute interval
  • 544 - for SHT21 sensor, 10 minute interval
  • 1616 - beacon mode, 30 minute interval

unsigned char NwkSkey[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
unsigned char AppSkey[16] = { 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
unsigned char DevAddr[4] = { 0xAA, 0xBB, 0xCC, 0xDD };

These are the Network / App Session Keys and Device Address from console.thethingsnetwork.org They have to be formated as a C byte array and in MSB order. The device has to be configured for the ABP activation method and with a 16bit Frame Counter.


#define HAS_ALARM
#define ALARM_PIN PIN_A0

This is not in the example above. With this configuration, the ATTNode acts as an Alarm Sensor. If the define ALARM_PIN is pulled to Ground, it will wake the ATTNode and send a Message immediately. Otherwise it will send a "Ping" every defined sleep Interval. You can also use this together with one of the climate sensor with the defines HAS_BME280_ALARM or HAS_SHT21_ALARM.

Payload Decoder

To get usable values from TheThingsNetwork, you will have to configure a payload decoder in the application. Here is the needed decoder function that can decode the values from the two sensors and beacon mode:

function Decoder(bytes, port) {
  var decoded = {};

  if (bytes.length == 16) {
    // Old Payload Format
    decoded.t = ((bytes[0]) | (bytes[1] << 8 ) | (bytes[2] << 16 ) | (bytes[3] << 24)) / 100.0;
    decoded.p = ((bytes[4]) | (bytes[5] << 8 ) | (bytes[6] << 16 ) | (bytes[7] << 24)) / 100.0;
    decoded.h = ((bytes[8]) | (bytes[9] << 8 ) | (bytes[10] << 16 ) | (bytes[11] << 24)) / 100.0;
    decoded.v = ((bytes[12]) | (bytes[13] << 8 ) | (bytes[14] << 16 ) | (bytes[15] << 24)) / 1000.0;
  } else {
    // New Payload Format
    // We always have Battery Voltage (uint8_t)
    decoded.v = (bytes[0] * 20) / 1000.0;

    // Alarm Triggered (uint8_t)
    if (bytes.length == 2)
      decoded.a = bytes[1];

    // Temperature (int32_t)
    if (bytes.length >= 5)
      decoded.t = ((bytes[1]) | (bytes[2] << 8 ) | (bytes[3] << 16 ) | (bytes[4] << 24)) / 100.0;

    // Humidity (int32_t)
    if (bytes.length >= 9)
      decoded.h = ((bytes[5]) | (bytes[6] << 8 ) | (bytes[7] << 16 ) | (bytes[8] << 24)) / 100.0;

    // Alarm Triggered (uint8_t)
    if (bytes.length == 10)
      decoded.a = bytes[9];

    // SHT21 + Brightness (int16_t)
    if (bytes.length == 11)
      decoded.b = ((bytes[9]) | (bytes[10] << 8 ));

    // Atmospheric Pressure (int32_t)
    if (bytes.length >= 13)
      decoded.p = ((bytes[9]) | (bytes[10] << 8 ) | (bytes[11] << 16 ) | (bytes[12] << 24)) / 100.0;

    // Alarm Triggered (uint8_t)
    if (bytes.length == 14)
      decoded.a = bytes[13];

    // BME280 + Brightness (int16_t)
    if (bytes.length == 15)
      decoded.b = ((bytes[13]) | (bytes[14] << 8 ));
  }
  return decoded;
}

The code above needs to be put as a custom decoder under Payload Formats in the TheThingsNetwork Console.

The output is a json object containg battery voltage (v), temperature (t), humidity (h) and atmospheric pressure (p). Depending on the used sensor / mode some of the values might not be available.

Programming

For programming the firmware the board has a standard Atmel compatible 6-Pin programming header. The pin mapping on the header is as follows, starting form pin 1 at the top left (denoted with a square pin on the PCB, looking from the top side of the board, which has the ATTiny and RFM95):

| MISO | 3V3  |
| SCK  | MOSI |
| RST  | GND  | 

I use the DIAMEX-AVR USB-Programmer, which uses the stk500v2 protokoll and is compatible with the ATTiny84.

Any other AVR programmer compatible with the ATTiny84 should work as well. Please note that you should set your programmer to 3.3V if you use it to power the node during programming. not all the used components are 5V tolerant. Especially the RFM95W has an absolute maximum Rating of only 3.9V! Depending on the programmer you might have to change the settings in the platformio.ini to choose the correct type and parameters. Please refer to PlatformIOs and your programmers documentation for the details.