step: ArtNet

This commit is contained in:
2026-10-23 23:05:15 +02:00
parent 8c49e2f11c
commit efe64c3238
4 changed files with 158 additions and 167 deletions
+7 -4
View File
@@ -10,6 +10,11 @@
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <chrono>
#include <string>
#include <map>
#include <vector>
#include "sdkconfig.h"
#include "esp_event.h"
#include "esp_log.h"
@@ -43,7 +48,5 @@
#include "spit_player.hpp"
#include "spit_type.hpp"
#include "spit_client.hpp"
#include <chrono>
#include <string>
#include <map>
#include <vector>
#include "addressableLeRtz.hpp"
#include "systemFunctions.hpp"
+149 -161
View File
@@ -13,6 +13,9 @@
// ETH_PHY_LAN8720, // eth_type,
// ETH_CLOCK_GPIO0_IN // eth_clk_mode as input
// },
//
// LED-Strips: 14 - 22 - 30
// update http://172.16.16.10:8000/WatchController.bin
#include "config.hpp"
#include "lighsMain.hpp"
@@ -22,6 +25,8 @@ using namespace std;
string ClientName = "LeoLightsDev";
string ProjectName = "LeoLightsProj";
gpio_num_t LedPin = gpio_num_t::GPIO_NUM_16;
gpio_num_t RelayPin = gpio_num_t::GPIO_NUM_18;
extern "C" { // This switch allows the ROS C-implementation to find this main
void app_main(void);
@@ -32,52 +37,24 @@ struct DeviceState {
vector<uint8_t> counts;
vector<uint8_t> times;
};
struct StartCommand {
struct MasterRequest {
uint8_t pos; // 0 for none / Valid indexes 1 .. 6
};
static fos::Queue<StartCommand> commandQ(2);
struct SlaveResponse {
uint8_t pos; // 0 for none / Valid indexes 1 .. 6
};
static fos::Queue<MasterRequest> commandQ(2);
static fos::Queue<SlaveResponse> responseQ(2);
DeviceState State;
static const char* TAG = "Light";
static const char* VERSION = "V 0.6.0";
unique_ptr<Cli> pShell;
void onCommand(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) {
pShell->writeLine("Need URL to application");
return;
}
// @ToDo: Start Update here
}
void getConfig(Cli::CArgs args) {
if (args.size() != 1) {
pShell->writeLine("Needs string key for query");
return;
}
auto key = args[0];
auto& store = NvsApi::getInstance();
auto val = store.getValue(key);
pShell->writeLine(key + ": " + val + "");
}
void setConfig(Cli::CArgs args) {
if (args.size() != 2) {
pShell->writeLine("Needs string key and value");
return;
}
auto key = args[0];
auto val = args[1];
auto& store = NvsApi::getInstance();
pShell->writeLine(string{"Storing value = '"} + val + "'");
store.setValue(key, val);
}
void setLogServerConfig(Cli::CArgs args) {
ESP_LOGI(TAG, "on showLogServerConfig:");
@@ -94,7 +71,7 @@ void setLogServerConfig(Cli::CArgs args) {
void testLights(Cli::CArgs args) {
ESP_LOGI(TAG, "on testLights:");
commandQ.enqueue(make_unique<StartCommand>());
commandQ.enqueue(make_unique<MasterRequest>());
}
void showProcessValues(Cli::CArgs args) {
@@ -146,22 +123,16 @@ static void telnetSocketTask(int addrFamily) {
telnet.run(
[](TcpSession& session) {
pShell = make_unique<Cli>(session, [](string n, Cli::CArgs a) { onCommand(n, a); });
pShell->addBinding(
{"update", "Start OTA-Update-Process",
[](Cli::CArgs a) { startUpdate(a); }});
pShell->addBinding(
{"setLogserver", "Set log server config ip",
[](Cli::CArgs a) { setLogServerConfig(a); }});
pShell->addBinding(
{"setLogLevel", "Set severity log server",
[](Cli::CArgs a) { setSyslogLevel(a); }});
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); }});
auto& store = NvsApi::getInstance();
store.registerCli(*pShell);
SystemFunctions::registerCli(*pShell);
pShell->addBinding(
{"setDyn", "set dynamics of movement (velocity,accelerations) as floats",
@@ -184,29 +155,62 @@ void startShow(string data) {
ESP_LOGI(TAG, "StartShuffle command %s", data.c_str());
try {
Syslog::logv(Syslog::Severity::Informational, "got Shuffle-Command");
commandQ.enqueue(make_unique<StartCommand>());
commandQ.enqueue(make_unique<MasterRequest>());
} catch (const exception& e) {
ESP_LOGE(TAG, "Error to process data %s, with ex: %s", data.c_str(), e.what());
}
}
constexpr uint32_t DmxUniverse = 0;
constexpr int DmxChannelCount = 9;
array<uint8_t, 512> LedData;
static void UniverseCallback(const uint8_t* data, const uint16_t size) {
ESP_LOGD(TAG, "Got DMX");
ESP_LOG_BUFFER_HEXDUMP("DMX", data, size, ESP_LOG_DEBUG);
Syslog::logv(Syslog::Severity::Debug, "Got DMX: r%0x g%0x b%0x i%0x ...",
/* RGB*/ data[0], data[1], data[2], /*Intens*/ data[3]);
// DMX 512
for (int i = 0; i < LedData.size() / 4;) {
LedData[i] = data[i];
i++;
LedData[i] = data[i];
i++;
LedData[i] = data[i];
i++;
LedData[i] = data[i];
i++;
}
}
/**
* @brief DMX-Socket for ArtNet TCP/IP
*
* @details Tested with [Q Light Controller+](https://www.qlcplus.org/)
* @param args ignored task argument
*/
static void dmxSocketSenderTask(UdpSocket& socket) {
static void dmxSocketTask(int addrFamily) {
ESP_LOGI(TAG, "Starting ArtNet task");
constexpr uint16_t ArtNetPort = arx::artnet::DEFAULT_PORT;
IpAdr address = addrFamily == AF_INET ? IpAdr(INADDR_ANY, ArtNetPort) : IpAdr(ArtNetPort);
try {
ArtnetSender artnet(socket);
array<uint8_t, 16> data;
while (socket.isActive()) {
data[0]++;
artnet.send("", 0, data.data(), data.size());
fos::Task::delayMs(500);
while (true) {
UdpSocket socket(address);
socket.bindTo();
ArtnetReceiver artnet(socket);
artnet.subscribe(DmxUniverse, UniverseCallback);
artnet.parse();
// parse artnet as long socket is active, then restart socket
while (socket.isActive()) {
ESP_LOGD(TAG, "Waiting for data");
artnet.parse();
}
}
} catch (const exception& e) {
} catch (const std::exception& e) {
ESP_LOGE("UDP", "%s", e.what());
}
}
@@ -285,31 +289,19 @@ const Macro macDemoFlicker = {
void LightingTask() {
ESP_LOGI(TAG, "Start Lighting");
auto const& colorSource = State.color;
array<Effect::EffectProcessor, 2> processors{
Effect::EffectProcessor(8),
Effect::EffectProcessor(8)};
processors[0].setEffect(Effect::macDark, &colorSource, 0);
processors[1].setEffect(Effect::macDark, &colorSource, 0);
const array<Effect::Macro, 2> PictoEffects{
Effect::macDemoPulse,
Effect::macDemoPulse};
AddressableLeRtz strip(LedPin, AddressableLeRtz::Type::Rgb, LedData.size()/4);
OutputPort relay(RelayPin);
relay.write(true);
while (true) {
// ESP_LOGI(TAG, "Current color set r%i g%i b%i ",
// State.color.getRed(), State.color.getGreen(), State.color.getBlue());
// processors[0].setEffect(Effect::macDark, &colorSource, 0);
// auto idx = 0;
// Syslog::logv(Syslog::Severity::Informational, "Running show");
// processors[idx].setEffect(PictoEffects[idx], &colorSource, 0);
// for (size_t i = 0; i < processors.size(); i++) {
// auto col = processors[i].tick();
// }
// fos::Task::delayMs(40);
static int cnt{};
fos::Task::delayMs(1000);
printf("running %d\n", cnt++);
for (int i = 0; i < (LedData.size() / 4); i++) {
auto* c = &LedData[i * 4];
Color C{c[0], c[1], c[2], c[3]};
strip.set(C, i, 1);
}
strip.send(); // takes ca. 5ms to send all the data
fos::Task::delayMs(20);
}
}
@@ -318,7 +310,7 @@ struct LocalSpitObj {
string groupName;
string name;
};
map<string, StartCommand> ProvidedSpitActions;
map<string, MasterRequest> ProvidedSpitActions;
map<string, string> ExpectedObjects;
void onServerEvent(SClient::ServerEvent event, const SadDetectedServer& p_DetectedServer) {
@@ -514,7 +506,7 @@ void onPlaySpitType(string p_PlayerID, string spitTypeId, bool p_PlayerIsRunning
if (p_Started == true) {
auto command = ProvidedSpitActions[spitTypeId];
// ESP_LOGI(TAG, "Player: %s is running: %s Object", p_PlayerID.data(), spitTypeId.c_str());
commandQ.enqueue(make_unique<StartCommand>());
commandQ.enqueue(make_unique<MasterRequest>());
Syslog::logv(Syslog::Severity::Informational, "Got SPIT command for move ot : r%d", command.pos);
}
}
@@ -531,90 +523,83 @@ void onPlayRemoteObjectStatus(const SType& sType, const SObject& sObject, uint64
ESP_EARLY_LOGD(TAG, "eventPlayRemoteObjectStatus RemoteSPITType: %s RemoteSPITObject: %s Position: %lld Value: %d", sType.Name.get().data(), sObject.Name.get().data(), frameInsideObject, value);
}
// void SpitClientApp() {
// ESP_LOGI(TAG, "Starting SPIT task");
void SpitClientApp() {
ESP_LOGI(TAG, "Starting SPIT task");
// auto& provided = SpitTypes.Provided;
// auto& expected = SpitTypes.Expected;
// vector<LocalSpitObj> ProvidedObj;
// ProvidedSpitActions[provided.startShuffle] = MoveCommand{.type = MoveType::Shuffle, .pos = 0};
// ProvidedObj.push_back(LocalSpitObj{.iD = provided.startShuffle, .groupName = provided.group, .name = provided.startShuffle});
// ExpectedObjects[expected.changingPos] = "";
// for (size_t i = 0; i < 6; i++) {
// ProvidedSpitActions[provided.moveToX[i]] = MoveCommand{.type = MoveType::StepTo, .pos = (uint8_t)(i + 1)};
// ProvidedObj.push_back(LocalSpitObj{.iD = provided.moveToX[i], .groupName = provided.group, .name = provided.moveToX[i]});
// ExpectedObjects[expected.inPosX[i]] = "";
// }
auto& provided = SpitTypes.Provided;
auto& expected = SpitTypes.Expected;
// auto ip = spit::Socket::getAdaptersInfo();
// spit::Socket socket(ip);
// SClient spitClient(ClientName, ProjectName, socket,
// {
// .onServerEvent = onServerEvent,
// .onConnectionStatus = onConnectionStatus,
// .onProjectUpdate = onProjectUpdate,
// .onSpitType = onSpitType,
// .onSpitObject = onSpitObject,
// .onPlay = onPlay,
// .onPlaySpitType = onPlaySpitType,
// .onPlayRemoteObjectStatus = onPlayRemoteObjectStatus,
// .onCheckSpitTypeVeto = onCheckSpitTypeVeto,
// .onCheckSpitObjectVeto = onCheckSpitObjectVeto,
// });
vector<LocalSpitObj> ProvidedObj;
ProvidedSpitActions[provided.group] = MasterRequest{.pos = 0};
ProvidedObj.push_back(LocalSpitObj{.iD = provided.group, .groupName = provided.group, .name = provided.group});
ProvidedSpitActions[provided.startShow] = MasterRequest{.pos = 0};
ProvidedObj.push_back(LocalSpitObj{.iD = provided.startShow, .groupName = provided.group, .name = provided.startShow});
// for (auto& obj : ProvidedObj) {
// SType* ptrSPITType1Fix = spitClient.createTypeFixed(obj.iD, obj.groupName, obj.name, 0, 0);
// spitClient.setActionType(ptrSPITType1Fix->getID(), true); // should be executed whenever a SPITObject with this SPITType is played
// }
// spitClient.clientProjectLoaded();
// spitClient.startServerAutoDetect();
ExpectedObjects[expected.showDone] = "";
// spitProjectFinish.take();
// string serverProjectID = spitClient.getServerProjectID();
// string serverProjectName = spitClient.getClientProjectName();
// int serverFramesPerSecond = spitClient.getServerFramesPerSecond(); // set when the server reports his project, see eventProjectReport
// ESP_LOGI(TAG, "eventProjectReport ID: %s Name: %s FramesPerSecond: %d", serverProjectID.data(), serverProjectName.data(), serverFramesPerSecond);
auto ip = spit::Socket::getAdaptersInfo();
spit::Socket socket(ip);
SClient spitClient(ClientName, ProjectName, socket,
{
.onServerEvent = onServerEvent,
.onConnectionStatus = onConnectionStatus,
.onProjectUpdate = onProjectUpdate,
.onSpitType = onSpitType,
.onSpitObject = onSpitObject,
.onPlay = onPlay,
.onPlaySpitType = onPlaySpitType,
.onPlayRemoteObjectStatus = onPlayRemoteObjectStatus,
.onCheckSpitTypeVeto = onCheckSpitTypeVeto,
.onCheckSpitObjectVeto = onCheckSpitObjectVeto,
});
// for (auto&& obj : ExpectedObjects) {
// if (obj.second == "") {
// ESP_LOGE(TAG, "No matching Remote ID for %s", obj.first.c_str());
// }
// }
for (auto& obj : ProvidedObj) {
SType* ptrSPITType1Fix = spitClient.createTypeFixed(obj.iD, obj.groupName, obj.name, 0, 0);
spitClient.setActionType(ptrSPITType1Fix->getID(), true); // should be executed whenever a SPITObject with this SPITType is played
}
spitClient.clientProjectLoaded();
spitClient.startServerAutoDetect();
// while (true) {
// auto res = responseQueue.dequeue();
// if (res->type == ResponseType::StartMoving) {
// spitClient.sendRemoteObject(ExpectedObjects[expected.changingPos]);
// } else if (res->type == ResponseType::StepAt) {
// spitClient.sendRemoteObject(ExpectedObjects[expected.inPosX[res->pos - 1]]);
// }
// }
// }
spitProjectFinish.take();
string serverProjectID = spitClient.getServerProjectID();
string serverProjectName = spitClient.getClientProjectName();
int serverFramesPerSecond = spitClient.getServerFramesPerSecond(); // set when the server reports his project, see eventProjectReport
ESP_LOGI(TAG, "eventProjectReport ID: %s Name: %s FramesPerSecond: %d", serverProjectID.data(), serverProjectName.data(), serverFramesPerSecond);
// void MqttThread() {
// ESP_LOGI(TAG, "Starting MQTT task");
for (auto&& obj : ExpectedObjects) {
if (obj.second == "") {
ESP_LOGE(TAG, "No matching Remote ID for %s", obj.first.c_str());
}
}
// auto& store = NvsApi::getInstance();
// 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(MqttTopics.moveTo, [](string d) { startShuffle(d); });
// client.start();
// }
while (true) {
auto res = responseQ.dequeue();
spitClient.sendRemoteObject(ExpectedObjects[expected.showDone]);
}
}
// while (true) {
// auto res = responseQueue.dequeue();
// if (res->type == ResponseType::StartMoving) {
// client.publish(MqttTopics.showDone, "");
// } else if (res->type == ResponseType::StepAt) {
// client.publish(MqttTopics.inPosition, to_string(res->pos));
// }
// }
// }
// }
void MqttThread() {
ESP_LOGI(TAG, "Starting MQTT task");
auto& store = NvsApi::getInstance();
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(MqttTopics.startShow, [](string d) {
commandQ.enqueue(make_unique<MasterRequest>());
});
client.start();
}
while (true) {
auto res = responseQ.dequeue();
client.publish(MqttTopics.showDone, "");
}
}
}
void app_main(void) {
initialize_ethernet();
@@ -624,11 +609,11 @@ void app_main(void) {
etherActive.take();
ESP_LOGI(TAG, "Start net services");
// static int ipVersion = AF_INET;
// static fos::TaskT<int> telnetSocket(telnetSocketTask, ipVersion, "tcpServer", 4096);
static int ipVersion = AF_INET;
static fos::TaskT<int> telnetSocket(telnetSocketTask, ipVersion, "tcpServer", 4096);
// static fos::Task spit(SpitClientApp, "SpitClient");
// // static fos::Task mqtt(MqttThread, "MqttEndpoint");
// // static Task otaUpdate(SystemFunctions::OtaTaskEntry, "otaTask", 8192);
// static fos::Task mqtt(MqttThread, "MqttEndpoint");
static fos::TaskT<int> dmxNode(dmxSocketTask, ipVersion, "dmxNode", 4096);
auto& store = NvsApi::getInstance();
auto logger = store.getValue(ConfigKeys.syslogDest);
@@ -638,9 +623,12 @@ void app_main(void) {
ESP_LOGI(TAG, "Configuration");
ESP_LOGI(TAG, "%s: %s", ConfigKeys.syslogDest, store.getValue(ConfigKeys.syslogDest).c_str());
ESP_LOGI(TAG, "%s: %s", ConfigKeys.mqttBroker, store.getValue(ConfigKeys.mqttBroker).c_str());
ESP_LOGI(TAG, "%s: %s", ConfigKeys.mqttPrefix, store.getValue(ConfigKeys.mqttPrefix).c_str());
ESP_LOGI(TAG, "%s: %s", ConfigKeys.spitServer, store.getValue(ConfigKeys.spitServer).c_str());
// ESP_LOGI(TAG, "%s: %s", ConfigKeys.mqttBroker, store.getValue(ConfigKeys.mqttBroker).c_str());
// ESP_LOGI(TAG, "%s: %s", ConfigKeys.mqttPrefix, store.getValue(ConfigKeys.mqttPrefix).c_str());
// ESP_LOGI(TAG, "%s: %s", ConfigKeys.spitServer, store.getValue(ConfigKeys.spitServer).c_str());
ESP_LOGI(TAG, "## Starting : %s : %s",
CONFIG_LWIP_LOCAL_HOSTNAME, VERSION);
while (true) {
fos::Task::delayMs(5000);