diff --git a/.gitignore b/.gitignore index c7276bdd..961869c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,14 @@ -.vscode/* +/.vscode/* *.log # File system image -data/ -data-unused/ +/data/ +/data-unused/ # ESP-IDF generated files /build/ /managed_components/ -sdkconfig* -!sdkconfig*.defaults -dependencies*.lock +/sdkconfig* +/!sdkconfig*.defaults +/dependencies*.lock diff --git a/CMakeLists.txt b/CMakeLists.txt index fa4a5c3c..9d0d4a79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,14 +9,6 @@ if(UD_GEN STREQUAL "") endif() string(TOLOWER "${UD_GEN}" UD_GEN_LOWER) -if(NOT DEFINED UD_DEBUG) - set(UD_DEBUG "$ENV{UD_DEBUG}") -endif() -if(UD_DEBUG STREQUAL "") - message("UD_DEBUG is not set, assuming 0.") - set(UD_GEN 0) -endif() - # Make sure we reconfigure if UD_GEN changes set_property(GLOBAL PROPERTY UD_GEN_TRACKER "${UD_GEN}") diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 24b65b91..a4b253a0 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -4,9 +4,14 @@ if(NOT DEFINED UD_DEBUG) set(UD_DEBUG "$ENV{UD_DEBUG}") endif() if(UD_DEBUG STREQUAL "") + message("UD_DEBUG is not set, assuming 0.") set(UD_DEBUG 0) endif() +if(NOT DEFINED WOKWI) + set(WOKWI "$ENV{WOKWI}") +endif() + # Make sure we reconfigure if UD_DEBUG changes set_property(DIRECTORY PROPERTY UD_DEBUG_TRACKER "${UD_DEBUG}") @@ -26,6 +31,10 @@ else() add_compile_options(-O2) endif() +if(WOKWI) + add_compile_definitions(WOKWI) +endif() + # Set Git version execute_process( COMMAND git describe --tags --dirty diff --git a/main/devices/Device.hpp b/main/devices/Device.hpp index 1df914c5..04e62441 100644 --- a/main/devices/Device.hpp +++ b/main/devices/Device.hpp @@ -238,10 +238,12 @@ class ConsoleProvider : public LogConsumer { ConsoleProvider(Queue& logRecords, Level recordedLevel) : logRecords(logRecords) , recordedLevel(recordedLevel) { +#ifndef WOKWI Serial.begin(115200); Serial1.begin(115200, SERIAL_8N1, pins::RXD0->getGpio(), pins::TXD0->getGpio()); #if Serial != Serial0 Serial0.begin(115200); +#endif #endif Log.setConsumer(this); } diff --git a/main/kernel/Log.hpp b/main/kernel/Log.hpp index 90caf58e..0ff27de3 100644 --- a/main/kernel/Log.hpp +++ b/main/kernel/Log.hpp @@ -136,27 +136,17 @@ class FarmhubLog : public LogConsumer { } void printlnToSerial(const char* message = "") { - Serial.println(message); - Serial1.println(message); -#if Serial != Serial0 - Serial0.println(line); -#endif + printf("%s\n", message); } void printToSerial(const char* message) { - Serial.print(message); - Serial1.print(message); -#if Serial != Serial0 - Serial0.print(line); -#endif + printf("%s", message); } inline void __attribute__((format(printf, 2, 3))) printfToSerial(const char* format, ...) { va_list args; va_start(args, format); - withFormattedString(format, args, [this](const char* buffer) { - printToSerial(buffer); - }); + vprintf(format, args); va_end(args); } diff --git a/main/kernel/drivers/MqttDriver.hpp b/main/kernel/drivers/MqttDriver.hpp index 4414fbd1..942897a1 100644 --- a/main/kernel/drivers/MqttDriver.hpp +++ b/main/kernel/drivers/MqttDriver.hpp @@ -372,6 +372,11 @@ class MqttDriver { String clientKey; MdnsRecord mqttServer; +#ifdef WOKWI + Log.debug("MQTT: Using MQTT server on Wokwi host"); + mqttServer.hostname = "host.wokwi.internal"; + mqttServer.port = 1883; +#else if (config.host.get().length() > 0) { mqttServer.hostname = config.host.get(); mqttServer.port = config.port.get(); @@ -384,6 +389,7 @@ class MqttDriver { // TODO Handle lookup failure mdns.lookupService("mqtt", "tcp", mqttServer, trustMdnsCache); } +#endif String hostname; if (mqttServer.ip == IPAddress()) { diff --git a/main/kernel/drivers/RtcDriver.hpp b/main/kernel/drivers/RtcDriver.hpp index 56901717..ff08699b 100644 --- a/main/kernel/drivers/RtcDriver.hpp +++ b/main/kernel/drivers/RtcDriver.hpp @@ -90,6 +90,10 @@ class RtcDriver { return; } +#ifdef WOKWI + Log.debug("RTC: using default NTP server for Wokwi"); + ntpClient.emplace(udp); +#else if (ntpConfig.host.get().length() > 0) { Log.info("RTC: using NTP server %s from configuration", ntpConfig.host.get().c_str()); @@ -107,6 +111,7 @@ class RtcDriver { ntpClient.emplace(udp); } } +#endif // TODO Use built in configTime() instead // We are using the external NTP client library, because the built in configTime() does not diff --git a/main/kernel/drivers/WiFiDriver.hpp b/main/kernel/drivers/WiFiDriver.hpp index c46552ee..de9736cd 100644 --- a/main/kernel/drivers/WiFiDriver.hpp +++ b/main/kernel/drivers/WiFiDriver.hpp @@ -148,6 +148,11 @@ class WiFiDriver { // Print wifi status Log.debug("WiFi status: %d", WiFi.status()); +#ifdef WOKWI + Log.debug("Skipping WiFi provisioning on Wokwi"); + // Use Wokwi WiFi network + WiFi.begin("Wokwi-GUEST", "", 6); +#else StringPrint qr; // BLE Provisioning using the ESP SoftAP Prov works fine for any BLE SoC, including ESP32, ESP32S3 and ESP32C3. #if CONFIG_BLUEDROID_ENABLED && !defined(USE_SOFT_AP) @@ -165,6 +170,7 @@ class WiFiDriver { #endif Log.debug("%s", qr.buffer.c_str()); +#endif } static constexpr const char* pop = "abcd1234"; diff --git a/main/main.cpp b/main/main.cpp index 208c0664..f41b5c53 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,3 +1,7 @@ +// Helper macros to convert macro to string +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + #include #include diff --git a/wokwi/devenv/.gitignore b/wokwi/devenv/.gitignore new file mode 100644 index 00000000..9852410d --- /dev/null +++ b/wokwi/devenv/.gitignore @@ -0,0 +1,3 @@ +/.env +/mosquitto/data/ +/mosquitto/log/ diff --git a/wokwi/devenv/docker-compose.yaml b/wokwi/devenv/docker-compose.yaml new file mode 100644 index 00000000..9f0bb821 --- /dev/null +++ b/wokwi/devenv/docker-compose.yaml @@ -0,0 +1,15 @@ +services: + mqtt-relay: + build: mosquitto + environment: + - BROKER_ADDRESS=${BROKER_ADDRESS} + ports: + - "1883:1883" + volumes: + - ./mosquitto/data:/mosquitto/data + - ./mosquitto/log:/mosquitto/log + + wokwi: + image: wokwi/wokwi-ci-server + ports: + - "9177:3000" diff --git a/wokwi/devenv/mosquitto/Dockerfile b/wokwi/devenv/mosquitto/Dockerfile new file mode 100644 index 00000000..f8fd3658 --- /dev/null +++ b/wokwi/devenv/mosquitto/Dockerfile @@ -0,0 +1,12 @@ +# syntax=docker/dockerfile:1 +FROM eclipse-mosquitto:2.0-openssl + +# Install envsubst (part of GNU gettext) +RUN apk add --no-cache gettext + +COPY mosquitto.conf.template /mosquitto/config/mosquitto.conf.template + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/wokwi/devenv/mosquitto/entrypoint.sh b/wokwi/devenv/mosquitto/entrypoint.sh new file mode 100644 index 00000000..431dbc83 --- /dev/null +++ b/wokwi/devenv/mosquitto/entrypoint.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +envsubst < /mosquitto/config/mosquitto.conf.template > /mosquitto/config/mosquitto.conf +exec mosquitto -c /mosquitto/config/mosquitto.conf diff --git a/wokwi/devenv/mosquitto/mosquitto.conf.template b/wokwi/devenv/mosquitto/mosquitto.conf.template new file mode 100644 index 00000000..05635965 --- /dev/null +++ b/wokwi/devenv/mosquitto/mosquitto.conf.template @@ -0,0 +1,6 @@ +allow_anonymous true +listener 1883 0.0.0.0 + +connection bumblebee_bridge +address ${BROKER_ADDRESS}:1883 +topic # both 0 diff --git a/wokwi/diagram.flow-control-mk6.json b/wokwi/diagram.flow-control-mk6.json new file mode 100644 index 00000000..b29a3646 --- /dev/null +++ b/wokwi/diagram.flow-control-mk6.json @@ -0,0 +1,156 @@ +{ + "version": 1, + "author": "Lóránt Pintér", + "editor": "wokwi", + "parts": [ + { "type": "board-esp32-s3-devkitc-1", "id": "esp", "top": -0.18, "left": 4.57, "attrs": {} }, + { + "type": "wokwi-led", + "id": "statusLed", + "top": -32.4, + "left": 186.2, + "attrs": { "color": "white", "label": "Status" } + }, + { + "type": "wokwi-resistor", + "id": "r1", + "top": 32.75, + "left": 163.2, + "attrs": { "value": "22" } + }, + { + "type": "wokwi-led", + "id": "statusLed2", + "top": -32.4, + "left": 253.4, + "attrs": { "color": "white", "label": "Status-2" } + }, + { + "type": "wokwi-resistor", + "id": "r7", + "top": 32.75, + "left": 230.4, + "attrs": { "value": "22" } + }, + { + "type": "wokwi-potentiometer", + "id": "battery", + "top": -106.9, + "left": 9.4, + "attrs": { "value": "950" } + }, + { + "type": "wokwi-resistor", + "id": "r2", + "top": 119.45, + "left": -116.6, + "rotate": 180, + "attrs": { "value": "22" } + }, + { + "type": "wokwi-led", + "id": "dain1Led", + "top": 63.6, + "left": -178.6, + "attrs": { "color": "yellow", "label": "DAIN1", "flip": "" } + }, + { + "type": "wokwi-led", + "id": "dain2Led", + "top": 63.6, + "left": -111.4, + "attrs": { "color": "orange", "label": "DAIN2", "flip": "" } + }, + { + "type": "wokwi-resistor", + "id": "r3", + "top": 119.45, + "left": -193.4, + "rotate": 180, + "attrs": { "value": "22" } + }, + { + "type": "wokwi-led", + "id": "nsleepLed", + "top": 140.4, + "left": -53.8, + "attrs": { "color": "red", "label": "NSleep", "flip": "" } + }, + { + "type": "wokwi-resistor", + "id": "r4", + "top": 196.25, + "left": -59, + "rotate": 180, + "attrs": { "value": "22" } + }, + { + "type": "wokwi-led", + "id": "dbin1Led", + "top": 169.2, + "left": -178.6, + "attrs": { "color": "yellow", "label": "DBIN1", "flip": "" } + }, + { + "type": "wokwi-resistor", + "id": "r5", + "top": 227.25, + "left": -109.6, + "rotate": 180, + "attrs": { "value": "22" } + }, + { + "type": "wokwi-led", + "id": "dbin2Led", + "top": 169.2, + "left": -111.4, + "attrs": { "color": "orange", "label": "DBIN2", "flip": "" } + }, + { + "type": "wokwi-resistor", + "id": "r6", + "top": 225.05, + "left": -193.4, + "rotate": 180, + "attrs": { "value": "22" } + }, + { + "type": "wokwi-pushbutton", + "id": "btn1", + "top": 169.4, + "left": 153.6, + "attrs": { "color": "green", "label": "Flash", "bounce": "0" } + } + ], + "connections": [ + [ "esp:TX", "$serialMonitor:RX", "", [] ], + [ "esp:RX", "$serialMonitor:TX", "", [] ], + [ "statusLed:C", "esp:2", "black", [] ], + [ "statusLed:A", "r1:2", "green", [] ], + [ "r1:1", "esp:3V3.2", "green", [ "v0" ] ], + [ "battery:VCC", "esp:3V3.1", "red", [ "v0" ] ], + [ "battery:GND", "esp:GND.2", "black", [ "v0" ] ], + [ "battery:SIG", "esp:1", "green", [ "v0" ] ], + [ "dain1Led:A", "esp:16", "green", [] ], + [ "dain1Led:C", "r3:2", "green", [ "h-28.4", "v19.2" ] ], + [ "r2:1", "esp:GND.1", "black", [ "v0" ] ], + [ "dain2Led:A", "esp:17", "green", [] ], + [ "dain2Led:C", "r2:2", "green", [ "h0" ] ], + [ "r3:1", "esp:GND.1", "black", [ "v0" ] ], + [ "nsleepLed:A", "esp:47", "green", [ "v0" ] ], + [ "dbin1Led:A", "esp:8", "green", [] ], + [ "dbin1Led:C", "r6:2", "green", [] ], + [ "r5:1", "esp:GND.1", "black", [ "v0" ] ], + [ "dbin2Led:A", "esp:18", "green", [] ], + [ "dbin2Led:C", "r5:2", "green", [] ], + [ "r6:1", "esp:GND.1", "black", [ "v0" ] ], + [ "statusLed2:A", "r7:2", "green", [] ], + [ "r7:1", "esp:3V3.2", "green", [ "v0" ] ], + [ "statusLed2:C", "esp:4", "green", [ "v0" ] ], + [ "nsleepLed:C", "r4:2", "green", [ "h-18.8", "v28.8" ] ], + [ "r4:1", "esp:GND.1", "black", [ "v0" ] ], + [ "esp:GND.4", "btn1:2.l", "black", [ "h0" ] ], + [ "esp:0", "btn1:1.l", "green", [ "h0" ] ] + ], + "dependencies": {} +} diff --git a/wokwi/wokwi.toml b/wokwi/wokwi.toml new file mode 100644 index 00000000..54f63b75 --- /dev/null +++ b/wokwi/wokwi.toml @@ -0,0 +1,4 @@ +[wokwi] +version = 1 +firmware = '../build/flasher_args.json' +elf = '../build/ugly-duckling.elf'