From 3dd87a4a9e1eebfee3566db285892fcb6f4bb36e Mon Sep 17 00:00:00 2001 From: Mikey Date: Mon, 24 May 2021 17:16:59 +1200 Subject: [PATCH] clean up code (#2) --- .vscode/settings.json | 10 +- include/overload.hpp | 4 + include/{store.hpp => redux.hpp} | 6 +- package.json | 2 +- src/effects/bot.hpp | 10 ++ src/effects/context.hpp | 9 ++ src/effects/leds.hpp | 26 ++++ src/main.cpp | 201 ++++--------------------------- src/models/bot.hpp | 36 ++++++ src/models/clock.hpp | 23 ++++ src/models/leds.hpp | 38 ++++++ src/server.hpp | 70 +++++++++++ src/store.hpp | 10 ++ src/timer.hpp | 43 +++++++ tools/cdata.js | 1 - ui/index.html | 41 +++---- 16 files changed, 326 insertions(+), 204 deletions(-) rename include/{store.hpp => redux.hpp} (96%) create mode 100644 src/effects/bot.hpp create mode 100644 src/effects/context.hpp create mode 100644 src/effects/leds.hpp create mode 100644 src/models/bot.hpp create mode 100644 src/models/clock.hpp create mode 100644 src/models/leds.hpp create mode 100644 src/server.hpp create mode 100644 src/store.hpp create mode 100644 src/timer.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 4eb122d..7fdd295 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -46,6 +46,14 @@ "cinttypes": "cpp", "typeinfo": "cpp", "variant": "cpp", - "iostream": "cpp" + "iostream": "cpp", + "bitset": "cpp", + "chrono": "cpp", + "condition_variable": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "ratio": "cpp", + "regex": "cpp", + "shared_mutex": "cpp" } } \ No newline at end of file diff --git a/include/overload.hpp b/include/overload.hpp index 0b4a87b..e3d655c 100644 --- a/include/overload.hpp +++ b/include/overload.hpp @@ -1,3 +1,7 @@ +// https://github.com/mpark/variant/issues/44#issuecomment-377333877 + +#pragma once + template struct OverLoaded; diff --git a/include/store.hpp b/include/redux.hpp similarity index 96% rename from include/store.hpp rename to include/redux.hpp index d5f5b96..05d0e76 100644 --- a/include/store.hpp +++ b/include/redux.hpp @@ -30,6 +30,8 @@ SOFTWARE. See also: */ +#pragma once + #include #include @@ -42,7 +44,7 @@ class Store { Store(Reducer, STATE_T); void subscribe(Subscriber); void dispatch(ACTION_T); - STATE_T getState(); + STATE_T get_state(); private: Reducer reducer; @@ -71,6 +73,6 @@ void Store::dispatch(ACTION_T action) { } template -STATE_T Store::getState() { +STATE_T Store::get_state() { return state; } \ No newline at end of file diff --git a/package.json b/package.json index 1da2cbd..7cebda3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "", "scripts": { "build": "node tools/cdata.js", - "dev": "nodemon -e js,html,htm,css,png,jpg,gif,ico,js -w tools/ -w wled00/data/ -x node tools/cdata.js" + "dev": "nodemon -e js,html,htm,css,png,jpg,gif,ico,js -w tools/ -w src/ui/ -x node tools/cdata.js" }, "repository": { "type": "git", diff --git a/src/effects/bot.hpp b/src/effects/bot.hpp new file mode 100644 index 0000000..7d2c291 --- /dev/null +++ b/src/effects/bot.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +namespace BotEffects { + void setup(BotContext *context) { + LedsEffects::setup(context); + } +} \ No newline at end of file diff --git a/src/effects/context.hpp b/src/effects/context.hpp new file mode 100644 index 0000000..b91ec0d --- /dev/null +++ b/src/effects/context.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +struct BotContext { + BotStore *store; + BotTimer *timer; +}; \ No newline at end of file diff --git a/src/effects/leds.hpp b/src/effects/leds.hpp new file mode 100644 index 0000000..a7444c8 --- /dev/null +++ b/src/effects/leds.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include + +namespace LedsEffects { + void output(void *params) { + BotContext *context = (BotContext*) params; + auto store = context->store; + auto state = store->get_state(); + digitalWrite(LED_GREEN, state.leds.green); + digitalWrite(LED_BLUE, state.leds.blue); + digitalWrite(LED_RED, state.leds.red); + } + + void setup(BotContext *context) { + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_BLUE, OUTPUT); + pinMode(LED_RED, OUTPUT); + + auto timer = context->timer; + timer->set_interval(10L, output, context); + } +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0d3173f..cd438c3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,15 +1,11 @@ #include -#include -#include -#include #include #include -#include -#include #include - -#include +#include +#include +#include #if !( defined(STM32F0) || defined(STM32F1) || defined(STM32F2) || defined(STM32F3) ||defined(STM32F4) || defined(STM32F7) || \ defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32H7) ||defined(STM32G0) || defined(STM32G4) || \ @@ -20,121 +16,14 @@ // pins // - https://github.com/stm32duino/Arduino_Core_STM32/blob/master/variants/STM32F7xx/F765Z(G-I)T_F767Z(G-I)T_F777ZIT/variant_NUCLEO_F767ZI.h -#define TIMER_INTERVAL_MS 100 -#define HW_TIMER_INTERVAL_MS 50 - -// F767ZI can select Timer from TIM1-TIM14 -STM32Timer ITimer(TIM1); - -STM32_ISR_Timer ISR_Timer; - -struct StateLeds { - bool green = true; - bool blue = true; - bool red = true; +BotTimer timer; +BotServer server; +BotStore store; +BotContext context = { + &store, + &timer }; -enum class LED_ID { GREEN, RED, BLUE }; -struct ActionLedToggle { - LED_ID led_id; -}; -using ActionLeds = mpark::variant; - -StateLeds reducer_leds(StateLeds state, ActionLeds action) { - mpark::visit(overload( - [&state](const ActionLedToggle action) { - switch (action.led_id) { - case LED_ID::GREEN: - state.green = !state.green; - break; - case LED_ID::BLUE: - state.blue = !state.blue; - break; - case LED_ID::RED: - state.red = !state.red; - break; - } - } - ), action); - - return state; -} - -struct ActionClockTick {}; -using ActionClock = mpark::variant; - -struct StateClock { - uint16_t ticks = 0; -}; - -StateClock reducer_clock(StateClock state, ActionClock action) { - mpark::visit(overload( - [&state](const ActionClockTick) { - state.ticks++; - } - ), action); - - return state; -} - -struct StateBot { - StateLeds leds = StateLeds {}; - StateClock clock = StateClock {}; -}; - -using ActionBot = mpark::variant; - -StateBot reducer_bot(StateBot state, ActionBot action) { - noInterrupts(); - - mpark::visit(overload( - [&state](const ActionLeds a) { - state.leds = reducer_leds(state.leds, a); - }, - [&state](const ActionClock a) { - state.clock = reducer_clock(state.clock, a); - } - ), action); - - interrupts(); - - return state; -} - -Store store(reducer_bot, StateBot {}); - -IPAddress ip(10, 0, 0, 2); -AsyncWebServer server(80); -// uint8_t mac[] = { MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5 }; -uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x01 }; -AsyncEventSource events("/events"); - -void handleNotFound(AsyncWebServerRequest *request) -{ - String message = "File Not Found\n\n"; - - message += "URI: "; - //message += server.uri(); - message += request->url(); - message += "\nMethod: "; - message += (request->method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; - message += request->args(); - message += "\n"; - - for (uint8_t i = 0; i < request->args(); i++) - { - message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; - } - - request->send(404, "text/plain", message); -} - -void TimerHandler() -{ - ISR_Timer.run(); -} - volatile bool has_state_changed; void setup() @@ -143,72 +32,30 @@ void setup() while (!Serial); delay(1000); - - Serial.print(F("\nStarting TimerInterrupt on ")); Serial.println(BOARD_NAME); - Serial.println(STM32_TIMER_INTERRUPT_VERSION); - Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); - - // Interval in microsecs - if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler)) - { - Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(millis()); - } - else { - Serial.println(F("Can't set ITimer. Select another freq. or timer")); - } - - Serial.println("\nStart AsyncWebServer on " + String(BOARD_NAME)); - Ethernet.begin(mac, ip); - - server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) - { - AsyncWebServerResponse *response = request->beginResponse(200, "text/html", PAGE_INDEX); - request->send(response); - }); - - events.onConnect([](AsyncEventSourceClient * client) - { - client->send("hello!", NULL); - }); - - has_state_changed = false; - store.subscribe([](StateBot state){ - has_state_changed = true; - }); - server.addHandler(&events); - server.onNotFound(handleNotFound); + timer.setup(); server.begin(); + BotEffects::setup(&context); - Serial.print(F("HTTP EthernetWebServer is @ IP : ")); - Serial.println(Ethernet.localIP()); - - pinMode(LED_GREEN, OUTPUT); - pinMode(LED_BLUE, OUTPUT); - pinMode(LED_RED, OUTPUT); - - ISR_Timer.setInterval(10L, [](){ - StateBot state = store.getState(); - digitalWrite(LED_GREEN, state.leds.green); - digitalWrite(LED_BLUE, state.leds.blue); - digitalWrite(LED_RED, state.leds.red); + store.subscribe([](BotModel::State state) { + has_state_changed = true; }); - ISR_Timer.setInterval(1000L, [](){ - store.dispatch(ActionLedToggle { - led_id: LED_ID::GREEN + timer.set_interval(1000L, [](){ + store.dispatch(LedsModel::ActionToggle { + led_id: LedsModel::LED_ID::GREEN }); }); - ISR_Timer.setInterval(2000L, [](){ - store.dispatch(ActionLedToggle { - led_id: LED_ID::BLUE + timer.set_interval(2000L, [](){ + store.dispatch(LedsModel::ActionToggle { + led_id: LedsModel::LED_ID::BLUE }); }); - ISR_Timer.setInterval(4000L, [](){ - store.dispatch(ActionLedToggle { - led_id: LED_ID::RED + timer.set_interval(4000L, [](){ + store.dispatch(LedsModel::ActionToggle { + led_id: LedsModel::LED_ID::RED }); }); } @@ -216,14 +63,14 @@ void setup() void loop() { if (has_state_changed) { - StateBot state = store.getState(); + BotModel::State state = store.get_state(); String status = ""; if (state.leds.green) status += ":green"; if (state.leds.blue) status += ":blue"; if (state.leds.red) status += ":red"; - events.send(status.c_str(), "status", millis()); + server.events.send(status.c_str(), "status", millis()); has_state_changed = false; } } \ No newline at end of file diff --git a/src/models/bot.hpp b/src/models/bot.hpp new file mode 100644 index 0000000..44865dc --- /dev/null +++ b/src/models/bot.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +namespace BotModel { + struct State { + LedsModel::State leds = LedsModel::State {}; + ClockModel::State clock = ClockModel::State {}; + }; + + using Action = mpark::variant; + + State reducer(State state, Action action) { + noInterrupts(); + + mpark::visit(overload( + [&state](const LedsModel::Action a) { + state.leds = LedsModel::reducer(state.leds, a); + }, + [&state](const ClockModel::Action a) { + state.clock = ClockModel::reducer(state.clock, a); + } + ), action); + + interrupts(); + + return state; + } +} \ No newline at end of file diff --git a/src/models/clock.hpp b/src/models/clock.hpp new file mode 100644 index 0000000..d576f0f --- /dev/null +++ b/src/models/clock.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace ClockModel { + struct ActionTick {}; + using Action = mpark::variant; + + struct State { + uint16_t ticks = 0; + }; + + State reducer(State state, Action action) { + mpark::visit(overload( + [&state](const ActionTick) { + state.ticks++; + } + ), action); + + return state; + } +} \ No newline at end of file diff --git a/src/models/leds.hpp b/src/models/leds.hpp new file mode 100644 index 0000000..e876211 --- /dev/null +++ b/src/models/leds.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace LedsModel { + struct State { + bool green = true; + bool blue = true; + bool red = true; + }; + + enum class LED_ID { GREEN, RED, BLUE }; + struct ActionToggle { + LED_ID led_id; + }; + using Action = mpark::variant; + + State reducer(State state, Action action) { + mpark::visit(overload( + [&state](const ActionToggle action) { + switch (action.led_id) { + case LED_ID::GREEN: + state.green = !state.green; + break; + case LED_ID::BLUE: + state.blue = !state.blue; + break; + case LED_ID::RED: + state.red = !state.red; + break; + } + } + ), action); + + return state; + } +} \ No newline at end of file diff --git a/src/server.hpp b/src/server.hpp new file mode 100644 index 0000000..81c4df5 --- /dev/null +++ b/src/server.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include + +// TODO: generate random mac on first run and store in EEPROM +uint8_t mac[] = { 0x7F, 0x41, 0x26, 0xB1, 0x0E, 0xC6 }; + +class BotServer { + public: + IPAddress ip; + AsyncWebServer web_server; + AsyncEventSource events; + + BotServer() : + ip(10, 0, 0, 2), + web_server(80), + events("/events") {}; + + void begin() { + Serial.println("\nStart AsyncWebServer on " + String(BOARD_NAME)); + Ethernet.begin(mac, ip); + + web_server.on("/", HTTP_ANY, std::bind(&BotServer::handle_index, this, std::placeholders::_1)); + web_server.onNotFound(std::bind(&BotServer::handle_not_found, this, std::placeholders::_1)); + + events.onConnect(std::bind(&BotServer::on_events_connect, this, std::placeholders::_1)); + web_server.addHandler(&events); + + web_server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(Ethernet.localIP()); + } + + void handle_index (AsyncWebServerRequest *request) { + AsyncWebServerResponse *response = request->beginResponse(200, "text/html", PAGE_INDEX); + request->send(response); + } + + void handle_not_found (AsyncWebServerRequest *request) { + String message = "File Not Found\n\n"; + + message += "URI: "; + //message += server.uri(); + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + } + + void on_events_connect (AsyncEventSourceClient *client) { + client->send("hello!", NULL); + } +}; \ No newline at end of file diff --git a/src/store.hpp b/src/store.hpp new file mode 100644 index 0000000..e913f97 --- /dev/null +++ b/src/store.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include + +class BotStore : public Store { + public: + BotStore(): Store(BotModel::reducer, BotModel::State {}) {}; +}; \ No newline at end of file diff --git a/src/timer.hpp b/src/timer.hpp new file mode 100644 index 0000000..8dcb8aa --- /dev/null +++ b/src/timer.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#define HW_TIMER_INTERVAL_MS 50 + +STM32_ISR_Timer isr_timer; + +void timer_handler() { + isr_timer.run(); +} + +class BotTimer { + public: + STM32Timer itimer; + + // F767ZI can select itimer from TIM1-TIM14 + BotTimer() : itimer(TIM1) {}; + + void setup() { + Serial.print(F("\nStarting TimerInterrupt on ")); Serial.println(BOARD_NAME); + Serial.println(STM32_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (itimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, timer_handler)) + { + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(millis()); + } + else { + Serial.println(F("Can't set ITimer. Select another freq. or timer")); + } + } + + int16_t set_interval(uint32_t interval, timerCallback callback) { + return isr_timer.setInterval(interval, callback); + } + + int16_t set_interval(uint32_t interval, timerCallback_p callback, void *params) { + return isr_timer.setInterval(interval, callback, params); + } +}; \ No newline at end of file diff --git a/tools/cdata.js b/tools/cdata.js index 231433c..c3c1873 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -6,7 +6,6 @@ const inliner = require("inliner") const MinifyHTML = require("html-minifier-terser").minify const fs = require("fs") -const packageJson = require("../package.json") writeHtmlGzipped("INDEX", "ui/index.html", "src/ui/index.h") diff --git a/ui/index.html b/ui/index.html index 22ead42..d57926a 100644 --- a/ui/index.html +++ b/ui/index.html @@ -5,28 +5,25 @@

GridBot

\ No newline at end of file