|
@ -3,36 +3,6 @@ |
|
|
|
|
|
|
|
|
#include <Arduino.h> |
|
|
#include <Arduino.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Wiring (Heltec OLED Lora)
|
|
|
|
|
|
* GND O O GND |
|
|
|
|
|
* 5V O O 5V |
|
|
|
|
|
* 3V3 O O 3V3 |
|
|
|
|
|
* GND O < 36 only input, no pullups |
|
|
|
|
|
* RX * ? * 17 |
|
|
|
|
|
* TX * < 38 ROTARY_A only input, no pullups |
|
|
|
|
|
* RST * BUTTON < 39 ROTARY_B only input, no pullups |
|
|
|
|
|
* 0 * BUTTON < 34 only input, no pullups |
|
|
|
|
|
* ROTARY_BTN 22 * < 35 only input, no pullups |
|
|
|
|
|
* 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 |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
/* Rotary Encoder
|
|
|
|
|
|
* 1 LED |
|
|
|
|
|
* pinA A 2 LED |
|
|
|
|
|
* GND C 3 BTN -| |
|
|
|
|
|
* pinB B 4 LED / |
|
|
|
|
|
* 5 Vin -| |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
//Include Basecamp in this sketch
|
|
|
//Include Basecamp in this sketch
|
|
|
#include <Basecamp.hpp> |
|
|
#include <Basecamp.hpp> |
|
|
#include <Configuration.hpp> |
|
|
#include <Configuration.hpp> |
|
@ -49,6 +19,7 @@ |
|
|
#include <NTPClient.h> |
|
|
#include <NTPClient.h> |
|
|
|
|
|
|
|
|
#include "main.h" |
|
|
#include "main.h" |
|
|
|
|
|
#include "hardware.h" |
|
|
#include "mp3.h" |
|
|
#include "mp3.h" |
|
|
#include "BME280.h" |
|
|
#include "BME280.h" |
|
|
#include "rotary.h" |
|
|
#include "rotary.h" |
|
@ -72,102 +43,50 @@ Screen* screen; |
|
|
menuType menuChange = eNone; |
|
|
menuType menuChange = eNone; |
|
|
uint32_t lastButtonPress = 0; |
|
|
uint32_t lastButtonPress = 0; |
|
|
uint32_t lastUpdate = 0; |
|
|
uint32_t lastUpdate = 0; |
|
|
|
|
|
uint32_t lastTransmit = 0; |
|
|
//Variables for the sensor and the battery
|
|
|
|
|
|
static const int buttonPin = 0; |
|
|
|
|
|
static const int ResetPin = 17; |
|
|
|
|
|
static const int SensorPin = 0; |
|
|
|
|
|
static const int BatteryPin = 34; |
|
|
|
|
|
static const int rotaryPinA = 38; |
|
|
|
|
|
static const int rotaryPinB = 39; |
|
|
|
|
|
static const int rotaryPinButton = 22; |
|
|
|
|
|
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
|
|
|
//Variables for the mqtt packages and topics
|
|
|
uint16_t statusPacketIdSub = 0; |
|
|
uint16_t statusPacketIdSub = 0; |
|
|
String delaySleepTopic; |
|
|
String commandTopic; |
|
|
String statusTopic; |
|
|
String temperatureTopic; |
|
|
String batteryTopic; |
|
|
String humidityTopic; |
|
|
String batteryValueTopic; |
|
|
String pressureTopic; |
|
|
|
|
|
|
|
|
void setup() { |
|
|
void setup() { |
|
|
// #include "soc/rtc_io_reg.h"
|
|
|
// gpio configuration
|
|
|
// #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(buttonPin, INPUT_PULLUP); |
|
|
|
|
|
|
|
|
pinMode(18, OUTPUT); |
|
|
pinMode(18, OUTPUT); |
|
|
digitalWrite(18, HIGH); // disable LoRa_CS
|
|
|
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
|
|
|
//Initialize Basecamp
|
|
|
iot.begin(); |
|
|
iot.begin(); |
|
|
|
|
|
|
|
|
|
|
|
u8g2.begin(); |
|
|
|
|
|
// delay(50);
|
|
|
|
|
|
screen = new WelcomeScreen(); |
|
|
|
|
|
screen->draw(); |
|
|
|
|
|
menuChange = eMainScreen; |
|
|
|
|
|
u8g2.setContrast(127); |
|
|
|
|
|
|
|
|
bme280.begin(); |
|
|
bme280.begin(); |
|
|
for (int i=0; i<5; i++) { |
|
|
|
|
|
delay(500); |
|
|
|
|
|
bme280.printValues(); |
|
|
bme280.printValues(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
u8g2.begin(); |
|
|
|
|
|
delay(50); |
|
|
|
|
|
screen = new MainScreen(); |
|
|
|
|
|
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
|
|
|
//Configure the MQTT topics
|
|
|
delaySleepTopic = "esp32-node/cmd/" + iot.hostname + "/delaysleep"; |
|
|
commandTopic = "esp32-node/cmd/" + iot.hostname + "/command"; |
|
|
statusTopic = "esp32-node/stat/" + iot.hostname + "/status"; |
|
|
temperatureTopic = "esp32-node/stat/" + iot.hostname + "/temperature"; |
|
|
batteryTopic = "esp32-node/stat/" + iot.hostname + "/battery"; |
|
|
humidityTopic = "esp32-node/stat/" + iot.hostname + "/humidity"; |
|
|
batteryValueTopic = "esp32-node/stat/" + iot.hostname + "/batteryvalue"; |
|
|
pressureTopic = "esp32-node/stat/" + iot.hostname + "/pressure"; |
|
|
|
|
|
|
|
|
//Set up the Callbacks for the MQTT instance. Refer to the Async MQTT Client documentation
|
|
|
//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...
|
|
|
// TODO: We should do this actually _before_ connecting the mqtt client...
|
|
|
iot.mqtt.onConnect(onMqttConnect); |
|
|
iot.mqtt.onConnect(onMqttConnect); |
|
|
iot.mqtt.onPublish(suspendESP); |
|
|
//iot.mqtt.onPublish(NULL);
|
|
|
iot.mqtt.onMessage(onMqttMessage); |
|
|
iot.mqtt.onMessage(onMqttMessage); |
|
|
|
|
|
|
|
|
while (iot.wifi.status() != WL_CONNECTED) { |
|
|
while (iot.wifi.status() != WL_CONNECTED) { |
|
|
delay(500); |
|
|
delay(500); |
|
|
Serial.print("."); |
|
|
Serial.print("."); |
|
|
|
|
|
screen->draw(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
IPAddress localIP = iot.wifi.getIP(); |
|
|
IPAddress localIP = iot.wifi.getIP(); |
|
@ -176,18 +95,9 @@ void setup() { |
|
|
|
|
|
|
|
|
timeClient.begin(); |
|
|
timeClient.begin(); |
|
|
timeClient.update(); |
|
|
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(); |
|
|
mp3.begin(); |
|
|
|
|
|
|
|
|
u8g2.setContrast(127); |
|
|
|
|
|
|
|
|
|
|
|
rotary.registerCallback(rotation); |
|
|
rotary.registerCallback(rotation); |
|
|
rotary.begin(rotaryPinA, rotaryPinB, rotaryPinButton); |
|
|
rotary.begin(rotaryPinA, rotaryPinB, rotaryPinButton); |
|
|
} |
|
|
} |
|
@ -198,78 +108,40 @@ void onMqttConnect(bool sessionPresent) { |
|
|
DEBUG_PRINTLN(__func__); |
|
|
DEBUG_PRINTLN(__func__); |
|
|
|
|
|
|
|
|
//Subscribe to the delay topic
|
|
|
//Subscribe to the delay topic
|
|
|
iot.mqtt.subscribe(delaySleepTopic.c_str(), 0); |
|
|
iot.mqtt.subscribe(commandTopic.c_str(), 0); |
|
|
//Trigger the transmission of the current state.
|
|
|
//Trigger the transmission of the current state.
|
|
|
transmitStatus(); |
|
|
transmitStatus(); |
|
|
|
|
|
lastTransmit = millis(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//This function transfers the state of the sensor. That includes the door status, battery status and level
|
|
|
|
|
|
void transmitStatus() { |
|
|
void transmitStatus() { |
|
|
DEBUG_PRINTLN(__func__); |
|
|
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]; |
|
|
char sensorC[6]; |
|
|
//convert the sensor value to a string
|
|
|
sprintf(sensorC, "%04i", bme280.readTemperature()); |
|
|
sprintf(sensorC, "%04i", sensorValue); |
|
|
statusPacketIdSub = iot.mqtt.publish(temperatureTopic.c_str(), 1, true, sensorC); |
|
|
//Send the sensor value to the MQTT broker
|
|
|
sprintf(sensorC, "%04i", bme280.readHumidity()); |
|
|
iot.mqtt.publish(batteryValueTopic.c_str(), 1, true, sensorC); |
|
|
statusPacketIdSub = iot.mqtt.publish(humidityTopic.c_str(), 1, true, sensorC); |
|
|
//Check the battery level and publish the state
|
|
|
sprintf(sensorC, "%04i", bme280.readPressure()); |
|
|
if (sensorValue < batteryLimit) { |
|
|
statusPacketIdSub = iot.mqtt.publish(pressureTopic.c_str(), 1, true, sensorC); |
|
|
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
|
|
|
//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) { |
|
|
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { |
|
|
DEBUG_PRINTLN(__func__); |
|
|
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
|
|
|
//Since we only subscribed to one topic, we only have to compare the payload
|
|
|
if (strcmp(payload, "true") == 0) { |
|
|
if (strcmp(payload, "true") == 0) { |
|
|
delaySleep = true; |
|
|
|
|
|
} else { |
|
|
|
|
|
delaySleep = false; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void suspendESP(uint16_t packetId) { |
|
|
void suspend() { |
|
|
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"); |
|
|
DEBUG_PRINTLN("Entering deep sleep"); |
|
|
|
|
|
esp_sleep_enable_ext0_wakeup((gpio_num_t)sensorPin, 0); |
|
|
//properly disconnect from the MQTT broker
|
|
|
//properly disconnect from the MQTT broker
|
|
|
iot.mqtt.disconnect(); |
|
|
iot.mqtt.disconnect(); |
|
|
//send the ESP into deep sleep
|
|
|
//send the ESP into deep sleep
|
|
|
esp_deep_sleep_start(); |
|
|
esp_deep_sleep_start(); |
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -293,6 +165,11 @@ void loop() |
|
|
sprintf(weatherStr, "%.1f°C %.1f%% %.0fhPa", bme280.readTemperature(), bme280.readHumidity(), bme280.readPressure()); |
|
|
sprintf(weatherStr, "%.1f°C %.1f%% %.0fhPa", bme280.readTemperature(), bme280.readHumidity(), bme280.readPressure()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(millis() - lastTransmit >= 60000) { |
|
|
|
|
|
lastTransmit = millis(); |
|
|
|
|
|
transmitStatus(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (menuChange != eNone) { |
|
|
if (menuChange != eNone) { |
|
|
delete screen; |
|
|
delete screen; |
|
|
if (menuChange == eMainScreen) screen = new MainScreen(); |
|
|
if (menuChange == eMainScreen) screen = new MainScreen(); |
|
|