diff --git a/d1-mini-esp8266-board-sh_fixled.jpg b/d1-mini-esp8266-board-sh_fixled.jpg new file mode 100755 index 0000000..a6ae70a Binary files /dev/null and b/d1-mini-esp8266-board-sh_fixled.jpg differ diff --git a/esp8266-sensor.ino b/esp8266-sensor.ino new file mode 100644 index 0000000..57562f4 --- /dev/null +++ b/esp8266-sensor.ino @@ -0,0 +1,133 @@ +/************************* settings *********************************/ +#define SERIAL_DEBUG +//#define OLED_OUTPUT +//#define BATTERY_USE +//#define BME2_USE +//#define BME6_USE +//#define BH_USE +#define SHT_USE +//#define VEML_USE +//#define CCS_USE +const bool metric = true; +/************************* generic libs *********************************/ +#include +#include +#include +#include +#include +#include +/* + reset causes: + 0: + 1: normal boot + 2: reset pin + 3: software reset + 4: watchdog reset + + boot device: + 0: + 1: ram + 3: flash + + Label to GPIO NodeMCU + D0 = 16; + D1 = 5; SCL + D2 = 4; SDA + D3 = 0; + D4 = 2; + D5 = 14; + D6 = 12; + D7 = 13; + D8 = 15; + D9 = 3; + D10 = 1; builtinLED + + Wemos D1 mini + builtinLED = 2 + D0 = 16; + D1 = 5; SCL + D2 = 4; SDA + D3 = 0; + D4 = 2; builtinLED + D5 = 14; + D6 = 12; + D7 = 13; CCS INTERRUPT + D8 = 15; + RX = 3; + TX = 1; +*/ +/************************* state machine *********************************/ +//the charger board cuts at 2.5V +//state definitions +#define STATE_COLDSTART 0 +#define STATE_SLEEP_WAKE 1 +#define STATE_FIRMWARE 2 +#define STATE_MEASUREMENT 4 + +#define SLEEP_TIME 60*1000000UL // sleep intervalls in us (1min) +#define SLEEP_TIME_MEASUREMENT 1*SLEEP_TIME // sleep 60s(1min) after measurement +#define SLEEP_TIME_WIFI_TIMEOUT 1*SLEEP_TIME // sleep a minute +#ifdef BATTERY_USE +#define SLEEP_TIME_LOW_BAT 60*SLEEP_TIME // sleep 3600s(60min) after measurement +#define BATT_WARNING_VOLTAGE 270 // Voltage for Low-Bat warning in mV --rember the drop-out of the voltage regulator -- Be aware not to measure the VCC! +#define BATT_MEASUREMENT_OFFSET 20 //Meh, somewhere and somehow is something not that well... +#endif + +/************************* WiFi Client *********************************/ + +#define WLAN_SSID "IoT" +#define WLAN_PASS "TF0xJJHtMz5kJUdh3dqf" +const int WIFI_CONNECT_TIMEOUT = 10; // seconds - max time for wifi connect to router, if exceeded go to sleep + + +//ToDo Legacy IPv4 +IPAddress ip(10, 0, 4, 7); +IPAddress gateway(10, 0, 4, 1); +IPAddress subnet(255, 255, 255, 240); +IPAddress dns(10, 0, 4, 1); +IPAddress mqttBroker(10, 0, 4, 1); //failsafe +/********************* Firmware update **************************************/ +const int FW_VERSION = 10; +const char* fwUrlBase = "http://10.0.4.2/fota/"; +/************************* MQTT Broker Setup *********************************/ +const int MQTT_PORT = 1883; +const char MQTT_SERVER[] = "mqtt.koelner.dynvpn.de"; +char MQTT_CLIENTID[10]; +const char MQTT_USERNAME[] = "input"; +const char MQTT_PASSWORD[] = "tFnlKJu9"; +const char MQTT_WILL_TOPIC[] = "bathroom/sys/will"; +const int MQTT_WILL_QOS = 0; +const int MQTT_WILL_RETAIN = 0; +const char MQTT_WILL_MESSAGE[] = "Finally died..."; +/****************************** Feeds ***************************************/ +//ToDo: generate feed from CLIENTID and determine the ClientID from the ChipID +#if defined(BME2_USE) || defined(BME6_USE) +const char TEMPERATURE_FEED[] = "bathroom/sensors/temperature"; +const char PRESSURE_FEED[] = "bathroom/sensors/pressure"; +const char HUMIDITY_FEED[] = "bathroom/sensors/humidity"; +const char ABSHUMIDITY_FEED[] = "bathroom/sensors/abshumidity"; +#endif +#if defined(SHT_USE) +const char TEMPERATURE_FEED[] = "bathroom/sensors/temperature"; +const char HUMIDITY_FEED[] = "bathroom/sensors/humidity"; +const char ABSHUMIDITY_FEED[] = "bathroom/sensors/abshumidity"; +#endif +#ifdef BME6_USE +const char IAQ_FEED[] = "bathroom/sensors/iaq"; +const char IAQPREC_FEED[] = "bathroom/sensors/iaqprec"; +#endif +#ifdef VEML_USE +const char UVA_FEED[] = "bathroom/sensors/uva"; +const char UVB_FEED[] = "bathroom/sensors/uvb"; +const char UVINDEX_FEED[] = "bathroom/sensors/uvindex"; +#endif +#ifdef BH_USE +const char LIGHT_FEED[] = "bathroom/sensors/light"; +#endif +#ifdef CCS_USE +const char ECO2_FEED[] = "bathroom/sensors/eco2"; +const char TVOC_FEED[] = "bathroom/sensors/tvoc"; +#endif +#ifdef BATTERY_USE +const char BATTERY_FEED[] = "bathroom/sys/battery"; +#endif diff --git a/i2c_scan.ino b/i2c_scan.ino new file mode 100644 index 0000000..5d2b757 --- /dev/null +++ b/i2c_scan.ino @@ -0,0 +1,46 @@ +/*************************** I2C scan function **********************************/ +#ifdef SERIAL_DEBUG +void I2Cscan() +{ + // scan for i2c devices + byte error, address; + int nDevices; + + Serial.println(F("Scanning...")); + + nDevices = 0; + for (address = 1; address < 127; address++ ) + { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print(F("I2C device found at address 0x")); + if (address < 16) + Serial.print(F("0")); + Serial.print(address, HEX); + Serial.println(F(" !")); + + nDevices++; + } + else if (error == 4) + { + Serial.print(F("Unknown error at address 0x")); + if (address < 16) + Serial.print(F("0")); + Serial.println(address, HEX); + } + } + if (nDevices == 0) + Serial.println(F("No I2C devices found\n")); + else + Serial.println(F("done\n")); + +} +#endif + + diff --git a/main.ino b/main.ino new file mode 100644 index 0000000..39c6721 --- /dev/null +++ b/main.ino @@ -0,0 +1,654 @@ + +// The ESP8266 RTC memory is arranged into blocks of 4 bytes. The access methods read and write 4 bytes at a time, +// so the RTC data structure should be padded to a 4-byte multiple. +struct { + uint32_t crc32; // 4 bytes + uint8_t channel; // 1 byte, 5 in total + uint8_t bssid[6]; // 6 bytes, 11 in total + uint8_t txPow = 0; // 1 byte, 12 in total + IPAddress mqttServer=mqttBroker; //save the dns lookup +#ifdef CCS_USE + bool ccsInit = false; +#endif +} rtcData; +/************************* RTC Mem *********************************/ +#define WiFi_RTC 0 +/************************* SSD1306 SPI *********************************/ +#ifdef OLED_OUTPUT +#include +SSD1306 display(0x3c, D4, D3); +//#include "OLEDDisplayUi.h" +#include "DejaVu_Sans_Mono_18.h" +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D1 -> RES +// D2 -> DC +// D8 -> CS +SSD1306Spi display(D1, D2, D8); +OLEDDisplayUi ui( &display ); +#endif +/************************* BMP280 I2C *********************************/ +#ifdef BME2_USE +//https://github.com/finitespace/BME280#usage +#include +#include "EnvironmentCalculations.h" +BME280I2C bme; +BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); +BME280::PresUnit presUnit(BME280::PresUnit_hPa); +#endif +/************************* BMP680 I2C *********************************/ +#ifdef BME6_USE //ToDo +/* + //https://github.com/finitespace/BME280#usage + #include + #include "EnvironmentCalculations.h" + BME280I2C bme6; + const bool metric = true; + BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); + BME280::PresUnit presUnit(BME280::PresUnit_hPa); +*/ +#endif +/************************* SHTXX I2C *********************************/ +#ifdef SHT_USE +#include +SHTSensor sht(SHTSensor::SHT3X_ALT); +#endif +/************************* VEML6075 I2C *********************************/ +#ifdef VEML_USE +//https://github.com/schizobovine/VEML6075 +#include +VEML6075 uvMeter; +#endif +/************************* BH1750 I2C *********************************/ +#ifdef BH_USE +//https://github.com/claws/BH1750 +#include +BH1750 lightMeter; +#endif +/************************* CCS811 I2C *********************************/ +#ifdef CCS_USE +//https://github.com/LucAce/CCS811 +#include "CCS811.h" + +// Error ID code +enum { + CCS811_ERROR_CODE_WRITE_REG_INVALID = B00000001, + CCS811_ERROR_CODE_READ_REG_INVALID = B00000010, + CCS811_ERROR_CODE_MEASMODE_INVALID = B00000100, + CCS811_ERROR_CODE_MAX_RESISTANCE = B00001000, + CCS811_ERROR_CODE_HEATER_FAULT = B00010000, + CCS811_ERROR_CODE_HEATER_SUPPLY = B00100000, +}; +#ifdef SERIAL_DEBUG +//***************************************************************************** +// Function: errorDecode +// Environment decodes the single bits from the error return +//***************************************************************************** +void errorDecode(uint8_t error_code) { + if (bitRead(error_code, CCS811_ERROR_CODE_WRITE_REG_INVALID)) Serial.println(F("ERROR: Wrong write request received")); + if (bitRead(error_code, CCS811_ERROR_CODE_READ_REG_INVALID)) Serial.println(F("ERROR: Wrong read request received")); + if (bitRead(error_code, CCS811_ERROR_CODE_MEASMODE_INVALID)) Serial.println(F("ERROR: Wrong meausrement mode request received")); + if (bitRead(error_code, CCS811_ERROR_CODE_MAX_RESISTANCE)) Serial.println(F("ERROR: Maximum sensor resistance reached!")); + if (bitRead(error_code, CCS811_ERROR_CODE_HEATER_FAULT)) Serial.println(F("ERROR: Heater current out of range!")); + if (bitRead(error_code, CCS811_ERROR_CODE_HEATER_SUPPLY)) Serial.println(F("ERROR: Heater supply voltage out of bounce!")); +} +#endif + +// CCS811 I2C Interface +CCS811 ccs; +#define CCSINTERRUPT D7 //GPIO13 +#define CCSRESET D6 //GPIO12 +#endif + + +void messageReceived(String &topic, String &payload) { +#ifdef SERIAL_DEBUG + Serial.println("incoming: " + topic + " - " + payload); +#endif +} +/************** generic settings **************************/ +void sleep(int duration, RFMode type) { +#ifdef SERIAL_DEBUG + //flush UART (pg. 11 esp8266 low power solution) + Serial.flush(); + delay(10); +#endif + ESP.deepSleep(duration, type); + yield(); + delay(100); +} +uint32_t calculateCRC32( const uint8_t *data, size_t length ) { + uint32_t crc = 0xffffffff; + while ( length-- ) { + uint8_t c = *data++; + for ( uint32_t i = 0x80; i > 0; i >>= 1 ) { + bool bit = crc & 0x80000000; + if ( c & i ) { + bit = !bit; + } + + crc <<= 1; + if ( bit ) { + crc ^= 0x04c11db7; + } + } + } + + return crc; +} +uint32_t voltageDivCorrelation(uint32_t u3) { +#ifdef BATTERY_USE + const unsigned int offset = 8; + + if (u3 < offset) { + return BATT_MEASUREMENT_OFFSET; + } +#endif + //translate the 10bit value to reference voltage + //uint32_t adc = map (u3-offset, 0, 1023, 0 , 110); + uint32_t r1 = 390; + uint32_t r2 = 220; + uint32_t r3 = 100; + uint32_t rges = r1 + r2 + r3; + uint32_t uges = u3 * rges / r3; + //TODO why?! + uges = uges / 10; + return uges; +} + +uint32_t adc = 0; + +#if defined(BME2_USE) || defined (BME6_USE) +float temperature(NAN); +float humidity(NAN); +float absHumidity(NAN); +float pressure(NAN); +float dew(NAN); +String temp_str; +String hum_str; +String absHum_str; +String pres_str; +char temp[7]; +char hum[8]; +char absHum[8]; +char pres[7]; +#endif +#ifdef BME6_USE +float iaqF(NAN); +float iaqprecF(NAN); +String iaq_str; +String iaqprec_str; +char iaq[5]; +char iaqprec[5]; +#endif +#if defined(SHT_USE) || defined(SI7021_USE) +float temperature(NAN); +float humidity(NAN); +float absHumidity(NAN); +float dew(NAN); +String temp_str; +String hum_str; +String absHum_str; +char temp[7]; +char hum[8]; +char absHum[8]; +#endif +#ifdef BH_USE +float vislight(NAN); +String light_str; +char light[8]; +#endif +#ifdef VEML_USE +float uvalight(NAN); +float uvblight(NAN); +float uvi(NAN); +String uva_str; +String uvb_str; +String uvindex_str; +char uva[8]; +char uvb[8]; +char uvindex[5]; +#endif +#ifdef CCS_USE +float eco2(NAN); +String eco2_str; +char eco2C[8]; +float tvoc(NAN); +String tvoc_str; +char tvocC[8]; +#endif +#ifdef BATTERY_USE +float battery(NAN); +String batt_str; +char batt[7]; +#endif + +bool wifiCheck = false; +bool mqttCheck = false; +bool bme280Check = false; +bool bme680Check = false; +bool veml6075Check = false; +bool bh1750Check = false; +bool ccs811Check = false; +bool sht30Check = false; + +WiFiClient mqttSocket; +MQTTClient mqttClient; +/*************************** Sketch Code ************************************/ +void initSensors() { + //assign i2c pins to different pins. SPI is hardware defined. | Wire.begin(SDA,SCL) | CLK D3 | DATA D4 --> be aware that a lot libs do that internally + //Wire.begin(D4, D3); //nodemcu + Wire.begin(D2, D1); //wemos D1 mini + /*********************SSD1306 OLED init **************************************/ +#ifdef OLED_OUTPUT + display.init(); + display.flipScreenVertically(); + //display.setFont(DejaVu_Sans_Mono_18); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.displayOn(); +#endif + + /*********************BH1750 I2C init **************************************/ +#ifdef BH_USE +#ifdef SERIAL_DEBUG + Serial.print("\tBH1750 init...\t"); +#endif + +#ifdef OLED_OUTPUT + // clear the display + display.clear(); + display.drawStringMaxWidth(0, 0, 128, "BH1750 init" ); + // write the buffer to the display + display.display(); +#endif + if (!lightMeter.begin(BH1750::ONE_TIME_HIGH_RES_MODE)) + { +#ifdef SERIAL_DEBUG + Serial.println("no BH1750 anwering"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "BH1750 FAILED" ); + // write the buffer to the display + display.display(); +#endif + } + else { + bh1750Check = true; +#ifdef SERIAL_DEBUG + Serial.println("BH1750 ready."); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "BH1750 ready" ); + // write the buffer to the display + display.display(); +#endif + } +#endif + /*********************VEML6075 I2C init **************************************/ +#ifdef VEML_USE +#ifdef SERIAL_DEBUG + Serial.print(F("\tVEML6075 init...\t")); +#endif + +#ifdef OLED_OUTPUT + // clear the display + display.clear(); + display.drawStringMaxWidth(0, 0, 128, "VEML6075 init" ); + // write the buffer to the display + display.display(); +#endif + uvMeter.begin(); + if (!uvMeter.getDevID()) + { + delay(10); +#ifdef SERIAL_DEBUG + Serial.println(F("no VEML6075 detected!")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "VEML6075 FAILED" ); + // write the buffer to the display + display.display(); +#endif + + } + else { + veml6075Check = true; +#ifdef SERIAL_DEBUG + Serial.println(F("VEML6075 ready.")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "VEML6075 ready" ); + // write the buffer to the display + display.display(); +#endif + } +#endif + /*********************BME680 I2C init **************************************/ +#ifdef BME6_USE +#ifdef SERIAL_DEBUG + Serial.print(F("\tBME680 init...\t")); +#endif + +#ifdef OLED_OUTPUT + // clear the display + display.clear(); + display.drawStringMaxWidth(0, 0, 128, "BME680 init" ); + // write the buffer to the display + display.display(); +#endif + if (!bme6.begin()) + { + delay(10); +#ifdef SERIAL_DEBUG + Serial.println(F("no BME680 detected!")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "BME680 FAILED" ); + // write the buffer to the display + display.display(); +#endif + + } + else { + bme680Check = true; +#ifdef SERIAL_DEBUG + Serial.println(F("BME680 ready.")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "BME680 ready" ); + // write the buffer to the display + display.display(); +#endif + } +#endif + +/*********************BME280 I2C init **************************************/ +#ifdef BME2_USE +#ifdef SERIAL_DEBUG +Serial.print(F("\tBME280 init...\t")); +#endif + +#ifdef OLED_OUTPUT + // clear the display + display.clear(); + display.drawStringMaxWidth(0, 0, 128, "BME280 init" ); + // write the buffer to the display + display.display(); +#endif + if (!bme.begin()) + { +#ifdef SERIAL_DEBUG + Serial.println(F("no BME280 detected!")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "BME280 FAILED" ); + // write the buffer to the display + display.display(); +#endif + + } + else { + bme280Check = true; +#ifdef SERIAL_DEBUG + Serial.println(F("BME280 ready.")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "BME280 ready" ); + // write the buffer to the display + display.display(); +#endif + } +#endif +/*********************SHT3X I2C init **************************************/ +#ifdef SHT_USE +#ifdef SERIAL_DEBUG +Serial.print(F("\tSHT30 init...\t")); +#endif + +#ifdef OLED_OUTPUT + // clear the display + display.clear(); + display.drawStringMaxWidth(0, 0, 128, "SHT30 init" ); + // write the buffer to the display + display.display(); +#endif + if (!sht.init()) + { +#ifdef SERIAL_DEBUG + Serial.println(F("no SHT30 detected!")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "SHT30 FAILED" ); + // write the buffer to the display + display.display(); +#endif + + } + else { + sht30Check = true; + sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // only supported by SHT3x + yield(); +#ifdef SERIAL_DEBUG + Serial.println(F("SHT30 ready.")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "SHT30 ready" ); + // write the buffer to the display + display.display(); +#endif + } +#endif + /*********************CCS811 I2C init **************************************/ +#ifdef CCS_USE + // Extend I2C clock stretch timeout (See Notes) + //Wire.setClockStretchLimit(500); + Wire.setClockStretchLimit(200000); + pinMode(CCSINTERRUPT, INPUT); + //pinMode(CCSRESET, OUTPUT); + delay(10); + digitalWrite(CCSRESET, HIGH); + delay(10); +#ifdef SERIAL_DEBUG + Serial.print(F("\tCCS811 init...\t")); +#endif + +#ifdef OLED_OUTPUT + // clear the display + display.clear(); + display.drawStringMaxWidth(0, 0, 128, "CCS811 init" ); + // write the buffer to the display + display.display(); +#endif + if (!ccs.begin()) + { + delay(10); +#ifdef SERIAL_DEBUG + Serial.println(F("no CCS811 detected!")); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "CCS811 FAILED" ); + // write the buffer to the display + display.display(); +#endif + + } + else { + ccs811Check = true; + if (!rtcData.ccsInit) { + Serial.println(F("CCS811 not initialised")); + rtcData.ccsInit = true; + } + //set CCS811 measurement mode + ccs.writeMeasModeRegister(CCS811_DRIVE_MODE_1SEC , 1 , 0); + //Serial.println(F("INFO: Set measurement refresh to 1s")); + //enable Interrupt + ccs.enableInterrupt(); + //Serial.println(F("INFO: Enable interrupt")); + /*while (!digitalRead(CCSINTERRUPT)) + { + delay(200); + Serial.print("."); + } + while (!ccs.isDATA_READY()) + { + delay(100); + ccs.readStatusRegister(); + Serial.print("."); + }; + */ + // ToDo why and reasons + //float temp = ccs.calculateTemperature(); + ccs.setTempOffset(temperature - 25.0); + +#ifdef SERIAL_DEBUG + Serial.println(F("CCS811 ready.")); + /* + // Print CCS811 sensor information + Serial.println(F("CCS811 Sensor Enabled:")); + Serial.print(F("Hardware ID: 0x")); + Serial.println(ccs.getHWID(), HEX); + Serial.print(F("Hardware Version: 0x")); + Serial.println(ccs.getHWVersion(), HEX); + Serial.print(F("Firmware Boot Version: 0x")); + Serial.println(ccs.getFWBootVersion(), HEX); + Serial.print(F("Firmware App Version: 0x")); + Serial.println(ccs.getFWAppVersion(), HEX); + Serial.println(); */ +#endif + + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, "CCS811 ready" ); + // write the buffer to the display + display.display(); +#endif + } +#endif + + /******************** i2c init END ****************/ +} + + + +void setup() { + // if serial is not initialized all following calls to serial end dead. +#ifdef SERIAL_DEBUG + Serial.flush(); + Serial.begin(115200); + while (!Serial) { + delay(1); + } // Wait + Serial.print(F("\nReset reason : \t\t\t")); + Serial.println(ESP.getResetReason()); +#endif + WiFi.forceSleepBegin(); // send wifi to sleep to reduce power consumption + yield(); + delay(100); +#ifdef BATTERY_USE + //check battery status + //Battery Voltage Measurement + //pinMode(D6, OUTPUT); + //digitalWrite(D6, HIGH); //activate transistor + //yield(); + //delay(10); + pinMode(A0, INPUT); + yield(); + adc = analogRead(A0); + yield(); + delay(10); +#ifdef SERIAL_DEBUG + Serial.print(F("ADC_measurement: \t\t\t")); + Serial.println (String(adc)); + Serial.print(F("VoltageDividerCorrelation in V: \t")); + Serial.println(String(voltageDivCorrelation(adc) - BATT_MEASUREMENT_OFFSET)); +#endif + yield(); + delay(10); + // go to sleep if to low + if (voltageDivCorrelation(adc) - BATT_MEASUREMENT_OFFSET < BATT_WARNING_VOLTAGE) { +#ifdef SERIAL_DEBUG + Serial.println(F("Low Battery Voltage. Got to sleep")); +#endif + sleep(SLEEP_TIME_LOW_BAT, WAKE_RF_DISABLED); + } +#endif + + //go further if not + //init wifi + wifiCheck = wifi_connect(); + if (wifiCheck) { + //connect mqtt + if (!mqttClient.loop()) { + //connect mqtt + mqttCheck = mqtt_connect(); + } + if (mqttCheck) { +#ifdef SERIAL_DEBUG + Serial.println(F("Sensor init routine")); + yield(); +#endif + initSensors(); + yield(); +#ifdef SERIAL_DEBUG + Serial.println(F("--------measurement--------")); + yield(); +#endif + measurement(); + yield(); +#ifdef SERIAL_DEBUG + Serial.println(F("---------------------------")); + yield(); +#endif + mqtt_disconnect(); + yield(); + } + //firmware update check + /*if (firmware_available() + //{ + // get firmware + }*/ + //regular --check whether 5min leads to wifi timeout + wifi_disconnect(); + yield(); + if (mqttCheck) { +#ifdef SERIAL_DEBUG + Serial.println(F("regular sleep")); + Serial.println(F("***********************************************************")); +#endif + sleep(SLEEP_TIME_MEASUREMENT, WAKE_RF_DEFAULT); + + } + else { + //ToDo Do more useful stuff +#ifdef SERIAL_DEBUG + Serial.println(F("mqtt failed - wifi timeout sleep")); +#endif + sleep(SLEEP_TIME_WIFI_TIMEOUT, WAKE_RF_DEFAULT); + + } + } + //no wifi available + else { +#ifdef SERIAL_DEBUG + Serial.println(F("Wifi failed")); +#endif + sleep(SLEEP_TIME_WIFI_TIMEOUT, WAKE_RFCAL); + } +} + +void loop() { + yield(); +} diff --git a/measurement.ino b/measurement.ino new file mode 100644 index 0000000..414cc0b --- /dev/null +++ b/measurement.ino @@ -0,0 +1,774 @@ + +/********************* Measurement **************************************/ +void measurement() { + +#ifdef BME2_USE + if (bme280Check) { + bme.read(pressure, temperature, humidity, tempUnit, presUnit); + yield(); + delay(10); + bme.read(pressure, temperature, humidity, tempUnit, presUnit); + yield(); + } +#endif + +#ifdef SHT_USE + if (sht30Check) { + if (sht.readSample()) + { + yield(); + humidity = sht.getHumidity(); + yield(); + temperature = sht.getTemperature(); + yield(); + } + } +#endif + +#ifdef SI7021_USE + if (si7021Check) { + // Measure Relative Humidity from the HTU21D or Si7021 + humidity = THMeter.getRH(); + yield(); + delay(10); + // Measure Temperature from the HTU21D or Si7021 + temperature = THMeter.getTemp(); + yield(); + //ToDo Heater https://www.silabs.com/community/sensors/forum.topic.html/when_si7021_is_neede-8a2a + if (humidity >= 80.0) { + THMeter.heaterOn(); + delay(50); + //THMeter.heaterOff(); + } + } +#endif + +#ifdef BH_USE + if (bh1750Check) { + //vislight = float(lightMeter.readLightLevel(true, false)); + vislight = lightMeter.readLightLevel(true); + yield(); + if (vislight > 40000.0 ) { + if (lightMeter.setMTreg(32)) { + yield(); + + } + } + else { + if (vislight > 10.0 && vislight <= 40000.0 && lightMeter.setMTreg(69)) + { + yield(); + } + } + if (vislight < 10.0) { + if (lightMeter.setMTreg(254)) { + yield(); + } + } + yield(); + if (vislight < 0.0) + { + vislight = -1; + } + else { + vislight = lightMeter.readLightLevel(true); + yield(); + } + + } +#endif + +#ifdef VEML_USE + if (veml6075Check) { + //uvMeter.triggerMeasurement(); + delay(10); + uvMeter.poll(); + yield(); + uvalight = float(uvMeter.getUVA()); + yield(); + uvblight = float(uvMeter.getUVB()); + yield(); + uvi = uvMeter.getUVIndex(); + yield(); + uvMeter.sleep(true); + yield(); + } +#endif +#ifdef CCS_USE + if (ccs811Check) { + yield(); + uint8_t ccs_error = 0; + // set environmental data + ccs.setEnvData(temperature, humidity); + while (digitalRead(CCSINTERRUPT) == HIGH) + { + delay(100); + Serial.print("."); + } + // Read the sensor data, this updates multiple fields + // in the CCS811 library + ccs.readAlgResultDataRegister(); + if (ccs.hasERROR()) { + ccs_error = ccs.getERROR_ID(); +#ifdef SERIAL_DEBUG + Serial.println(F("ERROR: CCS811 Error Flag Set")); + Serial.print(F("INFO: CCS811 Error Register = ")); + Serial.println(ccs_error); + errorDecode(ccs_error); + Serial.println(); +#endif + ccs811Check = false; + } + if (ccs811Check) { + // Data is ready + eco2 = float(ccs.geteCO2()); + tvoc = float(ccs.getTVOC()); + } +#ifdef SERIAL_DEBUG + //Serial.println(F("ERROR: CCS811 Data Not Ready")); +#endif + } +#endif + /************************** ADC Measurement *********************************/ +#ifdef BATTERY_USE + adc = analogRead(A0); +#ifdef SERIAL_DEBUG + Serial.println("ADC Read:\t" + String(adc) ); +#endif + yield(); + delay(10); + batt_str = String(float(voltageDivCorrelation(adc) - BATT_MEASUREMENT_OFFSET) / 100); + batt_str.toCharArray(batt, batt_str.length() + 1); +#ifdef SERIAL_DEBUG + Serial.print("Battery Read:\t" + batt_str + " V" + "\t\t\t"); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish Battery: \t\t"); +#endif + if (! mqttClient.publish(BATTERY_FEED, batt, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + } + else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + } + yield(); + mqttClient.loop(); + yield(); + delay(10); +#endif + /********************* Temperature **************************************/ +#ifdef BME2_USE + if (bme280Check) { +#ifdef SERIAL_DEBUG + Serial.print("Temperature:\t"); + Serial.print(temperature); + Serial.print(" °" + String(metric ? 'C' : 'F') ); + Serial.print("\t\t\t"); +#endif + temp_str = String(temperature); + temp_str.toCharArray(temp, temp_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 0, 128, " " + temp_str + "°" + String(metric ? 'C' : 'F')); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish Temperature: \t"); +#endif + if (! mqttClient.publish(TEMPERATURE_FEED, temp, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + /********************* Pressure **************************************/ +#ifdef SERIAL_DEBUG + Serial.print("Pressure:\t"); + Serial.print(pressure); + Serial.print(" hPa"); + Serial.print("\t\t"); +#endif + pres_str = String(pressure); + pres_str.toCharArray(pres, pres_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 16, 128, pres_str + " hPa"); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish Pressure: \t"); +#endif + if (! mqttClient.publish(PRESSURE_FEED, pres, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 16, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 16, 128, "o"); +#endif + + } + mqttClient.loop(); + yield(); + delay(10); + /********************* Humidity **************************************/ +#ifdef SERIAL_DEBUG + Serial.print("Humidity:\t"); + Serial.print(humidity); + Serial.print(" %" ); + Serial.print("\t\t\t"); +#endif + hum_str = String(humidity); + hum_str.toCharArray(hum, hum_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, " " + String(hum) + " %"); +#endif +#ifdef SERIAL_DEBUG + Serial.print("Publish Humidity: \t"); +#endif + + if (! mqttClient.publish(HUMIDITY_FEED, hum, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "x"); +#endif + + } + else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + } +#endif + /********************* Temperature **************************************/ +#ifdef SHT_USE + if (sht30Check) { +#ifdef SERIAL_DEBUG + Serial.print("Temperature:\t"); + Serial.print(temperature); + Serial.print(" °" + String(metric ? 'C' : 'F') ); + Serial.print("\t\t\t"); +#endif + temp_str = String(temperature); + temp_str.toCharArray(temp, temp_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 0, 128, " " + temp_str + "°" + String(metric ? 'C' : 'F')); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish Temperature: \t"); +#endif + if (! mqttClient.publish(TEMPERATURE_FEED, temp, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + + /********************* Humidity **************************************/ +#ifdef SERIAL_DEBUG + Serial.print("Humidity:\t"); + Serial.print(humidity); + Serial.print(" %" ); + Serial.print("\t\t\t"); +#endif + hum_str = String(humidity); + hum_str.toCharArray(hum, hum_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, " " + String(hum) + " %"); +#endif +#ifdef SERIAL_DEBUG + Serial.print("Publish Humidity: \t"); +#endif + + if (! mqttClient.publish(HUMIDITY_FEED, hum, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "x"); +#endif + + } + else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + } +#endif //SHT_USE + /********************* Temperature **************************************/ +#ifdef SI7021_USE + if (si7021Check) { +#ifdef SERIAL_DEBUG + Serial.print("Temperature:\t"); + Serial.print(temperature); + Serial.print(" °" + String(metric ? 'C' : 'F') ); + Serial.print("\t\t\t"); +#endif + temp_str = String(temperature); + temp_str.toCharArray(temp, temp_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 0, 128, " " + temp_str + "°" + String(metric ? 'C' : 'F')); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish Temperature: \t"); +#endif + if (! mqttClient.publish(TEMPERATURE_FEED, temp, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + + /********************* Humidity **************************************/ +#ifdef SERIAL_DEBUG + Serial.print("Humidity:\t"); + Serial.print(humidity); + Serial.print(" %" ); + Serial.print("\t\t\t"); +#endif + hum_str = String(humidity); + hum_str.toCharArray(hum, hum_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, " " + String(hum) + " %"); +#endif +#ifdef SERIAL_DEBUG + Serial.print("Publish Humidity: \t"); +#endif + + if (! mqttClient.publish(HUMIDITY_FEED, hum, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "x"); +#endif + + } + else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + } +#endif + /********************* UVA **************************************/ +#ifdef VEML_USE + if (veml6075Check) { +#ifdef SERIAL_DEBUG + Serial.print("UVA:\t\t"); + Serial.print(uvalight); + Serial.print(" Ctr per mW/cm²" ); + Serial.print("\t"); +#endif + uva_str = String(uvalight); + uva_str.toCharArray(uva, uva_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, " " + String(uva) + " "); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish UVA: \t\t"); +#endif + + if (! mqttClient.publish(UVA_FEED, uva, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "x"); +#endif + + } + else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "o"); +#endif + + } + mqttClient.loop(); + yield(); + delay(10); + /********************* UVB **************************************/ +#ifdef SERIAL_DEBUG + Serial.print("UVB:\t\t"); + Serial.print(uvblight); + Serial.print(" Ctr per mW/cm²" ); + Serial.print("\t"); +#endif + uvb_str = String(uvblight); + uvb_str.toCharArray(uvb, uvb_str.length() + 1); + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, " " + String(uvb) + " "); +#endif +#ifdef SERIAL_DEBUG + Serial.print("Publish UVB: \t\t"); +#endif + if (! mqttClient.publish(UVB_FEED, uvb, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "x"); +#endif + + } + else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + /********************* UVIndex **************************************/ +#ifdef SERIAL_DEBUG + Serial.print("UVIndex:\t\t"); + Serial.print(uvi); + Serial.print("\t\t\t"); +#endif + uvindex_str = String(uvi); + uvindex_str.toCharArray(uvindex, uvindex_str.length() + 1); + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, " " + String(uvindex) + " "); +#endif +#ifdef SERIAL_DEBUG + Serial.print("Publish UVIndex: \t\t"); +#endif + if (! mqttClient.publish(UVINDEX_FEED, uvindex, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "x"); +#endif + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "o"); +#endif + } + mqttClient.loop(); + delay(10); + } +#endif + /********************* visible light **************************************/ +#ifdef BH_USE + if (bh1750Check && vislight >= 0.0) { +#ifdef SERIAL_DEBUG + Serial.print("Light:\t\t"); + Serial.print(vislight); + Serial.print(" lx" ); + Serial.print("\t\t"); +#endif + light_str = String(int(vislight)); + light_str.toCharArray(light, light_str.length() + 1); + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, " " + String(light) + " lx"); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("\tPublish Light: \t\t"); +#endif + + if (!mqttClient.publish(LIGHT_FEED, light, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 32, 128, "o"); +#endif + } + mqttClient.loop(); + delay(10); + } +#endif + /********************* Absolute Humidity **************************************/ +#ifdef SI7021_USE + if (si7021Check) { + absHumidity = EnvironmentCalculations::AbsoluteHumidity(temperature, humidity, EnvironmentCalculations::TempUnit_Celsius); +#ifdef SERIAL_DEBUG + Serial.print("abs Humidity:\t"); + Serial.print(absHumidity); + Serial.print(" g/m^3"); + Serial.print("\t\t"); +#endif + absHum_str = String(absHumidity); + absHum_str.toCharArray(absHum, absHum_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 0, 128, " " + absHum_str + "g/m^3" ); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish abs Humidity: \t"); +#endif + if (! mqttClient.publish(ABSHUMIDITY_FEED, absHum, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + } +#endif + + /********************* Absolute Humidity **************************************/ +#ifdef BME2_USE + if (bme280Check) { + absHumidity = EnvironmentCalculations::AbsoluteHumidity(temperature, humidity, EnvironmentCalculations::TempUnit_Celsius); +#ifdef SERIAL_DEBUG + Serial.print("abs Humidity:\t"); + Serial.print(absHumidity); + Serial.print(" g/m^3"); + Serial.print("\t\t"); +#endif + absHum_str = String(absHumidity); + absHum_str.toCharArray(absHum, absHum_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 0, 128, " " + absHum_str + "g/m^3" ); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish abs Humidity: \t"); +#endif + if (! mqttClient.publish(ABSHUMIDITY_FEED, absHum, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + } +#endif + + /********************* eCO2 **************************************/ +#ifdef CCS_USE + //fix sensors internal wait delay + if (ccs811Check && eco2 > 0.00) { +#ifdef SERIAL_DEBUG + Serial.print("eCO2:\t\t"); + Serial.print(eco2); + Serial.print(" ppm"); + Serial.print("\t\t"); +#endif + eco2_str = String(eco2); + eco2_str.toCharArray(eco2C, eco2_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 0, 128, " " + eco2_str + "ppm" ); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish eCO2: \t\t"); +#endif + if (! mqttClient.publish(ECO2_FEED, eco2C, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + } +#endif + /********************* TVOC **************************************/ +#ifdef CCS_USE + //fix sensors internal wait delay + if (ccs811Check && eco2 > 0.00) { +#ifdef SERIAL_DEBUG + Serial.print("TVOC:\t\t"); + Serial.print(tvoc); + Serial.print(" ppb"); + Serial.print("\t\t"); +#endif + tvoc_str = String(tvoc); + tvoc_str.toCharArray(tvocC, tvoc_str.length() + 1); +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 0, 128, " " + tvoc_str + "ppb" ); +#endif + +#ifdef SERIAL_DEBUG + Serial.print("Publish TVOC: \t\t"); +#endif + if (! mqttClient.publish(TVOC_FEED, tvocC, true, 1) || !mqttClient.loop()) { +#ifdef SERIAL_DEBUG + Serial.println("Failed!"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "x"); +#endif + + } else { +#ifdef SERIAL_DEBUG + Serial.println("OK"); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(110, 0, 128, "o"); +#endif + } + mqttClient.loop(); + yield(); + delay(10); + } +#endif + /************************************ END *******************************/ + mqttClient.loop(); + yield(); + delay(10); + return; +} diff --git a/mqtt.ino b/mqtt.ino new file mode 100755 index 0000000..1aa040a --- /dev/null +++ b/mqtt.ino @@ -0,0 +1,80 @@ +/*********************MQTT connect **************************************/ +bool mqtt_connect() { + const int8_t maxRetryMqtt = 5; + int8_t currRetryMqtt = 0; + char mqttBrokerBuf[16]; //ToDo Legacy IPv4 + + mqttBroker.toString().toCharArray( mqttBrokerBuf, mqttBroker.toString().length()+1 ); +#ifdef SERIAL_DEBUG + //Serial.println("MQTT Status: " + String(mqttClient.state())); +#endif + + if (mqttClient.connected()) { + mqttCheck = true; + return true; + } + +#ifdef SERIAL_DEBUG + Serial.println("Connecting to MQTT Broker...\t"+ String(mqttBrokerBuf)); +#endif + +#ifdef OLED_OUTPUT + display.clear(); + //display.drawStringMaxWidth(0, 0, 128, "MQTT: conn"); +#endif + //set options, may be obsolete + //mqttClient.setOptions(2, true, 750); + mqttClient.begin(mqttBrokerBuf, MQTT_PORT, mqttSocket); + mqttClient.onMessage(messageReceived); + mqttClient.setWill(MQTT_WILL_TOPIC, MQTT_WILL_MESSAGE, MQTT_WILL_RETAIN, MQTT_WILL_QOS); + bool retVal = mqttClient.connect(MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD); + delay(10); + while (currRetryMqtt <= maxRetryMqtt && !mqttClient.connect(MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD)) + { // connect will return 0 for connected + currRetryMqtt++; + if (mqttClient.connected()) { + currRetryMqtt=0; + return true; + } + else { + #ifdef SERIAL_DEBUG + if (currRetryMqtt == 1) { + Serial.print("Retrying MQTT connection in 1 seconds \t."); + } + else { + Serial.print("."); + } + #endif + delay(1000);// wait 1 seconds + } + } + retVal = mqttClient.connected(); + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, "MQTT: Error " + String(retVal)); + display.display(); +#endif + +#ifdef OLED_OUTPUT + display.clear(); +#endif + return retVal; +} + +bool mqtt_disconnect() { + bool retVal = mqttClient.disconnect(); + delay(10); + mqttClient.loop(); + delay(10); +#ifdef SERIAL_DEBUG + if ( retVal ) { + Serial.println("mqtt disconnected"); + } + else { + Serial.println("mqtt NOT disconnected"); + } +#endif + return retVal; +} + + diff --git a/ota.ino b/ota.ino new file mode 100644 index 0000000..7381d53 --- /dev/null +++ b/ota.ino @@ -0,0 +1,69 @@ +//String getMAC() +//{ +// uint8_t mac[6]= {00, 00, 00, 00, 00, 00}; +// char result[14]; +// +// snprintf( result, sizeof( result ), "%02x%02x%02x%02x%02x%02x", mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ] ); +// +// return String( result ); +//} +// +//void checkForUpdates() { +// String mac = getMAC(); +// String fwURL = String( fwUrlBase ); +// fwURL.concat( mac ); +// String fwVersionURL = fwURL; +// fwVersionURL.concat( ".version" ); +// +// Serial.println( "Checking for firmware updates." ); +// Serial.print( "MAC address: " ); +// Serial.println( mac ); +// Serial.print( "Firmware version URL: " ); +// Serial.println( fwVersionURL ); +// +// WiFiClient ota; +// +// HTTPClient httpClient; +// httpClient.begin( ota, fwVersionURL ); +// int httpCode = httpClient.GET(); +// if( httpCode == 200 ) { +// String newFWVersion = httpClient.getString(); +// +// Serial.print( "Current firmware version: " ); +// Serial.println( FW_VERSION ); +// Serial.print( "Available firmware version: " ); +// Serial.println( newFWVersion ); +// +// int newVersion = newFWVersion.toInt(); +// +// if( newVersion > FW_VERSION ) { +// Serial.println( "Preparing to update" ); +// +// String fwImageURL = fwURL; +// fwImageURL.concat( ".bin" ); +// t_httpUpdate_return ret = ESPhttpUpdate.update( fwImageURL ); +// +// switch(ret) { +// case HTTP_UPDATE_FAILED: +// Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); +// break; +// +// case HTTP_UPDATE_NO_UPDATES: +// Serial.println("HTTP_UPDATE_NO_UPDATES"); +// break; +// case HTTP_UPDATE_OK: +// Serial.println("HTTP_UPDATE_OK"); +// break; +// } +// } +// else { +// Serial.println( "Already on latest version" ); +// } +// } +// else { +// Serial.print( "Firmware version check failed, got HTTP response code " ); +// Serial.println( httpCode ); +// } +// httpClient.end(); +//} +// diff --git a/wemos-d1-mini-shematics.jpg b/wemos-d1-mini-shematics.jpg new file mode 100755 index 0000000..983de10 Binary files /dev/null and b/wemos-d1-mini-shematics.jpg differ diff --git a/wifi.ino b/wifi.ino new file mode 100644 index 0000000..6fef19d --- /dev/null +++ b/wifi.ino @@ -0,0 +1,156 @@ +/*********************Wifi Client init **************************************/ +bool resolveHosts(bool rtcValid) { + if (rtcValid && rtcData.mqttServer.isV4()) { + mqttBroker = rtcData.mqttServer; + } + else { + //DNS lookup + if(WiFi.hostByName(MQTT_SERVER, mqttBroker)) { + rtcData.mqttServer = mqttBroker; + } + else { + return false; + } + } + return true; +} +bool wifi_connect() { + bool rtcValid = false; + // Try to read WiFi settings from RTC memory + if( ESP.rtcUserMemoryRead( WiFi_RTC, (uint32_t*)&rtcData, sizeof( rtcData ) ) ) { + // Calculate the CRC of what we just read from RTC memory, but skip the first 4 bytes as that's the checksum itself. + uint32_t crc = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 ); + if( crc == rtcData.crc32 ) { + rtcValid = true; + } + #ifdef SERIAL_DEBUG + Serial.println("\trtcData.channel: \t\t" + String(rtcData.channel)); + Serial.println("\trtcData.txPow: \t\t" + String(rtcData.txPow)); + //Serial.println("\trtcData.mqttServer:\t" + rtcData.mqttServer); + #endif + } +#ifdef OLED_OUTPUT + display.clear(); + display.drawStringMaxWidth(0, 0, 128, "WiFi init" ); + display.display(); + display.drawStringMaxWidth(0, 16, 128, "WiFi: " + String(WLAN_SSID)); + display.display(); +#endif + +#ifdef SERIAL_DEBUG + Serial.println(); + Serial.print("Connecting to "); + Serial.println(WLAN_SSID); +#endif + WiFi.forceSleepWake(); + delay(10); + WiFi.mode(WIFI_STA); + yield(); + // Disable the WiFi persistence. The ESP8266 will not load and save WiFi settings in the flash memory. + // http://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/generic-class.html#persistent + WiFi.persistent (false); + WiFi.hostname("bathroom"); //ToDo Change to correct value + WiFi.config(ip, gateway, subnet, dns); + // Connect to WiFi access point. + if( rtcValid ) { + // The RTC data was good, make a quick connection + if (rtcData.txPow > 0 ) { + rtcData.txPow=rtcData.txPow-1; + WiFi.setOutputPower(rtcData.txPow); + yield(); + } + WiFi.begin( WLAN_SSID, WLAN_PASS, rtcData.channel, rtcData.bssid, true ); + #ifdef SERIAL_DEBUG + Serial.println("RTC WiFi used"); + Serial.println("\tCurrent Powerlevel: \t" + String(rtcData.txPow*4) + "dB"); + #endif + } + else { + //raise outputPower to max + WiFi.setOutputPower(10); + rtcData.txPow = 10; + // The RTC data was not valid, so make a regular connection + WiFi.begin( WLAN_SSID, WLAN_PASS ); + yield(); + delay(10); + } +#ifdef SERIAL_DEBUG + Serial.print("WiFi started"); + //WiFi.printDiag(Serial); +#endif + int i = 0; + uint64_t sTime = millis(); + while (WiFi.status() != WL_CONNECTED) { + delay(200); + i++; + yield(); + #ifdef SERIAL_DEBUG + Serial.print("."); + #endif + if ((millis() - sTime) > WIFI_CONNECT_TIMEOUT * 1000) // wifi connection lasts too ling, retry + { + #ifdef SERIAL_DEBUG + Serial.println("WiFi connect timeout!"); + #endif + //raise transmit power for next retry + rtcData.txPow=rtcData.txPow+1; + //make the crc invalid + ESP.rtcUserMemoryWrite (WiFi_RTC, (uint32_t*)&rtcData , sizeof( rtcData ) ); + return false; + } + //every two second raise the TX power + if (i%4 == 0) + { + rtcData.txPow = rtcData.txPow+1; + WiFi.setOutputPower(rtcData.txPow); + yield(); + #ifdef SERIAL_DEBUG + Serial.println("\tWiFi TX Power raised: \t" + String(rtcData.txPow) + "counter"); + #endif + } + yield(); + } + //ToDo stupid TX power correlation --needs improvement + long rssi = WiFi.RSSI(); + //rtcData.txPow = int((72+rssi)/4)+rtcData.txPow; + if (rssi > -60 ) + { + rtcData.txPow = 0; + } +#ifdef SERIAL_DEBUG + Serial.println("\n\r\tWiFi Connect Time: \t" + String(float((millis() - sTime)/1000)) + " s"); + Serial.println("\tWiFi RSSI: \t\t" + String(rssi) + " dB"); + Serial.println("\tWiFi next TX Power: \t" + String(rtcData.txPow) + " counter"); +#endif + // Write current connection info back to RTC + resolveHosts( rtcValid ); + rtcData.channel = WiFi.channel(); + memcpy( rtcData.bssid, WiFi.BSSID(), 6 ); // Copy 6 bytes of BSSID (AP's MAC address) + rtcData.crc32 = calculateCRC32( ((uint8_t*)&rtcData) + 4, sizeof( rtcData ) - 4 ); + ESP.rtcUserMemoryWrite (WiFi_RTC, (uint32_t*)&rtcData , sizeof( rtcData ) ); + +#ifdef SERIAL_DEBUG + Serial.println("\tIP address: \t\t" + WiFi.localIP().toString() ); +#endif + +#ifdef OLED_OUTPUT + display.drawStringMaxWidth(0, 32, 128, "IP: " + WiFi.localIP().toString()); + display.display(); +#endif + return true; +} + +bool wifi_disconnect() { + //exit gracefully from WiFi AP - delete credentials + WiFi.disconnect(true); + yield(); +#ifdef SERIAL_DEBUG + Serial.println("WiFi disconnected"); +#endif + //shutdown WiFi modul + WiFi.mode(WIFI_OFF); + // set WiFi to sleep + WiFi.forceSleepBegin(); + yield(); + return true; +}