//Define DEBUG to get the Output from DEBUG_PRINTLN #define DEBUG 1 #include //Include Basecamp in this sketch #include #include #include #ifdef U8X8_HAVE_HW_SPI #include #endif #ifdef U8X8_HAVE_HW_I2C #include #endif #include #include #include "apps/sntp/sntp.h" #include "main.h" #include "hardware.h" #include "mp3.h" #include "BME280.h" #include "rotary.h" #include "screen.h" U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ 16, /* clock=*/ 15, /* data=*/ 4); char timeStr[20]; char weatherStr[32]; RTC_DATA_ATTR static int boot_count = 0; //Create a new Basecamp instance called iot Basecamp iot; BME280 bme280; MP3 mp3; Rotary rotary; Screen* screen; menuType menuChange = eNone; uint32_t lastButtonPress = 0; uint32_t lastUpdate = 0; uint32_t lastTransmit = 0; //Variables for the mqtt packages and topics uint16_t statusPacketIdSub = 0; String commandTopic; String bme280Topic; void setup() { // gpio configuration pinMode(buttonPin, INPUT_PULLUP); pinMode(18, OUTPUT); digitalWrite(18, HIGH); // disable LoRa_CS boot_count++; time_t now; struct tm timeinfo; time(&now); setenv("TZ", "Europe/Berlin", 1); tzset(); localtime_r(&now, &timeinfo); if (timeinfo.tm_year < (2016 - 1900)) { // time not set } //Initialize Basecamp iot.begin(); esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); switch(wakeup_reason) { case ESP_SLEEP_WAKEUP_EXT0: Serial.println("wakeup by RTC_IO"); break; case ESP_SLEEP_WAKEUP_EXT1: Serial.println("wakeup by RTC_CNTL"); break; case ESP_SLEEP_WAKEUP_TIMER: Serial.println("wakeup by timer"); break; case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("wakeup by touchpad"); break; case ESP_SLEEP_WAKEUP_ULP: Serial.println("wakeup by ULP"); break; default: // ESP_SLEEP_WAKEUP_UNDEFINED Serial.println("wakeup not by deep sleep"); break; } char strftime_buf[64]; strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); Serial.println(strftime_buf); u8g2.begin(); // delay(50); screen = new WelcomeScreen(); screen->draw(); menuChange = eMainScreen; u8g2.setContrast(127); bme280.begin(); bme280.printValues(); //Configure the MQTT topics commandTopic = "esp32-node/cmd/" + iot.hostname + "/play"; bme280Topic = "esp32-node/stat/" + iot.hostname + "/bme280"; //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(NULL); iot.mqtt.onMessage(onMqttMessage); while (iot.wifi.status() != WL_CONNECTED) { delay(500); Serial.print("."); screen->draw(); } IPAddress localIP = iot.wifi.getIP(); char ipStr[16]; localIP.toString().toCharArray(ipStr, 16); if (timeinfo.tm_year < (2016 - 1900)) { ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP."); obtain_time(); // update 'now' variable with current time time(&now); } mp3.begin(); rotary.registerCallback(rotation); rotary.begin(rotaryPinA, rotaryPinB, rotaryPinButton); } //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(commandTopic.c_str(), 0); //Trigger the transmission of the current state. transmitStatus(); lastTransmit = millis(); } void transmitStatus() { DEBUG_PRINTLN(__func__); StaticJsonBuffer<200> jsonBuffer; JsonObject& root = jsonBuffer.createObject(); root["temperature"] = bme280.readTemperature(); root["humidity"] = bme280.readHumidity(); root["pressure"] = bme280.readPressure(); char sensorBuf[root.measureLength()+1]; root.printTo(sensorBuf, sizeof(sensorBuf)); statusPacketIdSub = iot.mqtt.publish(bme280Topic.c_str(), 0, false, sensorBuf); // statusPacketIdSub = iot.mqtt.publish(bme280Topic.c_str(), 1, true, sensorBuf); } //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__); //Since we only subscribed to one topic, we only have to compare the payload if (strcmp(payload, "ON") == 0) { mp3.start(); } else if (strcmp(payload, "OFF") == 0) { mp3.stop(); } } void suspend() { DEBUG_PRINTLN("Entering deep sleep"); esp_sleep_enable_timer_wakeup(1000000LL * secondsToSleep); esp_sleep_enable_ext0_wakeup((gpio_num_t)sensorPin, 0); const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1; const uint64_t ext_wakeup_pin_2_mask = 1ULL << ext_wakeup_pin_2; // esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_1_mask | ext_wakeup_pin_2_mask, ESP_EXT1_WAKEUP_ANY_HIGH); //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); // power down all peripherals //properly disconnect from the MQTT broker iot.mqtt.disconnect(); //send the ESP into deep sleep esp_deep_sleep_start(); } void obtain_time(void) { sntp_setoperatingmode(SNTP_OPMODE_POLL); sntp_setservername(0, "de.pool.ntp.org"); sntp_init(); // wait for time to be set time_t now = 0; struct tm timeinfo = { 0 }; int retry = 0; const int retry_count = 10; while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) { ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count); vTaskDelay(2000 / portTICK_PERIOD_MS); time(&now); localtime_r(&now, &timeinfo); } } void rotation(int i, int direction, int buttonPressed) { if(millis() - lastButtonPress >= 300) { lastButtonPress = millis(); if (buttonPressed == 1) screen->select(); } if (direction == 1) screen->next(); else if (direction == -1) screen->previous(); } void loop() { if(millis() - lastUpdate >= 1000) { lastUpdate = millis(); time_t now; struct tm timeinfo; time(&now); setenv("TZ", "Europe/Berlin", 1); tzset(); localtime_r(&now, &timeinfo); strftime(timeStr, sizeof(timeStr), "%H:%M:%S", &timeinfo); sprintf(weatherStr, "%.1f°C %.1f%% %.0fhPa", bme280.readTemperature(), bme280.readHumidity(), bme280.readPressure()); } if(millis() - lastTransmit >= 60000) { lastTransmit = millis(); transmitStatus(); } if (menuChange != eNone) { delete screen; if (menuChange == eMainScreen) screen = new MainScreen(); else if (menuChange == eMainMenu) screen = new MainMenu(); else if (menuChange == eStationMenu) screen = new StationMenu(); else screen = new MainScreen(); menuChange = eNone; } screen->draw(); delay(100); }