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 Io_moon 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 , lastActivityIn(0) 00037 , lastActivityOut(0) 00038 , lastHeartbeat(0) 00039 #ifdef BLYNK_MSG_LIMIT 00040 , deltaCmd(0) 00041 #endif 00042 , currentMsgId(0) 00043 , state(CONNECTING) 00044 {} 00045 00046 bool connected () { 00047 LOG_ENTER; 00048 return state == CONNECTED; 00049 } 00050 00051 bool connect(uint32_t timeout = BLYNK_TIMEOUT_MS*3) { 00052 LOG_ENTER; 00053 conn.disconnect(); 00054 state = CONNECTING; 00055 millis_time_t started = this->getMillis(); 00056 while ((state != CONNECTED) && 00057 (this->getMillis() - started < timeout)) { 00058 run(); 00059 } 00060 return state == CONNECTED; 00061 } 00062 00063 void disconnect() { 00064 LOG_ENTER; 00065 conn.disconnect(); 00066 state = DISCONNECTED; 00067 BLYNK_LOG1(BLYNK_F("Disconnected")); 00068 } 00069 00070 bool run(bool avail = false); 00071 00072 // TODO: Fixme 00073 void startSession() { 00074 LOG_ENTER; 00075 conn.connect(); 00076 state = CONNECTING; 00077 #ifdef BLYNK_MSG_LIMIT 00078 deltaCmd = 1000; 00079 #endif 00080 currentMsgId = 0; 00081 lastHeartbeat = lastActivityIn = lastActivityOut = this->getMillis(); // TODO: - 5005UL 00082 } 00083 00084 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); 00085 00086 private: 00087 int readHeader(BlynkHeader& hdr); 00088 uint16_t getNextMsgId(); 00089 00090 protected: 00091 void begin(const char* auth) { 00092 BLYNK_LOG1(BLYNK_F("Blynk v" BLYNK_VERSION " on " BLYNK_INFO_DEVICE)); 00093 this->authkey = auth; 00094 } 00095 bool processInput(void); 00096 00097 Transp& conn; 00098 00099 private: 00100 const char* authkey; 00101 millis_time_t lastActivityIn; 00102 millis_time_t lastActivityOut; 00103 union { 00104 millis_time_t lastHeartbeat; 00105 millis_time_t lastLogin; 00106 }; 00107 #ifdef BLYNK_MSG_LIMIT 00108 millis_time_t deltaCmd; 00109 #endif 00110 uint16_t currentMsgId; 00111 protected: 00112 BlynkState state; 00113 }; 00114 00115 template <class Transp> 00116 bool BlynkProtocol<Transp>::run(bool avail) 00117 { 00118 //LOG_ENTER; 00119 #if !defined(BLYNK_NO_YIELD) 00120 asdf 00121 yield(); 00122 #endif 00123 00124 if (state == DISCONNECTED) { 00125 return false; 00126 } 00127 00128 const bool tconn = conn.connected(); 00129 00130 if (tconn) { 00131 if (avail || conn.available() > 0) { 00132 //BLYNK_LOG2(BLYNK_F("Available: "), conn.available()); 00133 //const unsigned long t = micros(); 00134 if (!processInput()) { 00135 conn.disconnect(); 00136 // TODO: Only when in direct mode? 00137 #ifdef BLYNK_USE_DIRECT_CONNECT 00138 state = CONNECTING; 00139 #endif 00140 //BlynkOnDisconnected(); 00141 return false; 00142 } 00143 //BLYNK_LOG2(BLYNK_F("Proc time: "), micros() - t); 00144 } 00145 } 00146 00147 const millis_time_t t = this->getMillis(); 00148 00149 if (state == CONNECTED) { 00150 if (!tconn) { 00151 state = CONNECTING; 00152 lastHeartbeat = t; 00153 //BlynkOnDisconnected(); 00154 return false; 00155 } 00156 00157 if (t - lastActivityIn > (1000UL * BLYNK_HEARTBEAT + BLYNK_TIMEOUT_MS*3)) { 00158 #ifdef BLYNK_DEBUG 00159 BLYNK_LOG6(BLYNK_F("Heartbeat timeout: "), t, BLYNK_F(", "), lastActivityIn, BLYNK_F(", "), lastHeartbeat); 00160 #else 00161 BLYNK_LOG1(BLYNK_F("Heartbeat timeout")); 00162 #endif 00163 conn.disconnect(); 00164 state = CONNECTING; 00165 //BlynkOnDisconnected(); 00166 return false; 00167 } else if ((t - lastActivityIn > 1000UL * BLYNK_HEARTBEAT || 00168 t - lastActivityOut > 1000UL * BLYNK_HEARTBEAT) && 00169 t - lastHeartbeat > BLYNK_TIMEOUT_MS) { 00170 // Send ping if we didn't either send or receive something 00171 // for BLYNK_HEARTBEAT seconds 00172 sendCmd(BLYNK_CMD_PING); 00173 lastHeartbeat = t; 00174 } 00175 #ifndef BLYNK_USE_DIRECT_CONNECT 00176 } else if (state == CONNECTING) { 00177 if (tconn && (t - lastLogin > BLYNK_TIMEOUT_MS)) { 00178 BLYNK_LOG1(BLYNK_F("Login timeout")); 00179 conn.disconnect(); 00180 state = CONNECTING; 00181 return false; 00182 } else if (!tconn && (t - lastLogin > 5000UL)) { 00183 conn.disconnect(); 00184 if (!conn.connect()) { 00185 lastLogin = t; 00186 return false; 00187 } 00188 00189 #ifdef BLYNK_MSG_LIMIT 00190 deltaCmd = 1000; 00191 #endif 00192 sendCmd(BLYNK_CMD_LOGIN, 1, authkey, strlen(authkey)); 00193 lastLogin = lastActivityOut; 00194 return true; 00195 } 00196 #else 00197 } else if (state == CONNECTING) { 00198 if (!tconn) 00199 conn.connect(); 00200 #endif 00201 } 00202 return true; 00203 } 00204 00205 template <class Transp> 00206 BLYNK_FORCE_INLINE 00207 bool BlynkProtocol<Transp>::processInput(void) 00208 { 00209 BlynkHeader hdr; 00210 const int ret = readHeader(hdr); 00211 00212 if (ret == 0) { 00213 return true; // Considered OK (no data on input) 00214 } 00215 00216 if (ret < 0 || hdr.msg_id == 0) { 00217 #ifdef BLYNK_DEBUG 00218 BLYNK_LOG2(BLYNK_F("Bad hdr len: "), ret); 00219 #endif 00220 return false; 00221 } 00222 00223 if (hdr.type == BLYNK_CMD_RESPONSE) { 00224 lastActivityIn = this->getMillis(); 00225 00226 #ifndef BLYNK_USE_DIRECT_CONNECT 00227 if (state == CONNECTING && (1 == hdr.msg_id)) { 00228 switch (hdr.length) { 00229 case BLYNK_SUCCESS: 00230 case BLYNK_ALREADY_LOGGED_IN: 00231 BLYNK_LOG3(BLYNK_F("Ready (ping: "), lastActivityIn-lastHeartbeat, BLYNK_F("ms).")); 00232 lastHeartbeat = lastActivityIn; 00233 state = CONNECTED; 00234 this->sendInfo(); 00235 #if !defined(BLYNK_NO_YIELD) 00236 yield(); 00237 #endif 00238 BlynkOnConnected(); 00239 return true; 00240 case BLYNK_INVALID_TOKEN: 00241 BLYNK_LOG1(BLYNK_F("Invalid auth token")); 00242 break; 00243 default: 00244 BLYNK_LOG2(BLYNK_F("Connect failed. code: "), hdr.length); 00245 } 00246 return false; 00247 } 00248 if (BLYNK_NOT_AUTHENTICATED == hdr.length) { 00249 return false; 00250 } 00251 #endif 00252 // TODO: return code may indicate App presence 00253 return true; 00254 } 00255 00256 if (hdr.length > BLYNK_MAX_READBYTES) { 00257 #ifdef BLYNK_DEBUG 00258 BLYNK_LOG2(BLYNK_F("Packet too big: "), hdr.length); 00259 #endif 00260 // TODO: Flush 00261 conn.connect(); 00262 return true; 00263 } 00264 00265 uint8_t inputBuffer[hdr.length+1]; // Add 1 to zero-terminate 00266 if (hdr.length != conn.read(inputBuffer, hdr.length)) { 00267 #ifdef DEBUG 00268 BLYNK_LOG1(BLYNK_F("Can't read body")); 00269 #endif 00270 return false; 00271 } 00272 inputBuffer[hdr.length] = '\0'; 00273 00274 BLYNK_DBG_DUMP(">", inputBuffer, hdr.length); 00275 00276 lastActivityIn = this->getMillis(); 00277 00278 switch (hdr.type) { 00279 case BLYNK_CMD_LOGIN: { 00280 #ifdef BLYNK_USE_DIRECT_CONNECT 00281 if (!strncmp(authkey, (char*)inputBuffer, 32)) { 00282 BLYNK_LOG1(BLYNK_F("Ready")); 00283 state = CONNECTED; 00284 sendCmd(BLYNK_CMD_RESPONSE, hdr.msg_id, NULL, BLYNK_SUCCESS); 00285 this->sendInfo(); 00286 } else { 00287 BLYNK_LOG1(BLYNK_F("Invalid token")); 00288 sendCmd(BLYNK_CMD_RESPONSE, hdr.msg_id, NULL, BLYNK_INVALID_TOKEN); 00289 } 00290 #else 00291 BLYNK_LOG1(BLYNK_F("Ready")); 00292 state = CONNECTED; 00293 sendCmd(BLYNK_CMD_RESPONSE, hdr.msg_id, NULL, BLYNK_SUCCESS); 00294 this->sendInfo(); 00295 #endif 00296 } 00297 break; 00298 case BLYNK_CMD_PING: { 00299 sendCmd(BLYNK_CMD_RESPONSE, hdr.msg_id, NULL, BLYNK_SUCCESS); 00300 } 00301 break; 00302 case BLYNK_CMD_HARDWARE: 00303 case BLYNK_CMD_BRIDGE: { 00304 currentMsgId = hdr.msg_id; 00305 this->processCmd(inputBuffer, hdr.length); 00306 currentMsgId = 0; 00307 } 00308 break; 00309 default: { 00310 #ifdef BLYNK_DEBUG 00311 BLYNK_LOG2(BLYNK_F("Invalid header type: "), hdr.type); 00312 #endif 00313 // TODO: Flush 00314 conn.connect(); 00315 } 00316 break; 00317 } 00318 00319 return true; 00320 } 00321 00322 template <class Transp> 00323 int BlynkProtocol<Transp>::readHeader(BlynkHeader& hdr) 00324 { 00325 size_t rlen = conn.read(&hdr, sizeof(hdr)); 00326 if (rlen == 0) { 00327 return 0; 00328 } 00329 00330 if (sizeof(hdr) != rlen) { 00331 return -1; 00332 } 00333 00334 BLYNK_DBG_DUMP(">", &hdr, sizeof(BlynkHeader)); 00335 00336 hdr.msg_id = ntohs(hdr.msg_id); 00337 hdr.length = ntohs(hdr.length); 00338 00339 return rlen; 00340 } 00341 00342 #ifndef BLYNK_SEND_THROTTLE 00343 #define BLYNK_SEND_THROTTLE 10 00344 #endif 00345 00346 #ifndef BLYNK_SEND_CHUNK 00347 #define BLYNK_SEND_CHUNK 1024 // Just a big number 00348 #endif 00349 00350 template <class Transp> 00351 void BlynkProtocol<Transp>::sendCmd(uint8_t cmd, uint16_t id, const void* data, size_t length, const void* data2, size_t length2) 00352 { 00353 //pc.printf("sendCmd: cmd=%d, id = %d, state=%d\r\n", cmd, id, state); 00354 if (0 == id) { 00355 id = getNextMsgId(); 00356 } 00357 //pc.printf("next Id:%d\r\n", id); 00358 #ifdef BLYNK_MICRODUINO 00359 if (!conn.connected()) { 00360 //pc.printf("not connected, so just return!\r\n"); 00361 return; 00362 } 00363 #else 00364 if (!conn.connected() || (cmd != BLYNK_CMD_RESPONSE && cmd != BLYNK_CMD_PING && cmd != BLYNK_CMD_LOGIN && state != CONNECTED) ) { 00365 //if (!conn.connected() || (cmd != BLYNK_CMD_RESPONSE && cmd != BLYNK_CMD_PING && cmd != BLYNK_CMD_LOGIN) ) { 00366 #ifdef BLYNK_DEBUG 00367 BLYNK_LOG2(BLYNK_F("Cmd skipped:"), cmd); 00368 #endif 00369 //pc.printf("Cmd skipped:", cmd); 00370 return; 00371 } 00372 asdf 00373 #endif 00374 const int full_length = (sizeof(BlynkHeader)) + 00375 (data ? length : 0) + 00376 (data2 ? length2 : 0); 00377 //pc.printf("full_length = %d\r\n", full_length); 00378 #if defined(BLYNK_SEND_ATOMIC) || defined(ESP8266) || defined(SPARK) || defined(PARTICLE) || defined(ENERGIA) 00379 // Those have more RAM and like single write at a time... 00380 00381 uint8_t buff[full_length]; 00382 00383 BlynkHeader* hdr = (BlynkHeader*)buff; 00384 hdr->type = cmd; 00385 hdr->msg_id = htons(id); 00386 hdr->length = htons(length+length2); 00387 00388 size_t pos = sizeof(BlynkHeader); 00389 if (data && length) { 00390 memcpy(buff + pos, data, length); 00391 pos += length; 00392 } 00393 if (data2 && length2) { 00394 memcpy(buff + pos, data2, length2); 00395 } 00396 00397 size_t wlen = 0; 00398 while (wlen < full_length) { 00399 const size_t chunk = BlynkMin(size_t(BLYNK_SEND_CHUNK), full_length - wlen); 00400 BLYNK_DBG_DUMP("<", buff + wlen, chunk); 00401 const size_t w = conn.write(buff + wlen, chunk); 00402 wait_ms(BLYNK_SEND_THROTTLE); 00403 if (w == 0) { 00404 #ifdef BLYNK_DEBUG 00405 BLYNK_LOG1(BLYNK_F("Cmd error")); 00406 #endif 00407 conn.disconnect(); 00408 state = CONNECTING; 00409 //BlynkOnDisconnected(); 00410 return; 00411 } 00412 wlen += w; 00413 } 00414 00415 #else 00416 BlynkHeader hdr; 00417 hdr.type = cmd; 00418 hdr.msg_id = htons(id); 00419 hdr.length = htons(length+length2); 00420 00421 BLYNK_DBG_DUMP("<", &hdr, sizeof(hdr)); 00422 size_t wlen = conn.write(&hdr, sizeof(hdr)); 00423 wait_ms(BLYNK_SEND_THROTTLE); 00424 00425 if (cmd != BLYNK_CMD_RESPONSE) { 00426 if (length) { 00427 BLYNK_DBG_DUMP("<", data, length); 00428 wlen += conn.write(data, length); 00429 wait_ms(BLYNK_SEND_THROTTLE); 00430 } 00431 if (length2) { 00432 BLYNK_DBG_DUMP("<", data2, length2); 00433 wlen += conn.write(data2, length2); 00434 wait_ms(BLYNK_SEND_THROTTLE); 00435 } 00436 } 00437 00438 #endif 00439 00440 if (wlen != full_length) { 00441 #ifdef BLYNK_DEBUG 00442 BLYNK_LOG4(BLYNK_F("Sent "), wlen, '/', full_length); 00443 #endif 00444 conn.disconnect(); 00445 state = CONNECTING; 00446 //BlynkOnDisconnected(); 00447 return; 00448 } 00449 00450 #if defined BLYNK_MSG_LIMIT && BLYNK_MSG_LIMIT > 0 00451 const millis_time_t ts = this->getMillis(); 00452 BlynkAverageSample<32>(deltaCmd, ts - lastActivityOut); 00453 lastActivityOut = ts; 00454 //BLYNK_LOG2(BLYNK_F("Delta: "), deltaCmd); 00455 if (deltaCmd < (1000/BLYNK_MSG_LIMIT)) { 00456 BLYNK_LOG_TROUBLE(BLYNK_F("flood-error")); 00457 conn.disconnect(); 00458 state = CONNECTING; 00459 //BlynkOnDisconnected(); 00460 } 00461 #else 00462 lastActivityOut = this->getMillis(); 00463 #endif 00464 00465 } 00466 00467 template <class Transp> 00468 uint16_t BlynkProtocol<Transp>::getNextMsgId() 00469 { 00470 static uint16_t last = 0; 00471 if (currentMsgId != 0) 00472 return currentMsgId; 00473 if (++last == 0) 00474 last = 1; 00475 return last; 00476 } 00477 00478 #endif 00479
Generated on Tue Jul 19 2022 01:01:49 by
1.7.2
