You wanted to monitor your city environmental data - like PM2.5, VOC, temp, humidity and air pressure... this could be difficult, specially when you are new to sensors and wiring all together. Oh yes, and you wanted to use the fastest growing low power WAN in your city The Things Netwok (TTN) ... an other complex beast to tame!
So this project started... just 30 min ago, plug in the famous #IoT Octopus (has BME680 pre installed) together with the brand new Grove PM 2.5 laser sensor, which is using the latest I2C connection and not old fashioned serial stuff.
15 min - coding, and connecting things... upload code to the #IoT Octopus!
5 min - configure your TTN device and application... www.thethingsnetwork.org
(don't forget to copy the payload decoder!)
10 min - doing the documentation (this page)
Done! Any have fun exploring your city... in a different way.
The PM2.5, BME680, LoRaWAN (TTN) code
Arduino#include <lmic.h>
#include <hal/hal.h>
#include <Adafruit_BME680.h>
#include <Wire.h>
// Grove HM330X - Particulate Matters Sensor (PM2.5)
#include "Seeed_HM330X.h"
HM330X sensor;
u8 buf[30];
u16 value25=0;
const char *str[]={"sensor num: ","PM1.0 concentration(CF=1,Standard particulate matter,unit:ug/m3): ",
"PM2.5 concentration(CF=1,Standard particulate matter,unit:ug/m3): ",
"PM10 concentration(CF=1,Standard particulate matter,unit:ug/m3): ",
"PM1.0 concentration(Atmospheric environment,unit:ug/m3): ",
"PM2.5 concentration(Atmospheric environment,unit:ug/m3): ",
"PM10 concentration(Atmospheric environment,unit:ug/m3): ",
};
err_t print_result(const char* str,u16 value)
{
if(NULL==str)
return ERROR_PARAM;
Serial.print(str);
Serial.println(value);
return NO_ERROR;
}
/*parse buf with 29 u8-data*/
err_t parse_result(u8 *data)
{
u16 value=0;
err_t NO_ERROR;
if(NULL==data)
return ERROR_PARAM;
for(int i=1;i<8;i++)
{
value = (u16)data[i*2]<<8|data[i*2+1];
print_result(str[i-1],value);
}
int i = 6; // PM2.5 concentration(Atmospheric environment,unit:ug/m3)
value25 = value = (u16)data[i*2]<<8|data[i*2+1];
Serial.println(value25);
}
err_t parse_result_value(u8 *data)
{
if(NULL==data)
return ERROR_PARAM;
for(int i=0;i<28;i++)
{
Serial.print(data[i],HEX);
Serial.print(" ");
if((0==(i)%5)||(0==i))
{
Serial.println(" ");
}
}
u8 sum=0;
for(int i=0;i<28;i++)
{
sum+=data[i];
}
if(sum!=data[28])
{
Serial.println("wrong checkSum!!!!");
}
Serial.println(" ");
Serial.println(" ");
return NO_ERROR;
}
// LoraWAN Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
// https://github.com/matthijskooijman/arduino-lmic
// -------- LoRa PinMapping FeatherWing Octopus
const lmic_pinmap lmic_pins = {
.nss = 2, // Connected to pin D
.rxtx = LMIC_UNUSED_PIN, // For placeholder only, Do not connected on RFM92/RFM95
.rst = LMIC_UNUSED_PIN, // Needed on RFM92/RFM95? (probably not) D0/GPIO16
.dio = {
15, 15, LMIC_UNUSED_PIN }
};
static const u1_t PROGMEM DEVEUI[8]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // <------- change here
void os_getDevEui (u1_t* buf) {
memcpy_P(buf, DEVEUI, 8);
}
static const u1_t PROGMEM APPEUI[8]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // <------- change here
void os_getArtEui (u1_t* buf) {
memcpy_P(buf, APPEUI, 8);
}
static const u1_t PROGMEM APPKEY[16]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // <------- change here
void os_getDevKey (u1_t* buf) {
memcpy_P(buf, APPKEY, 16);
};
volatile int LoRaWAN_Tx_Ready = 0; // Merker für ACK
int LoRaWAN_Rx_Payload = 0 ;
// -------- LoRa Event
void onEvent (ev_t ev) {
Serial.print(os_getTime());
Serial.print(": ");
switch(ev) {
case EV_SCAN_TIMEOUT:
Serial.println(F("EV_SCAN_TIMEOUT"));
break;
case EV_BEACON_FOUND:
Serial.println(F("EV_BEACON_FOUND"));
break;
case EV_BEACON_MISSED:
Serial.println(F("EV_BEACON_MISSED"));
break;
case EV_BEACON_TRACKED:
Serial.println(F("EV_BEACON_TRACKED"));
break;
case EV_JOINING:
Serial.println(F("EV_JOINING"));
break;
case EV_JOINED:
Serial.println(F("EV_JOINED"));
// Disable link check validation (automatically enabled
// during join, but not supported by TTN at this time).
LMIC_setLinkCheckMode(0);
break;
case EV_RFU1:
Serial.println(F("EV_RFU1"));
break;
case EV_JOIN_FAILED:
Serial.println(F("EV_JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
Serial.println(F("EV_REJOIN_FAILED"));
break;
break;
case EV_TXCOMPLETE:
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
if (LMIC.txrxFlags & TXRX_ACK)
Serial.println(F("Received ack"));
if (LMIC.dataLen) {
Serial.println(F("Received "));
Serial.println(LMIC.dataLen);
Serial.println(F(" bytes of payload"));
LoRaWAN_Rx_Payload = 0;
for (int i = 0;i<LMIC.dataLen;i++) {
Serial.println(LMIC.frame[i+ LMIC.dataBeg],HEX);
LoRaWAN_Rx_Payload = 256*LoRaWAN_Rx_Payload+LMIC.frame[i+ LMIC.dataBeg];
}
}
LoRaWAN_Tx_Ready = 1;
// Schedule next transmission
//os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
break;
case EV_LOST_TSYNC:
Serial.println(F("EV_LOST_TSYNC"));
break;
case EV_RESET:
Serial.println(F("EV_RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
Serial.println(F("EV_RXCOMPLETE"));
break;
case EV_LINK_DEAD:
Serial.println(F("EV_LINK_DEAD"));
break;
case EV_LINK_ALIVE:
Serial.println(F("EV_LINK_ALIVE"));
break;
default:
Serial.println(F("Unknown event"));
break;
}
}
// BME680 Lib written by Limor Fried & Kevin Townsend for Adafruit Industries, http://www.adafruit.com/products/3660
Adafruit_BME680 boschBME680; // Objekt Bosch Umweltsensor
void setup(){ // Einmalige Initialisierung
Serial.begin(115200);
// -- Initialisiere LoraWAN
os_init(); // LMIC LoraWAN
LMIC_reset(); // Reset the MAC state
LMIC.txpow = 27; // Maximum TX power
LMIC.datarate=DR_SF12; // Long Range
LMIC.rps = updr2rps(LMIC.datarate);
Wire.begin(); // ---- Initialisiere den I2C-Bus
if (Wire.status() != I2C_OK) Serial.println("Something wrong with I2C");
if (!boschBME680.begin(118)) {
Serial.println("Failed to communicate BME680");
while (1) {
delay(1);
};
}
// Set up Bosch BME 680
boschBME680.setTemperatureOversampling(BME680_OS_8X);
boschBME680.setHumidityOversampling(BME680_OS_2X);
boschBME680.setPressureOversampling(BME680_OS_4X);
boschBME680.setIIRFilterSize(BME680_FILTER_SIZE_3);
boschBME680.setGasHeater(320, 150); // 320*C for 150 ms
// Init HM330X on I2C
if(sensor.init())
{
Serial.println("HM330X init failed!!!");
while(1);
}
}
void loop() { // Kontinuierliche Wiederholung
if(sensor.read_sensor_value(buf,29))
{
Serial.println("HM330X read result failed!!!");
}
//parse_result_value(buf);
parse_result(buf);
{ //Block------------------------------ sende Daten an TTN
int port = 10;
static uint8_t mydata[10];
int wert=round(boschBME680.readTemperature()*10);
mydata[0] = wert >> 8;
mydata[1] = wert & 0xFF;
wert=round(boschBME680.readHumidity()*10);
mydata[2] = wert >> 8;
mydata[3] = wert & 0xFF;
wert=round(boschBME680.readPressure()/100.*10);
mydata[4] = wert >> 8;
mydata[5] = wert & 0xFF;
wert=round(boschBME680.readGas()/1000.*10);
mydata[6] = wert >> 8;
mydata[7] = wert & 0xFF;
wert=round(value25*10);
mydata[8] = wert >> 8;
mydata[9] = wert & 0xFF;
// Check if there is not a current TX/RX job running
//if (LMIC.opmode & OP_TXRXPEND) {
if (LMIC.opmode & (1 << 7)) {
Serial.println(F("OP_TXRXPEND, not sending"));
}
else {
// Prepare upstream data transmission at the next possible time.
LoRaWAN_Tx_Ready = 0; // Merker für ACK
LMIC_setTxData2(port, mydata, sizeof(mydata), 0); // Sende
Serial.println(F("Packet queued"));
while(LoRaWAN_Tx_Ready==0) {
yield();
os_runloop_once();
}; // Warte bis gesendet
}
} // Blockende
delay( 30000 );
}
function Decoder(bytes, port) { // Decode an uplink message from a buffer (array) of bytes to an object of fields.
var decoded = {}; //
decoded.port = port;
decoded.humidity = 0; decoded.temp = 0; decoded.gas=0;
if (port === 10) { // Port selection
decoded.temp = (parseFloat((bytes[0] << 8) | bytes[1]).toFixed(2))/10;
decoded.humidity = (parseFloat((bytes[2] << 8) | bytes[3]).toFixed(2))/10;
decoded.pressure = (parseFloat((bytes[4] << 8) | bytes[5]).toFixed(2))/10;
decoded.gas = (parseFloat((bytes[6] << 8) | bytes[7]).toFixed(2))/10;
decoded.pm25 = (parseFloat((bytes[8] << 8) | bytes[9]).toFixed(2))/10;
}
return decoded;
}
Comments
Please log in or sign up to comment.