commit 805e492fe957c7e8593198449db5acd40e8a4c33 Author: Patrick Haßel Date: Wed Aug 27 23:12:18 2025 +0200 WiFi, Hostname, OTA, Button, Relay, Status, config diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03f4a3c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.pio diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..b0e3e11 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,8 @@ +[env:Sonoff4ChPro] +platform = espressif8266 +board = esp8285 +framework = arduino +upload_speed = 921600 +upload_port = 10.0.0.178 +monitor_speed = 115200 +build.filesystem = littlefs \ No newline at end of file diff --git a/src/Button.h b/src/Button.h new file mode 100644 index 0000000..0f33a90 --- /dev/null +++ b/src/Button.h @@ -0,0 +1,74 @@ +#ifndef BUTTON_H +#define BUTTON_H + +#include + +enum ButtonEvent { + BUTTON_PRESSED, + BUTTON_PRESSED_LONG, + BUTTON_RELEASED, + BUTTON_RELEASED_SHORT, + BUTTON_RELEASED_LONG, +}; + +class Button { + +public: + + typedef void (*callback_t)(ButtonEvent event); + +private: + + const uint8_t pin; + + const bool pullup; + + const bool inverted; + + const callback_t callback; + + bool lastState = false; + + bool pressedLong = false; + + unsigned long lastMillis = 0; + +public: + + explicit Button(const uint8_t pin, const bool pullup, const bool inverted, const callback_t callback) : pin(pin), pullup(pullup), inverted(inverted), callback(callback) { + // + } + + void setup() { + pinMode(pin, pullup ? INPUT_PULLUP : INPUT); + lastMillis = millis(); + lastState = (digitalRead(pin) == HIGH) ^ inverted; + } + + void loop() { + const auto currentMillis = millis(); + const auto currentState = digitalRead(pin) == LOW; + const auto duration = currentMillis - lastMillis; + if (lastState != currentState && duration >= 200) { + if (currentState) { + callback(BUTTON_PRESSED); + } else { + callback(BUTTON_RELEASED); + if (pressedLong) { + callback(BUTTON_RELEASED_LONG); + } else { + callback(BUTTON_RELEASED_SHORT); + } + pressedLong = false; + } + lastState = currentState; + lastMillis = currentMillis; + } else if (lastState && duration >= 3000 && !pressedLong) { + pressedLong = true; + callback(BUTTON_PRESSED_LONG); + } + } + +}; + +#endif diff --git a/src/Output.h b/src/Output.h new file mode 100644 index 0000000..d5d406b --- /dev/null +++ b/src/Output.h @@ -0,0 +1,47 @@ +#ifndef OUTPUT_H +#define OUTPUT_H + +#include + +class Output { + + const char *name; + + const uint8_t pin; + + const bool inverted; + + const bool logState; + +public: + + Output(const char *name, const uint8_t pin, const bool inverted, const bool logState): name(name), pin(pin), inverted(inverted), logState(logState) { + // + } + + void setup() const { + pinMode(pin, OUTPUT); + } + + void set(const bool state) const { + if (logState && state != get()) { + Serial.printf("%s: %s\n", name, state ? "ON" : "OFF"); + } + digitalWrite(pin, state ^ inverted ? HIGH : LOW); + } + + bool get() const { + return (digitalRead(pin) == HIGH) ^ inverted; + } + + void toggle() const { + set(!get()); + } + + void loop() const { + // TODO auto off etc + } + +}; + +#endif diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..3aa0c5e --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,35 @@ +#include "config.h" + +#include + +void configSetup() { + LittleFS.begin(); +} + +File configOpen(const char *name, const char *mode) { + char path[64]; + snprintf(path, sizeof(path), "/%s", name); + return LittleFS.open(path, mode); +} + +String configReadString(const char *name, const char *fallback) { + if (auto file = configOpen(name, "r")) { + const auto content = file.readString(); + file.close(); + return content; + } + return fallback; +} + +bool configWriteString(const char *name, const char *fallback, const String &value) { + if (configReadString(name, fallback) == value) { + return false; + } + Serial.printf("\"%s\" = \"%s\"", name, value.c_str()); + if (auto file = configOpen(name, "w")) { + file.write(value.c_str(), value.length()); + file.close(); + return true; + } + return false; +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..b1a4a9a --- /dev/null +++ b/src/config.h @@ -0,0 +1,12 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include + +void configSetup(); + +String configReadString(const char *name, const char *fallback); + +bool configWriteString(const char *name, const char *fallback, const String &value); + +#endif diff --git a/src/io.cpp b/src/io.cpp new file mode 100644 index 0000000..dcbdd49 --- /dev/null +++ b/src/io.cpp @@ -0,0 +1,7 @@ +#include "io.h" + +void buttonCallback(const Output &output, const ButtonEvent event) { + if (event == BUTTON_PRESSED) { + output.toggle(); + } +} diff --git a/src/io.h b/src/io.h new file mode 100644 index 0000000..5ce4547 --- /dev/null +++ b/src/io.h @@ -0,0 +1,55 @@ +#ifndef IO_H +#define IO_H + +#include "Button.h" +#include "Output.h" + +void buttonCallback(const Output &output, ButtonEvent event); + +inline Output relay1("RELAY #1", 12, false, true); + +inline Output relay2("RELAY #2", 5, false, true); + +inline Output relay3("RELAY #3", 4, false, true); + +inline Output relay4("RELAY #4", 15, false, true); + +inline Output status("Status", 13, true, false); + +inline Button button1(0, true, true, [](const ButtonEvent event) { buttonCallback(relay1, event); }); + +inline Button button2(9, true, true, [](const ButtonEvent event) { buttonCallback(relay2, event); }); + +inline Button button3(10, true, true, [](const ButtonEvent event) { buttonCallback(relay3, event); }); + +inline Button button4(14, true, true, [](const ButtonEvent event) { buttonCallback(relay4, event); }); + +inline void ioSetup() { + button1.setup(); + button2.setup(); + button3.setup(); + button4.setup(); + + status.setup(); + + relay1.setup(); + relay2.setup(); + relay3.setup(); + relay4.setup(); +} + +inline void ioLoop() { + button1.loop(); + button2.loop(); + button3.loop(); + button4.loop(); + + status.loop(); + + relay1.loop(); + relay2.loop(); + relay3.loop(); + relay4.loop(); +} + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..3b38c71 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,20 @@ +#include + +#include "config.h" +#include "io.h" +#include "wifi.h" + +void setup() { + delay(500); + Serial.begin(115200); + Serial.print("\n\n\nStartup!\n"); + + configSetup(); + ioSetup(); + wifiSetup(); +} + +void loop() { + ioLoop(); + ArduinoOTA.handle(); +} diff --git a/src/wifi.cpp b/src/wifi.cpp new file mode 100644 index 0000000..963d2eb --- /dev/null +++ b/src/wifi.cpp @@ -0,0 +1,63 @@ +#include "wifi.h" + +#include + +#include "config.h" +#include "io.h" + +#define DEFAULT_HOSTNAME "PatrixSonoff4ChPro" +#define DEFAULT_WIFI_SSID "HappyNet" +#define DEFAULT_WIFI_PASS "1Grausame!Sackratte7" + +void wifiChangeHostname(const char *hostname) { + if (configWriteString("hostname", DEFAULT_HOSTNAME, hostname)) { + WiFi.setHostname(hostname); + Serial.printf("Changed hostname to: %s\n", hostname); + } +} + +void wifiChangeSSID(const char *ssid) { + if (configWriteString("wifiSSID", DEFAULT_WIFI_SSID, ssid)) { + Serial.printf("Changed SSID to: %s\n", ssid); + } +} + +void wifiChangePassword(const char *password) { + configWriteString("wifiPass", DEFAULT_WIFI_PASS, password); + Serial.printf("Changed password\n"); +} + +void wifiSetup() { + const auto hostname = configReadString("hostname", DEFAULT_HOSTNAME); + const auto wifiSSID = configReadString("wifiSSID", DEFAULT_WIFI_SSID); + const auto wifiPass = configReadString("wifiPass", DEFAULT_WIFI_PASS); + WiFi.hostname(hostname); + WiFi.begin(wifiSSID, wifiPass); + while (WiFi.localIP() == 0UL) { + delay(500); + status.toggle(); + } + + yield(); + status.set(false); + Serial.printf("Connected as \"%s\" (%s)\n", WiFi.hostname().c_str(), WiFi.localIP().toString().c_str()); + + ArduinoOTA.onStart([] { + Serial.println("OTA begin..."); + status.set(true); + }); + ArduinoOTA.onProgress([](const unsigned progress, const unsigned total) { + Serial.printf("OTA: %3d%%\r", 100 * progress / total); + status.toggle(); + }); + ArduinoOTA.onEnd([] { + Serial.println("\nOTA success!"); + status.set(true); + }); + ArduinoOTA.onError([](const ota_error_t error) { + Serial.printf("\nOTA error %u\n", error); + status.set(false); + }); + ArduinoOTA.begin(); + delay(1000); +} diff --git a/src/wifi.h b/src/wifi.h new file mode 100644 index 0000000..cb6cf16 --- /dev/null +++ b/src/wifi.h @@ -0,0 +1,12 @@ +#ifndef WIFI_H +#define WIFI_H + +void wifiChangeHostname(const char *hostname); + +void wifiChangeSSID(const char *ssid); + +void wifiChangePassword(const char *password); + +void wifiSetup(); + +#endif