blynk & neopixelring & w7500
Fork of WIZwiki-7500_Blynk by
Embed:
(wiki syntax)
Show/hide line numbers
BlynkProtocol.h
Go to the documentation of this file.
00001 /** 00002 * @file BlynkProtocol.h 00003 * @author Volodymyr Shymanskyy 00004 * @license This project is released under the MIT License (MIT) 00005 * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 00006 * @date Jan 2015 00007 * @brief Blynk protocol implementation 00008 * 00009 */ 00010 00011 #ifndef BlynkProtocol_h 00012 #define BlynkProtocol_h 00013 00014 #include <string.h> 00015 #include <stdlib.h> 00016 #include <Blynk/BlynkDebug.h> 00017 #include <Blynk/BlynkProtocolDefs.h> 00018 #include <Blynk/BlynkApi.h> 00019 #include <utility/BlynkUtility.h> 00020 00021 template <class Transp> 00022 class BlynkProtocol 00023 : public BlynkApi< BlynkProtocol<Transp> > 00024 { 00025 friend class BlynkApi< BlynkProtocol<Transp> >; 00026 public: 00027 enum BlynkState { 00028 CONNECTING, 00029 CONNECTED, 00030 DISCONNECTED, 00031 }; 00032 00033 BlynkProtocol(Transp& transp) 00034 : conn(transp) 00035 , authkey(NULL) 00036 , redir_serv(NULL) 00037 , lastActivityIn(0) 00038 , lastActivityOut(0) 00039 , lastHeartbeat(0) 00040 #ifdef BLYNK_MSG_LIMIT 00041 , deltaCmd(0) 00042 #endif 00043 , msgIdOut(0) 00044 , msgIdOutOverride(0) 00045 , state(CONNECTING) 00046 {} 00047 00048 bool connected () { return state == CONNECTED; } 00049 00050 bool connect(uint32_t timeout = BLYNK_TIMEOUT_MS*3) { 00051 conn.disconnect(); 00052 state = CONNECTING; 00053 millis_time_t started = this->getMillis(); 00054 while ((state != CONNECTED) && 00055 (this->getMillis() - started < timeout)) 00056 { 00057 run(); 00058 } 00059 run(); // Workaround for #325: Getting wrong bytes with ESP8266-SSL 00060 return state == CONNECTED; 00061 } 00062 00063 void disconnect() { 00064 conn.disconnect(); 00065 state = DISCONNECTED; 00066 BLYNK_LOG1(BLYNK_F("Disconnected")); 00067 } 00068 00069 bool run(bool avail = false); 00070 00071 // TODO: Fixme 00072 void startSession() { 00073 conn.connect(); 00074 state = CONNECTING; 00075 #ifdef BLYNK_MSG_LIMIT 00076 deltaCmd = 1000; 00077 #endif 00078 msgIdOut = 0; 00079 lastHeartbeat = lastActivityIn = lastActivityOut = this->getMillis(); // TODO: - 5005UL 00080 } 00081 00082 void sendCmd(uint8_t cmd, uint16_t id = 0, const void* data = NULL, size_t length = 0, const void* data2 = NULL, size_t length2 = 0); 00083 00084 private: 00085 00086 void internalReconnect() { 00087 state = CONNECTING; 00088 conn.disconnect(); 00089 BlynkOnDisconnected(); 00090 } 00091 00092 int readHeader(BlynkHeader& hdr); 00093 uint16_t getNextMsgId(); 00094 00095 protected: 00096 void begin(const char* auth) { 00097 printf("blink beginnnnnnnnning\r\n"); 00098 this->authkey = auth; 00099 00100 #if defined(BLYNK_NO_FANCY_LOGO) 00101 BLYNK_LOG1(BLYNK_F("Blynk v" BLYNK_VERSION " on " BLYNK_INFO_DEVICE)); 00102 #elif defined(BLYNK_FANCY_LOGO_3D) 00103 BLYNK_LOG1(BLYNK_F("\n" 00104 " ____ ___ __\n" 00105 " /\\ _`\\ /\\_ \\ /\\ \\ _\n" 00106 " \\ \\ \\_\\ \\\\//\\ \\ __ __ ___ \\ \\ \\/ \\\n" 00107 " \\ \\ _ < \\ \\ \\ /\\ \\/\\ \\ /' _ `\\\\ \\ , <\n" 00108 " \\ \\ \\_\\ \\ \\_\\ \\_\\ \\ \\_\\ \\ /\\ \\/\\ \\\\ \\ \\\\`\\\n" 00109 " \\ \\____/ /\\____\\\\/`____ \\\\ \\_\\ \\_\\\\ \\_\\\\_\\\n" 00110 " \\/___/ \\/____/ `/___/\\ \\\\/_/\\/_/ \\/_//_/\n" 00111 " /\\___/\n" 00112 " \\/__/ " BLYNK_VERSION " on " BLYNK_INFO_DEVICE "\n" 00113 )); 00114 #else 00115 BLYNK_LOG1(BLYNK_F("\n" 00116 " ___ __ __\n" 00117 " / _ )/ /_ _____ / /__\n" 00118 " / _ / / // / _ \\/ '_/\n" 00119 " /____/_/\\_, /_//_/_/\\_\\\n" 00120 " /___/ v" BLYNK_VERSION " on " BLYNK_INFO_DEVICE "\n" 00121 )); 00122 #endif 00123 00124 #ifdef BLYNK_DEBUG 00125 if (size_t ram = BlynkFreeRam()) { 00126 BLYNK_LOG2(BLYNK_F("Free RAM: "), ram); 00127 } 00128 #endif 00129 } 00130 bool processInput(void); 00131 00132 Transp& conn; 00133 00134 private: 00135 const char* authkey; 00136 char* redir_serv; 00137 millis_time_t lastActivityIn; 00138 millis_time_t lastActivityOut; 00139 union { 00140 millis_time_t lastHeartbeat; 00141 millis_time_t lastLogin; 00142 }; 00143 #ifdef BLYNK_MSG_LIMIT 00144 millis_time_t deltaCmd; 00145 #endif 00146 uint16_t msgIdOut; 00147 uint16_t msgIdOutOverride; 00148 protected: 00149 BlynkState state; 00150 }; 00151 00152 template <class Transp> 00153 bool BlynkProtocol<Transp>::run(bool avail) 00154 { 00155 BLYNK_RUN_YIELD(); 00156 //printf("debuging...............1\r\n"); 00157 if (state == DISCONNECTED) { 00158 return false; 00159 } 00160 00161 const bool tconn = conn.connected(); 00162 //printf("debuging...............2\r\n"); 00163 if (tconn) { 00164 if (avail || conn.available() > 0) { 00165 //printf("debuging...............3\r\n"); 00166 //BLYNK_LOG2(BLYNK_F("Available: "), conn.available()); 00167 //const unsigned long t = micros(); 00168 if (!processInput()) { 00169 // printf("debuging...............4\r\n"); 00170 conn.disconnect(); 00171 // TODO: Only when in direct mode? 00172 #ifdef BLYNK_USE_DIRECT_CONNECT 00173 state = CONNECTING; 00174 #endif 00175 BlynkOnDisconnected(); 00176 return false; 00177 } 00178 //printf("debuging...............5r\n"); 00179 //BLYNK_LOG2(BLYNK_F("Proc time: "), micros() - t); 00180 } 00181 } 00182 00183 const millis_time_t t = this->getMillis(); 00184 00185 if (state == CONNECTED) { 00186 if (!tconn) { 00187 lastHeartbeat = t; 00188 internalReconnect(); 00189 return false; 00190 } 00191 00192 if (t - lastActivityIn > (1000UL * BLYNK_HEARTBEAT + BLYNK_TIMEOUT_MS*3)) { 00193 #ifdef BLYNK_DEBUG 00194 BLYNK_LOG6(BLYNK_F("Heartbeat timeout: "), t, BLYNK_F(", "), lastActivityIn, BLYNK_F(", "), lastHeartbeat); 00195 #else 00196 BLYNK_LOG1(BLYNK_F("Heartbeat timeout")); 00197 #endif 00198 internalReconnect(); 00199 return false; 00200 } else if ((t - lastActivityIn > 1000UL * BLYNK_HEARTBEAT || 00201 t - lastActivityOut > 1000UL * BLYNK_HEARTBEAT) && 00202 t - lastHeartbeat > BLYNK_TIMEOUT_MS) 00203 { 00204 // Send ping if we didn't either send or receive something 00205 // for BLYNK_HEARTBEAT seconds 00206 sendCmd(BLYNK_CMD_PING); 00207 lastHeartbeat = t; 00208 } 00209 } else if (state == CONNECTING) { 00210 #ifdef BLYNK_USE_DIRECT_CONNECT 00211 if (!tconn) 00212 conn.connect(); 00213 #else 00214 if (tconn && (t - lastLogin > BLYNK_TIMEOUT_MS)) { 00215 BLYNK_LOG1(BLYNK_F("Login timeout")); 00216 conn.disconnect(); 00217 state = CONNECTING; 00218 return false; 00219 } else if (!tconn && (t - lastLogin > 5000UL)) { 00220 conn.disconnect(); 00221 if (!conn.connect()) { 00222 lastLogin = t; 00223 return false; 00224 } 00225 00226 #ifdef BLYNK_MSG_LIMIT 00227 deltaCmd = 1000; 00228 #endif 00229 msgIdOut = 1; 00230 sendCmd(BLYNK_CMD_LOGIN, 1, authkey, strlen(authkey)); 00231 lastLogin = lastActivityOut; 00232 return true; 00233 } 00234 #endif 00235 } 00236 return true; 00237 } 00238 00239 template <class Transp> 00240 BLYNK_FORCE_INLINE 00241 bool BlynkProtocol<Transp>::processInput(void) 00242 { 00243 BlynkHeader hdr; 00244 const int ret = readHeader(hdr); 00245 00246 if (ret == 0) { 00247 return true; // Considered OK (no data on input) 00248 } 00249 00250 if (ret < 0 || hdr.msg_id == 0) { 00251 #ifdef BLYNK_DEBUG 00252 BLYNK_LOG2(BLYNK_F("Bad hdr len: "), ret); 00253 #endif 00254 return false; 00255 } 00256 00257 if (hdr.type == BLYNK_CMD_RESPONSE) { 00258 lastActivityIn = this->getMillis(); 00259 00260 #ifndef BLYNK_USE_DIRECT_CONNECT 00261 if (state == CONNECTING && (1 == hdr.msg_id)) { 00262 switch (hdr.length) { 00263 case BLYNK_SUCCESS: 00264 case BLYNK_ALREADY_REGISTERED: 00265 BLYNK_LOG3(BLYNK_F("Ready (ping: "), lastActivityIn-lastHeartbeat, BLYNK_F("ms).")); 00266 lastHeartbeat = lastActivityIn; 00267 state = CONNECTED; 00268 this->sendInfo(); 00269 BLYNK_RUN_YIELD(); 00270 BlynkOnConnected(); 00271 return true; 00272 case BLYNK_INVALID_TOKEN: 00273 BLYNK_LOG1(BLYNK_F("Invalid auth token")); 00274 break; 00275 default: 00276 BLYNK_LOG2(BLYNK_F("Connect failed. code: "), hdr.length); 00277 } 00278 return false; 00279 } 00280 if (BLYNK_NOT_AUTHENTICATED == hdr.length) { 00281 return false; 00282 } 00283 #endif 00284 // TODO: return code may indicate App presence 00285 return true; 00286 } 00287 00288 if (hdr.length > BLYNK_MAX_READBYTES) { 00289 #ifdef BLYNK_DEBUG 00290 BLYNK_LOG2(BLYNK_F("Packet too big: "), hdr.length); 00291 #endif 00292 // TODO: Flush 00293 conn.connect(); 00294 return true; 00295 } 00296 00297 uint8_t inputBuffer[hdr.length+1]; // Add 1 to zero-terminate 00298 if (hdr.length != conn.read(inputBuffer, hdr.length)) { 00299 #ifdef DEBUG 00300 BLYNK_LOG1(BLYNK_F("Can't read body")); 00301 #endif 00302 return false; 00303 } 00304 inputBuffer[hdr.length] = '\0'; 00305 00306 BLYNK_DBG_DUMP(">", inputBuffer, hdr.length); 00307 00308 lastActivityIn = this->getMillis(); 00309 00310 switch (hdr.type) 00311 { 00312 case BLYNK_CMD_LOGIN: { 00313 #ifdef BLYNK_USE_DIRECT_CONNECT 00314 if (strncmp(authkey, (char*)inputBuffer, 32)) { 00315 BLYNK_LOG1(BLYNK_F("Invalid token")); 00316 sendCmd(BLYNK_CMD_RESPONSE, hdr.msg_id, NULL, BLYNK_INVALID_TOKEN); 00317 break; 00318 } 00319 #endif 00320 if (state == CONNECTING) { 00321 BLYNK_LOG1(BLYNK_F("Ready")); 00322 state = CONNECTED; 00323 this->sendInfo(); 00324 BlynkOnConnected(); 00325 } 00326 sendCmd(BLYNK_CMD_RESPONSE, hdr.msg_id, NULL, BLYNK_SUCCESS); 00327 } break; 00328 case BLYNK_CMD_PING: { 00329 sendCmd(BLYNK_CMD_RESPONSE, hdr.msg_id, NULL, BLYNK_SUCCESS); 00330 } break; 00331 case BLYNK_CMD_REDIRECT: { 00332 if (!redir_serv) { 00333 redir_serv = (char*)malloc(32); 00334 } 00335 BlynkParam param(inputBuffer, hdr.length); 00336 uint16_t redir_port = BLYNK_DEFAULT_PORT; // TODO: Fixit 00337 00338 BlynkParam::iterator it = param.begin(); 00339 if (it >= param.end()) 00340 return false; 00341 strncpy(redir_serv, it.asStr(), 32); 00342 if (++it < param.end()) 00343 redir_port = it.asLong(); 00344 BLYNK_LOG4(BLYNK_F("Redirecting to "), redir_serv, ':', redir_port); 00345 conn.disconnect(); 00346 conn.begin(redir_serv, redir_port); 00347 lastLogin = lastActivityIn - 5000L; // Reconnect immediately 00348 state = CONNECTING; 00349 } break; 00350 case BLYNK_CMD_HARDWARE: 00351 case BLYNK_CMD_BRIDGE: { 00352 msgIdOutOverride = hdr.msg_id; 00353 this->processCmd(inputBuffer, hdr.length); 00354 msgIdOutOverride = 0; 00355 } break; 00356 case BLYNK_CMD_INTERNAL: { 00357 BlynkReq req = { 0 }; 00358 BlynkParam param(inputBuffer, hdr.length); 00359 BlynkParam::iterator it = param.begin(); 00360 if (it >= param.end()) 00361 return true; 00362 00363 uint32_t cmd32; 00364 memcpy(&cmd32, it.asStr(), sizeof(cmd32)); 00365 00366 ++it; 00367 char* start = (char*)(it).asStr(); 00368 unsigned length = hdr.length - (start - (char*)inputBuffer); 00369 BlynkParam param2(start, length); 00370 00371 switch (cmd32) { 00372 case BLYNK_INT_RTC: BlynkWidgetWriteInternalPinRTC(req, param2); break; 00373 case BLYNK_INT_OTA: BlynkWidgetWriteInternalPinOTA(req, param2); break; 00374 case BLYNK_INT_ACON: BlynkWidgetWriteInternalPinACON(req, param2); break; 00375 case BLYNK_INT_ADIS: BlynkWidgetWriteInternalPinADIS(req, param2); break; 00376 #ifdef DEBUG 00377 default: BLYNK_LOG2(BLYNK_F("Invalid internal cmd:"), param.asStr()); 00378 #endif 00379 } 00380 } break; 00381 case BLYNK_CMD_DEBUG_PRINT: { 00382 if (hdr.length) { 00383 BLYNK_LOG2(BLYNK_F("Server: "), (char*)inputBuffer); 00384 } 00385 } break; 00386 default: { 00387 #ifdef BLYNK_DEBUG 00388 BLYNK_LOG2(BLYNK_F("Invalid header type: "), hdr.type); 00389 #endif 00390 // TODO: Flush 00391 conn.connect(); 00392 } break; 00393 } 00394 00395 return true; 00396 } 00397 00398 template <class Transp> 00399 int BlynkProtocol<Transp>::readHeader(BlynkHeader& hdr) 00400 { 00401 size_t rlen = conn.read(&hdr, sizeof(hdr)); 00402 if (rlen == 0) { 00403 return 0; 00404 } 00405 00406 if (sizeof(hdr) != rlen) { 00407 return -1; 00408 } 00409 00410 BLYNK_DBG_DUMP(">", &hdr, sizeof(BlynkHeader)); 00411 00412 hdr.msg_id = ntohs(hdr.msg_id); 00413 hdr.length = ntohs(hdr.length); 00414 00415 return rlen; 00416 } 00417 00418 #ifndef BLYNK_SEND_THROTTLE 00419 #define BLYNK_SEND_THROTTLE 0 00420 #endif 00421 00422 #ifndef BLYNK_SEND_CHUNK 00423 #define BLYNK_SEND_CHUNK 1024 // Just a big number 00424 #endif 00425 00426 template <class Transp> 00427 void BlynkProtocol<Transp>::sendCmd(uint8_t cmd, uint16_t id, const void* data, size_t length, const void* data2, size_t length2) 00428 { 00429 if (0 == id) { 00430 id = getNextMsgId(); 00431 } 00432 00433 if (!conn.connected() || (cmd != BLYNK_CMD_RESPONSE && cmd != BLYNK_CMD_PING && cmd != BLYNK_CMD_LOGIN && state != CONNECTED) ) { 00434 #ifdef BLYNK_DEBUG 00435 BLYNK_LOG2(BLYNK_F("Cmd skipped:"), cmd); 00436 #endif 00437 return; 00438 } 00439 00440 const size_t full_length = (sizeof(BlynkHeader)) + 00441 (data ? length : 0) + 00442 (data2 ? length2 : 0); 00443 00444 #if defined(BLYNK_SEND_ATOMIC) || defined(ESP8266) || defined(SPARK) || defined(PARTICLE) || defined(ENERGIA) 00445 // Those have more RAM and like single write at a time... 00446 00447 uint8_t buff[full_length]; 00448 00449 BlynkHeader* hdr = (BlynkHeader*)buff; 00450 hdr->type = cmd; 00451 hdr->msg_id = htons(id); 00452 hdr->length = htons(length+length2); 00453 00454 size_t pos = sizeof(BlynkHeader); 00455 if (data && length) { 00456 memcpy(buff + pos, data, length); 00457 pos += length; 00458 } 00459 if (data2 && length2) { 00460 memcpy(buff + pos, data2, length2); 00461 } 00462 00463 size_t wlen = 0; 00464 while (wlen < full_length) { 00465 const size_t chunk = BlynkMin(size_t(BLYNK_SEND_CHUNK), full_length - wlen); 00466 BLYNK_DBG_DUMP("<", buff + wlen, chunk); 00467 const size_t w = conn.write(buff + wlen, chunk); 00468 ::delay(BLYNK_SEND_THROTTLE); 00469 if (w == 0) { 00470 #ifdef BLYNK_DEBUG 00471 BLYNK_LOG1(BLYNK_F("Cmd error")); 00472 #endif 00473 conn.disconnect(); 00474 state = CONNECTING; 00475 //BlynkOnDisconnected(); 00476 return; 00477 } 00478 wlen += w; 00479 } 00480 00481 #else 00482 00483 BlynkHeader hdr; 00484 hdr.type = cmd; 00485 hdr.msg_id = htons(id); 00486 hdr.length = htons(length+length2); 00487 00488 BLYNK_DBG_DUMP("<", &hdr, sizeof(hdr)); 00489 size_t wlen = conn.write(&hdr, sizeof(hdr)); 00490 ::delay(BLYNK_SEND_THROTTLE); 00491 00492 if (cmd != BLYNK_CMD_RESPONSE) { 00493 if (length) { 00494 BLYNK_DBG_DUMP("<", data, length); 00495 wlen += conn.write(data, length); 00496 ::delay(BLYNK_SEND_THROTTLE); 00497 } 00498 if (length2) { 00499 BLYNK_DBG_DUMP("<", data2, length2); 00500 wlen += conn.write(data2, length2); 00501 ::delay(BLYNK_SEND_THROTTLE); 00502 } 00503 } 00504 00505 #endif 00506 00507 if (wlen != full_length) { 00508 #ifdef BLYNK_DEBUG 00509 BLYNK_LOG4(BLYNK_F("Sent "), wlen, '/', full_length); 00510 #endif 00511 internalReconnect(); 00512 return; 00513 } 00514 00515 const millis_time_t ts = this->getMillis(); 00516 #if defined BLYNK_MSG_LIMIT && BLYNK_MSG_LIMIT > 0 00517 BlynkAverageSample<32>(deltaCmd, ts - lastActivityOut); 00518 //BLYNK_LOG2(BLYNK_F("Delta: "), deltaCmd); 00519 if (deltaCmd < (1000/BLYNK_MSG_LIMIT)) { 00520 BLYNK_LOG_TROUBLE(BLYNK_F("flood-error")); 00521 internalReconnect(); 00522 } 00523 #endif 00524 lastActivityOut = ts; 00525 00526 } 00527 00528 template <class Transp> 00529 uint16_t BlynkProtocol<Transp>::getNextMsgId() 00530 { 00531 if (msgIdOutOverride != 0) 00532 return msgIdOutOverride; 00533 if (++msgIdOut == 0) 00534 msgIdOut = 1; 00535 return msgIdOut; 00536 } 00537 00538 #endif
Generated on Thu Jul 14 2022 00:21:50 by 1.7.2