├── .gitignore ├── my.cfg ├── mqtt ├── include │ ├── debug.h │ ├── utils.h │ ├── wifi.h │ ├── typedef.h │ ├── ringbuf.h │ ├── proto.h │ ├── queue.h │ ├── mqtt_msg.h │ └── mqtt.h ├── Makefile ├── ringbuf.c ├── queue.c ├── wifi.c ├── proto.c ├── utils.c ├── mqtt_msg.c └── mqtt.c ├── include ├── user_config.h ├── driver │ ├── uart.h │ └── uart_register.h └── easygpio.h ├── util ├── util.h ├── kvstore.h ├── kvstore.c ├── util.c └── easygpio.c ├── driver ├── Makefile └── uart.c ├── user ├── Makefile └── user_main.c ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .cproject 2 | .project 3 | build/ 4 | firmware/ 5 | 6 | -------------------------------------------------------------------------------- /my.cfg: -------------------------------------------------------------------------------- 1 | [general] 2 | WIFISSID = myssid 3 | WIFIPASS = mypass 4 | MQTTHOST = mymqtthost 5 | MQTTDEVPATH = /home/lab/relay 6 | -------------------------------------------------------------------------------- /mqtt/include/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * debug.h 3 | * 4 | * Created on: Dec 4, 2014 5 | * Author: Minh 6 | */ 7 | 8 | #ifndef USER_DEBUG_H_ 9 | #define USER_DEBUG_H_ 10 | 11 | #ifndef INFO 12 | #define INFO os_printf 13 | #endif 14 | 15 | #endif /* USER_DEBUG_H_ */ 16 | -------------------------------------------------------------------------------- /mqtt/include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H_ 2 | #define _UTILS_H_ 3 | 4 | #include "c_types.h" 5 | 6 | uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s); 7 | uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip); 8 | uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str); 9 | #endif 10 | -------------------------------------------------------------------------------- /mqtt/include/wifi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * wifi.h 3 | * 4 | * Created on: Dec 30, 2014 5 | * Author: Minh 6 | */ 7 | 8 | #ifndef USER_WIFI_H_ 9 | #define USER_WIFI_H_ 10 | #include "os_type.h" 11 | typedef void (*WifiCallback)(uint8_t); 12 | void WIFI_Connect(uint8_t* ssid, uint8_t* pass, WifiCallback cb); 13 | 14 | 15 | #endif /* USER_WIFI_H_ */ 16 | -------------------------------------------------------------------------------- /mqtt/include/typedef.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * Standard Types definition 4 | */ 5 | 6 | #ifndef _TYPE_DEF_H_ 7 | #define _TYPE_DEF_H_ 8 | 9 | typedef char I8; 10 | typedef unsigned char U8; 11 | typedef short I16; 12 | typedef unsigned short U16; 13 | typedef long I32; 14 | typedef unsigned long U32; 15 | typedef unsigned long long U64; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /mqtt/include/ringbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef _RING_BUF_H_ 2 | #define _RING_BUF_H_ 3 | 4 | #include 5 | #include 6 | #include "typedef.h" 7 | 8 | typedef struct{ 9 | U8* p_o; /**< Original pointer */ 10 | U8* volatile p_r; /**< Read pointer */ 11 | U8* volatile p_w; /**< Write pointer */ 12 | volatile I32 fill_cnt; /**< Number of filled slots */ 13 | I32 size; /**< Buffer size */ 14 | }RINGBUF; 15 | 16 | I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size); 17 | I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c); 18 | I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c); 19 | #endif 20 | -------------------------------------------------------------------------------- /include/user_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _USER_CONFIG_H_ 2 | #define _USER_CONFIG_H_ 3 | 4 | #define PROTOCOL_NAMEv31 5 | 6 | #define CFG_HOLDER 0x00FF55A4 /* Change this value to load default configurations */ 7 | #define CFG_LOCATION 0x3C /* Please don't change or if you know what you doing */ 8 | #define CLIENT_SSL_ENABLE 9 | 10 | /*DEFAULT CONFIGURATIONS*/ 11 | 12 | #define MQTT_HOST "127.0.0.1" //or "mqtt.yourdomain.com" 13 | #define MQTT_PORT 1880 14 | #define MQTT_BUF_SIZE 1024 15 | #define MQTT_KEEPALIVE 120 /*second*/ 16 | 17 | #define MQTT_CLIENT_ID "DVES_%08X" 18 | #define MQTT_USER "DVES_USER" 19 | #define MQTT_PASS "DVES_PASS" 20 | 21 | #define STA_SSID "nunya" 22 | #define STA_PASS "itsasecret" 23 | #define STA_TYPE AUTH_WPA2_PSK 24 | 25 | #define MQTT_RECONNECT_TIMEOUT 5 /*second*/ 26 | 27 | #define DEFAULT_SECURITY 0 28 | #define QUEUE_BUFFER_SIZE 2048 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /mqtt/include/proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: proto.h 3 | * Author: ThuHien 4 | * 5 | * Created on November 23, 2012, 8:57 AM 6 | */ 7 | 8 | #ifndef _PROTO_H_ 9 | #define _PROTO_H_ 10 | #include 11 | #include "typedef.h" 12 | #include "ringbuf.h" 13 | 14 | typedef void(PROTO_PARSE_CALLBACK)(); 15 | 16 | typedef struct{ 17 | U8 *buf; 18 | U16 bufSize; 19 | U16 dataLen; 20 | U8 isEsc; 21 | U8 isBegin; 22 | PROTO_PARSE_CALLBACK* callback; 23 | }PROTO_PARSER; 24 | 25 | I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize); 26 | I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len); 27 | I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize); 28 | I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len); 29 | I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value); 30 | I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, U8 *bufOut, U16* len, U16 maxBufLen); 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /util/util.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #define util_assert(t, f, ...){\ 4 | if(!(t)){\ 5 | os_printf("Assertion failed. File: %s Line: %d\r\nMessage: ", __FILE__, __LINE__);\ 6 | os_printf(f, __VA_ARGS__);\ 7 | util_assert_handler();\ 8 | }\ 9 | } 10 | 11 | #define util_zalloc(sz) ((void *) os_zalloc((sz))) 12 | #define util_free(p) os_free((p)) 13 | 14 | void util_restart(void); 15 | void util_assert_handler(void); 16 | char * util_str_realloc(const char *p, size_t new_len); 17 | char * util_string_split(const char *in_str, char **list, char sep, int max_list_length); 18 | char * util_make_sub_topic(const char *rootTopic, char *subTopic); 19 | char * util_strdup(const char *s); 20 | char * util_strndup(const char *s, int len); 21 | int util_parse_json_param(void *state, const char *paramname, char *paramvalue, int paramvaluesize); 22 | bool util_parse_command_int(const char *commandrcvd, const char *command, const char *message, int *val); 23 | bool util_parse_command_qstring(const char *commandrcvd, const char *command, const char *message, char **val); 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /util/kvstore.h: -------------------------------------------------------------------------------- 1 | 2 | #define KVS_ENTRY_SIZE 128 3 | #define KVS_MAX_ENTRIES ((SPI_FLASH_SEC_SIZE/KVS_ENTRY_SIZE) - 1) 4 | #define KVS_MAX_KEY 16 5 | #define KVS_MAX_VAL (KVS_ENTRY_SIZE-KVS_MAX_KEY) 6 | #define KVS_DEFAULT_LOC 0x3C 7 | 8 | 9 | 10 | struct flash_config_entry_tag{ 11 | char key[KVS_MAX_KEY]; 12 | char value[KVS_MAX_VAL]; 13 | } __attribute__((__packed__)); 14 | 15 | typedef struct flash_config_entry_tag flash_config_entry_s; 16 | 17 | struct flash_sector_tag { 18 | uint8_t sig[4]; 19 | uint8_t pad[KVS_ENTRY_SIZE - 4]; 20 | flash_config_entry_s entries[KVS_MAX_ENTRIES]; 21 | } __attribute__((__packed__)); 22 | 23 | typedef struct flash_sector_tag flash_sector_s; 24 | 25 | typedef struct { 26 | flash_sector_s *sector; 27 | bool dirty; 28 | unsigned location; 29 | } flash_handle_s; 30 | 31 | flash_handle_s * kvstore_open(unsigned cfg_sector_loc); 32 | bool kvstore_exists(flash_handle_s *h, const char *key); 33 | char *kvstore_get_string(flash_handle_s *h, const char *key); 34 | int kvstore_get_integer(flash_handle_s *h, const char *key, int *int_var); 35 | bool kvstore_put(flash_handle_s *h, const char *key, const char *value); 36 | bool kvstore_flush(flash_handle_s *h); 37 | bool kvstore_close(flash_handle_s *h); 38 | bool kvstore_update_number(flash_handle_s *h, const char *key, int new_val); 39 | -------------------------------------------------------------------------------- /mqtt/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ############################################################# 3 | # Required variables for each makefile 4 | # Discard this section from all parent makefiles 5 | # Expected variables (with automatic defaults): 6 | # CSRCS (all "C" files in the dir) 7 | # SUBDIRS (all subdirs with a Makefile) 8 | # GEN_LIBS - list of libs to be generated () 9 | # GEN_IMAGES - list of images to be generated () 10 | # COMPONENTS_xxx - a list of libs/objs in the form 11 | # subdir/lib to be extracted and rolled up into 12 | # a generated lib/image xxx.a () 13 | # 14 | ifndef PDIR 15 | GEN_LIBS = libmqtt.a 16 | endif 17 | 18 | 19 | ############################################################# 20 | # Configuration i.e. compile options etc. 21 | # Target specific stuff (defines etc.) goes in here! 22 | # Generally values applying to a tree are captured in the 23 | # makefile at its root level - these are then overridden 24 | # for a subtree within the makefile rooted therein 25 | # 26 | #DEFINES += 27 | 28 | ############################################################# 29 | # Recursion Magic - Don't touch this!! 30 | # 31 | # Each subtree potentially has an include directory 32 | # corresponding to the common APIs applicable to modules 33 | # rooted at that subtree. Accordingly, the INCLUDE PATH 34 | # of a module can only contain the include directories up 35 | # its parent path, and not its siblings 36 | # 37 | # Required for each makefile to inherit from the parent 38 | # 39 | 40 | INCLUDES := $(INCLUDES) -I $(PDIR)include 41 | INCLUDES += -I ./ 42 | PDIR := ../$(PDIR) 43 | sinclude $(PDIR)Makefile 44 | 45 | -------------------------------------------------------------------------------- /driver/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ############################################################# 3 | # Required variables for each makefile 4 | # Discard this section from all parent makefiles 5 | # Expected variables (with automatic defaults): 6 | # CSRCS (all "C" files in the dir) 7 | # SUBDIRS (all subdirs with a Makefile) 8 | # GEN_LIBS - list of libs to be generated () 9 | # GEN_IMAGES - list of images to be generated () 10 | # COMPONENTS_xxx - a list of libs/objs in the form 11 | # subdir/lib to be extracted and rolled up into 12 | # a generated lib/image xxx.a () 13 | # 14 | ifndef PDIR 15 | GEN_LIBS = libdriver.a 16 | endif 17 | 18 | 19 | ############################################################# 20 | # Configuration i.e. compile options etc. 21 | # Target specific stuff (defines etc.) goes in here! 22 | # Generally values applying to a tree are captured in the 23 | # makefile at its root level - these are then overridden 24 | # for a subtree within the makefile rooted therein 25 | # 26 | #DEFINES += 27 | 28 | ############################################################# 29 | # Recursion Magic - Don't touch this!! 30 | # 31 | # Each subtree potentially has an include directory 32 | # corresponding to the common APIs applicable to modules 33 | # rooted at that subtree. Accordingly, the INCLUDE PATH 34 | # of a module can only contain the include directories up 35 | # its parent path, and not its siblings 36 | # 37 | # Required for each makefile to inherit from the parent 38 | # 39 | 40 | INCLUDES := $(INCLUDES) -I $(PDIR)include 41 | INCLUDES += -I ./ 42 | PDIR := ../$(PDIR) 43 | sinclude $(PDIR)Makefile 44 | 45 | -------------------------------------------------------------------------------- /user/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ############################################################# 3 | # Required variables for each makefile 4 | # Discard this section from all parent makefiles 5 | # Expected variables (with automatic defaults): 6 | # CSRCS (all "C" files in the dir) 7 | # SUBDIRS (all subdirs with a Makefile) 8 | # GEN_LIBS - list of libs to be generated () 9 | # GEN_IMAGES - list of images to be generated () 10 | # COMPONENTS_xxx - a list of libs/objs in the form 11 | # subdir/lib to be extracted and rolled up into 12 | # a generated lib/image xxx.a () 13 | # 14 | ifndef PDIR 15 | GEN_LIBS = libuser.a 16 | endif 17 | 18 | 19 | ############################################################# 20 | # Configuration i.e. compile options etc. 21 | # Target specific stuff (defines etc.) goes in here! 22 | # Generally values applying to a tree are captured in the 23 | # makefile at its root level - these are then overridden 24 | # for a subtree within the makefile rooted therein 25 | # 26 | #DEFINES += 27 | 28 | ############################################################# 29 | # Recursion Magic - Don't touch this!! 30 | # 31 | # Each subtree potentially has an include directory 32 | # corresponding to the common APIs applicable to modules 33 | # rooted at that subtree. Accordingly, the INCLUDE PATH 34 | # of a module can only contain the include directories up 35 | # its parent path, and not its siblings 36 | # 37 | # Required for each makefile to inherit from the parent 38 | # 39 | 40 | INCLUDES := $(INCLUDES) -I $(PDIR)include 41 | INCLUDES += -I ./ 42 | INCLUDES += -I ../../rom/include 43 | INCLUDES += -I ../../include/ets 44 | PDIR := ../$(PDIR) 45 | sinclude $(PDIR)Makefile 46 | 47 | -------------------------------------------------------------------------------- /mqtt/ringbuf.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * Ring Buffer library 4 | */ 5 | 6 | #include "ringbuf.h" 7 | 8 | 9 | /** 10 | * \brief init a RINGBUF object 11 | * \param r pointer to a RINGBUF object 12 | * \param buf pointer to a byte array 13 | * \param size size of buf 14 | * \return 0 if successfull, otherwise failed 15 | */ 16 | I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size) 17 | { 18 | if(r == NULL || buf == NULL || size < 2) return -1; 19 | 20 | r->p_o = r->p_r = r->p_w = buf; 21 | r->fill_cnt = 0; 22 | r->size = size; 23 | 24 | return 0; 25 | } 26 | /** 27 | * \brief put a character into ring buffer 28 | * \param r pointer to a ringbuf object 29 | * \param c character to be put 30 | * \return 0 if successfull, otherwise failed 31 | */ 32 | I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c) 33 | { 34 | if(r->fill_cnt>=r->size)return -1; // ring buffer is full, this should be atomic operation 35 | 36 | 37 | r->fill_cnt++; // increase filled slots count, this should be atomic operation 38 | 39 | 40 | *r->p_w++ = c; // put character into buffer 41 | 42 | if(r->p_w >= r->p_o + r->size) // rollback if write pointer go pass 43 | r->p_w = r->p_o; // the physical boundary 44 | 45 | return 0; 46 | } 47 | /** 48 | * \brief get a character from ring buffer 49 | * \param r pointer to a ringbuf object 50 | * \param c read character 51 | * \return 0 if successfull, otherwise failed 52 | */ 53 | I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c) 54 | { 55 | if(r->fill_cnt<=0)return -1; // ring buffer is empty, this should be atomic operation 56 | 57 | 58 | r->fill_cnt--; // decrease filled slots count 59 | 60 | 61 | *c = *r->p_r++; // get the character out 62 | 63 | if(r->p_r >= r->p_o + r->size) // rollback if write pointer go pass 64 | r->p_r = r->p_o; // the physical boundary 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /mqtt/include/queue.h: -------------------------------------------------------------------------------- 1 | /* str_queue.h -- 2 | * 3 | * Copyright (c) 2014-2015, Tuan PM 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef USER_QUEUE_H_ 32 | #define USER_QUEUE_H_ 33 | #include "os_type.h" 34 | #include "ringbuf.h" 35 | typedef struct { 36 | uint8_t *buf; 37 | RINGBUF rb; 38 | } QUEUE; 39 | 40 | void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize); 41 | int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len); 42 | int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); 43 | BOOL ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue); 44 | #endif /* USER_QUEUE_H_ */ 45 | -------------------------------------------------------------------------------- /mqtt/queue.c: -------------------------------------------------------------------------------- 1 | /* str_queue.c 2 | * 3 | * Copyright (c) 2014-2015, Tuan PM 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | #include "queue.h" 31 | 32 | #include "user_interface.h" 33 | #include "osapi.h" 34 | #include "os_type.h" 35 | #include "mem.h" 36 | #include "proto.h" 37 | void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize) 38 | { 39 | queue->buf = (uint8_t*)os_zalloc(bufferSize); 40 | RINGBUF_Init(&queue->rb, queue->buf, bufferSize); 41 | } 42 | int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len) 43 | { 44 | return PROTO_AddRb(&queue->rb, buffer, len); 45 | } 46 | int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) 47 | { 48 | 49 | return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); 50 | } 51 | 52 | BOOL ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue) 53 | { 54 | if(queue->rb.fill_cnt<=0) 55 | return TRUE; 56 | return FALSE; 57 | } 58 | -------------------------------------------------------------------------------- /mqtt/wifi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * wifi.c 3 | * 4 | * Created on: Dec 30, 2014 5 | * Author: Minh 6 | */ 7 | #include "wifi.h" 8 | #include "user_interface.h" 9 | #include "osapi.h" 10 | #include "espconn.h" 11 | #include "os_type.h" 12 | #include "mem.h" 13 | #include "mqtt_msg.h" 14 | #include "debug.h" 15 | #include "user_config.h" 16 | 17 | static ETSTimer WiFiLinker; 18 | WifiCallback wifiCb = NULL; 19 | static uint8_t wifiStatus = STATION_IDLE, lastWifiStatus = STATION_IDLE; 20 | static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg) 21 | { 22 | struct ip_info ipConfig; 23 | 24 | os_timer_disarm(&WiFiLinker); 25 | wifi_get_ip_info(STATION_IF, &ipConfig); 26 | wifiStatus = wifi_station_get_connect_status(); 27 | if (wifiStatus == STATION_GOT_IP && ipConfig.ip.addr != 0) 28 | { 29 | 30 | os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); 31 | os_timer_arm(&WiFiLinker, 2000, 0); 32 | 33 | 34 | } 35 | else 36 | { 37 | if(wifi_station_get_connect_status() == STATION_WRONG_PASSWORD) 38 | { 39 | 40 | INFO("STATION_WRONG_PASSWORD\r\n"); 41 | wifi_station_connect(); 42 | 43 | 44 | } 45 | else if(wifi_station_get_connect_status() == STATION_NO_AP_FOUND) 46 | { 47 | 48 | INFO("STATION_NO_AP_FOUND\r\n"); 49 | wifi_station_connect(); 50 | 51 | 52 | } 53 | else if(wifi_station_get_connect_status() == STATION_CONNECT_FAIL) 54 | { 55 | 56 | INFO("STATION_CONNECT_FAIL\r\n"); 57 | wifi_station_connect(); 58 | 59 | } 60 | else 61 | { 62 | INFO("STATION_IDLE\r\n"); 63 | } 64 | 65 | os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); 66 | os_timer_arm(&WiFiLinker, 500, 0); 67 | } 68 | if(wifiStatus != lastWifiStatus){ 69 | lastWifiStatus = wifiStatus; 70 | if(wifiCb) 71 | wifiCb(wifiStatus); 72 | } 73 | } 74 | 75 | void WIFI_Connect(uint8_t* ssid, uint8_t* pass, WifiCallback cb) 76 | { 77 | struct station_config stationConf; 78 | 79 | INFO("WIFI_INIT\r\n"); 80 | wifi_set_opmode(STATION_MODE); 81 | wifi_station_set_auto_connect(FALSE); 82 | wifiCb = cb; 83 | 84 | os_memset(&stationConf, 0, sizeof(struct station_config)); 85 | 86 | os_sprintf(stationConf.ssid, "%s", ssid); 87 | os_sprintf(stationConf.password, "%s", pass); 88 | 89 | wifi_station_set_config(&stationConf); 90 | 91 | os_timer_disarm(&WiFiLinker); 92 | os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); 93 | os_timer_arm(&WiFiLinker, 1000, 0); 94 | 95 | wifi_station_set_auto_connect(TRUE); 96 | wifi_station_connect(); 97 | } 98 | 99 | -------------------------------------------------------------------------------- /include/driver/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef UART_APP_H 2 | #define UART_APP_H 3 | 4 | #include "uart_register.h" 5 | #include "eagle_soc.h" 6 | #include "c_types.h" 7 | 8 | #define RX_BUFF_SIZE 256 9 | #define TX_BUFF_SIZE 100 10 | #define UART0 0 11 | #define UART1 1 12 | 13 | typedef enum { 14 | FIVE_BITS = 0x0, 15 | SIX_BITS = 0x1, 16 | SEVEN_BITS = 0x2, 17 | EIGHT_BITS = 0x3 18 | } UartBitsNum4Char; 19 | 20 | typedef enum { 21 | ONE_STOP_BIT = 0, 22 | ONE_HALF_STOP_BIT = BIT2, 23 | TWO_STOP_BIT = BIT2 24 | } UartStopBitsNum; 25 | 26 | typedef enum { 27 | NONE_BITS = 0, 28 | ODD_BITS = 0, 29 | EVEN_BITS = BIT4 30 | } UartParityMode; 31 | 32 | typedef enum { 33 | STICK_PARITY_DIS = 0, 34 | STICK_PARITY_EN = BIT3 | BIT5 35 | } UartExistParity; 36 | 37 | typedef enum { 38 | BIT_RATE_9600 = 9600, 39 | BIT_RATE_19200 = 19200, 40 | BIT_RATE_38400 = 38400, 41 | BIT_RATE_57600 = 57600, 42 | BIT_RATE_74880 = 74880, 43 | BIT_RATE_115200 = 115200, 44 | BIT_RATE_230400 = 230400, 45 | BIT_RATE_256000 = 256000, 46 | BIT_RATE_460800 = 460800, 47 | BIT_RATE_921600 = 921600 48 | } UartBautRate; 49 | 50 | typedef enum { 51 | NONE_CTRL, 52 | HARDWARE_CTRL, 53 | XON_XOFF_CTRL 54 | } UartFlowCtrl; 55 | 56 | typedef enum { 57 | EMPTY, 58 | UNDER_WRITE, 59 | WRITE_OVER 60 | } RcvMsgBuffState; 61 | 62 | typedef struct { 63 | uint32 RcvBuffSize; 64 | uint8 *pRcvMsgBuff; 65 | uint8 *pWritePos; 66 | uint8 *pReadPos; 67 | uint8 TrigLvl; //JLU: may need to pad 68 | RcvMsgBuffState BuffState; 69 | } RcvMsgBuff; 70 | 71 | typedef struct { 72 | uint32 TrxBuffSize; 73 | uint8 *pTrxBuff; 74 | } TrxMsgBuff; 75 | 76 | typedef enum { 77 | BAUD_RATE_DET, 78 | WAIT_SYNC_FRM, 79 | SRCH_MSG_HEAD, 80 | RCV_MSG_BODY, 81 | RCV_ESC_CHAR, 82 | } RcvMsgState; 83 | 84 | typedef struct { 85 | UartBautRate baut_rate; 86 | UartBitsNum4Char data_bits; 87 | UartExistParity exist_parity; 88 | UartParityMode parity; // chip size in byte 89 | UartStopBitsNum stop_bits; 90 | UartFlowCtrl flow_ctrl; 91 | RcvMsgBuff rcv_buff; 92 | TrxMsgBuff trx_buff; 93 | RcvMsgState rcv_state; 94 | int received; 95 | int buff_uart_no; //indicate which uart use tx/rx buffer 96 | } UartDevice; 97 | 98 | void uart0_init(UartBautRate uart0_br); 99 | void uart0_sendStr(const char *str); 100 | #endif 101 | 102 | -------------------------------------------------------------------------------- /include/easygpio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * easygpio.h 3 | * 4 | * Copyright (c) 2015, eadf (https://github.com/eadf) 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef EASYGPIO_INCLUDE_EASYGPIO_EASYGPIO_H_ 33 | #define EASYGPIO_INCLUDE_EASYGPIO_EASYGPIO_H_ 34 | 35 | #include "c_types.h" 36 | 37 | typedef enum { 38 | EASYGPIO_INPUT=0, 39 | EASYGPIO_OUTPUT=1 40 | } EasyGPIO_PinMode; 41 | 42 | typedef enum { 43 | EASYGPIO_PULLDOWN=2, 44 | EASYGPIO_PULLUP=3, 45 | EASYGPIO_NOPULL=4 46 | } EasyGPIO_PullStatus; 47 | 48 | /** 49 | * Returns the gpio name and func for a specific pin. 50 | */ 51 | bool easygpio_getGPIONameFunc(uint8_t gpio_pin, uint32_t *gpio_name, uint8_t *gpio_func); 52 | 53 | /** 54 | * Sets the 'gpio_pin' pin as a GPIO and sets the interrupt to trigger on that pin 55 | */ 56 | bool easygpio_attachInterrupt(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus, void (*interruptHandler)(void)); 57 | 58 | /** 59 | * Deatach the interrupt handler from the 'gpio_pin' pin. 60 | */ 61 | bool easygpio_detachInterrupt(uint8_t gpio_pin); 62 | 63 | /** 64 | * Returns the number of active pins in the gpioMask. 65 | */ 66 | uint8_t easygpio_countBits(uint32_t gpioMask); 67 | 68 | /** 69 | * Sets the 'gpio_pin' pin as an input GPIO and sets the pull up and 70 | * pull down registers for that pin. 71 | */ 72 | bool easygpio_pinMode(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus, EasyGPIO_PinMode pinMode); 73 | 74 | /** 75 | * Sets the pull up and pull down registers for a pin. 76 | * 'pullUp' takes precedence over pullDown 77 | */ 78 | bool easygpio_pullMode(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus); 79 | 80 | 81 | #endif /* EASYGPIO_INCLUDE_EASYGPIO_EASYGPIO_H_ */ 82 | -------------------------------------------------------------------------------- /mqtt/proto.c: -------------------------------------------------------------------------------- 1 | #include "proto.h" 2 | #include "ringbuf.h" 3 | I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize) 4 | { 5 | parser->buf = buf; 6 | parser->bufSize = bufSize; 7 | parser->dataLen = 0; 8 | parser->callback = completeCallback; 9 | parser->isEsc = 0; 10 | return 0; 11 | } 12 | 13 | I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value) 14 | { 15 | switch(value){ 16 | case 0x7D: 17 | parser->isEsc = 1; 18 | break; 19 | 20 | case 0x7E: 21 | parser->dataLen = 0; 22 | parser->isEsc = 0; 23 | parser->isBegin = 1; 24 | break; 25 | 26 | case 0x7F: 27 | if (parser->callback != NULL) 28 | parser->callback(); 29 | parser->isBegin = 0; 30 | return 0; 31 | break; 32 | 33 | default: 34 | if(parser->isBegin == 0) break; 35 | 36 | if(parser->isEsc){ 37 | value ^= 0x20; 38 | parser->isEsc = 0; 39 | } 40 | 41 | if(parser->dataLen < parser->bufSize) 42 | parser->buf[parser->dataLen++] = value; 43 | 44 | break; 45 | } 46 | return -1; 47 | } 48 | 49 | I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len) 50 | { 51 | while(len--) 52 | PROTO_ParseByte(parser, *buf++); 53 | 54 | return 0; 55 | } 56 | I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, U8 *bufOut, U16* len, U16 maxBufLen) 57 | { 58 | U8 c; 59 | 60 | PROTO_PARSER proto; 61 | PROTO_Init(&proto, NULL, bufOut, maxBufLen); 62 | while(RINGBUF_Get(rb, &c) == 0){ 63 | if(PROTO_ParseByte(&proto, c) == 0){ 64 | *len = proto.dataLen; 65 | return 0; 66 | } 67 | } 68 | return -1; 69 | } 70 | I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize) 71 | { 72 | U16 i = 2; 73 | U16 len = *(U16*) packet; 74 | 75 | if (bufSize < 1) return -1; 76 | 77 | *buf++ = 0x7E; 78 | bufSize--; 79 | 80 | while (len--) { 81 | switch (*packet) { 82 | case 0x7D: 83 | case 0x7E: 84 | case 0x7F: 85 | if (bufSize < 2) return -1; 86 | *buf++ = 0x7D; 87 | *buf++ = *packet++ ^ 0x20; 88 | i += 2; 89 | bufSize -= 2; 90 | break; 91 | default: 92 | if (bufSize < 1) return -1; 93 | *buf++ = *packet++; 94 | i++; 95 | bufSize--; 96 | break; 97 | } 98 | } 99 | 100 | if (bufSize < 1) return -1; 101 | *buf++ = 0x7F; 102 | 103 | return i; 104 | } 105 | 106 | I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len) 107 | { 108 | U16 i = 2; 109 | if(RINGBUF_Put(rb, 0x7E) == -1) return -1; 110 | while (len--) { 111 | switch (*packet) { 112 | case 0x7D: 113 | case 0x7E: 114 | case 0x7F: 115 | if(RINGBUF_Put(rb, 0x7D) == -1) return -1; 116 | if(RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; 117 | i += 2; 118 | break; 119 | default: 120 | if(RINGBUF_Put(rb, *packet++) == -1) return -1; 121 | i++; 122 | break; 123 | } 124 | } 125 | if(RINGBUF_Put(rb, 0x7F) == -1) return -1; 126 | 127 | return i; 128 | } 129 | 130 | -------------------------------------------------------------------------------- /mqtt/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Tuan PM 3 | * Email: tuanpm@live.com 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | */ 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "utils.h" 39 | 40 | 41 | uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str) 42 | { 43 | uint8_t segs = 0; /* Segment count. */ 44 | uint8_t chcnt = 0; /* Character count within segment. */ 45 | uint8_t accum = 0; /* Accumulator for segment. */ 46 | /* Catch NULL pointer. */ 47 | if (str == 0) 48 | return 0; 49 | /* Process every character in string. */ 50 | 51 | while (*str != '\0') { 52 | /* Segment changeover. */ 53 | 54 | if (*str == '.') { 55 | /* Must have some digits in segment. */ 56 | if (chcnt == 0) 57 | return 0; 58 | /* Limit number of segments. */ 59 | if (++segs == 4) 60 | return 0; 61 | /* Reset segment values and restart loop. */ 62 | chcnt = accum = 0; 63 | str++; 64 | continue; 65 | } 66 | 67 | /* Check numeric. */ 68 | if ((*str < '0') || (*str > '9')) 69 | return 0; 70 | 71 | /* Accumulate and check segment. */ 72 | 73 | if ((accum = accum * 10 + *str - '0') > 255) 74 | return 0; 75 | /* Advance other segment specific stuff and continue loop. */ 76 | 77 | chcnt++; 78 | str++; 79 | } 80 | 81 | /* Check enough segments and enough characters in last segment. */ 82 | 83 | if (segs != 3) 84 | return 0; 85 | if (chcnt == 0) 86 | return 0; 87 | /* Address okay. */ 88 | 89 | return 1; 90 | } 91 | uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip) 92 | { 93 | 94 | /* The count of the number of bytes processed. */ 95 | int i; 96 | /* A pointer to the next digit to process. */ 97 | const char * start; 98 | 99 | start = str; 100 | for (i = 0; i < 4; i++) { 101 | /* The digit being processed. */ 102 | char c; 103 | /* The value of this byte. */ 104 | int n = 0; 105 | while (1) { 106 | c = * start; 107 | start++; 108 | if (c >= '0' && c <= '9') { 109 | n *= 10; 110 | n += c - '0'; 111 | } 112 | /* We insist on stopping at "." if we are still parsing 113 | the first, second, or third numbers. If we have reached 114 | the end of the numbers, we will allow any character. */ 115 | else if ((i < 3 && c == '.') || i == 3) { 116 | break; 117 | } 118 | else { 119 | return 0; 120 | } 121 | } 122 | if (n >= 256) { 123 | return 0; 124 | } 125 | ((uint8_t*)ip)[i] = n; 126 | } 127 | return 1; 128 | 129 | } 130 | uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s) 131 | { 132 | uint32_t value = 0, digit; 133 | int8_t c; 134 | 135 | while((c = *s++)){ 136 | if('0' <= c && c <= '9') 137 | digit = c - '0'; 138 | else if('A' <= c && c <= 'F') 139 | digit = c - 'A' + 10; 140 | else if('a' <= c && c<= 'f') 141 | digit = c - 'a' + 10; 142 | else break; 143 | 144 | value = (value << 4) | digit; 145 | } 146 | 147 | return value; 148 | } 149 | 150 | -------------------------------------------------------------------------------- /include/driver/uart_register.h: -------------------------------------------------------------------------------- 1 | //Generated at 2012-07-03 18:44:06 2 | /* 3 | * Copyright (c) 2010 - 2011 Espressif System 4 | * 5 | */ 6 | 7 | #ifndef UART_REGISTER_H_INCLUDED 8 | #define UART_REGISTER_H_INCLUDED 9 | #define REG_UART_BASE( i ) (0x60000000+(i)*0xf00) 10 | //version value:32'h062000 11 | 12 | #define UART_FIFO( i ) (REG_UART_BASE( i ) + 0x0) 13 | #define UART_RXFIFO_RD_BYTE 0x000000FF 14 | #define UART_RXFIFO_RD_BYTE_S 0 15 | 16 | #define UART_INT_RAW( i ) (REG_UART_BASE( i ) + 0x4) 17 | #define UART_RXFIFO_TOUT_INT_RAW (BIT(8)) 18 | #define UART_BRK_DET_INT_RAW (BIT(7)) 19 | #define UART_CTS_CHG_INT_RAW (BIT(6)) 20 | #define UART_DSR_CHG_INT_RAW (BIT(5)) 21 | #define UART_RXFIFO_OVF_INT_RAW (BIT(4)) 22 | #define UART_FRM_ERR_INT_RAW (BIT(3)) 23 | #define UART_PARITY_ERR_INT_RAW (BIT(2)) 24 | #define UART_TXFIFO_EMPTY_INT_RAW (BIT(1)) 25 | #define UART_RXFIFO_FULL_INT_RAW (BIT(0)) 26 | 27 | #define UART_INT_ST( i ) (REG_UART_BASE( i ) + 0x8) 28 | #define UART_RXFIFO_TOUT_INT_ST (BIT(8)) 29 | #define UART_BRK_DET_INT_ST (BIT(7)) 30 | #define UART_CTS_CHG_INT_ST (BIT(6)) 31 | #define UART_DSR_CHG_INT_ST (BIT(5)) 32 | #define UART_RXFIFO_OVF_INT_ST (BIT(4)) 33 | #define UART_FRM_ERR_INT_ST (BIT(3)) 34 | #define UART_PARITY_ERR_INT_ST (BIT(2)) 35 | #define UART_TXFIFO_EMPTY_INT_ST (BIT(1)) 36 | #define UART_RXFIFO_FULL_INT_ST (BIT(0)) 37 | 38 | #define UART_INT_ENA( i ) (REG_UART_BASE( i ) + 0xC) 39 | #define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) 40 | #define UART_BRK_DET_INT_ENA (BIT(7)) 41 | #define UART_CTS_CHG_INT_ENA (BIT(6)) 42 | #define UART_DSR_CHG_INT_ENA (BIT(5)) 43 | #define UART_RXFIFO_OVF_INT_ENA (BIT(4)) 44 | #define UART_FRM_ERR_INT_ENA (BIT(3)) 45 | #define UART_PARITY_ERR_INT_ENA (BIT(2)) 46 | #define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) 47 | #define UART_RXFIFO_FULL_INT_ENA (BIT(0)) 48 | 49 | #define UART_INT_CLR( i ) (REG_UART_BASE( i ) + 0x10) 50 | #define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) 51 | #define UART_BRK_DET_INT_CLR (BIT(7)) 52 | #define UART_CTS_CHG_INT_CLR (BIT(6)) 53 | #define UART_DSR_CHG_INT_CLR (BIT(5)) 54 | #define UART_RXFIFO_OVF_INT_CLR (BIT(4)) 55 | #define UART_FRM_ERR_INT_CLR (BIT(3)) 56 | #define UART_PARITY_ERR_INT_CLR (BIT(2)) 57 | #define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) 58 | #define UART_RXFIFO_FULL_INT_CLR (BIT(0)) 59 | 60 | #define UART_CLKDIV( i ) (REG_UART_BASE( i ) + 0x14) 61 | #define UART_CLKDIV_CNT 0x000FFFFF 62 | #define UART_CLKDIV_S 0 63 | 64 | #define UART_AUTOBAUD( i ) (REG_UART_BASE( i ) + 0x18) 65 | #define UART_GLITCH_FILT 0x000000FF 66 | #define UART_GLITCH_FILT_S 8 67 | #define UART_AUTOBAUD_EN (BIT(0)) 68 | 69 | #define UART_STATUS( i ) (REG_UART_BASE( i ) + 0x1C) 70 | #define UART_TXD (BIT(31)) 71 | #define UART_RTSN (BIT(30)) 72 | #define UART_DTRN (BIT(29)) 73 | #define UART_TXFIFO_CNT 0x000000FF 74 | #define UART_TXFIFO_CNT_S 16 75 | #define UART_RXD (BIT(15)) 76 | #define UART_CTSN (BIT(14)) 77 | #define UART_DSRN (BIT(13)) 78 | #define UART_RXFIFO_CNT 0x000000FF 79 | #define UART_RXFIFO_CNT_S 0 80 | 81 | #define UART_CONF0( i ) (REG_UART_BASE( i ) + 0x20) 82 | #define UART_TXFIFO_RST (BIT(18)) 83 | #define UART_RXFIFO_RST (BIT(17)) 84 | #define UART_IRDA_EN (BIT(16)) 85 | #define UART_TX_FLOW_EN (BIT(15)) 86 | #define UART_LOOPBACK (BIT(14)) 87 | #define UART_IRDA_RX_INV (BIT(13)) 88 | #define UART_IRDA_TX_INV (BIT(12)) 89 | #define UART_IRDA_WCTL (BIT(11)) 90 | #define UART_IRDA_TX_EN (BIT(10)) 91 | #define UART_IRDA_DPLX (BIT(9)) 92 | #define UART_TXD_BRK (BIT(8)) 93 | #define UART_SW_DTR (BIT(7)) 94 | #define UART_SW_RTS (BIT(6)) 95 | #define UART_STOP_BIT_NUM 0x00000003 96 | #define UART_STOP_BIT_NUM_S 4 97 | #define UART_BIT_NUM 0x00000003 98 | #define UART_BIT_NUM_S 2 99 | #define UART_PARITY_EN (BIT(1)) 100 | #define UART_PARITY (BIT(0)) 101 | 102 | #define UART_CONF1( i ) (REG_UART_BASE( i ) + 0x24) 103 | #define UART_RX_TOUT_EN (BIT(31)) 104 | #define UART_RX_TOUT_THRHD 0x0000007F 105 | #define UART_RX_TOUT_THRHD_S 24 106 | #define UART_RX_FLOW_EN (BIT(23)) 107 | #define UART_RX_FLOW_THRHD 0x0000007F 108 | #define UART_RX_FLOW_THRHD_S 16 109 | #define UART_TXFIFO_EMPTY_THRHD 0x0000007F 110 | #define UART_TXFIFO_EMPTY_THRHD_S 8 111 | #define UART_RXFIFO_FULL_THRHD 0x0000007F 112 | #define UART_RXFIFO_FULL_THRHD_S 0 113 | 114 | #define UART_LOWPULSE( i ) (REG_UART_BASE( i ) + 0x28) 115 | #define UART_LOWPULSE_MIN_CNT 0x000FFFFF 116 | #define UART_LOWPULSE_MIN_CNT_S 0 117 | 118 | #define UART_HIGHPULSE( i ) (REG_UART_BASE( i ) + 0x2C) 119 | #define UART_HIGHPULSE_MIN_CNT 0x000FFFFF 120 | #define UART_HIGHPULSE_MIN_CNT_S 0 121 | 122 | #define UART_PULSE_NUM( i ) (REG_UART_BASE( i ) + 0x30) 123 | #define UART_PULSE_NUM_CNT 0x0003FF 124 | #define UART_PULSE_NUM_CNT_S 0 125 | 126 | #define UART_DATE( i ) (REG_UART_BASE( i ) + 0x78) 127 | #define UART_ID( i ) (REG_UART_BASE( i ) + 0x7C) 128 | #endif // UART_REGISTER_H_INCLUDED 129 | -------------------------------------------------------------------------------- /mqtt/include/mqtt_msg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: mqtt_msg.h 3 | * Author: Minh Tuan 4 | * 5 | * Created on July 12, 2014, 1:05 PM 6 | */ 7 | 8 | #ifndef MQTT_MSG_H 9 | #define MQTT_MSG_H 10 | #include "c_types.h" 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | /* 16 | * Copyright (c) 2014, Stephen Robinson 17 | * All rights reserved. 18 | * 19 | * Redistribution and use in source and binary forms, with or without 20 | * modification, are permitted provided that the following conditions 21 | * are met: 22 | * 23 | * 1. Redistributions of source code must retain the above copyright 24 | * notice, this list of conditions and the following disclaimer. 25 | * 2. Redistributions in binary form must reproduce the above copyright 26 | * notice, this list of conditions and the following disclaimer in the 27 | * documentation and/or other materials provided with the distribution. 28 | * 3. Neither the name of the copyright holder nor the names of its 29 | * contributors may be used to endorse or promote products derived 30 | * from this software without specific prior written permission. 31 | * 32 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 33 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 36 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 37 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 38 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 39 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 40 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 41 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 42 | * POSSIBILITY OF SUCH DAMAGE. 43 | * 44 | */ 45 | /* 7 6 5 4 3 2 1 0*/ 46 | /*| --- Message Type---- | DUP Flag | QoS Level | Retain | 47 | /* Remaining Length */ 48 | 49 | 50 | enum mqtt_message_type 51 | { 52 | MQTT_MSG_TYPE_CONNECT = 1, 53 | MQTT_MSG_TYPE_CONNACK = 2, 54 | MQTT_MSG_TYPE_PUBLISH = 3, 55 | MQTT_MSG_TYPE_PUBACK = 4, 56 | MQTT_MSG_TYPE_PUBREC = 5, 57 | MQTT_MSG_TYPE_PUBREL = 6, 58 | MQTT_MSG_TYPE_PUBCOMP = 7, 59 | MQTT_MSG_TYPE_SUBSCRIBE = 8, 60 | MQTT_MSG_TYPE_SUBACK = 9, 61 | MQTT_MSG_TYPE_UNSUBSCRIBE = 10, 62 | MQTT_MSG_TYPE_UNSUBACK = 11, 63 | MQTT_MSG_TYPE_PINGREQ = 12, 64 | MQTT_MSG_TYPE_PINGRESP = 13, 65 | MQTT_MSG_TYPE_DISCONNECT = 14 66 | }; 67 | 68 | typedef struct mqtt_message 69 | { 70 | uint8_t* data; 71 | uint16_t length; 72 | 73 | } mqtt_message_t; 74 | 75 | typedef struct mqtt_connection 76 | { 77 | mqtt_message_t message; 78 | 79 | uint16_t message_id; 80 | uint8_t* buffer; 81 | uint16_t buffer_length; 82 | 83 | } mqtt_connection_t; 84 | 85 | typedef struct mqtt_connect_info 86 | { 87 | char* client_id; 88 | char* username; 89 | char* password; 90 | char* will_topic; 91 | char* will_message; 92 | int keepalive; 93 | int will_qos; 94 | int will_retain; 95 | int clean_session; 96 | 97 | } mqtt_connect_info_t; 98 | 99 | 100 | static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } 101 | static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } 102 | static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } 103 | static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } 104 | 105 | void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); 106 | int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length); 107 | const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); 108 | const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); 109 | uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length); 110 | 111 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); 112 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); 113 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); 114 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); 115 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); 116 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); 117 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); 118 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); 119 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection); 120 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection); 121 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection); 122 | 123 | 124 | #ifdef __cplusplus 125 | } 126 | #endif 127 | 128 | #endif /* MQTT_MSG_H */ 129 | 130 | -------------------------------------------------------------------------------- /mqtt/include/mqtt.h: -------------------------------------------------------------------------------- 1 | /* mqtt.h 2 | * 3 | * Copyright (c) 2014-2015, Tuan PM 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | #ifndef USER_AT_MQTT_H_ 31 | #define USER_AT_MQTT_H_ 32 | #include "mqtt_msg.h" 33 | #include "user_interface.h" 34 | 35 | #include "queue.h" 36 | typedef struct mqtt_event_data_t 37 | { 38 | uint8_t type; 39 | const char* topic; 40 | const char* data; 41 | uint16_t topic_length; 42 | uint16_t data_length; 43 | uint16_t data_offset; 44 | } mqtt_event_data_t; 45 | 46 | typedef struct mqtt_state_t 47 | { 48 | uint16_t port; 49 | int auto_reconnect; 50 | mqtt_connect_info_t* connect_info; 51 | uint8_t* in_buffer; 52 | uint8_t* out_buffer; 53 | int in_buffer_length; 54 | int out_buffer_length; 55 | uint16_t message_length; 56 | uint16_t message_length_read; 57 | mqtt_message_t* outbound_message; 58 | mqtt_connection_t mqtt_connection; 59 | uint16_t pending_msg_id; 60 | int pending_msg_type; 61 | int pending_publish_qos; 62 | } mqtt_state_t; 63 | 64 | typedef enum { 65 | WIFI_INIT, 66 | WIFI_CONNECTING, 67 | WIFI_CONNECTING_ERROR, 68 | WIFI_CONNECTED, 69 | DNS_RESOLVE, 70 | TCP_DISCONNECTED, 71 | TCP_RECONNECT_REQ, 72 | TCP_RECONNECT, 73 | TCP_CONNECTING, 74 | TCP_CONNECTING_ERROR, 75 | TCP_CONNECTED, 76 | MQTT_CONNECT_SEND, 77 | MQTT_CONNECT_SENDING, 78 | MQTT_SUBSCIBE_SEND, 79 | MQTT_SUBSCIBE_SENDING, 80 | MQTT_DATA, 81 | MQTT_PUBLISH_RECV, 82 | MQTT_PUBLISHING 83 | } tConnState; 84 | 85 | typedef void (*MqttCallback)(uint32_t *args); 86 | typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh); 87 | 88 | typedef struct { 89 | struct espconn *pCon; 90 | uint8_t security; 91 | uint8_t* host; 92 | uint32_t port; 93 | ip_addr_t ip; 94 | mqtt_state_t mqtt_state; 95 | mqtt_connect_info_t connect_info; 96 | MqttCallback connectedCb; 97 | MqttCallback disconnectedCb; 98 | MqttCallback publishedCb; 99 | MqttDataCallback dataCb; 100 | ETSTimer mqttTimer; 101 | uint32_t keepAliveTick; 102 | uint32_t reconnectTick; 103 | uint32_t sendTimeout; 104 | tConnState connState; 105 | QUEUE msgQueue; 106 | } MQTT_Client; 107 | 108 | #define SEC_NONSSL 0 109 | #define SEC_SSL 1 110 | 111 | #define MQTT_FLAG_CONNECTED 1 112 | #define MQTT_FLAG_READY 2 113 | #define MQTT_FLAG_EXIT 4 114 | 115 | #define MQTT_EVENT_TYPE_NONE 0 116 | #define MQTT_EVENT_TYPE_CONNECTED 1 117 | #define MQTT_EVENT_TYPE_DISCONNECTED 2 118 | #define MQTT_EVENT_TYPE_SUBSCRIBED 3 119 | #define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 120 | #define MQTT_EVENT_TYPE_PUBLISH 5 121 | #define MQTT_EVENT_TYPE_PUBLISHED 6 122 | #define MQTT_EVENT_TYPE_EXITED 7 123 | #define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 124 | 125 | void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security); 126 | void ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession); 127 | void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain); 128 | void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); 129 | void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); 130 | void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); 131 | void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); 132 | BOOL ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); 133 | void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient); 134 | void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient); 135 | BOOL ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain); 136 | 137 | #endif /* USER_AT_MQTT_H_ */ 138 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Changelog 2 | # Changed the variables to include the header file directory 3 | # Added global var for the XTENSA tool root 4 | # 5 | # This make file still needs some work. 6 | # 7 | # 8 | # Output directors to store intermediate compiled files 9 | # relative to the project directory 10 | BUILD_BASE = build 11 | FW_BASE = firmware 12 | 13 | # Base directory for the compiler 14 | XTENSA_TOOLS_ROOT ?= /opt/esp-open-sdk/xtensa-lx106-elf/bin 15 | 16 | # base directory of the ESP8266 SDK package, absolute 17 | SDK_BASE ?= /opt/esp-open-sdk/esp_iot_sdk_v1.0.0 18 | 19 | #Esptool.py path and port 20 | ESPTOOL ?= esptool.py 21 | ESPCFGPATCHER ?= espcfgpatcher.py 22 | ESPPORT ?= /dev/ttyUSB0 23 | 24 | # name for the target project 25 | TARGET = app 26 | 27 | # which modules (subdirectories) of the project to include in compiling 28 | MODULES = driver mqtt util user 29 | EXTRA_INCDIR = include $(SDK_BASE)/../include 30 | 31 | # libraries used in this project, mainly provided by the SDK 32 | LIBS = c gcc hal phy pp net80211 lwip wpa upgrade main ssl json 33 | 34 | # compiler flags using during compilation of source files 35 | CFLAGS = -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH 36 | 37 | # linker flags used to generate the main object file 38 | LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static 39 | 40 | # linker script used for the above linkier step 41 | LD_SCRIPT = eagle.app.v6.ld 42 | 43 | # various paths from the SDK used in this project 44 | SDK_LIBDIR = lib 45 | SDK_LDDIR = ld 46 | SDK_INCDIR = include include/json 47 | 48 | # we create two different files for uploading into the flash 49 | # these are the names and options to generate them 50 | FW_FILE_1 = 0x00000 51 | FW_FILE_1_ARGS = -bo $@ -bs .text -bs .data -bs .rodata -bc -ec 52 | FW_FILE_2 = 0x40000 53 | FW_FILE_2_ARGS = -es .irom0.text $@ -ec 54 | 55 | # select which tools to use as compiler, librarian and linker 56 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 57 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 58 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 59 | 60 | #### 61 | #### no user configurable options below here 62 | #### 63 | FW_TOOL ?= /usr/bin/esptool 64 | SRC_DIR := $(MODULES) 65 | BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) 66 | 67 | SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) 68 | SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) 69 | 70 | SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) 71 | OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) 72 | LIBS := $(addprefix -l,$(LIBS)) 73 | APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) 74 | TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) 75 | 76 | LD_SCRIPT := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT)) 77 | 78 | INCDIR := $(addprefix -I,$(SRC_DIR)) 79 | EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) 80 | MODULE_INCDIR := $(addsuffix /include,$(INCDIR)) 81 | 82 | FW_FILE_1 := $(addprefix $(FW_BASE)/,$(FW_FILE_1).bin) 83 | FW_FILE_2 := $(addprefix $(FW_BASE)/,$(FW_FILE_2).bin) 84 | BLANKER := $(addprefix $(SDK_BASE)/,bin/blank.bin) 85 | 86 | V ?= $(VERBOSE) 87 | ifeq ("$(V)","1") 88 | Q := 89 | vecho := @true 90 | else 91 | Q := @ 92 | vecho := @echo 93 | endif 94 | 95 | vpath %.c $(SRC_DIR) 96 | 97 | define compile-objects 98 | $1/%.o: %.c 99 | $(vecho) "CC $$<" 100 | $(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 101 | endef 102 | 103 | .PHONY: all checkdirs clean 104 | 105 | all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2) 106 | 107 | $(FW_FILE_1): $(TARGET_OUT) 108 | $(vecho) "FW $@" 109 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_1_ARGS) 110 | 111 | $(FW_FILE_2): $(TARGET_OUT) 112 | $(vecho) "FW $@" 113 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_2_ARGS) 114 | 115 | $(TARGET_OUT): $(APP_AR) 116 | $(vecho) "LD $@" 117 | $(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ 118 | 119 | $(APP_AR): $(OBJ) 120 | $(vecho) "AR $@" 121 | $(Q) $(AR) cru $@ $^ 122 | 123 | checkdirs: $(BUILD_DIR) $(FW_BASE) 124 | 125 | $(BUILD_DIR): 126 | $(Q) mkdir -p $@ 127 | 128 | firmware: 129 | $(Q) mkdir -p $@ 130 | 131 | cfgflash: firmware/0x00000.bin firmware/0x40000.bin my.cfg 132 | $(PYTHON) $(ESPCFGPATCHER) patch firmware/0x00000.bin my.cfg firmware/0x00000-patched.bin 133 | $(PYTHON) $(ESPTOOL) -p $(ESPPORT) write_flash 0x00000 firmware/0x00000-patched.bin 0x3C000 $(BLANKER) 0x40000 firmware/0x40000.bin 134 | 135 | cft: firmware/0x00000.bin firmware/0x40000.bin my.cfg 136 | -killall gtkterm 137 | xtensa-lx106-elf-objdump -t build/app.out >app.sym 138 | -dumpmem.sh build/app.out 139 | sleep 1 140 | $(PYTHON) $(ESPCFGPATCHER) patch firmware/0x00000.bin cft.cfg firmware/0x00000-patched.bin 141 | $(PYTHON) $(ESPTOOL) -p $(ESPPORT) write_flash 0x00000 firmware/0x00000-patched.bin 0x3C000 $(BLANKER) 0x40000 firmware/0x40000.bin 142 | sleep 1 143 | gtkterm & 144 | 145 | flash: firmware/0x00000.bin firmware/0x40000.bin 146 | $(PYTHON) $(ESPTOOL) -p $(ESPPORT) write_flash 0x00000 firmware/0x00000.bin 0x3C000 $(BLANKER) 0x40000 firmware/0x40000.bin 147 | 148 | test: flash 149 | screen $(ESPPORT) 115200 150 | 151 | clean: 152 | $(Q) rm -f $(APP_AR) 153 | $(Q) rm -f $(TARGET_OUT) 154 | $(Q) rm -rf $(BUILD_DIR) 155 | $(Q) rm -rf $(BUILD_BASE) 156 | $(Q) rm -f $(FW_FILE_1) 157 | $(Q) rm -f $(FW_FILE_2) 158 | $(Q) rm -rf $(FW_BASE) 159 | 160 | $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir)))) 161 | -------------------------------------------------------------------------------- /util/kvstore.c: -------------------------------------------------------------------------------- 1 | #include "ets_sys.h" 2 | #include "os_type.h" 3 | #include "mem.h" 4 | #include "osapi.h" 5 | #include "debug.h" 6 | #include "user_interface.h" 7 | #include "util.h" 8 | #include "kvstore.h" 9 | 10 | 11 | static uint8_t sig[4] = {0x55,0x00,0xFF,0xAA}; 12 | 13 | /* 14 | * Return the index of an entry in the sector 15 | * Returns MAX_ENTRIES if the key is not found 16 | */ 17 | 18 | 19 | LOCAL unsigned ICACHE_FLASH_ATTR 20 | kvstoreIndex(flash_handle_s *h, const char *key) 21 | { 22 | int i; 23 | if(!h){ 24 | INFO("Bad kvs handle for key %s!\r\n", key); 25 | return 0; 26 | } 27 | for(i = 0; i < KVS_MAX_ENTRIES; i++){ 28 | if(!strcmp(h->sector->entries[i].key, key)) 29 | break; 30 | } 31 | return i; 32 | } 33 | 34 | /* 35 | * Get a key's value from the store 36 | */ 37 | 38 | LOCAL char * ICACHE_FLASH_ATTR 39 | kvstoreGet(flash_handle_s *h, const char *key) 40 | { 41 | unsigned idx = kvstoreIndex(h, key); 42 | 43 | // If not in store, return 0 44 | if(KVS_MAX_ENTRIES == idx){ 45 | //INFO("KVS get no such key: %s\r\n", key); 46 | return NULL; 47 | } 48 | return h->sector->entries[idx].value; 49 | } 50 | 51 | 52 | /* 53 | * Read the contents of the flash sector into a buffer 54 | */ 55 | 56 | flash_handle_s * 57 | ICACHE_FLASH_ATTR kvstore_open(unsigned cfg_sector_loc) 58 | { 59 | 60 | 61 | // Allocate the memory for the handle struct 62 | flash_handle_s *h = (flash_handle_s *) os_zalloc(SPI_FLASH_SEC_SIZE); 63 | 64 | // Allocate the memory to hold the sector 65 | h->sector = (flash_sector_s *) os_zalloc(SPI_FLASH_SEC_SIZE); 66 | 67 | // Note location for future reference 68 | h->location = cfg_sector_loc; 69 | 70 | // Read it in 71 | spi_flash_read(cfg_sector_loc * SPI_FLASH_SEC_SIZE, 72 | (uint32 *) h->sector, SPI_FLASH_SEC_SIZE); 73 | // If the signature doesn't match we will need to init the sector 74 | if(os_memcmp(sig, h->sector->sig, sizeof(sig))){ 75 | INFO("Flash configuration area not initialized\r\n"); 76 | os_memset(h->sector, 0, SPI_FLASH_SEC_SIZE); 77 | os_memcpy(h->sector->sig, sig, sizeof(sig)); 78 | h->dirty = TRUE; 79 | 80 | } 81 | return h; 82 | 83 | } 84 | 85 | /* 86 | * Search the sector for an existing key 87 | */ 88 | 89 | bool ICACHE_FLASH_ATTR 90 | kvstore_exists(flash_handle_s *h, const char *key) 91 | { 92 | unsigned i = kvstoreIndex(h, key); 93 | if(KVS_MAX_ENTRIES == i) 94 | return FALSE; 95 | else 96 | return TRUE; 97 | } 98 | 99 | 100 | /* 101 | * Get a key's value from the store, allocate memory and copy it there. 102 | * Returned string must be freed after it is no longer needed. 103 | */ 104 | 105 | char * ICACHE_FLASH_ATTR 106 | kvstore_get_string(flash_handle_s *h, const char *key) 107 | { 108 | char *v; 109 | 110 | v = kvstoreGet(h, key); 111 | 112 | if(!v) 113 | return NULL; 114 | 115 | return util_strdup(v); 116 | } 117 | 118 | /* 119 | * Return the integer stored by a key 120 | */ 121 | 122 | int ICACHE_FLASH_ATTR 123 | kvstore_get_integer(flash_handle_s *h, const char *key, int *int_var) 124 | { 125 | char *dup, *v; 126 | 127 | v = kvstoreGet(h, key); 128 | INFO("KVS Value: %s\r\n", v); 129 | 130 | if(!v){ 131 | return FALSE; 132 | } 133 | *int_var = atoi(v); 134 | return TRUE; 135 | } 136 | 137 | 138 | 139 | /* 140 | * Put a key/value in the store. Return nonzero if success 141 | */ 142 | 143 | 144 | bool ICACHE_FLASH_ATTR 145 | kvstore_put(flash_handle_s *h, const char *key, const char *value) 146 | { 147 | unsigned idx = kvstoreIndex(h, key); 148 | 149 | // If not in store, return 0 150 | if(KVS_MAX_ENTRIES == idx){ 151 | // Find an unused entry 152 | for(idx = 0; idx < KVS_MAX_ENTRIES; idx++){ 153 | //os_printf("IDX: %d First byte: %02x\r\n", idx, h->sector->entries[idx].key[0]); 154 | if(!h->sector->entries[idx].key[0]) 155 | break; 156 | } 157 | if(KVS_MAX_ENTRIES == idx){ 158 | // Error: No room left 159 | INFO("KVS full!\r\n"); 160 | return FALSE; 161 | } 162 | } 163 | /* Store the new key */ 164 | os_strncpy(h->sector->entries[idx].key, key, KVS_MAX_KEY - 1); 165 | h->sector->entries[idx].key[KVS_MAX_KEY - 1] = 0; 166 | 167 | /* Store the new value */ 168 | os_strncpy(h->sector->entries[idx].value, value, KVS_MAX_VAL - 1); 169 | h->sector->entries[idx].key[KVS_MAX_VAL - 1] = 0; 170 | 171 | //INFO("Value supplied: %s, Stored value: %s\r\n", value, h->sector->entries[idx].value); 172 | 173 | /* Set the dirty flag */ 174 | h->dirty = TRUE; 175 | 176 | return TRUE; 177 | } 178 | 179 | /* 180 | * Flush the sector back to flash 181 | */ 182 | 183 | bool ICACHE_FLASH_ATTR 184 | kvstore_flush(flash_handle_s *h) 185 | { 186 | if(!h){ 187 | INFO("Bad KVS handle"); 188 | return FALSE; 189 | } 190 | if(h->dirty){ 191 | INFO("Flushing sector to flash\r\n"); 192 | spi_flash_erase_sector(h->location); 193 | spi_flash_write(h->location * SPI_FLASH_SEC_SIZE, 194 | (uint32 *) h->sector, SPI_FLASH_SEC_SIZE); 195 | h->dirty = FALSE; 196 | } 197 | return TRUE; 198 | } 199 | 200 | /* 201 | * Flush and free memory used to buffer sector 202 | */ 203 | 204 | bool ICACHE_FLASH_ATTR kvstore_close(flash_handle_s *h) 205 | { 206 | if(!kvstore_flush(h)) 207 | return FALSE; 208 | 209 | os_free(h->sector); 210 | os_free(h); 211 | return TRUE; 212 | } 213 | 214 | /* 215 | * Update or add a config key value 216 | */ 217 | 218 | bool ICACHE_FLASH_ATTR kvstore_update_number(flash_handle_s *h, const char *key, int new_val) 219 | { 220 | int res; 221 | char *valbuf = util_zalloc(16); 222 | 223 | if(!h){ 224 | util_free(valbuf); 225 | return FALSE; 226 | } 227 | 228 | os_sprintf(valbuf, "%d", new_val); 229 | 230 | res = kvstore_put(h, key, valbuf); 231 | 232 | kvstore_flush(h); 233 | 234 | util_free(valbuf); 235 | 236 | return res; 237 | } 238 | 239 | 240 | -------------------------------------------------------------------------------- /util/util.c: -------------------------------------------------------------------------------- 1 | #include "ets_sys.h" 2 | #include "os_type.h" 3 | #include "mem.h" 4 | #include "osapi.h" 5 | #include "user_interface.h" 6 | #include "jsonparse.h" 7 | #include "util.h" 8 | 9 | /* 10 | * Restart system 11 | */ 12 | 13 | void ICACHE_FLASH_ATTR util_restart(void) 14 | { 15 | os_printf("\r\nRestarting...\r\n"); 16 | ets_delay_us(250000); 17 | system_restart(); 18 | } 19 | 20 | 21 | /* 22 | * Assert error handler 23 | */ 24 | 25 | 26 | void ICACHE_FLASH_ATTR util_assert_handler(void) 27 | { 28 | util_restart(); 29 | } 30 | 31 | 32 | 33 | /* 34 | * util_str_realloc - return a new string pointer in a larger buffer 35 | */ 36 | 37 | char * ICACHE_FLASH_ATTR util_str_realloc(const char *p, size_t new_len) 38 | { 39 | size_t oldlen = strlen(p) + 1; 40 | util_assert(new_len > oldlen + 1, "New string smaller than original string. new length: %d, old length: %d", new_len, oldlen); 41 | char *n = util_zalloc(new_len); 42 | os_strcpy(n, p); 43 | util_free(p); 44 | return n; 45 | } 46 | 47 | 48 | 49 | /* 50 | * strdup function missing from os_* calls. 51 | */ 52 | 53 | char * ICACHE_FLASH_ATTR util_strdup(const char *s) 54 | { 55 | char *p = util_zalloc(os_strlen(s) + 1); 56 | os_strcpy(p, s); 57 | return p; 58 | } 59 | 60 | 61 | 62 | /* 63 | * strndup function missing from os_* calls. 64 | */ 65 | 66 | char * ICACHE_FLASH_ATTR util_strndup(const char *s, int len) 67 | { 68 | char *p = util_zalloc(len + 1); 69 | os_strncpy(p, s, len); 70 | p[len] = 0; 71 | return p; 72 | } 73 | 74 | 75 | 76 | 77 | 78 | /* 79 | * Split a string into substrings and return the result as a list of pointers 80 | * List of pointers should be 1 more than what is required. 81 | * max_list_length includes the NULL marker. 82 | * 83 | * Returned char pointer must be freed when no longer required. 84 | * 85 | * Does not remove white space 86 | */ 87 | char * ICACHE_FLASH_ATTR util_string_split(const char *in_str, char **list, char sep, int max_list_length) 88 | { 89 | int i, j; 90 | char *str = util_strdup(in_str); 91 | 92 | for(i = 0, j = 0; i < max_list_length - 1; i++){ 93 | 94 | // Skip leading seps 95 | while(sep == str[j]){ 96 | if(!str[j]) 97 | break; 98 | j++; 99 | } 100 | 101 | // Test for empty entry 102 | if(!str[j]) 103 | break; 104 | 105 | list[i] = str + j; // Save the beginning of the string 106 | while(str[j] && (str[j] != sep)) 107 | j++; 108 | // Test for end of string 109 | if(!str[j]){ 110 | i++; 111 | break; 112 | } 113 | str[j] = 0; // Terminate substring 114 | j++; 115 | } 116 | list[i] = NULL; // Terminate end of list 117 | return str; 118 | } 119 | 120 | /* 121 | * Allocate and make a subtopic string 122 | */ 123 | 124 | char * ICACHE_FLASH_ATTR util_make_sub_topic(const char *rootTopic, char *subTopic) 125 | { 126 | char *r = (char *) os_zalloc(strlen(rootTopic) + 127 | 2 + strlen(subTopic)); 128 | os_strcpy(r, rootTopic); 129 | os_strcat(r, "/"); 130 | os_strcat(r, subTopic); 131 | return r; 132 | } 133 | 134 | /* 135 | * Parse a json parameter from a parsed json string 136 | * Returns 0 if no name was found or matched. 137 | * Returns 1 if a name was matched, but no parameter was found 138 | * Returns 2 if a name was found and matched, and a parameter was found and matched. 139 | * If 2 is returned, the parameter will be copied into the paramvalue string up to the paramvaluesize 140 | */ 141 | 142 | int ICACHE_FLASH_ATTR util_parse_json_param(void *state, const char *paramname, char *paramvalue, int paramvaluesize) 143 | { 144 | int i = 0; 145 | int res; 146 | 147 | while((res = jsonparse_next((struct jsonparse_state *) state)) != 0){ 148 | if(res == 'N'){ 149 | if(!jsonparse_strcmp_value((struct jsonparse_state *) state, paramname)){ 150 | i++; 151 | } 152 | } 153 | if(i && (res == '"')){ 154 | i++; 155 | jsonparse_copy_value((struct jsonparse_state *) state, paramvalue, paramvaluesize); 156 | break; 157 | } 158 | } 159 | return i; 160 | } 161 | 162 | /* 163 | * Local function to to support util_parse_command_int and util_parse_command_qstring 164 | * Comares command and parses message looking for a parameter field. This can return -1 in addition to 165 | * 0,1, and 2 from parse_json_param. -1 indicates the command did not match. 166 | */ 167 | 168 | LOCAL int ICACHE_FLASH_ATTR processCommandParam(const char *commandrcvd, const char *command, const char *message, char *param, int paramsize) 169 | { 170 | struct jsonparse_state state; 171 | 172 | if(strcmp(command, commandrcvd)){ 173 | return -1; 174 | } 175 | param[0] = 0; 176 | jsonparse_setup(&state, message, os_strlen(message)); 177 | return util_parse_json_param(&state, "param", param, paramsize); 178 | 179 | } 180 | 181 | 182 | /* 183 | * Parse a command with a single numeric parameter 184 | * If the command is not matched, and the number is not present, FALSE is returned. 185 | */ 186 | 187 | bool ICACHE_FLASH_ATTR util_parse_command_int(const char *commandrcvd, const char *command, const char *message, int *val) 188 | { 189 | int res; 190 | char param[32]; 191 | struct jsonparse_state state; 192 | 193 | res = processCommandParam(commandrcvd, command, message, param, sizeof(param)); 194 | 195 | if(2 == res){ 196 | *val = atoi(param); 197 | return TRUE; 198 | } 199 | return FALSE; 200 | } 201 | 202 | /* 203 | * Parse a command with an optional string parameter 204 | * Returns FALSE if the command is not matched. 205 | * Sets val to NULL if no parameter found, else returns an allocated string pointing to the 206 | * value. This string must be freed when no longer required. 207 | */ 208 | 209 | bool ICACHE_FLASH_ATTR util_parse_command_qstring(const char *commandrcvd, const char *command, const char *message, char **val) 210 | { 211 | int res; 212 | char param[32]; 213 | struct jsonparse_state state; 214 | 215 | res = processCommandParam(commandrcvd, command, message, param, sizeof(param)); 216 | 217 | if(-1 == res) 218 | return FALSE; 219 | 220 | if(2 == res){ 221 | *val = util_strdup(param); 222 | } 223 | else{ 224 | *val = NULL; 225 | } 226 | return TRUE; 227 | 228 | } 229 | 230 | 231 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **esp-8266-MQTT-io-node** 2 | ========== 3 | This is an implementation of an MQTT Relay Node which runs natively on an ESP8266 ESP-03 module or other variant with enough free GPIO's. 4 | Additionally, a hardware design is now available using the ESP12 ESP8266 module. Please refer to my esp12-appliance-mod project. 5 | 6 | Code is compiled using the toolchain referenced below. 7 | 8 | **Features:** 9 | 10 | Provides one relay channel on one GPIO or one bistable latching relay channel using 2 GPIO's and one button channel. The button channel can be linked to the relay for local control or isolated for separate use. 11 | A GPIO can optionally be reserved for a connection state LED. GPIO ports are configurable in the source code. 12 | 13 | **Device Path** 14 | 15 | The device path encompasses subtopics command and status. Commands are sent to $devicepath/command (which the nodes subscribes to.) All status messages are 16 | published by the node on $devicepath/status except for the node configuration which is published on /node/info. The device path is set using the patching procedure described later. 17 | 18 | **Control Messages** 19 | 20 | Control messages are received by all nodes on /node/control. These are meant to be used to interrogate the nodes connected to the network, 21 | and perform other system-wide control functions. 22 | 23 | One control message is currently supported: *muster*. This directs the node to re-send the node configuration information to /node/info. See the power on message below for further details 24 | 25 | 26 | **Command Messages** 27 | 28 | Command messages are sent using JSON encoding as follows: 29 | 30 | {"command":"command from table below"} For commands without a parameter 31 | 32 | {"command":"$COMMAND","param","$PARAM"} For commands with a parameter 33 | 34 | Because of limitations with the Espressif JSON parser library, all numbers should be sent as text fields 35 | (i.e. quoted) 36 | 37 | MQTT commands supported: 38 | 39 | |Command| Description | 40 | |--------| ----------- | 41 | |on | Turns relay on| 42 | |off | Turns relay off| 43 | |pulse | Pulses relay for $PARAM milliseconds| 44 | |toggle | Toggles relay state| 45 | |query | Returns relay state| 46 | |survey | Returns WIFI survey information as seen by the node| 47 | |btlocal | $PARAM: 1 = link button to relay toggle, 0 = keep button separate| 48 | |ssid | Query or set SSID| 49 | |restart | Restart system| 50 | |wifipass| Query or set WIFI Password| 51 | |cycle | Start or stop relay cycling where $PARAM is the half cycle time in milliseconds 52 | |mqttdevpath| Query or set MQTT device path 53 | 54 | Notes: 55 | * $ indicates a variable. e.g.: $COMMAND would be one of the commands in the table above. 56 | * Sending an ssid, or wifi command without "parameter":"$PARAM" will return the current value. 57 | * ssid, wifipass, mqttdevpath change not effective until next system restart 58 | 59 | **Status Messages** 60 | 61 | These are JSON encoded as follows: 62 | 63 | Status messages which can be published: 64 | 65 | * {"buttonstate":"depressed"} 66 | * {"buttonstate":"released"} 67 | * {"relaystate":"on"} 68 | * {"relaystate":"off"} 69 | * WIFI survey data in the following format: {"access_points":["$AP":{"chan":"$CHAN","rssi":"$RSSI"}...]} 70 | 71 | **Power on Message** 72 | 73 | After booting, the node posts a JSON encoded "muster" message to /node/info with the following data: 74 | 75 | |Field | Description| 76 | |----- | -----------| 77 | |connstate | Connection state (online) 78 | |device | A device path (e.g. /home/lab/relay)| 79 | |ip4 | The IP address assigned to the node| 80 | |schema | A schema name of hwstar_relaynode (vendor_product)| 81 | |ssid | SSID utilized| 82 | 83 | 84 | The schema may be used to design a database of supported commands for each device. 85 | 86 | Here is an example: 87 | 88 | {"muster":{"connstate":"online","device":"/home/lab/relay","ip4":"$IP","schema":"hwstar_relaynode","ssid":"$SSID"}} 89 | 90 | **Last Will and Testament** 91 | 92 | The following will be published to /node/info if the node is not heard from by the MQTT broker: 93 | 94 | {"muster":{"connstate":"offline","device":"$DEVICE"}} 95 | 96 | Where $DEVICE is the configured device path 97 | 98 | **Configuration Patcher** 99 | 100 | WIFI and MQTT Configuration can optionally not be stored in the source files. It can be patched in using a custom Python utility which is available on my github account as 101 | a separate project: 102 | 103 | https://github.com/hwstar/ESP8266-MQTT-config-patcher 104 | 105 | Post patching allows the configuration to be changed without having sensitive information in the source files. 106 | 107 | NB:Post-patching is optional. You can just edit the user_main.c source file and hardcode the configuration. 108 | 109 | **Electrical Details** 110 | 111 | The code is configured to be used with an ESP module with 1 uncommitted I/O for standard mode and 2 GPIO's for bistable latching mode. GPIO12 is the default. It can be reconfigured, but use of GPIO2 is problematic as that pin needs to be 112 | high during boot, and that makes the electrical interface more complex. 113 | 114 | As mentioned above, a hardware design is available using the ESP12 module and a lastching relay. 115 | 116 | The relay GPIO outputs are low true to be compatible with the bootloader's initial pin states. 117 | (This prevents the relay from pulsing at power on). 118 | 119 | **Toolchain** 120 | 121 | Requires the ESP8266 toolchain be installed on the Linux system per the instructions available here: 122 | 123 | https://github.com/pfalcon/esp-open-sdk 124 | 125 | Toolchain should be installed in the /opt directory. Other directories will require Makefile modifications. 126 | 127 | NB:Current Makefile supports Linux build hosts only at this time. If someone wants to submit a working Makefile for Windows, I'd be happy to add it to the repository. 128 | 129 | **LICENSE - "MIT License"** 130 | 131 | Copyright (c) 2015 Stephen Rodgers 132 | Copyright (c) 2014-2015 Tuan PM, https://twitter.com/TuanPMT 133 | 134 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 135 | 136 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 137 | 138 | 139 | -------------------------------------------------------------------------------- /util/easygpio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * easygpio.c 3 | * 4 | * Copyright (c) 2015, eadf (https://github.com/eadf) 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "easygpio.h" 33 | #include "gpio.h" 34 | #include "osapi.h" 35 | #include "ets_sys.h" 36 | 37 | /** 38 | * Returns the number of active pins in the gpioMask. 39 | */ 40 | uint8_t ICACHE_FLASH_ATTR 41 | easygpio_countBits(uint32_t gpioMask) { 42 | 43 | uint8_t i=0; 44 | uint8_t numberOfPins=0; 45 | for (i=0; i<32; i++){ 46 | numberOfPins += (gpioMask & 1<= 17) { 58 | os_printf("easygpio_getGPIONameFunc Error: There is no GPIO%d, check your code\n", gpio_pin); 59 | return false; 60 | } 61 | if (gpio_pin == 16) { 62 | os_printf("easygpio_getGPIONameFunc Error: GPIO16 is not implemented\n"); 63 | return false; 64 | } 65 | switch ( gpio_pin ) { 66 | case 0: 67 | *gpio_func = FUNC_GPIO0; 68 | *gpio_name = PERIPHS_IO_MUX_GPIO0_U; 69 | return true; 70 | case 1: 71 | *gpio_func = FUNC_GPIO1; 72 | *gpio_name = PERIPHS_IO_MUX_U0TXD_U; 73 | return true; 74 | case 2: 75 | *gpio_func = FUNC_GPIO2; 76 | *gpio_name = PERIPHS_IO_MUX_GPIO2_U; 77 | return true; 78 | case 3: 79 | *gpio_func = FUNC_GPIO3; 80 | *gpio_name = PERIPHS_IO_MUX_U0RXD_U; 81 | return true; 82 | case 4: 83 | *gpio_func = FUNC_GPIO4; 84 | *gpio_name = PERIPHS_IO_MUX_GPIO4_U; 85 | return true; 86 | case 5: 87 | *gpio_func = FUNC_GPIO5; 88 | *gpio_name = PERIPHS_IO_MUX_GPIO5_U; 89 | return true; 90 | case 9: 91 | *gpio_func = FUNC_GPIO9; 92 | *gpio_name = PERIPHS_IO_MUX_SD_DATA2_U; 93 | return true; 94 | case 10: 95 | *gpio_func = FUNC_GPIO10; 96 | *gpio_name = PERIPHS_IO_MUX_SD_DATA3_U; 97 | return true; 98 | case 12: 99 | *gpio_func = FUNC_GPIO12; 100 | *gpio_name = PERIPHS_IO_MUX_MTDI_U; 101 | return true; 102 | case 13: 103 | *gpio_func = FUNC_GPIO13; 104 | *gpio_name = PERIPHS_IO_MUX_MTCK_U; 105 | return true; 106 | case 14: 107 | *gpio_func = FUNC_GPIO14; 108 | *gpio_name = PERIPHS_IO_MUX_MTMS_U; 109 | return true; 110 | case 15: 111 | *gpio_func = FUNC_GPIO15; 112 | *gpio_name = PERIPHS_IO_MUX_MTDO_U; 113 | return true; 114 | default: 115 | return false; 116 | } 117 | return true; 118 | } 119 | 120 | /** 121 | * Sets the pull up and pull down registers for a pin. 122 | * 'pullUp' takes precedence over pullDown 123 | */ 124 | static void ICACHE_FLASH_ATTR 125 | easygpio_setupPullsByName(uint32_t gpio_name, EasyGPIO_PullStatus pullStatus) { 126 | 127 | if (EASYGPIO_PULLUP == pullStatus){ 128 | PIN_PULLDWN_DIS(gpio_name); 129 | PIN_PULLUP_EN(gpio_name); 130 | } else if (EASYGPIO_PULLDOWN == pullStatus){ 131 | PIN_PULLUP_DIS(gpio_name); 132 | PIN_PULLDWN_EN(gpio_name); 133 | } else { 134 | PIN_PULLDWN_DIS(gpio_name); 135 | PIN_PULLUP_DIS(gpio_name); 136 | } 137 | } 138 | 139 | /** 140 | * Sets the pull up and pull down registers for a pin. 141 | * 'pullUp' takes precedence over pullDown 142 | */ 143 | bool ICACHE_FLASH_ATTR 144 | easygpio_pullMode(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus) { 145 | uint32_t gpio_name; 146 | uint8_t gpio_func; 147 | 148 | if (!easygpio_getGPIONameFunc(gpio_pin, &gpio_name, &gpio_func) ) { 149 | return false; 150 | } 151 | 152 | easygpio_setupPullsByName(gpio_name, pullStatus); 153 | return true; 154 | } 155 | 156 | /** 157 | * Sets the 'gpio_pin' pin as an input GPIO and sets the pull up and 158 | * pull down registers for that pin. 159 | */ 160 | bool ICACHE_FLASH_ATTR 161 | easygpio_pinMode(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus, EasyGPIO_PinMode pinMode) { 162 | uint32_t gpio_name; 163 | uint8_t gpio_func; 164 | 165 | if (!easygpio_getGPIONameFunc(gpio_pin, &gpio_name, &gpio_func) ) { 166 | return false; 167 | } 168 | 169 | PIN_FUNC_SELECT(gpio_name, gpio_func); 170 | easygpio_setupPullsByName(gpio_name, pullStatus); 171 | 172 | if (EASYGPIO_OUTPUT != pinMode) { 173 | GPIO_DIS_OUTPUT(gpio_pin); 174 | } 175 | return true; 176 | } 177 | 178 | /** 179 | * Sets the 'gpio_pin' pin as a GPIO and sets the interrupt to trigger on that pin 180 | */ 181 | bool ICACHE_FLASH_ATTR 182 | easygpio_attachInterrupt(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus, void (*interruptHandler)(void)) { 183 | uint32_t gpio_name; 184 | uint8_t gpio_func; 185 | 186 | if (gpio_pin == 16) { 187 | os_printf("easygpio_setupInterrupt Error: GPIO16 does not have interrupts\n"); 188 | return false; 189 | } 190 | if (!easygpio_getGPIONameFunc(gpio_pin, &gpio_name, &gpio_func) ) { 191 | return false; 192 | } 193 | 194 | ETS_GPIO_INTR_ATTACH(interruptHandler, NULL); 195 | ETS_GPIO_INTR_DISABLE(); 196 | 197 | PIN_FUNC_SELECT(gpio_name, gpio_func); 198 | 199 | easygpio_setupPullsByName(gpio_name, pullStatus); 200 | 201 | // disable output 202 | GPIO_DIS_OUTPUT(gpio_pin); 203 | 204 | gpio_register_set(GPIO_PIN_ADDR(gpio_pin), GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) 205 | | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) 206 | | GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE)); 207 | 208 | //clear gpio14 status 209 | GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(gpio_pin)); 210 | ETS_GPIO_INTR_ENABLE(); 211 | 212 | return true; 213 | } 214 | 215 | /** 216 | * Detach the interrupt handler from the 'gpio_pin' pin. 217 | */ 218 | bool ICACHE_FLASH_ATTR 219 | easygpio_detachInterrupt(uint8_t gpio_pin) { 220 | 221 | if (gpio_pin == 16) { 222 | os_printf("easygpio_setupInterrupt Error: GPIO16 does not have interrupts\n"); 223 | return false; 224 | } 225 | 226 | // Don't know how to detach interrupt, yet. 227 | // Quick and dirty fix - just disable the interrupt 228 | gpio_pin_intr_state_set(GPIO_ID_PIN(gpio_pin), GPIO_PIN_INTR_DISABLE); 229 | return true; 230 | } 231 | 232 | -------------------------------------------------------------------------------- /driver/uart.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright 2013-2014 Espressif Systems (Wuxi) 3 | * 4 | * FileName: uart.c 5 | * 6 | * Description: Two UART mode configration and interrupt handler. 7 | * Check your hardware connection while use this mode. 8 | * 9 | * Modification history: 10 | * 2014/3/12, v1.0 create this file. 11 | *******************************************************************************/ 12 | #include "ets_sys.h" 13 | #include "osapi.h" 14 | #include "driver/uart.h" 15 | #include "osapi.h" 16 | #include "driver/uart_register.h" 17 | //#include "ssc.h" 18 | 19 | 20 | // UartDev is defined and initialized in rom code. 21 | extern UartDevice UartDev; 22 | //extern os_event_t at_recvTaskQueue[at_recvTaskQueueLen]; 23 | 24 | LOCAL void uart0_rx_intr_handler(void *para); 25 | 26 | /****************************************************************************** 27 | * FunctionName : uart_config 28 | * Description : Internal used function 29 | * UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled 30 | * UART1 just used for debug output 31 | * Parameters : uart_no, use UART0 or UART1 defined ahead 32 | * Returns : NONE 33 | *******************************************************************************/ 34 | LOCAL void ICACHE_FLASH_ATTR 35 | uart_config(uint8 uart_no) 36 | { 37 | if (uart_no == UART1) 38 | { 39 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); 40 | } 41 | else 42 | { 43 | /* rcv_buff size if 0x100 */ 44 | ETS_UART_INTR_ATTACH(uart0_rx_intr_handler, &(UartDev.rcv_buff)); 45 | PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); 46 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); 47 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS); 48 | } 49 | 50 | uart_div_modify(uart_no, UART_CLK_FREQ / (UartDev.baut_rate)); 51 | 52 | WRITE_PERI_REG(UART_CONF0(uart_no), UartDev.exist_parity 53 | | UartDev.parity 54 | | (UartDev.stop_bits << UART_STOP_BIT_NUM_S) 55 | | (UartDev.data_bits << UART_BIT_NUM_S)); 56 | 57 | //clear rx and tx fifo,not ready 58 | SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); 59 | CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); 60 | 61 | //set rx fifo trigger 62 | // WRITE_PERI_REG(UART_CONF1(uart_no), 63 | // ((UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | 64 | // ((96 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S) | 65 | // UART_RX_FLOW_EN); 66 | if (uart_no == UART0) 67 | { 68 | //set rx fifo trigger 69 | WRITE_PERI_REG(UART_CONF1(uart_no), 70 | ((0x10 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | 71 | ((0x10 & UART_RX_FLOW_THRHD) << UART_RX_FLOW_THRHD_S) | 72 | UART_RX_FLOW_EN | 73 | (0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S | 74 | UART_RX_TOUT_EN); 75 | SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_TOUT_INT_ENA | 76 | UART_FRM_ERR_INT_ENA); 77 | } 78 | else 79 | { 80 | WRITE_PERI_REG(UART_CONF1(uart_no), 81 | ((UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S)); 82 | } 83 | 84 | //clear all interrupt 85 | WRITE_PERI_REG(UART_INT_CLR(uart_no), 0xffff); 86 | //enable rx_interrupt 87 | SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA); 88 | } 89 | 90 | /****************************************************************************** 91 | * FunctionName : uart1_tx_one_char 92 | * Description : Internal used function 93 | * Use uart1 interface to transfer one char 94 | * Parameters : uint8 TxChar - character to tx 95 | * Returns : OK 96 | *******************************************************************************/ 97 | LOCAL STATUS 98 | uart_tx_one_char(uint8 uart, uint8 TxChar) 99 | { 100 | while (true) 101 | { 102 | uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT<> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) { 104 | break; 105 | } 106 | } 107 | 108 | WRITE_PERI_REG(UART_FIFO(uart) , TxChar); 109 | return OK; 110 | } 111 | 112 | /****************************************************************************** 113 | * FunctionName : uart1_write_char 114 | * Description : Internal used function 115 | * Do some special deal while tx char is '\r' or '\n' 116 | * Parameters : char c - character to tx 117 | * Returns : NONE 118 | *******************************************************************************/ 119 | void ICACHE_FLASH_ATTR 120 | uart1_write_char(char c) 121 | { 122 | if (c == '\n') 123 | { 124 | uart_tx_one_char(UART1, '\r'); 125 | uart_tx_one_char(UART1, '\n'); 126 | } 127 | else if (c == '\r') 128 | { 129 | } 130 | else 131 | { 132 | uart_tx_one_char(UART1, c); 133 | } 134 | } 135 | 136 | void ICACHE_FLASH_ATTR 137 | uart0_write_char(char c) 138 | { 139 | if (c == '\n') 140 | { 141 | uart_tx_one_char(UART0, '\r'); 142 | uart_tx_one_char(UART0, '\n'); 143 | } 144 | else if (c == '\r') 145 | { 146 | } 147 | else 148 | { 149 | uart_tx_one_char(UART0, c); 150 | } 151 | } 152 | /****************************************************************************** 153 | * FunctionName : uart0_tx_buffer 154 | * Description : use uart0 to transfer buffer 155 | * Parameters : uint8 *buf - point to send buffer 156 | * uint16 len - buffer len 157 | * Returns : 158 | *******************************************************************************/ 159 | void ICACHE_FLASH_ATTR 160 | uart0_tx_buffer(uint8 *buf, uint16 len) 161 | { 162 | uint16 i; 163 | 164 | for (i = 0; i < len; i++) 165 | { 166 | uart_tx_one_char(UART0, buf[i]); 167 | } 168 | } 169 | 170 | /****************************************************************************** 171 | * FunctionName : uart0_sendStr 172 | * Description : use uart0 to transfer buffer 173 | * Parameters : uint8 *buf - point to send buffer 174 | * uint16 len - buffer len 175 | * Returns : 176 | *******************************************************************************/ 177 | void ICACHE_FLASH_ATTR 178 | uart0_sendStr(const char *str) 179 | { 180 | while(*str) 181 | { 182 | uart_tx_one_char(UART0, *str++); 183 | } 184 | } 185 | 186 | /****************************************************************************** 187 | * FunctionName : uart0_rx_intr_handler 188 | * Description : Internal used function 189 | * UART0 interrupt handler, add self handle code inside 190 | * Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg 191 | * Returns : NONE 192 | *******************************************************************************/ 193 | //extern void at_recvTask(void); 194 | 195 | LOCAL void 196 | uart0_rx_intr_handler(void *para) 197 | { 198 | /* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents 199 | * uart1 and uart0 respectively 200 | */ 201 | // RcvMsgBuff *pRxBuff = (RcvMsgBuff *)para; 202 | uint8 RcvChar; 203 | uint8 uart_no = UART0;//UartDev.buff_uart_no; 204 | 205 | // if (UART_RXFIFO_FULL_INT_ST != (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)) 206 | // { 207 | // return; 208 | // } 209 | // if (UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)) 210 | // { 211 | //// at_recvTask(); 212 | // RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; 213 | // system_os_post(at_recvTaskPrio, NULL, RcvChar); 214 | // WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_FULL_INT_CLR); 215 | // } 216 | if(UART_FRM_ERR_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_FRM_ERR_INT_ST)) 217 | { 218 | os_printf("FRM_ERR\r\n"); 219 | WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR); 220 | } 221 | 222 | if(UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)) 223 | { 224 | // os_printf("fifo full\r\n"); 225 | ETS_UART_INTR_DISABLE();///////// 226 | 227 | //system_os_post(at_recvTaskPrio, 0, 0); 228 | 229 | // WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_FULL_INT_CLR); 230 | // while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) 231 | // { 232 | //// at_recvTask(); 233 | // RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; 234 | // system_os_post(at_recvTaskPrio, NULL, RcvChar); 235 | // } 236 | } 237 | else if(UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST)) 238 | { 239 | ETS_UART_INTR_DISABLE();///////// 240 | 241 | //system_os_post(at_recvTaskPrio, 0, 0); 242 | 243 | // WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_TOUT_INT_CLR); 244 | //// os_printf("rx time over\r\n"); 245 | // while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) 246 | // { 247 | //// os_printf("process recv\r\n"); 248 | //// at_recvTask(); 249 | // RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; 250 | // system_os_post(at_recvTaskPrio, NULL, RcvChar); 251 | // } 252 | } 253 | 254 | // WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_FULL_INT_CLR); 255 | 256 | // if (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) 257 | // { 258 | // RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; 259 | // at_recvTask(); 260 | // *(pRxBuff->pWritePos) = RcvChar; 261 | 262 | // system_os_post(at_recvTaskPrio, NULL, RcvChar); 263 | 264 | // //insert here for get one command line from uart 265 | // if (RcvChar == '\r') 266 | // { 267 | // pRxBuff->BuffState = WRITE_OVER; 268 | // } 269 | // 270 | // pRxBuff->pWritePos++; 271 | // 272 | // if (pRxBuff->pWritePos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) 273 | // { 274 | // // overflow ...we may need more error handle here. 275 | // pRxBuff->pWritePos = pRxBuff->pRcvMsgBuff ; 276 | // } 277 | // } 278 | } 279 | 280 | /****************************************************************************** 281 | * FunctionName : uart0_init 282 | * Description : user interface for init uart 283 | * Parameters : UartBautRate uart0_br - uart0 bautrate 284 | * Returns : NONE 285 | *******************************************************************************/ 286 | void ICACHE_FLASH_ATTR 287 | uart0_init(UartBautRate uart0_br) 288 | { 289 | // rom use 74880 baut_rate, here reinitialize 290 | UartDev.baut_rate = uart0_br; 291 | uart_config(UART0); 292 | ETS_UART_INTR_ENABLE(); 293 | 294 | // install uart0 putc callback 295 | os_install_putc1((void *)uart0_write_char); 296 | } 297 | 298 | void ICACHE_FLASH_ATTR 299 | uart_reattach() 300 | { 301 | uart0_init(BIT_RATE_74880); 302 | // ETS_UART_INTR_ATTACH(uart_rx_intr_handler_ssc, &(UartDev.rcv_buff)); 303 | // ETS_UART_INTR_ENABLE(); 304 | } 305 | -------------------------------------------------------------------------------- /mqtt/mqtt_msg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Stephen Robinson 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the copyright holder nor the names of its 15 | * contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | #include 33 | #include "mqtt_msg.h" 34 | #include "user_config.h" 35 | #define MQTT_MAX_FIXED_HEADER_SIZE 3 36 | 37 | enum mqtt_connect_flag 38 | { 39 | MQTT_CONNECT_FLAG_USERNAME = 1 << 7, 40 | MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, 41 | MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, 42 | MQTT_CONNECT_FLAG_WILL = 1 << 2, 43 | MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 44 | }; 45 | 46 | struct __attribute((__packed__)) mqtt_connect_variable_header 47 | { 48 | uint8_t lengthMsb; 49 | uint8_t lengthLsb; 50 | #if defined(PROTOCOL_NAMEv31) 51 | uint8_t magic[6]; 52 | #elif defined(PROTOCOL_NAMEv311) 53 | uint8_t magic[4]; 54 | #else 55 | #error "Please define protocol name" 56 | #endif 57 | uint8_t version; 58 | uint8_t flags; 59 | uint8_t keepaliveMsb; 60 | uint8_t keepaliveLsb; 61 | }; 62 | 63 | static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len) 64 | { 65 | if(connection->message.length + len + 2 > connection->buffer_length) 66 | return -1; 67 | 68 | connection->buffer[connection->message.length++] = len >> 8; 69 | connection->buffer[connection->message.length++] = len & 0xff; 70 | memcpy(connection->buffer + connection->message.length, string, len); 71 | connection->message.length += len; 72 | 73 | return len + 2; 74 | } 75 | 76 | static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id) 77 | { 78 | // If message_id is zero then we should assign one, otherwise 79 | // we'll use the one supplied by the caller 80 | while(message_id == 0) 81 | message_id = ++connection->message_id; 82 | 83 | if(connection->message.length + 2 > connection->buffer_length) 84 | return 0; 85 | 86 | connection->buffer[connection->message.length++] = message_id >> 8; 87 | connection->buffer[connection->message.length++] = message_id & 0xff; 88 | 89 | return message_id; 90 | } 91 | 92 | static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection) 93 | { 94 | connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; 95 | return MQTT_MAX_FIXED_HEADER_SIZE; 96 | } 97 | 98 | static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection) 99 | { 100 | connection->message.data = connection->buffer; 101 | connection->message.length = 0; 102 | return &connection->message; 103 | } 104 | 105 | static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) 106 | { 107 | int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; 108 | 109 | if(remaining_length > 127) 110 | { 111 | connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); 112 | connection->buffer[1] = 0x80 | (remaining_length % 128); 113 | connection->buffer[2] = remaining_length / 128; 114 | connection->message.length = remaining_length + 3; 115 | connection->message.data = connection->buffer; 116 | } 117 | else 118 | { 119 | connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); 120 | connection->buffer[2] = remaining_length; 121 | connection->message.length = remaining_length + 2; 122 | connection->message.data = connection->buffer + 1; 123 | } 124 | 125 | return &connection->message; 126 | } 127 | 128 | void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) 129 | { 130 | memset(connection, 0, sizeof(connection)); 131 | connection->buffer = buffer; 132 | connection->buffer_length = buffer_length; 133 | } 134 | 135 | int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length) 136 | { 137 | int i; 138 | int totlen = 0; 139 | 140 | for(i = 1; i < length; ++i) 141 | { 142 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 143 | if((buffer[i] & 0x80) == 0) 144 | { 145 | ++i; 146 | break; 147 | } 148 | } 149 | totlen += i; 150 | 151 | return totlen; 152 | } 153 | 154 | const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) 155 | { 156 | int i; 157 | int totlen = 0; 158 | int topiclen; 159 | 160 | for(i = 1; i < *length; ++i) 161 | { 162 | totlen += (buffer[i] & 0x7f) << (7 * (i -1)); 163 | if((buffer[i] & 0x80) == 0) 164 | { 165 | ++i; 166 | break; 167 | } 168 | } 169 | totlen += i; 170 | 171 | if(i + 2 >= *length) 172 | return NULL; 173 | topiclen = buffer[i++] << 8; 174 | topiclen |= buffer[i++]; 175 | 176 | if(i + topiclen > *length) 177 | return NULL; 178 | 179 | *length = topiclen; 180 | return (const char*)(buffer + i); 181 | } 182 | 183 | const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) 184 | { 185 | int i; 186 | int totlen = 0; 187 | int topiclen; 188 | 189 | for(i = 1; i < *length; ++i) 190 | { 191 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 192 | if((buffer[i] & 0x80) == 0) 193 | { 194 | ++i; 195 | break; 196 | } 197 | } 198 | totlen += i; 199 | 200 | if(i + 2 >= *length) 201 | return NULL; 202 | topiclen = buffer[i++] << 8; 203 | topiclen |= buffer[i++]; 204 | 205 | if(i + topiclen >= *length){ 206 | *length = 0; 207 | return NULL; 208 | } 209 | i += topiclen; 210 | 211 | if(mqtt_get_qos(buffer) > 0) 212 | { 213 | if(i + 2 >= *length) 214 | return NULL; 215 | i += 2; 216 | } 217 | 218 | if(totlen < i) 219 | return NULL; 220 | 221 | if(totlen <= *length) 222 | *length = totlen - i; 223 | else 224 | *length = *length - i; 225 | return (const char*)(buffer + i); 226 | } 227 | 228 | uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length) 229 | { 230 | if(length < 1) 231 | return 0; 232 | 233 | switch(mqtt_get_type(buffer)) 234 | { 235 | case MQTT_MSG_TYPE_PUBLISH: 236 | { 237 | int i; 238 | int topiclen; 239 | 240 | for(i = 1; i < length; ++i) 241 | { 242 | if((buffer[i] & 0x80) == 0) 243 | { 244 | ++i; 245 | break; 246 | } 247 | } 248 | 249 | if(i + 2 >= length) 250 | return 0; 251 | topiclen = buffer[i++] << 8; 252 | topiclen |= buffer[i++]; 253 | 254 | if(i + topiclen >= length) 255 | return 0; 256 | i += topiclen; 257 | 258 | if(mqtt_get_qos(buffer) > 0) 259 | { 260 | if(i + 2 >= length) 261 | return 0; 262 | //i += 2; 263 | } else { 264 | return 0; 265 | } 266 | 267 | return (buffer[i] << 8) | buffer[i + 1]; 268 | } 269 | case MQTT_MSG_TYPE_PUBACK: 270 | case MQTT_MSG_TYPE_PUBREC: 271 | case MQTT_MSG_TYPE_PUBREL: 272 | case MQTT_MSG_TYPE_PUBCOMP: 273 | case MQTT_MSG_TYPE_SUBACK: 274 | case MQTT_MSG_TYPE_UNSUBACK: 275 | case MQTT_MSG_TYPE_SUBSCRIBE: 276 | { 277 | // This requires the remaining length to be encoded in 1 byte, 278 | // which it should be. 279 | if(length >= 4 && (buffer[1] & 0x80) == 0) 280 | return (buffer[2] << 8) | buffer[3]; 281 | else 282 | return 0; 283 | } 284 | 285 | default: 286 | return 0; 287 | } 288 | } 289 | 290 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) 291 | { 292 | struct mqtt_connect_variable_header* variable_header; 293 | 294 | init_message(connection); 295 | 296 | if(connection->message.length + sizeof(*variable_header) > connection->buffer_length) 297 | return fail_message(connection); 298 | variable_header = (void*)(connection->buffer + connection->message.length); 299 | connection->message.length += sizeof(*variable_header); 300 | 301 | variable_header->lengthMsb = 0; 302 | #if defined(PROTOCOL_NAMEv31) 303 | variable_header->lengthLsb = 6; 304 | memcpy(variable_header->magic, "MQIsdp", 6); 305 | variable_header->version = 3; 306 | #elif defined(PROTOCOL_NAMEv311) 307 | variable_header->lengthLsb = 4; 308 | memcpy(variable_header->magic, "MQTT", 4); 309 | variable_header->version = 4; 310 | #else 311 | #error "Please define protocol name" 312 | #endif 313 | 314 | variable_header->flags = 0; 315 | variable_header->keepaliveMsb = info->keepalive >> 8; 316 | variable_header->keepaliveLsb = info->keepalive & 0xff; 317 | 318 | if(info->clean_session) 319 | variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; 320 | 321 | if(info->client_id != NULL && info->client_id[0] != '\0') 322 | { 323 | if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) 324 | return fail_message(connection); 325 | } 326 | else 327 | return fail_message(connection); 328 | 329 | if(info->will_topic != NULL && info->will_topic[0] != '\0') 330 | { 331 | if(append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) 332 | return fail_message(connection); 333 | 334 | if(append_string(connection, info->will_message, strlen(info->will_message)) < 0) 335 | return fail_message(connection); 336 | 337 | variable_header->flags |= MQTT_CONNECT_FLAG_WILL; 338 | if(info->will_retain) 339 | variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; 340 | variable_header->flags |= (info->will_qos & 3) << 3; 341 | } 342 | 343 | if(info->username != NULL && info->username[0] != '\0') 344 | { 345 | if(append_string(connection, info->username, strlen(info->username)) < 0) 346 | return fail_message(connection); 347 | 348 | variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; 349 | } 350 | 351 | if(info->password != NULL && info->password[0] != '\0') 352 | { 353 | if(append_string(connection, info->password, strlen(info->password)) < 0) 354 | return fail_message(connection); 355 | 356 | variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; 357 | } 358 | 359 | return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); 360 | } 361 | 362 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) 363 | { 364 | init_message(connection); 365 | 366 | if(topic == NULL || topic[0] == '\0') 367 | return fail_message(connection); 368 | 369 | if(append_string(connection, topic, strlen(topic)) < 0) 370 | return fail_message(connection); 371 | 372 | if(qos > 0) 373 | { 374 | if((*message_id = append_message_id(connection, 0)) == 0) 375 | return fail_message(connection); 376 | } 377 | else 378 | *message_id = 0; 379 | 380 | if(connection->message.length + data_length > connection->buffer_length) 381 | return fail_message(connection); 382 | memcpy(connection->buffer + connection->message.length, data, data_length); 383 | connection->message.length += data_length; 384 | 385 | return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); 386 | } 387 | 388 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) 389 | { 390 | init_message(connection); 391 | if(append_message_id(connection, message_id) == 0) 392 | return fail_message(connection); 393 | return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); 394 | } 395 | 396 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) 397 | { 398 | init_message(connection); 399 | if(append_message_id(connection, message_id) == 0) 400 | return fail_message(connection); 401 | return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); 402 | } 403 | 404 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) 405 | { 406 | init_message(connection); 407 | if(append_message_id(connection, message_id) == 0) 408 | return fail_message(connection); 409 | return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); 410 | } 411 | 412 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) 413 | { 414 | init_message(connection); 415 | if(append_message_id(connection, message_id) == 0) 416 | return fail_message(connection); 417 | return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); 418 | } 419 | 420 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) 421 | { 422 | init_message(connection); 423 | 424 | if(topic == NULL || topic[0] == '\0') 425 | return fail_message(connection); 426 | 427 | if((*message_id = append_message_id(connection, 0)) == 0) 428 | return fail_message(connection); 429 | 430 | if(append_string(connection, topic, strlen(topic)) < 0) 431 | return fail_message(connection); 432 | 433 | if(connection->message.length + 1 > connection->buffer_length) 434 | return fail_message(connection); 435 | connection->buffer[connection->message.length++] = qos; 436 | 437 | return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); 438 | } 439 | 440 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) 441 | { 442 | init_message(connection); 443 | 444 | if(topic == NULL || topic[0] == '\0') 445 | return fail_message(connection); 446 | 447 | if((*message_id = append_message_id(connection, 0)) == 0) 448 | return fail_message(connection); 449 | 450 | if(append_string(connection, topic, strlen(topic)) < 0) 451 | return fail_message(connection); 452 | 453 | return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); 454 | } 455 | 456 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection) 457 | { 458 | init_message(connection); 459 | return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); 460 | } 461 | 462 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection) 463 | { 464 | init_message(connection); 465 | return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); 466 | } 467 | 468 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection) 469 | { 470 | init_message(connection); 471 | return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); 472 | } 473 | -------------------------------------------------------------------------------- /mqtt/mqtt.c: -------------------------------------------------------------------------------- 1 | /* mqtt.c 2 | * Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html 3 | * 4 | * Copyright (c) 2014-2015, Tuan PM 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "user_interface.h" 33 | #include "osapi.h" 34 | #include "espconn.h" 35 | #include "os_type.h" 36 | #include "mem.h" 37 | #include "mqtt_msg.h" 38 | #include "debug.h" 39 | #include "user_config.h" 40 | #include "mqtt.h" 41 | #include "queue.h" 42 | 43 | #define MQTT_TASK_PRIO 0 44 | #define MQTT_TASK_QUEUE_SIZE 1 45 | #define MQTT_SEND_TIMOUT 5 46 | 47 | #ifndef QUEUE_BUFFER_SIZE 48 | #define QUEUE_BUFFER_SIZE 2048 49 | #endif 50 | 51 | unsigned char *default_certificate; 52 | unsigned int default_certificate_len = 0; 53 | unsigned char *default_private_key; 54 | unsigned int default_private_key_len = 0; 55 | 56 | os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; 57 | 58 | LOCAL void ICACHE_FLASH_ATTR 59 | mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) 60 | { 61 | struct espconn *pConn = (struct espconn *)arg; 62 | MQTT_Client* client = (MQTT_Client *)pConn->reverse; 63 | 64 | 65 | if(ipaddr == NULL) 66 | { 67 | INFO("DNS: Found, but got no ip, try to reconnect\r\n"); 68 | client->connState = TCP_RECONNECT_REQ; 69 | return; 70 | } 71 | 72 | INFO("DNS: found ip %d.%d.%d.%d\n", 73 | *((uint8 *) &ipaddr->addr), 74 | *((uint8 *) &ipaddr->addr + 1), 75 | *((uint8 *) &ipaddr->addr + 2), 76 | *((uint8 *) &ipaddr->addr + 3)); 77 | 78 | if(client->ip.addr == 0 && ipaddr->addr != 0) 79 | { 80 | os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); 81 | if(client->security){ 82 | espconn_secure_connect(client->pCon); 83 | } 84 | else { 85 | espconn_connect(client->pCon); 86 | } 87 | 88 | client->connState = TCP_CONNECTING; 89 | INFO("TCP: connecting...\r\n"); 90 | } 91 | 92 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 93 | } 94 | 95 | 96 | 97 | LOCAL void ICACHE_FLASH_ATTR 98 | deliver_publish(MQTT_Client* client, uint8_t* message, int length) 99 | { 100 | mqtt_event_data_t event_data; 101 | 102 | event_data.topic_length = length; 103 | event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); 104 | event_data.data_length = length; 105 | event_data.data = mqtt_get_publish_data(message, &event_data.data_length); 106 | 107 | if(client->dataCb) 108 | client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); 109 | 110 | } 111 | 112 | 113 | /** 114 | * @brief Client received callback function. 115 | * @param arg: contain the ip link information 116 | * @param pdata: received data 117 | * @param len: the lenght of received data 118 | * @retval None 119 | */ 120 | void ICACHE_FLASH_ATTR 121 | mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len) 122 | { 123 | uint8_t msg_type; 124 | uint8_t msg_qos; 125 | uint16_t msg_id; 126 | 127 | struct espconn *pCon = (struct espconn*)arg; 128 | MQTT_Client *client = (MQTT_Client *)pCon->reverse; 129 | 130 | READPACKET: 131 | INFO("TCP: data received %d bytes\r\n", len); 132 | if(len < MQTT_BUF_SIZE && len > 0){ 133 | os_memcpy(client->mqtt_state.in_buffer, pdata, len); 134 | 135 | msg_type = mqtt_get_type(client->mqtt_state.in_buffer); 136 | msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); 137 | msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); 138 | switch(client->connState){ 139 | case MQTT_CONNECT_SENDING: 140 | if(msg_type == MQTT_MSG_TYPE_CONNACK){ 141 | if(client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT){ 142 | INFO("MQTT: Invalid packet\r\n"); 143 | if(client->security){ 144 | espconn_secure_disconnect(client->pCon); 145 | } 146 | else { 147 | espconn_disconnect(client->pCon); 148 | } 149 | } else { 150 | INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port); 151 | client->connState = MQTT_DATA; 152 | if(client->connectedCb) 153 | client->connectedCb((uint32_t*)client); 154 | } 155 | 156 | } 157 | break; 158 | case MQTT_DATA: 159 | client->mqtt_state.message_length_read = len; 160 | client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); 161 | 162 | 163 | switch(msg_type) 164 | { 165 | 166 | case MQTT_MSG_TYPE_SUBACK: 167 | if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) 168 | INFO("MQTT: Subscribe successful\r\n"); 169 | break; 170 | case MQTT_MSG_TYPE_UNSUBACK: 171 | if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) 172 | INFO("MQTT: UnSubscribe successful\r\n"); 173 | break; 174 | case MQTT_MSG_TYPE_PUBLISH: 175 | if(msg_qos == 1) 176 | client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); 177 | else if(msg_qos == 2) 178 | client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); 179 | if(msg_qos == 1 || msg_qos == 2){ 180 | INFO("MQTT: Queue response QoS: %d\r\n", msg_qos); 181 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 182 | INFO("MQTT: Queue full\r\n"); 183 | } 184 | } 185 | 186 | deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); 187 | break; 188 | case MQTT_MSG_TYPE_PUBACK: 189 | if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){ 190 | INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n"); 191 | } 192 | 193 | break; 194 | case MQTT_MSG_TYPE_PUBREC: 195 | client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); 196 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 197 | INFO("MQTT: Queue full\r\n"); 198 | } 199 | break; 200 | case MQTT_MSG_TYPE_PUBREL: 201 | client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); 202 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 203 | INFO("MQTT: Queue full\r\n"); 204 | } 205 | break; 206 | case MQTT_MSG_TYPE_PUBCOMP: 207 | if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){ 208 | INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n"); 209 | } 210 | break; 211 | case MQTT_MSG_TYPE_PINGREQ: 212 | client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); 213 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 214 | INFO("MQTT: Queue full\r\n"); 215 | } 216 | break; 217 | case MQTT_MSG_TYPE_PINGRESP: 218 | // Ignore 219 | break; 220 | } 221 | // NOTE: this is done down here and not in the switch case above 222 | // because the PSOCK_READBUF_LEN() won't work inside a switch 223 | // statement due to the way protothreads resume. 224 | if(msg_type == MQTT_MSG_TYPE_PUBLISH) 225 | { 226 | len = client->mqtt_state.message_length_read; 227 | 228 | if(client->mqtt_state.message_length < client->mqtt_state.message_length_read) 229 | { 230 | //client->connState = MQTT_PUBLISH_RECV; 231 | //Not Implement yet 232 | len -= client->mqtt_state.message_length; 233 | pdata += client->mqtt_state.message_length; 234 | 235 | INFO("Get another published message\r\n"); 236 | goto READPACKET; 237 | } 238 | 239 | } 240 | break; 241 | } 242 | } else { 243 | INFO("ERROR: Message too long\r\n"); 244 | } 245 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 246 | } 247 | 248 | /** 249 | * @brief Client send over callback function. 250 | * @param arg: contain the ip link information 251 | * @retval None 252 | */ 253 | void ICACHE_FLASH_ATTR 254 | mqtt_tcpclient_sent_cb(void *arg) 255 | { 256 | struct espconn *pCon = (struct espconn *)arg; 257 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 258 | INFO("TCP: Sent\r\n"); 259 | client->sendTimeout = 0; 260 | if(client->connState == MQTT_DATA && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH){ 261 | if(client->publishedCb) 262 | client->publishedCb((uint32_t*)client); 263 | } 264 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 265 | } 266 | 267 | void ICACHE_FLASH_ATTR mqtt_timer(void *arg) 268 | { 269 | MQTT_Client* client = (MQTT_Client*)arg; 270 | 271 | if(client->connState == MQTT_DATA){ 272 | client->keepAliveTick ++; 273 | if(client->keepAliveTick > client->mqtt_state.connect_info->keepalive){ 274 | 275 | INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port); 276 | client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); 277 | client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; 278 | client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); 279 | client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 280 | 281 | 282 | client->sendTimeout = MQTT_SEND_TIMOUT; 283 | INFO("MQTT: Sending, type: %d, id: %04X\r\n",client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); 284 | if(client->security){ 285 | espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 286 | } 287 | else{ 288 | espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 289 | } 290 | 291 | client->mqtt_state.outbound_message = NULL; 292 | 293 | client->keepAliveTick = 0; 294 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 295 | } 296 | 297 | } else if(client->connState == TCP_RECONNECT_REQ){ 298 | client->reconnectTick ++; 299 | if(client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { 300 | client->reconnectTick = 0; 301 | client->connState = TCP_RECONNECT; 302 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 303 | } 304 | } 305 | if(client->sendTimeout > 0) 306 | client->sendTimeout --; 307 | } 308 | 309 | void ICACHE_FLASH_ATTR 310 | mqtt_tcpclient_discon_cb(void *arg) 311 | { 312 | 313 | struct espconn *pespconn = (struct espconn *)arg; 314 | MQTT_Client* client = (MQTT_Client *)pespconn->reverse; 315 | INFO("TCP: Disconnected callback\r\n"); 316 | client->connState = TCP_RECONNECT_REQ; 317 | if(client->disconnectedCb) 318 | client->disconnectedCb((uint32_t*)client); 319 | 320 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 321 | } 322 | 323 | 324 | 325 | /** 326 | * @brief Tcp client connect success callback function. 327 | * @param arg: contain the ip link information 328 | * @retval None 329 | */ 330 | void ICACHE_FLASH_ATTR 331 | mqtt_tcpclient_connect_cb(void *arg) 332 | { 333 | struct espconn *pCon = (struct espconn *)arg; 334 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 335 | 336 | espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); 337 | espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// 338 | espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// 339 | INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port); 340 | 341 | mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); 342 | client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); 343 | client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); 344 | client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 345 | 346 | 347 | client->sendTimeout = MQTT_SEND_TIMOUT; 348 | INFO("MQTT: Sending, type: %d, id: %04X\r\n",client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); 349 | if(client->security){ 350 | espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 351 | } 352 | else{ 353 | espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 354 | } 355 | 356 | client->mqtt_state.outbound_message = NULL; 357 | client->connState = MQTT_CONNECT_SENDING; 358 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 359 | } 360 | 361 | /** 362 | * @brief Tcp client connect repeat callback function. 363 | * @param arg: contain the ip link information 364 | * @retval None 365 | */ 366 | void ICACHE_FLASH_ATTR 367 | mqtt_tcpclient_recon_cb(void *arg, sint8 errType) 368 | { 369 | struct espconn *pCon = (struct espconn *)arg; 370 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 371 | 372 | INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port); 373 | 374 | client->connState = TCP_RECONNECT_REQ; 375 | 376 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 377 | 378 | } 379 | 380 | /** 381 | * @brief MQTT publish function. 382 | * @param client: MQTT_Client reference 383 | * @param topic: string topic will publish to 384 | * @param data: buffer data send point to 385 | * @param data_length: length of data 386 | * @param qos: qos 387 | * @param retain: retain 388 | * @retval TRUE if success queue 389 | */ 390 | BOOL ICACHE_FLASH_ATTR 391 | MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain) 392 | { 393 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 394 | uint16_t dataLen; 395 | client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, 396 | topic, data, data_length, 397 | qos, retain, 398 | &client->mqtt_state.pending_msg_id); 399 | if(client->mqtt_state.outbound_message->length == 0){ 400 | INFO("MQTT: Queuing publish failed\r\n"); 401 | return FALSE; 402 | } 403 | INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); 404 | while(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 405 | INFO("MQTT: Queue full\r\n"); 406 | if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { 407 | INFO("MQTT: Serious buffer error\r\n"); 408 | return FALSE; 409 | } 410 | } 411 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 412 | return TRUE; 413 | } 414 | 415 | /** 416 | * @brief MQTT subscibe function. 417 | * @param client: MQTT_Client reference 418 | * @param topic: string topic will subscribe 419 | * @param qos: qos 420 | * @retval TRUE if success queue 421 | */ 422 | BOOL ICACHE_FLASH_ATTR 423 | MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) 424 | { 425 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 426 | uint16_t dataLen; 427 | 428 | client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, 429 | topic, 0, 430 | &client->mqtt_state.pending_msg_id); 431 | INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n",topic, client->mqtt_state.pending_msg_id); 432 | while(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 433 | INFO("MQTT: Queue full\r\n"); 434 | if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { 435 | INFO("MQTT: Serious buffer error\r\n"); 436 | return FALSE; 437 | } 438 | } 439 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 440 | return TRUE; 441 | } 442 | 443 | void ICACHE_FLASH_ATTR 444 | MQTT_Task(os_event_t *e) 445 | { 446 | MQTT_Client* client = (MQTT_Client*)e->par; 447 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 448 | uint16_t dataLen; 449 | switch(client->connState){ 450 | 451 | case TCP_RECONNECT_REQ: 452 | break; 453 | case TCP_RECONNECT: 454 | MQTT_Connect(client); 455 | INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port); 456 | client->connState = TCP_CONNECTING; 457 | break; 458 | case MQTT_DATA: 459 | if(QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) { 460 | break; 461 | } 462 | if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0){ 463 | client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); 464 | client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); 465 | 466 | 467 | client->sendTimeout = MQTT_SEND_TIMOUT; 468 | INFO("MQTT: Sending, type: %d, id: %04X\r\n",client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); 469 | if(client->security){ 470 | espconn_secure_sent(client->pCon, dataBuffer, dataLen); 471 | } 472 | else{ 473 | espconn_sent(client->pCon, dataBuffer, dataLen); 474 | } 475 | 476 | client->mqtt_state.outbound_message = NULL; 477 | break; 478 | } 479 | break; 480 | } 481 | } 482 | 483 | /** 484 | * @brief MQTT initialization connection function 485 | * @param client: MQTT_Client reference 486 | * @param host: Domain or IP string 487 | * @param port: Port to connect 488 | * @param security: 1 for ssl, 0 for none 489 | * @retval None 490 | */ 491 | void ICACHE_FLASH_ATTR 492 | MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security) 493 | { 494 | uint32_t temp; 495 | INFO("MQTT_InitConnection\r\n"); 496 | os_memset(mqttClient, 0, sizeof(MQTT_Client)); 497 | temp = os_strlen(host); 498 | mqttClient->host = (uint8_t*)os_zalloc(temp + 1); 499 | os_strcpy(mqttClient->host, host); 500 | mqttClient->host[temp] = 0; 501 | mqttClient->port = port; 502 | mqttClient->security = security; 503 | 504 | } 505 | 506 | /** 507 | * @brief MQTT initialization mqtt client function 508 | * @param client: MQTT_Client reference 509 | * @param clientid: MQTT client id 510 | * @param client_user:MQTT client user 511 | * @param client_pass:MQTT client password 512 | * @param client_pass:MQTT keep alive timer, in second 513 | * @retval None 514 | */ 515 | void ICACHE_FLASH_ATTR 516 | MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession) 517 | { 518 | uint32_t temp; 519 | INFO("MQTT_InitClient\r\n"); 520 | 521 | os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); 522 | 523 | temp = os_strlen(client_id); 524 | mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1); 525 | os_strcpy(mqttClient->connect_info.client_id, client_id); 526 | mqttClient->connect_info.client_id[temp] = 0; 527 | 528 | temp = os_strlen(client_user); 529 | mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1); 530 | os_strcpy(mqttClient->connect_info.username, client_user); 531 | mqttClient->connect_info.username[temp] = 0; 532 | 533 | temp = os_strlen(client_pass); 534 | mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1); 535 | os_strcpy(mqttClient->connect_info.password, client_pass); 536 | mqttClient->connect_info.password[temp] = 0; 537 | 538 | 539 | mqttClient->connect_info.keepalive = keepAliveTime; 540 | mqttClient->connect_info.clean_session = cleanSession; 541 | 542 | mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); 543 | mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; 544 | mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); 545 | mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; 546 | mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; 547 | 548 | mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); 549 | 550 | QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); 551 | 552 | system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); 553 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); 554 | } 555 | void ICACHE_FLASH_ATTR 556 | MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) 557 | { 558 | uint32_t temp; 559 | temp = os_strlen(will_topic); 560 | mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1); 561 | os_strcpy(mqttClient->connect_info.will_topic, will_topic); 562 | mqttClient->connect_info.will_topic[temp] = 0; 563 | 564 | temp = os_strlen(will_msg); 565 | mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1); 566 | os_strcpy(mqttClient->connect_info.will_message, will_msg); 567 | mqttClient->connect_info.will_message[temp] = 0; 568 | 569 | 570 | mqttClient->connect_info.will_qos = will_qos; 571 | mqttClient->connect_info.will_retain = will_retain; 572 | } 573 | /** 574 | * @brief Begin connect to MQTT broker 575 | * @param client: MQTT_Client reference 576 | * @retval None 577 | */ 578 | void ICACHE_FLASH_ATTR 579 | MQTT_Connect(MQTT_Client *mqttClient) 580 | { 581 | MQTT_Disconnect(mqttClient); 582 | mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); 583 | mqttClient->pCon->type = ESPCONN_TCP; 584 | mqttClient->pCon->state = ESPCONN_NONE; 585 | mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); 586 | mqttClient->pCon->proto.tcp->local_port = espconn_port(); 587 | mqttClient->pCon->proto.tcp->remote_port = mqttClient->port; 588 | mqttClient->pCon->reverse = mqttClient; 589 | espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); 590 | espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); 591 | 592 | mqttClient->keepAliveTick = 0; 593 | mqttClient->reconnectTick = 0; 594 | 595 | 596 | os_timer_disarm(&mqttClient->mqttTimer); 597 | os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); 598 | os_timer_arm(&mqttClient->mqttTimer, 1000, 1); 599 | 600 | if(UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) { 601 | INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port); 602 | if(mqttClient->security){ 603 | espconn_secure_connect(mqttClient->pCon); 604 | } 605 | else { 606 | espconn_connect(mqttClient->pCon); 607 | } 608 | } 609 | else { 610 | INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port); 611 | espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found); 612 | } 613 | mqttClient->connState = TCP_CONNECTING; 614 | } 615 | 616 | void ICACHE_FLASH_ATTR 617 | MQTT_Disconnect(MQTT_Client *mqttClient) 618 | { 619 | if(mqttClient->pCon){ 620 | INFO("Free memory\r\n"); 621 | if(mqttClient->pCon->proto.tcp) 622 | os_free(mqttClient->pCon->proto.tcp); 623 | os_free(mqttClient->pCon); 624 | mqttClient->pCon = NULL; 625 | } 626 | 627 | os_timer_disarm(&mqttClient->mqttTimer); 628 | } 629 | void ICACHE_FLASH_ATTR 630 | MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) 631 | { 632 | mqttClient->connectedCb = connectedCb; 633 | } 634 | 635 | void ICACHE_FLASH_ATTR 636 | MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) 637 | { 638 | mqttClient->disconnectedCb = disconnectedCb; 639 | } 640 | 641 | void ICACHE_FLASH_ATTR 642 | MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) 643 | { 644 | mqttClient->dataCb = dataCb; 645 | } 646 | 647 | void ICACHE_FLASH_ATTR 648 | MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) 649 | { 650 | mqttClient->publishedCb = publishedCb; 651 | } 652 | -------------------------------------------------------------------------------- /user/user_main.c: -------------------------------------------------------------------------------- 1 | /* main.c -- ESP8266 MQTT relay node 2 | * 3 | * Provides addressable relay contact, and button sense functions. 4 | * Button can be configured for local control, or isolated for use 5 | * as a separate resource. 6 | * 7 | * Configuration parameters set with the Makefile using a Python patching 8 | * utility which is avalable on my github site. This allows the configurations 9 | * to differ between nodes and also protects the WIFI login credentials by 10 | * removing them from the source. 11 | * 12 | * Copyright (C) 2015, Stephen Rodgers 13 | * Copyright (c) 2014-2015, Tuan PM 14 | * All rights reserved. 15 | * 16 | * Redistribution and use in source and binary forms, with or without 17 | * modification, are permitted provided that the following conditions are met: 18 | * 19 | * 20 | * Redistributions of source code must retain the above copyright notice, 21 | * this list of conditions and the following disclaimer. 22 | * Redistributions in binary form must reproduce the above copyright 23 | * notice, this list of conditions and the following disclaimer in the 24 | * documentation and/or other materials provided with the distribution. 25 | * Neither the name of Redis nor the names of its contributors may be used 26 | * to endorse or promote products derived from this software without 27 | * specific prior written permission. 28 | * 29 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 30 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 33 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39 | * POSSIBILITY OF SUCH DAMAGE. 40 | */ 41 | 42 | // API includes 43 | #include "ets_sys.h" 44 | #include "osapi.h" 45 | #include "debug.h" 46 | #include "gpio.h" 47 | #include "user_interface.h" 48 | #include "mem.h" 49 | #include "jsonparse.h" 50 | // Project includes 51 | #include "driver/uart.h" 52 | #include "mqtt.h" 53 | #include "wifi.h" 54 | #include "easygpio.h" 55 | #include "util.h" 56 | #include "kvstore.h" 57 | 58 | /* Operational modes */ 59 | 60 | #define STANDARD 0 // Single output 61 | #define LATCHING 1 // Pulsed dual output (latching relay use) 62 | 63 | #ifndef MODE // In initially udefined, 64 | #define MODE LATCHING // Set mode of operation 65 | // either STANDARD OR LATCHING 66 | #endif 67 | 68 | // Include connection state LED code if defined 69 | 70 | #define WITH_LED 71 | 72 | 73 | #if STANDARD==MODE // GPIO setup for standard 74 | 75 | #define RELAY_GPIO 12 // GPIO for relay control output 76 | 77 | #define BUTTON_GPIO 0 // GPIO to use for button 78 | 79 | #define LED_GPIO 14 // GPIO to use for LED 80 | 81 | #define RELAY_ON 0 // Low true relay outputs 82 | #define RELAY_OFF 1 83 | 84 | 85 | #endif // End standard setup 86 | 87 | #if LATCHING==MODE // Gpio setup for latching 88 | 89 | #define RELAY_SET_GPIO 13 // GPIO for set coil on bistable relay 90 | #define RELAY_CLEAR_GPIO 12 // GPIO for clear coil on bistable relay 91 | 92 | #define BUTTON_GPIO 0 // GPIO to use for button 93 | 94 | #define LED_GPIO 14 // GPIO to use for LED 95 | 96 | #define RELAY_ON 0 // Low true relay outputs 97 | #define RELAY_OFF 1 98 | 99 | 100 | #endif // End latching setup 101 | 102 | 103 | #ifdef WITH_LED // LED configuration 104 | 105 | #define LED_ON 0 // Low true LED output 106 | #define LED_OFF 1 107 | 108 | #define LED_FLASH_COUNT 2 // 400mSec LED flash period (n*100)*2 109 | 110 | #endif // End WITH_LED 111 | 112 | /* General definitions */ 113 | 114 | #define ON 1 115 | #define OFF 0 116 | 117 | #define MAX_INFO_ELEMENTS 16 // Patcher number of elements 118 | #define INFO_BLOCK_MAGIC 0x3F2A6C17 // Patcher magic 119 | #define INFO_BLOCK_SIG "ESP8266HWSTARSR"// Patcher pattern 120 | #define CONFIG_FLD_REQD 0x01 // Patcher field required flag 121 | 122 | // Definition for a patcher config element 123 | 124 | struct config_info_element_tag{ 125 | uint8_t flags; 126 | uint8_t key[15]; 127 | uint8_t value[80]; 128 | } __attribute__((__packed__)); 129 | 130 | typedef struct config_info_element_tag config_info_element; 131 | 132 | // Definition for a patcher config element 133 | 134 | struct config_info_block_tag{ 135 | uint8_t signature[16]; 136 | uint32_t magic; 137 | uint8_t numelements; 138 | uint8_t recordLength; 139 | uint8_t pad[10]; 140 | config_info_element e[MAX_INFO_ELEMENTS]; 141 | } __attribute__((__packed__)); 142 | 143 | // Definition of a common element for MQTT command parameters 144 | 145 | typedef union { 146 | char *sp; 147 | unsigned u; 148 | int i; 149 | } pu; 150 | 151 | // Definition of an MQTT command element 152 | 153 | typedef struct { 154 | const char *command; 155 | uint8_t type; 156 | pu p; 157 | } command_element; 158 | 159 | 160 | typedef struct config_info_block_tag config_info_block; 161 | 162 | // Definition of command codes and types 163 | enum {WIFISSID=0, WIFIPASS, MQTTHOST, MQTTPORT, MQTTSECUR, MQTTDEVID, MQTTUSER, MQTTPASS, MQTTKPALIV, MQTTDEVPATH, MQTTBTLOCAL}; 164 | enum {CP_NONE= 0, CP_INT, CP_BOOL, CP_QSTRING}; 165 | 166 | /* Local storage */ 167 | 168 | // Patcher configuration information 169 | 170 | 171 | LOCAL config_info_block configInfoBlock = { 172 | .signature = INFO_BLOCK_SIG, 173 | .magic = INFO_BLOCK_MAGIC, 174 | .numelements = MAX_INFO_ELEMENTS, 175 | .recordLength = sizeof(config_info_element), 176 | .e[WIFISSID] = {.flags = CONFIG_FLD_REQD, .key = "WIFISSID", .value="your_ssid_here"}, 177 | .e[WIFIPASS] = {.flags = CONFIG_FLD_REQD, .key = "WIFIPASS", .value="its_a_secret"}, 178 | .e[MQTTHOST] = {.flags = CONFIG_FLD_REQD, .key = "MQTTHOST", .value="your_mqtt_broker_hostname_here"}, // May also be an IP address 179 | .e[MQTTPORT] = {.key = "MQTTPORT", .value="1883"}, // destination Port for mqtt broker 180 | .e[MQTTSECUR] = {.key = "MQTTSECUR",.value="0"}, // Security 0 - no encryption 181 | .e[MQTTDEVID] = {.key = "MQTTDEVID", .value="your_mqtt_device_id_here"}, // Unique device ID 182 | .e[MQTTUSER] = {.key = "MQTTUSER", .value="your_mqtt_client_name_here"}, // MQTT User name 183 | .e[MQTTPASS] = {.key = "MQTTPASS", .value="its_a_secret"},// MQTT Password 184 | .e[MQTTKPALIV] = {.key = "MQTTKPALIV", .value="120"}, // Keepalive interval 185 | .e[MQTTDEVPATH] = {.flags = CONFIG_FLD_REQD, .key = "MQTTDEVPATH", .value = "/home/lab/relay"}, // Device path 186 | .e[MQTTBTLOCAL] = {.key = "MQTTBTLOCAL", .value = "1"}, // Optional local toggle control using GPIO0 187 | }; 188 | 189 | // Command elements 190 | // Additional commands are added here 191 | 192 | enum {CMD_OFF = 0, CMD_ON, CMD_TOGGLE, CMD_PULSE, CMD_BTLOCAL, CMD_QUERY, CMD_SURVEY, CMD_SSID, CMD_RESTART, CMD_WIFIPASS, CMD_CYCLE, CMD_MQTTDEVPATH}; 193 | 194 | LOCAL command_element commandElements[] = { 195 | {.command = "off", .type = CP_NONE}, 196 | {.command = "on", .type = CP_NONE}, 197 | {.command = "toggle", .type = CP_NONE}, 198 | {.command = "pulse", .type = CP_INT}, 199 | {.command = "btlocal", .type = CP_INT}, 200 | {.command = "query", .type = CP_NONE}, 201 | {.command = "survey", .type = CP_NONE}, 202 | {.command = "ssid", .type = CP_QSTRING}, 203 | {.command = "restart",.type = CP_NONE}, 204 | {.command = "wifipass",.type = CP_QSTRING}, 205 | {.command = "cycle",.type = CP_INT}, 206 | {.command = "mqttdevpath",.type = CP_QSTRING}, 207 | {.command = ""} /* End marker */ 208 | }; 209 | 210 | // Misc Local variables 211 | 212 | LOCAL int relayState = OFF; 213 | 214 | #if MODE==LATCHING 215 | typedef enum {LR_IDLE = 0, LR_SET, LR_CLEAR, LR_DONE} lrstatetype; 216 | LOCAL lrstatetype lrState; 217 | #endif 218 | 219 | #ifdef WITH_LED 220 | typedef enum {LC_OFF = 0, LC_ON, LC_FLASH, LC_FLASH_OFF} lcstatetype; 221 | LOCAL lcstatetype lcState; 222 | #endif 223 | 224 | LOCAL char *schema = "hwstar_relaynode"; 225 | LOCAL int buttonState = 1; 226 | LOCAL char *commandTopic, *statusTopic; 227 | LOCAL char *controlTopic = "/node/control"; 228 | LOCAL char *infoTopic = "/node/info"; 229 | LOCAL flash_handle_s *configHandle; 230 | LOCAL os_timer_t pulseTimer, buttonTimer; 231 | 232 | MQTT_Client mqttClient; // Control block used by MQTT functions 233 | 234 | 235 | /** 236 | * Publish connection info 237 | */ 238 | LOCAL void ICACHE_FLASH_ATTR publishConnInfo(MQTT_Client *client) 239 | { 240 | struct ip_info ipConfig; 241 | char *buf = util_zalloc(256); 242 | 243 | // Publish who we are and where we live 244 | wifi_get_ip_info(STATION_IF, &ipConfig); 245 | os_sprintf(buf, "{\"muster\":{\"connstate\":\"online\",\"device\":\"%s\",\"ip4\":\"%d.%d.%d.%d\",\"schema\":\"%s\",\"ssid\":\"%s\"}}", 246 | configInfoBlock.e[MQTTDEVPATH].value, 247 | *((uint8_t *) &ipConfig.ip.addr), 248 | *((uint8_t *) &ipConfig.ip.addr + 1), 249 | *((uint8_t *) &ipConfig.ip.addr + 2), 250 | *((uint8_t *) &ipConfig.ip.addr + 3), 251 | schema, 252 | commandElements[CMD_SSID].p.sp); 253 | 254 | INFO("MQTT Node info: %s\r\n", buf); 255 | 256 | // Publish 257 | MQTT_Publish(client, infoTopic, buf, os_strlen(buf), 0, 0); 258 | 259 | // Free the buffer 260 | util_free(buf); 261 | 262 | } 263 | 264 | 265 | 266 | /** 267 | * Handle qstring command 268 | */ 269 | 270 | LOCAL void ICACHE_FLASH_ATTR handleQstringCommand(char *new_value, command_element *ce) 271 | { 272 | char *buf = util_zalloc(128); 273 | 274 | 275 | if(!new_value){ 276 | const char *cur_value = kvstore_get_string(configHandle, ce->command); 277 | os_sprintf(buf, "{\"%s\":\"%s\"}", ce->command, cur_value); 278 | util_free(cur_value); 279 | INFO("Query Result: %s\r\n", buf ); 280 | MQTT_Publish(&mqttClient, statusTopic, buf, os_strlen(buf), 0, 0); 281 | } 282 | else{ 283 | util_free(ce->p.sp); // Free old value 284 | ce->p.sp = new_value; // Save reference to new value 285 | kvstore_put(configHandle, ce->command, ce->p.sp); 286 | 287 | } 288 | 289 | util_free(buf); 290 | 291 | } 292 | 293 | /** 294 | * Send MQTT message to update relay state 295 | */ 296 | 297 | LOCAL void ICACHE_FLASH_ATTR updateRelayState(int s) 298 | { 299 | char *state = s ? "\"on\"}" : "\"off\"}"; 300 | char result[16]; 301 | os_strcpy(result,"{\"relaystate\":"); 302 | os_strcat(result, state); 303 | INFO("MQTT: New Relay State: %s\r\n", state); 304 | MQTT_Publish(&mqttClient, statusTopic, result, os_strlen(result), 0, 0); 305 | 306 | } 307 | 308 | /** 309 | * Set new relay state 310 | */ 311 | 312 | LOCAL void ICACHE_FLASH_ATTR relaySet(bool new_state) 313 | { 314 | relayState = new_state; // Save new state 315 | #if STANDARD==MODE 316 | GPIO_OUTPUT_SET(RELAY_GPIO, ((relayState) ? RELAY_ON : RELAY_OFF)); // Set relay GPIO 317 | #endif 318 | #if LATCHING==MODE 319 | if(!lrState) 320 | lrState = (new_state) ? LR_SET : LR_CLEAR; // Get new latching relay state 321 | else 322 | INFO("Overlapped latching relay commands not supported yet. Command ignored while busy\r\n"); 323 | #endif 324 | updateRelayState(relayState); // Send MQTT message indicating new state 325 | } 326 | 327 | /** 328 | * Toggle the relay output 329 | */ 330 | 331 | LOCAL void ICACHE_FLASH_ATTR relayToggle(void) 332 | { 333 | bool new_relayState = (relayState) ? OFF : ON; 334 | relaySet(new_relayState); 335 | } 336 | 337 | /** 338 | * Update the button state 339 | */ 340 | 341 | LOCAL void ICACHE_FLASH_ATTR updateButtonState(int s) 342 | { 343 | 344 | 345 | char *state = s ? "\"released\"}" : "\"depressed\"}"; 346 | char result[25]; 347 | os_strcpy(result,"{\"buttonstate\":"); 348 | os_strcat(result, state); 349 | INFO("MQTT: New Button State: %s\r\n", state); 350 | MQTT_Publish(&mqttClient, statusTopic, result, os_strlen(result), 0, 0); 351 | 352 | } 353 | 354 | /** 355 | * WIFI connect call back 356 | */ 357 | 358 | 359 | LOCAL void ICACHE_FLASH_ATTR wifiConnectCb(uint8_t status) 360 | { 361 | if(status == STATION_GOT_IP){ 362 | #ifdef WITH_LED 363 | lcState = LC_FLASH; // Start flashing LED 364 | #endif 365 | MQTT_Connect(&mqttClient); 366 | } 367 | #ifdef WITH_LED 368 | else 369 | lcState = LC_OFF; // LED off 370 | #endif 371 | 372 | } 373 | 374 | /** 375 | * Survey complete, 376 | * publish results 377 | */ 378 | 379 | 380 | LOCAL void ICACHE_FLASH_ATTR 381 | surveyCompleteCb(void *arg, STATUS status) 382 | { 383 | struct bss_info *bss = arg; 384 | 385 | #define SURVEY_CHUNK_SIZE 256 386 | 387 | if(status == OK){ 388 | uint8_t i; 389 | char *buf = util_zalloc(SURVEY_CHUNK_SIZE); 390 | bss = bss->next.stqe_next; //ignore first 391 | for(i = 2; (bss); i++){ 392 | if(2 == i) 393 | os_sprintf(strlen(buf) + buf,"{\"access_points\":["); 394 | else 395 | os_strcat(buf,","); 396 | os_sprintf(strlen(buf)+ buf, "\"%s\":{\"chan\":\"%d\",\"rssi\":\"%d\"}", bss->ssid, bss->channel, bss->rssi); 397 | bss = bss->next.stqe_next; 398 | buf = util_str_realloc(buf, i * SURVEY_CHUNK_SIZE); // Grow buffer 399 | } 400 | if(buf[0]) 401 | os_strcat(buf,"]}"); 402 | 403 | INFO("Survey Results:\r\n", buf); 404 | INFO(buf); 405 | MQTT_Publish(&mqttClient, statusTopic, buf, os_strlen(buf), 0, 0); 406 | util_free(buf); 407 | } 408 | 409 | } 410 | 411 | 412 | /** 413 | * MQTT Connect call back 414 | */ 415 | 416 | LOCAL void ICACHE_FLASH_ATTR mqttConnectedCb(uint32_t *args) 417 | { 418 | 419 | MQTT_Client* client = (MQTT_Client*)args; 420 | 421 | 422 | 423 | INFO("MQTT: Connected\r\n"); 424 | #ifdef WITH_LED 425 | lcState = LC_ON; // LED on solid 426 | #endif 427 | 428 | publishConnInfo(client); 429 | 430 | // Subscribe to the control topic 431 | MQTT_Subscribe(client, controlTopic, 0); 432 | // Subscribe to command topic 433 | MQTT_Subscribe(client, commandTopic, 0); 434 | // Publish relay state 435 | updateRelayState(relayState); 436 | 437 | } 438 | 439 | /** 440 | * MQTT Disconnect call back 441 | */ 442 | 443 | 444 | LOCAL void ICACHE_FLASH_ATTR mqttDisconnectedCb(uint32_t *args) 445 | { 446 | MQTT_Client* client = (MQTT_Client*)args; 447 | INFO("MQTT: Disconnected\r\n"); 448 | #ifdef WITH_LED 449 | lcState = LC_FLASH; 450 | #endif 451 | } 452 | 453 | /** 454 | * MQTT published call back 455 | */ 456 | 457 | LOCAL void ICACHE_FLASH_ATTR mqttPublishedCb(uint32_t *args) 458 | { 459 | MQTT_Client* client = (MQTT_Client*)args; 460 | INFO("MQTT: Published\r\n"); 461 | } 462 | 463 | /** 464 | * MQTT Data call back 465 | * Commands are decoded and acted upon here 466 | */ 467 | 468 | LOCAL void ICACHE_FLASH_ATTR 469 | mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, 470 | const char *data, uint32_t data_len) 471 | { 472 | char *topicBuf, *dataBuf; 473 | uint8_t i; 474 | struct jsonparse_state state; 475 | char command[32]; 476 | 477 | MQTT_Client* client = (MQTT_Client*)args; // Pointer to MQTT control block passed in as args 478 | 479 | command[0] = 0; // Zero command string length to prevent stack junk from printing during debug 480 | 481 | // Save local copies of the topic and data 482 | topicBuf = util_strndup(topic, topic_len); 483 | dataBuf = util_strndup(data, data_len); 484 | 485 | INFO("Receive topic: %s, data: %s \r\n", topicBuf, dataBuf); 486 | 487 | // Control Message? 488 | if(!os_strcmp(topicBuf, controlTopic)){ 489 | jsonparse_setup(&state, dataBuf, data_len); 490 | if (util_parse_json_param(&state, "control", command, sizeof(command)) != 2) 491 | goto cleanup; /* Command not present in json object */ 492 | if(!os_strcmp(command, "muster")){ 493 | publishConnInfo(&mqttClient); 494 | } 495 | } 496 | 497 | // Command Message? 498 | else if (!os_strcmp(topicBuf, commandTopic)){ // Check for match to command topic 499 | // Parse command 500 | jsonparse_setup(&state, dataBuf, data_len); 501 | if (util_parse_json_param(&state, "command", command, sizeof(command)) != 2) 502 | goto cleanup; /* Command not present in json object */ 503 | 504 | for(i = 0; commandElements[i].command[0]; i++){ 505 | command_element *ce = &commandElements[i]; 506 | //INFO("Trying %s\r\n", ce->command); 507 | if(CP_NONE == ce->type){ // Parameterless command 508 | if(!os_strcmp(command, ce->command)){ 509 | switch(i){ 510 | case CMD_OFF: 511 | relaySet(OFF); 512 | break; 513 | 514 | case CMD_ON: 515 | relaySet(ON); 516 | break; 517 | 518 | case CMD_TOGGLE: 519 | relayToggle(); 520 | break; 521 | 522 | case CMD_QUERY: 523 | updateRelayState(relayState); 524 | break; 525 | 526 | case CMD_SURVEY: 527 | wifi_station_scan(NULL, surveyCompleteCb); 528 | break; 529 | 530 | case CMD_RESTART: 531 | util_restart(); 532 | break; 533 | 534 | default: 535 | util_assert(FALSE, "Unsupported command: %d", i); 536 | } 537 | break; 538 | } 539 | } 540 | 541 | if((CP_INT == ce->type) || (CP_BOOL == ce->type)){ // Integer/bool parameter 542 | int arg; 543 | if(util_parse_command_int(command, ce->command, dataBuf, &arg)){ 544 | switch(i){ 545 | case CMD_PULSE: // Pulse rely on then off for a specific time in mSec 546 | relaySet(ON); 547 | os_timer_arm(&pulseTimer, arg, 0); 548 | break; 549 | 550 | case CMD_BTLOCAL: // Link or break button control from relay 551 | ce->p.i = (arg) ? 1: 0; 552 | kvstore_update_number(configHandle, ce->command, ce->p.i); 553 | break; 554 | 555 | case CMD_CYCLE: // Cycle relay 556 | if(arg && (arg < 500)) 557 | arg = 500; // Clip to 1/2 sec half cycle 558 | arg = arg/100; // Convert 1ms to 100ms count 559 | ce->p.i = arg; 560 | if(!arg) // If paramter is 0, this is a request to stop cycling. 561 | relaySet(OFF); 562 | break; 563 | 564 | default: 565 | util_assert(FALSE, "Unsupported command: %d", i); 566 | } 567 | break; 568 | } 569 | } 570 | if(CP_QSTRING == ce->type){ // Query strings 571 | char *val = NULL; 572 | if(util_parse_command_qstring(command, ce->command, dataBuf, &val) != FALSE){ 573 | if((CMD_SSID == i) || (CMD_WIFIPASS == i) || (CMD_MQTTDEVPATH == i)){ // SSID, MQTTDEVPATH or WIFIPASS? 574 | handleQstringCommand(val, ce); 575 | } 576 | } 577 | } 578 | 579 | } /* END for */ 580 | kvstore_flush(configHandle); // Flush any changes back to the kvs 581 | } /* END if topic test */ 582 | 583 | // Free local copies of the topic and data strings 584 | cleanup: 585 | util_free(topicBuf); 586 | util_free(dataBuf); 587 | } 588 | 589 | /** 590 | * Pulse timer callback function. This is used to time 591 | * the length of the relay on state after a pulse:n command 592 | * is received. 593 | */ 594 | 595 | LOCAL void ICACHE_FLASH_ATTR pulseTmerExpireCb(void *arg) 596 | { 597 | relaySet(OFF); 598 | } 599 | 600 | 601 | /** 602 | * 100 millisecond timer callback 603 | * This function handles button sampling, 604 | * latching relay control, and LED control 605 | */ 606 | 607 | LOCAL void ICACHE_FLASH_ATTR nodeTimerCb(void *arg) 608 | { 609 | int newstate = GPIO_INPUT_GET(BUTTON_GPIO); 610 | static int relayCycleTimer; 611 | 612 | if(newstate != buttonState){ 613 | if(newstate){ 614 | char result[32]; 615 | 616 | if(commandElements[CMD_BTLOCAL].p.i) // If local control enabled 617 | relayToggle(); // Toggle the relay state 618 | INFO("Button released\r\n"); 619 | } 620 | else{ 621 | INFO("Button pressed\r\n"); 622 | } 623 | buttonState = newstate; 624 | updateButtonState(buttonState); 625 | } 626 | 627 | #if LATCHING==MODE // Handle latching relay as state machine 628 | if(lrState){ 629 | switch(lrState){ 630 | 631 | case LR_SET: 632 | GPIO_OUTPUT_SET(RELAY_SET_GPIO, RELAY_ON); 633 | lrState = LR_DONE; 634 | break; 635 | 636 | case LR_CLEAR: 637 | //INFO("lr_clear\r\n"); 638 | GPIO_OUTPUT_SET(RELAY_CLEAR_GPIO, RELAY_ON); 639 | lrState = LR_DONE; 640 | break; 641 | 642 | case LR_DONE: 643 | //INFO("lr_done\r\n"); 644 | GPIO_OUTPUT_SET(RELAY_SET_GPIO, RELAY_OFF); 645 | GPIO_OUTPUT_SET(RELAY_CLEAR_GPIO, RELAY_OFF); 646 | lrState = LR_IDLE; 647 | break; 648 | 649 | default: 650 | util_assert(FALSE, "Bad latching relay state reached: %d", lrState); 651 | } 652 | } 653 | #endif // End LATCHING 654 | 655 | #ifdef WITH_LED 656 | 657 | switch(lcState){ 658 | static uint8_t flashCount; 659 | 660 | case LC_OFF: 661 | flashCount = 0; 662 | GPIO_OUTPUT_SET(LED_GPIO, LED_OFF); 663 | break; 664 | 665 | case LC_ON: 666 | flashCount = 0; 667 | GPIO_OUTPUT_SET(LED_GPIO, LED_ON); 668 | break; 669 | 670 | case LC_FLASH: 671 | if(flashCount) 672 | flashCount--; 673 | else{ 674 | GPIO_OUTPUT_SET(LED_GPIO, LED_OFF); 675 | flashCount = LED_FLASH_COUNT; 676 | lcState = LC_FLASH_OFF; 677 | } 678 | break; 679 | 680 | case LC_FLASH_OFF: 681 | if(flashCount) 682 | flashCount--; 683 | else{ 684 | GPIO_OUTPUT_SET(LED_GPIO, LED_ON); 685 | flashCount = LED_FLASH_COUNT; 686 | lcState = LC_FLASH; 687 | } 688 | break; 689 | 690 | default: 691 | util_assert(FALSE, "Bad LED control state reached: %d", lcState); 692 | break; 693 | } 694 | 695 | #endif // End WITH_LED 696 | 697 | // For relay cycle 698 | if(commandElements[CMD_CYCLE].p.i){ 699 | if(!relayCycleTimer){ 700 | relayCycleTimer = commandElements[CMD_CYCLE].p.i; 701 | relayToggle(); 702 | } 703 | else relayCycleTimer--; 704 | } 705 | else if(relayCycleTimer) 706 | relayCycleTimer = 0; 707 | } 708 | 709 | /** 710 | * System initialization 711 | * Called once from user_init 712 | */ 713 | 714 | LOCAL void ICACHE_FLASH_ATTR sysInit(void) 715 | { 716 | 717 | char *buf = util_zalloc(256); // Working buffer 718 | 719 | // I/O initialization 720 | gpio_init(); 721 | 722 | #if STANDARD==MODE // Standard init 723 | // Initialize relay GPIO as an output 724 | easygpio_pinMode(RELAY_GPIO, EASYGPIO_NOPULL, EASYGPIO_OUTPUT); 725 | // Initialize output state 726 | GPIO_OUTPUT_SET(RELAY_GPIO, RELAY_OFF); 727 | 728 | // Initialize button GPIO input 729 | easygpio_pinMode(BUTTON_GPIO, EASYGPIO_PULLUP, EASYGPIO_INPUT); 730 | 731 | #endif // End standard init 732 | 733 | #if LATCHING==MODE // Latching Init 734 | // Initialize relay GPIO as an output 735 | easygpio_pinMode(RELAY_SET_GPIO, EASYGPIO_NOPULL, EASYGPIO_OUTPUT); 736 | easygpio_pinMode(RELAY_CLEAR_GPIO, EASYGPIO_NOPULL, EASYGPIO_OUTPUT); 737 | 738 | // Initialize output state 739 | GPIO_OUTPUT_SET(RELAY_SET_GPIO, RELAY_OFF); 740 | GPIO_OUTPUT_SET(RELAY_CLEAR_GPIO, RELAY_OFF); 741 | 742 | // Initialize button GPIO input 743 | easygpio_pinMode(BUTTON_GPIO, EASYGPIO_PULLUP, EASYGPIO_INPUT); 744 | #endif // End Latching Init 745 | 746 | // LED output setup 747 | #ifdef WITH_LED 748 | easygpio_pinMode(LED_GPIO, EASYGPIO_NOPULL, EASYGPIO_OUTPUT); 749 | GPIO_OUTPUT_SET(LED_GPIO, LED_OFF); 750 | #endif // End LED setup 751 | 752 | 753 | // Uart init 754 | uart0_init(BIT_RATE_115200); 755 | 756 | os_delay_us(2000000); // To allow gtkterm to come up 757 | 758 | 759 | // Read in the config sector from flash 760 | configHandle = kvstore_open(KVS_DEFAULT_LOC); 761 | 762 | 763 | const char *ssidKey = commandElements[CMD_SSID].command; 764 | const char *WIFIPassKey = commandElements[CMD_WIFIPASS].command; 765 | const char *btLocalKey = commandElements[CMD_BTLOCAL].command; 766 | const char *devicePathKey = commandElements[CMD_MQTTDEVPATH].command; 767 | 768 | // Check for default configuration overrides 769 | if(!kvstore_exists(configHandle, ssidKey)){ // if no ssid, assume the rest of the defaults need to be set as well 770 | kvstore_put(configHandle, btLocalKey, configInfoBlock.e[MQTTBTLOCAL].value); 771 | kvstore_put(configHandle, ssidKey, configInfoBlock.e[WIFISSID].value); 772 | kvstore_put(configHandle, WIFIPassKey, configInfoBlock.e[WIFIPASS].value); 773 | kvstore_put(configHandle, devicePathKey, configInfoBlock.e[MQTTDEVPATH].value); 774 | 775 | // Write the KVS back out to flash 776 | 777 | kvstore_flush(configHandle); 778 | } 779 | 780 | // Get the configurations we need from the KVS and store them in the commandElement data area 781 | 782 | kvstore_get_integer(configHandle, btLocalKey, &commandElements[CMD_BTLOCAL].p.i); // Retrieve button local 783 | 784 | commandElements[CMD_SSID].p.sp = kvstore_get_string(configHandle, ssidKey); // Retrieve SSID 785 | 786 | commandElements[CMD_WIFIPASS].p.sp = kvstore_get_string(configHandle, WIFIPassKey); // Retrieve WIFI Pass 787 | 788 | commandElements[CMD_MQTTDEVPATH].p.sp = kvstore_get_string(configHandle, devicePathKey); // Retrieve MQTT Device Path 789 | 790 | // Initialize MQTT connection 791 | 792 | uint8_t *host = configInfoBlock.e[MQTTHOST].value; 793 | uint32_t port = (uint32_t) atoi(configInfoBlock.e[MQTTPORT].value); 794 | 795 | MQTT_InitConnection(&mqttClient, host, port, 796 | (uint8_t) atoi(configInfoBlock.e[MQTTSECUR].value)); 797 | 798 | MQTT_InitClient(&mqttClient, configInfoBlock.e[MQTTDEVID].value, 799 | configInfoBlock.e[MQTTUSER].value, configInfoBlock.e[MQTTPASS].value, 800 | atoi(configInfoBlock.e[MQTTKPALIV].value), 1); 801 | 802 | MQTT_OnConnected(&mqttClient, mqttConnectedCb); 803 | MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); 804 | MQTT_OnPublished(&mqttClient, mqttPublishedCb); 805 | MQTT_OnData(&mqttClient, mqttDataCb); 806 | 807 | // Last will and testament 808 | 809 | os_sprintf(buf, "{\"muster\":{\"connstate\":\"offline\",\"device\":\"%s\"}}", configInfoBlock.e[MQTTDEVPATH].value); 810 | MQTT_InitLWT(&mqttClient, infoTopic, buf, 0, 0); 811 | 812 | // Subtopics 813 | commandTopic = util_make_sub_topic(commandElements[CMD_MQTTDEVPATH].p.sp, "command"); 814 | statusTopic = util_make_sub_topic(commandElements[CMD_MQTTDEVPATH].p.sp, "status"); 815 | INFO("Command subtopic: %s\r\n", commandTopic); 816 | INFO("Status subtopic: %s\r\n", statusTopic); 817 | 818 | 819 | // Timers 820 | os_timer_disarm(&pulseTimer); 821 | os_timer_setfn(&pulseTimer, (os_timer_func_t *)pulseTmerExpireCb, (void *)0); 822 | os_timer_disarm(&buttonTimer); 823 | os_timer_setfn(&buttonTimer, (os_timer_func_t *)nodeTimerCb, (void *)0); 824 | 825 | // Attempt WIFI connection 826 | 827 | char *wifipass = commandElements[CMD_WIFIPASS].p.sp; 828 | char *ssid = commandElements[CMD_SSID].p.sp; 829 | 830 | INFO("Attempting connection with: %s\r\n", ssid); 831 | 832 | // Attempt to connect to AP 833 | WIFI_Connect(ssid, wifipass, wifiConnectCb); 834 | 835 | // Timer to execute code every 100 mSec 836 | 837 | os_timer_arm(&buttonTimer, 100, 1); 838 | 839 | #if LATCHING==MODE 840 | lrState = LR_CLEAR; // Latching relay to off state 841 | #endif 842 | 843 | // Free working buffer 844 | util_free(buf); 845 | 846 | INFO("\r\nSystem started ...\r\n"); 847 | 848 | } 849 | 850 | /** 851 | * Called from startup 852 | */ 853 | 854 | void user_init(void) 855 | { 856 | sysInit(); 857 | } 858 | 859 | --------------------------------------------------------------------------------