diff --git a/src/hardware.h b/src/hardware.h new file mode 100644 index 0000000..1ce4c5f --- /dev/null +++ b/src/hardware.h @@ -0,0 +1,42 @@ +#ifndef _HARDWARE_H +#define _HARDWARE_H + +/* pin assignments */ +static constexpr uint8_t buttonPin = 0; +static constexpr uint8_t sensorPin = 0; +static constexpr uint8_t rotaryPinA = 38; +static constexpr uint8_t rotaryPinB = 39; +static constexpr uint8_t rotaryPinButton = 22; + + +/* 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 -| + */ + + +#endif /* _HARDWARE_H */ diff --git a/src/main.cpp b/src/main.cpp index c05b364..e89e7ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,36 +3,6 @@ #include - -/* 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 #include @@ -49,6 +19,7 @@ #include #include "main.h" +#include "hardware.h" #include "mp3.h" #include "BME280.h" #include "rotary.h" @@ -72,102 +43,50 @@ Screen* screen; menuType menuChange = eNone; uint32_t lastButtonPress = 0; uint32_t lastUpdate = 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; +uint32_t lastTransmit = 0; //Variables for the mqtt packages and topics uint16_t statusPacketIdSub = 0; -String delaySleepTopic; -String statusTopic; -String batteryTopic; -String batteryValueTopic; +String commandTopic; +String temperatureTopic; +String humidityTopic; +String pressureTopic; 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); + // gpio configuration 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); - 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 +// delay(50); + screen = new WelcomeScreen(); + screen->draw(); + menuChange = eMainScreen; + u8g2.setContrast(127); + + bme280.begin(); + bme280.printValues(); //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"; + commandTopic = "esp32-node/cmd/" + iot.hostname + "/command"; + temperatureTopic = "esp32-node/stat/" + iot.hostname + "/temperature"; + humidityTopic = "esp32-node/stat/" + iot.hostname + "/humidity"; + pressureTopic = "esp32-node/stat/" + iot.hostname + "/pressure"; //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.onPublish(NULL); iot.mqtt.onMessage(onMqttMessage); while (iot.wifi.status() != WL_CONNECTED) { delay(500); Serial.print("."); + screen->draw(); } IPAddress localIP = iot.wifi.getIP(); @@ -176,18 +95,9 @@ void setup() { 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); - rotary.registerCallback(rotation); rotary.begin(rotaryPinA, rotaryPinB, rotaryPinButton); } @@ -198,78 +108,40 @@ void onMqttConnect(bool sessionPresent) { DEBUG_PRINTLN(__func__); //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. transmitStatus(); + lastTransmit = millis(); } - -//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"); + sprintf(sensorC, "%04i", bme280.readTemperature()); + statusPacketIdSub = iot.mqtt.publish(temperatureTopic.c_str(), 1, true, sensorC); + sprintf(sensorC, "%04i", bme280.readHumidity()); + statusPacketIdSub = iot.mqtt.publish(humidityTopic.c_str(), 1, true, sensorC); + sprintf(sensorC, "%04i", bme280.readPressure()); + statusPacketIdSub = iot.mqtt.publish(pressureTopic.c_str(), 1, true, sensorC); } + //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 suspend() { + DEBUG_PRINTLN("Entering deep sleep"); + esp_sleep_enable_ext0_wakeup((gpio_num_t)sensorPin, 0); + //properly disconnect from the MQTT broker + iot.mqtt.disconnect(); + //send the ESP into deep sleep + esp_deep_sleep_start(); } @@ -293,6 +165,11 @@ void loop() 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(); diff --git a/src/main.h b/src/main.h index 3c74e28..fb251d8 100644 --- a/src/main.h +++ b/src/main.h @@ -5,6 +5,6 @@ void setup(); void onMqttConnect(bool sessionPresent); void transmitStatus(); void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total); -void suspendESP(uint16_t packetId); +void suspend(); void loop(); void rotation(int i, int direction, int buttonPressed); diff --git a/src/mp3.cpp b/src/mp3.cpp index 0ebcced..7254799 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -41,7 +41,7 @@ void MP3::cTaskWrapper(void* parameters) { void MP3::stop() { Serial.println("Stopping mp3 playback"); - strcpy(titleStr, ""); + strcpy(titleStr, "stopping.."); playing = false; // while (audioTaskHandle && eTaskGetState(audioTaskHandle) != eDeleted) { // Serial.println("waiting for audio to finish"); @@ -86,7 +86,7 @@ void MP3::mp3_decoder_task(void *pvParameters) { AudioOutputI2S *out = nullptr; - strcpy(titleStr, "..."); + strcpy(titleStr, "loading..."); file = new AudioFileSourceICYStream(URL); file->RegisterMetadataCB(MDCallback, (void*)"ICY"); buff = new AudioFileSourceBuffer(file, preallocateBuffer, preallocateBufferSize); @@ -107,11 +107,6 @@ void MP3::mp3_decoder_task(void *pvParameters) { playing = true; while(decoder->isRunning()) { - if (millis()-lastms > 1000) { - lastms = millis(); - Serial.printf("Running for %d ms...\n", lastms); - Serial.flush(); - } if (volumeChanged) { volumeChanged = false; out->SetGain(((float)volume)/100.0); @@ -122,7 +117,7 @@ void MP3::mp3_decoder_task(void *pvParameters) { } Serial.printf("MP3 done\n"); - strcpy(titleStr, "___"); + strcpy(titleStr, "stopped"); playing = false; @@ -147,7 +142,7 @@ void MP3::mp3_decoder_task(void *pvParameters) { } bool MP3::begin() { - strcpy(titleStr, ">"); + strcpy(titleStr, "ready"); // First, preallocate all the memory needed for the buffering and codecs, never to be freed preallocateBuffer = malloc(preallocateBufferSize); diff --git a/src/mp3.h b/src/mp3.h index 2b32700..7ca3afe 100644 --- a/src/mp3.h +++ b/src/mp3.h @@ -33,8 +33,6 @@ class MP3 { static constexpr int preallocateCodecSize = 29192; // MP3 codec max mem needed void *preallocateBuffer = NULL; void *preallocateCodec = NULL; - - int lastms = 0; }; #endif /* _MP3_H */ diff --git a/src/screen.cpp b/src/screen.cpp index a4e3d91..00eda39 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -56,12 +56,13 @@ uint8_t SelectionList::select() { } MainMenu::MainMenu() { + title = "Main Menu"; string_list = { "PLAY", "Radio Stations", "test", "test3", - "« Return"}; + "\xAB Return"}; if (mp3.playing) { string_list[0] = "STOP"; } @@ -99,6 +100,7 @@ uint8_t MainMenu::select() { } StationMenu::StationMenu() { + title = "Radio Stations"; string_list = { "80s Planet", "Left Coast 70s", @@ -110,7 +112,7 @@ StationMenu::StationMenu() { "Live Ireland", "Ye Ol Celtic Pub", "Gone Country - NZCMR", - "« Return"}; + "\xAB Return"}; } uint8_t StationMenu::select() { @@ -161,6 +163,27 @@ namespace { /* anonymous namespace for helper functions */ } +void WelcomeScreen::draw() { + 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,"WiFi connecting"); + u8g2.setCursor(80, 40); + progress++; + for (int i = 0; i