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