Merge pull request #1023 from Aircoookie/websockets
Merge Websockets branch
This commit is contained in:
commit
533f86c035
@ -2,6 +2,8 @@
|
||||
|
||||
### Development versions after 0.10.0 release
|
||||
|
||||
#### Build 2007020
|
||||
|
||||
#### Build 2006251
|
||||
|
||||
- Added `SV=2` to HTTP API, allow selecting single segment only
|
||||
|
@ -165,7 +165,7 @@ lib_deps =
|
||||
ESPAsyncTCP@1.2.0
|
||||
ESPAsyncUDP@697c75a025
|
||||
AsyncTCP@1.0.3
|
||||
Esp Async WebServer@1.2.0
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer
|
||||
IRremoteESP8266@2.7.3
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
|
@ -1599,7 +1599,7 @@ uint16_t WS2812FX::mode_oscillate(void)
|
||||
uint16_t WS2812FX::mode_lightning(void)
|
||||
{
|
||||
uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash
|
||||
uint16_t ledlen = random16(SEGLEN -1 -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1)
|
||||
uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1)
|
||||
uint8_t bri = 255/random8(1, 3);
|
||||
|
||||
if (SEGENV.step == 0)
|
||||
@ -2247,7 +2247,7 @@ uint16_t WS2812FX::mode_ripple_rainbow(void) {
|
||||
CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
|
||||
{
|
||||
// Overall twinkle speed (changed)
|
||||
uint16_t ticks = ms / (32 - (SEGMENT.speed >> 3));
|
||||
uint16_t ticks = ms / SEGENV.aux0;
|
||||
uint8_t fastcycle8 = ticks;
|
||||
uint16_t slowcycle16 = (ticks >> 8) + salt;
|
||||
slowcycle16 += sin8(slowcycle16);
|
||||
@ -2312,10 +2312,11 @@ uint16_t WS2812FX::twinklefox_base(bool cat)
|
||||
// numbers that it generates is (paradoxically) stable.
|
||||
uint16_t PRNG16 = 11337;
|
||||
|
||||
// Calculate speed
|
||||
if (SEGMENT.speed > 100) SEGENV.aux0 = 3 + ((255 - SEGMENT.speed) >> 3);
|
||||
else SEGENV.aux0 = 22 + ((100 - SEGMENT.speed) >> 1);
|
||||
|
||||
// Set up the background color, "bg".
|
||||
// if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of
|
||||
// the current palette are identical, then a deeply faded version of
|
||||
// that color is used for the background color
|
||||
CRGB bg;
|
||||
bg = col_to_crgb(SEGCOLOR(1));
|
||||
uint8_t bglight = bg.getAverageLight();
|
||||
|
@ -87,7 +87,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id);
|
||||
void serializeState(JsonObject root);
|
||||
void serializeInfo(JsonObject root);
|
||||
void serveJson(AsyncWebServerRequest* request);
|
||||
void serveLiveLeds(AsyncWebServerRequest* request);
|
||||
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
|
||||
|
||||
//led.cpp
|
||||
void setValuesFromMainSeg();
|
||||
@ -210,6 +210,11 @@ String settingsProcessor(const String& var);
|
||||
String dmxProcessor(const String& var);
|
||||
void serveSettings(AsyncWebServerRequest* request);
|
||||
|
||||
//ws.cpp
|
||||
void handleWs();
|
||||
void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
|
||||
void sendDataWs(AsyncWebSocketClient * client = nullptr);
|
||||
|
||||
//xml.cpp
|
||||
void XML_response(AsyncWebServerRequest *request, char* dest = nullptr);
|
||||
void URL_response(AsyncWebServerRequest *request);
|
||||
|
@ -333,6 +333,12 @@ void serializeInfo(JsonObject root)
|
||||
root["lip"] = realtimeIP.toString();
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
root["ws"] = ws.count();
|
||||
#else
|
||||
root["ws"] = -1;
|
||||
#endif
|
||||
|
||||
root["fxcount"] = strip.getModeCount();
|
||||
root["palcount"] = strip.getPaletteCount();
|
||||
|
||||
@ -454,21 +460,37 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
|
||||
#define MAX_LIVE_LEDS 180
|
||||
|
||||
void serveLiveLeds(AsyncWebServerRequest* request)
|
||||
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
||||
{
|
||||
AsyncWebSocketClient * wsc;
|
||||
if (!request) { //not HTTP, use Websockets
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
wsc = ws.client(wsClient);
|
||||
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t used = ledCount;
|
||||
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
|
||||
char buffer[2000] = "{\"leds\":[";
|
||||
olen = 9;
|
||||
obuf = buffer;
|
||||
olen = 9;
|
||||
|
||||
for (uint16_t i= 0; i < used; i += n)
|
||||
{
|
||||
olen += sprintf(buffer + olen, "\"%06X\",", strip.getPixelColor(i));
|
||||
olen += sprintf(obuf + olen, "\"%06X\",", strip.getPixelColor(i));
|
||||
}
|
||||
olen -= 1;
|
||||
oappend("],\"n\":");
|
||||
oappendi(n);
|
||||
oappend("}");
|
||||
request->send(200, "application/json", buffer);
|
||||
if (request) {
|
||||
request->send(200, "application/json", buffer);
|
||||
}
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
else {
|
||||
wsc->text(obuf, olen);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
/*
|
||||
* LED methods
|
||||
*/
|
||||
|
||||
void setValuesFromMainSeg()
|
||||
{
|
||||
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
|
||||
@ -167,6 +166,7 @@ void colorUpdated(int callMode)
|
||||
|
||||
void updateInterfaces(uint8_t callMode)
|
||||
{
|
||||
sendDataWs();
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
if (espalexaDevice != nullptr && callMode != NOTIFIER_CALL_MODE_ALEXA) {
|
||||
espalexaDevice->setValue(bri);
|
||||
|
@ -14,8 +14,11 @@ WLED::WLED()
|
||||
void WLED::reset()
|
||||
{
|
||||
briT = 0;
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
ws.closeAll(1012);
|
||||
#endif
|
||||
long dly = millis();
|
||||
while (millis() - dly < 250) {
|
||||
while (millis() - dly < 450) {
|
||||
yield(); // enough time to send response to client
|
||||
}
|
||||
setAllLeds();
|
||||
@ -94,7 +97,8 @@ void WLED::loop()
|
||||
if (lastMqttReconnectAttempt > millis()) rolloverMillis++; //millis() rolls over every 50 days
|
||||
initMqtt();
|
||||
}
|
||||
|
||||
yield();
|
||||
handleWs();
|
||||
|
||||
// DEBUG serial logging
|
||||
#ifdef WLED_DEBUG
|
||||
@ -283,6 +287,10 @@ void WLED::initAP(bool resetAP)
|
||||
|
||||
void WLED::initConnection()
|
||||
{
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
ws.onEvent(wsEvent);
|
||||
#endif
|
||||
|
||||
WiFi.disconnect(true); // close old connections
|
||||
#ifdef ESP8266
|
||||
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2006251
|
||||
#define VERSION 2007020
|
||||
|
||||
// ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS).
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#endif
|
||||
#define WLED_ENABLE_ADALIGHT // saves 500b only
|
||||
//#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2)
|
||||
//#define WLED_ENABLE_WEBSOCKETS
|
||||
|
||||
#define WLED_DISABLE_FILESYSTEM // SPIFFS is not used by any WLED feature yet
|
||||
//#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version
|
||||
@ -480,6 +481,9 @@ WLED_GLOBAL bool doPublishMqtt _INIT(false);
|
||||
|
||||
// server library objects
|
||||
WLED_GLOBAL AsyncWebServer server _INIT_N(((80)));
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
WLED_GLOBAL AsyncWebSocket ws _INIT_N((("/ws")));
|
||||
#endif
|
||||
WLED_GLOBAL AsyncClient* hueClient _INIT(NULL);
|
||||
WLED_GLOBAL AsyncMqttClient* mqtt _INIT(NULL);
|
||||
|
||||
|
@ -205,10 +205,10 @@ void initServer()
|
||||
} else
|
||||
{
|
||||
server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254);
|
||||
serveMessage(request, 500, "Access Denied", F("Please unlock OTA in security settings!"), 254);
|
||||
});
|
||||
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254);
|
||||
serveMessage(request, 500, "Access Denied", F("Please unlock OTA in security settings!"), 254);
|
||||
});
|
||||
}
|
||||
|
||||
@ -227,6 +227,10 @@ void initServer()
|
||||
serveIndexOrWelcome(request);
|
||||
});
|
||||
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
server.addHandler(&ws);
|
||||
#endif
|
||||
|
||||
//called when the url is not defined here, ajax-in; get-settings
|
||||
server.onNotFound([](AsyncWebServerRequest *request){
|
||||
DEBUG_PRINTLN("Not-Found HTTP call:");
|
||||
@ -374,7 +378,7 @@ void serveSettings(AsyncWebServerRequest* request)
|
||||
|
||||
if (subPage == 1 && wifiLock && otaLock)
|
||||
{
|
||||
serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254); return;
|
||||
serveMessage(request, 500, "Access Denied", F("Please unlock OTA in security settings!"), 254); return;
|
||||
}
|
||||
|
||||
#ifdef WLED_DISABLE_MOBILE_UI //disable welcome page if not enough storage
|
||||
|
114
wled00/ws.cpp
Normal file
114
wled00/ws.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* WebSockets server for bidirectional communication
|
||||
*/
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
|
||||
uint16_t wsLiveClientId = 0;
|
||||
unsigned long wsLastLiveTime = 0;
|
||||
//uint8_t* wsFrameBuffer = nullptr;
|
||||
|
||||
#define WS_LIVE_INTERVAL 40
|
||||
|
||||
void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)
|
||||
{
|
||||
if(type == WS_EVT_CONNECT){
|
||||
//client connected
|
||||
sendDataWs(client);
|
||||
//client->ping();
|
||||
} else if(type == WS_EVT_DISCONNECT){
|
||||
//client disconnected
|
||||
if (client->id() == wsLiveClientId) wsLiveClientId = 0;
|
||||
} else if(type == WS_EVT_DATA){
|
||||
//data packet
|
||||
AwsFrameInfo * info = (AwsFrameInfo*)arg;
|
||||
if(info->final && info->index == 0 && info->len == len){
|
||||
//the whole message is in a single frame and we got all of it's data (max. 1450byte)
|
||||
if(info->opcode == WS_TEXT)
|
||||
{
|
||||
bool verboseResponse = false;
|
||||
{ //scope JsonDocument so it releases its buffer
|
||||
DynamicJsonDocument jsonBuffer(8192);
|
||||
DeserializationError error = deserializeJson(jsonBuffer, data, len);
|
||||
JsonObject root = jsonBuffer.as<JsonObject>();
|
||||
if (error || root.isNull()) return;
|
||||
|
||||
if (root.containsKey("lv"))
|
||||
{
|
||||
wsLiveClientId = root["lv"] ? client->id() : 0;
|
||||
}
|
||||
|
||||
verboseResponse = deserializeState(root);
|
||||
}
|
||||
if (verboseResponse || millis() - lastInterfaceUpdate < 1900) sendDataWs(client); //update if it takes longer than 100ms until next "broadcast"
|
||||
}
|
||||
} else {
|
||||
//message is comprised of multiple frames or the frame is split into multiple packets
|
||||
//if(info->index == 0){
|
||||
//if (!wsFrameBuffer && len < 4096) wsFrameBuffer = new uint8_t[4096];
|
||||
//}
|
||||
|
||||
//if (wsFrameBuffer && len < 4096 && info->index + info->)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
if((info->index + len) == info->len){
|
||||
if(info->final){
|
||||
if(info->message_opcode == WS_TEXT) {
|
||||
client->text("{\"error\":10}"); //we do not handle split packets right now
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(type == WS_EVT_ERROR){
|
||||
//error was received from the other end
|
||||
|
||||
} else if(type == WS_EVT_PONG){
|
||||
//pong message was received (in response to a ping request maybe)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void sendDataWs(AsyncWebSocketClient * client)
|
||||
{
|
||||
if (!ws.count()) return;
|
||||
AsyncWebSocketMessageBuffer * buffer;
|
||||
|
||||
{ //scope JsonDocument so it releases its buffer
|
||||
DynamicJsonDocument doc(8192);
|
||||
JsonObject state = doc.createNestedObject("state");
|
||||
serializeState(state);
|
||||
JsonObject info = doc.createNestedObject("info");
|
||||
serializeInfo(info);
|
||||
size_t len = measureJson(doc);
|
||||
buffer = ws.makeBuffer(len);
|
||||
if (!buffer) return; //out of memory
|
||||
|
||||
serializeJson(doc, (char *)buffer->get(), len +1);
|
||||
}
|
||||
if (client) {
|
||||
client->text(buffer);
|
||||
} else {
|
||||
ws.textAll(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void handleWs()
|
||||
{
|
||||
if (millis() - wsLastLiveTime > WS_LIVE_INTERVAL)
|
||||
{
|
||||
ws.cleanupClients();
|
||||
bool success = true;
|
||||
if (wsLiveClientId)
|
||||
success = serveLiveLeds(nullptr, wsLiveClientId);
|
||||
wsLastLiveTime = millis();
|
||||
if (!success) wsLastLiveTime -= 20; //try again in 20ms if failed due to non-empty WS queue
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void handleWs() {}
|
||||
void sendDataWs(AsyncWebSocketClient * client) {}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user