Implement Multisensor Capabilities #4

Merged
seiichiro merged 3 commits from multisensor into master 2021-03-18 16:58:24 +00:00
13 changed files with 201 additions and 97 deletions
Showing only changes of commit c970e4a8ee - Show all commits

44
include/attsensor.h Normal file
View file

@ -0,0 +1,44 @@
/*
attsensor.h - Define the Base Sensor Interface Class
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ATTSENSOR_H
#define ATTSENSOR_H
#include <inttypes.h>
class AttSensor {
public:
// Put the Data into the Payload Array starting at <startbyte>
// Return the next startbyte when done
virtual uint8_t getSensorData(char *payload, uint8_t startbyte) = 0;
// Called in Setup, Do any Necessary Initialization
virtual void initialize(void) = 0;
// Return the number of Bytes added to the Payload
virtual uint8_t numBytes(void) = 0;
};
#endif

View file

@ -1,5 +1,4 @@
#include <Arduino.h> #include <Arduino.h>
#include <stdint.h>
#include <Wire.h> #include <Wire.h>
#include "BME280.h" #include "BME280.h"
@ -78,10 +77,10 @@ int32_t BME280::compensate_h(int32_t adc_H)
return (uint32_t)((v_x1_u32r>>12)/10); return (uint32_t)((v_x1_u32r>>12)/10);
} }
void BME280::getSensorData(lora_data &loradata) { uint8_t BME280::getSensorData(char *payload, uint8_t startbyte) {
int32_t UP, UT, UH; int32_t UP, UT, UH;
int32_t rawP, rawT; int32_t rawP, rawT, value;
// Trigger Measurement // Trigger Measurement
// Set Sensor Config // Set Sensor Config
@ -105,10 +104,28 @@ void BME280::getSensorData(lora_data &loradata) {
// Read Humidity // Read Humidity
UH = read16(0xFD); UH = read16(0xFD);
// Compensate Values and Return
loradata.temperature = compensate_t(UT); // Temperature
loradata.pressure = compensate_p(UP); value = compensate_t(UT);
loradata.humidity = compensate_h(UH); payload[startbyte] = (value) & 0XFF;
payload[startbyte+1] = (value >> 8) & 0XFF;
payload[startbyte+2] = (value >> 16) & 0XFF;
payload[startbyte+3] = (value >> 24) & 0XFF;
// Humidity
value = compensate_h(UH);
payload[startbyte+4] = (value) & 0XFF;
payload[startbyte+5] = (value >> 8) & 0XFF;
payload[startbyte+6] = (value >> 16) & 0XFF;
payload[startbyte+7] = (value >> 24) & 0XFF;
// Pressure
value = compensate_p(UP);
payload[startbyte+8] = (value) & 0XFF;
payload[startbyte+9] = (value >> 8) & 0XFF;
payload[startbyte+10] = (value >> 16) & 0XFF;
payload[startbyte+11] = (value >> 24) & 0XFF;
return startbyte+12;
} }
uint8_t BME280::read8(uint8_t addr) { uint8_t BME280::read8(uint8_t addr) {

View file

@ -1,19 +1,11 @@
#ifndef BME280_H #ifndef BME280_H
#define BME280_H #define BME280_H
#include <stdint.h> #include "../../include/attsensor.h"
#define BME280_I2CADDR 0x76 #define BME280_I2CADDR 0x76
struct lora_data { class BME280 : public AttSensor {
uint8_t bat;
int32_t temperature;
int32_t humidity;
int32_t pressure;
} __attribute__ ((packed));
class BME280
{
private: private:
// Variables for Calibration Values // Variables for Calibration Values
uint8_t dig_H1, dig_H3; uint8_t dig_H1, dig_H3;
@ -36,13 +28,12 @@ private:
int16_t readS16(uint8_t addr); int16_t readS16(uint8_t addr);
int16_t readS16_LE(uint8_t addr); int16_t readS16_LE(uint8_t addr);
void write8(uint8_t addr, uint8_t data); void write8(uint8_t addr, uint8_t data);
void getCalData(void);
public: public:
BME280(void); BME280(void);
uint8_t getSensorData(char *payload, uint8_t startbyte);
// Get Calibration Data from Sensor void initialize(void) {getCalData();};
void getCalData(void); uint8_t numBytes(void) {return 12;};
// Read Pressure From Sensor
void getSensorData(lora_data &loradata);
}; };
#endif #endif

View file

@ -41,19 +41,23 @@ void MHZ19C::initialize(void) {
#endif #endif
} }
void MHZ19C::getSensorData(lora_data &loradata) { uint8_t MHZ19C::getSensorData(char * payload, uint8_t startbyte) {
write(MHZ19C_CMD_GET_PPM, 0x00); write(MHZ19C_CMD_GET_PPM, 0x00);
delay(50); delay(50);
uint8_t readBytes = read(); uint8_t readBytes = read();
loradata.ppm = 0; payload[startbyte] = 0x00;
payload[startbyte+1] = 0x00;
if (readBytes > 0) { if (readBytes > 0) {
switch(buffer[1]) { switch(buffer[1]) {
case 0x86: case 0x86:
loradata.ppm = (buffer[2]*256) + buffer[3]; int16_t value = (buffer[2]*256) + buffer[3];
payload[startbyte] = (value) & 0xFF;
payload[startbyte+1] = (value >> 8) & 0xFF;
break; break;
} }
} }
return startbyte+2;
} }
// Turn Self Calibration Routine On or Off // Turn Self Calibration Routine On or Off

View file

@ -27,11 +27,7 @@
#ifndef MHZ19C_H #ifndef MHZ19C_H
#define MHZ19C_H #define MHZ19C_H
// Data Structure for the LoRa Packet #include "../../include/attsensor.h"
struct lora_data {
uint8_t bat;
int16_t ppm;
} __attribute__ ((packed));
#define MHZ19C_READ_TIMEOUT 500 // Timeout for Serial Communication #define MHZ19C_READ_TIMEOUT 500 // Timeout for Serial Communication
#define MHZ19C_SER_BUF_LEN 9 // Length of the Internal Serial Message Buffer #define MHZ19C_SER_BUF_LEN 9 // Length of the Internal Serial Message Buffer
@ -39,7 +35,7 @@ struct lora_data {
#define MHZ19C_CMD_SET_AUTOCAL 0x79 // Turn Self Calibration on/off #define MHZ19C_CMD_SET_AUTOCAL 0x79 // Turn Self Calibration on/off
#define MHZ19C_CMD_GET_PPM 0x86 // Get Current PPM Reading #define MHZ19C_CMD_GET_PPM 0x86 // Get Current PPM Reading
class MHZ19C { class MHZ19C : public AttSensor {
private: private:
uint8_t buffer[MHZ19C_SER_BUF_LEN]; uint8_t buffer[MHZ19C_SER_BUF_LEN];
@ -51,8 +47,9 @@ class MHZ19C {
public: public:
MHZ19C(void); MHZ19C(void);
void MHZ19C::initialize(void); void initialize(void);
void getSensorData(lora_data &loradata); uint8_t numBytes(void) {return 2;};
uint8_t getSensorData(char *payload, uint8_t startbyte);
void setSelfCalibration(bool state); void setSelfCalibration(bool state);
}; };

View file

@ -33,7 +33,7 @@ SENSAIRS8::SENSAIRS8(void) {
Serial.setTimeout(READ_TIMEOUT); Serial.setTimeout(READ_TIMEOUT);
} }
void SENSAIRS8::getSensorData(lora_data &loradata) { uint8_t SENSAIRS8::getSensorData(char *payload, uint8_t startbyte) {
uint8_t _cmd[7] = {0xFE, 0x44, 0x00, 0x08, 0x02, 0x9F, 0x25}; uint8_t _cmd[7] = {0xFE, 0x44, 0x00, 0x08, 0x02, 0x9F, 0x25};
while (Serial.available() > 0) Serial.read(); while (Serial.available() > 0) Serial.read();
Serial.write(_cmd, 7); Serial.write(_cmd, 7);
@ -41,9 +41,14 @@ void SENSAIRS8::getSensorData(lora_data &loradata) {
delay(1000); delay(1000);
uint8_t readBytes = read(); uint8_t readBytes = read();
payload[startbyte] = 0x00;
payload[startbyte+1] = 0x00;
if (readBytes > 0) { if (readBytes > 0) {
loradata.ppm = (buffer[3]*256) + buffer[4]; int16_t value = (buffer[3]*256) + buffer[4];
payload[startbyte] = (value) & 0xFF;
payload[startbyte+1] = (value >> 8) & 0xFF;
} }
return startbyte+2;
} }
// Read a Sensor Response // Read a Sensor Response

View file

@ -27,16 +27,12 @@
#ifndef SENSAIRS8_H #ifndef SENSAIRS8_H
#define SENSAIRS8_H #define SENSAIRS8_H
// Data Structure for the LoRa Packet #include "../../include/attsensor.h"
struct lora_data {
uint8_t bat;
int16_t ppm;
} __attribute__ ((packed));
#define READ_TIMEOUT 500 // Timeout for Serial Communication #define READ_TIMEOUT 500 // Timeout for Serial Communication
#define SER_BUF_LEN 7 // Length of the Internal Serial Message Buffer #define SER_BUF_LEN 7 // Length of the Internal Serial Message Buffer
class SENSAIRS8 { class SENSAIRS8 : public AttSensor {
private: private:
uint8_t buffer[SER_BUF_LEN]; uint8_t buffer[SER_BUF_LEN];
@ -46,7 +42,9 @@ class SENSAIRS8 {
public: public:
SENSAIRS8(void); SENSAIRS8(void);
void getSensorData(lora_data &loradata); uint8_t getSensorData(char *payload, uint8_t startbyte);
void initialize(void) {};
uint8_t numBytes(void) {return 2;};
}; };
#endif #endif

View file

@ -33,18 +33,24 @@ SG112A::SG112A(void) {
Serial.setTimeout(READ_TIMEOUT); Serial.setTimeout(READ_TIMEOUT);
} }
void SG112A::getSensorData(lora_data &loradata) {
uint8_t SG112A::getSensorData(char *payload, uint8_t startbyte) {
write(CMD_GET_PPM); write(CMD_GET_PPM);
delay(50); delay(50);
uint8_t readBytes = read(); uint8_t readBytes = read();
payload[startbyte] = 0x00;
payload[startbyte+1] = 0x00;
if (readBytes > 0) { if (readBytes > 0) {
switch(buffer[2]) { switch(buffer[2]) {
case 0x15: case 0x15:
loradata.ppm = (buffer[5]*256) + buffer[4]; uint16_t value = (buffer[5]*256) + buffer[4];
payload[startbyte] = (value) & 0xFF;
payload[startbyte+1] = (value >> 8) & 0xFF;
break; break;
} }
} }
return startbyte+2;
} }
// Write a Command to the Sensor // Write a Command to the Sensor

View file

@ -27,11 +27,7 @@
#ifndef SG112A_H #ifndef SG112A_H
#define SG112A_H #define SG112A_H
// Data Structure for the LoRa Packet #include "../../include/attsensor.h"
struct lora_data {
uint8_t bat;
int16_t ppm;
} __attribute__ ((packed));
#define READ_TIMEOUT 500 // Timeout for Serial Communication #define READ_TIMEOUT 500 // Timeout for Serial Communication
#define SER_BUF_LEN 16 // Length of the Internal Serial Message Buffer #define SER_BUF_LEN 16 // Length of the Internal Serial Message Buffer
@ -52,7 +48,9 @@ class SG112A {
public: public:
SG112A(void); SG112A(void);
void getSensorData(lora_data &loradata); uint8_t getSensorData(char *payload, uint8_t startbyte);
void initialize(void) {};
uint8_t numBytes(void) {return 2;};
}; };
#endif #endif

View file

@ -52,7 +52,20 @@ uint16_t SHT21::sensorRead(uint8_t command) {
return result; return result;
} }
void SHT21::getSensorData(lora_data &loradata) { uint8_t SHT21::getSensorData(char *payload, uint8_t startbyte) {
loradata.temperature = (int32_t)((-46.85 + 175.72 / 65536.0 * (float)(sensorRead(SHT21_TEMPHOLD)))*100); // Temperature
loradata.humidity = (int32_t)((-6.0 + 125.0 / 65536.0 * (float)(sensorRead(SHT21_HUMIHOLD)))*100); int32_t value = (int32_t)((-46.85 + 175.72 / 65536.0 * (float)(sensorRead(SHT21_TEMPHOLD)))*100);
payload[startbyte] = (value) & 0XFF;
payload[startbyte+1] = (value >> 8) & 0XFF;
payload[startbyte+2] = (value >> 16) & 0XFF;
payload[startbyte+3] = (value >> 24) & 0XFF;
// Humidity
value = (int32_t)((-6.0 + 125.0 / 65536.0 * (float)(sensorRead(SHT21_HUMIHOLD)))*100);
payload[startbyte+4] = (value) & 0XFF;
payload[startbyte+5] = (value >> 8) & 0XFF;
payload[startbyte+6] = (value >> 16) & 0XFF;
payload[startbyte+7] = (value >> 24) & 0XFF;
return startbyte+8;
} }

View file

@ -27,7 +27,7 @@
#ifndef SHT21_H #ifndef SHT21_H
#define SHT21_H #define SHT21_H
#include <inttypes.h> #include "../../include/attsensor.h"
#define SHT21_I2CADDR 0x40 #define SHT21_I2CADDR 0x40
@ -37,12 +37,6 @@
#define SHT21_HUMINOHOLD 0xF5 #define SHT21_HUMINOHOLD 0xF5
#define SHT21_SOFTRESET 0xFE #define SHT21_SOFTRESET 0xFE
struct lora_data {
uint8_t bat;
int32_t temperature;
int32_t humidity;
} __attribute__ ((packed));
class SHT21 class SHT21
{ {
private: private:
@ -50,7 +44,9 @@ class SHT21
public: public:
SHT21(void); SHT21(void);
void getSensorData(lora_data &loradata); uint8_t getSensorData(char *payload, uint8_t startbyte);
void initialize(void) {};
uint8_t numBytes(void) {return 8;};
}; };
#endif #endif

View file

@ -23,12 +23,19 @@
// Serial Debug Output is not available with this Sensor. // Serial Debug Output is not available with this Sensor.
// #define DEBUG // #define DEBUG
// Number of Active Sensors (used as long as HAS_NO_SENSOR is not enabled)
// Set to the correct number of enabled sensors below
// Not doing so will lead to unpredictable results or not work at all
#define NUM_SENSORS 1
// Define which Sensor is installed // Define which Sensor is installed
#define HAS_NO_SENSOR #define HAS_NO_SENSOR
// #define HAS_BME280 // #define HAS_BME280
// #define HAS_SHT21 // #define HAS_SHT21
// #define HAS_SG112A // #define HAS_SG112A
// #define HAS_MHZ19C // #define HAS_MHZ19C
// #define HAS_SENSAIRS8
// How many minutes to sleep between Measuring/Sending // How many minutes to sleep between Measuring/Sending

View file

@ -13,6 +13,23 @@
#include "config.h" #include "config.h"
#include "debug.h" #include "debug.h"
#include "attsensor.h"
#ifdef HAS_MHZ19C
#include <MHZ19C.h>
#endif
#ifdef HAS_SG112A
#include <SG112A.h>
#endif
#ifdef HAS_SENSAIRS8
#include <SENSAIRS8.h>
#endif
#ifdef HAS_BME280
#include <BME280.h>
#endif
#ifdef HAS_SHT21
#include <SHT21.h>
#endif
// define the blink function and BLINK_LED Macro depending // define the blink function and BLINK_LED Macro depending
// on the definition of LED_PIN // on the definition of LED_PIN
@ -44,30 +61,10 @@ void blink(uint8_t num) {
#define WS2812B_BLINK(led,r,g,b,ms) #define WS2812B_BLINK(led,r,g,b,ms)
#endif #endif
// Create the Sensor Objects #ifndef HAS_NO_SENSORS
#if defined HAS_NO_SENSOR AttSensor* sensors[NUM_SENSORS];
struct lora_data {
uint8_t bat;
} __attribute ((packed));
#elif defined HAS_MHZ19C
#include <MHZ19C.h>
MHZ19C sensor;
#elif defined HAS_SG112A
#include <SG112A.h>
SG112A sensor;
#elif defined HAS_SENSAIRS8
#include <SENSAIRS8.h>
SENSAIRS8 sensor;
#elif defined HAS_BME280
#include <BME280.h>
BME280 sensor;
#elif defined HAS_SHT21
#include <SHT21.h>
SHT21 sensor;
#elif defined HAS_SG112A
#include <SG112A.h>
SG112A sensor;
#endif #endif
int payloadBytes = 1;
// Define some LMIC Callbacks and Variables // Define some LMIC Callbacks and Variables
void os_getArtEui (u1_t* buf) { void os_getArtEui (u1_t* buf) {
@ -196,24 +193,28 @@ uint16_t readSupplyVoltage() { //returns value in millivolts to avoid floating p
void do_send(osjob_t* j) { void do_send(osjob_t* j) {
// Prepare LoRa Data Packet // Prepare LoRa Data Packet
// The struct is defined in the sensor class (or above for use without a sensor) // The struct is defined in the sensor class (or above for use without a sensor)
lora_data data; char payload[payloadBytes];
if (LMIC.opmode & OP_TXRXPEND) { if (LMIC.opmode & OP_TXRXPEND) {
delay(1); delay(1);
} else { } else {
uint8_t curByte = 0;
// Add Battery Voltage (0.2V Accuracy stored in 1 byte) // Add Battery Voltage (0.2V Accuracy stored in 1 byte)
uint32_t batv = readSupplyVoltage(); uint32_t batv = readSupplyVoltage();
data.bat = (uint8_t)(batv / 20); payload[curByte] = (uint8_t)(batv / 20);
if (batv % 20 > 9) if (batv % 20 > 9)
data.bat += 1; payload[curByte] += 1;
curByte++;
// Get Sensor Readings Into Data Paket // Get Sensor Readings Into Data Paket
#ifndef HAS_NO_SENSOR #ifndef HAS_NO_SENSOR
sensor.getSensorData(data); for (int i=0; i < NUM_SENSORS; i++)
curByte = sensors[i]->getSensorData(payload, curByte);
// Queue Packet for Sending // Queue Packet for Sending
DEBUG_PRINTLN("LoRa-Packet Queued"); DEBUG_PRINTLN("LoRa-Packet Queued");
LMIC_setTxData2(1, (unsigned char *)&data, sizeof(data), 0); LMIC_setTxData2(1, payload, sizeof(payload), 0);
#if defined WS2812B_PIN && (defined HAS_SG112A || defined HAS_MHZ19C) #if defined WS2812B_PIN && (defined HAS_SG112A || defined HAS_MHZ19C)
@ -261,19 +262,46 @@ void setup()
attachInterrupt(digitalPinToInterrupt(BTN_PIN), btn_press, FALLING); attachInterrupt(digitalPinToInterrupt(BTN_PIN), btn_press, FALLING);
#endif #endif
// Setup all Sensors
uint8_t i = 0;
#ifdef HAS_MHZ19C
sensors[i] = new MHZ19C();
payloadBytes += sensors[i]->numBytes();
i++;
#endif
#ifdef HAS_SG112A
sensors[i] = new SG112A();
payloadBytes += sensors[i]->numBytes();
i++;
#endif
#ifdef HAS_SENSAIRS8
sensors[i] = new SENSAIRS8();
payloadBytes += sensors[i]->numBytes();
i++;
#endif
#ifdef HAS_BME280
sensors[i] = new BME280();
payloadBytes += sensors[i]->numBytes();
i++;
#endif
#ifdef HAS_SHT21
sensors[i] = new SHT21();
payloadBytes += sensors[i]->numBytes();
i++;
#endif
// Initialize all Sensors
#ifndef HAS_NO_SENSOR
for (i = 0; i < NUM_SENSORS; i++)
sensors[i]->initialize();
#endif
// Set RTC // Set RTC
while (RTC.STATUS > 0) {} while (RTC.STATUS > 0) {}
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc; RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;
while (RTC.PITSTATUS > 0) {} while (RTC.PITSTATUS > 0) {}
// Initialize Sensor(s)
#ifdef HAS_BME280
sensor.getCalData();
#endif
#ifdef HAS_MHZ19C
sensor.initialize();
#endif
// Setup LMIC // Setup LMIC
DEBUG_PRINT("Initializing LMIC...") DEBUG_PRINT("Initializing LMIC...")
os_init(); os_init();