//Define DEBUG to get the Output from DEBUG_PRINTLN #define DEBUG 1 #include <Arduino.h> /* Wiring (Heltec OLED Lora) * GND O O GND * 5V O O 5V * 3V3 O O 3V3 * GND O < 36 * RX * * 17 * TX * < 38 * RST * BUTTON < 39 * 0 * < 34 * 22 * < 35 * BME280_SDO 19 * LoRa_MISO * 32 * BME280_CS 23 * * 33 * 18 x LoRa_CS * 25 MAX98_DIN * BME280_SCL/SCK 5 * LoRa_SCK LoRa_IRQ * 26 * 15 * OLED_SCL LoRa_MOSI * 27 BME280_SDA/SDI * 2 * LoRa_RST * 14 * 4 * OLED_SDA * 12 MAX98_BCLK * 17 * * 13 MAX98_LRC * 16 * OLED_RST * 21 */ //Include Basecamp in this sketch #include <Basecamp.hpp> #include <Configuration.hpp> #include <U8g2lib.h> #ifdef U8X8_HAVE_HW_SPI #include <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include <Wire.h> #endif #include <WiFiUdp.h> #include <NTPClient.h> #include "main.h" #include "mp3.h" #include "BME280.h" #include "image.h" U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16); //Create a new Basecamp instance called iot Basecamp iot; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "de.pool.ntp.org", 3600, 60000); char timeStr[20]; BME280 bme280; MP3 mp3; uint32_t lastButtonPress = 0; //Variables for the sensor and the battery static const int buttonPin = 0; static const int ResetPin = 17; static const int SensorPin = 32; static const int BatteryPin = 34; int sensorValue = 0; //The batteryLimit defines the point at which the battery is considered empty. int batteryLimit = 3300; //This is used to control if the ESP should enter sleep mode or not bool delaySleep = true; //Variables for the mqtt packages and topics uint16_t statusPacketIdSub = 0; String delaySleepTopic; String statusTopic; String batteryTopic; String batteryValueTopic; void setup() { // #include "soc/rtc_io_reg.h" // #include "soc/sens_reg.h" // // SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); // CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE | RTC_IO_X32N_RDE); // CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); // SET_PERI_REG_BITS(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, 1, RTC_IO_DAC_XTAL_32K_S); // SET_PERI_REG_BITS(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, 3, RTC_IO_DRES_XTAL_32K_S); // SET_PERI_REG_BITS(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, 0, RTC_IO_DBIAS_XTAL_32K_S); // SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); // REG_SET_BIT(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M); // REG_CLR_BIT(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RDE_M | RTC_IO_PDAC1_RUE_M); // REG_SET_FIELD(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_FUN_SEL, 1); // REG_SET_FIELD(SENS_SAR_DAC_CTRL1_REG, SENS_DEBUG_BIT_SEL, 0); // const uint8_t sel = 4; /* sel = 4 : 32k XTAL; sel = 5 : internal 150k RC */ // REG_SET_FIELD(RTC_IO_RTC_DEBUG_SEL_REG, RTC_IO_DEBUG_SEL0, sel); // //configuration of the battery and sensor pins // pinMode(ResetPin, INPUT_PULLDOWN); // pinMode(SensorPin, INPUT_PULLDOWN); // pinMode(BatteryPin, INPUT); pinMode(buttonPin, INPUT_PULLUP); pinMode(18, OUTPUT); digitalWrite(18, HIGH); // disable LoRa_CS //read the status of the doorsensor as soon as possible to determine the state that triggered it // sensorValue = digitalRead(SensorPin); //Initialize Basecamp iot.begin(); bme280.begin(); for (int i=0; i<5; i++) { delay(500); bme280.printValues(); } u8g2.begin(); delay(50); u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font u8g2.drawStr(2,10,"esp32-node by hendrik"); // write something to the internal memory if (iot.configuration.get("WifiConfigured") != "True") { u8g2.drawStr(2,30,"NOT CONFIGURED!"); u8g2.drawStr(12,40,"SSID: \"ESP32\""); u8g2.drawStr(12,55,"http://192.168.4.1"); } else { u8g2.drawStr(2,30,"Welcome!"); u8g2.drawStr(12,40,"connecting.."); } u8g2.drawFrame(0,0,u8g2.getDisplayWidth(),u8g2.getDisplayHeight() ); u8g2.sendBuffer(); // transfer internal memory to the display //Configure the MQTT topics delaySleepTopic = "esp32-node/cmd/" + iot.hostname + "/delaysleep"; statusTopic = "esp32-node/stat/" + iot.hostname + "/status"; batteryTopic = "esp32-node/stat/" + iot.hostname + "/battery"; batteryValueTopic = "esp32-node/stat/" + iot.hostname + "/batteryvalue"; //Set up the Callbacks for the MQTT instance. Refer to the Async MQTT Client documentation // TODO: We should do this actually _before_ connecting the mqtt client... iot.mqtt.onConnect(onMqttConnect); iot.mqtt.onPublish(suspendESP); iot.mqtt.onMessage(onMqttMessage); while (iot.wifi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } IPAddress localIP = iot.wifi.getIP(); char ipStr[16]; localIP.toString().toCharArray(ipStr, 16); timeClient.begin(); timeClient.update(); timeClient.getFormattedTime().toCharArray(timeStr, 50); u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font // u8g2.setDrawColor(2); u8g2.drawStr(30, 30, timeStr); u8g2.drawStr(30, 50, ipStr); u8g2.sendBuffer(); mp3.begin(); u8g2.setContrast(127); } //This function is called when the MQTT-Server is connected void onMqttConnect(bool sessionPresent) { DEBUG_PRINTLN(__func__); //Subscribe to the delay topic iot.mqtt.subscribe(delaySleepTopic.c_str(), 0); //Trigger the transmission of the current state. transmitStatus(); } //This function transfers the state of the sensor. That includes the door status, battery status and level void transmitStatus() { DEBUG_PRINTLN(__func__); if (sensorValue == 0) { DEBUG_PRINTLN("Door open"); //Transfer the current state of the sensor to the MQTT broker statusPacketIdSub = iot.mqtt.publish(statusTopic.c_str(), 1, true, "open" ); //Configure the wakeup pin to wake if the door is closed esp_sleep_enable_ext0_wakeup((gpio_num_t)SensorPin, 1); } else { DEBUG_PRINTLN("Door closed"); //Transfer the current state of the sensor to the MQTT broker statusPacketIdSub = iot.mqtt.publish(statusTopic.c_str(), 1, true, "closed" ); //Configure the wakeup pin to wake if the door is closed esp_sleep_enable_ext0_wakeup((gpio_num_t)SensorPin, 0); } //Read the current analog battery value // sensorValue = analogRead(BatteryPin); //sensorC stores the battery value as a char char sensorC[6]; //convert the sensor value to a string sprintf(sensorC, "%04i", sensorValue); //Send the sensor value to the MQTT broker iot.mqtt.publish(batteryValueTopic.c_str(), 1, true, sensorC); //Check the battery level and publish the state if (sensorValue < batteryLimit) { DEBUG_PRINTLN("Battery empty"); iot.mqtt.publish(batteryTopic.c_str(), 1, true, "empty" ); } else { DEBUG_PRINTLN("Battery full"); iot.mqtt.publish(batteryTopic.c_str(), 1, true, "full" ); } DEBUG_PRINTLN("Data published"); } //This topic is called if an MQTT message is received void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { DEBUG_PRINTLN(__func__); //Check if the payload eqals "true" and set delaySleep correspondigly //Since we only subscribed to one topic, we only have to compare the payload if (strcmp(payload, "true") == 0) { delaySleep = true; } else { delaySleep = false; } } void suspendESP(uint16_t packetId) { DEBUG_PRINTLN(__func__); //Check if the published package is the one of the door sensor if (packetId == statusPacketIdSub) { if (delaySleep == true) { DEBUG_PRINTLN("Delaying Sleep"); return; } DEBUG_PRINTLN("Entering deep sleep"); //properly disconnect from the MQTT broker iot.mqtt.disconnect(); //send the ESP into deep sleep esp_deep_sleep_start(); } } void loop() { timeClient.update(); timeClient.getFormattedTime().toCharArray(timeStr, 50); if (digitalRead(buttonPin) == LOW) { if(millis() - lastButtonPress >= 1000) { Serial.println("Button pressed"); lastButtonPress = millis(); if (mp3.playing) { mp3.stop(); } else { mp3.start(); } } } u8g2.clearBuffer(); // clear the internal memory if (digitalRead(buttonPin) == HIGH) { u8g2.setFont(u8g2_font_inb19_mf); u8g2.drawStr(0, 20, timeStr); char title1[32]; char title2[32]; strncpy(title1, titleStr, 22); title1[22] = '\0'; strncpy(title2, titleStr+22, 32); title2[31] = '\0'; char weather[32]; u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font sprintf(weather, "%.1f°C %.1f%% %.0fhPa", bme280.readTemperature(), bme280.readHumidity(), bme280.readPressure()); u8g2.drawUTF8(0, 30, weather); u8g2.setFont(u8g2_font_prospero_bold_nbp_tf); // choose a suitable font u8g2.drawUTF8(0, 54, title1); u8g2.drawUTF8(0, 64, title2); } else { u8g2.drawXBMP(0,0, IMG_1872_width, IMG_1872_height, IMG_1872_bits); } u8g2.sendBuffer(); // bme280.printValues(); delay(100); }