Difference between revisions of "LoraWanDustSensor"

From RevSpace
Jump to navigation Jump to search
(Hardware)
(Next steps)
(42 intermediate revisions by the same user not shown)
Line 14: Line 14:
 
This has been done before by other people, but it appears there is no universal solution.
 
This has been done before by other people, but it appears there is no universal solution.
 
I am publishing all source code on github and will put up documentation on this wiki.
 
I am publishing all source code on github and will put up documentation on this wiki.
This concept uses Cayenne because it is the closest practical thing towards a universal but still reasonably compact format.
+
This concept uses Cayenne because it is the closest practical thing towards a universal format but still reasonably compact format.
  
 
A similar thing has been done by:
 
A similar thing has been done by:
Line 25: Line 25:
  
 
One thing in particular that my concept does better than existing solutions is to use proper OTAA for the LoRa connection to TTN.
 
One thing in particular that my concept does better than existing solutions is to use proper OTAA for the LoRa connection to TTN.
OTAA means over-the-air-activation and is a mechanism to dynamically negotiate communication/encryption keys instead of having to hard-code each node with individual keys.
+
OTAA means over-the-air-activation and is a mechanism to dynamically negotiate encryption keys and communication settings.
Once the OTAA is done successfully, the node remembers the network id, device address, session keys, etc for future communication, as per TTN recommendations.
+
 
 +
The OTAA key is hardcoded into the node, the session keys are not.
 +
The node identifies itself by its built-in unique ESP32 serial number.
  
 
This makes it possible to have a *single* firmware image for all sensor nodes and it simplifies the setup:
 
This makes it possible to have a *single* firmware image for all sensor nodes and it simplifies the setup:
* you flash the node with a unified firmware
+
* each node can be flashed with the same firmware
 
* the node shows its unique Device EUI on the OLED
 
* the node shows its unique Device EUI on the OLED
 
* at the TTN console, you register the node with the unique Device EUI
 
* at the TTN console, you register the node with the unique Device EUI
* the sensor node receives encryption keys over the air automatically
+
* the sensor node receives encryption keys over the air automatically by OTAA
 
* done!
 
* done!
  
Line 38: Line 40:
  
 
=== Next steps ===
 
=== Next steps ===
* support more BME280 i2c addresses
+
* Implement the ESP-Now lamp/display protocol, for easy visualisation, see https://revspace.nl/StofAnanas#Next_generation
* disable screen when idle, to avoid burn-in of the OLED?
+
* <strike>Update TTN upload schedule, set interval to luftdaten interval of 5 minutes, add some upload time randomness and avoid synchronised collisions between nodes</strike>
* support for other meteo sensors?
+
* <strike>Disable screen when idle, to avoid burn-in of the OLED. Press the PRG button to enable it again.</strike>
* internal web server, for configuration, information?
+
* Internal web server, for configuration, information?
 +
* Firmware update over WiFi:
 +
** basic OTA-over-WiFi has been implemented, allows remote flashing from the development environment, not so user-friendly, not secure
 +
** basic OTA-over-WiFi with a timeout, not so user-friendly, not very secure
 +
** next step is to use the ESP32 as access point + web server, user-friendly (only a browser needed to upload firmware images), not very secure
 +
** final step is to use the ESP32 as access point + web server, using signed images, this is user-friendly and secure
  
 
=== Links ===
 
=== Links ===
Line 55: Line 62:
  
 
== Hardware ==
 
== Hardware ==
The node is based on Arduino, in particular a TTGO ESP32 board ("LORA v1" I think) with onboard SX1276 LoRa chip.
+
The node is based on the Arduino framework:
The sensor is an SDS-011, just like in the luftdaten project.
+
* for the processor board, either the TTGO ESP32 board ("ttgo-lora32-v1") and the Heltec LoRa32 V2 can be used.
For humidity/temperature, I am using a BME280 (superior to the DHT11/22).
+
* the particulate matter sensor is the SDS-011, just like in the luftdaten project.
 +
* the humidity/temperature sensor is the BME280 (superior to the DHT11/22).
  
[https://primalcortex.wordpress.com/tag/lora32/ Page with correct pinout of the ESP32 LoRa board].
+
TODO: plaatjes van de hardware
  
 +
=== Pinout ===
 
{| class="wikitable"
 
{| class="wikitable"
! ESP32 !! Module !! Remark
+
! [https://primalcortex.files.wordpress.com/2017/11/ttgolorapinout_v2.jpg TTGO LoRa v1] !! [https://resource.heltec.cn/download/WiFi_LoRa_32/WIFI_LoRa_32_V2.pdf Heltec LoRa v2] !! Sensor !! Remark
 
|-  
 
|-  
| 5V || SDS011 5V || pin 3
+
| 5V || 5V || SDS011 5V (pin 3) || triple-check this, swapping 5V/GND destroys the SDS011
 
|-  
 
|-  
| GND || SDS011 GND || pin 5
+
| GND || GND || SDS011 GND (pin 5) || triple-check this, swapping 5V/GND destroys the SDS011
 
|-  
 
|-  
| GPIO25 || SDS011 RXD || pin 6
+
| GPIO23 || GPIO23 || SDS011 RXD (pin 6) ||
 
|-  
 
|-  
| GPIO35 || SDS011 TXD || pin 7
+
| GPIO22 || GPIO22 || SDS011 TXD (pin 7) ||
 
|-  
 
|-  
| 3.3V || BME280 3V || power
+
| 3.3V || 3.3V/Vext || BME280 3V || Both Vext and 3.3V can be used
 
|-  
 
|-  
| GPIO4 || BME280 SDA || data
+
| GND || GND || BME280 GND || ground
 
|-  
 
|-  
| GPIO15 || BME280 SCL || data
+
| GPIO15 || GPIO15 || BME280 SCL ||
 
|-  
 
|-  
| GND || BME280 GND || ground
+
| GPIO4 || GPIO4 || BME280 SDA ||
 
|}
 
|}
  
Line 86: Line 95:
  
 
== Software ==
 
== Software ==
=== Source code ===
+
=== Compile and upload the firmware ===
 
Source code is hosted on github:
 
Source code is hosted on github:
 
* [https://github.com/bertrik/LoraWanPmSensor Arduino node], written in C/Arduino, built using platformio. This firmware joins TTN by OTAA and sends the measurement data using Cayenne.
 
* [https://github.com/bertrik/LoraWanPmSensor Arduino node], written in C/Arduino, built using platformio. This firmware joins TTN by OTAA and sends the measurement data using Cayenne.
 
* [https://github.com/bertrik/LoraLuftdatenForwarder TTN-to-luftdaten forwarder], written in Java, built using gradle. This picks up the Cayenne encoded data and forwards it to the Luftdaten API.
 
* [https://github.com/bertrik/LoraLuftdatenForwarder TTN-to-luftdaten forwarder], written in Java, built using gradle. This picks up the Cayenne encoded data and forwards it to the Luftdaten API.
 +
 +
On Linux, with platformio (the command line tool), instructions for Debian Linux are:
 +
* install platformio:
 +
  sudo apt install python3-pip
 +
  sudo pip3 install platformio
 +
  pio update
 +
* get the source from github:
 +
  git clone https://github.com/bertrik/LoraWanPmSensor
 +
* enter the correct directory:
 +
  cd LoraWanPmSensor
 +
  cd Esp32PmSensor
 +
* compile and upload (for TTGO LoRa32 v1 board):
 +
  pio run -e ttgov1 -t upload
 +
or (for Heltec LoRa32 v2 board):
 +
  pio run -e heltecv2 -t upload
 +
 +
On Windows, with the Arduino IDE
 +
* Install the Arduino IDE ...
 +
* Get the ESP32 support package ...
 +
* Install the following libraries
 +
** squix78/ESP8266_SSD1306
 +
** mcci-catena/MCCI LoRaWAN LMIC library, version 3.2.0
 +
** sparkfun/SparkFun BME280
 +
* Set the target to either the TTGO LoRa32 v1 or Heltec LoRa32 v2
 +
* Ctrl-U to compile and upload
  
 
=== Payload encoding ===
 
=== Payload encoding ===
Line 116: Line 150:
  
 
Particulate matter values encoded are averaged over the measurement interval.
 
Particulate matter values encoded are averaged over the measurement interval.
 +
 +
Update: discovered that the Cayenne standard *does* actually support a "particle concentration" id, this is 125,
 +
derived from IPSO id 3325 http://www.openmobilealliance.org/tech/profiles/lwm2m/3325.xml .
 +
No idea yet though how to specify the type of particle measurement, if there's any convention for PM10, PM2.5, etc.
 +
The resolution is only 1 ppm, with typical sensors delivering 0.1 ppm resolution.
  
 
===== Binary =====
 
===== Binary =====
Line 148: Line 187:
  
 
=== LoraWan time budget ===
 
=== LoraWan time budget ===
 +
The number of bytes per telemetry packet and the number of packets sent per day determine how much of the available "airtime" we use.
 +
TheThingsNetwork states a fair-use-policy of 30 seconds per day total uplink time.
 +
 
Consider the following:
 
Consider the following:
 
* radio regulations generally have a 1% duty cycle requirement for the two bands used by the 8 LoRaWAN (EU) frequencies, so according to the legal limits, there is about 86400x0.01x2 = 1728 seconds per day send time at the best case.
 
* radio regulations generally have a 1% duty cycle requirement for the two bands used by the 8 LoRaWAN (EU) frequencies, so according to the legal limits, there is about 86400x0.01x2 = 1728 seconds per day send time at the best case.
* TheThingsNetwork has a [https://www.thethingsnetwork.org/forum/t/limitations-data-rate-packet-size-30-seconds-uplink-and-10-messages-downlink-per-day-fair-access-policy-guidelines/1300 FUP of 30s of data upload per day], actually a huge restriction compared to the send time allowed purely by radio regulations.
+
* TheThingsNetwork has a [https://www.thethingsnetwork.org/forum/t/limitations-data-rate-packet-size-30-seconds-uplink-and-10-messages-downlink-per-day-fair-access-policy-guidelines/1300 FUP of 30s of data upload per day]. This is actually a *lot* more strict than allowed purely by radio regulations.
* The Luftdaten backend appears to run on a 5 minute interval, or 288 measurements per day.
+
* The sensor.community backend runs on a 5 minute interval, or 288 measurements per day.
* The default Luftdaten firmware sends new data every 145s by default
+
* The default sensor.community WiFi firmware sends new data every 145s by default
  
 
With the TTN FUP of 30s upload per day, we can spend 30s / 288 = 104 ms on each transmission.
 
With the TTN FUP of 30s upload per day, we can spend 30s / 288 = 104 ms on each transmission.
Line 161: Line 203:
  
 
With a smaller payload, can we use higher spreading factors? :
 
With a smaller payload, can we use higher spreading factors? :
* skip T/RH/P completely (8 bytes left): SF7 is possible, at SF8 we spend 102 ms per transmission
+
* smallest possible binary payload is 4 bytes (PM10 and PM2.5, no temperature, no humidity): SF7 takes 51.5 ms, SF8 takes 92.7 ms, SF9 takes 164.9 ms
 +
* smallest Cayenne payload is 8 bytes (PM10 and PM2.5, no temperature, no humidity): SF7 takes 56.6 ms, SF8 takes 102.9 ms, SF9 takes 185.3 ms
 
* skip P only (15 bytes left): SF7 is possible, at SF8 we spend 123 ms per transmission
 
* skip P only (15 bytes left): SF7 is possible, at SF8 we spend 123 ms per transmission
  
So, in conclusion: <b>With the FUP of TTN and use of Cayenne encoding, you can just barely send enough data to transport Luftdaten PM data!</b>
+
So, in conclusion: <b>With the FUP of TTN and use of Cayenne encoding, you can just barely send enough data to transport PM data!</b>
 +
Also note that the payload size does not actually differ *that* much, this is because of the LoRaWAN overhead of 13 bytes minimum and pre-amble symbols which are sent anyway.
  
 
=== Node design ===
 
=== Node design ===
Line 170: Line 214:
  
 
The luftdaten backend has a 5 minute "heartbeat", so at least one measurement per 5 minutes should be sent to avoid disappearing from the map.
 
The luftdaten backend has a 5 minute "heartbeat", so at least one measurement per 5 minutes should be sent to avoid disappearing from the map.
The node firmware (attempts to) send a message every 145 seconds, just like the luftdaten WiFi sensor.
+
The node firmware (attempts to) send a message every 4m30s seconds, just like the luftdaten WiFi sensor.
 +
A little bit of randomness (30 s) is added to avoid nodes transmitting at exactly the same time over long periods of time and interfering with each other.
  
 
Measurements run in a cycle running through the following states:
 
Measurements run in a cycle running through the following states:
Line 192: Line 237:
 
The node needs to be registered at TheThingsNetwork, in order for its messages to be accepted by the TTN.
 
The node needs to be registered at TheThingsNetwork, in order for its messages to be accepted by the TTN.
  
 +
To keep things simple, there is no key provisioning for the nodes themselves.
 +
All nodes use the same firmware with the same APP EUI and the same APP KEY.
 +
The thing that makes each node unique is its DEVEUI, this is derived from the built-in unique ESP32 id.
 +
 +
On the TTN side, each node will have to be registered by its DEV EUI.
 
The following scheme is used to make TTN provisioning as simple as possible:
 
The following scheme is used to make TTN provisioning as simple as possible:
 
* Each node can be programmed with the *same* software, no source code modification is required
 
* Each node can be programmed with the *same* software, no source code modification is required
Line 198: Line 248:
 
** The App EUI has a fixed value and is the same for all nodes
 
** The App EUI has a fixed value and is the same for all nodes
 
** The App Key has a fixed value and is the same for all nodes
 
** The App Key has a fixed value and is the same for all nodes
** Use 32-bit frame counter, disable frame counter checks
+
** Use 32-bit frame counter, leave frame counter checks enabled
* The node does over-the-air-activation (OTAA) only once and then stores the OTAA parameters in internal (simulated) EEPROM. Upon reboot, the node resumes the connection with these parameters
 
** A long press on the PRG button restarts the OTAA procedure
 
  
 
=== Backend ===
 
=== Backend ===
Line 206: Line 254:
  
 
It currently supports the following:
 
It currently supports the following:
* subscribe to a TTN MQTT stream and receive incoming message
+
* subscribe to a TTN MQTT stream and receive incoming messages
 
* decode Cayenne and custom payloads
 
* decode Cayenne and custom payloads
 
* forward to luftdaten.info/sensor.community
 
* forward to luftdaten.info/sensor.community
Line 212: Line 260:
 
* forward to feinstaub app (experimental), https://pm.mrgames-server.de/
 
* forward to feinstaub app (experimental), https://pm.mrgames-server.de/
  
Example, to receive data using mosquitto, separately from the backend:
+
== Development ==
 +
To receive data using mosquitto, separately from the backend:
 
   mosquitto_sub -h eu.thethings.network -p 1883 -t +/devices/+/up -u particulatematter -P ttn-account-v2.cNaB2zO-nRiXaCUYmSAugzm-BaG_ZSHbEc5KgHNQFsk
 
   mosquitto_sub -h eu.thethings.network -p 1883 -t +/devices/+/up -u particulatematter -P ttn-account-v2.cNaB2zO-nRiXaCUYmSAugzm-BaG_ZSHbEc5KgHNQFsk
  
Line 223: Line 272:
 
Gateway API:
 
Gateway API:
 
   https://account.thethingsnetwork.org/api/v2/gateways/eui-xxxxxxxxxxx
 
   https://account.thethingsnetwork.org/api/v2/gateways/eui-xxxxxxxxxxx
 +
 +
Useful tools:
 +
* packet decoder: https://lorawan-packet-decoder-0ta6puiniaut.runkit.sh/
 +
* airtime calculator: ...

Revision as of 17:13, 3 December 2020

Project LoRaWAN dust Sensor
LoraWanDustSensor.jpg
LoRaWAN airborne particulate matter sensor
Status In progress
Contact bertrik
Last Update 2020-12-03

The concept

The concept consists of:

  • a sensor that measures airborne particulate matter and sends the measurement data using LoRa to TheThingsNetwork.
  • a forwarder application that collects the data from TTN and forwards it to luftdaten.info, opensensemap, mycayenne dashboard, etc.

This has been done before by other people, but it appears there is no universal solution. I am publishing all source code on github and will put up documentation on this wiki. This concept uses Cayenne because it is the closest practical thing towards a universal format but still reasonably compact format.

A similar thing has been done by:

One thing in particular that my concept does better than existing solutions is to use proper OTAA for the LoRa connection to TTN. OTAA means over-the-air-activation and is a mechanism to dynamically negotiate encryption keys and communication settings.

The OTAA key is hardcoded into the node, the session keys are not. The node identifies itself by its built-in unique ESP32 serial number.

This makes it possible to have a *single* firmware image for all sensor nodes and it simplifies the setup:

  • each node can be flashed with the same firmware
  • the node shows its unique Device EUI on the OLED
  • at the TTN console, you register the node with the unique Device EUI
  • the sensor node receives encryption keys over the air automatically by OTAA
  • done!

(idea: an ESP32 has a wifi connection too, perhaps registering the node can be done fully automatically, over wifi/internet)

Next steps

  • Implement the ESP-Now lamp/display protocol, for easy visualisation, see https://revspace.nl/StofAnanas#Next_generation
  • Update TTN upload schedule, set interval to luftdaten interval of 5 minutes, add some upload time randomness and avoid synchronised collisions between nodes
  • Disable screen when idle, to avoid burn-in of the OLED. Press the PRG button to enable it again.
  • Internal web server, for configuration, information?
  • Firmware update over WiFi:
    • basic OTA-over-WiFi has been implemented, allows remote flashing from the development environment, not so user-friendly, not secure
    • basic OTA-over-WiFi with a timeout, not so user-friendly, not very secure
    • next step is to use the ESP32 as access point + web server, user-friendly (only a browser needed to upload firmware images), not very secure
    • final step is to use the ESP32 as access point + web server, using signed images, this is user-friendly and secure

Links

Useful links for the TTGO LoRa board:

Hardware

The node is based on the Arduino framework:

  • for the processor board, either the TTGO ESP32 board ("ttgo-lora32-v1") and the Heltec LoRa32 V2 can be used.
  • the particulate matter sensor is the SDS-011, just like in the luftdaten project.
  • the humidity/temperature sensor is the BME280 (superior to the DHT11/22).

TODO: plaatjes van de hardware

Pinout

TTGO LoRa v1 Heltec LoRa v2 Sensor Remark
5V 5V SDS011 5V (pin 3) triple-check this, swapping 5V/GND destroys the SDS011
GND GND SDS011 GND (pin 5) triple-check this, swapping 5V/GND destroys the SDS011
GPIO23 GPIO23 SDS011 RXD (pin 6)
GPIO22 GPIO22 SDS011 TXD (pin 7)
3.3V 3.3V/Vext BME280 3V Both Vext and 3.3V can be used
GND GND BME280 GND ground
GPIO15 GPIO15 BME280 SCL
GPIO4 GPIO4 BME280 SDA

For reference:

Software

Compile and upload the firmware

Source code is hosted on github:

  • Arduino node, written in C/Arduino, built using platformio. This firmware joins TTN by OTAA and sends the measurement data using Cayenne.
  • TTN-to-luftdaten forwarder, written in Java, built using gradle. This picks up the Cayenne encoded data and forwards it to the Luftdaten API.

On Linux, with platformio (the command line tool), instructions for Debian Linux are:

  • install platformio:
 sudo apt install python3-pip
 sudo pip3 install platformio
 pio update
  • get the source from github:
 git clone https://github.com/bertrik/LoraWanPmSensor
  • enter the correct directory:
 cd LoraWanPmSensor
 cd Esp32PmSensor
  • compile and upload (for TTGO LoRa32 v1 board):
 pio run -e ttgov1 -t upload

or (for Heltec LoRa32 v2 board):

 pio run -e heltecv2 -t upload

On Windows, with the Arduino IDE

  • Install the Arduino IDE ...
  • Get the ESP32 support package ...
  • Install the following libraries
    • squix78/ESP8266_SSD1306
    • mcci-catena/MCCI LoRaWAN LMIC library, version 3.2.0
    • sparkfun/SparkFun BME280
  • Set the target to either the TTGO LoRa32 v1 or Heltec LoRa32 v2
  • Ctrl-U to compile and upload

Payload encoding

Cayenne

It's reasonably compact, it's a standard format, you can get a preview of the data in the TTN console. Interacts nicely with other platforms.

Specification for Cayenne LPP 2.0

Over-the-air payload encoding:

  • PM1.0: digital input (type 2), channel id 0, with value in units of 0.01 ug/m3, saturated to 327.67 ug/m3 (optional)
  • PM10: digital input (type 2), channel id 1, with value in units of 0.01 ug/m3, saturated to 327.67 ug/m3
  • PM2.5: digital input (type 2), channel id 2, with value in units of 0.01 ug/m3, saturated to 327.67 ug/m3
  • Temperature: temperature (type 103), with value in units of 0.1 degrees celcius
  • Humidity: humidity (type 104), with value in units of 0.5 %
  • Pressure: barometer (type 115), with value in units of 0.1 mbar, or 10 Pa (optional)

Dust values higher than 327.67 are encoded as 327.67, this is the maximum that can be represented as analog value in Cayenne. A nice thing about Cayenne is that you can simply leave items out if you don't support them, which results in a shorter yet still valid message.

Example:

 0x01 0x01 0xXX 0xXX  0x02 0x01 0xYY 0xYY  0x03 0x67 0xTT 0xTT  0x04 0x68 0xHH
 <== PM10 value ===>  <== PM2.5 value ==>  <== temperature ==>  <= humidity =>

Total payload size is 15 bytes. The LoRaWAN header adds 13 bytes (at least).

Particulate matter values encoded are averaged over the measurement interval.

Update: discovered that the Cayenne standard *does* actually support a "particle concentration" id, this is 125, derived from IPSO id 3325 http://www.openmobilealliance.org/tech/profiles/lwm2m/3325.xml . No idea yet though how to specify the type of particle measurement, if there's any convention for PM10, PM2.5, etc. The resolution is only 1 ppm, with typical sensors delivering 0.1 ppm resolution.

Binary

How other projects encode the data:

A smaller payload means less time in the air, smaller chance of collision with other LoRaWAN packets and more packets per hour. However, there is always an overhead from the LoRaWAN package (minimum 13 bytes), so using the smallest encoding (5 bytes) compared to the largest (16 bytes), reduces the on-air-time by only 23%.

LoraWan time budget

The number of bytes per telemetry packet and the number of packets sent per day determine how much of the available "airtime" we use. TheThingsNetwork states a fair-use-policy of 30 seconds per day total uplink time.

Consider the following:

  • radio regulations generally have a 1% duty cycle requirement for the two bands used by the 8 LoRaWAN (EU) frequencies, so according to the legal limits, there is about 86400x0.01x2 = 1728 seconds per day send time at the best case.
  • TheThingsNetwork has a FUP of 30s of data upload per day. This is actually a *lot* more strict than allowed purely by radio regulations.
  • The sensor.community backend runs on a 5 minute interval, or 288 measurements per day.
  • The default sensor.community WiFi firmware sends new data every 145s by default

With the TTN FUP of 30s upload per day, we can spend 30s / 288 = 104 ms on each transmission.

Using the LoRaWAN airtime calculator we can determine which modes can be used. At 15 bytes payload, this is only possible at SF7, the highest LoRa speed. Stretching the TTN guideline a bit, say by a factor 2, we can achieve those transfers still only at SF7 and SF8. So you need to be relatively close to a gateway.

With a smaller payload, can we use higher spreading factors? :

  • smallest possible binary payload is 4 bytes (PM10 and PM2.5, no temperature, no humidity): SF7 takes 51.5 ms, SF8 takes 92.7 ms, SF9 takes 164.9 ms
  • smallest Cayenne payload is 8 bytes (PM10 and PM2.5, no temperature, no humidity): SF7 takes 56.6 ms, SF8 takes 102.9 ms, SF9 takes 185.3 ms
  • skip P only (15 bytes left): SF7 is possible, at SF8 we spend 123 ms per transmission

So, in conclusion: With the FUP of TTN and use of Cayenne encoding, you can just barely send enough data to transport PM data! Also note that the payload size does not actually differ *that* much, this is because of the LoRaWAN overhead of 13 bytes minimum and pre-amble symbols which are sent anyway.

Node design

Source code for the particulate matter measurement node can be found on the github page.

The luftdaten backend has a 5 minute "heartbeat", so at least one measurement per 5 minutes should be sent to avoid disappearing from the map. The node firmware (attempts to) send a message every 4m30s seconds, just like the luftdaten WiFi sensor. A little bit of randomness (30 s) is added to avoid nodes transmitting at exactly the same time over long periods of time and interfering with each other.

Measurements run in a cycle running through the following states:

  • INIT: determine presence of the SDS011, print the SDS011 serial number
  • IDLE: wait until the start of the cycle, then turn on the fan
  • WARMUP: wait 20 seconds while the sensor "warms up"
  • MEASURE: measure 10 seconds, then turn off fan, calculate median/average and send LoRaWAN message

Building with platformio

Platformio is used to compile and upload the code to the node.

To install platformio (example for Debian):

 sudo apt install python3-pip
 sudo pip3 install platformio
 pio update

To compile and upload:

 pio run -t upload

TTN key provisioning

The node needs to be registered at TheThingsNetwork, in order for its messages to be accepted by the TTN.

To keep things simple, there is no key provisioning for the nodes themselves. All nodes use the same firmware with the same APP EUI and the same APP KEY. The thing that makes each node unique is its DEVEUI, this is derived from the built-in unique ESP32 id.

On the TTN side, each node will have to be registered by its DEV EUI. The following scheme is used to make TTN provisioning as simple as possible:

  • Each node can be programmed with the *same* software, no source code modification is required
  • The node administrator needs to enter the following properties at the TTN console, for each node:
    • The Device EUI is derived from the node-specific ESP32 MAC address, the node shows this on its OLED
    • The App EUI has a fixed value and is the same for all nodes
    • The App Key has a fixed value and is the same for all nodes
    • Use 32-bit frame counter, leave frame counter checks enabled

Backend

This is implemented by my LoraLuftdatenForwarder.

It currently supports the following:

  • subscribe to a TTN MQTT stream and receive incoming messages
  • decode Cayenne and custom payloads
  • forward to luftdaten.info/sensor.community
  • forward to opensensemap.org
  • forward to feinstaub app (experimental), https://pm.mrgames-server.de/

Development

To receive data using mosquitto, separately from the backend:

 mosquitto_sub -h eu.thethings.network -p 1883 -t +/devices/+/up -u particulatematter -P ttn-account-v2.cNaB2zO-nRiXaCUYmSAugzm-BaG_ZSHbEc5KgHNQFsk

Example upstream data:

 particulatematter/devices/ttgo_mac/up {"app_id":"particulatematter","dev_id":"ttgo_mac","hardware_serial":"000084B14CA4AE30","port":1,"counter":16,"payload_raw":"AAEALAAd/////w==","metadata":{"time":"2019-04-13T08:37:45.338427686Z","frequency":868.3,"modulation":"LORA","data_rate":"SF11BW125","airtime":823296000,"coding_rate":"4/5","gateways":[{"gtw_id":"eui-008000000000b8b6","timestamp":2000599916,"time":"2019-04-13T08:37:45.320735Z","channel":1,"rssi":-115,"snr":-3,"rf_chain":1,"latitude":52.0182,"longitude":4.70844,"altitude":27}]}}

Example downstream data:

 particulatematter/devices/ttgo_mac/events/down/sent {"payload":"YPUvASalGgEDEf8AAcqtmOw=","message":{"app_id":"particulatematter","dev_id":"ttgo_mac","port":0},"gateway_id":"eui-008000000000b8b6","config":{"modulation":"LORA","data_rate":"SF9BW125","airtime":164864000,"counter":282,"frequency":869525000,"power":27}}

Gateway API:

 https://account.thethingsnetwork.org/api/v2/gateways/eui-xxxxxxxxxxx

Useful tools: