commit 0dfcd46b56dc0c24d76a9f65d3977dd595a9dd64 Author: Julien Lazarewicz Date: Wed May 21 01:47:20 2025 +0200 first commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..974946b --- /dev/null +++ b/.clang-format @@ -0,0 +1,57 @@ +--- +BasedOnStyle: Mozilla +AccessModifierOffset: '-4' +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: 'true' +AlignConsecutiveDeclarations: 'true' +AlignOperands: 'true' +AlignTrailingComments: 'true' +AllowAllParametersOfDeclarationOnNextLine: 'true' +AllowShortBlocksOnASingleLine: 'true' +AllowShortCaseLabelsOnASingleLine: 'false' +AllowShortFunctionsOnASingleLine: Empty +AllowShortLoopsOnASingleLine: 'false' +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Allman +BreakBeforeTernaryOperators: 'true' +BreakConstructorInitializers: AfterColon +BreakStringLiterals: 'false' +ColumnLimit: '100' +ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' +ConstructorInitializerIndentWidth: '0' +ContinuationIndentWidth: '4' +Cpp11BracedListStyle: 'false' +DerivePointerAlignment: 'false' +DisableFormat: 'false' +ExperimentalAutoDetectBinPacking: 'true' +IndentCaseLabels: 'true' +IndentWidth: '4' +IndentWrappedFunctionNames: 'false' +JavaScriptQuotes: Single +KeepEmptyLinesAtTheStartOfBlocks: 'false' +Language: Cpp +MaxEmptyLinesToKeep: '2' +NamespaceIndentation: None +ObjCBlockIndentWidth: '4' +ObjCSpaceAfterProperty: 'false' +ObjCSpaceBeforeProtocolList: 'false' +PointerAlignment: Left +ReflowComments: 'true' +SortIncludes: 'false' +SpaceAfterCStyleCast: 'true' +SpaceBeforeAssignmentOperators: 'true' +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: 'false' +SpacesBeforeTrailingComments: '2' +SpacesInAngles: 'false' +SpacesInCStyleCastParentheses: 'false' +SpacesInContainerLiterals: 'false' +SpacesInParentheses: 'false' +SpacesInSquareBrackets: 'false' +Standard: Cpp11 +TabWidth: '4' +UseTab: Always + +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/include/README b/include/README new file mode 100644 index 0000000..49819c0 --- /dev/null +++ b/include/README @@ -0,0 +1,37 @@ + +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 convention is to give header files names that end with `.h'. + +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/include/config.h b/include/config.h new file mode 100644 index 0000000..e7a2612 --- /dev/null +++ b/include/config.h @@ -0,0 +1,61 @@ +// AHT20 SENSOR ENABLE / DISABLE +#define AHT20_ENABLE true + +// SGP40 SENSOR ENABLE / DISABLE +#define SGP40_ENABLE true + +// BH1750 SENSOR ENABLE / DISABLE +#define BH1750_ENABLE true + +// BATTERY TESTING ENABLE / DISABLE +#define BAT_TEST_ENABLE true + +// Thingsboard library debug +#define THINGSBOARD_ENABLE_DEBUG true + +// Serial debug output +#define SERIAL_DEBUG true +#if SERIAL_DEBUG +#define SERIAL_PRINT_SENSOR_VALUES false +#endif + +constexpr char WIFI_SSID[] = "thingsboard"; +constexpr char WIFI_PASSWORD[] = "thingsboard"; + +// See https://thingsboard.io/docs/getting-started-guides/helloworld/ +// to understand how to obtain an access token +constexpr char TOKEN[] = "HOMECTRL"; +// constexpr char mqtt_attribute_topic[] = "v1/devices/me/attributes"; + +// Thingsboard we want to establish a connection too +constexpr char THINGSBOARD_SERVER[] = "10.42.0.1"; + +// Whether the given script is using encryption or not, +// generally recommended as it increases security (communication with the server is not in clear +// text anymore), it does come with an overhead tough as having an encrypted session requires a lot +// of memory, which might not be avaialable on lower end devices. +#define ENCRYPTED false + +// While we wait for Feather ESP32 V2 to get added to the Espressif BSP, +// we have to select PICO D4 and UNCOMMENT this line! +#define ADAFRUIT_FEATHER_ESP32_V2 + +// Sending data can either be done over MQTT and the PubSubClient +// or HTTPS and the HTTPClient, when using the ESP32 or ESP8266 +#define USING_HTTPS false + +// Enables the ThingsBoard class to be fully dynamic instead of requiring template arguments to +// statically allocate memory. If enabled the program might be slightly slower and all the memory +// will be placed onto the heap instead of the stack. +#define THINGSBOARD_ENABLE_DYNAMIC 1 + +// If the THINGSBOARD_ENABLE_DYNAMIC 1 setting causes this error log message to appear [TB] Unable +// to de-serialize received json data with error (DeserializationError::NoMemory). Simply add this +// configuration line as well. +// #define THINGSBOARD_ENABLE_PSRAM 0 + +// Enables sending messages that are bigger than the predefined message size, +// where the message will be sent byte by byte as a fallback instead. +// Requires an additional library, see https://github.com/bblanchon/ArduinoStreamUtils for more +// information. Simply install that library and the feature will be enabled automatically. +#define THINGSBOARD_ENABLE_STREAM_UTILS 0 \ No newline at end of file diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..af8db4f --- /dev/null +++ b/include/version.h @@ -0,0 +1 @@ +#define VERSION "0.0.2" \ No newline at end of file diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..9379397 --- /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 the executable file. + +The source code of each library should be placed in a separate directory +("lib/your_library_name/[Code]"). + +For example, see the structure of the following example libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional. for 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 + +Example contents of `src/main.c` using Foo and Bar: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +The PlatformIO Library Dependency Finder will find automatically dependent +libraries by 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..5ac0da4 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,21 @@ +; 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:esp32dev] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip +board = adafruit_feather_esp32_v2 +framework = arduino +lib_deps = + thingsboard/ThingsBoard@^0.15.0 + adafruit/Adafruit AHTX0@^2.0.5 + adafruit/Adafruit SGP40 Sensor@^1.1.3 + adafruit/Adafruit NeoPixel@^1.12.5 + starmbi/hp_BH1750@^1.0.2 +upload_port = /dev/cu.usbserial-59170079101 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..01dd642 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,553 @@ +#include "config.h" +#include "version.h" + +#include "driver/rtc_io.h" + +#define USE_EXT0_WAKEUP 0 + +#include +#include +#include + +#include +#include + +// AHT20 Temperature & Humidity sensor +#if AHT20_ENABLE +#include +#endif + +// SGP40 Air Quality Sensor +#if SGP40_ENABLE +#include +#endif + +// BH1750 Luxmeter +#if BH1750_ENABLE +#include +#endif + +// Battery pin +#define VBATPIN A13 + +// then these pins will be defined for us +#if defined(ADAFRUIT_FEATHER_ESP32_V2) or defined(ARDUINO_ADAFRUIT_ITSYBITSY_ESP32) +#define PIN_NEOPIXEL 0 +#define NEOPIXEL_I2C_POWER 2 +#endif + +#if defined(PIN_NEOPIXEL) +Adafruit_NeoPixel pixel(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); +#endif + +#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ +#define TIME_TO_SLEEP 300 /* Time ESP32 will go to sleep (in seconds) */ +#define MINUTES_5 300000 +#define SECONDS_80 80000 + +RTC_DATA_ATTR int bootCount = 0; + +#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO) // 2 ^ GPIO_NUMBER in hex +#define WAKEUP_GPIO GPIO_NUM_33 + +void enableInternalPower(); +void disableInternalPower(); +void print_wakeup_reason(); +void InitWiFi(); +bool reconnect(); + +// MQTT port used to communicate with the server, 1883 is the default unencrypted MQTT port, +// whereas 8883 would be the default encrypted SSL MQTT port +#if ENCRYPTED +constexpr uint16_t THINGSBOARD_PORT = 8883U; +#else +constexpr uint16_t THINGSBOARD_PORT = 1883U; +#endif + +// Maximum size packets will ever be sent or received by the underlying MQTT client, +// if the size is to small messages might not be sent or received messages will be discarded +constexpr uint16_t MAX_MESSAGE_SEND_SIZE = 128U; +constexpr uint16_t MAX_MESSAGE_RECEIVE_SIZE = 128U; + +// Baud rate for the debugging serial connection +// If the Serial output is mangled, ensure to change the monitor speed accordingly to this variable +constexpr uint32_t SERIAL_DEBUG_BAUD = 115200U; + +#if ENCRYPTED +// See https://comodosslstore.com/resources/what-is-a-root-ca-certificate-and-how-do-i-download-it/ +// on how to get the root certificate of the server we want to communicate with, +// this is needed to establish a secure connection and changes depending on the website. +constexpr char ROOT_CERT[] = R"(-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)"; +#endif + +constexpr char CONNECTING_MSG[] = "Connecting to: (%s) with token (%s)\n"; +constexpr char TEMPERATURE_KEY[] = "temperature"; +constexpr char HUMIDITY_KEY[] = "humidity"; +constexpr char VOCC_KEY[] = "voc"; +constexpr char LUX_KEY[] = "lux"; +constexpr char BAT_KEY[] = "battery"; +constexpr char DOOR_1_KEY[] = "door_1"; +constexpr char FIRMWARE_KEY[] = "version"; + + +// Initialize underlying client, used to establish a connection +#if ENCRYPTED +WiFiClientSecure espClient; +#else +WiFiClient espClient; +#endif + +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); + +// Initialize used apis +const std::array apis = {}; +// Initialize ThingsBoard instance with the maximum needed buffer size +#if THINGSBOARD_ENABLE_DYNAMIC +#if THINGSBOARD_ENABLE_STREAM_UTILS +ThingsBoard tb(mqttClient, + MAX_MESSAGE_RECEIVE_SIZE, + MAX_MESSAGE_SEND_SIZE, + Default_Max_Stack_Size, + Default_Buffering_Size, + Default_Max_Response_Size, + apis.cbegin(), + apis.cend()); +#else +ThingsBoard tb(mqttClient, + MAX_MESSAGE_RECEIVE_SIZE, + MAX_MESSAGE_SEND_SIZE, + Default_Max_Stack_Size, + Default_Max_Response_Size, + apis.cbegin(), + apis.cend()); +#endif +#else +#if THINGSBOARD_ENABLE_STREAM_UTILS +ThingsBoard tb(mqttClient, + MAX_MESSAGE_RECEIVE_SIZE, + MAX_MESSAGE_SEND_SIZE, + Default_Max_Stack_Size, + Default_Buffering_Size, + apis.cbegin(), + apis.cend()); +#else +ThingsBoard tb(mqttClient, + MAX_MESSAGE_RECEIVE_SIZE, + MAX_MESSAGE_SEND_SIZE, + Default_Max_Stack_Size, + apis.cbegin(), + apis.cend()); +#endif +#endif + +// AHT20 Temperature & Humidity sensor +#if AHT20_ENABLE +Adafruit_AHTX0 aht; +#endif + +// SGP40 Air Quality Sensor +#if SGP40_ENABLE +Adafruit_SGP40 sgp; +#endif + +// BH1750 Luxmeter +#if BH1750_ENABLE +hp_BH1750 BH1750; +#endif + +#if BAT_TEST_ENABLE +float measuredvbat; +#endif + +void setup() +{ + // If analog input pin 0 is unconnected, random analog + // noise will cause the call to randomSeed() to generate + // different seed numbers each time the sketch runs. + // randomSeed() will then shuffle the random function. + randomSeed(analogRead(0)); +// Initalize serial connection for debugging +#if SERIAL_DEBUG + Serial.begin(SERIAL_DEBUG_BAUD); + delay(200); +#endif + + // Increment boot number and print it every reboot + ++bootCount; +#if SERIAL_DEBUG + Serial.println("Boot number: " + String(bootCount)); +#endif + + // Print the wakeup reason for ESP32 + print_wakeup_reason(); + + // Enable wakeup timer + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); +#if SERIAL_DEBUG + Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds"); +#endif + + // Enable wakeup from RTC peripherical change + esp_sleep_enable_ext1_wakeup_io(BUTTON_PIN_BITMASK(WAKEUP_GPIO), ESP_EXT1_WAKEUP_ANY_HIGH); + rtc_gpio_pullup_dis(WAKEUP_GPIO); + rtc_gpio_pulldown_en(WAKEUP_GPIO); + + // Turn on any internal power switches for TFT, NeoPixels, I2C, etc! + enableInternalPower(); + +#if AHT20_ENABLE + // AHT20 init + if (!aht.begin()) + { +#if SERIAL_DEBUG + Serial.println("AHT sensor not found !"); +#endif + while (true) {}; + } +#endif + +#if SERIAL_DEBUG + Serial.println("AHT10 or AHT20 found"); +#endif + + +// SGP40 init +#if SGP40_ENABLE + if (!sgp.begin()) + { +#if SERIAL_DEBUG + Serial.println("SGP40 sensor not found !"); +#endif + while (true) {}; + } + +#if SERIAL_DEBUG + Serial.print("SGP40 found serial #"); + Serial.print(sgp.serialnumber[0], HEX); + Serial.print(sgp.serialnumber[1], HEX); + Serial.println(sgp.serialnumber[2], HEX); +#endif +#endif + +// BH1750 init +#if BH1750_ENABLE + if (!BH1750.begin(BH1750_TO_GROUND)) + { +#if SERIAL_DEBUG + Serial.println("BH1750 sensor not found !"); +#endif + while (true) {}; + } + unsigned int BH1750convtime; + BH1750convtime = BH1750.getMtregTime(); +#if SERIAL_DEBUG + Serial.println("BH1750 sensor found"); +#if SERIAL_PRINT_SENSOR_VALUES + Serial.printf("BH1750 conversion time: %dms\n", BH1750convtime); +#endif +#endif + BH1750.start(); +#endif + + InitWiFi(); + + // DATA COLLECTION START + + sensors_event_t humidity, temperature; + float lux; + + int32_t voc_index; + uint16_t sraw; + +#if SERIAL_DEBUG + Serial.println("Waiting for 80 sec. for sensors accuracy ..."); +#endif + // Run sensors data collection for 80 sec, needed for SGP sensor + while (SECONDS_80 > millis()) + { + aht.getEvent(&humidity, + &temperature); // populate temp and humidity objects with fresh data + + // TODO : if no AHT20 sensor get temperature and humidity from thingsboard + + sraw = sgp.measureRaw(temperature.temperature, humidity.relative_humidity); + +#if SERIAL_DEBUG +#if SERIAL_PRINT_SENSOR_VALUES + Serial.print("SGP40 - Raw measurement: "); + Serial.println(sraw); +#endif +#endif + voc_index = sgp.measureVocIndex(temperature.temperature, humidity.relative_humidity); +#if SERIAL_DEBUG +#if SERIAL_PRINT_SENSOR_VALUES + Serial.print("Voc Index: "); + Serial.println(voc_index); +#endif +#endif + if (BH1750.hasValue() == true) + { // non blocking reading + lux = BH1750.getLux(); +#if SERIAL_DEBUG +#if SERIAL_PRINT_SENSOR_VALUES + Serial.printf("BH1750 lux value : %.2f\n", lux); +#endif +#endif + BH1750.start(); + } + +#if BAT_TEST_ENABLE + // Read battery voltage + measuredvbat = analogReadMilliVolts(VBATPIN); + measuredvbat *= 2; // we divided by 2, so multiply back + measuredvbat /= 1000; // convert to volts! +#endif + +#if SERIAL_DEBUG +#if SERIAL_PRINT_SENSOR_VALUES + Serial.print("VBat: "); + Serial.println(measuredvbat); +#endif +#endif + delay(100); + } + + + // Uploads new telemetry to ThingsBoard using HTTP. + // See https://thingsboard.io/docs/reference/http-api/#telemetry-upload-api + // for more details + + if (!reconnect()) + { + return; + } + + if (!tb.connected()) + { +// Reconnect to the ThingsBoard server, +// if a connection was disrupted or has not yet been established +#if SERIAL_DEBUG + Serial.printf(CONNECTING_MSG, THINGSBOARD_SERVER, TOKEN); +#endif + if (!tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT)) + { +#if SERIAL_DEBUG + Serial.println("Failed to connect"); +#endif + return; + } + } + +#if AHT20_ENABLE +#if SERIAL_DEBUG + Serial.println("Sending temperature data..."); +#endif + tb.sendTelemetryData(TEMPERATURE_KEY, temperature.temperature); + +#if SERIAL_DEBUG + Serial.println("Sending humidity data..."); +#endif + tb.sendTelemetryData(HUMIDITY_KEY, humidity.relative_humidity); +#endif + +#if SGP40_ENABLE +#if SERIAL_DEBUG + Serial.println("Sending VOC data..."); +#endif + tb.sendTelemetryData(VOCC_KEY, voc_index); +#endif + +#if BH1750_ENABLE +#if SERIAL_DEBUG + Serial.println("Sending lux data..."); +#endif + tb.sendTelemetryData(LUX_KEY, lux); +#endif + +#if BAT_TEST_ENABLE +#if SERIAL_DEBUG + Serial.println("Sending battery..."); +#endif + tb.sendTelemetryData(BAT_KEY, measuredvbat); +#endif + +#if SERIAL_DEBUG + Serial.println("Sending version..."); +#endif + tb.sendTelemetryData(FIRMWARE_KEY, VERSION); + + tb.loop(); + +#if SERIAL_DEBUG + Serial.println("Going to sleep now"); +#endif + delay(100); + disableInternalPower(); + delay(100); + Serial.flush(); + esp_deep_sleep_start(); +#if SERIAL_DEBUG + Serial.println("This will never be printed"); +#endif +} + +void loop() {} + +void enableInternalPower() +{ +#if defined(NEOPIXEL_POWER) + pinMode(NEOPIXEL_POWER, OUTPUT); + digitalWrite(NEOPIXEL_POWER, HIGH); +#endif + +#if defined(NEOPIXEL_I2C_POWER) + pinMode(NEOPIXEL_I2C_POWER, OUTPUT); + digitalWrite(NEOPIXEL_I2C_POWER, HIGH); + delay(100); +#endif + +#if defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) + // turn on the I2C power by setting pin to opposite of 'rest state' + pinMode(PIN_I2C_POWER, INPUT); + delay(1); + bool polarity = digitalRead(PIN_I2C_POWER); + pinMode(PIN_I2C_POWER, OUTPUT); + digitalWrite(PIN_I2C_POWER, !polarity); + pinMode(NEOPIXEL_POWER, OUTPUT); + digitalWrite(NEOPIXEL_POWER, HIGH); +#endif +} + +void disableInternalPower() +{ +#if defined(NEOPIXEL_POWER) + pinMode(NEOPIXEL_POWER, OUTPUT); + digitalWrite(NEOPIXEL_POWER, LOW); +#endif + +#if defined(NEOPIXEL_I2C_POWER) + pinMode(NEOPIXEL_I2C_POWER, OUTPUT); + digitalWrite(NEOPIXEL_I2C_POWER, LOW); +#endif + +#if defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) + // turn off the I2C power by setting pin to rest state (off) + pinMode(PIN_I2C_POWER, INPUT); + pinMode(NEOPIXEL_POWER, OUTPUT); + digitalWrite(NEOPIXEL_POWER, LOW); +#endif +} + +void print_wakeup_reason() +{ + esp_sleep_wakeup_cause_t wakeup_reason; + + wakeup_reason = esp_sleep_get_wakeup_cause(); + + switch (wakeup_reason) + { + case ESP_SLEEP_WAKEUP_EXT0: +#if SERIAL_DEBUG + Serial.println("Wakeup caused by external signal using RTC_IO"); +#endif + break; + case ESP_SLEEP_WAKEUP_EXT1: +#if SERIAL_DEBUG + Serial.println("Wakeup caused by external signal using RTC_CNTL"); +#endif + break; + case ESP_SLEEP_WAKEUP_TIMER: +#if SERIAL_DEBUG + Serial.println("Wakeup caused by timer"); +#endif + break; + case ESP_SLEEP_WAKEUP_TOUCHPAD: +#if SERIAL_DEBUG + Serial.println("Wakeup caused by touchpad"); +#endif + break; + case ESP_SLEEP_WAKEUP_ULP: +#if SERIAL_DEBUG + Serial.println("Wakeup caused by ULP program"); +#endif + break; + default: +#if SERIAL_DEBUG + Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason); +#endif + break; + } +} + +/// @brief Initalizes WiFi connection, +// will endlessly delay until a connection has been successfully established +void InitWiFi() +{ +#if SERIAL_DEBUG + Serial.println("Connecting to AP ..."); +#endif + // Attempting to establish a connection to the given WiFi network + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + // Delay 500ms until a connection has been successfully established + delay(500); +#if SERIAL_DEBUG + Serial.print("."); +#endif + } +#if SERIAL_DEBUG + Serial.printf("\nConnected to AP : %s\n", WIFI_SSID); +#endif +#if ENCRYPTED + espClient.setCACert(ROOT_CERT); +#endif +} + +/// @brief Reconnects the WiFi uses InitWiFi if the connection has been removed +/// @return Returns true as soon as a connection has been established again +bool reconnect() +{ + // Check to ensure we aren't connected yet + const wl_status_t status = WiFi.status(); + if (status == WL_CONNECTED) + { + return true; + } + + // If we aren't establish a new connection to the given WiFi network + InitWiFi(); + return true; +} \ No newline at end of file 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