Compare commits

..

No commits in common. "d86edf5e9a3bfb0fdc5cd0752438fdca94b8a23c" and "f97ab5f58bf286652a9a1c222d8539370217279c" have entirely different histories.

11 changed files with 274 additions and 351 deletions

View file

@ -1,61 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1611574965,
"narHash": "sha256-IYtGS077nJFIqSq6Cb8WEJwbqqd4K7S4vUuwPZKfLeo=",
"owner": "nixos",
"repo": "nixpkgs-channels",
"rev": "4762fba469e2baa82f983b262e2c06ac2fdaae67",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs-channels",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,28 +0,0 @@
{
inputs = {
nixpkgs = {
url = "github:nixos/nixpkgs-channels/nixos-unstable";
};
flake-utils = {
url = "github:numtide/flake-utils";
};
};
outputs = {
nixpkgs,
flake-utils,
...
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {
inherit system;
};
in rec {
devShell = pkgs.mkShell {
buildInputs = with pkgs; [
platformio
];
};
}
);
}

View file

@ -16,12 +16,7 @@ public:
UBaseType_t priority, BaseType_t coreID) UBaseType_t priority, BaseType_t coreID)
: port{port}, messageQueue{messageQueue}, : port{port}, messageQueue{messageQueue},
serialReceiverTask{ serialReceiverTask{
exec, "receive serial data", stackDepth, this, priority, coreID} { exec, "receive serial data", stackDepth, this, priority, coreID} {}
for (size_t i = 0; i < bufferSize; ++i) {
message::Message &msg = buffer.pop();
msg.second.give();
}
}
private: private:
static void exec(void *receiverPtr) { static void exec(void *receiverPtr) {
@ -33,32 +28,22 @@ private:
receiver->magicNumberBuf.clear(); receiver->magicNumberBuf.clear();
receiver->magicNumberBuf.resize(receiver->magicNumber.length()); receiver->magicNumberBuf.resize(receiver->magicNumber.length());
size_t availableBytes = receiver->port.available(); size_t availableBytes = receiver->port.available();
if (receiver->port.available() > 0 && receiver->port.peek() != 'H') { if (availableBytes >= receiver->magicNumber.length() + 1) {
receiver->port.read();
receiver->port.println("flush");
} else if (availableBytes >= receiver->magicNumber.length() + 1) {
receiver->port.readBytes(receiver->magicNumberBuf.data(), receiver->port.readBytes(receiver->magicNumberBuf.data(),
receiver->magicNumber.length()); receiver->magicNumber.length());
receiver->port.println("Received Magic Number:"); receiver->port.println(receiver->magicNumberBuf ==
receiver->port.println(receiver->magicNumberBuf == receiver->magicNumber receiver->magicNumber);
? "correct"
: "incorrect");
if (receiver->magicNumberBuf == receiver->magicNumber) { uint8_t size;
uint8_t size; receiver->port.readBytes(&size, sizeof(size));
receiver->port.readBytes(&size, sizeof(size)); if (receiver->port.available() >= size) {
if (receiver->port.available() >= size) { message::Message &msg = receiver->buffer.pop();
message::Message &msg = receiver->buffer.pop(); msg.second.lock();
receiver->port.printf("Current semaphore count: %u\n", msg.first.clear();
msg.second.getCount()); msg.first.resize(size);
msg.second.take(); receiver->port.readBytes(msg.first.data(), size);
msg.first.clear(); msg.second.unlock();
msg.first.resize(size); receiver->messageQueue.push(&msg);
receiver->port.readBytes(msg.first.data(), size);
// msg.second.unlock();
receiver->port.println("Push message to queue");
receiver->messageQueue.push(&msg);
}
} }
} }
} }

View file

@ -14,11 +14,11 @@ auto freertos::BinarySemaphore::operator=(
} }
auto freertos::BinarySemaphore::take(TickType_t timeout) -> bool { auto freertos::BinarySemaphore::take(TickType_t timeout) -> bool {
BaseType_t success = xSemaphoreTake(handle, timeout); BaseType_t success = xSemaphoreTake(handle, timeout);
return success == pdTRUE; return success == pdTRUE ? true : false;
} }
auto freertos::BinarySemaphore::give() -> bool { auto freertos::BinarySemaphore::give() -> bool {
BaseType_t success = xSemaphoreGive(handle); BaseType_t success = xSemaphoreGive(handle);
return success == pdTRUE; return success == pdTRUE ? true : false;
} }
auto freertos::BinarySemaphore::getCount() -> size_t { auto freertos::BinarySemaphore::getCount() -> size_t {

View file

@ -12,9 +12,9 @@ auto freertos::Mutex::operator=(const Mutex &&other) noexcept -> Mutex & {
} }
auto freertos::Mutex::lock(TickType_t timeout) -> bool{ auto freertos::Mutex::lock(TickType_t timeout) -> bool{
BaseType_t success = xSemaphoreTake(handle, timeout); BaseType_t success = xSemaphoreTake(handle, timeout);
return success == pdTRUE; return success == pdTRUE ? true : false;
} }
auto freertos::Mutex::unlock() -> bool { auto freertos::Mutex::unlock() -> bool {
BaseType_t success = xSemaphoreGive(handle); BaseType_t success = xSemaphoreGive(handle);
return success == pdTRUE; return success == pdTRUE ? true : false;
} }

View file

@ -1,9 +1,7 @@
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include <FreeRTOS/Mutex.h>
#include <FreeRTOS/Queue.h> #include <FreeRTOS/Queue.h>
#include <FreeRTOS/Task.h> #include <FreeRTOS/Task.h>
#include <FreeRTOS/Util.h>
#include <etl/optional.h> #include <etl/optional.h>
#include <etl/span.h> #include <etl/span.h>
@ -13,18 +11,13 @@ using LightAction = etl::pair<LightActionType, etl::optional<uint8_t>>;
template <int... PIN> class Controller { template <int... PIN> class Controller {
public: public:
Controller(size_t actionQueueSize, UBaseType_t controllerPriority, Controller(size_t actionQueueSize, UBaseType_t priority, BaseType_t coreID)
BaseType_t controllerCoreID, UBaseType_t faderPriority,
BaseType_t faderCoreID)
: lightPins{PIN...}, lightActions{actionQueueSize}, : lightPins{PIN...}, lightActions{actionQueueSize},
lightControllerTask{exec, "control lights", stackDepth, lightControllerTask{exec, "control lights", stackDepth,
this, controllerPriority, controllerCoreID}, this, priority, coreID} {
lightFaderTask{execFade, "fade lights", stackDepth,
this, faderPriority, faderCoreID} {
for (int pin : lightPins) { for (int pin : lightPins) {
pinMode(pin, OUTPUT); pinMode(pin, OUTPUT);
} }
lightStatesCurrent.second.unlock();
} }
freertos::Queue<LightAction> &getActionQueue() { return lightActions; } freertos::Queue<LightAction> &getActionQueue() { return lightActions; }
@ -33,7 +26,10 @@ public:
private: private:
static void exec(void *controllerPtr) { static void exec(void *controllerPtr) {
Controller *controller{static_cast<Controller *>(controllerPtr)}; Controller<PIN...> *controller{
static_cast<Controller<PIN...> *>(controllerPtr)};
etl::array<uint8_t, sizeof...(PIN)> lightStates;
const uint8_t step = 5;
while (true) { while (true) {
auto action{controller->getActionQueue().pop()}; auto action{controller->getActionQueue().pop()};
@ -43,84 +39,45 @@ private:
switch (type) { switch (type) {
case LightActionType::INCREASE: case LightActionType::INCREASE:
if (index.has_value() && if (index.has_value() && index.value() < lightStates.size() &&
index.value() < controller->lightStatesFinal.size() && lightStates[index.value()] <= 255 - step) {
controller->lightStatesFinal[index.value()] <= lightStates[index.value()] += step;
255 - controller->step) {
controller->lightStatesFinal[index.value()] += controller->step;
} }
break; break;
case LightActionType::DECREASE: case LightActionType::DECREASE:
if (index.has_value() && if (index.has_value() && index.value() < lightStates.size() &&
index.value() < controller->lightStatesFinal.size() && lightStates[index.value()] >= 0) {
controller->lightStatesFinal[index.value()] >= 0) { lightStates[index.value()] -= step;
controller->lightStatesFinal[index.value()] -= controller->step;
} }
break; break;
case LightActionType::TOGGLE: case LightActionType::TOGGLE:
if (index.has_value() && if (index.has_value() && index.value() < lightStates.size()) {
index.value() < controller->lightStatesFinal.size()) { lightStates[index.value()] =
controller->lightStatesCurrent.second.lock(); lightStates[index.value()] == 0 ? 255 : 0;
controller->lightStatesFinal[index.value()] =
controller->lightStatesFinal[index.value()] == 0 ? 255 : 0;
controller->lightStatesCurrent.first[index.value()] =
controller->lightStatesFinal[index.value()];
controller->lightStatesCurrent.second.unlock();
} }
break; break;
case LightActionType::ON: case LightActionType::ON:
controller->lightStatesCurrent.second.lock(); lightStates.fill(255);
controller->lightStatesFinal.fill(255);
controller->lightStatesCurrent.first.fill(255);
controller->lightStatesCurrent.second.unlock();
break; break;
case LightActionType::OFF: case LightActionType::OFF:
controller->lightStatesCurrent.second.lock(); lightStates.fill(0);
controller->lightStatesFinal.fill(0);
controller->lightStatesCurrent.first.fill(0);
controller->lightStatesCurrent.second.unlock();
break; break;
default: default:
break; break;
} }
}
}
}
static void execFade(void *controllerPtr) { for (size_t lightIndex = 0; lightIndex < lightStates.size();
Controller *controller{static_cast<Controller *>(controllerPtr)}; ++lightIndex) {
while (true) { analogWrite(controller->getPins()[lightIndex],
freertos::sleep(controller->fadeRate); lightStates[lightIndex]);
controller->lightStatesCurrent.second.lock();
for (size_t i = 0; i < controller->lightStatesFinal.size(); i++) {
if (controller->lightStatesCurrent.first[i] <
controller->lightStatesFinal[i]) {
controller->lightStatesCurrent.first[i] += 1;
} else if (controller->lightStatesCurrent.first[i] >
controller->lightStatesFinal[i]) {
controller->lightStatesCurrent.first[i] -= 1;
} }
} }
for (size_t lightIndex = 0;
lightIndex < controller->lightStatesCurrent.first.size();
++lightIndex) {
analogWrite(controller->getPins()[lightIndex],
controller->lightStatesCurrent.first[lightIndex]);
}
controller->lightStatesCurrent.second.unlock();
} }
} }
static const uint32_t stackDepth = 2048; static const uint32_t stackDepth = 2048;
const uint8_t step = 5;
const size_t fadeRate = 50;
etl::array<int, sizeof...(PIN)> lightPins; etl::array<int, sizeof...(PIN)> lightPins;
freertos::Queue<LightAction> lightActions; freertos::Queue<LightAction> lightActions;
freertos::Task lightControllerTask; freertos::Task lightControllerTask;
freertos::Task lightFaderTask;
etl::array<uint8_t, sizeof...(PIN)> lightStatesFinal;
etl::pair<etl::array<uint8_t, sizeof...(PIN)>, freertos::Mutex>
lightStatesCurrent;
}; };
} // namespace LightController } // namespace LightController

View file

@ -1,21 +1,9 @@
#pragma once #pragma once
#include "Protocol.h" #include "Protocol.h"
#include <etl/optional.h>
#include <etl/span.h>
#include <etl/string.h> #include <etl/string.h>
namespace message { namespace message {
namespace composer { namespace composer {
enum class ComposableType {
MESSAGE_LIGHTDATA,
MESSAGE_INFO,
MESSAGE_WARNING,
MESSAGE_ERROR,
MESSAGE_SUCCESS,
COMMAND_HELP,
COMMAND_VERSION,
};
using Composable = etl::pair<ComposableType, etl::optional<etl::span<uint8_t>>>;
bool sendMessage(protocol::Message type, etl::string_view content); bool sendMessage(protocol::Message type, etl::string_view content);
} // namespace composer } // namespace composer
} // namespace message } // namespace message

View file

@ -1,13 +1,12 @@
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include <FreeRTOS/BinarySemaphore.h> #include <FreeRTOS/Mutex.h>
#include <Messages/Protocol.h> #include <Messages/Protocol.h>
#include <etl/utility.h> #include <etl/utility.h>
#include <etl/vector.h> #include <etl/vector.h>
namespace message { namespace message {
/// @brief first: data, second: data ready to reuse
using Message = using Message =
etl::pair<etl::vector<uint8_t, message::protocol::MAX_PAYLOAD_LEN>, etl::pair<etl::vector<uint8_t, message::protocol::MAX_PAYLOAD_LEN>,
freertos::BinarySemaphore>; freertos::Mutex>;
} // namespace message } // namespace message

View file

@ -1,23 +1,58 @@
#include "Parser.h" #include "Parser.h"
#include <FreeRTOS/Util.h>
#include <PinMap/PinMap.h>
#include <etl/vector.h> #include <etl/vector.h>
message::parser::StateVisitor::StateVisitor( message::parser::StateVisitor::StateVisitor(
message::Message *message, etl::byte_stream_reader &&sreader,
freertos::Queue<LightController::LightAction> &lightActionQueue) etl::function<void, etl::string_view> invalidCallback,
: stream{message->first.begin(), message->first.size(), etl::endian::big}, etl::function<void, etl::span<const char>> messageLightDataCallback,
message{message}, lightActionQueue{lightActionQueue} {} etl::function<void, etl::span<const char>> messageInfoCallback,
etl::function<void, etl::span<const char>> messageWarningCallback,
etl::function<void, etl::span<const char>> messageErrorCallback,
etl::function<void, etl::span<const char>> messageSuccessCallback,
etl::function<void, uint32_t> settingsSetBaudCallback,
etl::function<void, etl::span<const char>> settingsWifiPasswordCallback,
etl::function<void, etl::span<const char>> settingsWifiSSIDCallback,
etl::function<void, void> lightControlOnCallback,
etl::function<void, void> lightControlOffCallback,
etl::function<void, uint8_t> lightControlToggleCallback,
etl::function<void, uint8_t> lightControlIncreaseCallback,
etl::function<void, uint8_t> lightControlDecreaseCallback,
etl::function<void, void> commandRequestLightDataCallback,
etl::function<void, void> commandEnterConsoleFlashingCallback,
etl::function<void, void> commandExitConsoleFlashingCallback,
etl::function<void, void> commandPairBluetoothCallback,
etl::function<void, void> commandHelpCallback,
etl::function<void, void> commandVersionCallback)
: stream{etl::move(sreader)}, invalidCallback{invalidCallback},
messageLightDataCallback{messageLightDataCallback},
messageInfoCallback{messageInfoCallback},
messageWarningCallback{messageWarningCallback},
messageErrorCallback{messageErrorCallback},
messageSuccessCallback{messageSuccessCallback},
settingsSetBaudCallback{settingsSetBaudCallback},
settingsWifiPasswordCallback{settingsWifiPasswordCallback},
settingsWifiSSIDCallback{settingsWifiSSIDCallback},
lightControlOnCallback{lightControlOnCallback},
lightControlOffCallback{lightControlOffCallback},
lightControlToggleCallback{lightControlToggleCallback},
lightControlIncreaseCallback{lightControlIncreaseCallback},
lightControlDecreaseCallback{lightControlDecreaseCallback},
commandRequestLightDataCallback{commandRequestLightDataCallback},
commandEnterConsoleFlashingCallback{commandEnterConsoleFlashingCallback},
commandExitConsoleFlashingCallback{commandExitConsoleFlashingCallback},
commandPairBluetoothCallback{commandPairBluetoothCallback},
commandHelpCallback{commandHelpCallback}, commandVersionCallback{
commandVersionCallback} {}
auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State { auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State {
if (!stream.available<uint8_t>()) { if (!stream.available<uint8_t>()) {
// invalidCallback("ModeSelection: Stream not available"); invalidCallback("ModeSelection: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto mode = stream.read<uint8_t>(); auto mode = stream.read<uint8_t>();
if (!mode.has_value()) { if (!mode.has_value()) {
// invalidCallback("ModeSelection: Mode has no value"); invalidCallback("ModeSelection: Mode has no value");
return state::Invalid{}; return state::Invalid{};
} }
@ -31,20 +66,20 @@ auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State {
case static_cast<uint8_t>(protocol::Mode::Command): case static_cast<uint8_t>(protocol::Mode::Command):
return state::Command{}; return state::Command{};
default: default:
// invalidCallback("ModeSelection: Invalid index"); invalidCallback("ModeSelection: Invalid index");
return state::Invalid{}; return state::Invalid{};
} }
} }
auto message::parser::StateVisitor::operator()(state::Message) -> State { auto message::parser::StateVisitor::operator()(state::Message) -> State {
if (!stream.available<uint8_t>()) { if (!stream.available<uint8_t>()) {
// invalidCallback("Message: Stream not available"); invalidCallback("Message: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto mode = stream.read<uint8_t>(); auto mode = stream.read<uint8_t>();
if (!mode.has_value()) { if (!mode.has_value()) {
// invalidCallback("Message: Mode has no value"); invalidCallback("Message: Mode has no value");
return state::Invalid{}; return state::Invalid{};
} }
@ -60,7 +95,7 @@ auto message::parser::StateVisitor::operator()(state::Message) -> State {
case static_cast<uint8_t>(protocol::Message::Success): case static_cast<uint8_t>(protocol::Message::Success):
return state::MessageSuccess{}; return state::MessageSuccess{};
default: default:
// invalidCallback("Message: Invalid index"); invalidCallback("Message: Invalid index");
return state::Invalid{}; return state::Invalid{};
} }
} }
@ -68,84 +103,84 @@ auto message::parser::StateVisitor::operator()(state::Message) -> State {
auto message::parser::StateVisitor::operator()(state::MessageLightData) auto message::parser::StateVisitor::operator()(state::MessageLightData)
-> State { -> State {
if (!stream.available<char>()) { if (!stream.available<char>()) {
// invalidCallback("MessageLightData: Stream not available"); invalidCallback("MessageLightData: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto msg = stream.read<char>(stream.available<char>()); auto msg = stream.read<char>(stream.available<char>());
if (!msg.has_value()) { if (!msg.has_value()) {
// invalidCallback("MessageLightData: Message has no value"); invalidCallback("MessageLightData: Message has no value");
return state::Invalid{}; return state::Invalid{};
} }
messageLightDataCallback(msg.value());
return state::Invalid{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State { auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State {
if (!stream.available<char>()) { if (!stream.available<char>()) {
// invalidCallback("MessageInfo: Stream not available"); invalidCallback("MessageInfo: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto msg = stream.read<char>(stream.available<char>()); auto msg = stream.read<char>(stream.available<char>());
if (!msg.has_value()) { if (!msg.has_value()) {
// invalidCallback("MessageInfo: Message has no value"); invalidCallback("MessageInfo: Message has no value");
return state::Invalid{}; return state::Invalid{};
} }
messageInfoCallback(msg.value());
return state::Invalid{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State { auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State {
if (!stream.available<char>()) { if (!stream.available<char>()) {
// invalidCallback("MessageWarning: Stream not available"); invalidCallback("MessageWarning: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto msg = stream.read<char>(stream.available<char>()); auto msg = stream.read<char>(stream.available<char>());
if (!msg.has_value()) { if (!msg.has_value()) {
// invalidCallback("MessageWarning: Message has no value"); invalidCallback("MessageWarning: Message has no value");
return state::Invalid{}; return state::Invalid{};
} }
messageWarningCallback(msg.value());
return state::Invalid{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::MessageError) -> State { auto message::parser::StateVisitor::operator()(state::MessageError) -> State {
if (!stream.available<char>()) { if (!stream.available<char>()) {
// invalidCallback("MessageError: Stream not available"); invalidCallback("MessageError: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto msg = stream.read<char>(stream.available<char>()); auto msg = stream.read<char>(stream.available<char>());
if (!msg.has_value()) { if (!msg.has_value()) {
// invalidCallback("MessageError: Message has no value"); invalidCallback("MessageError: Message has no value");
return state::Invalid{}; return state::Invalid{};
} }
messageErrorCallback(msg.value());
return state::Invalid{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State { auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State {
if (!stream.available<char>()) { if (!stream.available<char>()) {
// invalidCallback("MessageSuccess: Stream not available"); invalidCallback("MessageSuccess: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto msg = stream.read<char>(stream.available<char>()); auto msg = stream.read<char>(stream.available<char>());
if (!msg.has_value()) { if (!msg.has_value()) {
// invalidCallback("MessageSuccess: Message has no value"); invalidCallback("MessageSuccess: Message has no value");
return state::Invalid{}; return state::Invalid{};
} }
messageSuccessCallback(msg.value());
return state::Invalid{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::Settings) -> State { auto message::parser::StateVisitor::operator()(state::Settings) -> State {
if (!stream.available<uint8_t>()) { if (!stream.available<uint8_t>()) {
// invalidCallback("Settings: Stream not available"); invalidCallback("Settings: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto mode = stream.read<uint8_t>(); auto mode = stream.read<uint8_t>();
if (!mode.has_value()) { if (!mode.has_value()) {
// invalidCallback("Settings: Mode has no value"); invalidCallback("Settings: Mode has no value");
return state::Invalid{}; return state::Invalid{};
} }
@ -157,66 +192,69 @@ auto message::parser::StateVisitor::operator()(state::Settings) -> State {
case static_cast<uint8_t>(protocol::Settings::SetWifiSSID): case static_cast<uint8_t>(protocol::Settings::SetWifiSSID):
return state::SettingsWifiSSID{}; return state::SettingsWifiSSID{};
default: default:
// invalidCallback("Settings: Invalid index"); invalidCallback("Settings: Invalid index");
return state::Invalid{}; return state::Invalid{};
} }
} }
auto message::parser::StateVisitor::operator()(state::SettingsSetBaud) auto message::parser::StateVisitor::operator()(state::SettingsSetBaud)
-> State { -> State {
if (!stream.available<uint32_t>()) { if (!stream.available<uint32_t>()) {
// invalidCallback("Settings: Stream not available"); invalidCallback("Settings: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto baud = stream.read<uint32_t>(); auto baud = stream.read<uint32_t>();
if (!baud.has_value()) { if (!baud.has_value()) {
// invalidCallback("SettingsSetBaud: Baud has no value"); invalidCallback("SettingsSetBaud: Baud has no value");
return state::Invalid{}; return state::Invalid{};
} }
return state::Invalid{}; settingsSetBaudCallback(baud.value());
return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword) auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword)
-> State { -> State {
if (!stream.available<char>()) { if (!stream.available<char>()) {
// invalidCallback("SettingsWifiPassword: Stream not available"); invalidCallback("SettingsWifiPassword: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto passwd = stream.read<char>(stream.available<char>()); auto passwd = stream.read<char>(stream.available<char>());
if (!passwd.has_value()) { if (!passwd.has_value()) {
// invalidCallback("SettingsWifiPassword: Passwd has no value"); invalidCallback("SettingsWifiPassword: Passwd has no value");
return state::Invalid{}; return state::Invalid{};
} }
return state::Invalid{}; settingsWifiPasswordCallback(passwd.value());
return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID) auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID)
-> State { -> State {
if (!stream.available<char>()) { if (!stream.available<char>()) {
// invalidCallback("SettingsWifiSSID: Stream not available"); invalidCallback("SettingsWifiSSID: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto ssid = stream.read<char>(stream.available<char>()); auto ssid = stream.read<char>(stream.available<char>());
if (!ssid.has_value()) { if (!ssid.has_value()) {
// invalidCallback("SettingsWifiSSID: SSID has no value"); invalidCallback("SettingsWifiSSID: SSID has no value");
return state::Invalid{}; return state::Invalid{};
} }
return state::Invalid{}; settingsWifiSSIDCallback(ssid.value());
return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::LightControl) -> State { auto message::parser::StateVisitor::operator()(state::LightControl) -> State {
if (!stream.available<uint8_t>()) { if (!stream.available<uint8_t>()) {
// invalidCallback("LightControl: Stream not available"); invalidCallback("LightControl: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto mode = stream.read<uint8_t>(); auto mode = stream.read<uint8_t>();
if (!mode.has_value()) { if (!mode.has_value()) {
// invalidCallback("LightControl: Mode has no value"); invalidCallback("LightControl: Mode has no value");
return state::Invalid{}; return state::Invalid{};
} }
@ -232,92 +270,83 @@ auto message::parser::StateVisitor::operator()(state::LightControl) -> State {
case static_cast<uint8_t>(protocol::LightControl::Decrease): case static_cast<uint8_t>(protocol::LightControl::Decrease):
return state::LightControlDecrease{}; return state::LightControlDecrease{};
default: default:
// invalidCallback("LightControl: Invalid index"); invalidCallback("LightControl: Invalid index");
return state::Invalid{}; return state::Invalid{};
} }
} }
auto message::parser::StateVisitor::operator()(state::LightControlOn) -> State { auto message::parser::StateVisitor::operator()(state::LightControlOn) -> State {
if (stream.available_bytes() > 0) { if (stream.available_bytes() > 0) {
// invalidCallback("LightControlOn: Too many bytes left"); invalidCallback("LightControlOn: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} }
lightControlOnCallback();
message->second.give();
Serial.println("ON");
lightActionQueue.push({LightController::LightActionType::ON, etl::nullopt});
return state::Complete{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::LightControlOff) auto message::parser::StateVisitor::operator()(state::LightControlOff)
-> State { -> State {
if (stream.available_bytes() > 0) { if (stream.available_bytes() > 0) {
// invalidCallback("LightControlOff: Too many bytes left"); invalidCallback("LightControlOff: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} }
lightControlOffCallback();
message->second.give();
Serial.println("OFF");
lightActionQueue.push({LightController::LightActionType::OFF, etl::nullopt});
return state::Complete{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::LightControlToggle) auto message::parser::StateVisitor::operator()(state::LightControlToggle)
-> State { -> State {
if (stream.available_bytes() > 1) { if (stream.available_bytes() > 1) {
// invalidCallback("LightControlToggle: Too many bytes left"); invalidCallback("LightControlToggle: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} else if (!stream.available<uint8_t>()) { } else if (!stream.available<uint8_t>()) {
// invalidCallback("LightControlToggle: Stream not available"); invalidCallback("LightControlToggle: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto id = stream.read<uint8_t>(); auto id = stream.read<uint8_t>();
message->second.give();
Serial.println("Toggle"); lightControlToggleCallback(id.value());
lightActionQueue.push({LightController::LightActionType::TOGGLE, id});
return state::Complete{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::LightControlIncrease) auto message::parser::StateVisitor::operator()(state::LightControlIncrease)
-> State { -> State {
if (stream.available_bytes() > 1) { if (stream.available_bytes() > 1) {
// invalidCallback("LightControlIncrease: Too many bytes left"); invalidCallback("LightControlIncrease: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} else if (!stream.available<uint8_t>()) { } else if (!stream.available<uint8_t>()) {
// invalidCallback("LightControlIncrease: Stream not available"); invalidCallback("LightControlIncrease: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto id = stream.read<uint8_t>(); auto id = stream.read<uint8_t>();
message->second.give();
Serial.println("Increase"); lightControlIncreaseCallback(id.value());
lightActionQueue.push({LightController::LightActionType::INCREASE, id});
return state::Complete{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::LightControlDecrease) auto message::parser::StateVisitor::operator()(state::LightControlDecrease)
-> State { -> State {
if (stream.available_bytes() > 1) { if (stream.available_bytes() > 1) {
// invalidCallback("LightControlDecrease: Too many bytes left"); invalidCallback("LightControlDecrease: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} else if (!stream.available<uint8_t>()) { } else if (!stream.available<uint8_t>()) {
// invalidCallback("LightControlDecrease: Stream not available"); invalidCallback("LightControlDecrease: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto id = stream.read<uint8_t>(); auto id = stream.read<uint8_t>();
message->second.give();
Serial.println("Decrease"); lightControlDecreaseCallback(id.value());
lightActionQueue.push({LightController::LightActionType::DECREASE, id});
return state::Complete{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::Command) -> State { auto message::parser::StateVisitor::operator()(state::Command) -> State {
if (!stream.available<uint8_t>()) { if (!stream.available<uint8_t>()) {
// invalidCallback("Command: Stream not available"); invalidCallback("Command: Stream not available");
return state::Invalid{}; return state::Invalid{};
} }
auto mode = stream.read<uint8_t>(); auto mode = stream.read<uint8_t>();
if (!mode.has_value()) { if (!mode.has_value()) {
// invalidCallback("Command: Mode has no value"); invalidCallback("Command: Mode has no value");
return state::Invalid{}; return state::Invalid{};
} }
@ -334,62 +363,59 @@ auto message::parser::StateVisitor::operator()(state::Command) -> State {
return state::CommandHelp{}; return state::CommandHelp{};
case static_cast<uint8_t>(protocol::Command::Version): case static_cast<uint8_t>(protocol::Command::Version):
return state::CommandVersion{}; return state::CommandVersion{};
case static_cast<uint8_t>(protocol::Command::Reset):
return state::CommandReset{};
default: default:
// invalidCallback("Command: Invalid index"); invalidCallback("Command: Invalid index");
return state::Invalid{}; return state::Invalid{};
} }
} }
auto message::parser::StateVisitor::operator()(state::CommandRequestLightData) auto message::parser::StateVisitor::operator()(state::CommandRequestLightData)
-> State { -> State {
return state::Invalid{}; commandRequestLightDataCallback();
return state::Complete{};
} }
auto message::parser::StateVisitor::operator()( auto message::parser::StateVisitor::operator()(
state::CommandEnterConsoleFlashing) -> State { state::CommandEnterConsoleFlashing) -> State {
if (stream.available_bytes() > 0) { if (stream.available_bytes() > 0) {
// invalidCallback("CommandEnterConsoleFlashing: Too many bytes left"); invalidCallback("CommandEnterConsoleFlashing: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} }
return state::Invalid{}; commandEnterConsoleFlashingCallback();
return state::Complete{};
} }
auto message::parser::StateVisitor::operator()( auto message::parser::StateVisitor::operator()(
state::CommandExitConsoleFlashing) -> State { state::CommandExitConsoleFlashing) -> State {
if (stream.available_bytes() > 0) { if (stream.available_bytes() > 0) {
// invalidCallback("CommandExitConsoleFlashing: Too many bytes left"); invalidCallback("CommandExitConsoleFlashing: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} }
return state::Invalid{}; commandExitConsoleFlashingCallback();
return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth) auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth)
-> State { -> State {
if (stream.available_bytes() > 0) { if (stream.available_bytes() > 0) {
// invalidCallback("CommandPairBluetooth: Too many bytes left"); invalidCallback("CommandPairBluetooth: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} }
return state::Invalid{}; commandPairBluetoothCallback();
return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::CommandHelp) -> State { auto message::parser::StateVisitor::operator()(state::CommandHelp) -> State {
if (stream.available_bytes() > 0) { if (stream.available_bytes() > 0) {
// invalidCallback("CommandHelp: Too many bytes left"); invalidCallback("CommandHelp: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} }
return state::Invalid{};
commandHelpCallback();
return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State { auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State {
if (stream.available_bytes() > 0) { if (stream.available_bytes() > 0) {
// invalidCallback("CommandVersion: Too many bytes left"); invalidCallback("CommandVersion: Too many bytes left");
return state::Invalid{}; return state::Invalid{};
} }
return state::Invalid{};
}
auto message::parser::StateVisitor::operator()(state::CommandReset) -> State { commandVersionCallback();
if (stream.available_bytes() > 0) {
// invalidCallback("CommandVersion: Too many bytes left");
return state::Invalid{};
}
ESP.restart();
return state::Complete{}; return state::Complete{};
} }
@ -397,35 +423,55 @@ auto message::parser::StateVisitor::operator()(state::Complete) -> State {
return state::Complete{}; return state::Complete{};
} }
auto message::parser::StateVisitor::operator()(state::Invalid) -> State { auto message::parser::StateVisitor::operator()(state::Invalid) -> State {
message->second.give(); return state::Invalid{};
return state::Complete{};
} }
auto message::parser::parse( auto message::parser::parse(
message::Message *message, etl::byte_stream_reader &&reader,
freertos::Queue<LightController::LightAction> &lightActionQueue) -> void { etl::function<void, etl::string_view> invalidCallback,
StateVisitor visitor{message, lightActionQueue}; etl::function<void, etl::span<const char>> messageLightDataCallback,
etl::function<void, etl::span<const char>> messageInfoCallback,
etl::function<void, etl::span<const char>> messageWarningCallback,
etl::function<void, etl::span<const char>> messageErrorCallback,
etl::function<void, etl::span<const char>> messageSuccessCallback,
etl::function<void, uint32_t> settingsSetBaudCallback,
etl::function<void, etl::span<const char>> settingsWifiPasswordCallback,
etl::function<void, etl::span<const char>> settingsWifiSSIDCallback,
etl::function<void, void> lightControlOnCallback,
etl::function<void, void> lightControlOffCallback,
etl::function<void, uint8_t> lightControlToggleCallback,
etl::function<void, uint8_t> lightControlIncreaseCallback,
etl::function<void, uint8_t> lightControlDecreaseCallback,
etl::function<void, void> commandRequestLightDataCallback,
etl::function<void, void> commandEnterConsoleFlashingCallback,
etl::function<void, void> commandExitConsoleFlashingCallback,
etl::function<void, void> commandPairBluetoothCallback,
etl::function<void, void> commandHelpCallback,
etl::function<void, void> commandVersionCallback) -> void {
StateVisitor visitor{etl::move(reader),
invalidCallback,
messageLightDataCallback,
messageInfoCallback,
messageWarningCallback,
messageErrorCallback,
messageSuccessCallback,
settingsSetBaudCallback,
settingsWifiPasswordCallback,
settingsWifiSSIDCallback,
lightControlOnCallback,
lightControlOffCallback,
lightControlToggleCallback,
lightControlIncreaseCallback,
lightControlDecreaseCallback,
commandRequestLightDataCallback,
commandEnterConsoleFlashingCallback,
commandExitConsoleFlashingCallback,
commandPairBluetoothCallback,
commandHelpCallback,
commandVersionCallback};
State state{state::ModeSelection{}}; State state{state::ModeSelection{}};
while (!etl::holds_alternative<state::Complete>(state)) { while (!etl::holds_alternative<state::Invalid>(state) &&
!etl::holds_alternative<state::Complete>(state)) {
state = etl::visit(visitor, state); state = etl::visit(visitor, state);
} }
} }
message::parser::Parser::Parser(
freertos::Queue<LightController::LightAction> &lightActionQueue,
UBaseType_t queueCapacity, UBaseType_t priority, BaseType_t coreID)
: parserTask{exec, "parse received data", stackDepth, this, priority,
coreID},
messages{queueCapacity}, lightActionQueue{lightActionQueue} {}
void message::parser::Parser::exec(void *parserPtr) {
Parser *parser = static_cast<Parser *>(parserPtr);
while (true) {
auto message = parser->messages.pop();
Serial.println("starting parse");
if (message.has_value()) {
parse(message.value(), parser->lightActionQueue);
}
}
}

View file

@ -1,9 +1,5 @@
#pragma once #pragma once
#include "Protocol.h" #include "Protocol.h"
#include <FreeRTOS/Queue.h>
#include <FreeRTOS/Task.h>
#include <LightController/Controller.h>
#include <Messages/Message.h>
#include <etl/byte_stream.h> #include <etl/byte_stream.h>
#include <etl/function.h> #include <etl/function.h>
#include <etl/span.h> #include <etl/span.h>
@ -38,7 +34,6 @@ struct CommandExitConsoleFlashing {};
struct CommandPairBluetooth {}; struct CommandPairBluetooth {};
struct CommandHelp {}; struct CommandHelp {};
struct CommandVersion {}; struct CommandVersion {};
struct CommandReset {};
struct Complete {}; struct Complete {};
} // namespace state } // namespace state
@ -52,16 +47,55 @@ using State = etl::variant<
state::LightControlIncrease, state::LightControlDecrease, state::Command, state::LightControlIncrease, state::LightControlDecrease, state::Command,
state::CommandRequestLightData, state::CommandEnterConsoleFlashing, state::CommandRequestLightData, state::CommandEnterConsoleFlashing,
state::CommandExitConsoleFlashing, state::CommandPairBluetooth, state::CommandExitConsoleFlashing, state::CommandPairBluetooth,
state::CommandHelp, state::CommandVersion, state::CommandReset, state::CommandHelp, state::CommandVersion, state::Complete>;
state::Complete>;
void parse(message::Message *message, void parse(
freertos::Queue<LightController::LightAction> &lightActionQueue); etl::byte_stream_reader &&reader,
etl::function<void, etl::string_view> invalidCallback,
etl::function<void, etl::span<const char>> messageLightDataCallback,
etl::function<void, etl::span<const char>> messageInfoCallback,
etl::function<void, etl::span<const char>> messageWarningCallback,
etl::function<void, etl::span<const char>> messageErrorCallback,
etl::function<void, etl::span<const char>> messageSuccessCallback,
etl::function<void, uint32_t> settingsSetBaudCallback,
etl::function<void, etl::span<const char>> settingsWifiPasswordCallback,
etl::function<void, etl::span<const char>> settingsWifiSSIDCallback,
etl::function<void, void> lightControlOnCallback,
etl::function<void, void> lightControlOffCallback,
etl::function<void, uint8_t> lightControlToggleCallback,
etl::function<void, uint8_t> lightControlIncreaseCallback,
etl::function<void, uint8_t> lightControlDecreaseCallback,
etl::function<void, void> commandRequestLightDataCallback,
etl::function<void, void> commandEnterConsoleFlashingCallback,
etl::function<void, void> commandExitConsoleFlashingCallback,
etl::function<void, void> commandPairBluetoothCallback,
etl::function<void, void> commandHelpCallback,
etl::function<void, void> commandVersionCallback);
class StateVisitor { class StateVisitor {
public: public:
StateVisitor(message::Message *message, StateVisitor(
freertos::Queue<LightController::LightAction> &lightActionQueue); etl::byte_stream_reader &&sreader,
etl::function<void, etl::string_view> invalidCallback,
etl::function<void, etl::span<const char>> messageLightDataCallback,
etl::function<void, etl::span<const char>> messageInfoCallback,
etl::function<void, etl::span<const char>> messageWarningCallback,
etl::function<void, etl::span<const char>> messageErrorCallback,
etl::function<void, etl::span<const char>> messageSuccessCallback,
etl::function<void, uint32_t> settingsSetBaudCallback,
etl::function<void, etl::span<const char>> settingsWifiPasswordCallback,
etl::function<void, etl::span<const char>> settingsWifiSSIDCallback,
etl::function<void, void> lightControlOnCallback,
etl::function<void, void> lightControlOffCallback,
etl::function<void, uint8_t> lightControlToggleCallback,
etl::function<void, uint8_t> lightControlIncreaseCallback,
etl::function<void, uint8_t> lightControlDecreaseCallback,
etl::function<void, void> commandRequestLightDataCallback,
etl::function<void, void> commandEnterConsoleFlashingCallback,
etl::function<void, void> commandExitConsoleFlashingCallback,
etl::function<void, void> commandPairBluetoothCallback,
etl::function<void, void> commandHelpCallback,
etl::function<void, void> commandVersionCallback);
State operator()(state::ModeSelection); State operator()(state::ModeSelection);
State operator()(state::Message); State operator()(state::Message);
State operator()(state::MessageLightData); State operator()(state::MessageLightData);
@ -88,29 +122,33 @@ public:
State operator()(state::CommandPairBluetooth); State operator()(state::CommandPairBluetooth);
State operator()(state::CommandHelp); State operator()(state::CommandHelp);
State operator()(state::CommandVersion); State operator()(state::CommandVersion);
State operator()(state::CommandReset);
State operator()(state::Complete); State operator()(state::Complete);
State operator()(state::Invalid); State operator()(state::Invalid);
private: private:
etl::byte_stream_reader stream; etl::byte_stream_reader stream;
message::Message *message; // TODO: callback function parameter types
freertos::Queue<LightController::LightAction> &lightActionQueue; etl::function<void, etl::string_view> invalidCallback;
}; etl::function<void, etl::span<const char>> messageLightDataCallback;
etl::function<void, etl::span<const char>> messageInfoCallback;
class Parser { etl::function<void, etl::span<const char>> messageWarningCallback;
public: etl::function<void, etl::span<const char>> messageErrorCallback;
Parser(freertos::Queue<LightController::LightAction> &lightActionQueue, etl::function<void, etl::span<const char>> messageSuccessCallback;
UBaseType_t queueCapacity, UBaseType_t priority, BaseType_t coreID); etl::function<void, uint32_t> settingsSetBaudCallback;
freertos::Queue<message::Message *> &getMessageQueue() { return messages; } etl::function<void, etl::span<const char>> settingsWifiPasswordCallback;
etl::function<void, etl::span<const char>> settingsWifiSSIDCallback;
private: etl::function<void, void> lightControlOnCallback;
static void exec(void *parserPtr); etl::function<void, void> lightControlOffCallback;
freertos::Task parserTask; etl::function<void, uint8_t> lightControlToggleCallback;
static const uint32_t stackDepth = 2048; etl::function<void, uint8_t> lightControlIncreaseCallback;
freertos::Queue<message::Message *> messages; etl::function<void, uint8_t> lightControlDecreaseCallback;
freertos::Queue<LightController::LightAction> &lightActionQueue; etl::function<void, void> commandRequestLightDataCallback;
etl::function<void, void> commandEnterConsoleFlashingCallback;
etl::function<void, void> commandExitConsoleFlashingCallback;
etl::function<void, void> commandPairBluetoothCallback;
etl::function<void, void> commandHelpCallback;
etl::function<void, void> commandVersionCallback;
}; };
} // namespace parser } // namespace parser
} // namespace message } // namespace message

View file

@ -47,7 +47,6 @@ enum class Command : uint8_t {
PairBluetooth = 0x3, PairBluetooth = 0x3,
Help = 0x4, Help = 0x4,
Version = 0x5, Version = 0x5,
Reset = 0x6,
}; };
using Submode = etl::variant<Mode, Message, Settings, LightControl, Command>; using Submode = etl::variant<Mode, Message, Settings, LightControl, Command>;