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(); }