blynk & neopixelring & w7500

Fork of WIZwiki-7500_Blynk by IOP

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BlynkProtocol.h Source File

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