mirror of
https://github.com/wled/WLED.git
synced 2025-04-19 20:37:23 +00:00

Break the actual JSON assembly apart from the file writing code. This permits calling it in other contexts, allowing us to pull the live config data even if the filesystem is out of date.
276 lines
8.1 KiB
C++
276 lines
8.1 KiB
C++
#include "wled.h"
|
|
|
|
#ifdef WLED_DEBUG_IMPROV
|
|
#define DIMPROV_PRINT(x) Serial.print(x)
|
|
#define DIMPROV_PRINTLN(x) Serial.println(x)
|
|
#define DIMPROV_PRINTF(x...) Serial.printf(x)
|
|
#else
|
|
#define DIMPROV_PRINT(x)
|
|
#define DIMPROV_PRINTLN(x)
|
|
#define DIMPROV_PRINTF(x...)
|
|
#endif
|
|
|
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
#undef WLED_DISABLE_IMPROV_WIFISCAN
|
|
#define WLED_DISABLE_IMPROV_WIFISCAN
|
|
#endif
|
|
|
|
#define IMPROV_VERSION 1
|
|
|
|
void parseWiFiCommand(char *rpcData);
|
|
|
|
enum ImprovPacketType {
|
|
Current_State = 0x01,
|
|
Error_State = 0x02,
|
|
RPC_Command = 0x03,
|
|
RPC_Response = 0x04
|
|
};
|
|
|
|
enum ImprovPacketByte {
|
|
Version = 6,
|
|
PacketType = 7,
|
|
Length = 8,
|
|
RPC_CommandType = 9
|
|
};
|
|
|
|
#ifndef WLED_DISABLE_IMPROV_WIFISCAN
|
|
static bool improvWifiScanRunning = false;
|
|
#endif
|
|
|
|
//blocking function to parse an Improv Serial packet
|
|
void handleImprovPacket() {
|
|
uint8_t header[6] = {'I','M','P','R','O','V'};
|
|
|
|
bool timeout = false;
|
|
unsigned waitTime = 25;
|
|
unsigned packetByte = 0;
|
|
unsigned packetLen = 9;
|
|
unsigned checksum = 0;
|
|
|
|
unsigned rpcCommandType = 0;
|
|
char rpcData[128];
|
|
rpcData[0] = 0;
|
|
|
|
while (!timeout) {
|
|
if (Serial.available() < 1) {
|
|
delay(1);
|
|
waitTime--;
|
|
if (!waitTime) timeout = true;
|
|
continue;
|
|
}
|
|
byte next = Serial.read();
|
|
|
|
DIMPROV_PRINT("Received improv byte: "); DIMPROV_PRINTF("%x\r\n",next);
|
|
|
|
switch (packetByte) {
|
|
case ImprovPacketByte::Version: {
|
|
if (next != IMPROV_VERSION) {
|
|
DIMPROV_PRINTLN(F("Invalid version"));
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case ImprovPacketByte::PacketType: {
|
|
if (next != ImprovPacketType::RPC_Command) {
|
|
DIMPROV_PRINTF("Non RPC-command improv packet type %i\n",next);
|
|
return;
|
|
}
|
|
if (!improvActive) improvActive = 1;
|
|
break;
|
|
}
|
|
case ImprovPacketByte::Length: packetLen = 9 + next; break;
|
|
case ImprovPacketByte::RPC_CommandType: rpcCommandType = next; break;
|
|
default: {
|
|
if (packetByte >= packetLen) { //end of packet, check checksum match
|
|
|
|
if (checksum != next) {
|
|
DIMPROV_PRINTF("Got RPC checksum %i, expected %i",next,checksum);
|
|
sendImprovStateResponse(0x01, true);
|
|
return;
|
|
}
|
|
|
|
switch (rpcCommandType) {
|
|
case ImprovRPCType::Command_Wifi: parseWiFiCommand(rpcData); break;
|
|
case ImprovRPCType::Request_State: {
|
|
unsigned improvState = 0x02; //authorized
|
|
if (WLED_WIFI_CONFIGURED) improvState = 0x03; //provisioning
|
|
if (Network.isConnected()) improvState = 0x04; //provisioned
|
|
sendImprovStateResponse(improvState, false);
|
|
if (improvState == 0x04) sendImprovIPRPCResult(ImprovRPCType::Request_State);
|
|
break;
|
|
}
|
|
case ImprovRPCType::Request_Info: sendImprovInfoResponse(); break;
|
|
#ifndef WLED_DISABLE_IMPROV_WIFISCAN
|
|
case ImprovRPCType::Request_Scan: startImprovWifiScan(); break;
|
|
#endif
|
|
default: {
|
|
DIMPROV_PRINTF("Unknown RPC command %i\n",next);
|
|
sendImprovStateResponse(0x02, true);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (packetByte < 6) { //check header
|
|
if (next != header[packetByte]) {
|
|
DIMPROV_PRINTLN(F("Invalid improv header"));
|
|
return;
|
|
}
|
|
} else if (packetByte > 9) { //RPC data
|
|
rpcData[packetByte - 10] = next;
|
|
if (packetByte > 137) return; //prevent buffer overflow
|
|
}
|
|
}
|
|
}
|
|
|
|
checksum += next;
|
|
checksum &= 0xFF;
|
|
packetByte++;
|
|
}
|
|
}
|
|
|
|
void sendImprovStateResponse(uint8_t state, bool error) {
|
|
if (!error && improvError > 0 && improvError < 3) sendImprovStateResponse(0x00, true);
|
|
if (error) improvError = state;
|
|
char out[11] = {'I','M','P','R','O','V'};
|
|
out[6] = IMPROV_VERSION;
|
|
out[7] = error? ImprovPacketType::Error_State : ImprovPacketType::Current_State;
|
|
out[8] = 1;
|
|
out[9] = state;
|
|
|
|
unsigned checksum = 0;
|
|
for (unsigned i = 0; i < 10; i++) checksum += out[i];
|
|
out[10] = checksum;
|
|
Serial.write((uint8_t*)out, 11);
|
|
Serial.write('\n');
|
|
}
|
|
|
|
// used by sendImprovIPRPCResult(), sendImprovInfoResponse(), and handleImprovWifiScan()
|
|
void sendImprovRPCResult(ImprovRPCType type, uint8_t n_strings, const char **strings) {
|
|
if (improvError > 0 && improvError < 3) sendImprovStateResponse(0x00, true);
|
|
unsigned packetLen = 12;
|
|
char out[256] = {'I','M','P','R','O','V'};
|
|
out[6] = IMPROV_VERSION;
|
|
out[7] = ImprovPacketType::RPC_Response;
|
|
//out[8] = 2; //Length (set below)
|
|
out[9] = type;
|
|
//out[10] = 0; //Data len (set below)
|
|
unsigned pos = 11;
|
|
|
|
for (unsigned s = 0; s < n_strings; s++) {
|
|
size_t len = strlen(strings[s]);
|
|
if (pos + len > 254) continue; // simple buffer overflow guard
|
|
out[pos++] = len;
|
|
strcpy(out + pos, strings[s]);
|
|
pos += len;
|
|
}
|
|
|
|
packetLen = pos +1;
|
|
out[8] = pos -9; // Length of packet (excluding first 9 header bytes and final checksum byte)
|
|
out[10] = pos -11; // Data len
|
|
|
|
unsigned checksum = 0;
|
|
for (unsigned i = 0; i < packetLen -1; i++) checksum += out[i];
|
|
out[packetLen -1] = checksum;
|
|
Serial.write((uint8_t*)out, packetLen);
|
|
Serial.write('\n');
|
|
DIMPROV_PRINT("RPC result checksum");
|
|
DIMPROV_PRINTLN(checksum);
|
|
}
|
|
|
|
void sendImprovIPRPCResult(ImprovRPCType type) {
|
|
if (Network.isConnected())
|
|
{
|
|
char urlStr[64];
|
|
IPAddress localIP = Network.localIP();
|
|
unsigned len = sprintf(urlStr, "http://%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
|
if (len > 24) return; //sprintf fail?
|
|
const char *str[1] = {urlStr};
|
|
sendImprovRPCResult(type, 1, str);
|
|
} else {
|
|
sendImprovRPCResult(type, 0);
|
|
}
|
|
|
|
improvActive = 1; //no longer provisioning
|
|
}
|
|
|
|
void sendImprovInfoResponse() {
|
|
char bString[32];
|
|
#ifdef ESP8266
|
|
strcpy(bString, "esp8266");
|
|
#else // ESP32
|
|
strncpy(bString, ESP.getChipModel(), 31);
|
|
#if CONFIG_IDF_TARGET_ESP32
|
|
bString[5] = '\0'; // disregard chip revision for classic ESP32
|
|
#else
|
|
bString[31] = '\0'; // just in case
|
|
#endif
|
|
strlwr(bString);
|
|
#endif
|
|
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
|
|
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
|
|
char vString[32];
|
|
sprintf_P(vString, PSTR("%s/%i"), versionString, VERSION);
|
|
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
|
|
|
|
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);
|
|
}
|
|
|
|
#ifndef WLED_DISABLE_IMPROV_WIFISCAN
|
|
void startImprovWifiScan() {
|
|
if (improvWifiScanRunning) return;
|
|
WiFi.scanNetworks(true);
|
|
improvWifiScanRunning = true;
|
|
}
|
|
|
|
void handleImprovWifiScan() {
|
|
if (!improvWifiScanRunning) return;
|
|
int16_t status = WiFi.scanComplete();
|
|
if (status == WIFI_SCAN_RUNNING) return;
|
|
// here scan completed or failed (-2)
|
|
improvWifiScanRunning = false;
|
|
|
|
for (int i = 0; i < status; i++) {
|
|
char rssiStr[8];
|
|
sprintf(rssiStr, "%d", WiFi.RSSI(i));
|
|
#ifdef ESP8266
|
|
bool isOpen = WiFi.encryptionType(i) == ENC_TYPE_NONE;
|
|
#else
|
|
bool isOpen = WiFi.encryptionType(i) == WIFI_AUTH_OPEN;
|
|
#endif
|
|
|
|
char ssidStr[33];
|
|
strcpy(ssidStr, WiFi.SSID(i).c_str());
|
|
const char *str[3] = {ssidStr, rssiStr, isOpen ? "NO":"YES"};
|
|
sendImprovRPCResult(ImprovRPCType::Request_Scan, 3, str);
|
|
}
|
|
sendImprovRPCResult(ImprovRPCType::Request_Scan, 0);
|
|
|
|
WiFi.scanDelete();
|
|
}
|
|
#else
|
|
void startImprovWifiScan() {}
|
|
void handleImprovWifiScan() {}
|
|
#endif
|
|
|
|
void parseWiFiCommand(char* rpcData) {
|
|
unsigned len = rpcData[0];
|
|
if (!len || len > 126) return;
|
|
|
|
unsigned ssidLen = rpcData[1];
|
|
if (ssidLen > len -1 || ssidLen > 32) return;
|
|
memset(multiWiFi[0].clientSSID, 0, 32);
|
|
memcpy(multiWiFi[0].clientSSID, rpcData+2, ssidLen);
|
|
|
|
memset(multiWiFi[0].clientPass, 0, 64);
|
|
if (len > ssidLen +1) {
|
|
unsigned passLen = rpcData[2+ssidLen];
|
|
memset(multiWiFi[0].clientPass, 0, 64);
|
|
memcpy(multiWiFi[0].clientPass, rpcData+3+ssidLen, passLen);
|
|
}
|
|
|
|
sendImprovStateResponse(0x03); //provisioning
|
|
improvActive = 2;
|
|
|
|
forceReconnect = true;
|
|
serializeConfigToFS();
|
|
} |