Branching from Cathaswatch project

https://git.nerdyssey.de/exodus/1NiM-WatingRoom-WatchController
This commit is contained in:
2024-11-16 11:53:28 +01:00
parent 1a13428c9d
commit 8c49e2f11c
116 changed files with 14510 additions and 590 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
idf_component_register(SRCS "ethernet_example_main.c"
idf_component_register(SRCS "lightsMain.cpp"
INCLUDE_DIRS ".")
+4 -4
View File
@@ -1,25 +1,25 @@
menu "Example Configuration"
config EXAMPLE_WIFI_SSID
config DEVICE_WIFI_SSID
string "Wi-Fi SSID"
default "eth2ap"
help
Set the SSID of Wi-Fi ap interface.
config EXAMPLE_WIFI_PASSWORD
config DEVICE_WIFI_PASSWORD
string "Wi-Fi Password"
default "12345678"
help
Set the password of Wi-Fi ap interface.
config EXAMPLE_WIFI_CHANNEL
config DEVICE_WIFI_CHANNEL
int "WiFi channel"
range 1 13
default 1
help
Set the channel of Wi-Fi ap.
config EXAMPLE_MAX_STA_CONN
config DEVICE_MAX_STA_CONN
int "Maximum STA connections"
default 4
help
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
struct {
const char *syslogDest = "syslogDst"; // example: "172.16.16.10"
const char *mqttBroker = "mqttBroker"; // example: "172.16.16.10"
const char *mqttPrefix = "mqttPrefix"; // example: "SomeTag"
const char *spitServer = "spitServer"; // example: "172.16.16.10"
const char *motorPower = "motorPower"; // example: "50"
} ConfigKeys;
// MQTT Topics
struct {
// Subscriptions:
const char *startShow = /* <mqttPrefix>/ */ "elixirLights/startShow";
// Publishes:
const char *showDone = /* <mqttPrefix>/ */ "elixirLights/showDone";
} MqttTopics;
struct {
const char *expectedServer = "";
struct {
const char *group = "CatasWatchMovement";
const char *startShow = "StartShow";
} Provided;
struct {
const char *showDone = "ShowDone";
} Expected;
} SpitTypes;
-222
View File
@@ -1,222 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* eth2ap (Ethernet to Wi-Fi AP packet forwarding) Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <stdlib.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_eth_driver.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "esp_private/wifi.h"
#include "ethernet_init.h"
static const char *TAG = "eth2ap_example";
static esp_eth_handle_t s_eth_handle = NULL;
static QueueHandle_t flow_control_queue = NULL;
static bool s_sta_is_connected = false;
static bool s_ethernet_is_connected = false;
static uint8_t s_eth_mac[6];
#define FLOW_CONTROL_QUEUE_TIMEOUT_MS (100)
#define FLOW_CONTROL_QUEUE_LENGTH (40)
#define FLOW_CONTROL_WIFI_SEND_TIMEOUT_MS (100)
typedef struct {
void *packet;
uint16_t length;
} flow_control_msg_t;
// Forward packets from Wi-Fi to Ethernet
static esp_err_t pkt_wifi2eth(void *buffer, uint16_t len, void *eb)
{
if (s_ethernet_is_connected) {
if (esp_eth_transmit(s_eth_handle, buffer, len) != ESP_OK) {
ESP_LOGE(TAG, "Ethernet send packet failed");
}
}
esp_wifi_internal_free_rx_buffer(eb);
return ESP_OK;
}
// Forward packets from Ethernet to Wi-Fi
// Note that, Ethernet works faster than Wi-Fi on ESP32,
// so we need to add an extra queue to balance their speed difference.
static esp_err_t pkt_eth2wifi(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t len, void *priv)
{
esp_err_t ret = ESP_OK;
flow_control_msg_t msg = {
.packet = buffer,
.length = len
};
if (xQueueSend(flow_control_queue, &msg, pdMS_TO_TICKS(FLOW_CONTROL_QUEUE_TIMEOUT_MS)) != pdTRUE) {
ESP_LOGE(TAG, "send flow control message failed or timeout");
free(buffer);
ret = ESP_FAIL;
}
return ret;
}
// This task will fetch the packet from the queue, and then send out through Wi-Fi.
// Wi-Fi handles packets slower than Ethernet, we might add some delay between each transmitting.
static void eth2wifi_flow_control_task(void *args)
{
flow_control_msg_t msg;
int res = 0;
uint32_t timeout = 0;
while (1) {
if (xQueueReceive(flow_control_queue, &msg, pdMS_TO_TICKS(FLOW_CONTROL_QUEUE_TIMEOUT_MS)) == pdTRUE) {
timeout = 0;
if (s_sta_is_connected && msg.length) {
do {
vTaskDelay(pdMS_TO_TICKS(timeout));
timeout += 2;
res = esp_wifi_internal_tx(WIFI_IF_AP, msg.packet, msg.length);
} while (res && timeout < FLOW_CONTROL_WIFI_SEND_TIMEOUT_MS);
if (res != ESP_OK) {
ESP_LOGE(TAG, "WiFi send packet failed: %d", res);
}
}
free(msg.packet);
}
}
vTaskDelete(NULL);
}
// Event handler for Ethernet
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
s_ethernet_is_connected = true;
esp_eth_ioctl(s_eth_handle, ETH_CMD_G_MAC_ADDR, s_eth_mac);
esp_wifi_set_mac(WIFI_IF_AP, s_eth_mac);
ESP_ERROR_CHECK(esp_wifi_start());
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
s_ethernet_is_connected = false;
ESP_ERROR_CHECK(esp_wifi_stop());
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}
// Event handler for Wi-Fi
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
static uint8_t s_con_cnt = 0;
switch (event_id) {
case WIFI_EVENT_AP_STACONNECTED:
ESP_LOGI(TAG, "Wi-Fi AP got a station connected");
if (!s_con_cnt) {
s_sta_is_connected = true;
esp_wifi_internal_reg_rxcb(WIFI_IF_AP, pkt_wifi2eth);
}
s_con_cnt++;
break;
case WIFI_EVENT_AP_STADISCONNECTED:
ESP_LOGI(TAG, "Wi-Fi AP got a station disconnected");
s_con_cnt--;
if (!s_con_cnt) {
s_sta_is_connected = false;
esp_wifi_internal_reg_rxcb(WIFI_IF_AP, NULL);
}
break;
default:
break;
}
}
static void initialize_ethernet(void)
{
uint8_t eth_port_cnt = 0;
esp_eth_handle_t *eth_handles;
ESP_ERROR_CHECK(example_eth_init(&eth_handles, &eth_port_cnt));
if (eth_port_cnt > 1) {
ESP_LOGW(TAG, "multiple Ethernet devices detected, the first initialized is to be used!");
}
s_eth_handle = eth_handles[0];
free(eth_handles);
ESP_ERROR_CHECK(esp_eth_update_input_path(s_eth_handle, pkt_eth2wifi, NULL));
bool eth_promiscuous = true;
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_S_PROMISCUOUS, &eth_promiscuous));
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL));
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
}
static void initialize_wifi(void)
{
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
wifi_config_t wifi_config = {
.ap = {
.ssid = CONFIG_EXAMPLE_WIFI_SSID,
.ssid_len = strlen(CONFIG_EXAMPLE_WIFI_SSID),
.password = CONFIG_EXAMPLE_WIFI_PASSWORD,
.max_connection = CONFIG_EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK,
.channel = CONFIG_EXAMPLE_WIFI_CHANNEL // default: channel 1
},
};
if (strlen(CONFIG_EXAMPLE_WIFI_PASSWORD) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
}
static esp_err_t initialize_flow_control(void)
{
flow_control_queue = xQueueCreate(FLOW_CONTROL_QUEUE_LENGTH, sizeof(flow_control_msg_t));
if (!flow_control_queue) {
ESP_LOGE(TAG, "create flow control queue failed");
return ESP_FAIL;
}
BaseType_t ret = xTaskCreate(eth2wifi_flow_control_task, "flow_ctl", 2048, NULL, (tskIDLE_PRIORITY + 2), NULL);
if (ret != pdTRUE) {
ESP_LOGE(TAG, "create flow control task failed");
return ESP_FAIL;
}
return ESP_OK;
}
void app_main(void)
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(initialize_flow_control());
initialize_wifi();
initialize_ethernet();
}
+5 -2
View File
@@ -1,3 +1,6 @@
## IDF Component Manager Manifest File
# https://docs.espressif.com/projects/idf-component-manager/en/latest/reference/manifest_file.html
# https://components.espressif.com
dependencies:
ethernet_init:
path: ${IDF_PATH}/examples/ethernet/basic/components/ethernet_init
frozen-eye/esp_telnet: "^0.0.2"
+49
View File
@@ -0,0 +1,49 @@
/**
* @file watchMain.hpp
* @author Gustice
*
* @copyright Copyright (c) 2024
*/
#pragma once
#include <cstring>
#include <cstdlib>
#include <cmath>
#include "sdkconfig.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_eth_driver.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "esp_private/wifi.h"
#include "ethernet.hpp"
#include "esp_netif.h"
#include "esp_eth.h"
// Component header
#include "Task.hpp"
#include "cliWrapper.hpp"
#include "udpSockets.hpp"
#include "tcpSockets.hpp"
#include "syslog.hpp"
#include "telnetServer.hpp"
#include "Mutex.hpp"
#include "Queue.hpp"
#include "Semaphore.hpp"
#include "Task.hpp"
#include "Artnet.h"
#include "Processor.hpp"
#include "nvsFunctions.hpp"
#include "mqttClient.hpp"
#include "watchFace.hpp"
#include "random.hpp"
#include "sad_DetectedServer.hpp"
#include "spit_object.hpp"
#include "spit_player.hpp"
#include "spit_type.hpp"
#include "spit_client.hpp"
#include <chrono>
#include <string>
#include <map>
#include <vector>
+651
View File
@@ -0,0 +1,651 @@
/**
* @author Gustice
* @brief ChaosWatch Application
*/
// using Elite2D-EXMU
// Gledopto Series With Ethernet
// { // 13
// 1, // eth_address,
// 5, // eth_power,
// 23, // eth_mdc,
// 33, // eth_mdio,
// ETH_PHY_LAN8720, // eth_type,
// ETH_CLOCK_GPIO0_IN // eth_clk_mode as input
// },
#include "config.hpp"
#include "lighsMain.hpp"
using namespace spit;
using namespace std;
string ClientName = "LeoLightsDev";
string ProjectName = "LeoLightsProj";
extern "C" { // This switch allows the ROS C-implementation to find this main
void app_main(void);
}
struct DeviceState {
Color color{0x80, 0x80, 0x80};
vector<uint8_t> counts;
vector<uint8_t> times;
};
struct StartCommand {
uint8_t pos; // 0 for none / Valid indexes 1 .. 6
};
static fos::Queue<StartCommand> commandQ(2);
DeviceState State;
static const char* TAG = "Light";
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:");
auto& store = NvsApi::getInstance();
if (args.size() != 1) {
auto logger = store.getValue("syslogDst");
pShell->writeLine(string{"syslog destination = '"} + logger + "'");
pShell->writeLine("provide argument to change syslog server");
return;
}
pShell->writeLine(string{"setting syslog destination to = '"} + args[0] + "'");
store.setValue("syslogDst", args[0]);
}
void testLights(Cli::CArgs args) {
ESP_LOGI(TAG, "on testLights:");
commandQ.enqueue(make_unique<StartCommand>());
}
void showProcessValues(Cli::CArgs args) {
pShell->writeLine("Process values:");
char buff[256];
sprintf(buff, "%02x,%02x,%02x", State.color.getRed(), State.color.getGreen(), State.color.getBlue());
std::stringstream ssC;
for (auto& c : State.counts) {
ssC << string{c} << ",";
}
std::stringstream ssT;
for (auto& t : State.times) {
ssT << string{t} << ",";
}
pShell->writeLine(string{" Color RGB: "} + buff);
pShell->writeLine(string{" LED-Counts: "} + ssC.str());
pShell->writeLine(string{" Switch-Times: "} + ssT.str());
}
void setSyslogLevel(Cli::CArgs args) {
if (args.size() != 1) {
pShell->writeLine("Needs SyslogLevel Warning = 4, Notice = 5, Informational = 6, Debug = 7");
return;
}
try {
auto lvl = stoi(args[0]);
if (lvl >= (int)Syslog::Severity::Warning && lvl <= (int)Syslog::Severity::Debug) {
Syslog::setSeverity((Syslog::Severity)lvl);
} else {
pShell->writeLine("New level was not accepted");
}
} catch (const exception& e) {
ESP_LOGI(TAG, "Failed to parse %s", args[0].c_str());
}
}
/**
* @brief Telnet socket via TCP/IP
*
* @details The ini-file for TeraTerm only changes CRReceive and CRSend values both to "=CR"
* @param arg ignored task argument
*/
static void telnetSocketTask(int addrFamily) {
ESP_LOGI(TAG, "Starting Telnet task");
try {
TelnetServer telnet(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); }});
pShell->addBinding(
{"setDyn", "set dynamics of movement (velocity,accelerations) as floats",
[](Cli::CArgs a) { testLights(a); }});
pShell->addBinding(
{"showData", "Show process values that are input by DMX, MQTT and SPIT",
[](Cli::CArgs a) { showProcessValues(a); }});
},
[](TcpSession& session) {
pShell->process();
Syslog::setupTask(session.getOther());
});
} catch (const exception& e) {
ESP_LOGE("TCP", "%s", e.what());
}
vTaskDelete(NULL);
}
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>());
} catch (const exception& e) {
ESP_LOGE(TAG, "Error to process data %s, with ex: %s", data.c_str(), e.what());
}
}
/**
* @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) {
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);
}
} catch (const exception& e) {
ESP_LOGE("UDP", "%s", e.what());
}
}
fos::Semaphore etherActive;
fos::Semaphore spitProjectFinish;
/** Event handler for IP_EVENT_ETH_GOT_IP */
void got_ip_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data;
const esp_netif_ip_info_t* ip_info = &event->ip_info;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
etherActive.give();
}
static void initialize_ethernet(void) {
uint8_t eth_port_cnt = 0;
esp_eth_handle_t* eth_handles;
ESP_ERROR_CHECK(eth_init(&eth_handles, &eth_port_cnt));
if (eth_port_cnt > 1) {
ESP_LOGW(TAG, "multiple Ethernet devices detected, the first initialized is to be used!");
}
static esp_eth_handle_t s_eth_handle = NULL;
s_eth_handle = eth_handles[0];
free(eth_handles);
// Initialize TCP/IP network interface aka the esp-netif (should be called only once in application)
ESP_ERROR_CHECK(esp_netif_init());
// Create default event loop that running in background
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Use ESP_NETIF_DEFAULT_ETH when just one Ethernet interface is used and you don't need to modify
// default esp-netif configuration parameters.
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t* eth_netif = esp_netif_new(&cfg);
// Attach Ethernet driver to TCP/IP stack
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(s_eth_handle)));
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
}
namespace Effect {
extern const WaveForm_t OffsetPulseWave{gau8_offsetPulse, cu16_TemplateLength};
extern const WaveForm_t InitSlopeWave{gau8_initSlope, cu16_TemplateLength};
extern const WaveForm_t OffsetSlopeWave{gau8_offsetSlope, cu16_TemplateLength};
extern const WaveForm_t FullSlopeWave{gau8_fullSlope, cu16_TemplateLength};
extern const WaveForm_t FullPulseWave{gau8_fullPulse, cu16_TemplateLength};
const Macro macDemoStart = {
{64, 1, &InitSlopeWave, StateMachine::UpdateWave, 0xFF},
{32, 1, StateMachine::UpdateIdle, 0xFF},
};
const Macro macDemoPulse = {
{64, 1, &InitSlopeWave, StateMachine::UpdateWave, 0xFF},
{32, 2, StateMachine::UpdateIdle, 0xFF},
{64, 1, &OffsetPulseWave, StateMachine::UpdateWave, 0xFF},
};
const Macro macDemoFlicker = {
{16, 1, &InitSlopeWave, StateMachine::UpdateWave, 0xFF},
{32, 1, StateMachine::UpdateFlicker, 0xFF},
};
} // namespace Effect
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};
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++);
}
}
struct LocalSpitObj {
string iD;
string groupName;
string name;
};
map<string, StartCommand> ProvidedSpitActions;
map<string, string> ExpectedObjects;
void onServerEvent(SClient::ServerEvent event, const SadDetectedServer& p_DetectedServer) {
switch (event) {
case SClient::ServerEvent::Detect:
ESP_LOGI(TAG, "SERVER DETECTED");
ESP_LOGI(TAG, " Name: %s", p_DetectedServer.getName().data());
ESP_LOGI(TAG, " ID: %s", p_DetectedServer.getID().data());
ESP_LOGI(TAG, " Computer: %s", p_DetectedServer.getComputerName().data());
ESP_LOGI(TAG, " IP: %s", p_DetectedServer.getIP().data());
ESP_LOGI(TAG, " Port: %s", p_DetectedServer.getPort().data());
ESP_LOGI(TAG, " Addition: %s", p_DetectedServer.getAdditions().data());
ESP_LOGI(TAG, " ClientIP: %s", p_DetectedServer.getClientIP().data());
break;
case SClient::ServerEvent::Lost:
ESP_LOGW(TAG, "Server lost Name: %s IP: %s", p_DetectedServer.getName().data(), p_DetectedServer.getIP().data());
break;
case SClient::ServerEvent::Changed:
ESP_LOGW(TAG, "Server lost Name: %s IP: %s", p_DetectedServer.getName().data(), p_DetectedServer.getIP().data());
break;
default:
break;
}
}
void onConnectionStatus(string p_ServerID, string p_ServerComputerName, string p_ServerName, int p_ServerStepTime,
SClient::Connection p_ConnectionStatus, SClient::Connection p_OldConnectionStatus) {
ESP_LOGI(TAG, "MySPITApp: eventConnectionStatus %d->%d ", (int)p_OldConnectionStatus, (int)p_ConnectionStatus);
}
// The transfer of the server's SPITTypes and SPITObjects is finished.
void onProjectUpdate(SClient::ProjUpdate state, string projectId) {
switch (state) {
case SClient::ProjUpdate::ServerLoaded:
ESP_LOGI(TAG, "eventProjectReport ID: %s ", projectId.data());
break;
case SClient::ProjUpdate::StartTransfer:
ESP_LOGI(TAG, "eventStartProjectTransfer");
break;
case SClient::ProjUpdate::EndTransfer:
ESP_LOGI(TAG, "eventEndProjectTransfer");
spitProjectFinish.give();
break;
case SClient::ProjUpdate::ServerClosed:
ESP_LOGI(TAG, "eventProjectClosed ID: %s", projectId.data());
// If we don't save SPITServer's project specific information
// perhaps close all SPITObjects and RemoteSPITObjects to avoid conflicts, when the SPITServer loads a new project
// SPITObject::clearSPITObjects();
// SPITObject::clearSPITObjectsRemote();
break;
default:
break;
}
}
bool onCheckSpitTypeVeto(bool knownType, bool createdByClient) {
return true; // Bypass veto
if (!knownType || !createdByClient) { // Don't allow foreign SPITTypes
return false;
}
return true;
}
bool onCheckSpitObjectVeto(bool knownType) {
return true; // Bypass veto
if (!knownType) { // Don't allow objects of unregistered SPITTypes
return false;
}
return true;
}
// Called when the server has created, loaded, changed or deleted a (Remote) SPITType.
// Remote SPITType are used in Remote SPITObjects. A Remote SPITObject is used to remote control the SPITServer.
// May also occur when the server confirms a SPITType created or changed by the client.
void onSpitType(SClient::ObjectEvent event, bool isRemote, const SType& sType) {
if (isRemote) {
if (event == SClient::ObjectEvent::Created) {
ESP_LOGI(TAG, "NEW SPIT-REMOTE-Type ID: %s GroupName: %s Name: %s confirmed: %d", sType.getID().data(), sType.GroupName.get().data(), sType.Name.get().data(), sType.getConfirmed());
auto typeName = sType.Name.get();
if (ExpectedObjects.contains(typeName)) {
ExpectedObjects[typeName] = sType.getID();
}
} else if (event == SClient::ObjectEvent::Changed) {
// we had to inform all Remote SPITObjects using the p_SPITType
// perhaps the SPITObject uses information from the SPITType
// SPITObject *ptrSPITObject;
// int intSPITObjectCount;
// intSPITObjectCount = SPITObject::getSPITObjectRemoteCount();
// for (int intObject = 0; intObject < intSPITObjectCount; intObject++) {
// ptrSPITObject = SPITObject::getSPITObjectRemote(intObject);
// if (ptrSPITObject == nullptr)
// continue;
// if (ptrSPITObject->getSPITTypeID() == p_SPITType.getID()) {
// // call ecentSPITObjectRemote
// // onSpitObjectRemote(SClient::ObjectEvent::Changed, p_SPITType, *ptrSPITObject);
// }
// }
// ESP_LOGI(TAG, "SPIT-REMOTE-Type CHANGED ID: %s GroupName: %s Name: %s confirmed: %d", sType.getID().data(), sType.GroupName.get().data(), sType.Name.get().data(), sType.getConfirmed());
} else if (event == SClient::ObjectEvent::Deleted) {
ESP_LOGD(TAG, "SPIT-REMOTE-Type Removed ID: %s GroupName: %s Name: %s\n", sType.getID().data(), sType.GroupName.get().data(), sType.Name.get().data());
}
} else {
if (event == SClient::ObjectEvent::Created) {
// Whenever you provide an action for this SPITType take this over in your code.
// call _SpitClient.setActionSPITType(sType.getID(), true);
// ESP_LOGI(TAG, "NEW SPIT-Type ID: %s GroupName: %s Name: %s confirmed: %d", sType.getID().data(), sType.GroupName.get().data(), sType.Name.get().data(), sType.getConfirmed());
} else if (event == SClient::ObjectEvent::Changed) {
// ESP_LOGI(TAG, "SPIT-Type CHANGED ID: %s GroupName: %s Name: %s confirmed: %d", sType.getID().data(), sType.GroupName.get().data(), sType.Name.get().data(), sType.getConfirmed());
} else if (event == SClient::ObjectEvent::Deleted) {
// ESP_LOGI(TAG, "SPIT-Type Removed ID: %s GroupName: %s Name: %s\n", sType.getID().data(), sType.GroupName.get().data(), sType.Name.get().data());
}
}
}
// Called when the server has created, loaded or changed a SPITObject.
// A Remote SPITObject is used to remote control the SPITServer.
// May also occur when the server confirms a SPITObject created by the client, what the client should never do!!.
// false = server changed a existing SPITObject
// @param p_SPITType a reference to a copy of the associated SPITType instance.
// @param p_SPITObject a reference to a copy of the SPITObject instance.
void onSpitObject(SClient::ObjectEvent event, bool isRemote, const SType& sType, const SObject& sObject) {
if (isRemote) {
if (event == SClient::ObjectEvent::Created) {
// ESP_LOGI(TAG, "NEW SPIT-REMOTE-Object ID: %s Name: %s -> SPITType: %s", sObject.getID().data(), sObject.Name.get().data(), sType.Name.get().data());
// This is a remote SPITObject which can be used to remote control the server.
// If you want you can list this for later use
} else if (event == SClient::ObjectEvent::Changed) {
// ESP_LOGI(TAG, "SPIT-REMOTE-Object CHANGED ID: %s Name: %s -> SPITType: %s", sObject.getID().data(), sObject.Name.get().data(), sType.Name.get().data());
} else if (event == SClient::ObjectEvent::Deleted) {
ESP_LOGD(TAG, "SPIT-REMOTE-Object Removed ID: %s Name: %s\n", sObject.getID().data(), sObject.Name.get().data());
// This is a remote SPITObject
// If you have listet this prior you should remove it from the list
}
} else {
if (event == SClient::ObjectEvent::Created) {
// ESP_LOGI(TAG, "NEW SPIT-Object ID: %s Name: %s -> SPITType: %s", sObject.getID().data(), sObject.Name.get().data(), sType.Name.get().data());
// check your actions
// Whenever you provide an action for this SPITObject take this over in your code.
// call _SpitClient.setActionSPITObject(sObject.getID(), true);
// for our little excample as described in loadClientProject:
// We have only actions for SPITTypes - means every SPITObject with the associated "Actionx"-SPITTypes will trigger the action when it is played
//-> no need to do something here, because setActionSPITType has been set prior and the SPITObject will automatically inherits this
// SPITObjects with other SPITTypes asscciated will trigger no actions
} else if (event == SClient::ObjectEvent::Changed) {
// ESP_LOGI(TAG, "SPIT-Object CHANGED ID: %s Name: %s -> SPITType: %s", sObject.getID().data(), sObject.Name.get().data(), sType.Name.get().data());
} else if (event == SClient::ObjectEvent::Deleted) {
ESP_LOGD(TAG, "SPIT-Object Removed ID: %s Name: %s\n", sObject.getID().data(), sObject.Name.get().data());
// Check your actions
// take the removal of the SPITObject over in your code
// CAUTION: Deleted SPITObjects can be added later again (perhaps per UNDO on the SPITServer)
//-> don't remove your assigned action immediately - keep it to reassign it later
}
}
}
// SPITServer may use several methods for playing SPITObjects.
// e.g. liveSHOWsoftware has the timeline and jingles which plays objects, where the timeline the main player is.
// SPIT distinguishes between the different players. All eventPlay... functions contain the information which player plays the SPIT object.
void onPlay(string p_PlayerID, string p_PlayerName, char p_PlayerType, bool p_PlayerIsRunning, uint64_t p_TimeFrames) {
ESP_LOGD(TAG, "eventPlay Player: %s Runs: %d Time: %lld", p_PlayerName.data(), p_PlayerIsRunning, p_TimeFrames);
switch (p_PlayerType) {
case SClient::PlayerType::PTYPE_MAIN:
// p_FrameTimes contains the position inside the server's project, we can use it like a timecode
// int intMilliseconds = (int)(((double)p_TimeFrames / (double)m_ServerFramesPerSecond) * 1000);
break;
case SClient::PlayerType::PTYPE_CONTINOUS:
// The olayer is playing only single Object sequences sometime during the project
break;
case SClient::PlayerType::PTYPE_DISCRETE:
// Only discrete values where submitted, e.g the fader position of remote faders.
break;
}
}
void onPlaySpitType(string p_PlayerID, string spitTypeId, bool p_PlayerIsRunning, bool p_Started, bool p_Ended, double p_Value) {
// double doubleValue = ((double)p_PlayObject.getValue() / 1000000) * ((double)p_PlayObject.getValueFactor() / 1000000);
ESP_LOGD(TAG, "eventPlayObject Player: %s is running: %d Object Started: %d Stopped: %d", p_PlayerID.data(), p_PlayerIsRunning, p_Started, p_Ended);
// ESP_LOGD(TAG, " SPITType: %s SPITObject: %s Position: %lld Value: %f\n", spitTypeId.data(), p_SPITObject.getName().data(), p_PlayObject.getFrameInside(), doubleValue);
ESP_LOGD(TAG, " SPITType: %s Value: %f\n", spitTypeId.data(), p_Value);
if (!ProvidedSpitActions.contains(spitTypeId)) {
ESP_LOGE(TAG, "SPIT ID %s is not accepted", spitTypeId.c_str());
return;
}
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>());
Syslog::logv(Syslog::Severity::Informational, "Got SPIT command for move ot : r%d", command.pos);
}
}
/**
* Called when a remote SpitObject which value function is changed by the server.
* This allows the Client to adjust its display for the remote command.
* @param p_SPITType the assigned remote SPITType of p_SPITObject
* @param p_SPITObject the remote SPITObject which is changed
* @param p_FrameInsideObject can be discarded
* @param p_Value the new value
*/
void onPlayRemoteObjectStatus(const SType& sType, const SObject& sObject, uint64_t frameInsideObject, int value) {
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");
// 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 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 : 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();
// 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);
// for (auto&& obj : ExpectedObjects) {
// if (obj.second == "") {
// ESP_LOGE(TAG, "No matching Remote ID for %s", obj.first.c_str());
// }
// }
// 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]]);
// }
// }
// }
// 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.moveTo, [](string d) { startShuffle(d); });
// client.start();
// }
// 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 app_main(void) {
initialize_ethernet();
NvsApi::init();
static fos::Task lightingTask(LightingTask, "Lights", 0x8000uL);
etherActive.take();
ESP_LOGI(TAG, "Start net services");
// 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);
auto& store = NvsApi::getInstance();
auto logger = store.getValue(ConfigKeys.syslogDest);
if (logger != "") {
Syslog::setupTask(IpAdr{logger, 0});
}
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());
while (true) {
fos::Task::delayMs(5000);
Syslog::logv(Syslog::Severity::Notice, "Min heap size: %u / FreeHeap %lu",
heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT),
esp_get_free_heap_size());
}
}