From 618ff4dcef505a134be5c59b660b32f2f14a638f Mon Sep 17 00:00:00 2001 From: Alexander Gabriel Date: Fri, 3 Nov 2023 00:48:34 +0100 Subject: [PATCH] first commit --- .gitignore | 6 ++ .vscode/arduino.json | 4 + .vscode/extensions.json | 10 +++ include/README | 39 +++++++++ lib/README | 46 +++++++++++ platformio.ini | 25 ++++++ src/TTN-configuration.template.h | 31 +++++++ src/cp_lmic_project_config.sh | 2 + src/decoder.js | 57 +++++++++++++ src/lmic_project_config.h | 10 +++ src/main.cpp | 133 +++++++++++++++++++++++++++++++ src/main.h | 125 +++++++++++++++++++++++++++++ test/README | 11 +++ 13 files changed, 499 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/arduino.json create mode 100644 .vscode/extensions.json create mode 100644 include/README create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/TTN-configuration.template.h create mode 100755 src/cp_lmic_project_config.sh create mode 100644 src/decoder.js create mode 100644 src/lmic_project_config.h create mode 100644 src/main.cpp create mode 100644 src/main.h create mode 100644 test/README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e10b91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +src/TTN-configuration.h \ No newline at end of file diff --git a/.vscode/arduino.json b/.vscode/arduino.json new file mode 100644 index 0000000..13cca8e --- /dev/null +++ b/.vscode/arduino.json @@ -0,0 +1,4 @@ +{ + "configuration": "CPUFreq=240,UploadSpeed=921600,DebugLevel=none,LORAWAN_REGION=0,LoRaWanDebugLevel=0,LORAWAN_DEVEUI=0,LORAWAN_PREAMBLE_LENGTH=0", + "board": "Heltec-esp32:esp32:wifi_lora_32_V2" +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..b19deb8 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,25 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:heltec_wifi_lora_32_V2] +platform = espressif32 +board = heltec_wifi_lora_32_V2 +framework = arduino +lib_deps = + adafruit/Adafruit NeoPixel@^1.11.0 + https://github.com/olikraus/U8g2_Arduino + https://github.com/ricaun/SimpleLMIC + boschsensortec/BSEC Software Library@1.6.1480 + mcci-catena/MCCI LoRaWAN LMIC library@^4.1.1 + sabas1080/CayenneLPP@^1.1.0 +monitor_speed = 115200 +build_flags = + -D hal_init=LMICHAL_init + -D CFG_eu868=1 diff --git a/src/TTN-configuration.template.h b/src/TTN-configuration.template.h new file mode 100644 index 0000000..f5af8e0 --- /dev/null +++ b/src/TTN-configuration.template.h @@ -0,0 +1,31 @@ +const char *devAddr = ""; +const char *nwkSKey = ""; +const char *appSKey = ""; + +//config for Arduino Due, GPS connected to Serial2... Don't ask... +const lmic_pinmap lmic_pins = { + .nss = 10, + .rxtx = LMIC_UNUSED_PIN, + .rst = LMIC_UNUSED_PIN, + .dio = {2, 3, LMIC_UNUSED_PIN}, +}; +#define GPSSerial Serial2 + +//Config for TTGO T-Beam T22 v1.1 +const lmic_pinmap lmic_pins = { + .nss = 18, + .rxtx = LMIC_UNUSED_PIN, + .rst = 23, + .dio = {26, 33, 32}, +}; +#define GPSSerial Serial1 +//#define GPS_RX_PIN 34 +//#define GPS_TX_PIN 12 + +//Heltec Lora 32 v2 +const lmic_pinmap lmic_pins = { + .nss = 18, + .rxtx = LMIC_UNUSED_PIN, + .rst = 14, + .dio = {26, 34, 35}, +}; \ No newline at end of file diff --git a/src/cp_lmic_project_config.sh b/src/cp_lmic_project_config.sh new file mode 100755 index 0000000..ca39012 --- /dev/null +++ b/src/cp_lmic_project_config.sh @@ -0,0 +1,2 @@ +cp src/lmic_project_config.h ".pio/libdeps/heltec_wifi_lora_32_V2/MCCI LoRaWAN LMIC library/project_config" + \ No newline at end of file diff --git a/src/decoder.js b/src/decoder.js new file mode 100644 index 0000000..3d103b0 --- /dev/null +++ b/src/decoder.js @@ -0,0 +1,57 @@ +function decodeFloat32(bytes) { + var sign = (bytes & 0x80000000) ? -1 : 1; + var exponent = ((bytes >> 23) & 0xFF) - 127; + var significand = (bytes & ~(-1 << 23)); + + if (exponent == 128) + return sign * ((significand) ? Number.NaN : Number.POSITIVE_INFINITY); + + if (exponent == -127) { + if (significand == 0) return sign * 0.0; + exponent = -126; + significand /= (1 << 22); + } else significand = (significand | (1 << 23)) / (1 << 23); + + return sign * significand * Math.pow(2, exponent); +} + +function decodeInt16(bytes) { + if ((bytes & 1 << 15) > 0) { // value is negative (16bit 2's complement) + bytes = ((~bytes) & 0xffff) + 1; // invert 16bits & add 1 => now positive value + bytes = bytes * -1; + } + return bytes; +} + +function int16_LE(bytes, idx) { + bytes = bytes.slice(idx || 0); + return bytes[0] << 0 | bytes[1] << 8; +} + +function int32_LE(bytes, idx) { + bytes = bytes.slice(idx || 0); + return bytes[0] << 0 | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; +} + +function Decoder(bytes, port) { + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + var decoded = { + co2: decodeFloat32(int32_LE(bytes, 0)), + }; + + return decoded; +} + + + +function decodeUplink(input) { + bytes = (input.bytes[0] << 8 | input.bytes[1] << 16 | input.bytes[2] << 8 | input.bytes[3]); + return { + data: { + co2: bytes/100 + }, + warnings: [], + errors: [] + }; +} diff --git a/src/lmic_project_config.h b/src/lmic_project_config.h new file mode 100644 index 0000000..cf36e72 --- /dev/null +++ b/src/lmic_project_config.h @@ -0,0 +1,10 @@ +// project-specific definitions +#define CFG_eu868 1 +//#define CFG_us915 1 +//#define CFG_au915 1 +//#define CFG_as923 1 +// #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP; also define CFG_as923 */ +//#define CFG_kr920 1 +//#define CFG_in866 1 +#define CFG_sx1276_radio 1 +//#define LMIC_USE_INTERRUPTS diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ee8b830 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,133 @@ +#include +#include "bsec.h" + +#define USE_DISPLAY +#ifdef USE_DISPLAY +#include +#ifdef U8X8_HAVE_HW_SPI +#include +#endif +#ifdef U8X8_HAVE_HW_I2C +#include +#endif + +#include "CayenneLPP.h" +CayenneLPP lpp(160); + +#include +#define LED_PIN 23 +#define LED_COUNT 1 +#define LEDALWAYSON +Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); + +U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16); +#endif + +#include +#include "TTN-configuration.h" + +SimpleLMIC ttn; +int TTNDebugLoopInterval = 1000; +uint32_t TTNDebugLoopMillis = millis() - TTNDebugLoopInterval; + +int TTNSendLoopInterval = 60*1000; +uint32_t TTNSendLoopMillis = millis() - TTNSendLoopInterval; + +int DisplayUpdateLoopInterval = 1000; +uint32_t DisplayUpdateLoopMillis = millis() - DisplayUpdateLoopInterval; +int NeoPixelUpdateLoopInterval = 1000; +uint32_t NeoPixelUpdateLoopMillis = millis() - NeoPixelUpdateLoopInterval; + + +// Create an object of the class Bsec +Bsec iaqSensor; + +String output; + +#include "main.h" + +// Entry point for the example +void setup(void) +{ + Serial.begin(115200); + Wire.begin(); + + iaqSensor.begin(BME680_I2C_ADDR_PRIMARY, Wire); + output = "\nBSEC library version " + String(iaqSensor.version.major) + "." + String(iaqSensor.version.minor) + "." + String(iaqSensor.version.major_bugfix) + "." + String(iaqSensor.version.minor_bugfix); + Serial.println(output); + //checkIaqSensorStatus(); + + bsec_virtual_sensor_t sensorList[10] = { + BSEC_OUTPUT_RAW_TEMPERATURE, + BSEC_OUTPUT_RAW_PRESSURE, + BSEC_OUTPUT_RAW_HUMIDITY, + BSEC_OUTPUT_RAW_GAS, + BSEC_OUTPUT_IAQ, + BSEC_OUTPUT_STATIC_IAQ, + BSEC_OUTPUT_CO2_EQUIVALENT, + BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, + }; + + iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP); + checkIaqSensorStatus(); + + +#ifdef USE_DISPLAY + setupDisplay(); +#endif + + // Print the header + output = "Timestamp [ms], raw temperature [°C], pressure [hPa], raw relative humidity [%], gas [Ohm], IAQ, IAQ accuracy, temperature [°C], relative humidity [%], Static IAQ, CO2 equivalent, breath VOC equivalent"; + Serial.println(output); + + strip.begin(); + strip.show(); + strip.setBrightness(50); + + ttn.begin(); + ttn.setSubBand(2); + ttn.onMessage(message); + ttn.personalize(devAddr, nwkSKey, appSKey); +} + +// Function that is looped forever +void loop(void) +{ + ttn.loop(); + unsigned long time_trigger = millis(); + if (iaqSensor.run()) { // If new data is available + output = String(time_trigger); + output += ", " + String(iaqSensor.rawTemperature); + output += ", " + String(iaqSensor.pressure); + output += ", " + String(iaqSensor.rawHumidity); + output += ", " + String(iaqSensor.gasResistance); + output += ", " + String(iaqSensor.iaq); + output += ", " + String(iaqSensor.iaqAccuracy); + output += ", " + String(iaqSensor.temperature); + output += ", " + String(iaqSensor.humidity); + output += ", " + String(iaqSensor.staticIaq); + output += ", " + String(iaqSensor.co2Equivalent); + output += ", " + String(iaqSensor.breathVocEquivalent); + Serial.println(output); + } else { + checkIaqSensorStatus(); + } +#ifdef USE_DISPLAY + updateDisplay(); +#endif + updateNeoPixel(); + + if (!ttn.isBusy() && ttn.isLink() && millis() - TTNSendLoopMillis > TTNSendLoopInterval) + { + TTNSendLoopMillis = millis(); + Serial.println("Not Busy!"); + Serial.println(iaqSensor.co2Equivalent); + Serial.println(sizeof(iaqSensor.co2Equivalent)); + lpp.addTemperature(1, iaqSensor.co2Equivalent); + ttn.write(lpp.getBuffer(), lpp.getSize()); + ttn.send(); + } +} + diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..76d7c9b --- /dev/null +++ b/src/main.h @@ -0,0 +1,125 @@ +void errLeds(void) +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + delay(100); + digitalWrite(LED_BUILTIN, LOW); + delay(100); +} + +void printTTNDebug() { + Serial.print("ttn.isBusy(): "); + Serial.print(ttn.isBusy()); + Serial.print(" ttn.isLink(): "); + Serial.print(ttn.isLink()); + + Serial.println(); +} + +void message(uint8_t *payload, size_t size, uint8_t port) +{ + Serial.println("Received " + String(size) + " bytes on port " + String(port)); + switch (port) { + case 1: + break; + } +} + +void updateNeoPixel(){ + if(millis() - NeoPixelUpdateLoopMillis > NeoPixelUpdateLoopInterval) { + NeoPixelUpdateLoopMillis = millis(); + double warningValue = 800; + double criticalValue = 1500; + double steps = 2000/strip.numPixels(); + float co2 = iaqSensor.co2Equivalent; + for(int i=0; i= steps*(i+1)) { + #endif + if(co2 >= criticalValue) strip.setPixelColor(i, 255, 0, 0); + else if(co2 >= warningValue) strip.setPixelColor(i, 255, 165, 0); + else if(co2 < warningValue) strip.setPixelColor(i, 0, 255, 0); + #ifndef LEDALWAYSON + } + else { + strip.setPixelColor(i, 0, 0, 0); + } + #endif + strip.show(); + } + } +} + + + +// Helper function definitions +void checkIaqSensorStatus(void) +{ + if (iaqSensor.status != BSEC_OK) { + if (iaqSensor.status < BSEC_OK) { + output = "BSEC error code : " + String(iaqSensor.status); + Serial.println(output); + for (;;) + errLeds(); /* Halt in case of failure */ + } else { + output = "BSEC warning code : " + String(iaqSensor.status); + Serial.println(output); + } + } + + if (iaqSensor.bme680Status != BME680_OK) { + if (iaqSensor.bme680Status < BME680_OK) { + output = "BME680 error code : " + String(iaqSensor.bme680Status); + Serial.println(output); + for (;;) + errLeds(); /* Halt in case of failure */ + } else { + output = "BME680 warning code : " + String(iaqSensor.bme680Status); + Serial.println(output); + } + } +} + +#ifdef USE_DISPLAY + +void updateDisplay() { + if(millis() - DisplayUpdateLoopMillis > DisplayUpdateLoopInterval) { + DisplayUpdateLoopMillis = millis(); + char displayText[256]; + u8g2.clearBuffer(); + + char tempString[10]; + dtostrf(iaqSensor.temperature, 4, 2, tempString); + sprintf(displayText, "Temp: %s", tempString); + u8g2.drawStr(0,10,displayText); + + char humidString[10]; + dtostrf(iaqSensor.humidity, 4, 2, humidString); + sprintf(displayText, "Humidity: %s", humidString); + u8g2.drawStr(0,20,displayText); + + char co2String[10]; + dtostrf(iaqSensor.co2Equivalent, 4, 2, co2String); + sprintf(displayText, "CO2-Equivalent: %s", ""); + u8g2.drawStr(0,30,displayText); + sprintf(displayText, "%20s", co2String); + u8g2.drawStr(0,40,displayText); + + sprintf(displayText, "ttn.isBusy(): %d", ttn.isBusy()); + u8g2.drawStr(0,50,displayText); + + sprintf(displayText, "ttn.isLink(): %d", ttn.isLink()); + u8g2.drawStr(0,60,displayText); + + u8g2.sendBuffer(); + } +} + +void setupDisplay() { + u8g2.begin(); + u8g2.setFont(u8g2_font_courB08_tr); // choose a suitable font + updateDisplay(); +} + +#endif + diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html