diff --git a/openBeken_win32_mvsc2017.vcxproj b/openBeken_win32_mvsc2017.vcxproj
index c8b144f06..c69d43c55 100644
--- a/openBeken_win32_mvsc2017.vcxproj
+++ b/openBeken_win32_mvsc2017.vcxproj
@@ -214,6 +214,7 @@
+
diff --git a/openBeken_win32_mvsc2017.vcxproj.filters b/openBeken_win32_mvsc2017.vcxproj.filters
index 822968332..321786e40 100644
--- a/openBeken_win32_mvsc2017.vcxproj.filters
+++ b/openBeken_win32_mvsc2017.vcxproj.filters
@@ -410,6 +410,7 @@
+
diff --git a/src/driver/drv_ddp.c b/src/driver/drv_ddp.c
index e5a26f72e..2e426b0f3 100644
--- a/src/driver/drv_ddp.c
+++ b/src/driver/drv_ddp.c
@@ -21,8 +21,8 @@ static const char* group = "239.255.250.250";
static int port = 4048;
static int g_ddp_socket_receive = -1;
static int g_retry_delay = 5;
-static int stat_packetsReceived = 0;
-static int stat_bytesReceived = 0;
+int stat_ddpPacketsReceived = 0;
+static int stat_ddpBytesReceived = 0;
static char *g_ddp_buffer = 0;
static int g_ddp_bufferSize = 512;
@@ -184,12 +184,12 @@ void DRV_DDP_RunFrame() {
}
//addLogAdv(LOG_INFO, LOG_FEATURE_DDP,"Received %i bytes from %s\n",nbytes,inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr));
- stat_packetsReceived++;
- stat_bytesReceived += nbytes;
+ stat_ddpPacketsReceived++;
+ stat_ddpBytesReceived += nbytes;
DDP_Parse((byte*)g_ddp_buffer, nbytes);
- if (stat_packetsReceived % 10 == 0) {
+ if (stat_ddpPacketsReceived % 10 == 0) {
rtos_delay_milliseconds(5);
}
}
@@ -203,7 +203,7 @@ void DRV_DDP_Shutdown()
}
void DRV_DDP_AppendInformationToHTTPIndexPage(http_request_t* request)
{
- hprintf255(request, "DDP received: %i packets, %i bytes
", stat_packetsReceived, stat_bytesReceived);
+ hprintf255(request, "DDP received: %i packets, %i bytes
", stat_ddpPacketsReceived, stat_ddpBytesReceived);
}
void DRV_DDP_Init()
{
diff --git a/src/driver/drv_ddpSend.c b/src/driver/drv_ddpSend.c
new file mode 100644
index 000000000..e39778fc7
--- /dev/null
+++ b/src/driver/drv_ddpSend.c
@@ -0,0 +1,197 @@
+
+
+#include "../new_common.h"
+#include "../new_pins.h"
+#include "../new_cfg.h"
+// Commands register, execution API and cmd tokenizer
+#include "../cmnds/cmd_public.h"
+#include "../driver/drv_public.h"
+#include "../logging/logging.h"
+#include "lwip/sockets.h"
+#include "lwip/ip_addr.h"
+#include "lwip/inet.h"
+#include "../httpserver/new_http.h"
+#include "drv_local.h"
+#include "../quicktick.h"
+
+
+static int g_socket_ddpSend = -1;
+static int stat_sendBytes = 0;
+static int stat_sendPackets = 0;
+static int stat_failedPackets = 0;
+
+typedef struct ddpQueueItem_s {
+ int curSize;
+ int totalSize;
+ byte *data;
+ int delay;
+ struct sockaddr_in adr;
+ struct ddpQueueItem_s *next;
+} ddpQueueItem_t;
+
+ddpQueueItem_t *g_queue = 0;
+
+int DRV_DDPSend_SendInternal(struct sockaddr_in *adr, const byte *frame, int numBytes) {
+ int nbytes = sendto(
+ g_socket_ddpSend,
+ (const char*)frame,
+ numBytes,
+ 0,
+ (struct sockaddr*) adr,
+ sizeof(*adr)
+ );
+ if (nbytes == numBytes) {
+ stat_sendBytes += numBytes;
+ stat_sendPackets++;
+ return numBytes;
+ }
+ stat_failedPackets++;
+ return 0;
+}
+void DRV_DDPSend_Send(const char *ip, int port, const byte *frame, int numBytes, int delay) {
+ struct sockaddr_in adr;
+ memset(&adr, 0, sizeof(adr));
+ adr.sin_family = AF_INET;
+ adr.sin_addr.s_addr = inet_addr(ip);
+ adr.sin_port = htons(port);
+
+ if (delay <= 0) {
+ DRV_DDPSend_SendInternal(&adr, frame, numBytes);
+ return;
+ }
+
+ ddpQueueItem_t *it = g_queue;
+ while (it) {
+ if (it->curSize == 0) {
+ break; // found empty
+ }
+ it = it->next;
+ }
+ if (it == 0) {
+ it = (ddpQueueItem_t*)malloc(sizeof(ddpQueueItem_t));
+ if (it == 0) {
+ // malloc failed
+ return;
+ }
+ memset(it,0,sizeof(ddpQueueItem_t));
+ it->adr = adr;
+ it->totalSize = it->curSize = numBytes;
+ it->data = malloc(numBytes);
+ if (it->data == 0) {
+ // malloc failed
+ free(it);
+ return;
+ }
+ it->next = g_queue;
+ g_queue = it;
+ }
+ if (it->totalSize < numBytes) {
+ byte *r = (byte*)realloc(it->data, numBytes);
+ if (r == 0) {
+ // realloc failed
+ return;
+ }
+ it->data = r;
+ it->totalSize = numBytes;
+ }
+ it->delay = delay;
+ memcpy(it->data, frame, numBytes);
+ it->curSize = numBytes;
+}
+void DRV_DDPSend_RunFrame() {
+ ddpQueueItem_t *t;
+ t = g_queue;
+ while (t) {
+ if (t->curSize > 0) {
+ t->delay -= g_deltaTimeMS;
+ if (t->delay <= 0) {
+ int sendBytes = DRV_DDPSend_SendInternal(&t->adr, t->data, t->curSize);
+ // TODO - do not clear if didn't send?
+ t->curSize = 0; // mark as empty
+ }
+ }
+ t = t->next;
+ }
+}
+void DRV_DDPSend_Shutdown()
+{
+ if (g_socket_ddpSend >= 0) {
+ close(g_socket_ddpSend);
+ g_socket_ddpSend = -1;
+ }
+ ddpQueueItem_t *it = g_queue;
+ while (it) {
+ ddpQueueItem_t *n = it->next;
+ free(it->data);
+ free(it);
+ it = n;
+ }
+ g_queue = 0;
+}
+void DRV_DDPSend_AppendInformationToHTTPIndexPage(http_request_t* request)
+{
+ hprintf255(request, "DDP sent: %i packets, %i bytes, errored packets: %i
",
+ stat_sendPackets, stat_sendBytes, stat_failedPackets);
+}
+
+// ddp as in WLED
+#define DDP_TYPE_RGB24 0x0B // 00 001 011 (RGB , 8 bits per channel, 3 channels)
+#define DDP_TYPE_RGBW32 0x1B // 00 011 011 (RGBW, 8 bits per channel, 4 channels)
+#define DDP_FLAGS1_VER1 0x40 // version=1
+
+// https://github.com/wled/WLED/blob/main/wled00/udp.cpp
+void DDP_SetHeader(byte *data, int pixelSize, int bytesCount) {
+ // set ident
+ data[0] = DDP_FLAGS1_VER1;
+
+ // set pixel size
+ if (pixelSize == 4) {
+ data[2] = DDP_TYPE_RGBW32;
+ }
+ else if (pixelSize == 3) {
+ data[2] = DDP_TYPE_RGB24;
+ }
+
+ // set bytes count
+ data[8] = (byte)((bytesCount >> 8) & 0xFF); // MSB
+ data[9] = (byte)(bytesCount & 0xFF); // LSB
+}
+commandResult_t DDP_Send(const void* context, const char* cmd, const char* args, int cmdFlags) {
+ Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES | TOKENIZER_DONT_EXPAND);
+ if (Tokenizer_GetArgsCount() < 1) {
+ return CMD_RES_NOT_ENOUGH_ARGUMENTS;
+ }
+ const char *ip = Tokenizer_GetArg(0);
+ int host = Tokenizer_GetArgInteger(1);
+ int pixelSize = Tokenizer_GetArgInteger(2);
+ int delay = Tokenizer_GetArgInteger(3);
+ const char *pData = Tokenizer_GetArg(4);
+ int numBytes = strlen(pData) / 2;
+ int headerSize = 10;
+ byte *data = malloc(headerSize+numBytes);
+ int cur = headerSize;
+ while (*pData) {
+ data[cur] = CMD_ParseOrExpandHexByte(&pData);
+ cur++;
+ }
+ DDP_SetHeader(data, pixelSize, (cur- headerSize));
+ DRV_DDPSend_Send(ip, host, data, cur, delay);
+ free(data);
+ return CMD_RES_OK;
+}
+void DRV_DDPSend_Init()
+{
+ // create what looks like an ordinary UDP socket
+ //
+ g_socket_ddpSend = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (g_socket_ddpSend < 0) {
+ g_socket_ddpSend = -1;
+ addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP, "DRV_DDPSend_Init: failed to do socket\n");
+ return;
+ }
+
+ CMD_RegisterCommand("DDP_Send", DDP_Send, NULL);
+}
+
+
+
diff --git a/src/driver/drv_local.h b/src/driver/drv_local.h
index 68ce1425a..f79f8c2c4 100644
--- a/src/driver/drv_local.h
+++ b/src/driver/drv_local.h
@@ -224,6 +224,12 @@ void TCL_UART_RunEverySecond(void);
void TCL_AppendInformationToHTTPIndexPage(http_request_t *request, int bPreState);
void TCL_DoDiscovery(const char *topic);
+void DRV_DDPSend_Init();
+void DRV_DDPSend_Shutdown();
+void DRV_DDPSend_RunFrame();
+void DRV_DDPSend_AppendInformationToHTTPIndexPage(http_request_t* request);
+
+
#define SM2135_DELAY 4
// Software I2C
diff --git a/src/driver/drv_main.c b/src/driver/drv_main.c
index 51af121ec..4033ea77e 100644
--- a/src/driver/drv_main.c
+++ b/src/driver/drv_main.c
@@ -315,7 +315,14 @@ static driver_t g_drivers[] = {
#if ENABLE_DRIVER_IR2
{ "IR2", DRV_IR2_Init, NULL, NULL, NULL, NULL, NULL, NULL, false },
#endif
-
+
+#if ENABLE_DRIVER_DDPSEND
+ //drvdetail:{"name":"DDPSend",
+ //drvdetail:"title":"TODO",
+ //drvdetail:"descr":"DDPqqqqqqq. See [DDP topic](https://www.elektroda.com/rtvforum/topic4040325.html)",
+ //drvdetail:"requires":""}
+ { "DDPSend", DRV_DDPSend_Init, NULL, DRV_DDPSend_AppendInformationToHTTPIndexPage, DRV_DDPSend_RunFrame, DRV_DDPSend_Shutdown, NULL, NULL, false },
+#endif
#if ENABLE_DRIVER_DDP
//drvdetail:{"name":"DDP",
//drvdetail:"title":"TODO",
diff --git a/src/new_common.h b/src/new_common.h
index 44c88b32a..a8dfb444f 100644
--- a/src/new_common.h
+++ b/src/new_common.h
@@ -273,7 +273,6 @@ This platform is not supported, error!
#include
#define closesocket close
#define SOCKET int
-#define closesocket close
#define ISVALIDSOCKET(s) ((s) >= 0)
#define GETSOCKETERRNO() (errno)
#define ioctlsocket ioctl
@@ -286,6 +285,7 @@ This platform is not supported, error!
#else
+#define close closesocket
#define ISVALIDSOCKET(s) ((s) != INVALID_SOCKET)
#define GETSOCKETERRNO() (WSAGetLastError())
diff --git a/src/obk_config.h b/src/obk_config.h
index 4749d4f21..912be1201 100644
--- a/src/obk_config.h
+++ b/src/obk_config.h
@@ -163,6 +163,7 @@
#define ENABLE_DRIVER_DRAWERS 1
#define ENABLE_TASMOTA_JSON 1
#define ENABLE_DRIVER_DDP 1
+#define ENABLE_DRIVER_DDPSEND 1
#define ENABLE_DRIVER_SSDP 1
#define ENABLE_DRIVER_ADCBUTTON 1
#define ENABLE_DRIVER_SM15155E 1
diff --git a/src/selftest/selftest_ws2812b.c b/src/selftest/selftest_ws2812b.c
index e567cc27a..7496be3d7 100644
--- a/src/selftest/selftest_ws2812b.c
+++ b/src/selftest/selftest_ws2812b.c
@@ -57,7 +57,7 @@ void Test_DMX_RGB() {
// nothing is sent by OBK at that point
}
-void Test_DMX_RGBW() {
+void Test_DMX_RGBC() {
// reset whole device
SIM_ClearOBK(0);
@@ -101,6 +101,62 @@ void Test_DMX_RGBW() {
// nothing is sent by OBK at that point
}
+void Test_DMX_RGBW() {
+ // reset whole device
+ //SIM_ClearOBK(0);
+
+ //SIM_UART_InitReceiveRingBuffer(4096);
+ //SIM_ClearUART();
+
+ //SELFTEST_ASSERT_HAS_UART_EMPTY();
+
+ //CMD_ExecuteCommand("startDriver DMX", 0);
+ //CMD_ExecuteCommand("SM16703P_Init 3 RGBW", 0);
+ //CMD_ExecuteCommand("SM16703P_SetPixel all 255 0 128 255", 0);
+ //SELFTEST_ASSERT_PIXEL4(0, 255, 0, 128, 255);
+ //SELFTEST_ASSERT_PIXEL4(1, 255, 0, 128, 255);
+ //SELFTEST_ASSERT_PIXEL4(2, 255, 0, 128, 255);
+
+ //SELFTEST_ASSERT_HAS_UART_EMPTY();
+ //CMD_ExecuteCommand("SM16703P_Start", 0);
+ //SELFTEST_ASSERT_HAS_SOME_DATA_IN_UART();
+ //SELFTEST_ASSERT_HAS_SENT_UART_STRING("00 FF0080FF FF0080FF FF0080FF");
+ //// 512 channels, but checked already 12
+ //for (int i = 0; i < 100; i++) {
+ // SELFTEST_ASSERT_HAS_SENT_UART_STRING("00 00 00 00 00");
+ //}
+ //SELFTEST_ASSERT_HAS_UART_EMPTY();
+
+ //CMD_ExecuteCommand("SM16703P_SetPixel 0 128 128 128 128", 0);
+ //CMD_ExecuteCommand("SM16703P_SetPixel 1 255 255 255 255", 0);
+ //CMD_ExecuteCommand("SM16703P_SetPixel 2 15 15 15 15", 0);
+ //SELFTEST_ASSERT_PIXEL4(0, 128, 128, 128, 128);
+ //SELFTEST_ASSERT_PIXEL4(1, 255, 255, 255, 255);
+ //SELFTEST_ASSERT_PIXEL4(2, 15, 15, 15, 15);
+ //SELFTEST_ASSERT_HAS_UART_EMPTY();
+ //CMD_ExecuteCommand("SM16703P_Start", 0);
+ //SELFTEST_ASSERT_HAS_SOME_DATA_IN_UART();
+ //SELFTEST_ASSERT_HAS_SENT_UART_STRING("00 80808080 FFFFFFFF 0F0F0F0F");
+ //// 512 channels, but checked already 12
+ //for (int i = 0; i < 100; i++) {
+ // SELFTEST_ASSERT_HAS_SENT_UART_STRING("00 00 00 00 00");
+ //}
+ //SELFTEST_ASSERT_HAS_UART_EMPTY();
+
+ // nothing is sent by OBK at that point
+}
+extern int stat_ddpPacketsReceived;
+void SIM_WaitForDDPPacket() {
+ int prev = stat_ddpPacketsReceived;
+
+ for (int i = 0; i < 100; i++) {
+ Sim_RunFrames(1, true);
+ if (stat_ddpPacketsReceived > prev) {
+ break; // got it
+ }
+ }
+
+}
void Test_WS2812B() {
// reset whole device
SIM_ClearOBK(0);
@@ -230,6 +286,27 @@ void Test_WS2812B() {
SELFTEST_ASSERT_PIXEL(1, 0xFF, 0, 0);
SELFTEST_ASSERT_PIXEL(2, 0xFF, 0, 0xFF);
}
+ CMD_ExecuteCommand("startDriver DDP", 0);
+ CMD_ExecuteCommand("startDriver DDPSend", 0);
+ CMD_ExecuteCommand("DDP_Send 127.0.0.1 4048 3 0 FF00AB", 0);
+ SIM_WaitForDDPPacket();
+ // this requires udp to work so it can pass...
+ if (1) {
+ SELFTEST_ASSERT_PIXEL(0, 0xFF, 0x00, 0xAB);
+ }
+ CMD_ExecuteCommand("DDP_Send 127.0.0.1 4048 3 0 ABCDEF", 0);
+ SIM_WaitForDDPPacket();
+ if (1) {
+ SELFTEST_ASSERT_PIXEL(0, 0xAB, 0xCD, 0xEF);
+ }
+ CMD_ExecuteCommand("DDP_Send 127.0.0.1 4048 3 0 ABCDEFAABBCC", 0);
+ SIM_WaitForDDPPacket();
+ if (1) {
+ SELFTEST_ASSERT_PIXEL(0, 0xAB, 0xCD, 0xEF);
+ SELFTEST_ASSERT_PIXEL(1, 0xAA, 0xBB, 0xCC);
+ }
+
+
// fake DDP RGBW packet
{
byte ddpPacket[128];
@@ -348,6 +425,7 @@ void Test_WS2812B() {
void Test_LEDstrips() {
Test_DMX_RGB();
+ Test_DMX_RGBC();
Test_DMX_RGBW();
Test_WS2812B();
}