diff --git a/README.md b/README.md index 3ea97b9..ac6003d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ - count the turns between these positions and set the trimmer in the middle position with D1 blinking - if you have adjusted the trimmer, disconnect and connect the adapter to bus again - search for WiFi networks, you should see network with name "esp-eBus" -- connect to the network - a configuration page should open automatically +- connect to the network - a configuration page should open automatically, default password is: ebusebus - configure your WiFi network settings (SSID, password) - after reboot, you should be able to run `ping esp-ebus.local` successfully from a computer in your network (if your network is correctly configured for mDNS) - to verify there are bytes being received by the adapter, you can connect to `esp-ebus.local` port `3334` using telnet - you should see unreadable binary data @@ -138,15 +138,15 @@ There are following options: - heavier option - it will compile the firmware from source code and upload using internall tooling - [using espota.py](#espotapy) - lightweight - just needs OTA script and precompiled firmware file -- physically using a USB-TTL adaptor +- physically using a USB-TTL adaptor or device USB port (HW v5.0+) ### web interface -- [reset device](#config-reset) to access config portal -- connect to the esp-eBus WiFi network -- click blue `Update` button +- open web interface of the device by IP or on: http://esp-ebus.local +- find the update link - upload correct firmware file (see hardware revisions) -- click red `Update` button -- wait for restart, reconnect to adapter and configure WiFi +- click `Update` button +- wait for restart, reconnect to adapter and configure WiFi if not connected automatically +- in case you cannot open web interface, [reset device](#config-reset) to access it ### platform.io - clone this repository using git @@ -179,7 +179,19 @@ Uploading: [============================================================] 100% D 16:33:31 [INFO]: Result: OK ``` -### upgrading using USB-TTL adaptor +### upgrading over USB (HW v5.0+) + - this version has built-in USB serial interface + - download `firmware-fullflash-*` firmware from https://github.com/danielkucera/esp8266-arduino-ebus/releases + - connect `PROG` and `GND` + - connect adapter to a PC using USB-A - USB-C cable + - you should see a new serial port + - flash the firmware to address 0x0 using either one of tools: + - Web based: https://adafruit.github.io/Adafruit_WebSerial_ESPTool/ + - Windows: using Flash Download Tools from https://www.espressif.com/en/support/download/other-tools + - Linux esptool.py: `esptool.py write_flash 0x0 firmware-fullflash-*` + + +### upgrading over USB-TTL adaptor (before HW v5.0) You will need an USB-TTL adaptor (dongle) which suports 3V3 voltage levels and has 3V3 voltage output pin - download firmware bin file from https://github.com/danielkucera/esp8266-arduino-ebus/releases - download NodeMCU PyFlasher from https://github.com/marcelstoer/nodemcu-pyflasher/releases diff --git a/fullflash.sh b/fullflash.sh index 24c740a..dd0ce1b 100755 --- a/fullflash.sh +++ b/fullflash.sh @@ -2,8 +2,9 @@ OF=firmware-fullflash-HW_v5.x.bin -dd if=.pio/build/esp32-c3/bootloader.bin of=$OF -dd if=.pio/build/esp32-c3/partitions.bin of=$OF bs=1 seek=$((0x8000)) -dd if=$HOME/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin of=$OF bs=1 seek=$((0xe000)) +tr '\0' '\377' < /dev/zero | dd bs=1 count=$((0x10000)) of=$OF +dd if=.pio/build/esp32-c3/bootloader.bin of=$OF conv=notrunc +dd if=.pio/build/esp32-c3/partitions.bin of=$OF bs=1 seek=$((0x8000)) conv=notrunc +dd if=$HOME/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin of=$OF bs=1 seek=$((0xe000)) conv=notrunc dd if=.pio/build/esp32-c3/firmware.bin of=$OF bs=1 seek=$((0x10000)) diff --git a/platformio.ini b/platformio.ini index 52921d9..519fb8d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,9 +11,10 @@ [env] framework = arduino lib_deps = - https://github.com/tzapu/WiFiManager#v2.0.14-beta + https://github.com/prampec/IotWebConf#v3.2.1 extra_scripts = pre:auto_firmware_version.py +monitor_filters = esp32_exception_decoder [env:esp12e] platform = espressif8266 @@ -23,8 +24,11 @@ upload_speed = 921600 build_flags = -DRESET_PIN=5 -DTX_DISABLE_PIN=5 + -DBusSer=Serial + -DDebugSer=Serial1 + lib_deps = - https://github.com/tzapu/WiFiManager#v2.0.14-beta + ${env.lib_deps} https://github.com/marvinroger/ESP8266TrueRandom vshymanskyy/Preferences@^2.1.0 @@ -38,6 +42,8 @@ extends = env:esp12e build_flags = -DRESET_PIN=5 -DTX_DISABLE_PIN=2 + -DBusSer=Serial + -DDebugSer=Serial1 [env:esp12e-v3-ota] extends = env:esp12e-v3 @@ -45,14 +51,21 @@ upload_port = esp-ebus.local upload_protocol = espota [env:esp32-c3] -platform = espressif32 +platform = espressif32@6.5.0 board = esp32-c3-devkitm-1 build_flags = -DRESET_PIN=20 -DPWM_PIN=6 + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DBusSer=Serial1 + -DDebugSer=Serial + -DSTATUS_LED_PIN=3 + +monitor_filters = esp32_exception_decoder lib_deps = - https://github.com/tzapu/WiFiManager#v2.0.14-beta + ${env.lib_deps} https://github.com/guido4096/espsoftwareserial.git#add-startbit-timestamp [env:esp32-c3-ota] diff --git a/src/arbitration.cpp b/src/arbitration.cpp index a37b9e5..82a7397 100644 --- a/src/arbitration.cpp +++ b/src/arbitration.cpp @@ -27,7 +27,7 @@ Arbitration::result Arbitration::start(BusState& busstate, uint8_t master, unsig if (timeSinceStartBit > 4456 || Bus.available()) { // if we are too late, don't try to participate and retry next round - DEBUG_LOG("ARB LATE 0x%02x %lu us\n", Serial.peek(), timeSinceStartBit); + DEBUG_LOG("ARB LATE 0x%02x %lu us\n", BusSer.peek(), timeSinceStartBit); return late; } #if USE_ASYNCHRONOUS diff --git a/src/bus.cpp b/src/bus.cpp index 0fc4b0e..9ffa3f3 100644 --- a/src/bus.cpp +++ b/src/bus.cpp @@ -119,21 +119,21 @@ void BusType::begin() { #if USE_SOFTWARE_SERIAL #if defined(ESP32) - Serial.begin(2400, SERIAL_8N1, -1, UART_TX); // used for writing + BusSer.begin(2400, SERIAL_8N1, -1, UART_TX); // used for writing #elif defined(ESP8266) - Serial.begin(2400, SERIAL_8N1, SERIAL_TX_ONLY, UART_TX); + BusSer.begin(2400, SERIAL_8N1, SERIAL_TX_ONLY, UART_TX); #endif mySerial.enableStartBitTimeStampRecording(true); mySerial.enableTx(false); mySerial.enableIntTx(false); mySerial.begin(2400, SWSERIAL_8N1, UART_RX, -1, false, RXBUFFERSIZE); // used for reading #else - Serial.setRxBufferSize(RXBUFFERSIZE); + BusSer.setRxBufferSize(RXBUFFERSIZE); #if defined(ESP32) - Serial.begin(2400, SERIAL_8N1, UART_RX, UART_TX); // used for writing - Serial.setRxFIFOFull(1); + BusSer.begin(2400, SERIAL_8N1, UART_RX, UART_TX); // used for writing + BusSer.setRxFIFOFull(1); #elif defined(ESP8266) - Serial.begin(2400); + BusSer.begin(2400); #endif #endif @@ -145,7 +145,7 @@ void BusType::begin() { } void BusType::end() { - Serial.end(); + BusSer.end(); #if USE_SOFTWARE_SERIAL mySerial.end(); #endif @@ -161,11 +161,11 @@ void BusType::end() { int BusType::availableForWrite() { - return Serial.availableForWrite(); + return BusSer.availableForWrite(); } size_t BusType::write(uint8_t symbol) { - return Serial.write(symbol); + return BusSer.write(symbol); } bool BusType::read(data& d) { @@ -178,8 +178,8 @@ bool BusType::read(data& d) { receive(symbol, mySerial.readStartBitTimeStamp()); } #else - if (Serial.available()){ - uint8_t symbol = Serial.read(); + if (BusSer.available()){ + uint8_t symbol = BusSer.read(); receive(symbol, micros()); } #endif @@ -196,7 +196,7 @@ int BusType::available() { #if USE_SOFTWARE_SERIAL return mySerial.available(); #else - return Serial.available(); + return BusSer.available(); #endif } diff --git a/src/client.cpp b/src/client.cpp index b4cc8af..cc49ccf 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -9,7 +9,7 @@ bool handleNewClient(WiFiServer &server, WiFiClient clients[]) { int i; for (i = 0; i < MAX_SRV_CLIENTS; i++) { if (!clients[i]) { // equivalent to !serverClients[i].connected() - clients[i] = server.available(); + clients[i] = server.accept(); clients[i].setNoDelay(true); break; } @@ -17,7 +17,7 @@ bool handleNewClient(WiFiServer &server, WiFiClient clients[]) { // No free/disconnected slot so reject if (i == MAX_SRV_CLIENTS) { - server.available().println("busy"); + server.accept().println("busy"); // hints: server.available() is a WiFiClient with short-term scope // when out of scope, a WiFiClient will // - flush() - all data will be sent diff --git a/src/main.cpp b/src/main.cpp index 052d03d..8daaf3c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include -#include //https://github.com/tzapu/WiFiManager WiFi Configuration Magic +#include +#include #include "main.hpp" #include "enhanced.hpp" #include "bus.hpp" @@ -11,9 +12,15 @@ Preferences preferences; #include #include #include "esp32c3/rom/rtc.h" + #include + + HTTPUpdateServer httpUpdater; #else #include #include + #include + + ESP8266HTTPUpdateServer httpUpdater; #endif #define ALPHA 0.3 @@ -24,13 +31,18 @@ Preferences preferences; #define DEFAULT_AP "ebus-test" #define DEFAULT_PASS "lectronz" +#define DEFAULT_APMODE_PASS "ebusebus" #ifdef ESP32 TaskHandle_t Task1; #endif -WiFiManager wifiManager(Serial1); -WiFiManagerParameter param_pwm_value("pwm_value", "PWM value", "", 6); +#define CONFIG_VERSION "eea" +DNSServer dnsServer; +WebServer configServer(80); +char pwm_value_string[8]; +IotWebConf iotWebConf(HOSTNAME, &dnsServer, &configServer, "", CONFIG_VERSION); +IotWebConfNumberParameter pwm_value_param = IotWebConfNumberParameter("PWM value", "pwm_value", pwm_value_string, 8, "130", "1..255", "min='1' max='255' step='1'"); WiFiServer wifiServer(3333); WiFiServer wifiServerRO(3334); @@ -50,7 +62,7 @@ int reconnectCount = 0; int random_ch(){ #ifdef ESP32 - return 6; + return esp_random() % 13 + 1 ; #elif defined(ESP8266) return ESP8266TrueRandom.random(1, 13); #endif @@ -185,13 +197,75 @@ void data_loop(void * pvParameters){ } } + void saveParamsCallback () { - uint8_t new_pwm_value = atoi(param_pwm_value.getValue()); + + uint8_t new_pwm_value = atoi(pwm_value_string); if (new_pwm_value > 0){ set_pwm(new_pwm_value); preferences.putUInt("pwm_value", new_pwm_value); } - Serial1.printf("pwm_value set: %s %d\n", param_pwm_value.getValue(), new_pwm_value); + DebugSer.printf("pwm_value set: %s %d\n", pwm_value_string, new_pwm_value); + +} + +char* status_string(){ + static char status[1024]; + + int pos = 0; + + pos += sprintf(status + pos, "async mode: %s\n", USE_ASYNCHRONOUS ? "true" : "false"); + pos += sprintf(status + pos, "software serial mode: %s\n", USE_SOFTWARE_SERIAL ? "true" : "false"); + pos += sprintf(status + pos, "uptime: %ld ms\n", millis()); + pos += sprintf(status + pos, "last_connect_time: %ld ms\n", lastConnectTime); + pos += sprintf(status + pos, "reconnect_count: %d \n", reconnectCount); + pos += sprintf(status + pos, "rssi: %d dBm\n", WiFi.RSSI()); + pos += sprintf(status + pos, "free_heap: %d B\n", ESP.getFreeHeap()); + pos += sprintf(status + pos, "reset_code: %d\n", last_reset_code); + pos += sprintf(status + pos, "loop_duration: %ld us\r\n", loopDuration); + pos += sprintf(status + pos, "max_loop_duration: %ld us\r\n", maxLoopDuration); + pos += sprintf(status + pos, "version: %s\r\n", AUTO_VERSION); + pos += sprintf(status + pos, "nbr arbitrations: %i\r\n", (int)Bus._nbrArbitrations); + pos += sprintf(status + pos, "nbr restarts1: %i\r\n", (int)Bus._nbrRestarts1); + pos += sprintf(status + pos, "nbr restarts2: %i\r\n", (int)Bus._nbrRestarts2); + pos += sprintf(status + pos, "nbr lost1: %i\r\n", (int)Bus._nbrLost1); + pos += sprintf(status + pos, "nbr lost2: %i\r\n", (int)Bus._nbrLost2); + pos += sprintf(status + pos, "nbr won1: %i\r\n", (int)Bus._nbrWon1); + pos += sprintf(status + pos, "nbr won2: %i\r\n", (int)Bus._nbrWon2); + pos += sprintf(status + pos, "nbr late: %i\r\n", (int)Bus._nbrLate); + pos += sprintf(status + pos, "nbr errors: %i\r\n", (int)Bus._nbrErrors); + pos += sprintf(status + pos, "pwm_value: %i\r\n", get_pwm()); + + return status; +} + +void handleStatus() +{ + configServer.send(200, "text/plain", status_string()); +} + + +void handleRoot() +{ + // -- Let IotWebConf test and handle captive portal requests. + if (iotWebConf.handleCaptivePortal()) + { + // -- Captive portal request were already served. + return; + } + String s = "esp-eBus adapter"; + s += ""; + s += ""; + s += "Adapter status
"; + s += "Configuration - user: admin password: your configured AP mode password or default: "; + s += DEFAULT_APMODE_PASS; + s += "
"; + s += "Firmware update
"; + s += "
"; + s += "For more info see project page: https://github.com/danielkucera/esp-arduino-ebus"; + s += ""; + + configServer.send(200, "text/html", s); } void setup() { @@ -200,15 +274,14 @@ void setup() { check_reset(); #ifdef ESP32 - Serial1.begin(115200, SERIAL_8N1, 10, 9); last_reset_code = rtc_get_reset_reason(0); #elif defined(ESP8266) - Serial1.begin(115200); last_reset_code = (int) ESP.getResetInfoPtr(); #endif Bus.begin(); - Serial1.setDebugOutput(true); + DebugSer.begin(115200); + DebugSer.setDebugOutput(true); disableTX(); @@ -221,24 +294,49 @@ void setup() { if (preferences.getBool("firstboot", true)){ preferences.putBool("firstboot", false); - WiFi.begin(DEFAULT_AP, DEFAULT_PASS); + iotWebConf.init(); + strncpy(iotWebConf.getApPasswordParameter()->valueBuffer, DEFAULT_APMODE_PASS, IOTWEBCONF_WORD_LEN); + strncpy(iotWebConf.getWifiSsidParameter()->valueBuffer, "ebus-test", IOTWEBCONF_WORD_LEN); + strncpy(iotWebConf.getWifiPasswordParameter()->valueBuffer, "lectronz", IOTWEBCONF_WORD_LEN); + iotWebConf.saveConfig(); + } else { + iotWebConf.skipApStartup(); } - WiFi.enableAP(false); - WiFi.begin(); - #ifdef ESP32 WiFi.onEvent(on_connected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_CONNECTED); #endif - wifiManager.setSaveParamsCallback(saveParamsCallback); - wifiManager.addParameter(¶m_pwm_value); + int wifi_ch = random_ch(); + DebugSer.printf("Channel for AP mode: %d\n", wifi_ch); + WiFi.channel(wifi_ch); // doesn't work, https://github.com/prampec/IotWebConf/issues/286 + + iotWebConf.addSystemParameter(&pwm_value_param); + iotWebConf.setConfigSavedCallback(&saveParamsCallback); + iotWebConf.getApTimeoutParameter()->visible = true; + iotWebConf.setWifiConnectionTimeoutMs(7000); + +#ifdef STATUS_LED_PIN + iotWebConf.setStatusPin(STATUS_LED_PIN); +#endif - wifiManager.setHostname(HOSTNAME); - wifiManager.setConfigPortalTimeout(120); - wifiManager.setWiFiAPChannel(random_ch()); - wifiManager.autoConnect(HOSTNAME); - wifiManager.startWebPortal(); + // -- Initializing the configuration. + iotWebConf.init(); + + // -- Set up required URL handlers on the web server. + configServer.on("/", []{ handleRoot(); }); + configServer.on("/config", []{ iotWebConf.handleConfig(); }); + configServer.on("/param", []{ iotWebConf.handleConfig(); }); + configServer.on("/status", []{ handleStatus(); }); + configServer.onNotFound([](){ iotWebConf.handleNotFound(); }); + + iotWebConf.setupUpdateServer( + [](const char* updatePath) { httpUpdater.setup(&configServer, updatePath); }, + [](const char* userName, char* password) { httpUpdater.updateCredentials(userName, password); }); + + while (iotWebConf.getState() != iotwebconf::NetworkState::OnLine){ + iotWebConf.doLoop(); + } wifiServer.begin(); wifiServerRO.begin(); @@ -267,31 +365,10 @@ bool handleStatusServerRequests() { if (!statusServer.hasClient()) return false; - WiFiClient client = statusServer.available(); + WiFiClient client = statusServer.accept(); if (client.availableForWrite() >= AVAILABLE_THRESHOLD) { - client.printf("async mode: %s\n", USE_ASYNCHRONOUS ? "true" : "false"); - client.printf("software serial mode: %s\n", USE_SOFTWARE_SERIAL ? "true" : "false"); - client.printf("uptime: %ld ms\n", millis()); - client.printf("last_connect_time: %ld ms\n", lastConnectTime); - client.printf("reconnect_count: %d \n", reconnectCount); - client.printf("rssi: %d dBm\n", WiFi.RSSI()); - client.printf("free_heap: %d B\n", ESP.getFreeHeap()); - client.printf("reset_code: %d\n", last_reset_code); - client.printf("loop_duration: %ld us\r\n", loopDuration); - client.printf("max_loop_duration: %ld us\r\n", maxLoopDuration); - client.printf("version: %s\r\n", AUTO_VERSION); - client.printf("nbr arbitrations: %i\r\n", (int)Bus._nbrArbitrations); - client.printf("nbr restarts1: %i\r\n", (int)Bus._nbrRestarts1); - client.printf("nbr restarts2: %i\r\n", (int)Bus._nbrRestarts2); - client.printf("nbr lost1: %i\r\n", (int)Bus._nbrLost1); - client.printf("nbr lost2: %i\r\n", (int)Bus._nbrLost2); - client.printf("nbr won1: %i\r\n", (int)Bus._nbrWon1); - client.printf("nbr won2: %i\r\n", (int)Bus._nbrWon2); - client.printf("nbr late: %i\r\n", (int)Bus._nbrLate); - client.printf("nbr errors: %i\r\n", (int)Bus._nbrErrors); - client.printf("pwm_value: %i\r\n", get_pwm()); - + client.print(status_string()); client.flush(); client.stop(); } @@ -310,7 +387,7 @@ void loop() { wdt_feed(); #ifdef ESP32 - wifiManager.process(); + iotWebConf.doLoop(); #endif if (WiFi.status() != WL_CONNECTED) {