feature: Including MQTT client

This commit is contained in:
2025-01-16 22:44:55 +01:00
parent 968d078c07
commit 5fcc0dda1e
8 changed files with 383 additions and 91 deletions
+157 -70
View File
@@ -6,6 +6,25 @@
* @copyright Copyright (c) 2024
*/
// Conig-Key:
//
//
//
struct {
const char *syslogDest = "syslogDst"; // example: "172.16.16.10"
const char *mqttBroker = "mqttBroker"; // example: "172.16.17.62"
const char *mqttPrefix = "mqttPrefix"; // example: "SomeTag"
} ConfigKeys;
// MQTT Topics
// Subscriptions:
// <mqttPrefix>/watch/MoveTo
// <mqttPrefix>/watch/Shuffle
// Publishes:
// <mqttPrefix>/watch/inPosition
// <mqttPrefix>/watch/changingPosition
#include "watchMain.hpp"
extern "C" { // This switch allows the ROS C-implementation to find this main
@@ -13,23 +32,24 @@ void app_main(void);
}
struct DeviceState {
int jumps;
int pictogram;
int profile;
uint8_t minJumps;
uint8_t maxJumps;
uint8_t profile;
uint8_t restTime;
float speed = 1.0; // Rad/sec
float accel = 1.0; // Rad/sec^2
Color color{16, 16, 16};
uint8_t pictogram;
};
enum class MoveType {
Step,
StepTo,
Shuffle,
Demo
};
struct MoveCommand {
MoveType type;
uint8_t deltaPic;
uint8_t profile;
Color color;
uint8_t pos;
};
static fos::Queue<MoveCommand> commandQueue(8);
@@ -37,40 +57,62 @@ DeviceState activeState;
static const char *TAG = "Watch";
static bool EthernetIsConnected = false;
std::unique_ptr<Cli> shellPtr;
std::unique_ptr<Cli> pShell;
void onCommand(std::string name, Cli::CArgs args) {
ESP_LOGI(TAG, "Invoking: %s", name.c_str());
}
void startUpdate(Cli::CArgs args) {
ESP_LOGI(TAG, "Invoking Update");
if (args.size() != 1) {
shellPtr->write("Need URL to application'\n");
pShell->write("Need URL to application'\n");
return;
}
// @ToDo: Start Update here
}
void setLogServerConfig(Cli::CArgs args) {
ESP_LOGI(TAG, "on showLogServerConfig:");
void getConfig(Cli::CArgs args) {
if (args.size() != 1) {
shellPtr->write("Need IP address to syslog server'\n");
// auto &store = NvsApi::getInstance();
// auto logger = store.getValue("syslogDst");
// shellPtr->write(std::string{"syslog destination = '"} + logger + "'\n");
pShell->write("Needs string key for query'\n");
return;
}
// auto &store = NvsApi::getInstance();
// shellPtr->write(std::string{"setting syslog destination to = '"} + args[0] + "'\n");
// store.setValue("syslogDst", args[0]);
auto key = args[0];
auto &store = NvsApi::getInstance();
auto val = store.getValue(key);
pShell->write(key + ": " + val + "\n");
}
void setConfig(Cli::CArgs args) {
if (args.size() != 2) {
pShell->write("Needs string key and value'\n");
return;
}
auto key = args[0];
auto val = args[1];
auto &store = NvsApi::getInstance();
pShell->write(std::string{"Storing value = '"} + val + "'\n");
store.setValue(key, val);
}
void setLogServerConfig(Cli::CArgs args) {
ESP_LOGI(TAG, "on showLogServerConfig:");
auto &store = NvsApi::getInstance();
if (args.size() != 1) {
auto logger = store.getValue("syslogDst");
pShell->write(std::string{"syslog destination = '"} + logger + "'\n");
pShell->write("provide argument to change syslog server'\n");
return;
}
pShell->write(std::string{"setting syslog destination to = '"} + args[0] + "'\n");
store.setValue("syslogDst", args[0]);
}
void setDynamics(Cli::CArgs args) {
ESP_LOGI(TAG, "on setDynamics:");
if (args.size() != 2) {
shellPtr->write("need float values for velocity and acceleration'\n");
pShell->write("need float values for velocity and acceleration'\n");
return;
}
@@ -86,17 +128,15 @@ void setDynamics(Cli::CArgs args) {
void triggerMovement(Cli::CArgs args) {
ESP_LOGI(TAG, "on triggerMovement:");
if (args.size() != 2) {
shellPtr->write("need profile index and steps'\n");
if (args.size() != 1) {
pShell->write("need target position'\n");
return;
}
try {
int p = std::stoi(args[0]);
int j = std::stoi(args[1]);
activeState.profile = p;
activeState.jumps = j;
commandQueue.enqueue(std::make_unique<MoveCommand>(MoveType::Step, j, p, activeState.color));
commandQueue.enqueue(std::make_unique<MoveCommand>(MoveType::StepTo, p));
} catch (const std::exception &e) {
ESP_LOGI(TAG, "Failed to parse %s and %s", args[0].c_str(), args[1].c_str());
}
@@ -119,22 +159,30 @@ static void telnetSocketTask(int addrFamily) {
TelnetServer telnet(addrFamily);
telnet.run(
[](TcpSession &session) {
shellPtr = std::make_unique<Cli>(session, [](std::string n, Cli::CArgs a) { onCommand(n, a); });
shellPtr->addBinding(
pShell = std::make_unique<Cli>(session, [](std::string n, Cli::CArgs a) { onCommand(n, a); });
pShell->addBinding(
{"update", "Start OTA-Update-Process",
[](Cli::CArgs a) { startUpdate(a); }});
shellPtr->addBinding(
pShell->addBinding(
{"logserver_set", "Set log server config ip",
[](Cli::CArgs a) { setLogServerConfig(a); }});
shellPtr->addBinding(
pShell->addBinding(
{"getcfg", "Get configuration value for key",
[](Cli::CArgs a) { getConfig(a); }});
pShell->addBinding(
{"setcfg", "Set configuration value by key and value",
[](Cli::CArgs a) { setConfig(a); }});
pShell->addBinding(
{"dyn_set", "set dynamics of movement (velocity,accelerations) as floats",
[](Cli::CArgs a) { setDynamics(a); }});
shellPtr->addBinding(
pShell->addBinding(
{"move", "start movement (profile-idx,steps) as integers ",
[](Cli::CArgs a) { triggerMovement(a); }});
},
[](TcpSession &session) {
shellPtr->process();
pShell->process();
Syslog::setupTask(session.getOther());
});
} catch (const std::exception &e) {
@@ -160,8 +208,10 @@ static void UniverseCallback(const uint8_t *data, const uint16_t size) {
int len = std::min(static_cast<int>(size), DmxChannelCount);
ESP_LOGD(TAG, "Got DMX");
ESP_LOG_BUFFER_HEXDUMP("DMX", data, len, ESP_LOG_DEBUG);
Syslog::logv(Syslog::Severity::Debug, "Got DMX: r%0x g%0x b%0x i%0x trig%i j%i p%i a%i w%i",
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]);
Syslog::logv(Syslog::Severity::Debug, "Got DMX: r%0x g%0x b%0x i%0x m%i M%i p%i a%i w%i r%i",
/* RGB*/ data[0], data[1], data[2], /*Intens*/ data[3],
/* MinMaxJumps */ data[4], data[5], /*profile*/ data[6], /*accel/speed*/ data[7], data[8],
/*RestTime*/ data[9]);
float intens = (float)data[3] / UINT8_MAX;
activeState.color = Color{
@@ -170,23 +220,44 @@ static void UniverseCallback(const uint8_t *data, const uint16_t size) {
scaledRgbChannel(data[2], intens),
0};
static uint8_t lastTrig = 0;
uint8_t thisTrig = data[4];
if (thisTrig == lastTrig) {
return;
}
lastTrig = thisTrig;
uint8_t jumps = data[5];
activeState.jumps = jumps;
uint8_t profile = data[6];
activeState.minJumps = data[4];
activeState.maxJumps = data[5];
activeState.profile = data[6];
uint8_t accel = data[7];
uint8_t velo = data[8];
activeState.accel = (float)accel / 100 + 0.5; // min .5 / max 3
activeState.speed = (float)velo / 100 + 0.5; // min .5 / max 3
activeState.profile = profile;
Syslog::logv(Syslog::Severity::Notice, "Command detected step=%i prof=%i accel=%.1f velo=%.1f", jumps, profile, activeState.accel, activeState.speed);
commandQueue.enqueue(std::make_unique<MoveCommand>(MoveType::Step, jumps, profile, activeState.color));
activeState.restTime = data[9];
}
void moveToSubs(std::string data) {
// Syslog::logv(Syslog::Severity::Notice,
// "Running Stepper by %i steps with prof=%i a=%.2f w=%.2f",
// delta, profile, activeState.accel, activeState.speed);
ESP_LOGI(TAG, "MoveTo command '%s'", data.c_str());
try {
int p = std::stoi(data);
commandQueue.enqueue(std::make_unique<MoveCommand>(MoveType::StepTo, p));
auto &client = MqttClient::getInstance();
client.publish("watch/inPosition", data + "Picto");
} catch (const std::exception &e) {
ESP_LOGE(TAG, "Error to process data %s, with ex: %s", data.c_str(), e.what());
}
}
void startShuffle(std::string data) {
ESP_LOGI(TAG, "StartShuffle command %s", data.c_str());
try {
commandQueue.enqueue(std::make_unique<MoveCommand>(MoveType::Shuffle, 0));
auto &client = MqttClient::getInstance();
client.publish("watch/changingPosition", "....");
} catch (const std::exception &e) {
ESP_LOGE(TAG, "Error to process data %s, with ex: %s", data.c_str(), e.what());
}
}
/**
@@ -230,19 +301,11 @@ static void dmxSocketTask(int addrFamily) {
ArtnetReceiver artnet(socket);
artnet.subscribe(DmxUniverse, UniverseCallback);
artnet.parse();
// auto msg = std::make_unique<Display::Message>(Display::Message::Type::UpdateInfo, "DMX-IP-Mode");
// displayEvents.enqueue(std::move(msg));
// deviceState.setNewState(DeviceState::Mode::Remote);
// xTaskCreate(valueRefresherTask, "valueRefresher", 4096, nullptr, 5, NULL);
// static TaskT<UdpSocket &> dmxSender(dmxSocketSenderTask, socket, "dmxSender", 4096);
// parse artnet as long socket is active, then restart socket
while (socket.isActive()) {
ESP_LOGD(TAG, "Waiting for data");
artnet.parse();
// socket.write(data);
}
// deviceState.fallbackToLast();
}
} catch (const std::exception &e) {
@@ -250,12 +313,7 @@ static void dmxSocketTask(int addrFamily) {
}
}
void startServerTasks() {
static int ipVersion = AF_INET;
static fos::TaskT<int> telnetSocket(telnetSocketTask, ipVersion, "tcpServer", 4096);
static fos::TaskT<int> dmxNode(dmxSocketTask, ipVersion, "dmxNode", 4096);
// static Task otaUpdate(SystemFunctions::OtaTaskEntry, "otaTask", 8192);
}
fos::Semaphore etherActive;
/** Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
@@ -269,7 +327,7 @@ static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
startServerTasks();
etherActive.give();
}
// Event handler for Ethernet
@@ -390,7 +448,6 @@ void LightingTask() {
// processors[3].setEffect(Effect::macDemoPulse, &colorSource, 64);
// processors[4].setEffect(Effect::macStartFlicker, &colorSource, 80);
while (true) {
values[0].fill(0);
values[1].fill(0);
@@ -409,7 +466,6 @@ void LightingTask() {
ledDriver2.sendImage(values[1]);
fos::Task::delayMs(40);
}
}
void MovementTask() {
@@ -442,19 +498,17 @@ void MovementTask() {
while (true) {
ESP_LOGD(TAG, "Waiting for command");
auto cmd = commandQueue.dequeue();
if (cmd->type == MoveType::Step) {
if (cmd->type == MoveType::StepTo) {
if (stepper.isIdle()) {
auto delta = cmd->deltaPic;
auto profile = cmd->profile;
// activeState.pictogram = ledPos;
// ledRing.send(ledStrip);
delta = std::min<uint8_t>(delta, 128);
// activeState.pictogram
auto delta = std::min<uint8_t>(cmd->pos, 128);
Syslog::logv(Syslog::Severity::Notice,
"Running Stepper by %i steps with prof=%i a=%.2f w=%.2f",
delta, profile, activeState.accel, activeState.speed);
delta, activeState.profile, activeState.accel, activeState.speed);
stepper.moveBy(delta * std::numbers::pi / 3,
profile, activeState.accel, activeState.speed);
activeState.profile, activeState.accel, activeState.speed);
} else {
fos::Task::delayMs(100);
}
@@ -502,7 +556,40 @@ void app_main(void) {
}
ESP_ERROR_CHECK(ret);
initialize_ethernet();
NvsApi::init();
// static fos::Task lightingTask(LightingTask, "Lights", 0x4000uL);
static fos::Task movementTask(MovementTask, "Motor", 0x4000uL);
// static fos::Task movementTask(MovementTask, "Motor", 0x4000uL);
// static fos::Task ioTask(IoTest, "IO", 0x4000uL);
etherActive.take();
ESP_LOGI(TAG, "Start net services");
static int ipVersion = AF_INET;
static fos::TaskT<int> telnetSocket(telnetSocketTask, ipVersion, "tcpServer", 4096);
static fos::TaskT<int> dmxNode(dmxSocketTask, ipVersion, "dmxNode", 4096);
// static Task otaUpdate(SystemFunctions::OtaTaskEntry, "otaTask", 8192);
auto &store = NvsApi::getInstance();
auto logger = store.getValue(ConfigKeys.syslogDest);
if (logger != "") {
Syslog::setupTask(IpAdr{logger, 0});
}
auto broker = store.getValue(ConfigKeys.mqttBroker);
if (broker != "") {
ESP_LOGI(TAG, "Initializing MQTT");
auto prefix = store.getValue(ConfigKeys.mqttPrefix);
auto &client = MqttClient::init(broker, prefix);
if (!client.isRunning()) {
client.addSubscription("watch/moveTo", [](std::string d) { moveToSubs(d); });
client.addSubscription("watch/shuffle", [](std::string d) { startShuffle(d); });
client.start();
}
}
while (true) {
fos::Task::delayMs(5000);
// ToDo send
Syslog::logv(Syslog::Severity::Notice, "Health state");
}
}