|
|
|
//Define DEBUG to get the Output from DEBUG_PRINTLN
|
|
|
|
#define DEBUG 1
|
|
|
|
|
|
|
|
#include <Arduino.h>
|
|
|
|
|
|
|
|
//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 <time.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#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);
|
|
|
|
}
|