/** * @brief Allocate and enqueue mqtt message, kick sending, if appropriate */ static void ICACHE_FLASH_ATTR mqtt_enq_message(MQTT_Client *client, const uint8_t *data, uint16_t len) { PktBuf *buf = PktBuf_New(len); os_memcpy(buf->data, data, len); buf->filled = len; client->msgQueue = PktBuf_Push(client->msgQueue, buf); if (client->connState == MQTT_CONNECTED && !client->sending && client->pending_buffer == NULL) { mqtt_send_message(client); } }
/* * @brief: Timer function to handle timeouts */ static void ICACHE_FLASH_ATTR mqtt_timer(void* arg) { MQTT_Client* client = (MQTT_Client*)arg; //DBG_MQTT("MQTT: timer CB\n"); switch (client->connState) { default: break; case MQTT_CONNECTED: // first check whether we're timing out for an ACK if (client->pending_buffer != NULL && --client->timeoutTick == 0) { // looks like we're not getting a response in time, abort the connection mqtt_doAbort(client); client->timeoutTick = 0; // trick to make reconnect happen in 1 second return; } // check whether our last keep-alive timed out if (client->keepAliveAckTick > 0 && --client->keepAliveAckTick == 0) { os_printf("\nMQTT ERROR: Keep-alive timed out\n"); mqtt_doAbort(client); return; } // check whether we need to send a keep-alive message if (client->keepAliveTick > 0 && --client->keepAliveTick == 0) { // timeout: we need to send a ping message //DBG_MQTT("MQTT: Send keepalive\n"); mqtt_msg_pingreq(&client->mqtt_connection); PktBuf *buf = PktBuf_New(client->mqtt_connection.message.length); os_memcpy(buf->data, client->mqtt_connection.message.data, client->mqtt_connection.message.length); buf->filled = client->mqtt_connection.message.length; client->msgQueue = PktBuf_Unshift(client->msgQueue, buf); mqtt_send_message(client); client->keepAliveTick = client->connect_info.keepalive; client->keepAliveAckTick = client->sendTimeout; } break; case TCP_RECONNECT_REQ: if (client->timeoutTick == 0 || --client->timeoutTick == 0) { // it's time to reconnect! start by re-enqueueing anything pending if (client->pending_buffer != NULL) { client->msgQueue = PktBuf_Unshift(client->msgQueue, client->pending_buffer); client->pending_buffer = NULL; } client->connect_info.clean_session = 0; // ask server to keep state MQTT_Connect(client); } } }
/** * ESP8266 callback function that is invoked when new data has arrived over * the TCP/IP connection. */ static void esp8266_callback_recvCB( void *arg, //!< A pointer to a `struct espconn`. char *pData, //!< A pointer to data received over the socket. unsigned short len //!< The length of the data. ) { struct espconn *pEspconn = (struct espconn *)arg; struct socketData *pSocketData = (struct socketData *)pEspconn->reverse; if (pSocketData == NULL) return; // we closed this socket //if (pEspconn != pSocketData->pEspconn) DBG("%s: pEspconn changed in recvCB ***\n", DBG_LIB); assert(pSocketData->state != SOCKET_STATE_UNUSED); //DBG("%s: socket %d recv %d\n", DBG_LIB, pSocketData->socketId, len); //DBG("%s: recv data: %p\n", DBG_LIB, pData); // if this is a dead connection then just ignore the callback if (pSocketData->state == SOCKET_STATE_ABORTING || pSocketData->state == SOCKET_STATE_TO_ABORT || pSocketData->state == SOCKET_STATE_CLOSED || pSocketData->state == SOCKET_STATE_DISCONNECTING) { return; } // Allocate a buffer and add to the receive queue PktBuf *buf = PktBuf_New(len); if (!buf) { // handle out of memory condition DBG("%s: Out of memory allocating %d for recv\n", DBG_LIB, len); // at this point we're gonna deallocate all receive buffers as a panic measure while (pSocketData->rxBufQ != NULL) pSocketData->rxBufQ = PktBuf_ShiftFree(pSocketData->rxBufQ); // save the error setSocketInError(pSocketData, ESPCONN_MEM); // now reset the connection //espconn_abort(pEspconn); // can't do this: espconn crashes! pSocketData->state = SOCKET_STATE_TO_ABORT; // some function called from socket lib will abort //DBG("%s: ret from recvCB\n", DBG_LIB); return; } // if this is the second buffer then stop the flood! if (pSocketData->rxBufQ != NULL) espconn_recv_hold(pEspconn); // got buffer, fill it os_memcpy(buf->data, pData, len); buf->filled = len; pSocketData->rxBufQ = PktBuf_Push(pSocketData->rxBufQ, buf); }
/** * @brief Callback from SDK that socket is connected * @param arg: contain the ip link information * @retval None */ static void ICACHE_FLASH_ATTR mqtt_tcpclient_connect_cb(void* arg) { struct espconn* pCon = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pCon->reverse; if (client == NULL) return; // aborted connection espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv); espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb); os_printf("MQTT: TCP connected to %s:%d\n", client->host, client->port); // send MQTT connect message to broker mqtt_msg_connect(&client->mqtt_connection, &client->connect_info); PktBuf *buf = PktBuf_New(client->mqtt_connection.message.length); os_memcpy(buf->data, client->mqtt_connection.message.data, client->mqtt_connection.message.length); buf->filled = client->mqtt_connection.message.length; client->msgQueue = PktBuf_Unshift(client->msgQueue, buf); // prepend to send (rexmit) queue mqtt_send_message(client); client->connState = MQTT_CONNECTED; // v3.1.1 allows publishing while still connecting }
/** * @brief MQTT publish function. * @param client: MQTT_Client reference * @param topic: string topic will publish to * @param data: buffer data send point to * @param data_length: length of data * @param qos: qos * @param retain: retain * @retval TRUE if success queue */ bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain) { // estimate the packet size to allocate a buffer uint16_t topic_length = os_strlen(topic); uint16_t data_length = os_strlen(data); // estimate: fixed hdr, pkt-id, topic length, topic, data, fudge uint16_t buf_len = 3 + 2 + 2 + topic_length + data_length + 16; PktBuf *buf = PktBuf_New(buf_len); if (buf == NULL) { os_printf("MQTT ERROR: Cannot allocate buffer for %d byte publish\n", buf_len); return FALSE; } // use a temporary mqtt_message_t pointing to our buffer, this is a bit of a mess because we // need to keep track of the message_id that is embedded in it mqtt_connection_t msg; msg_conn_init(&msg, &client->mqtt_connection, buf->data, buf_len); uint16_t msg_id; if (!mqtt_msg_publish(&msg, topic, data, data_length, qos, retain, &msg_id)){ os_printf("MQTT ERROR: Queuing Publish failed\n"); os_free(buf); return FALSE; } client->mqtt_connection.message_id = msg.message_id; if (msg.message.data != buf->data) os_memcpy(buf->data, msg.message.data, msg.message.length); buf->filled = msg.message.length; DBG_MQTT("MQTT: Publish, topic: \"%s\", length: %d\n", topic, msg.message.length); //dumpMem(buf, buf_len); client->msgQueue = PktBuf_Push(client->msgQueue, buf); if (!client->sending && client->pending_buffer == NULL) { mqtt_send_message(client); } return TRUE; }