Blynk library for embedded hardware. Works with Arduino, ESP8266, Raspberry Pi, Intel Edison/Galileo, LinkIt ONE, Particle Core/Photon, Energia, ARM mbed, etc. http://www.blynk.cc/
Dependents: Blynk_RBL_BLE_Nano Blynk_MicroBit Blynk_Serial Blynk_RBL_BLE_Nano
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 , msgIdOut(0) 00041 , msgIdOutOverride(0) 00042 , nesting(0) 00043 , state(CONNECTING) 00044 {} 00045 00046 bool connected () { return state == CONNECTED; } 00047 00048 bool connect(uint32_t timeout = BLYNK_TIMEOUT_MS*3) { 00049 conn.disconnect(); 00050 state = CONNECTING; 00051 millis_time_t started = BlynkMillis(); 00052 while ((state != CONNECTED) && 00053 (BlynkMillis() - started < timeout)) 00054 { 00055 run(); 00056 } 00057 return state == CONNECTED; 00058 } 00059 00060 void disconnect() { 00061 conn.disconnect(); 00062 state = DISCONNECTED; 00063 BLYNK_LOG1(BLYNK_F("Disconnected")); 00064 } 00065 00066 bool run(bool avail = false); 00067 00068 // TODO: Fixme 00069 void startSession() { 00070 conn.connect(); 00071 state = CONNECTING; 00072 msgIdOut = 0; 00073 lastHeartbeat = lastActivityIn = lastActivityOut = (BlynkMillis() - 5000UL); 00074 } 00075 00076 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); 00077 00078 private: 00079 00080 void internalReconnect() { 00081 state = CONNECTING; 00082 conn.disconnect(); 00083 BlynkOnDisconnected(); 00084 } 00085 00086 int readHeader(BlynkHeader& hdr); 00087 uint16_t getNextMsgId(); 00088 00089 protected: 00090 void begin(const char* auth) { 00091 this->authkey = auth; 00092 lastHeartbeat = lastActivityIn = lastActivityOut = (BlynkMillis() - 5000UL); 00093 00094 #if defined(BLYNK_NO_FANCY_LOGO) 00095 BLYNK_LOG1(BLYNK_F("Blynk v" BLYNK_VERSION " on " BLYNK_INFO_DEVICE)); 00096 #elif defined(BLYNK_FANCY_LOGO_3D) 00097 BLYNK_LOG1(BLYNK_F("\n" 00098 " ____ ___ __\n" 00099 " /\\ _`\\ /\\_ \\ /\\ \\ _\n" 00100 " \\ \\ \\_\\ \\\\//\\ \\ __ __ ___ \\ \\ \\/ \\\n" 00101 " \\ \\ _ < \\ \\ \\ /\\ \\/\\ \\ /' _ `\\\\ \\ , <\n" 00102 " \\ \\ \\_\\ \\ \\_\\ \\_\\ \\ \\_\\ \\ /\\ \\/\\ \\\\ \\ \\\\`\\\n" 00103 " \\ \\____/ /\\____\\\\/`____ \\\\ \\_\\ \\_\\\\ \\_\\\\_\\\n" 00104 " \\/___/ \\/____/ `/___/\\ \\\\/_/\\/_/ \\/_//_/\n" 00105 " /\\___/\n" 00106 " \\/__/ " BLYNK_VERSION " on " BLYNK_INFO_DEVICE "\n" 00107 )); 00108 #else 00109 BLYNK_LOG1(BLYNK_F("\n" 00110 " ___ __ __\n" 00111 " / _ )/ /_ _____ / /__\n" 00112 " / _ / / // / _ \\/ '_/\n" 00113 " /____/_/\\_, /_//_/_/\\_\\\n" 00114 " /___/ v" BLYNK_VERSION " on " BLYNK_INFO_DEVICE "\n" 00115 )); 00116 #endif 00117 } 00118 bool processInput(void); 00119 00120 Transp& conn; 00121 00122 private: 00123 const char* authkey; 00124 char* redir_serv; 00125 millis_time_t lastActivityIn; 00126 millis_time_t lastActivityOut; 00127 union { 00128 millis_time_t lastHeartbeat; 00129 millis_time_t lastLogin; 00130 }; 00131 uint16_t msgIdOut; 00132 uint16_t msgIdOutOverride; 00133 uint8_t nesting; 00134 protected: 00135 BlynkState state; 00136 }; 00137 00138 template <class Transp> 00139 bool BlynkProtocol<Transp>::run(bool avail) 00140 { 00141 BLYNK_RUN_YIELD(); 00142 00143 if (state == DISCONNECTED) { 00144 return false; 00145 } 00146 00147 // Detect nesting 00148 BlynkHelperAutoInc guard(nesting); 00149 if (msgIdOutOverride || nesting > 2) { 00150 #ifdef BLYNK_DEBUG_ALL 00151 BLYNK_LOG1(BLYNK_F("Nested run() skipped")); 00152 #endif 00153 return true; 00154 } 00155 00156 if (conn.connected()) { 00157 while (avail || conn.available() > 0) { 00158 //BLYNK_LOG2(BLYNK_F("Available: "), conn.available()); 00159 //const unsigned long t = micros(); 00160 if (!processInput()) { 00161 conn.disconnect(); 00162 // TODO: Only when in direct mode? 00163 #ifdef BLYNK_USE_DIRECT_CONNECT 00164 state = CONNECTING; 00165 #endif 00166 BlynkOnDisconnected(); 00167 return false; 00168 } 00169 avail = false; 00170 //BLYNK_LOG2(BLYNK_F("Proc time: "), micros() - t); 00171 } 00172 } 00173 00174 const millis_time_t t = BlynkMillis(); 00175 00176 // Update connection status after running commands 00177 const bool tconn = conn.connected(); 00178 00179 if (state == CONNECTED) { 00180 if (!tconn) { 00181 lastHeartbeat = t; 00182 internalReconnect(); 00183 return false; 00184 } 00185 00186 if (t - lastActivityIn > (1000UL * BLYNK_HEARTBEAT + BLYNK_TIMEOUT_MS*3)) { 00187 #ifdef BLYNK_DEBUG 00188 BLYNK_LOG6(BLYNK_F("Heartbeat timeout: "), t, BLYNK_F(", "), lastActivityIn, BLYNK_F(", "), lastHeartbeat); 00189 #else 00190 BLYNK_LOG1(BLYNK_F("Heartbeat timeout")); 00191 #endif 00192 internalReconnect(); 00193 return false; 00194 } else if ((t - lastActivityIn > 1000UL * BLYNK_HEARTBEAT || 00195 t - lastActivityOut > 1000UL * BLYNK_HEARTBEAT) && 00196 t - lastHeartbeat > BLYNK_TIMEOUT_MS) 00197 { 00198 // Send ping if we didn't either send or receive something 00199 // for BLYNK_HEARTBEAT seconds 00200 sendCmd(BLYNK_CMD_PING); 00201 lastHeartbeat = t; 00202 } 00203 } else if (state == CONNECTING) { 00204 #ifdef BLYNK_USE_DIRECT_CONNECT 00205 if (!tconn) 00206 conn.connect(); 00207 #else 00208 if (tconn && (t - lastLogin > BLYNK_TIMEOUT_MS)) { 00209 BLYNK_LOG1(BLYNK_F("Login timeout")); 00210 conn.disconnect(); 00211 state = CONNECTING; 00212 return false; 00213 } else if (!tconn && (t - lastLogin > 5000UL)) { 00214 conn.disconnect(); 00215 if (!conn.connect()) { 00216 lastLogin = t; 00217 return false; 00218 } 00219 00220 msgIdOut = 1; 00221 sendCmd(BLYNK_CMD_LOGIN, 1, authkey, strlen(authkey)); 00222 lastLogin = lastActivityOut; 00223 return true; 00224 } 00225 #endif 00226 } 00227 return true; 00228 } 00229 00230 template <class Transp> 00231 BLYNK_FORCE_INLINE 00232 bool BlynkProtocol<Transp>::processInput(void) 00233 { 00234 BlynkHeader hdr; 00235 const int ret = readHeader(hdr); 00236 00237 if (ret == 0) { 00238 return true; // Considered OK (no data on input) 00239 } 00240 00241 if (ret < 0 || hdr.msg_id == 0) { 00242 #ifdef BLYNK_DEBUG 00243 BLYNK_LOG2(BLYNK_F("Bad hdr len: "), ret); 00244 #endif 00245 return false; 00246 } 00247 00248 if (hdr.type == BLYNK_CMD_RESPONSE) { 00249 lastActivityIn = BlynkMillis(); 00250 00251 #ifndef BLYNK_USE_DIRECT_CONNECT 00252 if (state == CONNECTING && (1 == hdr.msg_id)) { 00253 switch (hdr.length) { 00254 case BLYNK_SUCCESS: 00255 case BLYNK_ALREADY_REGISTERED: 00256 BLYNK_LOG3(BLYNK_F("Ready (ping: "), lastActivityIn-lastHeartbeat, BLYNK_F("ms).")); 00257 lastHeartbeat = lastActivityIn; 00258 state = CONNECTED; 00259 #ifdef BLYNK_DEBUG 00260 if (size_t ram = BlynkFreeRam()) { 00261 BLYNK_LOG2(BLYNK_F("Free RAM: "), ram); 00262 } 00263 #endif 00264 this->sendInfo(); 00265 BLYNK_RUN_YIELD(); 00266 BlynkOnConnected(); 00267 return true; 00268 case BLYNK_INVALID_TOKEN: 00269 BLYNK_LOG1(BLYNK_F("Invalid auth token")); 00270 break; 00271 default: 00272 BLYNK_LOG2(BLYNK_F("Connect failed. code: "), hdr.length); 00273 } 00274 return false; 00275 } 00276 if (BLYNK_NOT_AUTHENTICATED == hdr.length) { 00277 return false; 00278 } 00279 #endif 00280 // TODO: return code may indicate App presence 00281 return true; 00282 } 00283 00284 if (hdr.length > BLYNK_MAX_READBYTES) { 00285 BLYNK_LOG2(BLYNK_F("Packet too big: "), hdr.length); 00286 // TODO: Flush 00287 internalReconnect(); 00288 return true; 00289 } 00290 00291 uint8_t inputBuffer[hdr.length+1]; // Add 1 to zero-terminate 00292 if (hdr.length != conn.read(inputBuffer, hdr.length)) { 00293 #ifdef BLYNK_DEBUG 00294 BLYNK_LOG1(BLYNK_F("Can't read body")); 00295 #endif 00296 return false; 00297 } 00298 inputBuffer[hdr.length] = '\0'; 00299 00300 BLYNK_DBG_DUMP(">", inputBuffer, hdr.length); 00301 00302 lastActivityIn = BlynkMillis(); 00303 00304 switch (hdr.type) 00305 { 00306 case BLYNK_CMD_LOGIN: { 00307 #ifdef BLYNK_USE_DIRECT_CONNECT 00308 if (strncmp(authkey, (char*)inputBuffer, 32)) { 00309 BLYNK_LOG1(BLYNK_F("Invalid token")); 00310 sendCmd(BLYNK_CMD_RESPONSE, hdr.msg_id, NULL, BLYNK_INVALID_TOKEN); 00311 break; 00312 } 00313 #endif 00314 if (state == CONNECTING) { 00315 BLYNK_LOG1(BLYNK_F("Ready")); 00316 state = CONNECTED; 00317 #ifdef BLYNK_DEBUG 00318 if (size_t ram = BlynkFreeRam()) { 00319 BLYNK_LOG2(BLYNK_F("Free RAM: "), ram); 00320 } 00321 #endif 00322 this->sendInfo(); 00323 BLYNK_RUN_YIELD(); 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 state = CONNECTING; 00348 lastHeartbeat = lastActivityIn = lastActivityOut = (BlynkMillis() - 5000UL); 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 BLYNK_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 internalReconnect(); 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 (!conn.connected() || (cmd != BLYNK_CMD_RESPONSE && cmd != BLYNK_CMD_PING && cmd != BLYNK_CMD_LOGIN && state != CONNECTED) ) { 00430 #ifdef BLYNK_DEBUG_ALL 00431 BLYNK_LOG2(BLYNK_F("Cmd skipped:"), cmd); 00432 #endif 00433 return; 00434 } 00435 00436 if (0 == id) { 00437 id = getNextMsgId(); 00438 } 00439 00440 #if defined(BLYNK_MSG_LIMIT) && BLYNK_MSG_LIMIT > 0 00441 if (cmd >= BLYNK_CMD_TWEET && cmd <= BLYNK_CMD_HARDWARE) { 00442 const millis_time_t allowed_time = BlynkMax(lastActivityOut, lastActivityIn) + 1000/BLYNK_MSG_LIMIT; 00443 long wait_time = allowed_time - BlynkMillis(); 00444 if (wait_time >= 0) { 00445 #ifdef BLYNK_DEBUG_ALL 00446 BLYNK_LOG2(BLYNK_F("Waiting:"), wait_time); 00447 #endif 00448 while (wait_time >= 0) { 00449 run(); 00450 wait_time = allowed_time - BlynkMillis(); 00451 } 00452 } else if (nesting == 0) { 00453 run(); 00454 } 00455 } 00456 #endif 00457 00458 const size_t full_length = (sizeof(BlynkHeader)) + 00459 (data ? length : 0) + 00460 (data2 ? length2 : 0); 00461 00462 #if defined(BLYNK_SEND_ATOMIC) || defined(ESP8266) || defined(ESP32) || defined(SPARK) || defined(PARTICLE) || defined(ENERGIA) 00463 // Those have more RAM and like single write at a time... 00464 00465 uint8_t buff[full_length]; 00466 00467 BlynkHeader* hdr = (BlynkHeader*)buff; 00468 hdr->type = cmd; 00469 hdr->msg_id = htons(id); 00470 hdr->length = htons(length+length2); 00471 00472 size_t pos = sizeof(BlynkHeader); 00473 if (data && length) { 00474 memcpy(buff + pos, data, length); 00475 pos += length; 00476 } 00477 if (data2 && length2) { 00478 memcpy(buff + pos, data2, length2); 00479 } 00480 00481 size_t wlen = 0; 00482 while (wlen < full_length) { 00483 const size_t chunk = BlynkMin(size_t(BLYNK_SEND_CHUNK), full_length - wlen); 00484 BLYNK_DBG_DUMP("<", buff + wlen, chunk); 00485 const size_t w = conn.write(buff + wlen, chunk); 00486 BlynkDelay(BLYNK_SEND_THROTTLE); 00487 if (w == 0) { 00488 #ifdef BLYNK_DEBUG 00489 BLYNK_LOG1(BLYNK_F("Cmd error")); 00490 #endif 00491 conn.disconnect(); 00492 state = CONNECTING; 00493 BlynkOnDisconnected(); 00494 return; 00495 } 00496 wlen += w; 00497 } 00498 00499 #else 00500 00501 BlynkHeader hdr; 00502 hdr.type = cmd; 00503 hdr.msg_id = htons(id); 00504 hdr.length = htons(length+length2); 00505 00506 BLYNK_DBG_DUMP("<", &hdr, sizeof(hdr)); 00507 size_t wlen = conn.write(&hdr, sizeof(hdr)); 00508 BlynkDelay(BLYNK_SEND_THROTTLE); 00509 00510 if (cmd != BLYNK_CMD_RESPONSE) { 00511 if (length) { 00512 BLYNK_DBG_DUMP("<", data, length); 00513 wlen += conn.write(data, length); 00514 BlynkDelay(BLYNK_SEND_THROTTLE); 00515 } 00516 if (length2) { 00517 BLYNK_DBG_DUMP("<", data2, length2); 00518 wlen += conn.write(data2, length2); 00519 BlynkDelay(BLYNK_SEND_THROTTLE); 00520 } 00521 } 00522 00523 #endif 00524 00525 if (wlen != full_length) { 00526 #ifdef BLYNK_DEBUG 00527 BLYNK_LOG4(BLYNK_F("Sent "), wlen, '/', full_length); 00528 #endif 00529 internalReconnect(); 00530 return; 00531 } 00532 00533 lastActivityOut = BlynkMillis(); 00534 00535 } 00536 00537 template <class Transp> 00538 uint16_t BlynkProtocol<Transp>::getNextMsgId() 00539 { 00540 if (msgIdOutOverride != 0) 00541 return msgIdOutOverride; 00542 if (++msgIdOut == 0) 00543 msgIdOut = 1; 00544 return msgIdOut; 00545 } 00546 00547 #endif
Generated on Tue Jul 12 2022 15:11:12 by 1.7.2