diff --git a/include/attsensor.h b/include/attsensor.h new file mode 100644 index 0000000..5be8118 --- /dev/null +++ b/include/attsensor.h @@ -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 + +class AttSensor { + public: + // Put the Data into the Payload Array starting at + // 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 \ No newline at end of file diff --git a/lib/BME280/BME280.cpp b/lib/BME280/BME280.cpp index 9204d20..846027d 100644 --- a/lib/BME280/BME280.cpp +++ b/lib/BME280/BME280.cpp @@ -1,5 +1,4 @@ #include -#include #include #include "BME280.h" @@ -78,10 +77,10 @@ int32_t BME280::compensate_h(int32_t adc_H) 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 rawP, rawT; + int32_t rawP, rawT, value; // Trigger Measurement // Set Sensor Config @@ -105,10 +104,28 @@ void BME280::getSensorData(lora_data &loradata) { // Read Humidity UH = read16(0xFD); - // Compensate Values and Return - loradata.temperature = compensate_t(UT); - loradata.pressure = compensate_p(UP); - loradata.humidity = compensate_h(UH); + + // Temperature + value = compensate_t(UT); + 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) { diff --git a/lib/BME280/BME280.h b/lib/BME280/BME280.h index 184ff44..85c0fe8 100644 --- a/lib/BME280/BME280.h +++ b/lib/BME280/BME280.h @@ -1,19 +1,11 @@ #ifndef BME280_H #define BME280_H -#include +#include "../../include/attsensor.h" #define BME280_I2CADDR 0x76 -struct lora_data { - uint8_t bat; - int32_t temperature; - int32_t humidity; - int32_t pressure; -} __attribute__ ((packed)); - -class BME280 -{ +class BME280 : public AttSensor { private: // Variables for Calibration Values uint8_t dig_H1, dig_H3; @@ -36,13 +28,12 @@ private: int16_t readS16(uint8_t addr); int16_t readS16_LE(uint8_t addr); void write8(uint8_t addr, uint8_t data); + void getCalData(void); public: BME280(void); - - // Get Calibration Data from Sensor - void getCalData(void); - // Read Pressure From Sensor - void getSensorData(lora_data &loradata); + uint8_t getSensorData(char *payload, uint8_t startbyte); + void initialize(void) {getCalData();}; + uint8_t numBytes(void) {return 12;}; }; #endif diff --git a/lib/MHZ19C/MHZ19C.cpp b/lib/MHZ19C/MHZ19C.cpp index c097840..b8bc2b5 100644 --- a/lib/MHZ19C/MHZ19C.cpp +++ b/lib/MHZ19C/MHZ19C.cpp @@ -41,19 +41,23 @@ void MHZ19C::initialize(void) { #endif } -void MHZ19C::getSensorData(lora_data &loradata) { +uint8_t MHZ19C::getSensorData(char * payload, uint8_t startbyte) { write(MHZ19C_CMD_GET_PPM, 0x00); delay(50); uint8_t readBytes = read(); - loradata.ppm = 0; + payload[startbyte] = 0x00; + payload[startbyte+1] = 0x00; if (readBytes > 0) { switch(buffer[1]) { 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; } } + return startbyte+2; } // Turn Self Calibration Routine On or Off diff --git a/lib/MHZ19C/MHZ19C.h b/lib/MHZ19C/MHZ19C.h index 0129ef0..2250445 100644 --- a/lib/MHZ19C/MHZ19C.h +++ b/lib/MHZ19C/MHZ19C.h @@ -27,11 +27,7 @@ #ifndef MHZ19C_H #define MHZ19C_H -// Data Structure for the LoRa Packet -struct lora_data { - uint8_t bat; - int16_t ppm; -} __attribute__ ((packed)); +#include "../../include/attsensor.h" #define MHZ19C_READ_TIMEOUT 500 // Timeout for Serial Communication #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_GET_PPM 0x86 // Get Current PPM Reading -class MHZ19C { +class MHZ19C : public AttSensor { private: uint8_t buffer[MHZ19C_SER_BUF_LEN]; @@ -51,8 +47,9 @@ class MHZ19C { public: MHZ19C(void); - void MHZ19C::initialize(void); - void getSensorData(lora_data &loradata); + void initialize(void); + uint8_t numBytes(void) {return 2;}; + uint8_t getSensorData(char *payload, uint8_t startbyte); void setSelfCalibration(bool state); }; diff --git a/lib/SENSAIRS8/SENSAIRS8.cpp b/lib/SENSAIRS8/SENSAIRS8.cpp index 68e96bf..d850c53 100644 --- a/lib/SENSAIRS8/SENSAIRS8.cpp +++ b/lib/SENSAIRS8/SENSAIRS8.cpp @@ -33,7 +33,7 @@ SENSAIRS8::SENSAIRS8(void) { 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}; while (Serial.available() > 0) Serial.read(); Serial.write(_cmd, 7); @@ -41,9 +41,14 @@ void SENSAIRS8::getSensorData(lora_data &loradata) { delay(1000); uint8_t readBytes = read(); + payload[startbyte] = 0x00; + payload[startbyte+1] = 0x00; 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 diff --git a/lib/SENSAIRS8/SENSAIRS8.h b/lib/SENSAIRS8/SENSAIRS8.h index c0b1230..3702ca1 100644 --- a/lib/SENSAIRS8/SENSAIRS8.h +++ b/lib/SENSAIRS8/SENSAIRS8.h @@ -27,16 +27,12 @@ #ifndef SENSAIRS8_H #define SENSAIRS8_H -// Data Structure for the LoRa Packet -struct lora_data { - uint8_t bat; - int16_t ppm; -} __attribute__ ((packed)); +#include "../../include/attsensor.h" #define READ_TIMEOUT 500 // Timeout for Serial Communication #define SER_BUF_LEN 7 // Length of the Internal Serial Message Buffer -class SENSAIRS8 { +class SENSAIRS8 : public AttSensor { private: uint8_t buffer[SER_BUF_LEN]; @@ -46,7 +42,9 @@ class SENSAIRS8 { public: 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 \ No newline at end of file diff --git a/lib/SG112A/SG112A.cpp b/lib/SG112A/SG112A.cpp index bd23734..e5fdd90 100644 --- a/lib/SG112A/SG112A.cpp +++ b/lib/SG112A/SG112A.cpp @@ -33,18 +33,24 @@ SG112A::SG112A(void) { Serial.setTimeout(READ_TIMEOUT); } -void SG112A::getSensorData(lora_data &loradata) { + +uint8_t SG112A::getSensorData(char *payload, uint8_t startbyte) { write(CMD_GET_PPM); delay(50); uint8_t readBytes = read(); + payload[startbyte] = 0x00; + payload[startbyte+1] = 0x00; if (readBytes > 0) { switch(buffer[2]) { 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; } } + return startbyte+2; } // Write a Command to the Sensor diff --git a/lib/SG112A/SG112A.h b/lib/SG112A/SG112A.h index 3db0677..93f365b 100644 --- a/lib/SG112A/SG112A.h +++ b/lib/SG112A/SG112A.h @@ -27,11 +27,7 @@ #ifndef SG112A_H #define SG112A_H -// Data Structure for the LoRa Packet -struct lora_data { - uint8_t bat; - int16_t ppm; -} __attribute__ ((packed)); +#include "../../include/attsensor.h" #define READ_TIMEOUT 500 // Timeout for Serial Communication #define SER_BUF_LEN 16 // Length of the Internal Serial Message Buffer @@ -52,7 +48,9 @@ class SG112A { public: 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 \ No newline at end of file diff --git a/lib/SHT21/SHT21.cpp b/lib/SHT21/SHT21.cpp index ba3b659..60f496f 100644 --- a/lib/SHT21/SHT21.cpp +++ b/lib/SHT21/SHT21.cpp @@ -52,7 +52,20 @@ uint16_t SHT21::sensorRead(uint8_t command) { return result; } -void SHT21::getSensorData(lora_data &loradata) { - loradata.temperature = (int32_t)((-46.85 + 175.72 / 65536.0 * (float)(sensorRead(SHT21_TEMPHOLD)))*100); - loradata.humidity = (int32_t)((-6.0 + 125.0 / 65536.0 * (float)(sensorRead(SHT21_HUMIHOLD)))*100); +uint8_t SHT21::getSensorData(char *payload, uint8_t startbyte) { + // Temperature + 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; } \ No newline at end of file diff --git a/lib/SHT21/SHT21.h b/lib/SHT21/SHT21.h index 17becde..ee49859 100644 --- a/lib/SHT21/SHT21.h +++ b/lib/SHT21/SHT21.h @@ -27,7 +27,7 @@ #ifndef SHT21_H #define SHT21_H -#include +#include "../../include/attsensor.h" #define SHT21_I2CADDR 0x40 @@ -37,12 +37,6 @@ #define SHT21_HUMINOHOLD 0xF5 #define SHT21_SOFTRESET 0xFE -struct lora_data { - uint8_t bat; - int32_t temperature; - int32_t humidity; -} __attribute__ ((packed)); - class SHT21 { private: @@ -50,7 +44,9 @@ class SHT21 public: 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 \ No newline at end of file diff --git a/src/config.h.example b/src/config.h.example index 6c80987..d5e3345 100644 --- a/src/config.h.example +++ b/src/config.h.example @@ -23,12 +23,19 @@ // Serial Debug Output is not available with this Sensor. // #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 HAS_NO_SENSOR // #define HAS_BME280 // #define HAS_SHT21 // #define HAS_SG112A // #define HAS_MHZ19C +// #define HAS_SENSAIRS8 // How many minutes to sleep between Measuring/Sending diff --git a/src/main.cpp b/src/main.cpp index a7baec9..959944e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,23 @@ #include "config.h" #include "debug.h" +#include "attsensor.h" + +#ifdef HAS_MHZ19C + #include +#endif +#ifdef HAS_SG112A + #include +#endif +#ifdef HAS_SENSAIRS8 + #include +#endif +#ifdef HAS_BME280 + #include +#endif +#ifdef HAS_SHT21 + #include +#endif // define the blink function and BLINK_LED Macro depending // on the definition of LED_PIN @@ -44,30 +61,10 @@ void blink(uint8_t num) { #define WS2812B_BLINK(led,r,g,b,ms) #endif -// Create the Sensor Objects -#if defined HAS_NO_SENSOR - struct lora_data { - uint8_t bat; - } __attribute ((packed)); -#elif defined HAS_MHZ19C - #include - MHZ19C sensor; -#elif defined HAS_SG112A - #include - SG112A sensor; -#elif defined HAS_SENSAIRS8 - #include - SENSAIRS8 sensor; -#elif defined HAS_BME280 - #include - BME280 sensor; -#elif defined HAS_SHT21 - #include - SHT21 sensor; -#elif defined HAS_SG112A - #include - SG112A sensor; +#ifndef HAS_NO_SENSORS +AttSensor* sensors[NUM_SENSORS]; #endif +int payloadBytes = 1; // Define some LMIC Callbacks and Variables 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) { // Prepare LoRa Data Packet // 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) { delay(1); } else { + uint8_t curByte = 0; + // Add Battery Voltage (0.2V Accuracy stored in 1 byte) uint32_t batv = readSupplyVoltage(); - data.bat = (uint8_t)(batv / 20); + payload[curByte] = (uint8_t)(batv / 20); if (batv % 20 > 9) - data.bat += 1; + payload[curByte] += 1; + curByte++; // Get Sensor Readings Into Data Paket #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 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) @@ -261,19 +262,46 @@ void setup() attachInterrupt(digitalPinToInterrupt(BTN_PIN), btn_press, FALLING); #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 while (RTC.STATUS > 0) {} RTC.CLKSEL = RTC_CLKSEL_INT1K_gc; while (RTC.PITSTATUS > 0) {} - // Initialize Sensor(s) - #ifdef HAS_BME280 - sensor.getCalData(); - #endif - #ifdef HAS_MHZ19C - sensor.initialize(); - #endif - // Setup LMIC DEBUG_PRINT("Initializing LMIC...") os_init();