From 1cf70b19a9f0fb8526aedd70f3dc9a8298312475 Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 28 Apr 2019 23:29:44 +0200 Subject: [PATCH] got the wemos udp strip working --- .gitignore | 3 + .../settings.example.h | 12 +- .../strip-wemosd1-websocket.ino | 92 +++++++++++++++ .../wemos_websocket_ledclient.ino | 107 ------------------ docker-compose.yml | 7 +- server/BackendProvider/WebSocketServer.py | 36 ++++-- server/BackendProvider/WemosStripUDPServer.py | 80 +++++++++---- server/rgbUtils/RGBStrip.py | 2 +- 8 files changed, 200 insertions(+), 139 deletions(-) create mode 100644 clients/strip-wemosd1-websocket/strip-wemosd1-websocket.ino delete mode 100644 clients/strip-wemosd1-websocket/wemos_websocket_ledclient.ino diff --git a/.gitignore b/.gitignore index d62506e..43536ba 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ node_modules clients/controller-html/old clients/strip-wemosd1-websocket/settings.h +clients/strip-wemosd1-websocket/websocket.old + +.gitignore .directory diff --git a/clients/strip-wemosd1-websocket/settings.example.h b/clients/strip-wemosd1-websocket/settings.example.h index 40cde6d..552cb0d 100644 --- a/clients/strip-wemosd1-websocket/settings.example.h +++ b/clients/strip-wemosd1-websocket/settings.example.h @@ -3,5 +3,15 @@ #define LED_TYPE WS2811 #define COLOR_ORDER GRB +#define MILLI_AMPS 2000 // IMPORTANT: set the max milli-Amps of your power supply (4A = 4000mA) +#define FRAMES_PER_SECOND 120 // here you can control the speed. With the Access Point / Web Server the animations run a bit slower. + const char* ssid = "none"; -const char* password = "none"; \ No newline at end of file +const char* password = "none"; + +//RecipientIP is overridden after wl connect, getting localip and set last octet to 255 (Broadcast) +//this will only work for /24 networks. If you want to set an other BC or a sinle IP adress set overriderecipient to true +bool overriderecipient = false; +IPAddress RecipientIP = IPAddress(0, 0, 0, 0); + +unsigned int RecipientPort = 8002; diff --git a/clients/strip-wemosd1-websocket/strip-wemosd1-websocket.ino b/clients/strip-wemosd1-websocket/strip-wemosd1-websocket.ino new file mode 100644 index 0000000..da2dfec --- /dev/null +++ b/clients/strip-wemosd1-websocket/strip-wemosd1-websocket.ino @@ -0,0 +1,92 @@ + +//#define FASTLED_ALLOW_INTERRUPTS 0 +//#define FASTLED_INTERRUPT_RETRY_COUNT 0 +//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC +#define UDP_TX_PACKET_MAX_SIZE = 50*12+1; + +#include +#include +#include + +#include "settings.h" + +CRGB leds[NUM_LEDS]; + +WiFiClient client; + +// buffers for receiving and sending data +char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; +WiFiUDP Udp; + +int c = -1; + +void setup() { + Serial.begin(115200); + FastLED.addLeds(leds, NUM_LEDS); // for WS2812 (Neopixel) + //FastLED.addLeds(leds, NUM_LEDS); // for APA102 (Dotstar) + FastLED.setDither(false); + FastLED.setCorrection(TypicalLEDStrip); + FastLED.setBrightness(255); + FastLED.setMaxPowerInVoltsAndMilliamps(5, MILLI_AMPS); + fill_solid(leds, NUM_LEDS, CRGB::Black); + wdt_enable(WDTO_4S); // Watchdog auf 4 s stellen + FastLED.show(); + Udp.begin(random(5000,5500)); + +} + +void loop() { + EVERY_N_MILLISECONDS( 250 ) { + sendUDP(String("s:ping")); + } + EVERY_N_MILLISECONDS( 15 ) { + wdt_reset(); + if (WiFi.status() != WL_CONNECTED) { // FIX FOR USING 2.3.0 CORE (only .begin if not connected) + WiFi.begin(ssid, password); // connect to the network + while (WiFi.status() != WL_CONNECTED) { + delay(500); + } + if (WiFi.status() == WL_CONNECTED) { + // Start UDP + Serial.println("start udp"); + if(!overriderecipient){ + RecipientIP = WiFi.localIP(); + RecipientIP[3] = 255; + } + } + } + + } + int packetSize = Udp.parsePacket(); + if(packetSize) + { + // read the packet into packetBufffer + Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE); + if(packetBuffer[0] == 's' && packetBuffer[1] == 'r'){ + sendUDP("r:1:"+StripName+":"+String(NUM_LEDS)+":0"); + } + if(packetBuffer[0] == 's' && packetBuffer[1] == 'u'){ + FastLED.show(); + } + if(packetBuffer[0] == 'd'){ + c=1; + while(c -#include -#include -#include - -#define MILLI_AMPS 2000 // IMPORTANT: set the max milli-Amps of your power supply (4A = 4000mA) -#define FRAMES_PER_SECOND 120 // here you can control the speed. With the Access Point / Web Server the animations run a bit slower. - - -CRGB leds[NUM_LEDS]; - - -WiFiUDP Udp; -unsigned int localUdpPort = 4210; // local port to listen on -char incomingPacket[255]; // buffer for incoming packets - -WiFiClient client; - -void setup() { - //Serial.begin(115200); - FastLED.addLeds(leds, NUM_LEDS); // for WS2812 (Neopixel) - //FastLED.addLeds(leds, NUM_LEDS); // for APA102 (Dotstar) - FastLED.setDither(false); - FastLED.setCorrection(TypicalLEDStrip); - FastLED.setBrightness(255); - FastLED.setMaxPowerInVoltsAndMilliamps(5, MILLI_AMPS); - fill_solid(leds, NUM_LEDS, CRGB::Black); - FastLED.show(); - - Udp.begin(localUdpPort); -} - -void loop() { - EVERY_N_MILLISECONDS( 15 ) { - if (WiFi.status() != WL_CONNECTED) { // FIX FOR USING 2.3.0 CORE (only .begin if not connected) - WiFi.begin(ssid, password); // connect to the network - while (WiFi.status() != WL_CONNECTED) { - delay(500); - } - } - - //Kompletter Strip in einer farbe - //for future use, when ws1812b strips are usable. - //int root_length = root.size(); - //for(int i=0; i 0) - { - incomingPacket[len] = 0; - } - Serial.printf("UDP packet contents: %s\n", incomingPacket); - - // send back a reply, to the IP address and port we got the packet from - Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); - Udp.write(replyPacket); - Udp.endPacket(); - } - - fill_solid( leds, NUM_LEDS, CRGB( red, green, blue) ); - FastLED.show(); - - -} -void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { - - switch(type) { - case WStype_DISCONNECTED: - //USE_SERIAL.printf("[WSc] Disconnected!\n"); - break; - case WStype_CONNECTED: { - //USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); - - // send message to server when Connected - webSocket.sendTXT("{\"register_client_type\":1,\"client_name\":\"wemosd1\"}"); - } - break; - case WStype_TEXT: - //USE_SERIAL.printf("[WSc] get text: %s\n", payload); - JsonObject& root = jsonBuffer.parseObject(payload); - if (!root.success()) { - jsonBuffer.clear(); - }else{ - red = root["data"]["0"]["red"]; - green = root["data"]["0"]["green"]; - blue = root["data"]["0"]["blue"]; - } - // send message to server - // webSocket.sendTXT("message here"); - break; - } - -} - diff --git a/docker-compose.yml b/docker-compose.yml index 761db00..ae7c8fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,9 +7,10 @@ services: image: ${USER}/ledserver-controller-html ledserver: build: server - ports: - - "8001:8001" - - "8002:8002/udp" + #ports: + # - "8001:8001" + # - "8002:8002/udp" + network_mode: host #docker is changing the source port of the udp packages on the bridge image: ${USER}/ledserver user: "${UID}:${GID}" #volumes: diff --git a/server/BackendProvider/WebSocketServer.py b/server/BackendProvider/WebSocketServer.py index 61104a6..66c7057 100644 --- a/server/BackendProvider/WebSocketServer.py +++ b/server/BackendProvider/WebSocketServer.py @@ -49,7 +49,7 @@ class HTTPWebSocketsHandler(WebSocket): # Client Registration on the Websocket Server # maybe it would be better to use a websocket server thread for each client type, # can be done in future if there is too much latency - if "register_client_type" in data: + if "register_client_type" in self.data: # the controler type, add handler on RGBStripContoller and send the current state of the controller if int(data['register_client_type']) is CLIENT_TYPE_CONTROLLER: self.client_type = CLIENT_TYPE_CONTROLLER @@ -60,10 +60,20 @@ class HTTPWebSocketsHandler(WebSocket): # register new Stripes elif int(data['register_client_type']) is CLIENT_TYPE_STRIPE and "client_name" in data: self.client_type = CLIENT_TYPE_STRIPE + ledcount=1 + if "led_count" in data: + ledcount = int(data["led_count"]) + self.nojson=0 + if "nojson" in data: + self.nojson = int(data["nojson"]) + self.nosend=0 + if "nosend" in data: + self.nosend = int(data["nosend"]) + # registers the strip with websocket object and name. the onRGBStripValueUpdate(rgbStrip) is called by # by the rgbStrip when an effectThread updates it # the self.rgbStrip variable is used to unregister the strip only - self.rgbStrip = self.rgbStripController.registerRGBStrip(data["client_name"],self.onRGBStripValueUpdate) + self.rgbStrip = self.rgbStripController.registerRGBStrip(data["client_name"],self.onRGBStripValueUpdate,ledcount) # register new Audio Recorders elif int(data['register_client_type']) is CLIENT_TYPE_RECORDER: self.client_type = CLIENT_TYPE_RECORDER @@ -79,6 +89,11 @@ class HTTPWebSocketsHandler(WebSocket): return # the stripe should usualy not send any data, i do not know why it should... elif self.client_type is CLIENT_TYPE_STRIPE: + if self.nosend == 1: + respdata = "d" + for i in range(self.rgbStrip.STRIP_LENGHT): + respdata += ":" + str(i) + ":"+str(self.rgbStrip.red[i])+":"+str(self.rgbStrip.green[i])+":"+str(self.rgbStrip.blue[i]) + self.sendMessage(respdata) return # audio recorder responses are handled by the effectControllerJsonHandler elif self.client_type is CLIENT_TYPE_RECORDER: @@ -124,8 +139,15 @@ class HTTPWebSocketsHandler(WebSocket): # when a rgbStrip value is changed, send json data to client def onRGBStripValueUpdate(self,rgbStrip): - self.sendMessage( - json.dumps({ - 'data': rgbStripControllerJsonHelper.getRGBData(rgbStrip) - }) - ) + if self.nosend == 0: + if self.nojson == 0: + self.sendMessage( + json.dumps({ + 'data': rgbStripControllerJsonHelper.getRGBData(rgbStrip) + }) + ) + if self.nojson == 1: + respdata = "d" + for i in range(rgbStrip.STRIP_LENGHT): + respdata += ":" + str(i) + ":"+str(rgbStrip.red[i])+":"+str(rgbStrip.green[i])+":"+str(rgbStrip.blue[i]) + self.sendMessage(respdata) diff --git a/server/BackendProvider/WemosStripUDPServer.py b/server/BackendProvider/WemosStripUDPServer.py index 6d69045..793c7ff 100644 --- a/server/BackendProvider/WemosStripUDPServer.py +++ b/server/BackendProvider/WemosStripUDPServer.py @@ -41,6 +41,7 @@ class ThreadedUDPServer(threading.Thread): while not self.stopped: for key in list(UDPClients.keys()): if UDPClients[key].lastping + 2 < time(): + print("ping missing, last ping: ",UDPClients[key].lastping," now is: ",time()) UDPClients[key].handleClose() del UDPClients[key] sleep(0.5) @@ -75,6 +76,7 @@ class UDPClient(): self.effectController = effectController self.rgbStripController = rgbStripController self.sendToClientLock = False + self.registered = False self.lastping = time() def handle(self, request): @@ -85,27 +87,57 @@ class UDPClient(): #print(time(),"clientdata -> ", clientdata) # socket.sendto(bytes("pong","utf-8"),self.client_address) + # Client Types: + # CLIENT_TYPE_CONTROLLER = 0 + # CLIENT_TYPE_STRIPE = 1 + # CLIENT_TYPE_RECORDER = 2 + + # UDP Registrierung Stripe: (nosend definiert, dass der stripe sich seine daten selbst anfordert) + # r:1:NUM_LEDS[:nosend] + # UDP Stripe sendet ping, woran festgemacht wird, ob er nocht lebt + # s:ping + # UDP + try: data = clientdata.split(':') - # print(data) + print(data) # r:1:srg strip name - if data[0] == "r" and int(data[1]) == CLIENT_TYPE_STRIPE and data[2] != None: + if data[0] == "r" and int(data[1]) == CLIENT_TYPE_STRIPE and data[2] != None and self.registered is False: + self.registered = True self.client_type = CLIENT_TYPE_STRIPE # registers the strip with websocket object and name. the onRGBStripValueUpdate(rgbStrip) is called by # by the rgbStrip when an effectThread updates it # the self.rgbStrip variable is used to unregister the strip only + + ledcount = 1 + if data[3] != None: + ledcount = int(data[3]) + + self.nosend = 0 + if data[4] != None: + self.nosend = int(data[4]) + self.rgbStrip = self.rgbStripController.registerRGBStrip( - data[2], self.onRGBStripValueUpdate) + data[2], self.onRGBStripValueUpdate, ledcount) # s:ping if data[0] == "s" and data[1] == "ping": # if we got a ping and the client has no client type defined, send status unregistered, so the client knows that he has to register if self.client_type is None and self.socket is not None: - self.sendToClient('s:unregistered') + self.sendToClient('sr') self.lastping = time() if data[0] == "u" and self.client_type == CLIENT_TYPE_STRIPE: - led = int(data[1]) - self.sendToClient('d:'+str(led)+':'+str(self.rgbStrip.red[led])+':'+str( - self.rgbStrip.green[led])+':'+str(self.rgbStrip.blue[led])+'') + #respdata = "d" + #for i in range(self.rgbStrip.STRIP_LENGHT): + # respdata += ":" + str(i) + ":"+str(self.rgbStrip.red[i])+":"+str(self.rgbStrip.green[i])+":"+str(self.rgbStrip.blue[i]) + #self.sendToClient(respdata) + for i in range(self.rgbStrip.STRIP_LENGHT): + self.sendToClient("d"+ + "{0:03}".format(i) + + "{0:03}".format(self.rgbStrip.red[i]) + + "{0:03}".format(self.rgbStrip.green[i]) + + "{0:03}".format(self.rgbStrip.blue[i]) + ) + #self.sendToClient('su') except Exception as e: print(e, traceback.format_exc()) @@ -117,21 +149,29 @@ class UDPClient(): def handleClose(self): if self.client_type is CLIENT_TYPE_STRIPE: self.rgbStripController.unregisterRGBStrip(self.rgbStrip) - #print(self.client_address, 'closed') + print(self.client_address, 'closed') # when a rgbStrip value is changed, send not json data but a formated string to client # d:[id off the LED, always 0 on RGB strips]:[red value 0-255]:[green value 0-255]:[blue value 0-255] - def onRGBStripValueUpdate(self, rgbStrip, led=0): - return # we send the update as requested, to prevent flooding the module - #self.sendToClient('d:'+str(led)+':'+str(rgbStrip.red[led])+':'+str( - # rgbStrip.green[led])+':'+str(rgbStrip.blue[led])+'') + def onRGBStripValueUpdate(self, rgbStrip): + if self.nosend == 0: + respdata = "d" + tmplen = 0 + for i in range(rgbStrip.STRIP_LENGHT): + if tmplen is 49: + self.sendToClient(respdata) + respdata = "d" + tmplen = 0 + respdata = respdata + "{0:03}".format(i) + "{0:03}".format(rgbStrip.red[i]) + "{0:03}".format(rgbStrip.green[i]) + "{0:03}".format(rgbStrip.blue[i]) + #self.sendToClient("d"+"{0:03}".format(i) + "{0:03}".format(rgbStrip.red[i]) + "{0:03}".format(rgbStrip.green[i]) + "{0:03}".format(rgbStrip.blue[i])) + #self.sendToClient("d:"+ str(i) + ":"+str(rgbStrip.red[i])+":"+str(rgbStrip.green[i])+":"+str(rgbStrip.blue[i])) + #respdata += ":" + str(i) + ":"+str(rgbStrip.red[i])+":"+str(rgbStrip.green[i])+":"+str(rgbStrip.blue[i]) + tmplen = tmplen+1 + self.sendToClient(respdata) + self.sendToClient('su') def sendToClient(self, message): - while self.sendToClientLock is True: - sleep(1) - self.sendToClientLock = True - if self.socket is not None: - self.socket.sendto( - message.encode(), self.client_address - ) - self.sendToClientLock = False + print("SendToClient:",self.client_address, message) + self.socket.sendto( + message.encode(), self.client_address + ) diff --git a/server/rgbUtils/RGBStrip.py b/server/rgbUtils/RGBStrip.py index 19ba775..2cb98e7 100644 --- a/server/rgbUtils/RGBStrip.py +++ b/server/rgbUtils/RGBStrip.py @@ -76,7 +76,7 @@ class RGBStrip: self.green[id] = int(green/100*brightness) self.blue[id] = int(blue/100*brightness) - self.onValuesUpdateHandler(self,id) + self.onValuesUpdateHandler(self) def off(self):