├── .gitignore ├── LICENSE ├── Makefile ├── Molgan_hack.jpg ├── README.md ├── driver └── stdout │ ├── README.md │ ├── include │ └── stdout │ │ ├── stdout.h │ │ └── uart_hw.h │ └── stdout.c ├── include ├── debug.h └── user_config.h ├── modules ├── Makefile ├── include │ ├── sntp.h │ ├── time_utils.h │ └── wifi.h ├── sntp.c ├── time_utils.c └── wifi.c ├── mqtt ├── Makefile ├── include │ ├── mqtt.h │ ├── mqtt_msg.h │ ├── proto.h │ ├── queue.h │ ├── ringbuf.h │ ├── typedef.h │ └── utils.h ├── mqtt.c ├── mqtt_msg.c ├── proto.c ├── queue.c ├── ringbuf.c └── utils.c ├── schematic.png ├── tools ├── .gitattributes ├── esptool.py └── makefile.sh └── user └── user_main.c /.gitignore: -------------------------------------------------------------------------------- 1 | user_config.h 2 | app_app.a 3 | app.out 4 | 0x*.bin 5 | 6 | # Compiled source # 7 | ################### 8 | *.com 9 | *.class 10 | *.dll 11 | *.exe 12 | *.o 13 | *.so 14 | 15 | # Packages # 16 | ############ 17 | # it's better to unpack these files and commit the raw source 18 | # git has its own built in compression methods 19 | *.7z 20 | *.dmg 21 | *.gz 22 | *.iso 23 | *.jar 24 | *.rar 25 | *.tar 26 | *.zip 27 | 28 | # Logs and databases # 29 | ###################### 30 | *.log 31 | *.sql 32 | *.sqlite 33 | 34 | # OS generated files # 35 | ###################### 36 | .DS_Store 37 | .DS_Store? 38 | ._* 39 | .Spotlight-V100 40 | .Trashes 41 | ehthumbs.db 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 terenceang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /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 | ESPTOOL ?= esptool.py 13 | 14 | 15 | # name for the target project 16 | TARGET = app 17 | 18 | # linker script used for the above linkier step 19 | LD_SCRIPT = eagle.app.v6.ld 20 | 21 | # we create two different files for uploading into the flash 22 | # these are the names and options to generate them 23 | FW_1 = 0x00000 24 | FW_2 = 0x40000 25 | FW_3 = 0x3C000 26 | 27 | BLANKER ?= /opt/Espressif/esp_iot_sdk_v0.9.5/bin/blank.bin 28 | 29 | FLAVOR ?= release 30 | 31 | # Base directory for the compiler 32 | XTENSA_TOOLS_ROOT ?= /opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin 33 | 34 | ############################################################# 35 | # Select compile 36 | # 37 | ifeq ($(OS),Windows_NT) 38 | # WIN32 39 | # We are under windows. 40 | ifeq ($(XTENSA_CORE),lx106) 41 | # It is xcc 42 | AR = xt-ar 43 | CC = xt-xcc 44 | LD = xt-xcc 45 | NM = xt-nm 46 | CPP = xt-cpp 47 | OBJCOPY = xt-objcopy 48 | #MAKE = xt-make 49 | CCFLAGS += -Os --rename-section .text=.irom0.text --rename-section .literal=.irom0.literal 50 | else 51 | # It is gcc, may be cygwin 52 | # Can we use -fdata-sections? 53 | CCFLAGS += -Os -ffunction-sections -fno-jump-tables 54 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 55 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 56 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 57 | NM = $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-nm 58 | CPP = $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-cpp 59 | OBJCOPY = $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-objcopy 60 | endif 61 | ESPPORT ?= com1 62 | SDK_BASE ?= c:/Espressif/ESP8266_SDK 63 | ifeq ($(PROCESSOR_ARCHITECTURE),AMD64) 64 | # ->AMD64 65 | endif 66 | ifeq ($(PROCESSOR_ARCHITECTURE),x86) 67 | # ->IA32 68 | endif 69 | else 70 | # We are under other system, may be Linux. Assume using gcc. 71 | # Can we use -fdata-sections? 72 | #ESPPORT ?= /dev/tty.usbmodem00000001 73 | ESPPORT ?= /dev/tty.uart-D6FF42A21E8C261A 74 | SDK_BASE ?= /opt/Espressif/ESP8266_SDK 75 | 76 | CCFLAGS += -Os -ffunction-sections -fno-jump-tables 77 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 78 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 79 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 80 | NM = $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-nm 81 | CPP = $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-cpp 82 | OBJCOPY = $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-objcopy 83 | UNAME_S := $(shell uname -s) 84 | 85 | ifeq ($(UNAME_S),Linux) 86 | # LINUX 87 | endif 88 | ifeq ($(UNAME_S),Darwin) 89 | # OSX 90 | endif 91 | UNAME_P := $(shell uname -p) 92 | ifeq ($(UNAME_P),x86_64) 93 | # ->AMD64 94 | endif 95 | ifneq ($(filter %86,$(UNAME_P)),) 96 | # ->IA32 97 | endif 98 | ifneq ($(filter arm%,$(UNAME_P)),) 99 | # ->ARM 100 | endif 101 | endif 102 | ############################################################# 103 | 104 | 105 | # which modules (subdirectories) of the project to include in compiling 106 | MODULES = driver mqtt user modules driver/stdout 107 | EXTRA_INCDIR = include $(SDK_BASE)/../include $(SDK_BASE)/ThirdParty/../include 108 | 109 | # libraries used in this project, mainly provided by the SDK 110 | LIBS = c gcc hal phy pp net80211 lwip wpa main ssl 111 | 112 | # compiler flags using during compilation of source files 113 | CFLAGS = -Os -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH 114 | 115 | # linker flags used to generate the main object file 116 | LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static 117 | 118 | ifeq ($(FLAVOR),debug) 119 | CFLAGS += -g -O0 120 | LDFLAGS += -g -O0 121 | endif 122 | 123 | ifeq ($(FLAVOR),release) 124 | CFLAGS += -g -O2 125 | LDFLAGS += -g -O2 126 | endif 127 | 128 | 129 | 130 | # various paths from the SDK used in this project 131 | SDK_LIBDIR = lib 132 | SDK_LDDIR = ld 133 | SDK_INCDIR = include include/json 134 | 135 | #### 136 | #### no user configurable options below here 137 | #### 138 | FW_TOOL ?= $(ESPTOOL) 139 | SRC_DIR := $(MODULES) 140 | BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) 141 | 142 | SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) 143 | SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) 144 | 145 | SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) 146 | OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) 147 | LIBS := $(addprefix -l,$(LIBS)) 148 | APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) 149 | TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) 150 | 151 | LD_SCRIPT := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT)) 152 | 153 | INCDIR := $(addprefix -I,$(SRC_DIR)) 154 | EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) 155 | MODULE_INCDIR := $(addsuffix /include,$(INCDIR)) 156 | 157 | FW_FILE_1 := $(addprefix $(FW_BASE)/,$(FW_1).bin) 158 | FW_FILE_2 := $(addprefix $(FW_BASE)/,$(FW_2).bin) 159 | 160 | V ?= $(VERBOSE) 161 | ifeq ("$(V)","1") 162 | Q := 163 | vecho := @true 164 | else 165 | Q := @ 166 | vecho := @echo 167 | endif 168 | 169 | vpath %.c $(SRC_DIR) 170 | 171 | define compile-objects 172 | $1/%.o: %.c 173 | $(vecho) "CC $$<" 174 | $(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 175 | endef 176 | 177 | .PHONY: all checkdirs clean 178 | 179 | all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2) 180 | 181 | $(FW_FILE_1): $(TARGET_OUT) 182 | $(vecho) "FW $@" 183 | $(ESPTOOL) elf2image $< -o $(FW_BASE)/ 184 | 185 | $(FW_FILE_2): $(TARGET_OUT) 186 | $(vecho) "FW $@" 187 | $(ESPTOOL) elf2image $< -o $(FW_BASE)/ 188 | 189 | $(TARGET_OUT): $(APP_AR) 190 | $(vecho) "LD $@" 191 | $(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ 192 | 193 | $(APP_AR): $(OBJ) 194 | $(vecho) "AR $@" 195 | $(Q) $(AR) cru $@ $^ 196 | 197 | checkdirs: $(BUILD_DIR) $(FW_BASE) 198 | 199 | $(BUILD_DIR): 200 | $(Q) mkdir -p $@ 201 | 202 | firmware: 203 | $(Q) mkdir -p $@ 204 | 205 | flash: $(FW_FILE_1) $(FW_FILE_2) 206 | $(ESPTOOL) -p $(ESPPORT) write_flash $(FW_1) $(FW_FILE_1) $(FW_2) $(FW_FILE_2) 207 | 208 | test: 209 | screen $(ESPPORT) 115200 210 | 211 | blank: $(FW_FILE_3) 212 | $(ESPTOOL) -p $(ESPPORT) write_flash $(FW_3) $(BLANKER) 213 | 214 | rebuild: clean all 215 | 216 | clean: 217 | $(Q) rm -f $(APP_AR) 218 | $(Q) rm -f $(TARGET_OUT) 219 | $(Q) rm -rf $(BUILD_DIR) 220 | $(Q) rm -rf $(BUILD_BASE) 221 | $(Q) rm -f $(FW_FILE_1) 222 | $(Q) rm -f $(FW_FILE_2) 223 | $(Q) rm -rf $(FW_BASE) 224 | 225 | $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir)))) 226 | -------------------------------------------------------------------------------- /Molgan_hack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terenceang/esp8266-PIR/02eff82440a65c36df6f8ddba35a3fb7f8433ed6/Molgan_hack.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **************************************************************** 2 | **ESP8266 PIR - Hacking the Ikea Molgan** 3 | 4 | Works and tested on ESP8266-01 5 | 6 | ![alt tag](https://raw.githubusercontent.com/terenceang/esp8266-PIR/master/Molgan_hack.jpg) 7 | 8 | **************************************************************** 9 | **Features** 10 | **************************************************************** 11 | 12 | Unit resets. 13 | Publishes a timestamped message to a MQTT broker and goes into deepsleep. 14 | 15 | **************************************************************** 16 | 17 | **GPIO code from SDK Example folder + various sources on the net.** 18 | 19 | **************************************************************** 20 | **esp8266_stdout** 21 | **************************************************************** 22 | 23 | 24 | **************************************************************** 25 | 26 | **MQTT code based on https://github.com/tuanpmt/esp_mqtt** 27 | 28 | **************************************************************** 29 | 30 | LED + and LED - goes replaces the LED on the PIR light. 31 | more info at :- coming soon. 32 | 33 | ![alt tag](https://raw.githubusercontent.com/terenceang/esp8266-PIR/master/schematic.png) 34 | 35 | # esp8266-PIR 36 | -------------------------------------------------------------------------------- /driver/stdout/README.md: -------------------------------------------------------------------------------- 1 | # esp8266_stdout 2 | A clone of Jeroen Domburg's () stdout driver. 3 | It's an alternative uart driver that only uses the TX pin, so the RX pin is free for GPIO use. 4 | 5 | Needless to say, but this driver can only transmit. 6 | 7 | ###License: 8 | "THE BEER-WARE LICENSE" (Revision 42): 9 | Jeroen Domburg wrote this file. As long as you retain 10 | this notice you can do whatever you want with this stuff. If we meet some day, 11 | and you think this stuff is worth it, you can buy me a beer in return. 12 | 13 | 14 | -------------------------------------------------------------------------------- /driver/stdout/include/stdout/stdout.h: -------------------------------------------------------------------------------- 1 | //Stupid bit of code that does the bare minimum to make os_printf work. 2 | 3 | /* 4 | * ---------------------------------------------------------------------------- 5 | * "THE BEER-WARE LICENSE" (Revision 42): 6 | * Jeroen Domburg wrote this file. As long as you retain 7 | * this notice you can do whatever you want with this stuff. If we meet some day, 8 | * and you think this stuff is worth it, you can buy me a beer in return. 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | 12 | void stdout_init(void); 13 | -------------------------------------------------------------------------------- /driver/stdout/include/stdout/uart_hw.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 | 130 | #define RX_BUFF_SIZE 256 131 | #define TX_BUFF_SIZE 100 132 | #define UART0 0 133 | #define UART1 1 134 | 135 | typedef enum { 136 | FIVE_BITS = 0x0, 137 | SIX_BITS = 0x1, 138 | SEVEN_BITS = 0x2, 139 | EIGHT_BITS = 0x3 140 | } UartBitsNum4Char; 141 | 142 | typedef enum { 143 | ONE_STOP_BIT = 0, 144 | ONE_HALF_STOP_BIT = BIT2, 145 | TWO_STOP_BIT = BIT2 146 | } UartStopBitsNum; 147 | 148 | typedef enum { 149 | NONE_BITS = 0, 150 | ODD_BITS = 0, 151 | EVEN_BITS = BIT4 152 | } UartParityMode; 153 | 154 | typedef enum { 155 | STICK_PARITY_DIS = 0, 156 | STICK_PARITY_EN = BIT3 | BIT5 157 | } UartExistParity; 158 | 159 | typedef enum { 160 | BIT_RATE_9600 = 9600, 161 | BIT_RATE_19200 = 19200, 162 | BIT_RATE_38400 = 38400, 163 | BIT_RATE_57600 = 57600, 164 | BIT_RATE_74880 = 74880, 165 | BIT_RATE_115200 = 115200, 166 | BIT_RATE_230400 = 230400, 167 | BIT_RATE_460800 = 460800, 168 | BIT_RATE_921600 = 921600 169 | } UartBautRate; 170 | 171 | typedef enum { 172 | NONE_CTRL, 173 | HARDWARE_CTRL, 174 | XON_XOFF_CTRL 175 | } UartFlowCtrl; 176 | 177 | typedef enum { 178 | EMPTY, 179 | UNDER_WRITE, 180 | WRITE_OVER 181 | } RcvMsgBuffState; 182 | 183 | typedef struct { 184 | uint32 TrxBuffSize; 185 | uint8 *pTrxBuff; 186 | } TrxMsgBuff; 187 | 188 | typedef enum { 189 | BAUD_RATE_DET, 190 | WAIT_SYNC_FRM, 191 | SRCH_MSG_HEAD, 192 | RCV_MSG_BODY, 193 | RCV_ESC_CHAR, 194 | } RcvMsgState; 195 | 196 | -------------------------------------------------------------------------------- /driver/stdout/stdout.c: -------------------------------------------------------------------------------- 1 | //Stupid bit of code that does the bare minimum to make os_printf work. 2 | 3 | /* 4 | * ---------------------------------------------------------------------------- 5 | * "THE BEER-WARE LICENSE" (Revision 42): 6 | * Jeroen Domburg wrote this file. As long as you retain 7 | * this notice you can do whatever you want with this stuff. If we meet some day, 8 | * and you think this stuff is worth it, you can buy me a beer in return. 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | #include "stdout/stdout.h" 12 | #include "ets_sys.h" 13 | #include "osapi.h" 14 | #include "stdout/uart_hw.h" 15 | 16 | static void ICACHE_FLASH_ATTR stdoutUartTxd(char c) { 17 | //Wait until there is room in the FIFO 18 | while (((READ_PERI_REG(UART_STATUS(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ; 19 | //Send the character 20 | WRITE_PERI_REG(UART_FIFO(0), c); 21 | } 22 | 23 | static void ICACHE_FLASH_ATTR stdoutPutchar(char c) { 24 | //convert \n -> \r\n 25 | if (c=='\n') stdoutUartTxd('\r'); 26 | stdoutUartTxd(c); 27 | } 28 | 29 | 30 | void ICACHE_FLASH_ATTR 31 | stdout_init() { 32 | //Enable TxD pin 33 | PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); 34 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); 35 | 36 | //Set baud rate and other serial parameters to 115200,n,8,1 37 | uart_div_modify(0, UART_CLK_FREQ/BIT_RATE_115200); 38 | WRITE_PERI_REG(UART_CONF0(0), (STICK_PARITY_DIS)|(ONE_STOP_BIT << UART_STOP_BIT_NUM_S)| \ 39 | (EIGHT_BITS << UART_BIT_NUM_S)); 40 | 41 | //Reset tx & rx fifo 42 | SET_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST|UART_TXFIFO_RST); 43 | CLEAR_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST|UART_TXFIFO_RST); 44 | //Clear pending interrupts 45 | WRITE_PERI_REG(UART_INT_CLR(0), 0xffff); 46 | 47 | //Install our own putchar handler 48 | os_install_putc1((void *)stdoutPutchar); 49 | } 50 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * debug.h 3 | * 4 | * Created on: Dec 4, 2014 5 | * Author: Minh 6 | */ 7 | 8 | #define DEBUG 0 9 | 10 | #ifndef USER_DEBUG_H_ 11 | #define USER_DEBUG_H_ 12 | 13 | #ifndef INFO 14 | //#define INFO os_printf 15 | #define INFO if (DEBUG == 1) os_printf 16 | #endif 17 | 18 | #endif /* USER_DEBUG_H_ */ 19 | -------------------------------------------------------------------------------- /include/user_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _USER_CONFIG_H_ 2 | #define _USER_CONFIG_H_ 3 | 4 | #define CLIENT_SSL_ENABLE 5 | 6 | #define LWIP_OPEN_SRC 7 | 8 | #define TZ_ADJ 28800 // Adjust time to GMT+8 9 | 10 | #define AP_RETRY 4 // no of retry before giving up 11 | 12 | /*DEFAULT CONFIGURATIONS*/ 13 | 14 | #define MQTT_HOST "192.168.1.100" //or "mqtt.yourdomain.com" 15 | #define MQTT_PORT 1883 16 | #define MQTT_BUF_SIZE 1024 17 | #define MQTT_KEEPALIVE 120 /*second*/ 18 | 19 | #define MQTT_CLIENT_ID "ESP-PIR" 20 | #define USE_CHIP_ID 21 | 22 | #define MQTT_USER "ESP01" 23 | #define MQTT_PASS "esp01" 24 | 25 | #define STA_SSID "yourSSID" 26 | #define STA_PASS "yourPASS" 27 | #define STA_TYPE AUTH_WPA2_PSK 28 | 29 | #define MQTT_RECONNECT_TIMEOUT 5 /*second*/ 30 | 31 | #define DEFAULT_SECURITY 0 32 | #define QUEUE_BUFFER_SIZE 2048 33 | 34 | //#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ 35 | #define PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/ 36 | #endif -------------------------------------------------------------------------------- /modules/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 | -------------------------------------------------------------------------------- /modules/include/sntp.h: -------------------------------------------------------------------------------- 1 | #ifndef __SNTP_H__ 2 | #define __SNTP_H__ 3 | 4 | #include "lwip/ip_addr.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #include 11 | 12 | extern time_t sntp_time; 13 | 14 | typedef void (*sntpCallback)(time_t); 15 | 16 | void sntp_init(sntpCallback cb); 17 | void sntp_stop(void); 18 | 19 | extern void sntp_send_request(ip_addr_t *server_addr); 20 | extern void sntp_request(void *arg); 21 | 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | #endif /* __SNTP_H__ */ -------------------------------------------------------------------------------- /modules/include/time_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __TIME_UTILS_H 2 | #define __TIME_UTILS_H 3 | 4 | char *epoch_to_str(unsigned long epoch); 5 | 6 | #endif -------------------------------------------------------------------------------- /modules/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 | 13 | extern uint8_t ssid_scan_done, ssid_found; 14 | 15 | void ICACHE_FLASH_ATTR WIFI_Connect(uint8_t* ssid, uint8_t* pass, WifiCallback cb); 16 | void ICACHE_FLASH_ATTR sleep_timer_func(void); 17 | 18 | #endif /* USER_WIFI_H_ */ 19 | -------------------------------------------------------------------------------- /modules/sntp.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * SNTP client module 4 | * 5 | * This is simple "SNTP" client for the lwIP raw API. 6 | * It is a minimal implementation of SNTPv4 as specified in RFC 4330. 7 | * 8 | * For a list of some public NTP servers, see this link : 9 | * http://support.ntp.org/bin/view/Servers/NTPPoolServers 10 | * 11 | * @todo: 12 | * - set/change servers at runtime 13 | * - complete SNTP_CHECK_RESPONSE checks 3 and 4 14 | * - support broadcast/multicast mode? 15 | */ 16 | 17 | /* 18 | * Redistribution and use in source and binary forms, with or without modification, 19 | * are permitted provided that the following conditions are met: 20 | * 21 | * 1. Redistributions of source code must retain the above copyright notice, 22 | * this list of conditions and the following disclaimer. 23 | * 2. Redistributions in binary form must reproduce the above copyright notice, 24 | * this list of conditions and the following disclaimer in the documentation 25 | * and/or other materials provided with the distribution. 26 | * 3. The name of the author may not be used to endorse or promote products 27 | * derived from this software without specific prior written permission. 28 | * 29 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 30 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 31 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 32 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 33 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 34 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 37 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 38 | * OF SUCH DAMAGE. 39 | * 40 | * This file is part of the lwIP TCP/IP stack. 41 | * 42 | * Author: Simon Goldschmidt (lwIP raw API part) 43 | */ 44 | 45 | #include "lwip/opt.h" 46 | 47 | #include "sntp.h" 48 | #include "debug.h" 49 | 50 | #include "lwip/timers.h" 51 | #include "lwip/udp.h" 52 | #include "lwip/dns.h" 53 | #include "lwip/ip_addr.h" 54 | #include "lwip/pbuf.h" 55 | 56 | #include "osapi.h" 57 | #include "os_type.h" 58 | 59 | #include 60 | #include 61 | 62 | #if LWIP_UDP 63 | 64 | /** SNTP server port */ 65 | #ifndef SNTP_PORT 66 | #define SNTP_PORT 123 67 | #endif 68 | 69 | /** Set this to 1 to allow SNTP_SERVER_ADDRESS to be a DNS name */ 70 | #ifndef SNTP_SERVER_DNS 71 | #define SNTP_SERVER_DNS 1 72 | #endif 73 | 74 | /** Set this to 1 to support more than one server */ 75 | #ifndef SNTP_SUPPORT_MULTIPLE_SERVERS 76 | #define SNTP_SUPPORT_MULTIPLE_SERVERS 0 77 | #endif 78 | 79 | /** \def SNTP_SERVER_ADDRESS 80 | * \brief SNTP server address: 81 | * - as IPv4 address in "u32_t" format 82 | * - as a DNS name if SNTP_SERVER_DNS is set to 1 83 | * May contain multiple server names (e.g. "pool.ntp.org","second.time.server") 84 | */ 85 | #ifndef SNTP_SERVER_ADDRESS 86 | #if SNTP_SERVER_DNS 87 | #define SNTP_SERVER_ADDRESS "pool.ntp.org" 88 | #else 89 | #define SNTP_SERVER_ADDRESS "213.161.194.93" /* pool.ntp.org */ 90 | #endif 91 | #endif 92 | 93 | /** Sanity check: 94 | * Define this to 95 | * - 0 to turn off sanity checks (default; smaller code) 96 | * - >= 1 to check address and port of the response packet to ensure the 97 | * response comes from the server we sent the request to. 98 | * - >= 2 to check returned Originate Timestamp against Transmit Timestamp 99 | * sent to the server (to ensure response to older request). 100 | * - >= 3 @todo: discard reply if any of the LI, Stratum, or Transmit Timestamp 101 | * fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast). 102 | * - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each 103 | * greater than or equal to 0 and less than infinity, where infinity is 104 | * currently a cozy number like one second. This check avoids using a 105 | * server whose synchronization source has expired for a very long time. 106 | */ 107 | #ifndef SNTP_CHECK_RESPONSE 108 | #define SNTP_CHECK_RESPONSE 0 109 | #endif 110 | 111 | /** According to the RFC, this shall be a random delay 112 | * between 1 and 5 minutes (in milliseconds) to prevent load peaks. 113 | * This can be defined to a random generation function, 114 | * which must return the delay in milliseconds as u32_t. 115 | * Turned off by default. 116 | */ 117 | #ifndef SNTP_STARTUP_DELAY 118 | #define SNTP_STARTUP_DELAY 0 119 | #endif 120 | 121 | /** SNTP receive timeout - in milliseconds 122 | * Also used as retry timeout - this shouldn't be too low. 123 | * Default is 3 seconds. 124 | */ 125 | #ifndef SNTP_RECV_TIMEOUT 126 | #define SNTP_RECV_TIMEOUT 3000 127 | #endif 128 | 129 | /** SNTP update delay - in milliseconds 130 | * Default is 1 hour. 131 | */ 132 | #ifndef SNTP_UPDATE_DELAY 133 | #define SNTP_UPDATE_DELAY 3600000 134 | #endif 135 | #if (SNTP_UPDATE_DELAY < 15000) && !SNTP_SUPPRESS_DELAY_CHECK 136 | #error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds!" 137 | #endif 138 | 139 | /** SNTP macro to change system time and/or the update the RTC clock */ 140 | #ifndef SNTP_SET_SYSTEM_TIME 141 | #define SNTP_SET_SYSTEM_TIME(sec) ((void)sec) 142 | #endif 143 | 144 | /** SNTP macro to change system time including microseconds */ 145 | #ifdef SNTP_SET_SYSTEM_TIME_US 146 | #define SNTP_CALC_TIME_US 1 147 | #define SNTP_RECEIVE_TIME_SIZE 2 148 | #else 149 | #define SNTP_SET_SYSTEM_TIME_US(sec, us) 150 | #define SNTP_CALC_TIME_US 0 151 | #define SNTP_RECEIVE_TIME_SIZE 1 152 | #endif 153 | 154 | /** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2 155 | * to send in request and compare in response. 156 | */ 157 | #ifndef SNTP_GET_SYSTEM_TIME 158 | #define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0) 159 | #endif 160 | 161 | /** Default retry timeout (in milliseconds) if the response 162 | * received is invalid. 163 | * This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached. 164 | */ 165 | #ifndef SNTP_RETRY_TIMEOUT 166 | #define SNTP_RETRY_TIMEOUT SNTP_RECV_TIMEOUT 167 | #endif 168 | 169 | /** Maximum retry timeout (in milliseconds). */ 170 | #ifndef SNTP_RETRY_TIMEOUT_MAX 171 | #define SNTP_RETRY_TIMEOUT_MAX (SNTP_RETRY_TIMEOUT * 10) 172 | #endif 173 | 174 | /** Increase retry timeout with every retry sent 175 | * Default is on to conform to RFC. 176 | */ 177 | #ifndef SNTP_RETRY_TIMEOUT_EXP 178 | #define SNTP_RETRY_TIMEOUT_EXP 1 179 | #endif 180 | 181 | /* the various debug levels for this file */ 182 | #define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE) 183 | #define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE) 184 | #define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING) 185 | #define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) 186 | #define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS) 187 | 188 | #define SNTP_ERR_KOD 1 189 | 190 | /* SNTP protocol defines */ 191 | #define SNTP_MSG_LEN 48 192 | 193 | #define SNTP_OFFSET_LI_VN_MODE 0 194 | #define SNTP_LI_MASK 0xC0 195 | #define SNTP_LI_NO_WARNING 0x00 196 | #define SNTP_LI_LAST_MINUTE_61_SEC 0x01 197 | #define SNTP_LI_LAST_MINUTE_59_SEC 0x02 198 | #define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */ 199 | 200 | #define SNTP_VERSION_MASK 0x38 201 | #define SNTP_VERSION (4/* NTP Version 4*/<<3) 202 | 203 | #define SNTP_MODE_MASK 0x07 204 | #define SNTP_MODE_CLIENT 0x03 205 | #define SNTP_MODE_SERVER 0x04 206 | #define SNTP_MODE_BROADCAST 0x05 207 | 208 | #define SNTP_OFFSET_STRATUM 1 209 | #define SNTP_STRATUM_KOD 0x00 210 | 211 | #define SNTP_OFFSET_ORIGINATE_TIME 24 212 | #define SNTP_OFFSET_RECEIVE_TIME 32 213 | #define SNTP_OFFSET_TRANSMIT_TIME 40 214 | 215 | /* number of seconds between 1900 and 1970 */ 216 | #define DIFF_SEC_1900_1970 (2208988800UL) 217 | 218 | #ifndef TZ_ADJ 219 | #define TZ_ADJ 0 220 | #endif 221 | 222 | /** 223 | * SNTP packet format (without optional fields) 224 | * Timestamps are coded as 64 bits: 225 | * - 32 bits seconds since Jan 01, 1970, 00:00 226 | * - 32 bits seconds fraction (0-padded) 227 | * For future use, if the MSB in the seconds part is set, seconds are based 228 | * on Feb 07, 2036, 06:28:16. 229 | */ 230 | #ifdef PACK_STRUCT_USE_INCLUDES 231 | # include "arch/bpstruct.h" 232 | #endif 233 | PACK_STRUCT_BEGIN 234 | struct sntp_msg { 235 | PACK_STRUCT_FIELD(u8_t li_vn_mode); 236 | PACK_STRUCT_FIELD(u8_t stratum); 237 | PACK_STRUCT_FIELD(u8_t poll); 238 | PACK_STRUCT_FIELD(u8_t precision); 239 | PACK_STRUCT_FIELD(u32_t root_delay); 240 | PACK_STRUCT_FIELD(u32_t root_dispersion); 241 | PACK_STRUCT_FIELD(u32_t reference_identifier); 242 | PACK_STRUCT_FIELD(u32_t reference_timestamp[2]); 243 | PACK_STRUCT_FIELD(u32_t originate_timestamp[2]); 244 | PACK_STRUCT_FIELD(u32_t receive_timestamp[2]); 245 | PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]); 246 | } PACK_STRUCT_STRUCT; 247 | PACK_STRUCT_END 248 | #ifdef PACK_STRUCT_USE_INCLUDES 249 | # include "arch/epstruct.h" 250 | #endif 251 | 252 | sntpCallback sntpCb = NULL; 253 | static os_timer_t ntp_timer; 254 | 255 | /* function prototypes */ 256 | //static void sntp_request(void *arg); 257 | 258 | /** The UDP pcb used by the SNTP client */ 259 | static struct udp_pcb* sntp_pcb; 260 | /** Addresses of servers */ 261 | static char* sntp_server_addresses[] = {SNTP_SERVER_ADDRESS}; 262 | #if SNTP_SUPPORT_MULTIPLE_SERVERS 263 | /** The currently used server (initialized to 0) */ 264 | static u8_t sntp_current_server; 265 | static u8_t sntp_num_servers = sizeof(sntp_server_addresses)/sizeof(char*); 266 | #else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ 267 | #define sntp_current_server 0 268 | #endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ 269 | 270 | #if SNTP_RETRY_TIMEOUT_EXP 271 | #define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT 272 | /** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */ 273 | static u32_t sntp_retry_timeout; 274 | #else /* SNTP_RETRY_TIMEOUT_EXP */ 275 | #define SNTP_RESET_RETRY_TIMEOUT() 276 | #define sntp_retry_timeout SNTP_RETRY_TIMEOUT 277 | #endif /* SNTP_RETRY_TIMEOUT_EXP */ 278 | 279 | #if SNTP_CHECK_RESPONSE >= 1 280 | /** Saves the last server address to compare with response */ 281 | static ip_addr_t sntp_last_server_address; 282 | #endif /* SNTP_CHECK_RESPONSE >= 1 */ 283 | 284 | #if SNTP_CHECK_RESPONSE >= 2 285 | /** Saves the last timestamp sent (which is sent back by the server) 286 | * to compare against in response */ 287 | static u32_t sntp_last_timestamp_sent[2]; 288 | #endif /* SNTP_CHECK_RESPONSE >= 2 */ 289 | 290 | time_t sntp_time; 291 | void ICACHE_FLASH_ATTR 292 | ntp_time_update(void *); 293 | 294 | /** 295 | * SNTP processing of received timestamp 296 | */ 297 | static void 298 | sntp_process(u32_t *receive_timestamp) 299 | { 300 | /* convert SNTP time (1900-based) to unix GMT time (1970-based) 301 | * @todo: if MSB is 1, SNTP time is 2036-based! 302 | */ 303 | time_t t = (ntohl(receive_timestamp[0]) - DIFF_SEC_1900_1970); 304 | sntp_time = t + TZ_ADJ; 305 | #if SNTP_CALC_TIME_US 306 | u32_t us = ntohl(receive_timestamp[1]) / 4295; 307 | SNTP_SET_SYSTEM_TIME_US(t, us); 308 | 309 | /* display local time from GMT time */ 310 | INFO("sntp_process: %u" , t); 311 | 312 | #else /* SNTP_CALC_TIME_US */ 313 | 314 | /* change system time and/or the update the RTC clock */ 315 | SNTP_SET_SYSTEM_TIME(t); 316 | 317 | /* display local time from GMT time */ 318 | INFO("sntp_process: %u", t); 319 | #endif /* SNTP_CALC_TIME_US */ 320 | os_timer_disarm(&ntp_timer); 321 | ets_timer_arm_new(&ntp_timer,1000,1); 322 | INFO("\nArming NTP timer...\n"); 323 | 324 | if(sntpCb) 325 | sntpCb(sntp_time); 326 | } 327 | 328 | /** 329 | * Initialize request struct to be sent to server. 330 | */ 331 | static void 332 | sntp_initialize_request(struct sntp_msg *req) 333 | { 334 | 335 | INFO("sntp_initialize_request\n"); 336 | 337 | memset(req, 0, SNTP_MSG_LEN); 338 | req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT; 339 | 340 | #if SNTP_CHECK_RESPONSE >= 2 341 | { 342 | u32_t sntp_time_sec, sntp_time_us; 343 | /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */ 344 | SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us); 345 | sntp_last_timestamp_sent[0] = htonl(sntp_time_sec + DIFF_SEC_1900_1970); 346 | req->transmit_timestamp[0] = sntp_last_timestamp_sent[0]; 347 | /* we send/save us instead of fraction to be faster... */ 348 | sntp_last_timestamp_sent[1] = htonl(sntp_time_us); 349 | req->transmit_timestamp[1] = sntp_last_timestamp_sent[1]; 350 | } 351 | #endif /* SNTP_CHECK_RESPONSE >= 2 */ 352 | } 353 | 354 | /** 355 | * Retry: send a new request (and increase retry timeout). 356 | * 357 | * @param arg is unused (only necessary to conform to sys_timeout) 358 | */ 359 | static void 360 | sntp_retry(void* arg) 361 | { 362 | LWIP_UNUSED_ARG(arg); 363 | 364 | /* set up a timer to send a retry and increase the retry delay */ 365 | sys_timeout(sntp_retry_timeout, sntp_request, NULL); 366 | 367 | #if SNTP_RETRY_TIMEOUT_EXP 368 | { 369 | u32_t new_retry_timeout; 370 | /* increase the timeout for next retry */ 371 | new_retry_timeout = sntp_retry_timeout << 1; 372 | /* limit to maximum timeout and prevent overflow */ 373 | if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) && 374 | (new_retry_timeout > sntp_retry_timeout)) { 375 | sntp_retry_timeout = new_retry_timeout; 376 | } 377 | } 378 | #endif /* SNTP_RETRY_TIMEOUT_EXP */ 379 | } 380 | 381 | #if SNTP_SUPPORT_MULTIPLE_SERVERS 382 | /** 383 | * If Kiss-of-Death is received (or another packet parsing error), 384 | * try the next server or retry the current server and increase the retry 385 | * timeout if only one server is available. 386 | * 387 | * @param arg is unused (only necessary to conform to sys_timeout) 388 | */ 389 | static void 390 | sntp_try_next_server(void* arg) 391 | { 392 | LWIP_UNUSED_ARG(arg); 393 | 394 | INFO("Trying next server\r\n"); 395 | 396 | if (sntp_num_servers > 1) { 397 | /* new server: reset retry timeout */ 398 | SNTP_RESET_RETRY_TIMEOUT(); 399 | sntp_current_server++; 400 | if (sntp_current_server >= sntp_num_servers) { 401 | sntp_current_server = 0; 402 | } 403 | 404 | (u16_t)sntp_current_server)); 405 | INFO("sntp_try_next_server: Sending request to server %"U16_F"\n", (u16_t)sntp_current_server); 406 | /* instantly send a request to the next server */ 407 | sntp_request(NULL); 408 | } else { 409 | INFO("Retrying null\n"); 410 | sntp_retry(NULL); 411 | } 412 | } 413 | #else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ 414 | /* Always retry on error if only one server is supported */ 415 | #define sntp_try_next_server sntp_retry 416 | #endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ 417 | 418 | /** UDP recv callback for the sntp pcb */ 419 | static void 420 | sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) 421 | { 422 | u8_t mode; 423 | u8_t stratum; 424 | u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE]; 425 | err_t err; 426 | 427 | LWIP_UNUSED_ARG(arg); 428 | LWIP_UNUSED_ARG(pcb); 429 | 430 | INFO("sntp recv\n"); 431 | 432 | /* packet received: stop retry timeout */ 433 | sys_untimeout(sntp_try_next_server, NULL); 434 | sys_untimeout(sntp_request, NULL); 435 | 436 | err = ERR_ARG; 437 | #if SNTP_CHECK_RESPONSE >= 1 438 | /* check server address and port */ 439 | if (ip_addr_cmp(addr, &sntp_last_server_address) && 440 | (port == SNTP_PORT)) 441 | #else /* SNTP_CHECK_RESPONSE >= 1 */ 442 | LWIP_UNUSED_ARG(addr); 443 | LWIP_UNUSED_ARG(port); 444 | #endif /* SNTP_CHECK_RESPONSE >= 1 */ 445 | { 446 | /* process the response */ 447 | if (p->tot_len == SNTP_MSG_LEN) { 448 | pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE); 449 | mode &= SNTP_MODE_MASK; 450 | /* if this is a SNTP response... */ 451 | if ((mode == SNTP_MODE_SERVER) || 452 | (mode == SNTP_MODE_BROADCAST)) { 453 | pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM); 454 | if (stratum == SNTP_STRATUM_KOD) { 455 | /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ 456 | err = SNTP_ERR_KOD; 457 | } else { 458 | #if SNTP_CHECK_RESPONSE >= 2 459 | /* check originate_timetamp against sntp_last_timestamp_sent */ 460 | u32_t originate_timestamp[2]; 461 | pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME); 462 | if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) || 463 | (originate_timestamp[1] != sntp_last_timestamp_sent[1])) 464 | { 465 | } else 466 | #endif /* SNTP_CHECK_RESPONSE >= 2 */ 467 | /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */ 468 | { 469 | /* correct answer */ 470 | err = ERR_OK; 471 | pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_RECEIVE_TIME); 472 | } 473 | } 474 | } else { 475 | } 476 | } else { 477 | } 478 | } 479 | pbuf_free(p); 480 | if (err == ERR_OK) { 481 | /* Correct response, reset retry timeout */ 482 | SNTP_RESET_RETRY_TIMEOUT(); 483 | 484 | sntp_process(receive_timestamp); 485 | 486 | /* Set up timeout for next request */ 487 | sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL); 488 | 489 | } else if (err == SNTP_ERR_KOD) { 490 | /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ 491 | sntp_try_next_server(NULL); 492 | } else { 493 | /* another error, try the same server again */ 494 | sntp_retry(NULL); 495 | } 496 | } 497 | 498 | /** Actually send an sntp request to a server. 499 | * 500 | * @param server_addr resolved IP address of the SNTP server 501 | */ 502 | void 503 | sntp_send_request(ip_addr_t *server_addr) 504 | { 505 | struct pbuf* p; 506 | p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM); 507 | if (p != NULL) { 508 | struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload; 509 | 510 | INFO("sntp_send_request: Sending request to server\n"); 511 | /* initialize request message */ 512 | sntp_initialize_request(sntpmsg); 513 | /* send request */ 514 | udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT); 515 | 516 | INFO("sntp_send_request: Sent request to server\n"); 517 | 518 | /* free the pbuf after sending it */ 519 | pbuf_free(p); 520 | 521 | INFO("sntp_send_request: freed request\n"); 522 | 523 | /* set up receive timeout: try next server or retry on timeout */ 524 | sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL); 525 | #if SNTP_CHECK_RESPONSE >= 1 526 | /* save server address to verify it in sntp_recv */ 527 | ip_addr_set(&sntp_last_server_address, server_addr); 528 | #endif /* SNTP_CHECK_RESPONSE >= 1 */ 529 | } else { 530 | 531 | INFO("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",(u32_t)SNTP_RETRY_TIMEOUT); 532 | /* out of memory: set up a timer to send a retry */ 533 | sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL); 534 | } 535 | INFO("sntp_send_request: finished\n"); 536 | } 537 | 538 | #if SNTP_SERVER_DNS 539 | /** 540 | * DNS found callback when using DNS names as server address. 541 | */ 542 | static void 543 | sntp_dns_found(const char* hostname, ip_addr_t *ipaddr, void *arg) 544 | { 545 | LWIP_UNUSED_ARG(hostname); 546 | LWIP_UNUSED_ARG(arg); 547 | 548 | if (ipaddr != NULL) { 549 | /* Address resolved, send request */ 550 | 551 | sntp_send_request(ipaddr); 552 | } else { 553 | /* DNS resolving failed -> try another server */ 554 | 555 | sntp_try_next_server(NULL); 556 | } 557 | } 558 | #endif /* SNTP_SERVER_DNS */ 559 | 560 | /** 561 | * Send out an sntp request. 562 | * 563 | * @param arg is unused (only necessary to conform to sys_timeout) 564 | */ 565 | void 566 | sntp_request(void *arg) 567 | { 568 | ip_addr_t sntp_server_address; 569 | err_t err; 570 | 571 | LWIP_UNUSED_ARG(arg); 572 | 573 | /* initialize SNTP server address */ 574 | #if SNTP_SERVER_DNS 575 | err = dns_gethostbyname(sntp_server_addresses[sntp_current_server], &sntp_server_address, 576 | sntp_dns_found, NULL); 577 | if (err == ERR_INPROGRESS) { 578 | /* DNS request sent, wait for sntp_dns_found being called */ 579 | return; 580 | } 581 | #else /* SNTP_SERVER_DNS */ 582 | err = ipaddr_aton(sntp_server_addresses[sntp_current_server], &sntp_server_address) 583 | ? ERR_OK : ERR_ARG; 584 | 585 | #endif /* SNTP_SERVER_DNS */ 586 | 587 | if (err == ERR_OK) { 588 | sntp_send_request(&sntp_server_address); 589 | } else { 590 | /* address conversion failed, try another server */ 591 | sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL); 592 | } 593 | } 594 | 595 | /** 596 | * Initialize this module. 597 | * Send out request instantly or after SNTP_STARTUP_DELAY. 598 | */ 599 | void 600 | sntp_init(sntpCallback cb) 601 | { 602 | INFO("Sntp initializing...\n"); 603 | sntpCb = cb; 604 | 605 | if (sntp_pcb == NULL) { 606 | os_timer_setfn(&ntp_timer,ntp_time_update,NULL); 607 | SNTP_RESET_RETRY_TIMEOUT(); 608 | sntp_pcb = udp_new(); 609 | LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL); 610 | 611 | if (sntp_pcb != NULL) { 612 | udp_recv(sntp_pcb, sntp_recv, NULL); 613 | #if SNTP_STARTUP_DELAY 614 | sys_timeout((u32_t)SNTP_STARTUP_DELAY, sntp_request, NULL); 615 | #else 616 | sntp_request(NULL); 617 | 618 | #endif 619 | } 620 | 621 | } 622 | } 623 | 624 | /** 625 | * Stop this module. 626 | */ 627 | void 628 | sntp_stop(void) 629 | { 630 | if (sntp_pcb != NULL) { 631 | sys_untimeout(sntp_request, NULL); 632 | udp_remove(sntp_pcb); 633 | sntp_pcb = NULL; 634 | } 635 | } 636 | 637 | void ICACHE_FLASH_ATTR 638 | ntp_time_update(void *ignored) { 639 | sntp_time++; 640 | } 641 | #endif /* LWIP_UDP */ -------------------------------------------------------------------------------- /modules/time_utils.c: -------------------------------------------------------------------------------- 1 | 2 | #include "c_types.h" 3 | #include "osapi.h" 4 | 5 | 6 | #define IS_LEAP(year) (year%4 == 0) 7 | #define SEC_IN_NON_LEAP (86400*365) 8 | #define SEC_IN_LEAP (86400*366) 9 | #define SEC_IN_YEAR(year) (IS_LEAP(year) ? SEC_IN_LEAP : SEC_IN_NON_LEAP) 10 | 11 | char buf[30]; 12 | 13 | unsigned char calendar [] = {31, 28, 31, 30,31, 30, 31, 31,30, 31, 30, 31}; 14 | unsigned char calendar_leap [] = {31, 29, 31, 30,31, 30, 31, 31,30, 31, 30, 31}; 15 | 16 | unsigned char *get_calendar(int year) { 17 | return IS_LEAP(year) ? calendar_leap : calendar; 18 | } 19 | 20 | int get_year(unsigned long *t) { 21 | int year=1970; 22 | while(*t>SEC_IN_YEAR(year)) { 23 | *t-=SEC_IN_YEAR(year); 24 | year++; 25 | } 26 | return year; 27 | } 28 | 29 | int get_month(unsigned long *t, int year) { 30 | unsigned char *cal = get_calendar(year); 31 | int i=0; 32 | while(*t > cal[i]*86400) { 33 | *t-=cal[i]*86400; 34 | i++; 35 | } 36 | return i+1; 37 | } 38 | 39 | char *epoch_to_str(unsigned long epoch) { 40 | int year=get_year(&epoch); 41 | unsigned char month=get_month(&epoch,year); 42 | unsigned char day=1+(epoch/86400); 43 | epoch=epoch%86400; 44 | unsigned char hour=epoch/3600; 45 | epoch%=3600; 46 | unsigned char min=epoch/60; 47 | unsigned char sec=epoch%60; 48 | 49 | os_sprintf(buf,"%02d:%02d:%02d %02d/%02d/%02d",hour,min,sec,day,month,year); 50 | return buf; 51 | } -------------------------------------------------------------------------------- /modules/wifi.c: -------------------------------------------------------------------------------- 1 | #include "wifi.h" 2 | #include "user_interface.h" 3 | #include "osapi.h" 4 | #include "espconn.h" 5 | #include "os_type.h" 6 | #include "mem.h" 7 | #include "mqtt_msg.h" 8 | #include "debug.h" 9 | #include "user_config.h" 10 | 11 | static os_timer_t wifi_timer; 12 | static os_timer_t scan_timer; 13 | WifiCallback wifiCb = NULL; 14 | static uint8_t wifiStatus = STATION_IDLE, last_wifiStatus = NULL; 15 | 16 | uint8_t ssid_scan_done, ssid_found; 17 | static volatile uint8_t ap_retry = AP_RETRY;; 18 | 19 | static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg) 20 | { 21 | struct ip_info ipConfig; 22 | 23 | if (ap_retry){ 24 | 25 | os_timer_disarm(&wifi_timer); 26 | 27 | wifi_get_ip_info(STATION_IF, &ipConfig); 28 | wifiStatus = wifi_station_get_connect_status(); 29 | 30 | 31 | if (wifiStatus == STATION_GOT_IP && ipConfig.ip.addr != 0) 32 | { 33 | char server_ip_string[25]; 34 | 35 | os_sprintf(server_ip_string,"%d.%d.%d.%d\r\n", 36 | *((uint8 *)&(ipConfig.ip.addr)), *((uint8 *)&(ipConfig.ip.addr) + 1), 37 | *((uint8 *)&(ipConfig.ip.addr) + 2), *((uint8 *)&(ipConfig.ip.addr) + 3)); 38 | 39 | wifi_station_set_auto_connect(TRUE); 40 | 41 | os_timer_setfn(&wifi_timer, (os_timer_func_t *)wifi_check_ip, NULL); 42 | os_timer_arm(&wifi_timer, 2000, 0); 43 | wifiCb(wifiStatus); 44 | 45 | } 46 | else 47 | { 48 | if(wifi_station_get_connect_status() == STATION_CONNECTING) 49 | { 50 | if (wifiStatus == last_wifiStatus) INFO("STATION_CONNECTING\r\n"); 51 | } 52 | else if(wifi_station_get_connect_status() == STATION_WRONG_PASSWORD) 53 | { 54 | INFO("STATION_WRONG_PASSWORD - retry : %d\r\n", ap_retry); 55 | ap_retry--; 56 | wifi_station_connect(); 57 | } 58 | else if(wifi_station_get_connect_status() == STATION_NO_AP_FOUND) 59 | { 60 | INFO("STATION_NO_AP_FOUND - retry : %d\r\n", ap_retry); 61 | ap_retry--; 62 | wifi_station_connect(); 63 | } 64 | else if(wifi_station_get_connect_status() == STATION_CONNECT_FAIL) 65 | { 66 | INFO("STATION_CONNECT_FAIL - retry : %d\r\n", ap_retry); 67 | ap_retry--; 68 | wifi_station_connect(); 69 | } 70 | else 71 | { 72 | if (wifiStatus == last_wifiStatus) INFO("STATION_IDLE : %d\r\n", wifiStatus); 73 | } 74 | 75 | os_timer_setfn(&wifi_timer, (os_timer_func_t *)wifi_check_ip, NULL); 76 | os_timer_arm(&wifi_timer, 500, 0); 77 | } 78 | } 79 | else 80 | { 81 | INFO("unable to connect to AP.\r\n"); 82 | INFO("Going for deep sleep. gd night\r\n"); 83 | deep_sleep_set_option(1); 84 | system_deep_sleep(0); 85 | } 86 | } 87 | 88 | void ICACHE_FLASH_ATTR WIFI_Connect(uint8_t* ssid, uint8_t* pass, WifiCallback cb) 89 | { 90 | struct station_config stationConf; 91 | 92 | INFO("Connect to AP\r\n"); 93 | wifi_set_opmode(STATION_MODE); 94 | wifi_station_set_auto_connect(FALSE); 95 | wifiCb = cb; 96 | 97 | os_memset(&stationConf, 0, sizeof(struct station_config)); 98 | 99 | os_sprintf(stationConf.ssid, "%s", ssid); 100 | os_sprintf(stationConf.password, "%s", pass); 101 | 102 | wifi_station_set_config(&stationConf); 103 | 104 | os_timer_disarm(&wifi_timer); 105 | os_timer_setfn(&wifi_timer, (os_timer_func_t *)wifi_check_ip, NULL); 106 | os_timer_arm(&wifi_timer, 1000, 0); 107 | 108 | wifi_station_connect(); 109 | } 110 | 111 | 112 | void ICACHE_FLASH_ATTR scan_done_callback(void *arg, STATUS status) 113 | { 114 | struct bss_info *bss = arg; 115 | 116 | ssid_found = 0; 117 | bss = STAILQ_NEXT(bss, next); // ignore first 118 | 119 | while (bss) 120 | { 121 | if(strcmp(bss->ssid,STA_SSID)==0) ssid_found++; 122 | INFO("%d : SSID:%s CH:%d RSSI:%d Authmode:%d\n", ssid_found, bss->ssid, bss->channel, bss->rssi, bss->authmode); 123 | bss = STAILQ_NEXT(bss, next); 124 | } 125 | 126 | ssid_scan_done = 1; 127 | } 128 | 129 | 130 | void ICACHE_FLASH_ATTR scan_wifi(){ 131 | //Connect to WIFI 132 | INFO("scan SSID\r\n"); 133 | 134 | struct scan_config config; 135 | 136 | os_memset(&config, 0, sizeof(struct scan_config)); 137 | 138 | char *ap_ssid = STA_SSID; 139 | config.ssid = ap_ssid; 140 | 141 | //WIFI_Connect(STA_SSID, STA_PASS, wifiConnectCb); 142 | wifi_set_opmode(STATION_MODE); 143 | wifi_station_set_auto_connect(FALSE); 144 | 145 | wifi_station_scan(&config, scan_done_callback); 146 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | void* user_data; 107 | } MQTT_Client; 108 | 109 | #define SEC_NONSSL 0 110 | #define SEC_SSL 1 111 | 112 | #define MQTT_FLAG_CONNECTED 1 113 | #define MQTT_FLAG_READY 2 114 | #define MQTT_FLAG_EXIT 4 115 | 116 | #define MQTT_EVENT_TYPE_NONE 0 117 | #define MQTT_EVENT_TYPE_CONNECTED 1 118 | #define MQTT_EVENT_TYPE_DISCONNECTED 2 119 | #define MQTT_EVENT_TYPE_SUBSCRIBED 3 120 | #define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 121 | #define MQTT_EVENT_TYPE_PUBLISH 5 122 | #define MQTT_EVENT_TYPE_PUBLISHED 6 123 | #define MQTT_EVENT_TYPE_EXITED 7 124 | #define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 125 | 126 | void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security); 127 | 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); 128 | 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); 129 | void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); 130 | void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); 131 | void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); 132 | void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); 133 | BOOL ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); 134 | void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient); 135 | void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient); 136 | BOOL ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain); 137 | 138 | #endif /* USER_AT_MQTT_H_ */ 139 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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 | if(e->par == 0) 450 | return; 451 | switch(client->connState){ 452 | 453 | case TCP_RECONNECT_REQ: 454 | break; 455 | case TCP_RECONNECT: 456 | MQTT_Connect(client); 457 | INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port); 458 | client->connState = TCP_CONNECTING; 459 | break; 460 | case MQTT_DATA: 461 | if(QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) { 462 | break; 463 | } 464 | if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0){ 465 | client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); 466 | client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); 467 | 468 | 469 | client->sendTimeout = MQTT_SEND_TIMOUT; 470 | INFO("MQTT: Sending, type: %d, id: %04X\r\n",client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); 471 | if(client->security){ 472 | espconn_secure_sent(client->pCon, dataBuffer, dataLen); 473 | } 474 | else{ 475 | espconn_sent(client->pCon, dataBuffer, dataLen); 476 | } 477 | 478 | client->mqtt_state.outbound_message = NULL; 479 | break; 480 | } 481 | break; 482 | } 483 | } 484 | 485 | /** 486 | * @brief MQTT initialization connection function 487 | * @param client: MQTT_Client reference 488 | * @param host: Domain or IP string 489 | * @param port: Port to connect 490 | * @param security: 1 for ssl, 0 for none 491 | * @retval None 492 | */ 493 | void ICACHE_FLASH_ATTR 494 | MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security) 495 | { 496 | uint32_t temp; 497 | INFO("MQTT_InitConnection\r\n"); 498 | os_memset(mqttClient, 0, sizeof(MQTT_Client)); 499 | temp = os_strlen(host); 500 | mqttClient->host = (uint8_t*)os_zalloc(temp + 1); 501 | os_strcpy(mqttClient->host, host); 502 | mqttClient->host[temp] = 0; 503 | mqttClient->port = port; 504 | mqttClient->security = security; 505 | 506 | } 507 | 508 | /** 509 | * @brief MQTT initialization mqtt client function 510 | * @param client: MQTT_Client reference 511 | * @param clientid: MQTT client id 512 | * @param client_user:MQTT client user 513 | * @param client_pass:MQTT client password 514 | * @param client_pass:MQTT keep alive timer, in second 515 | * @retval None 516 | */ 517 | void ICACHE_FLASH_ATTR 518 | MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession) 519 | { 520 | uint32_t temp; 521 | INFO("MQTT_InitClient\r\n"); 522 | 523 | os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); 524 | 525 | temp = os_strlen(client_id); 526 | mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1); 527 | os_strcpy(mqttClient->connect_info.client_id, client_id); 528 | mqttClient->connect_info.client_id[temp] = 0; 529 | 530 | temp = os_strlen(client_user); 531 | mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1); 532 | os_strcpy(mqttClient->connect_info.username, client_user); 533 | mqttClient->connect_info.username[temp] = 0; 534 | 535 | temp = os_strlen(client_pass); 536 | mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1); 537 | os_strcpy(mqttClient->connect_info.password, client_pass); 538 | mqttClient->connect_info.password[temp] = 0; 539 | 540 | 541 | mqttClient->connect_info.keepalive = keepAliveTime; 542 | mqttClient->connect_info.clean_session = cleanSession; 543 | 544 | mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); 545 | mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; 546 | mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); 547 | mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; 548 | mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; 549 | 550 | mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); 551 | 552 | QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); 553 | 554 | system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); 555 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); 556 | } 557 | void ICACHE_FLASH_ATTR 558 | MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) 559 | { 560 | uint32_t temp; 561 | temp = os_strlen(will_topic); 562 | mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1); 563 | os_strcpy(mqttClient->connect_info.will_topic, will_topic); 564 | mqttClient->connect_info.will_topic[temp] = 0; 565 | 566 | temp = os_strlen(will_msg); 567 | mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1); 568 | os_strcpy(mqttClient->connect_info.will_message, will_msg); 569 | mqttClient->connect_info.will_message[temp] = 0; 570 | 571 | 572 | mqttClient->connect_info.will_qos = will_qos; 573 | mqttClient->connect_info.will_retain = will_retain; 574 | } 575 | /** 576 | * @brief Begin connect to MQTT broker 577 | * @param client: MQTT_Client reference 578 | * @retval None 579 | */ 580 | void ICACHE_FLASH_ATTR 581 | MQTT_Connect(MQTT_Client *mqttClient) 582 | { 583 | MQTT_Disconnect(mqttClient); 584 | mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); 585 | mqttClient->pCon->type = ESPCONN_TCP; 586 | mqttClient->pCon->state = ESPCONN_NONE; 587 | mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); 588 | mqttClient->pCon->proto.tcp->local_port = espconn_port(); 589 | mqttClient->pCon->proto.tcp->remote_port = mqttClient->port; 590 | mqttClient->pCon->reverse = mqttClient; 591 | espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); 592 | espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); 593 | 594 | mqttClient->keepAliveTick = 0; 595 | mqttClient->reconnectTick = 0; 596 | 597 | 598 | os_timer_disarm(&mqttClient->mqttTimer); 599 | os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); 600 | os_timer_arm(&mqttClient->mqttTimer, 1000, 1); 601 | 602 | if(UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) { 603 | INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port); 604 | if(mqttClient->security){ 605 | espconn_secure_connect(mqttClient->pCon); 606 | } 607 | else { 608 | espconn_connect(mqttClient->pCon); 609 | } 610 | } 611 | else { 612 | INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port); 613 | espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found); 614 | } 615 | mqttClient->connState = TCP_CONNECTING; 616 | } 617 | 618 | void ICACHE_FLASH_ATTR 619 | MQTT_Disconnect(MQTT_Client *mqttClient) 620 | { 621 | if(mqttClient->pCon){ 622 | INFO("Free memory\r\n"); 623 | if(mqttClient->pCon->proto.tcp) 624 | os_free(mqttClient->pCon->proto.tcp); 625 | os_free(mqttClient->pCon); 626 | mqttClient->pCon = NULL; 627 | } 628 | 629 | os_timer_disarm(&mqttClient->mqttTimer); 630 | } 631 | void ICACHE_FLASH_ATTR 632 | MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) 633 | { 634 | mqttClient->connectedCb = connectedCb; 635 | } 636 | 637 | void ICACHE_FLASH_ATTR 638 | MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) 639 | { 640 | mqttClient->disconnectedCb = disconnectedCb; 641 | } 642 | 643 | void ICACHE_FLASH_ATTR 644 | MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) 645 | { 646 | mqttClient->dataCb = dataCb; 647 | } 648 | 649 | void ICACHE_FLASH_ATTR 650 | MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) 651 | { 652 | mqttClient->publishedCb = publishedCb; 653 | } 654 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terenceang/esp8266-PIR/02eff82440a65c36df6f8ddba35a3fb7f8433ed6/schematic.png -------------------------------------------------------------------------------- /tools/.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | *.css text eol=lf 3 | *.html text eol=lf 4 | *.js text eol=lf 5 | *.json text eol=lf 6 | *.less text eol=lf 7 | *.md text eol=lf 8 | *.svg text eol=lf 9 | *.yml text eol=lf 10 | *.py text eol=lf 11 | *.sh text eol=lf 12 | -------------------------------------------------------------------------------- /tools/esptool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # ESP8266 ROM Bootloader Utility 4 | # https://github.com/themadinventor/esptool 5 | # 6 | # Copyright (C) 2014 Fredrik Ahlberg 7 | # 8 | # This program is free software; you can redistribute it and/or modify it under 9 | # the terms of the GNU General Public License as published by the Free Software 10 | # Foundation; either version 2 of the License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but WITHOUT 13 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along with 17 | # this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 18 | # Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | import sys 21 | import struct 22 | import serial 23 | import math 24 | import time 25 | import argparse 26 | import os 27 | import subprocess 28 | 29 | class ESPROM: 30 | 31 | # These are the currently known commands supported by the ROM 32 | ESP_FLASH_BEGIN = 0x02 33 | ESP_FLASH_DATA = 0x03 34 | ESP_FLASH_END = 0x04 35 | ESP_MEM_BEGIN = 0x05 36 | ESP_MEM_END = 0x06 37 | ESP_MEM_DATA = 0x07 38 | ESP_SYNC = 0x08 39 | ESP_WRITE_REG = 0x09 40 | ESP_READ_REG = 0x0a 41 | 42 | # Maximum block sized for RAM and Flash writes, respectively. 43 | ESP_RAM_BLOCK = 0x1800 44 | ESP_FLASH_BLOCK = 0x100 45 | 46 | # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. 47 | ESP_ROM_BAUD = 115200 48 | 49 | # First byte of the application image 50 | ESP_IMAGE_MAGIC = 0xe9 51 | 52 | # Initial state for the checksum routine 53 | ESP_CHECKSUM_MAGIC = 0xef 54 | 55 | # OTP ROM addresses 56 | ESP_OTP_MAC0 = 0x3ff00050 57 | ESP_OTP_MAC1 = 0x3ff00054 58 | 59 | def __init__(self, port = 0, baud = ESP_ROM_BAUD): 60 | self._port = serial.Serial(port, baud) 61 | 62 | """ Read bytes from the serial port while performing SLIP unescaping """ 63 | def read(self, length = 1): 64 | b = '' 65 | while len(b) < length: 66 | c = self._port.read(1) 67 | if c == '\xdb': 68 | c = self._port.read(1) 69 | if c == '\xdc': 70 | b = b + '\xc0' 71 | elif c == '\xdd': 72 | b = b + '\xdb' 73 | else: 74 | raise Exception('Invalid SLIP escape') 75 | else: 76 | b = b + c 77 | return b 78 | 79 | """ Write bytes to the serial port while performing SLIP escaping """ 80 | def write(self, packet): 81 | buf = '\xc0' 82 | for b in packet: 83 | if b == '\xc0': 84 | buf += '\xdb\xdc' 85 | elif b == '\xdb': 86 | buf += '\xdb\xdd' 87 | else: 88 | buf += b 89 | buf += '\xc0' 90 | self._port.write(buf) 91 | 92 | """ Calculate checksum of a blob, as it is defined by the ROM """ 93 | @staticmethod 94 | def checksum(data, state = ESP_CHECKSUM_MAGIC): 95 | for b in data: 96 | state ^= ord(b) 97 | return state 98 | 99 | """ Send a request and read the response """ 100 | def command(self, op = None, data = None, chk = 0): 101 | if op: 102 | # Construct and send request 103 | pkt = struct.pack(' 16: 225 | raise Exception('Invalid firmware image') 226 | 227 | for i in xrange(segments): 228 | (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: 230 | raise Exception('Suspicious segment %x,%d' % (offset, size)) 231 | self.segments.append((offset, size, f.read(size))) 232 | 233 | # Skip the padding. The checksum is stored in the last byte so that the 234 | # file is a multiple of 16 bytes. 235 | align = 15-(f.tell() % 16) 236 | f.seek(align, 1) 237 | 238 | self.checksum = ord(f.read(1)) 239 | 240 | def add_segment(self, addr, data): 241 | # Data should be aligned on word boundary 242 | l = len(data) 243 | if l % 4: 244 | data += b"\x00" * (4 - l % 4) 245 | self.segments.append((addr, len(data), data)) 246 | 247 | def save(self, filename): 248 | f = file(filename, 'wb') 249 | f.write(struct.pack(' 0: 397 | esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) 398 | data = data[esp.ESP_RAM_BLOCK:] 399 | seq += 1 400 | print 'done!' 401 | 402 | print 'All segments done, executing at %08x' % image.entrypoint 403 | esp.mem_finish(image.entrypoint) 404 | 405 | elif args.operation == 'read_mem': 406 | print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address)) 407 | 408 | elif args.operation == 'write_mem': 409 | esp.write_reg(args.address, args.value, args.mask, 0) 410 | print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address) 411 | 412 | elif args.operation == 'dump_mem': 413 | f = file(args.filename, 'wb') 414 | for i in xrange(args.size/4): 415 | d = esp.read_reg(args.address+(i*4)) 416 | f.write(struct.pack(' 0: 434 | print '\rWriting at 0x%08x... (%d %%)' % (address + seq*esp.ESP_FLASH_BLOCK, 100*(seq+1)/blocks), 435 | sys.stdout.flush() 436 | block = image[0:esp.ESP_FLASH_BLOCK] 437 | block = block + '\xe0' * (esp.ESP_FLASH_BLOCK-len(block)) 438 | esp.flash_block(block, seq) 439 | image = image[esp.ESP_FLASH_BLOCK:] 440 | seq += 1 441 | print 442 | print '\nLeaving...' 443 | esp.flash_finish(False) 444 | 445 | elif args.operation == 'run': 446 | esp.run() 447 | 448 | elif args.operation == 'image_info': 449 | image = ESPFirmwareImage(args.filename) 450 | print ('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set' 451 | print '%d segments' % len(image.segments) 452 | print 453 | checksum = ESPROM.ESP_CHECKSUM_MAGIC 454 | for (idx, (offset, size, data)) in enumerate(image.segments): 455 | print 'Segment %d: %5d bytes at %08x' % (idx+1, size, offset) 456 | checksum = ESPROM.checksum(data, checksum) 457 | print 458 | print 'Checksum: %02x (%s)' % (image.checksum, 'valid' if image.checksum == checksum else 'invalid!') 459 | 460 | elif args.operation == 'make_image': 461 | image = ESPFirmwareImage() 462 | if len(args.segfile) == 0: 463 | raise Exception('No segments specified') 464 | if len(args.segfile) != len(args.segaddr): 465 | raise Exception('Number of specified files does not match number of specified addresses') 466 | for (seg, addr) in zip(args.segfile, args.segaddr): 467 | data = file(seg, 'rb').read() 468 | image.add_segment(addr, data) 469 | image.entrypoint = args.entrypoint 470 | image.save(args.output) 471 | 472 | elif args.operation == 'elf2image': 473 | if args.output is None: 474 | args.output = args.input + '-' 475 | e = ELFFile(args.input) 476 | image = ESPFirmwareImage() 477 | image.entrypoint = e.get_symbol_addr("call_user_start") 478 | for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")): 479 | data = e.load_section(section) 480 | image.add_segment(e.get_symbol_addr(start), data) 481 | image.save(args.output + "0x00000.bin") 482 | data = e.load_section(".irom0.text") 483 | off = e.get_symbol_addr("_irom0_text_start") - 0x40200000 484 | assert off >= 0 485 | f = open(args.output + "0x%05x.bin" % off, "wb") 486 | f.write(data) 487 | f.close() 488 | 489 | elif args.operation == 'read_mac': 490 | mac0 = esp.read_reg(esp.ESP_OTP_MAC0) 491 | mac1 = esp.read_reg(esp.ESP_OTP_MAC1) 492 | print 'MAC: 18:fe:34:%02x:%02x:%02x' % ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) 493 | -------------------------------------------------------------------------------- /tools/makefile.sh: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Generate the certificates and keys for encrypt. 4 | # 5 | 6 | # set default cert for use in the client 7 | xxd -i client.cer | sed -e \ 8 | "s/client_cer/default_certificate/" > cert.h 9 | # set default key for use in the server 10 | xxd -i server.key_1024 | sed -e \ 11 | "s/server_key_1024/default_private_key/" > private_key.h 12 | -------------------------------------------------------------------------------- /user/user_main.c: -------------------------------------------------------------------------------- 1 | /* main.c -- MQTT client example 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 "ets_sys.h" 31 | #include "osapi.h" 32 | #include "mqtt.h" 33 | #include "wifi.h" 34 | #include "debug.h" 35 | #include "gpio.h" 36 | #include "user_interface.h" 37 | #include "stdout/stdout.h" 38 | #include "sntp.h" 39 | #include "time_utils.h" 40 | 41 | MQTT_Client mqttClient; 42 | 43 | static char client_ID[25]; 44 | static volatile uint32_t chip_ID; 45 | 46 | static volatile os_timer_t sleep_timer; 47 | 48 | static volatile uint8_t sleep = 0; 49 | //uint8_t ssid_found; 50 | 51 | void ICACHE_FLASH_ATTR sntpGotTime(time_t ntp_time); 52 | 53 | void sleep_timer_func(void){ 54 | INFO("Going for deep sleep. gd night\r\n"); 55 | deep_sleep_set_option(1); 56 | system_deep_sleep(0); 57 | } 58 | 59 | void sntpGotTime(time_t ntp_time){ 60 | MQTT_Client* client = (MQTT_Client*)&mqttClient; 61 | 62 | char timestamp[100]; 63 | char topic[100]; 64 | sleep = 1; 65 | 66 | os_sprintf(topic,"/ESP-PIR/%d", chip_ID);//set topic 67 | os_sprintf(timestamp,"%s : motion detected at %s\r\n", client_ID, epoch_to_str(ntp_time)); //+8 SG time 68 | MQTT_Publish(client, topic, timestamp, strlen(timestamp), 1, 0); //publish message 69 | INFO("got time : %s\r\n",epoch_to_str(ntp_time)); 70 | } 71 | 72 | //Call back for WIFI connection 73 | void wifiConnectCb(uint8_t status) 74 | { 75 | if(status == STATION_GOT_IP){ 76 | MQTT_Connect(&mqttClient); 77 | } else { 78 | MQTT_Disconnect(&mqttClient); 79 | } 80 | } 81 | 82 | //Call back for connect 83 | void mqttConnectedCb(uint32_t *args) 84 | { 85 | char lwt[100]; 86 | MQTT_Client* client = (MQTT_Client*)args; 87 | INFO("MQTT: Connected\r\n"); 88 | os_sprintf(lwt,"/ESP-PIR/%d/status", chip_ID); //set LWT 89 | MQTT_Publish(client, lwt, NULL, 0, 0, 1); //clear LWT 90 | sntp_init(sntpGotTime); 91 | } 92 | 93 | //Call back for message published 94 | void mqttPublishedCb(uint32_t *args) 95 | { 96 | MQTT_Client* client = (MQTT_Client*)args; 97 | INFO("MQTT: Published\r\n"); 98 | 99 | if (sleep){ 100 | //setup timer 101 | INFO("Wait 1 sec\r\n"); 102 | os_timer_disarm(&sleep_timer); 103 | os_timer_setfn(&sleep_timer, (os_timer_func_t *)sleep_timer_func, NULL); 104 | os_timer_arm(&sleep_timer, 1000, 0); //wait 1 sec 1 shot only 105 | } 106 | } 107 | 108 | void ICACHE_FLASH_ATTR init_done(void) 109 | { 110 | WIFI_Connect(STA_SSID, STA_PASS, wifiConnectCb); 111 | } 112 | 113 | void ICACHE_FLASH_ATTR user_init(void) 114 | { 115 | char lwt[100]; 116 | // Initialize the GPIO subsystem. 117 | gpio_init(); 118 | 119 | stdout_init(); //init TXD 115200 baud. free up RXD for GPIO 120 | 121 | INFO("system init\r\n"); 122 | 123 | #ifdef USE_CHIP_ID 124 | chip_ID = system_get_chip_id(); 125 | os_sprintf(client_ID,"%s-%d", MQTT_CLIENT_ID, chip_ID); 126 | #else 127 | os_sprintf(client_ID,"%s", MQTT_CLIENT_ID); 128 | #endif 129 | 130 | //SetUp MQTT client 131 | MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, DEFAULT_SECURITY); 132 | MQTT_InitClient(&mqttClient, client_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, 1); 133 | os_sprintf(lwt,"/ESP-PIR/%d/status", chip_ID); //set LWT 134 | MQTT_InitLWT(&mqttClient, lwt, "offline", 0, 1); 135 | MQTT_OnConnected(&mqttClient, mqttConnectedCb); 136 | MQTT_OnPublished(&mqttClient, mqttPublishedCb); 137 | 138 | system_init_done_cb(init_done); 139 | 140 | INFO("\r\nSystem started ...\r\n"); 141 | 142 | } 143 | --------------------------------------------------------------------------------