From 29306a569950e465b151a33c64ddba5c2afba7a9 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Sat, 18 Aug 2018 14:58:03 -0400 Subject: [PATCH] Implemented basic receive functionality. See full log. Fixed the bug in PUBLISH. Tested with Adafruit IO and data can be published successfully! This was the original goal of this project -- being able to blindly fire values at Adafruit and not having to worry about receiving data. However, I've widened the scope of the project, and want to eventually do a full MQTT implementation per the spec. The basic receive functionality looks at what type of message is receieved, and then prints out basic messages depending on what the payload is. Mostly useful as a debugging tool, but it can easily be expanded. This should probably be split out into its own function at some point. strtoarr.py: Because of the weird bug that makes passing string pointers impossible with the ESP8266, strings have to be stored as hex arrays and then passed as a pointer. This is a pain for the user, so I've written a basic script which will take two arguments: the string to be encoded, and the variable name to be used. It then spits out C code which can be copied and pasted into the user code. This saves a bunch of time, and because it spits them out as static const, it also tells the compiler to store them in flash (as far as I can tell). As mentioned, it has now been tested with Adafruit IO. There are a few more functions in the "user" area of the file. These are only for debugging and will eventually be surrounded with \#ifdef DEBUG so they won't be included in the compiled version. Also, more ifdef preprocessor directives have been added so that debug messages won't be printed to a user's output unless they want them. The next step is to implement SUBSCRIBE, and to compile the code as mqtt.a - a linkable object file. --- .gitignore | 6 ++++-- mqtt.c | 62 +++++++++++++++++++++++++++++++---------------------- strtoarr.py | 22 +++++++++++++++++++ 3 files changed, 62 insertions(+), 28 deletions(-) create mode 100755 strtoarr.py diff --git a/.gitignore b/.gitignore index 19e4104..493b922 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ +# Most of these are random documentation/debugging files I used +# during development *.bin *~ *.o -*.pdf *.map +*.pdf mqtt \#*\# .\#* -*.pcap +pcap/ diff --git a/mqtt.c b/mqtt.c index c3de4cd..764ce3e 100644 --- a/mqtt.c +++ b/mqtt.c @@ -35,14 +35,16 @@ LOCAL void ICACHE_FLASH_ATTR data_recv_callback(void *arg, char *pdata, unsigned os_printf("Received data of length %d -- %s \r\n", len, pdata); os_printf("Hex dump: "); for(int i = 0; i < len; i++) { - os_printf("%x", pdata[i]); + os_printf("(%d): %x ", i, pdata[i]); } os_printf("\n"); } LOCAL void ICACHE_FLASH_ATTR connected_callback(void *arg) { struct espconn *pConn = arg; +#ifdef DEBUG os_printf("Connected callback\n"); +#endif espconn_regist_recvcb(pConn, (espconn_recv_callback)data_recv_callback); espconn_regist_sentcb(pConn, (espconn_sent_callback)data_sent_callback); // enable keepalive @@ -66,10 +68,14 @@ LOCAL uint8_t ICACHE_FLASH_ATTR tcpConnect(void *arg) { if (wifi_station_get_connect_status() == STATION_GOT_IP && ipConfig.ip.addr != 0) { os_printf("Everything looks good!\n"); } +#ifdef DEBUG os_printf("Entered tcpConnect\n"); +#endif wifi_get_ip_info(STATION_IF, &ipConfig); // set up basic TCP connection parameters +#ifdef DEBUG os_printf("about to set up TCP params\n"); +#endif conn.proto.tcp = &tcp_s; conn.type = ESPCONN_TCP; conn.proto.tcp->local_port = espconn_port(); @@ -90,7 +96,9 @@ LOCAL uint8_t ICACHE_FLASH_ATTR tcpConnect(void *arg) { os_printf("Connection error\n"); } session->activeConnection = &conn; - os_printf("About to return\n"); +#ifdef DEBUG + os_printf("About to return from TCP connect\n"); +#endif return 0; } @@ -140,7 +148,7 @@ LOCAL uint8_t ICACHE_FLASH_ATTR mqtt_send(mqtt_session_t *session, uint8_t *data uint8_t *fullPacket; uint8_t *remaining_len_encoded; switch(msgType) { - case MQTT_MSG_TYPE_CONNECT: ; // <-- do not remove this semicolon!! + case MQTT_MSG_TYPE_CONNECT: { const uint8_t varDefaults[10] = { 0x00, 0x04, 0x4D, 0x51, 0x54, 0x54, 0x04, 0xC2, 0x00, 0x32 }; // variable header is always the same for Connect pPacket->fixedHeader = (uint8_t *)os_zalloc(sizeof(uint8_t) * 5); // fixed header cannot be longer than 5 pPacket->fixedHeader[0] = ((uint8_t)msgType << 4) & 0xF0; // make sure lower 4 are clear @@ -197,16 +205,18 @@ LOCAL uint8_t ICACHE_FLASH_ATTR mqtt_send(mqtt_session_t *session, uint8_t *data os_memcpy(fullPacket + pPacket->fixedHeader_len, pPacket->varHeader, pPacket->varHeader_len); os_memcpy(fullPacket + pPacket->fixedHeader_len + pPacket->varHeader_len, pPacket->payload, pPacket->payload_len); break; - case MQTT_MSG_TYPE_PUBLISH: ; // <-- leave this semicolon! + } + case MQTT_MSG_TYPE_PUBLISH: { // prepare for publish pPacket->fixedHeader = (uint8_t *)os_zalloc(sizeof(uint8_t) * 5); pPacket->fixedHeader[0] = (MQTT_MSG_TYPE_PUBLISH << 4) & 0xF0; // clear lower 4 bits, we don't need DUP, QOS or RETAIN // variable header + // A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0 [MQTT-2.3.1-5]. pPacket->varHeader_len = session->topic_name_len + 2; // we have no packet identifier, as we are QOS 0 pPacket->varHeader = (uint8_t *)os_zalloc(sizeof(uint8_t) * pPacket->varHeader_len); os_memset(pPacket->varHeader, 0, 1); - os_memcpy(pPacket->varHeader + 1, &pPacket->varHeader_len, 1); + os_memcpy(pPacket->varHeader + 1, &session->topic_name_len, 1); os_memcpy(pPacket->varHeader + 2, session->topic_name, session->topic_name_len); //payload @@ -226,6 +236,7 @@ LOCAL uint8_t ICACHE_FLASH_ATTR mqtt_send(mqtt_session_t *session, uint8_t *data os_memcpy(fullPacket + pPacket->fixedHeader_len, pPacket->varHeader, pPacket->varHeader_len); os_memcpy(fullPacket + pPacket->fixedHeader_len + pPacket->varHeader_len, pPacket->payload, pPacket->payload_len); break; + } case MQTT_MSG_TYPE_SUBSCRIBE: // prepare for subscribe break; @@ -299,22 +310,18 @@ LOCAL void ICACHE_FLASH_ATTR test(void *arg) { os_timer_disarm(&oneTimer); os_timer_setfn(&oneTimer, (os_timer_func_t *)test2, arg); - os_timer_arm(&oneTimer, 2000, 0); + os_timer_arm(&oneTimer, 200, 0); } LOCAL void ICACHE_FLASH_ATTR test2(void *arg) { os_printf("Entered test2!\n"); mqtt_session_t *pSession = (mqtt_session_t *)arg; - uint8_t data[] = "value: 15"; + uint8_t data[] = "3.14159"; uint32_t dataLen = os_strlen(data); - uint8_t topicName[] = "test"; - uint32_t topicNameLen = os_strlen(topicName); - pSession->topic_name = &topicName[0]; - pSession->topic_name_len = topicNameLen; mqtt_send(pSession, &data[0], dataLen, MQTT_MSG_TYPE_PUBLISH); os_timer_disarm(&oneTimer); os_timer_setfn(&oneTimer, (os_timer_func_t *)ping, arg); - os_timer_arm(&oneTimer, 2000, 0); + os_timer_arm(&oneTimer, 200, 0); } LOCAL void ICACHE_FLASH_ATTR ping(void *arg) { @@ -325,7 +332,7 @@ LOCAL void ICACHE_FLASH_ATTR ping(void *arg) { mqtt_send(pSession, NULL, 0, MQTT_MSG_TYPE_PINGREQ); os_timer_disarm(&oneTimer); os_timer_setfn(&oneTimer, (os_timer_func_t *)discon, arg); - os_timer_arm(&oneTimer, 2000, 0); + os_timer_arm(&oneTimer, 200, 0); } LOCAL void ICACHE_FLASH_ATTR discon(void *arg) { @@ -353,31 +360,34 @@ void ICACHE_FLASH_ATTR user_init() { // prepare the TCP/MQTT connection stuff // test server is at 51.15.65.206 - char testUser[11] = { 0x4d, 0x72, 0x41, 0x75, 0x72, 0x65, 0x6c, 0x69, 0x75, 0x73, 0x52 }; - char *pTestUser = testUser; - char testPass[4] = { 0x74, 0x65, 0x73, 0x74 }; - char *pTestPass = testPass; - char testTopic[5] = "input"; - char clientID[5] = {0x46, 0x52, 0x5a, 0x4e, 0x30 }; - char *pClientID = clientID; + // Adafruit IO is at 52.5.238.97 + + static const char testUser[11] = { 0x4d, 0x72, 0x41, 0x75, 0x72, 0x65, 0x6c, 0x69, 0x75, 0x73, 0x52 }; + static const uint8_t testUser_len = 11; + static const char testPass[32] = { 0x63, 0x61, 0x62, 0x31, 0x39, 0x36, 0x36, 0x34, 0x31, 0x66, 0x33, 0x63, 0x34, 0x34, 0x36, 0x32, 0x61, 0x35, 0x30, 0x39, 0x64, 0x62, 0x64, 0x31, 0x34, 0x61, 0x30, 0x61, 0x66, 0x36, 0x64, 0x32 }; + static const uint8_t testPass_len = 32; + static const char testTopic[22] = { 0x4d, 0x72, 0x41, 0x75, 0x72, 0x65, 0x6c, 0x69, 0x75, 0x73, 0x52, 0x2f, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74 }; + static const uint8_t testTopic_len = 22; + static const char clientID[5] = { 0x46, 0x52, 0x5a, 0x4e, 0x30 }; + static const uint8_t clientID_len = 5; pGlobalSession->port = 1883; // mqtt port - const char esp_tcp_server_ip[4] = {51, 15, 65, 206}; // remote IP of TCP server + static const char esp_tcp_server_ip[4] = {52, 5, 238, 97}; // remote IP of Adafruit IO os_memcpy(pGlobalSession->ip, esp_tcp_server_ip, 4); - pGlobalSession->username_len = 11; + pGlobalSession->username_len = testUser_len; pGlobalSession->username = os_zalloc(sizeof(uint8_t) * pGlobalSession->username_len); os_memcpy(pGlobalSession->username, testUser, pGlobalSession->username_len); - pGlobalSession->password_len = 4; + pGlobalSession->password_len = testPass_len; pGlobalSession->password = os_zalloc(sizeof(uint8_t) * pGlobalSession->password_len); os_memcpy(pGlobalSession->password, testPass, pGlobalSession->password_len); - pGlobalSession->topic_name_len = 5; + pGlobalSession->topic_name_len = testTopic_len; pGlobalSession->topic_name = os_zalloc(sizeof(uint8_t) * pGlobalSession->topic_name_len); os_memcpy(pGlobalSession->topic_name, testTopic, pGlobalSession->topic_name_len); - pGlobalSession->client_id_len = 5; + pGlobalSession->client_id_len = clientID_len; pGlobalSession->client_id = os_zalloc(sizeof(uint8_t) * pGlobalSession->client_id_len); os_memcpy(pGlobalSession->client_id, clientID, pGlobalSession->client_id_len); os_timer_setfn(&oneTimer, (os_timer_func_t *)tcpConnect, pGlobalSession); os_timer_arm(&oneTimer, 15000, 0); os_timer_setfn(&testTimer, (os_timer_func_t *)test, pGlobalSession); - os_timer_arm(&testTimer, 17000, 0); + os_timer_arm(&testTimer, 16000, 0); } diff --git a/strtoarr.py b/strtoarr.py new file mode 100755 index 0000000..36cbe05 --- /dev/null +++ b/strtoarr.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +############################################### +# First argument is the value to be encoded, # +# second argument is the name of the variable # +# to be used. # +############################################### + +import sys +import os + +inString = str(sys.argv[1]); + +print("static const char {0}[{1}] = {{ ".format(sys.argv[2], len(inString)), end='') +for letter in inString[:-1]: + p = ord(letter) + print("{0:#x}, ".format(p), end='') + +p = ord(inString[-1]) +print("{0:#x} ".format(int(p)), end='') +print("};") +print("static const uint8_t {0}_len = {1};".format(sys.argv[2], len(inString)))