1+ #include " wled.h"
2+
3+ #ifdef WLED_DEBUG_IMPROV
4+ #define DIMPROV_PRINT (x ) Serial.print(x)
5+ #define DIMPROV_PRINTLN (x ) Serial.println(x)
6+ #define DIMPROV_PRINTF (x... ) Serial.printf(x)
7+ #else
8+ #define DIMPROV_PRINT (x )
9+ #define DIMPROV_PRINTLN (x )
10+ #define DIMPROV_PRINTF (x... )
11+ #endif
12+
13+ #define IMPROV_VERSION 1
14+
15+ void parseWiFiCommand (char *rpcData);
16+
17+ enum ImprovPacketType {
18+ Current_State = 0x01 ,
19+ Error_State = 0x02 ,
20+ RPC_Command = 0x03 ,
21+ RPC_Response = 0x04
22+ };
23+
24+ enum ImprovPacketByte {
25+ Version = 6 ,
26+ PacketType = 7 ,
27+ Length = 8 ,
28+ RPC_CommandType = 9
29+ };
30+
31+ enum ImprovRPCType {
32+ Command_Wifi = 0x01 ,
33+ Request_State = 0x02 ,
34+ Request_Info = 0x03
35+ };
36+
37+ // File dbgf;
38+
39+ // blocking function to parse an Improv Serial packet
40+ void handleImprovPacket () {
41+ uint8_t header[6 ] = {' I' ,' M' ,' P' ,' R' ,' O' ,' V' };
42+
43+ // dbgf = WLED_FS.open("/improv.log","a");
44+
45+ bool timeout = false ;
46+ uint8_t waitTime = 25 ;
47+ uint16_t packetByte = 0 ;
48+ uint8_t packetLen = 9 ;
49+ uint8_t checksum = 0 ;
50+
51+ uint8_t rpcCommandType = 0 ;
52+ char rpcData[128 ];
53+ rpcData[0 ] = 0 ;
54+
55+ while (!timeout) {
56+ if (Serial.available () < 1 ) {
57+ delay (1 );
58+ waitTime--;
59+ if (!waitTime) timeout = true ;
60+ continue ;
61+ }
62+ byte next = Serial.read ();
63+
64+ DIMPROV_PRINT (" Received improv byte: " ); DIMPROV_PRINTF (" %x\r\n " ,next);
65+ // f.write(next);
66+ switch (packetByte) {
67+ case ImprovPacketByte::Version: {
68+ if (next != IMPROV_VERSION) {
69+ DIMPROV_PRINTLN (F (" Invalid version" ));
70+ // dbgf.close();
71+ return ;
72+ }
73+ break ;
74+ }
75+ case ImprovPacketByte::PacketType: {
76+ if (next != ImprovPacketType::RPC_Command) {
77+ DIMPROV_PRINTF (" Non RPC-command improv packet type %i\n " ,next);
78+ // dbgf.close();
79+ return ;
80+ }
81+ if (!improvActive) improvActive = 1 ;
82+ break ;
83+ }
84+ case ImprovPacketByte::Length: packetLen = 9 + next; break ;
85+ case ImprovPacketByte::RPC_CommandType: rpcCommandType = next; break ;
86+ default : {
87+ if (packetByte >= packetLen) { // end of packet, check checksum match
88+
89+ if (checksum != next) {
90+ DIMPROV_PRINTF (" Got RPC checksum %i, expected %i" ,next,checksum);
91+ sendImprovStateResponse (0x01 , true );
92+ // dbgf.close();
93+ return ;
94+ }
95+
96+ switch (rpcCommandType) {
97+ case ImprovRPCType::Command_Wifi: parseWiFiCommand (rpcData); break ;
98+ case ImprovRPCType::Request_State: {
99+ uint8_t improvState = 0x02 ; // authorized
100+ if (WLED_WIFI_CONFIGURED) improvState = 0x03 ; // provisioning
101+ if (Network.isConnected ()) improvState = 0x04 ; // provisioned
102+ sendImprovStateResponse (improvState, false );
103+ if (improvState == 0x04 ) sendImprovRPCResponse (ImprovRPCType::Request_State);
104+ break ;
105+ }
106+ case ImprovRPCType::Request_Info: sendImprovInfoResponse (); break ;
107+ default : {
108+ DIMPROV_PRINTF (" Unknown RPC command %i\n " ,next);
109+ sendImprovStateResponse (0x02 , true );
110+ }
111+ }
112+ // dbgf.close();
113+ return ;
114+ }
115+ if (packetByte < 6 ) { // check header
116+ if (next != header[packetByte]) {
117+ DIMPROV_PRINTLN (F (" Invalid improv header" ));
118+ // dbgf.close();
119+ return ;
120+ }
121+ } else if (packetByte > 9 ) { // RPC data
122+ rpcData[packetByte - 10 ] = next;
123+ if (packetByte > 137 ) return ; // prevent buffer overflow
124+ }
125+ }
126+ }
127+
128+ checksum += next;
129+ packetByte++;
130+ }
131+ // dbgf.close();
132+ }
133+
134+ void sendImprovStateResponse (uint8_t state, bool error) {
135+ if (!error && improvError > 0 && improvError < 3 ) sendImprovStateResponse (0x00 , true );
136+ if (error) improvError = state;
137+ char out[11 ] = {' I' ,' M' ,' P' ,' R' ,' O' ,' V' };
138+ out[6 ] = IMPROV_VERSION;
139+ out[7 ] = error? ImprovPacketType::Error_State : ImprovPacketType::Current_State;
140+ out[8 ] = 1 ;
141+ out[9 ] = state;
142+
143+ uint8_t checksum = 0 ;
144+ for (uint8_t i = 0 ; i < 10 ; i++) checksum += out[i];
145+ out[10 ] = checksum;
146+ Serial.write ((uint8_t *)out, 11 );
147+ Serial.write (' \n ' );
148+ }
149+
150+ void sendImprovRPCResponse (byte commandId) {
151+ if (improvError > 0 && improvError < 3 ) sendImprovStateResponse (0x00 , true );
152+ uint8_t packetLen = 12 ;
153+ char out[64 ] = {' I' ,' M' ,' P' ,' R' ,' O' ,' V' };
154+ out[6 ] = IMPROV_VERSION;
155+ out[7 ] = ImprovPacketType::RPC_Response;
156+ out[8 ] = 2 ; // Length (set below)
157+ out[9 ] = commandId;
158+ out[10 ] = 0 ; // Data len (set below)
159+ out[11 ] = ' \0 ' ; // URL len (set below)
160+
161+ if (Network.isConnected ())
162+ {
163+ IPAddress localIP = Network.localIP ();
164+ uint8_t len = sprintf (out+12 , " http://%d.%d.%d.%d" , localIP[0 ], localIP[1 ], localIP[2 ], localIP[3 ]);
165+ if (len > 24 ) return ; // sprintf fail?
166+ out[11 ] = len;
167+ out[10 ] = 1 + len;
168+ out[8 ] = 3 + len; // RPC command type + data len + url len + url
169+ packetLen = 13 + len;
170+ }
171+
172+ uint8_t checksum = 0 ;
173+ for (uint8_t i = 0 ; i < packetLen -1 ; i++) checksum += out[i];
174+ out[packetLen -1 ] = checksum;
175+ Serial.write ((uint8_t *)out, packetLen);
176+ Serial.write (' \n ' );
177+ improvActive = 1 ; // no longer provisioning
178+ }
179+
180+ void sendImprovInfoResponse () {
181+ if (improvError > 0 && improvError < 3 ) sendImprovStateResponse (0x00 , true );
182+ uint8_t packetLen = 12 ;
183+ char out[128 ] = {' I' ,' M' ,' P' ,' R' ,' O' ,' V' };
184+ out[6 ] = IMPROV_VERSION;
185+ out[7 ] = ImprovPacketType::RPC_Response;
186+ // out[8] = 2; //Length (set below)
187+ out[9 ] = ImprovRPCType::Request_Info;
188+ // out[10] = 0; //Data len (set below)
189+ out[11 ] = 4 ; // Firmware len ("WLED")
190+ out[12 ] = ' W' ; out[13 ] = ' L' ; out[14 ] = ' E' ; out[15 ] = ' D' ;
191+ uint8_t lengthSum = 17 ;
192+ uint8_t vlen = sprintf_P (out+lengthSum,PSTR (" 0.13.0-b4/%i" ),VERSION);
193+ out[16 ] = vlen; lengthSum += vlen;
194+ uint8_t hlen = 7 ;
195+ #ifdef ESP8266
196+ strcpy (out+lengthSum+1 ," esp8266" );
197+ #else
198+ hlen = 5 ;
199+ strcpy (out+lengthSum+1 ," esp32" );
200+ #endif
201+ out[lengthSum] = hlen;
202+ lengthSum += hlen + 1 ;
203+ // Use serverDescription if it has been changed from the default "WLED", else mDNS name
204+ bool useMdnsName = (strcmp (serverDescription, " WLED" ) == 0 && strlen (cmDNS) > 0 );
205+ strcpy (out+lengthSum+1 ,useMdnsName ? cmDNS : serverDescription);
206+ uint8_t nlen = strlen (useMdnsName ? cmDNS : serverDescription);
207+ out[lengthSum] = nlen;
208+ lengthSum += nlen + 1 ;
209+
210+ packetLen = lengthSum +1 ;
211+ out[8 ] = lengthSum -9 ;
212+ out[10 ] = lengthSum -11 ;
213+
214+ uint8_t checksum = 0 ;
215+ for (uint8_t i = 0 ; i < packetLen -1 ; i++) checksum += out[i];
216+ out[packetLen -1 ] = checksum;
217+ Serial.write ((uint8_t *)out, packetLen);
218+ Serial.write (' \n ' );
219+ DIMPROV_PRINT (" Info checksum" );
220+ DIMPROV_PRINTLN (checksum);
221+ }
222+
223+ void parseWiFiCommand (char * rpcData) {
224+ uint8_t len = rpcData[0 ];
225+ if (!len || len > 126 ) return ;
226+
227+ uint8_t ssidLen = rpcData[1 ];
228+ if (ssidLen > len -1 || ssidLen > 32 ) return ;
229+ memset (clientSSID, 0 , 32 );
230+ memcpy (clientSSID, rpcData+2 , ssidLen);
231+
232+ memset (clientPass, 0 , 64 );
233+ if (len > ssidLen +1 ) {
234+ uint8_t passLen = rpcData[2 +ssidLen];
235+ memset (clientPass, 0 , 64 );
236+ memcpy (clientPass, rpcData+3 +ssidLen, passLen);
237+ }
238+
239+ sendImprovStateResponse (0x03 ); // provisioning
240+ improvActive = 2 ;
241+
242+ forceReconnect = true ;
243+ serializeConfig ();
244+ }
0 commit comments