Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of WIZwiki-7500_Blynk by
BlynkProtocol.h
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
