#include #include #include #include #include #include #include #include #include #include #include #include "ledcontrol.h" #define PIN_BUTTON D2 #define PIN_LED D1 #define LED_ON HIGH #define LED_OFF LOW #include "secrets.h" #define MQTT_BROKER "maqiatto.com" #define MQTT_BROKER_PORT 1883 //#define MQTT_USERNAME "" //#define MQTT_PASSWORD "" #define MQTT_BASE MQTT_USERNAME "/bsbutton" // fw update // - increment number + build // - scp .pio/build/$ENV/firmware.bin manuel@mausz.at:public_html/coding/.firmware/bsbutton.bin // - reboot device or send "fwupdate" to mqtt_topic_cmd // - fw update state go to mqtt_topic_state #define FIRMWARE_VERSION 4 //#define FIRMWARE_URL "" // tracks // - ffmpeg -i input.mp3 -vn -ar 44100 -ac 1 -b:a 32k output.mp3 // - xxd -i output.mp3 mytrack.h // - modify mytrack.h: const unsigned char xxx_mp3[] PROGMEM = ... // - add variable to arrays #include "alert.h" #include "manuel.h" #include "rickroll.h" #include "llama.h" #include "kit.h" #include "starwars.h" const unsigned char *tracks[] = { alert_mp3, manuel_mp3, rickroll_mp3, llama_mp3, kit_mp3, starwars_mp3 }; const unsigned int tracks_len[] = { sizeof(alert_mp3), sizeof(manuel_mp3), sizeof(rickroll_mp3), sizeof(llama_mp3), sizeof(kit_mp3), sizeof(starwars_mp3) }; long track_count = sizeof(tracks) / sizeof(unsigned char*); #define _STR(s) #s #define STR(s) _STR(s) const String mqtt_clientId = "BSButton-" + String(ESP.getChipId()) + "/v" + STR(FIRMWARE_VERSION); const char* mqtt_topic_ping = MQTT_BASE "/ping"; const char* mqtt_topic_cmd = MQTT_BASE "/command"; const String mqtt_topic_state = String(MQTT_BASE) + "/" + String(ESP.getChipId()); class LedControl; bool mqttLoop(); void mqttCallback(char *topic, byte *payload, unsigned int length); void blinkLed(const unsigned long interval); void checkFirmwareUpdate(); WiFiClient wifi_client; PubSubClient mqtt(wifi_client); Bounce button = Bounce(); LedControl led(PIN_LED, LED_ON); AudioGeneratorMP3 *mp3; AudioFileSourcePROGMEM *file; AudioOutputI2SNoDAC *out; void setup() { Serial.begin(115200); randomSeed(analogRead(PIN_LED)); pinMode(PIN_BUTTON, INPUT_PULLUP); button.attach(PIN_BUTTON); button.interval(25); led.on(); WiFiManager wifiManager; wifiManager.setConfigPortalTimeout(600); Serial.printf_P(PSTR("Setting up WiFi\n")); if (!wifiManager.autoConnect("Bullshit")) { Serial.printf_P(PSTR("Failed to connect and hit timeout\n")); delay(5000); ESP.restart(); } yield(); checkFirmwareUpdate(); yield(); mqtt.setServer(MQTT_BROKER, MQTT_BROKER_PORT); mqtt.setCallback(mqttCallback); mp3 = new AudioGeneratorMP3(); file = new AudioFileSourcePROGMEM(); out = new AudioOutputI2SNoDAC(); out->SetGain(3.0); //NOTE >=4.0 does not work? pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); } void loop() { if (mp3->isRunning()) { led.blink(200); if (!mp3->loop()) { mp3->stop(); led.off(); } } else { // turn off the blinking led //if (ledState == LED_ON) // blinkLed(); if (!mqttLoop()) { delay(5000); return; } // prohibit triggering the button multiple times static unsigned long button_lock_millis = 1000; static unsigned long button_pressed_millis = 0; button.update(); if (button.rose() && millis() - button_pressed_millis >= button_lock_millis) { const long track = random(track_count); Serial.printf_P(PSTR("Button pressed. It's track #%ld\n"), track); String tmp = "play" + String(track); mqtt.publish(mqtt_topic_cmd, tmp.c_str()); button_pressed_millis = millis(); } } } bool mqttLoop() { if (!mqtt.connected()) { led.on(); Serial.printf_P(PSTR("Connecting to MQTT\n")); if (!mqtt.connect(mqtt_clientId.c_str(), MQTT_USERNAME, MQTT_PASSWORD)) return false; Serial.printf_P(PSTR("MQTT connected\n")); led.off(); mqtt.publish(mqtt_topic_ping, mqtt_clientId.c_str()); mqtt.subscribe(mqtt_topic_ping); mqtt.subscribe(mqtt_topic_cmd); } yield(); mqtt.loop(); return true; } void mqttCallback(char *topic, byte *payload, unsigned int length) { char c_payload[length + 1]; memcpy(c_payload, payload, length); c_payload[length] = '\0'; if (!strcmp(topic, mqtt_topic_ping)) { if (!strcmp(c_payload, "ping")) mqtt.publish(mqtt_topic_ping, mqtt_clientId.c_str()); return; } else if (!strcmp(topic, mqtt_topic_cmd)) { if (!strncmp(c_payload, "play", 4)) { long track = atoi(c_payload + 4); if (track >= track_count) return; Serial.printf_P(PSTR("Playing track #%ld\n"), track); file->open(tracks[track], tracks_len[track]); mp3->begin(file, out); return; } else if (!strcmp(c_payload, "fwupdate")) { checkFirmwareUpdate(); return; } else if (!strcmp(c_payload, "reset")) { Serial.printf_P(PSTR("Resetting\n")); ESP.reset(); return; } } Serial.printf_P(PSTR("Unhandled MQTT message: [%s] %s\n"), topic, c_payload); } void checkFirmwareUpdate() { BearSSL::WiFiClientSecure update_client; update_client.setInsecure(); mqtt.publish(mqtt_topic_state.c_str(), "fwupdate running"); ESPhttpUpdate.setLedPin(PIN_LED, LED_ON); ESPhttpUpdate.rebootOnUpdate(true); t_httpUpdate_return ret = ESPhttpUpdate.update(update_client, FIRMWARE_URL, STR(FIRMWARE_VERSION)); switch(ret) { case HTTP_UPDATE_FAILED: { Serial.printf_P(PSTR("HTTP_UPDATE_FAILED Error (%d): %s\n"), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); String tmp = String("fwupdate error: ") + ESPhttpUpdate.getLastErrorString(); mqtt.publish(mqtt_topic_state.c_str(), tmp.c_str()); } break; case HTTP_UPDATE_NO_UPDATES: Serial.printf_P(PSTR("HTTP_UPDATE_NO_UPDATES\n")); mqtt.publish(mqtt_topic_state.c_str(), "fwupdate noupdates"); break; case HTTP_UPDATE_OK: Serial.printf_P(PSTR("HTTP_UPDATE_OK\n")); mqtt.publish(mqtt_topic_state.c_str(), "fwupdate ok"); break; } }