├── .gitignore ├── Makefile ├── Makefile.linux ├── Makefile.windows ├── README.md ├── driver ├── Makefile ├── dht22.c └── uart.c ├── include ├── driver │ ├── dht22.h │ ├── uart.h │ └── uart_register.h ├── httpclient.h └── user_config.h ├── mqtt ├── Makefile ├── config.c ├── config.h ├── debug.h ├── mqtt.c ├── mqtt.h ├── mqtt_msg.c ├── mqtt_msg.h ├── proto.c ├── proto.h ├── queue.c ├── queue.h ├── ringbuf.c ├── ringbuf.h ├── typedef.h ├── utils.c ├── utils.h ├── wifi.c └── wifi.h └── user ├── Makefile ├── httpclient.c └── user_main.c /.gitignore: -------------------------------------------------------------------------------- 1 | .cproject 2 | .project 3 | build/ 4 | firmware/ 5 | 6 | -------------------------------------------------------------------------------- /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 | FLAVOR = release 13 | #FLAVOR = debug 14 | 15 | # Base directory for the compiler 16 | XTENSA_TOOLS_ROOT ?= c:/Espressif/xtensa-lx106-elf/bin 17 | 18 | # base directory of the ESP8266 SDK package, absolute 19 | SDK_BASE ?= c:/Espressif/ESP8266_SDK 20 | 21 | #Esptool.py path and port 22 | PYTHON ?= C:\Python27\python.exe 23 | ESPTOOL ?= c:\Espressif\utils\esptool.py 24 | ESPPORT ?= COM7 25 | 26 | # name for the target project 27 | TARGET = app 28 | 29 | # which modules (subdirectories) of the project to include in compiling 30 | MODULES = driver mqtt user 31 | EXTRA_INCDIR = include 32 | 33 | # libraries used in this project, mainly provided by the SDK 34 | LIBS = c gcc hal phy pp net80211 lwip wpa upgrade main ssl json 35 | 36 | # compiler flags using during compilation of source files 37 | CFLAGS = -Os -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH 38 | 39 | # linker flags used to generate the main object file 40 | LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static 41 | 42 | ifeq ($(FLAVOR),debug) 43 | CFLAGS += -g -O0 44 | LDFLAGS += -g -O0 45 | endif 46 | 47 | ifeq ($(FLAVOR),release) 48 | CFLAGS += -g -O2 49 | LDFLAGS += -g -O2 50 | endif 51 | 52 | # linker script used for the above linkier step 53 | LD_SCRIPT = eagle.app.v6.ld 54 | 55 | # various paths from the SDK used in this project 56 | SDK_LIBDIR = lib 57 | SDK_LDDIR = ld 58 | SDK_INCDIR = include include/json 59 | 60 | # we create two different files for uploading into the flash 61 | # these are the names and options to generate them 62 | FW_FILE_1 = 0x00000 63 | FW_FILE_1_ARGS = -bo $@ -bs .text -bs .data -bs .rodata -bc -ec 64 | FW_FILE_2 = 0x40000 65 | FW_FILE_2_ARGS = -es .irom0.text $@ -ec 66 | 67 | # select which tools to use as compiler, librarian and linker 68 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 69 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 70 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 71 | 72 | #### 73 | #### no user configurable options below here 74 | #### 75 | FW_TOOL ?= $(XTENSA_TOOLS_ROOT)/esptool 76 | SRC_DIR := $(MODULES) 77 | BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) 78 | 79 | SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) 80 | SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) 81 | 82 | SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) 83 | OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) 84 | LIBS := $(addprefix -l,$(LIBS)) 85 | APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) 86 | TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) 87 | 88 | LD_SCRIPT := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT)) 89 | 90 | INCDIR := $(addprefix -I,$(SRC_DIR)) 91 | EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) 92 | MODULE_INCDIR := $(addsuffix /include,$(INCDIR)) 93 | 94 | FW_FILE_1 := $(addprefix $(FW_BASE)/,$(FW_FILE_1).bin) 95 | FW_FILE_2 := $(addprefix $(FW_BASE)/,$(FW_FILE_2).bin) 96 | BLANKER := $(addprefix $(SDK_BASE)/,bin/blank.bin) 97 | 98 | V ?= $(VERBOSE) 99 | ifeq ("$(V)","1") 100 | Q := 101 | vecho := @true 102 | else 103 | Q := @ 104 | vecho := @echo 105 | endif 106 | 107 | vpath %.c $(SRC_DIR) 108 | 109 | define compile-objects 110 | $1/%.o: %.c 111 | $(vecho) "CC $$<" 112 | $(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 113 | endef 114 | 115 | .PHONY: all checkdirs clean 116 | 117 | all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2) 118 | 119 | $(FW_FILE_1): $(TARGET_OUT) 120 | $(vecho) "FW $@" 121 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_1_ARGS) 122 | 123 | $(FW_FILE_2): $(TARGET_OUT) 124 | $(vecho) "FW $@" 125 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_2_ARGS) 126 | 127 | $(TARGET_OUT): $(APP_AR) 128 | $(vecho) "LD $@" 129 | $(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ 130 | 131 | $(APP_AR): $(OBJ) 132 | $(vecho) "AR $@" 133 | $(Q) $(AR) cru $@ $^ 134 | 135 | checkdirs: $(BUILD_DIR) $(FW_BASE) 136 | 137 | $(BUILD_DIR): 138 | $(Q) mkdir -p $@ 139 | 140 | firmware: 141 | $(Q) mkdir -p $@ 142 | 143 | flash: firmware/0x00000.bin firmware/0x40000.bin 144 | $(PYTHON) $(ESPTOOL) -p $(ESPPORT) write_flash 0x00000 firmware/0x00000.bin 0x3C000 $(BLANKER) 0x40000 firmware/0x40000.bin 145 | 146 | test: flash 147 | screen $(ESPPORT) 115200 148 | 149 | rebuild: clean all 150 | 151 | clean: 152 | $(Q) rm -f $(APP_AR) 153 | $(Q) rm -f $(TARGET_OUT) 154 | $(Q) rm -rf $(BUILD_DIR) 155 | $(Q) rm -rf $(BUILD_BASE) 156 | $(Q) rm -f $(FW_FILE_1) 157 | $(Q) rm -f $(FW_FILE_2) 158 | $(Q) rm -rf $(FW_BASE) 159 | 160 | $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir)))) 161 | -------------------------------------------------------------------------------- /Makefile.linux: -------------------------------------------------------------------------------- 1 | # Changelog 2 | # Changed the variables to include the header file directory 3 | # Added global var for the XTENSA tool root 4 | # 5 | # This make file still needs some work. 6 | # 7 | # 8 | # Output directors to store intermediate compiled files 9 | # relative to the project directory 10 | BUILD_BASE = build 11 | FW_BASE = firmware 12 | 13 | # Base directory for the compiler 14 | XTENSA_TOOLS_ROOT ?= /opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin 15 | 16 | # base directory of the ESP8266 SDK package, absolute 17 | SDK_BASE ?= /opt/Espressif/ESP8266_SDK 18 | 19 | #Esptool.py path and port 20 | ESPTOOL ?= esptool.py 21 | ESPPORT ?= /dev/ttyUSB0 22 | 23 | # name for the target project 24 | TARGET = app 25 | 26 | # which modules (subdirectories) of the project to include in compiling 27 | MODULES = driver mqtt user 28 | EXTRA_INCDIR = include $(SDK_BASE)/../include 29 | 30 | # libraries used in this project, mainly provided by the SDK 31 | LIBS = c gcc hal phy pp net80211 lwip wpa upgrade main ssl 32 | 33 | # compiler flags using during compilation of source files 34 | CFLAGS = -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH 35 | 36 | # linker flags used to generate the main object file 37 | LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static 38 | 39 | # linker script used for the above linkier step 40 | LD_SCRIPT = eagle.app.v6.ld 41 | 42 | # various paths from the SDK used in this project 43 | SDK_LIBDIR = lib 44 | SDK_LDDIR = ld 45 | SDK_INCDIR = include include/json 46 | 47 | # we create two different files for uploading into the flash 48 | # these are the names and options to generate them 49 | FW_FILE_1 = 0x00000 50 | FW_FILE_1_ARGS = -bo $@ -bs .text -bs .data -bs .rodata -bc -ec 51 | FW_FILE_2 = 0x40000 52 | FW_FILE_2_ARGS = -es .irom0.text $@ -ec 53 | 54 | # select which tools to use as compiler, librarian and linker 55 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 56 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 57 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 58 | 59 | #### 60 | #### no user configurable options below here 61 | #### 62 | FW_TOOL ?= /usr/bin/esptool 63 | SRC_DIR := $(MODULES) 64 | BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) 65 | 66 | SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) 67 | SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) 68 | 69 | SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) 70 | OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) 71 | LIBS := $(addprefix -l,$(LIBS)) 72 | APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) 73 | TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) 74 | 75 | LD_SCRIPT := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT)) 76 | 77 | INCDIR := $(addprefix -I,$(SRC_DIR)) 78 | EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) 79 | MODULE_INCDIR := $(addsuffix /include,$(INCDIR)) 80 | 81 | FW_FILE_1 := $(addprefix $(FW_BASE)/,$(FW_FILE_1).bin) 82 | FW_FILE_2 := $(addprefix $(FW_BASE)/,$(FW_FILE_2).bin) 83 | BLANKER := $(addprefix $(SDK_BASE)/,bin/blank.bin) 84 | 85 | V ?= $(VERBOSE) 86 | ifeq ("$(V)","1") 87 | Q := 88 | vecho := @true 89 | else 90 | Q := @ 91 | vecho := @echo 92 | endif 93 | 94 | vpath %.c $(SRC_DIR) 95 | 96 | define compile-objects 97 | $1/%.o: %.c 98 | $(vecho) "CC $$<" 99 | $(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 100 | endef 101 | 102 | .PHONY: all checkdirs clean 103 | 104 | all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2) 105 | 106 | $(FW_FILE_1): $(TARGET_OUT) 107 | $(vecho) "FW $@" 108 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_1_ARGS) 109 | 110 | $(FW_FILE_2): $(TARGET_OUT) 111 | $(vecho) "FW $@" 112 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_2_ARGS) 113 | 114 | $(TARGET_OUT): $(APP_AR) 115 | $(vecho) "LD $@" 116 | $(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ 117 | 118 | $(APP_AR): $(OBJ) 119 | $(vecho) "AR $@" 120 | $(Q) $(AR) cru $@ $^ 121 | 122 | checkdirs: $(BUILD_DIR) $(FW_BASE) 123 | 124 | $(BUILD_DIR): 125 | $(Q) mkdir -p $@ 126 | 127 | firmware: 128 | $(Q) mkdir -p $@ 129 | 130 | flash: firmware/0x00000.bin firmware/0x40000.bin 131 | $(PYTHON) $(ESPTOOL) -p $(ESPPORT) write_flash 0x00000 firmware/0x00000.bin 0x3C000 $(BLANKER) 0x40000 firmware/0x40000.bin 132 | 133 | test: flash 134 | screen $(ESPPORT) 115200 135 | 136 | clean: 137 | $(Q) rm -f $(APP_AR) 138 | $(Q) rm -f $(TARGET_OUT) 139 | $(Q) rm -rf $(BUILD_DIR) 140 | $(Q) rm -rf $(BUILD_BASE) 141 | $(Q) rm -f $(FW_FILE_1) 142 | $(Q) rm -f $(FW_FILE_2) 143 | $(Q) rm -rf $(FW_BASE) 144 | 145 | $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir)))) 146 | -------------------------------------------------------------------------------- /Makefile.windows: -------------------------------------------------------------------------------- 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 | FLAVOR = release 13 | #FLAVOR = debug 14 | 15 | # Base directory for the compiler 16 | XTENSA_TOOLS_ROOT ?= c:/Espressif/xtensa-lx106-elf/bin 17 | 18 | # base directory of the ESP8266 SDK package, absolute 19 | SDK_BASE ?= c:/Espressif/ESP8266_SDK 20 | 21 | #Esptool.py path and port 22 | PYTHON ?= C:\Python27\python.exe 23 | ESPTOOL ?= c:\Espressif\utils\esptool.py 24 | ESPPORT ?= COM7 25 | 26 | # name for the target project 27 | TARGET = app 28 | 29 | # which modules (subdirectories) of the project to include in compiling 30 | MODULES = driver mqtt user 31 | EXTRA_INCDIR = include 32 | 33 | # libraries used in this project, mainly provided by the SDK 34 | LIBS = c gcc hal phy pp net80211 lwip wpa upgrade main ssl json 35 | 36 | # compiler flags using during compilation of source files 37 | CFLAGS = -Os -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH 38 | 39 | # linker flags used to generate the main object file 40 | LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static 41 | 42 | ifeq ($(FLAVOR),debug) 43 | CFLAGS += -g -O0 44 | LDFLAGS += -g -O0 45 | endif 46 | 47 | ifeq ($(FLAVOR),release) 48 | CFLAGS += -g -O2 49 | LDFLAGS += -g -O2 50 | endif 51 | 52 | # linker script used for the above linkier step 53 | LD_SCRIPT = eagle.app.v6.ld 54 | 55 | # various paths from the SDK used in this project 56 | SDK_LIBDIR = lib 57 | SDK_LDDIR = ld 58 | SDK_INCDIR = include include/json 59 | 60 | # we create two different files for uploading into the flash 61 | # these are the names and options to generate them 62 | FW_FILE_1 = 0x00000 63 | FW_FILE_1_ARGS = -bo $@ -bs .text -bs .data -bs .rodata -bc -ec 64 | FW_FILE_2 = 0x40000 65 | FW_FILE_2_ARGS = -es .irom0.text $@ -ec 66 | 67 | # select which tools to use as compiler, librarian and linker 68 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 69 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 70 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 71 | 72 | #### 73 | #### no user configurable options below here 74 | #### 75 | FW_TOOL ?= $(XTENSA_TOOLS_ROOT)/esptool 76 | SRC_DIR := $(MODULES) 77 | BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) 78 | 79 | SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) 80 | SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) 81 | 82 | SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) 83 | OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) 84 | LIBS := $(addprefix -l,$(LIBS)) 85 | APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) 86 | TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) 87 | 88 | LD_SCRIPT := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT)) 89 | 90 | INCDIR := $(addprefix -I,$(SRC_DIR)) 91 | EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) 92 | MODULE_INCDIR := $(addsuffix /include,$(INCDIR)) 93 | 94 | FW_FILE_1 := $(addprefix $(FW_BASE)/,$(FW_FILE_1).bin) 95 | FW_FILE_2 := $(addprefix $(FW_BASE)/,$(FW_FILE_2).bin) 96 | BLANKER := $(addprefix $(SDK_BASE)/,bin/blank.bin) 97 | 98 | V ?= $(VERBOSE) 99 | ifeq ("$(V)","1") 100 | Q := 101 | vecho := @true 102 | else 103 | Q := @ 104 | vecho := @echo 105 | endif 106 | 107 | vpath %.c $(SRC_DIR) 108 | 109 | define compile-objects 110 | $1/%.o: %.c 111 | $(vecho) "CC $$<" 112 | $(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 113 | endef 114 | 115 | .PHONY: all checkdirs clean 116 | 117 | all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2) 118 | 119 | $(FW_FILE_1): $(TARGET_OUT) 120 | $(vecho) "FW $@" 121 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_1_ARGS) 122 | 123 | $(FW_FILE_2): $(TARGET_OUT) 124 | $(vecho) "FW $@" 125 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_2_ARGS) 126 | 127 | $(TARGET_OUT): $(APP_AR) 128 | $(vecho) "LD $@" 129 | $(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ 130 | 131 | $(APP_AR): $(OBJ) 132 | $(vecho) "AR $@" 133 | $(Q) $(AR) cru $@ $^ 134 | 135 | checkdirs: $(BUILD_DIR) $(FW_BASE) 136 | 137 | $(BUILD_DIR): 138 | $(Q) mkdir -p $@ 139 | 140 | firmware: 141 | $(Q) mkdir -p $@ 142 | 143 | flash: firmware/0x00000.bin firmware/0x40000.bin 144 | $(PYTHON) $(ESPTOOL) -p $(ESPPORT) write_flash 0x00000 firmware/0x00000.bin 0x3C000 $(BLANKER) 0x40000 firmware/0x40000.bin 145 | 146 | test: flash 147 | screen $(ESPPORT) 115200 148 | 149 | rebuild: clean all 150 | 151 | clean: 152 | $(Q) rm -f $(APP_AR) 153 | $(Q) rm -f $(TARGET_OUT) 154 | $(Q) rm -rf $(BUILD_DIR) 155 | $(Q) rm -rf $(BUILD_BASE) 156 | $(Q) rm -f $(FW_FILE_1) 157 | $(Q) rm -f $(FW_FILE_2) 158 | $(Q) rm -rf $(FW_BASE) 159 | 160 | $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir)))) 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Instructables page: 2 | http://www.instructables.com/id/An-inexpensive-IoT-enabler-using-ESP8266/ 3 | 4 | ESP8266 IoT enabler 5 | - Sends temperature/humidity readings from a DHT11 sensor 6 | to a MQTT broker and a Thingspeak channel 7 | - Listens for MQTT messages and turns on or off a relay 8 | connected to ESP8266. 9 | - Checks a Thingspeak channel periodically and turns on/off 10 | a relay if there is an update. 11 | 12 | Connections: 13 | - GPIO0 to relay 14 | - GPIO2 to data pin of DHT11 15 | 16 | Build: 17 | * Configure ESP8266 build environment: 18 | - http://signusx.com/esp8266-windows-compilation-tutorial/ 19 | * Update user_config.h and change MQTT host and login details 20 | as well as WiFi login details. 21 | #define MQTT_HOST "your cloudmqtt instance name" 22 | #define MQTT_PORT your cloudmqtt instance port 23 | 24 | #define MQTT_USER "your cloudmqtt instance username" 25 | #define MQTT_PASS "your cloudmqtt instance password" 26 | 27 | #define STA_SSID "your WiFi SSID" 28 | #define STA_PASS "your WiFi password" 29 | * Update following two global variables in user_main.c to your 30 | values: 31 | char YOUR_THINGSPEAK_API_KEY[]= "xxxxxxxxxxxxxxxxx"; 32 | char YOUR_THINGSPEAK_CHANNEL[]= "xxxxxxxxx"; 33 | 34 | * Connect GPIO0 to ground by changing the jumper position. 35 | * Clean, build and flash the firmware to the ESP8266 module. 36 | * Remove jumper and power cycle the ESP8266 module. 37 | * Connect GPIO0 to relay input by inserting the jumper. 38 | * Thats it. You can now view the readings and control the relay. 39 | 40 | Operation: 41 | - Connects to a free online MQTT broker (cloudmqtt.com) 42 | - Subscribes to three topics - /esp8266/temperature, /esp8266/humidity and 43 | /esp8266/relay 44 | - Starts a timer that calls a function to periodically: 45 | - Send temperature/humidity readings to the MQTT broker 46 | - Send temperature and humidity readings to the following Thingspeak 47 | Channel: 48 | http://thingspeak.com/channels/21370 49 | - Read the last value of field1 in the Thingspeak channel that 50 | corresponds to the relay state and if there is a change in the value 51 | then send appropriate MQTT messages to turn on/off relay. 52 | 53 | - Defines a call back routine when MQTT messages are received 54 | - If a message is received on the /esp8266/relay topic, it will then 55 | turn on or off relay by setting GPIO0 high or low. 56 | - Sends relay state to field3 of Thingspeak channel. 57 | 58 | Display: 59 | - The temperature/humidity readings is available at https://thingspeak.com/channels/21370 60 | - The relay state is also available in the Thingspeak channel. A value of -1.0 means 61 | the relay is off and 1.0 means it is on. 62 | - These readings can also be viewed through any MQTT client connected to the broker. 63 | 64 | Control: 65 | - The relay can be turned on/off using any MQTT client connected to the same 66 | MQTT broker by sending a message, "on" of "off" to /esp8266/relay topic. 67 | 68 | - The relay can also be turned on/off by updating field1 of the Thingspeak 69 | channel using the following URLs: 70 | 71 | on: https://api.thingspeak.com/update?key=YF2DC4HXFSUQUIEE&field1=1.0 72 | off: https://api.thingspeak.com/update?key=YF2DC4HXFSUQUIEE&field1=-1.0 73 | 74 | Copy/paste the URL in a broswer. If the response is a non zero integer, it means 75 | the field was successfully updated. If a zero is received back, try the URL again. 76 | 77 | References: 78 | This project is based mainly on the following two git repos: 79 | https://github.com/tuanpmt/esp_mqtt 80 | https://github.com/Caerbannog/esphttpclient 81 | -------------------------------------------------------------------------------- /driver/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ############################################################# 3 | # Required variables for each makefile 4 | # Discard this section from all parent makefiles 5 | # Expected variables (with automatic defaults): 6 | # CSRCS (all "C" files in the dir) 7 | # SUBDIRS (all subdirs with a Makefile) 8 | # GEN_LIBS - list of libs to be generated () 9 | # GEN_IMAGES - list of images to be generated () 10 | # COMPONENTS_xxx - a list of libs/objs in the form 11 | # subdir/lib to be extracted and rolled up into 12 | # a generated lib/image xxx.a () 13 | # 14 | ifndef PDIR 15 | GEN_LIBS = libdriver.a 16 | endif 17 | 18 | 19 | ############################################################# 20 | # Configuration i.e. compile options etc. 21 | # Target specific stuff (defines etc.) goes in here! 22 | # Generally values applying to a tree are captured in the 23 | # makefile at its root level - these are then overridden 24 | # for a subtree within the makefile rooted therein 25 | # 26 | #DEFINES += 27 | 28 | ############################################################# 29 | # Recursion Magic - Don't touch this!! 30 | # 31 | # Each subtree potentially has an include directory 32 | # corresponding to the common APIs applicable to modules 33 | # rooted at that subtree. Accordingly, the INCLUDE PATH 34 | # of a module can only contain the include directories up 35 | # its parent path, and not its siblings 36 | # 37 | # Required for each makefile to inherit from the parent 38 | # 39 | 40 | INCLUDES := $(INCLUDES) -I $(PDIR)include 41 | INCLUDES += -I ./ 42 | PDIR := ../$(PDIR) 43 | sinclude $(PDIR)Makefile 44 | 45 | -------------------------------------------------------------------------------- /driver/dht22.c: -------------------------------------------------------------------------------- 1 | /* 2 | Driver for the temperature and humidity sensor DHT11 and DHT22 3 | Official repository: https://github.com/CHERTS/esp8266-dht11_22 4 | 5 | Copyright (C) 2014 Mikhail Grigorev (CHERTS) 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #include "ets_sys.h" 23 | #include "osapi.h" 24 | #include "c_types.h" 25 | #include "user_interface.h" 26 | #include "gpio.h" 27 | #include "driver/dht22.h" 28 | 29 | enum DHTType sensor_type; 30 | #define sleepms(x) os_delay_us(x*1000); 31 | static ETSTimer dhtTimer; 32 | 33 | static inline float scale_humidity(int *data) { 34 | if(sensor_type == DHT11) { 35 | return data[0]; 36 | } else { 37 | float humidity = data[0] * 256 + data[1]; 38 | return humidity /= 10; 39 | } 40 | } 41 | 42 | static inline float scale_temperature(int *data) { 43 | if(sensor_type == DHT11) { 44 | return data[2]; 45 | } else { 46 | float temperature = data[2] & 0x7f; 47 | temperature *= 256; 48 | temperature += data[3]; 49 | temperature /= 10; 50 | if (data[2] & 0x80) 51 | temperature *= -1; 52 | return temperature; 53 | } 54 | } 55 | 56 | static struct dht_sensor_data reading = { 57 | .success = 0 58 | }; 59 | 60 | struct dht_sensor_data *ICACHE_FLASH_ATTR DHTRead(void) { 61 | return &reading; 62 | } 63 | 64 | struct dht_sensor_data *ICACHE_FLASH_ATTR DHTCb(void) 65 | { 66 | int counter = 0; 67 | int laststate = 1; 68 | int i = 0; 69 | int j = 0; 70 | int checksum = 0; 71 | int data[100]; 72 | data[0] = data[1] = data[2] = data[3] = data[4] = 0; 73 | 74 | // Wake up device, 250ms of high 75 | GPIO_OUTPUT_SET(DHT_PIN, 1); 76 | sleepms(250); 77 | // Hold low for 20ms 78 | GPIO_OUTPUT_SET(DHT_PIN, 0); 79 | sleepms(20); 80 | // High for 40ns 81 | GPIO_OUTPUT_SET(DHT_PIN, 1); 82 | os_delay_us(40); 83 | // Set DHT_PIN pin as an input 84 | GPIO_DIS_OUTPUT(DHT_PIN); 85 | 86 | // wait for pin to drop? 87 | while (GPIO_INPUT_GET(DHT_PIN) == 1 && i < DHT_MAXCOUNT) { 88 | os_delay_us(1); 89 | i++; 90 | } 91 | 92 | if(i == DHT_MAXCOUNT) { 93 | reading.success = 0; 94 | os_printf("Failed to get reading, dying\r\n"); 95 | return; 96 | } 97 | 98 | // read data 99 | for (i = 0; i < DHT_MAXTIMINGS; i++) 100 | { 101 | // Count high time (in approx us) 102 | counter = 0; 103 | while (GPIO_INPUT_GET(DHT_PIN) == laststate) 104 | { 105 | counter++; 106 | os_delay_us(1); 107 | if (counter == 1000) 108 | break; 109 | } 110 | laststate = GPIO_INPUT_GET(DHT_PIN); 111 | if (counter == 1000) 112 | break; 113 | // store data after 3 reads 114 | if ((i>3) && (i%2 == 0)) { 115 | // shove each bit into the storage bytes 116 | data[j/8] <<= 1; 117 | if (counter > DHT_BREAKTIME) 118 | data[j/8] |= 1; 119 | j++; 120 | } 121 | } 122 | 123 | if (j >= 39) { 124 | checksum = (data[0] + data[1] + data[2] + data[3]) & 0xFF; 125 | os_printf("DHT: %02x %02x %02x %02x [%02x] CS: %02x", data[0], data[1],data[2],data[3],data[4],checksum); 126 | if (data[4] == checksum) { 127 | // checksum is valid 128 | reading.temperature = scale_temperature(data); 129 | reading.humidity = scale_humidity(data); 130 | os_printf("Temperature = %d *C, Humidity = %d %%\r\n", (int)(reading.temperature * 100), (int)(reading.humidity * 100)); 131 | reading.success = 1; 132 | } else { 133 | os_printf("Checksum was incorrect after %d bits. Expected %d but got %d\r\n", j, data[4], checksum); 134 | reading.success = 0; 135 | } 136 | } else { 137 | os_printf("Got too few bits: %d should be at least 40\r\n", j); 138 | reading.success = 0; 139 | 140 | } 141 | return; 142 | } 143 | 144 | void ICACHE_FLASH_ATTR DHTInit(enum DHTType dht_type, uint32_t poll_time) 145 | { 146 | sensor_type = dht_type; 147 | PIN_FUNC_SELECT(DHT_MUX, DHT_FUNC); 148 | PIN_PULLUP_EN(DHT_MUX); 149 | os_printf("DHT setup for type %d\r\n", dht_type); 150 | os_timer_disarm(&dhtTimer); 151 | os_timer_setfn(&dhtTimer, DHTCb, NULL); 152 | os_timer_arm(&dhtTimer, poll_time, 1); 153 | } 154 | 155 | void ICACHE_FLASH_ATTR DHTStop() 156 | { 157 | os_timer_disarm(&dhtTimer); 158 | } 159 | -------------------------------------------------------------------------------- /driver/uart.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright 2013-2014 Espressif Systems (Wuxi) 3 | * 4 | * FileName: uart.c 5 | * 6 | * Description: Two UART mode configration and interrupt handler. 7 | * Check your hardware connection while use this mode. 8 | * 9 | * Modification history: 10 | * 2014/3/12, v1.0 create this file. 11 | *******************************************************************************/ 12 | #include "ets_sys.h" 13 | #include "osapi.h" 14 | #include "driver/uart.h" 15 | #include "osapi.h" 16 | #include "driver/uart_register.h" 17 | //#include "ssc.h" 18 | 19 | 20 | // UartDev is defined and initialized in rom code. 21 | extern UartDevice UartDev; 22 | //extern os_event_t at_recvTaskQueue[at_recvTaskQueueLen]; 23 | 24 | LOCAL void uart0_rx_intr_handler(void *para); 25 | 26 | /****************************************************************************** 27 | * FunctionName : uart_config 28 | * Description : Internal used function 29 | * UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled 30 | * UART1 just used for debug output 31 | * Parameters : uart_no, use UART0 or UART1 defined ahead 32 | * Returns : NONE 33 | *******************************************************************************/ 34 | LOCAL void ICACHE_FLASH_ATTR 35 | uart_config(uint8 uart_no) 36 | { 37 | if (uart_no == UART1) 38 | { 39 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); 40 | } 41 | else 42 | { 43 | /* rcv_buff size if 0x100 */ 44 | ETS_UART_INTR_ATTACH(uart0_rx_intr_handler, &(UartDev.rcv_buff)); 45 | PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); 46 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); 47 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS); 48 | } 49 | 50 | uart_div_modify(uart_no, UART_CLK_FREQ / (UartDev.baut_rate)); 51 | 52 | WRITE_PERI_REG(UART_CONF0(uart_no), UartDev.exist_parity 53 | | UartDev.parity 54 | | (UartDev.stop_bits << UART_STOP_BIT_NUM_S) 55 | | (UartDev.data_bits << UART_BIT_NUM_S)); 56 | 57 | //clear rx and tx fifo,not ready 58 | SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); 59 | CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); 60 | 61 | //set rx fifo trigger 62 | // WRITE_PERI_REG(UART_CONF1(uart_no), 63 | // ((UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | 64 | // ((96 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S) | 65 | // UART_RX_FLOW_EN); 66 | if (uart_no == UART0) 67 | { 68 | //set rx fifo trigger 69 | WRITE_PERI_REG(UART_CONF1(uart_no), 70 | ((0x10 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | 71 | ((0x10 & UART_RX_FLOW_THRHD) << UART_RX_FLOW_THRHD_S) | 72 | UART_RX_FLOW_EN | 73 | (0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S | 74 | UART_RX_TOUT_EN); 75 | SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_TOUT_INT_ENA | 76 | UART_FRM_ERR_INT_ENA); 77 | } 78 | else 79 | { 80 | WRITE_PERI_REG(UART_CONF1(uart_no), 81 | ((UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S)); 82 | } 83 | 84 | //clear all interrupt 85 | WRITE_PERI_REG(UART_INT_CLR(uart_no), 0xffff); 86 | //enable rx_interrupt 87 | SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA); 88 | } 89 | 90 | /****************************************************************************** 91 | * FunctionName : uart1_tx_one_char 92 | * Description : Internal used function 93 | * Use uart1 interface to transfer one char 94 | * Parameters : uint8 TxChar - character to tx 95 | * Returns : OK 96 | *******************************************************************************/ 97 | LOCAL STATUS 98 | uart_tx_one_char(uint8 uart, uint8 TxChar) 99 | { 100 | while (true) 101 | { 102 | uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT<> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) { 104 | break; 105 | } 106 | } 107 | 108 | WRITE_PERI_REG(UART_FIFO(uart) , TxChar); 109 | return OK; 110 | } 111 | 112 | /****************************************************************************** 113 | * FunctionName : uart1_write_char 114 | * Description : Internal used function 115 | * Do some special deal while tx char is '\r' or '\n' 116 | * Parameters : char c - character to tx 117 | * Returns : NONE 118 | *******************************************************************************/ 119 | void ICACHE_FLASH_ATTR 120 | uart1_write_char(char c) 121 | { 122 | if (c == '\n') 123 | { 124 | uart_tx_one_char(UART1, '\r'); 125 | uart_tx_one_char(UART1, '\n'); 126 | } 127 | else if (c == '\r') 128 | { 129 | } 130 | else 131 | { 132 | uart_tx_one_char(UART1, c); 133 | } 134 | } 135 | 136 | void ICACHE_FLASH_ATTR 137 | uart0_write_char(char c) 138 | { 139 | if (c == '\n') 140 | { 141 | uart_tx_one_char(UART0, '\r'); 142 | uart_tx_one_char(UART0, '\n'); 143 | } 144 | else if (c == '\r') 145 | { 146 | } 147 | else 148 | { 149 | uart_tx_one_char(UART0, c); 150 | } 151 | } 152 | /****************************************************************************** 153 | * FunctionName : uart0_tx_buffer 154 | * Description : use uart0 to transfer buffer 155 | * Parameters : uint8 *buf - point to send buffer 156 | * uint16 len - buffer len 157 | * Returns : 158 | *******************************************************************************/ 159 | void ICACHE_FLASH_ATTR 160 | uart0_tx_buffer(uint8 *buf, uint16 len) 161 | { 162 | uint16 i; 163 | 164 | for (i = 0; i < len; i++) 165 | { 166 | uart_tx_one_char(UART0, buf[i]); 167 | } 168 | } 169 | 170 | /****************************************************************************** 171 | * FunctionName : uart0_sendStr 172 | * Description : use uart0 to transfer buffer 173 | * Parameters : uint8 *buf - point to send buffer 174 | * uint16 len - buffer len 175 | * Returns : 176 | *******************************************************************************/ 177 | void ICACHE_FLASH_ATTR 178 | uart0_sendStr(const char *str) 179 | { 180 | while(*str) 181 | { 182 | uart_tx_one_char(UART0, *str++); 183 | } 184 | } 185 | 186 | /****************************************************************************** 187 | * FunctionName : uart0_rx_intr_handler 188 | * Description : Internal used function 189 | * UART0 interrupt handler, add self handle code inside 190 | * Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg 191 | * Returns : NONE 192 | *******************************************************************************/ 193 | //extern void at_recvTask(void); 194 | 195 | LOCAL void 196 | uart0_rx_intr_handler(void *para) 197 | { 198 | /* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents 199 | * uart1 and uart0 respectively 200 | */ 201 | // RcvMsgBuff *pRxBuff = (RcvMsgBuff *)para; 202 | uint8 RcvChar; 203 | uint8 uart_no = UART0;//UartDev.buff_uart_no; 204 | 205 | // if (UART_RXFIFO_FULL_INT_ST != (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)) 206 | // { 207 | // return; 208 | // } 209 | // if (UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)) 210 | // { 211 | //// at_recvTask(); 212 | // RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; 213 | // system_os_post(at_recvTaskPrio, NULL, RcvChar); 214 | // WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_FULL_INT_CLR); 215 | // } 216 | if(UART_FRM_ERR_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_FRM_ERR_INT_ST)) 217 | { 218 | os_printf("FRM_ERR\r\n"); 219 | WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR); 220 | } 221 | 222 | if(UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)) 223 | { 224 | // os_printf("fifo full\r\n"); 225 | ETS_UART_INTR_DISABLE();///////// 226 | 227 | //system_os_post(at_recvTaskPrio, 0, 0); 228 | 229 | // WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_FULL_INT_CLR); 230 | // while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) 231 | // { 232 | //// at_recvTask(); 233 | // RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; 234 | // system_os_post(at_recvTaskPrio, NULL, RcvChar); 235 | // } 236 | } 237 | else if(UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST)) 238 | { 239 | ETS_UART_INTR_DISABLE();///////// 240 | 241 | //system_os_post(at_recvTaskPrio, 0, 0); 242 | 243 | // WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_TOUT_INT_CLR); 244 | //// os_printf("rx time over\r\n"); 245 | // while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) 246 | // { 247 | //// os_printf("process recv\r\n"); 248 | //// at_recvTask(); 249 | // RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; 250 | // system_os_post(at_recvTaskPrio, NULL, RcvChar); 251 | // } 252 | } 253 | 254 | // WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_RXFIFO_FULL_INT_CLR); 255 | 256 | // if (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) 257 | // { 258 | // RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; 259 | // at_recvTask(); 260 | // *(pRxBuff->pWritePos) = RcvChar; 261 | 262 | // system_os_post(at_recvTaskPrio, NULL, RcvChar); 263 | 264 | // //insert here for get one command line from uart 265 | // if (RcvChar == '\r') 266 | // { 267 | // pRxBuff->BuffState = WRITE_OVER; 268 | // } 269 | // 270 | // pRxBuff->pWritePos++; 271 | // 272 | // if (pRxBuff->pWritePos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) 273 | // { 274 | // // overflow ...we may need more error handle here. 275 | // pRxBuff->pWritePos = pRxBuff->pRcvMsgBuff ; 276 | // } 277 | // } 278 | } 279 | 280 | /****************************************************************************** 281 | * FunctionName : uart_init 282 | * Description : user interface for init uart 283 | * Parameters : UartBautRate uart0_br - uart0 bautrate 284 | * UartBautRate uart1_br - uart1 bautrate 285 | * Returns : NONE 286 | *******************************************************************************/ 287 | void ICACHE_FLASH_ATTR 288 | uart_init(UartBautRate uart0_br, UartBautRate uart1_br) 289 | { 290 | // rom use 74880 baut_rate, here reinitialize 291 | UartDev.baut_rate = uart0_br; 292 | uart_config(UART0); 293 | UartDev.baut_rate = uart1_br; 294 | uart_config(UART1); 295 | ETS_UART_INTR_ENABLE(); 296 | 297 | // install uart1 putc callback 298 | os_install_putc1((void *)uart0_write_char); 299 | } 300 | 301 | void ICACHE_FLASH_ATTR 302 | uart_reattach() 303 | { 304 | uart_init(BIT_RATE_74880, BIT_RATE_74880); 305 | // ETS_UART_INTR_ATTACH(uart_rx_intr_handler_ssc, &(UartDev.rcv_buff)); 306 | // ETS_UART_INTR_ENABLE(); 307 | } 308 | -------------------------------------------------------------------------------- /include/driver/dht22.h: -------------------------------------------------------------------------------- 1 | /* 2 | Driver for the temperature and humidity sensor DHT11 and DHT22 3 | Official repository: https://github.com/CHERTS/esp8266-dht11_22 4 | 5 | Copyright (C) 2014 Mikhail Grigorev (CHERTS) 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | 22 | #ifndef __DHT22_H__ 23 | #define __DHT22_H__ 24 | 25 | #include "ets_sys.h" 26 | #include "osapi.h" 27 | #include "gpio.h" 28 | 29 | enum DHTType{ 30 | DHT11, 31 | DHT22 32 | }; 33 | 34 | struct dht_sensor_data { 35 | float temperature; 36 | float humidity; 37 | BOOL success; 38 | }; 39 | 40 | #define DHT_MAXTIMINGS 10000 41 | #define DHT_BREAKTIME 20 42 | #define DHT_MAXCOUNT 32000 43 | #define DHT_MUX PERIPHS_IO_MUX_GPIO2_U 44 | #define DHT_FUNC FUNC_GPIO2 45 | #define DHT_PIN 2 46 | 47 | void DHTInit(enum DHTType dht_type, uint32_t poll_time); 48 | struct dht_sensor_data *DHTRead(void); 49 | void DHTStop(); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /include/driver/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef UART_APP_H 2 | #define UART_APP_H 3 | 4 | #include "uart_register.h" 5 | #include "eagle_soc.h" 6 | #include "c_types.h" 7 | 8 | #define RX_BUFF_SIZE 256 9 | #define TX_BUFF_SIZE 100 10 | #define UART0 0 11 | #define UART1 1 12 | 13 | typedef enum { 14 | FIVE_BITS = 0x0, 15 | SIX_BITS = 0x1, 16 | SEVEN_BITS = 0x2, 17 | EIGHT_BITS = 0x3 18 | } UartBitsNum4Char; 19 | 20 | typedef enum { 21 | ONE_STOP_BIT = 0, 22 | ONE_HALF_STOP_BIT = BIT2, 23 | TWO_STOP_BIT = BIT2 24 | } UartStopBitsNum; 25 | 26 | typedef enum { 27 | NONE_BITS = 0, 28 | ODD_BITS = 0, 29 | EVEN_BITS = BIT4 30 | } UartParityMode; 31 | 32 | typedef enum { 33 | STICK_PARITY_DIS = 0, 34 | STICK_PARITY_EN = BIT3 | BIT5 35 | } UartExistParity; 36 | 37 | typedef enum { 38 | BIT_RATE_9600 = 9600, 39 | BIT_RATE_19200 = 19200, 40 | BIT_RATE_38400 = 38400, 41 | BIT_RATE_57600 = 57600, 42 | BIT_RATE_74880 = 74880, 43 | BIT_RATE_115200 = 115200, 44 | BIT_RATE_230400 = 230400, 45 | BIT_RATE_256000 = 256000, 46 | BIT_RATE_460800 = 460800, 47 | BIT_RATE_921600 = 921600 48 | } UartBautRate; 49 | 50 | typedef enum { 51 | NONE_CTRL, 52 | HARDWARE_CTRL, 53 | XON_XOFF_CTRL 54 | } UartFlowCtrl; 55 | 56 | typedef enum { 57 | EMPTY, 58 | UNDER_WRITE, 59 | WRITE_OVER 60 | } RcvMsgBuffState; 61 | 62 | typedef struct { 63 | uint32 RcvBuffSize; 64 | uint8 *pRcvMsgBuff; 65 | uint8 *pWritePos; 66 | uint8 *pReadPos; 67 | uint8 TrigLvl; //JLU: may need to pad 68 | RcvMsgBuffState BuffState; 69 | } RcvMsgBuff; 70 | 71 | typedef struct { 72 | uint32 TrxBuffSize; 73 | uint8 *pTrxBuff; 74 | } TrxMsgBuff; 75 | 76 | typedef enum { 77 | BAUD_RATE_DET, 78 | WAIT_SYNC_FRM, 79 | SRCH_MSG_HEAD, 80 | RCV_MSG_BODY, 81 | RCV_ESC_CHAR, 82 | } RcvMsgState; 83 | 84 | typedef struct { 85 | UartBautRate baut_rate; 86 | UartBitsNum4Char data_bits; 87 | UartExistParity exist_parity; 88 | UartParityMode parity; // chip size in byte 89 | UartStopBitsNum stop_bits; 90 | UartFlowCtrl flow_ctrl; 91 | RcvMsgBuff rcv_buff; 92 | TrxMsgBuff trx_buff; 93 | RcvMsgState rcv_state; 94 | int received; 95 | int buff_uart_no; //indicate which uart use tx/rx buffer 96 | } UartDevice; 97 | 98 | void uart_init(UartBautRate uart0_br, UartBautRate uart1_br); 99 | void uart0_sendStr(const char *str); 100 | #endif 101 | 102 | -------------------------------------------------------------------------------- /include/driver/uart_register.h: -------------------------------------------------------------------------------- 1 | //Generated at 2012-07-03 18:44:06 2 | /* 3 | * Copyright (c) 2010 - 2011 Espressif System 4 | * 5 | */ 6 | 7 | #ifndef UART_REGISTER_H_INCLUDED 8 | #define UART_REGISTER_H_INCLUDED 9 | #define REG_UART_BASE( i ) (0x60000000+(i)*0xf00) 10 | //version value:32'h062000 11 | 12 | #define UART_FIFO( i ) (REG_UART_BASE( i ) + 0x0) 13 | #define UART_RXFIFO_RD_BYTE 0x000000FF 14 | #define UART_RXFIFO_RD_BYTE_S 0 15 | 16 | #define UART_INT_RAW( i ) (REG_UART_BASE( i ) + 0x4) 17 | #define UART_RXFIFO_TOUT_INT_RAW (BIT(8)) 18 | #define UART_BRK_DET_INT_RAW (BIT(7)) 19 | #define UART_CTS_CHG_INT_RAW (BIT(6)) 20 | #define UART_DSR_CHG_INT_RAW (BIT(5)) 21 | #define UART_RXFIFO_OVF_INT_RAW (BIT(4)) 22 | #define UART_FRM_ERR_INT_RAW (BIT(3)) 23 | #define UART_PARITY_ERR_INT_RAW (BIT(2)) 24 | #define UART_TXFIFO_EMPTY_INT_RAW (BIT(1)) 25 | #define UART_RXFIFO_FULL_INT_RAW (BIT(0)) 26 | 27 | #define UART_INT_ST( i ) (REG_UART_BASE( i ) + 0x8) 28 | #define UART_RXFIFO_TOUT_INT_ST (BIT(8)) 29 | #define UART_BRK_DET_INT_ST (BIT(7)) 30 | #define UART_CTS_CHG_INT_ST (BIT(6)) 31 | #define UART_DSR_CHG_INT_ST (BIT(5)) 32 | #define UART_RXFIFO_OVF_INT_ST (BIT(4)) 33 | #define UART_FRM_ERR_INT_ST (BIT(3)) 34 | #define UART_PARITY_ERR_INT_ST (BIT(2)) 35 | #define UART_TXFIFO_EMPTY_INT_ST (BIT(1)) 36 | #define UART_RXFIFO_FULL_INT_ST (BIT(0)) 37 | 38 | #define UART_INT_ENA( i ) (REG_UART_BASE( i ) + 0xC) 39 | #define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) 40 | #define UART_BRK_DET_INT_ENA (BIT(7)) 41 | #define UART_CTS_CHG_INT_ENA (BIT(6)) 42 | #define UART_DSR_CHG_INT_ENA (BIT(5)) 43 | #define UART_RXFIFO_OVF_INT_ENA (BIT(4)) 44 | #define UART_FRM_ERR_INT_ENA (BIT(3)) 45 | #define UART_PARITY_ERR_INT_ENA (BIT(2)) 46 | #define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) 47 | #define UART_RXFIFO_FULL_INT_ENA (BIT(0)) 48 | 49 | #define UART_INT_CLR( i ) (REG_UART_BASE( i ) + 0x10) 50 | #define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) 51 | #define UART_BRK_DET_INT_CLR (BIT(7)) 52 | #define UART_CTS_CHG_INT_CLR (BIT(6)) 53 | #define UART_DSR_CHG_INT_CLR (BIT(5)) 54 | #define UART_RXFIFO_OVF_INT_CLR (BIT(4)) 55 | #define UART_FRM_ERR_INT_CLR (BIT(3)) 56 | #define UART_PARITY_ERR_INT_CLR (BIT(2)) 57 | #define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) 58 | #define UART_RXFIFO_FULL_INT_CLR (BIT(0)) 59 | 60 | #define UART_CLKDIV( i ) (REG_UART_BASE( i ) + 0x14) 61 | #define UART_CLKDIV_CNT 0x000FFFFF 62 | #define UART_CLKDIV_S 0 63 | 64 | #define UART_AUTOBAUD( i ) (REG_UART_BASE( i ) + 0x18) 65 | #define UART_GLITCH_FILT 0x000000FF 66 | #define UART_GLITCH_FILT_S 8 67 | #define UART_AUTOBAUD_EN (BIT(0)) 68 | 69 | #define UART_STATUS( i ) (REG_UART_BASE( i ) + 0x1C) 70 | #define UART_TXD (BIT(31)) 71 | #define UART_RTSN (BIT(30)) 72 | #define UART_DTRN (BIT(29)) 73 | #define UART_TXFIFO_CNT 0x000000FF 74 | #define UART_TXFIFO_CNT_S 16 75 | #define UART_RXD (BIT(15)) 76 | #define UART_CTSN (BIT(14)) 77 | #define UART_DSRN (BIT(13)) 78 | #define UART_RXFIFO_CNT 0x000000FF 79 | #define UART_RXFIFO_CNT_S 0 80 | 81 | #define UART_CONF0( i ) (REG_UART_BASE( i ) + 0x20) 82 | #define UART_TXFIFO_RST (BIT(18)) 83 | #define UART_RXFIFO_RST (BIT(17)) 84 | #define UART_IRDA_EN (BIT(16)) 85 | #define UART_TX_FLOW_EN (BIT(15)) 86 | #define UART_LOOPBACK (BIT(14)) 87 | #define UART_IRDA_RX_INV (BIT(13)) 88 | #define UART_IRDA_TX_INV (BIT(12)) 89 | #define UART_IRDA_WCTL (BIT(11)) 90 | #define UART_IRDA_TX_EN (BIT(10)) 91 | #define UART_IRDA_DPLX (BIT(9)) 92 | #define UART_TXD_BRK (BIT(8)) 93 | #define UART_SW_DTR (BIT(7)) 94 | #define UART_SW_RTS (BIT(6)) 95 | #define UART_STOP_BIT_NUM 0x00000003 96 | #define UART_STOP_BIT_NUM_S 4 97 | #define UART_BIT_NUM 0x00000003 98 | #define UART_BIT_NUM_S 2 99 | #define UART_PARITY_EN (BIT(1)) 100 | #define UART_PARITY (BIT(0)) 101 | 102 | #define UART_CONF1( i ) (REG_UART_BASE( i ) + 0x24) 103 | #define UART_RX_TOUT_EN (BIT(31)) 104 | #define UART_RX_TOUT_THRHD 0x0000007F 105 | #define UART_RX_TOUT_THRHD_S 24 106 | #define UART_RX_FLOW_EN (BIT(23)) 107 | #define UART_RX_FLOW_THRHD 0x0000007F 108 | #define UART_RX_FLOW_THRHD_S 16 109 | #define UART_TXFIFO_EMPTY_THRHD 0x0000007F 110 | #define UART_TXFIFO_EMPTY_THRHD_S 8 111 | #define UART_RXFIFO_FULL_THRHD 0x0000007F 112 | #define UART_RXFIFO_FULL_THRHD_S 0 113 | 114 | #define UART_LOWPULSE( i ) (REG_UART_BASE( i ) + 0x28) 115 | #define UART_LOWPULSE_MIN_CNT 0x000FFFFF 116 | #define UART_LOWPULSE_MIN_CNT_S 0 117 | 118 | #define UART_HIGHPULSE( i ) (REG_UART_BASE( i ) + 0x2C) 119 | #define UART_HIGHPULSE_MIN_CNT 0x000FFFFF 120 | #define UART_HIGHPULSE_MIN_CNT_S 0 121 | 122 | #define UART_PULSE_NUM( i ) (REG_UART_BASE( i ) + 0x30) 123 | #define UART_PULSE_NUM_CNT 0x0003FF 124 | #define UART_PULSE_NUM_CNT_S 0 125 | 126 | #define UART_DATE( i ) (REG_UART_BASE( i ) + 0x78) 127 | #define UART_ID( i ) (REG_UART_BASE( i ) + 0x7C) 128 | #endif // UART_REGISTER_H_INCLUDED 129 | -------------------------------------------------------------------------------- /include/httpclient.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPCLIENT_H 2 | #define HTTPCLIENT_H 3 | 4 | #define BUFFER_SIZE_MAX 5000 5 | 6 | /* 7 | * "full_response" is a string containing all response headers and the response body. 8 | * "response_body and "http_status" are extracted from "full_response" for convenience. 9 | * 10 | * A successful request corresponds to an HTTP status code of 200 (OK). 11 | * More info at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 12 | */ 13 | typedef void (* http_callback)(char * response_body, int http_status, char * full_response); 14 | 15 | /* 16 | * Download a web page from its URL. 17 | * Try: 18 | * http_get("http://wtfismyip.com/text", http_callback_example); 19 | */ 20 | void ICACHE_FLASH_ATTR http_get(const char * url, http_callback user_callback); 21 | 22 | /* 23 | * Post data to a web form. 24 | * The data should be encoded as application/x-www-form-urlencoded. 25 | * Try: 26 | * http_post("http://httpbin.org/post", "first_word=hello&second_word=world", http_callback_example); 27 | */ 28 | void ICACHE_FLASH_ATTR http_post(const char * url, const char * post_data, http_callback user_callback); 29 | 30 | /* 31 | * Call this function to skip URL parsing if the arguments are already in separate variables. 32 | */ 33 | void ICACHE_FLASH_ATTR http_raw_request(const char * hostname, int port, const char * path, const char * post_data, http_callback user_callback); 34 | 35 | /* 36 | * Output on the UART. 37 | */ 38 | void http_callback_example(char * response, int http_status, char * full_response); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/user_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _USER_CONFIG_H_ 2 | #define _USER_CONFIG_H_ 3 | 4 | #define CFG_HOLDER 0x00FF55A4 5 | #define CFG_LOCATION 0x3C /* Please don't change or if you know what you doing */ 6 | 7 | /*DEFAULT CONFIGURATIONS*/ 8 | 9 | #define MQTT_HOST "your cloudmqtt instance name" 10 | #define MQTT_PORT your cloudmqtt instance port 11 | #define MQTT_BUF_SIZE 1024 12 | #define MQTT_KEEPALIVE 120 /*second*/ 13 | 14 | #define MQTT_CLIENT_ID "DVES_%08X" 15 | #define MQTT_USER "your cloudmqtt instance username" 16 | #define MQTT_PASS "your cloudmqtt instance password" 17 | 18 | #define STA_SSID "your WiFi SSID" 19 | #define STA_PASS "your WiFi password" 20 | #define STA_TYPE AUTH_WPA2_PSK 21 | 22 | #define MQTT_RECONNECT_TIMEOUT 5 /*second*/ 23 | 24 | #define DEFAULT_SECURITY 0 25 | #define QUEUE_BUFFER_SIZE 2048 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /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/config.c: -------------------------------------------------------------------------------- 1 | /* 2 | /* config.c 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 | #include "ets_sys.h" 32 | #include "os_type.h" 33 | #include "mem.h" 34 | #include "osapi.h" 35 | #include "user_interface.h" 36 | 37 | #include "mqtt.h" 38 | #include "config.h" 39 | #include "user_config.h" 40 | #include "debug.h" 41 | 42 | SYSCFG sysCfg; 43 | SAVE_FLAG saveFlag; 44 | 45 | void CFG_Save() 46 | { 47 | spi_flash_read((CFG_LOCATION + 3) * SPI_FLASH_SEC_SIZE, 48 | (uint32 *)&saveFlag, sizeof(SAVE_FLAG)); 49 | 50 | if (saveFlag.flag == 0) { 51 | spi_flash_erase_sector(CFG_LOCATION + 1); 52 | spi_flash_write((CFG_LOCATION + 1) * SPI_FLASH_SEC_SIZE, 53 | (uint32 *)&sysCfg, sizeof(SYSCFG)); 54 | saveFlag.flag = 1; 55 | spi_flash_erase_sector(CFG_LOCATION + 3); 56 | spi_flash_write((CFG_LOCATION + 3) * SPI_FLASH_SEC_SIZE, 57 | (uint32 *)&saveFlag, sizeof(SAVE_FLAG)); 58 | } else { 59 | spi_flash_erase_sector(CFG_LOCATION + 0); 60 | spi_flash_write((CFG_LOCATION + 0) * SPI_FLASH_SEC_SIZE, 61 | (uint32 *)&sysCfg, sizeof(SYSCFG)); 62 | saveFlag.flag = 0; 63 | spi_flash_erase_sector(CFG_LOCATION + 3); 64 | spi_flash_write((CFG_LOCATION + 3) * SPI_FLASH_SEC_SIZE, 65 | (uint32 *)&saveFlag, sizeof(SAVE_FLAG)); 66 | } 67 | } 68 | 69 | void CFG_Load() 70 | { 71 | 72 | INFO("\r\nload ...\r\n"); 73 | spi_flash_read((CFG_LOCATION + 3) * SPI_FLASH_SEC_SIZE, 74 | (uint32 *)&saveFlag, sizeof(SAVE_FLAG)); 75 | if (saveFlag.flag == 0) { 76 | spi_flash_read((CFG_LOCATION + 0) * SPI_FLASH_SEC_SIZE, 77 | (uint32 *)&sysCfg, sizeof(SYSCFG)); 78 | } else { 79 | spi_flash_read((CFG_LOCATION + 1) * SPI_FLASH_SEC_SIZE, 80 | (uint32 *)&sysCfg, sizeof(SYSCFG)); 81 | } 82 | if(sysCfg.cfg_holder != CFG_HOLDER){ 83 | os_memset(&sysCfg, 0x00, sizeof sysCfg); 84 | 85 | 86 | sysCfg.cfg_holder = CFG_HOLDER; 87 | 88 | os_sprintf(sysCfg.sta_ssid, "%s", STA_SSID); 89 | os_sprintf(sysCfg.sta_pwd, "%s", STA_PASS); 90 | sysCfg.sta_type = STA_TYPE; 91 | 92 | os_sprintf(sysCfg.device_id, MQTT_CLIENT_ID, system_get_chip_id()); 93 | os_sprintf(sysCfg.mqtt_host, "%s", MQTT_HOST); 94 | sysCfg.mqtt_port = MQTT_PORT; 95 | os_sprintf(sysCfg.mqtt_user, "%s", MQTT_USER); 96 | os_sprintf(sysCfg.mqtt_pass, "%s", MQTT_PASS); 97 | 98 | sysCfg.security = DEFAULT_SECURITY; /* default non ssl */ 99 | 100 | sysCfg.mqtt_keepalive = MQTT_KEEPALIVE; 101 | 102 | INFO(" default configurations\r\n"); 103 | 104 | CFG_Save(); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /mqtt/config.h: -------------------------------------------------------------------------------- 1 | /* config.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_CONFIG_H_ 32 | #define USER_CONFIG_H_ 33 | #include "os_type.h" 34 | #include "user_config.h" 35 | typedef struct{ 36 | uint32_t cfg_holder; 37 | uint8_t device_id[16]; 38 | 39 | uint8_t sta_ssid[64]; 40 | uint8_t sta_pwd[64]; 41 | uint32_t sta_type; 42 | 43 | uint8_t mqtt_host[64]; 44 | uint32_t mqtt_port; 45 | uint8_t mqtt_user[32]; 46 | uint8_t mqtt_pass[32]; 47 | uint32_t mqtt_keepalive; 48 | uint8_t security; 49 | } SYSCFG; 50 | 51 | typedef struct { 52 | uint8 flag; 53 | uint8 pad[3]; 54 | } SAVE_FLAG; 55 | 56 | void CFG_Save(); 57 | void CFG_Load(); 58 | 59 | extern SYSCFG sysCfg; 60 | 61 | #endif /* USER_CONFIG_H_ */ 62 | -------------------------------------------------------------------------------- /mqtt/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * debug.h 3 | * 4 | * Created on: Dec 4, 2014 5 | * Author: Minh 6 | */ 7 | 8 | #ifndef USER_DEBUG_H_ 9 | #define USER_DEBUG_H_ 10 | 11 | #ifndef INFO 12 | #define INFO os_printf 13 | #endif 14 | 15 | #endif /* USER_DEBUG_H_ */ 16 | -------------------------------------------------------------------------------- /mqtt/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 "config.h" 41 | #include "mqtt.h" 42 | #include "queue.h" 43 | 44 | #define MQTT_TASK_PRIO 0 45 | #define MQTT_TASK_QUEUE_SIZE 1 46 | #define MQTT_SEND_TIMOUT 5 47 | 48 | #ifndef QUEUE_BUFFER_SIZE 49 | #define QUEUE_BUFFER_SIZE 2048 50 | #endif 51 | 52 | unsigned char *default_certificate; 53 | unsigned int default_certificate_len = 0; 54 | unsigned char *default_private_key; 55 | unsigned int default_private_key_len = 0; 56 | 57 | os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; 58 | 59 | LOCAL void ICACHE_FLASH_ATTR 60 | mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) 61 | { 62 | struct espconn *pConn = (struct espconn *)arg; 63 | MQTT_Client* client = (MQTT_Client *)pConn->reverse; 64 | 65 | 66 | if(ipaddr == NULL) 67 | { 68 | INFO("DNS: Found, but got no ip, try to reconnect\r\n"); 69 | client->connState = TCP_RECONNECT_REQ; 70 | return; 71 | } 72 | 73 | INFO("DNS: found ip %d.%d.%d.%d\n", 74 | *((uint8 *) &ipaddr->addr), 75 | *((uint8 *) &ipaddr->addr + 1), 76 | *((uint8 *) &ipaddr->addr + 2), 77 | *((uint8 *) &ipaddr->addr + 3)); 78 | 79 | if(client->ip.addr == 0 && ipaddr->addr != 0) 80 | { 81 | os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); 82 | if(client->security){ 83 | espconn_secure_connect(client->pCon); 84 | } 85 | else { 86 | espconn_connect(client->pCon); 87 | } 88 | 89 | client->connState = TCP_CONNECTING; 90 | INFO("TCP: connecting...\r\n"); 91 | } 92 | 93 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 94 | } 95 | 96 | 97 | 98 | LOCAL void ICACHE_FLASH_ATTR 99 | deliver_publish(MQTT_Client* client, uint8_t* message, int length) 100 | { 101 | mqtt_event_data_t event_data; 102 | 103 | event_data.topic_length = length; 104 | event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); 105 | event_data.data_length = length; 106 | event_data.data = mqtt_get_publish_data(message, &event_data.data_length); 107 | 108 | if(client->dataCb) 109 | client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); 110 | 111 | } 112 | 113 | 114 | /** 115 | * @brief Client received callback function. 116 | * @param arg: contain the ip link information 117 | * @param pdata: received data 118 | * @param len: the lenght of received data 119 | * @retval None 120 | */ 121 | void ICACHE_FLASH_ATTR 122 | mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len) 123 | { 124 | uint8_t msg_type; 125 | uint8_t msg_qos; 126 | uint16_t msg_id; 127 | 128 | struct espconn *pCon = (struct espconn*)arg; 129 | MQTT_Client *client = (MQTT_Client *)pCon->reverse; 130 | 131 | READPACKET: 132 | INFO("TCP: data received %d bytes\r\n", len); 133 | if(len < MQTT_BUF_SIZE && len > 0){ 134 | os_memcpy(client->mqtt_state.in_buffer, pdata, len); 135 | 136 | switch(client->connState){ 137 | case MQTT_DATA: 138 | client->mqtt_state.message_length_read = len; 139 | client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); 140 | msg_type = mqtt_get_type(client->mqtt_state.in_buffer); 141 | msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); 142 | msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); 143 | 144 | switch(msg_type) 145 | { 146 | case MQTT_MSG_TYPE_CONNACK: 147 | if(client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT){ 148 | INFO("MQTT: Invalid packet\r\n"); 149 | if(client->security){ 150 | espconn_secure_disconnect(client->pCon); 151 | } 152 | else { 153 | espconn_disconnect(client->pCon); 154 | } 155 | } else { 156 | INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port); 157 | if(client->connectedCb) 158 | client->connectedCb((uint32_t*)client); 159 | } 160 | break; 161 | case MQTT_MSG_TYPE_SUBACK: 162 | if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) 163 | INFO("MQTT: Subscribe successful\r\n"); 164 | break; 165 | case MQTT_MSG_TYPE_UNSUBACK: 166 | if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) 167 | INFO("MQTT: UnSubscribe successful\r\n"); 168 | break; 169 | case MQTT_MSG_TYPE_PUBLISH: 170 | if(msg_qos == 1) 171 | client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); 172 | else if(msg_qos == 2) 173 | client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); 174 | if(msg_qos == 1 || msg_qos == 2){ 175 | INFO("MQTT: Queue response QoS: %d\r\n", msg_qos); 176 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 177 | INFO("MQTT: Exceed the amount of queues\r\n"); 178 | } 179 | } 180 | 181 | deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); 182 | break; 183 | case MQTT_MSG_TYPE_PUBACK: 184 | if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){ 185 | INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n"); 186 | } 187 | 188 | break; 189 | case MQTT_MSG_TYPE_PUBREC: 190 | client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); 191 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 192 | INFO("MQTT: Exceed the amount of queues\r\n"); 193 | } 194 | break; 195 | case MQTT_MSG_TYPE_PUBREL: 196 | client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); 197 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 198 | INFO("MQTT: Exceed the amount of queues\r\n"); 199 | } 200 | break; 201 | case MQTT_MSG_TYPE_PUBCOMP: 202 | if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){ 203 | INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n"); 204 | } 205 | break; 206 | case MQTT_MSG_TYPE_PINGREQ: 207 | client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); 208 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 209 | INFO("MQTT: Exceed the amount of queues\r\n"); 210 | } 211 | break; 212 | case MQTT_MSG_TYPE_PINGRESP: 213 | // Ignore 214 | break; 215 | } 216 | // NOTE: this is done down here and not in the switch case above 217 | // because the PSOCK_READBUF_LEN() won't work inside a switch 218 | // statement due to the way protothreads resume. 219 | if(msg_type == MQTT_MSG_TYPE_PUBLISH) 220 | { 221 | len = client->mqtt_state.message_length_read; 222 | 223 | if(client->mqtt_state.message_length < client->mqtt_state.message_length_read) 224 | { 225 | //client->connState = MQTT_PUBLISH_RECV; 226 | //Not Implement yet 227 | len -= client->mqtt_state.message_length; 228 | pdata += client->mqtt_state.message_length; 229 | 230 | INFO("Get another published message\r\n"); 231 | goto READPACKET; 232 | } 233 | 234 | } 235 | break; 236 | } 237 | } else { 238 | INFO("ERROR: Too long message\r\n"); 239 | } 240 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 241 | } 242 | 243 | /** 244 | * @brief Client send over callback function. 245 | * @param arg: contain the ip link information 246 | * @retval None 247 | */ 248 | void ICACHE_FLASH_ATTR 249 | mqtt_tcpclient_sent_cb(void *arg) 250 | { 251 | struct espconn *pCon = (struct espconn *)arg; 252 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 253 | INFO("TCP: Sent\r\n"); 254 | client->sendTimeout = 0; 255 | if(client->connState == MQTT_DATA && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH){ 256 | if(client->publishedCb) 257 | client->publishedCb((uint32_t*)client); 258 | } 259 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 260 | } 261 | 262 | void ICACHE_FLASH_ATTR mqtt_timer(void *arg) 263 | { 264 | MQTT_Client* client = (MQTT_Client*)arg; 265 | 266 | if(client->connState == MQTT_DATA){ 267 | client->keepAliveTick ++; 268 | if(client->keepAliveTick > client->mqtt_state.connect_info->keepalive){ 269 | 270 | INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port); 271 | client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); 272 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 273 | INFO("MQTT: Exceed the amount of queues\r\n"); 274 | } 275 | 276 | client->keepAliveTick = 0; 277 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 278 | } 279 | 280 | } else if(client->connState == TCP_RECONNECT_REQ){ 281 | client->reconnectTick ++; 282 | if(client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { 283 | client->reconnectTick = 0; 284 | client->connState = TCP_RECONNECT; 285 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 286 | } 287 | } 288 | if(client->sendTimeout > 0) 289 | client->sendTimeout --; 290 | } 291 | 292 | void ICACHE_FLASH_ATTR 293 | mqtt_tcpclient_discon_cb(void *arg) 294 | { 295 | 296 | struct espconn *pespconn = (struct espconn *)arg; 297 | MQTT_Client* client = (MQTT_Client *)pespconn->reverse; 298 | INFO("TCP: Disconnected callback\r\n"); 299 | client->connState = TCP_RECONNECT_REQ; 300 | if(client->disconnectedCb) 301 | client->disconnectedCb((uint32_t*)client); 302 | 303 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 304 | } 305 | 306 | 307 | 308 | /** 309 | * @brief Tcp client connect success callback function. 310 | * @param arg: contain the ip link information 311 | * @retval None 312 | */ 313 | void ICACHE_FLASH_ATTR 314 | mqtt_tcpclient_connect_cb(void *arg) 315 | { 316 | struct espconn *pCon = (struct espconn *)arg; 317 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 318 | 319 | espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); 320 | espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// 321 | espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// 322 | INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port); 323 | 324 | mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); 325 | client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); 326 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 327 | INFO("MQTT: Exceed the amount of queues\r\n"); 328 | } 329 | client->mqtt_state.outbound_message = NULL; 330 | client->connState = MQTT_DATA; 331 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 332 | } 333 | 334 | /** 335 | * @brief Tcp client connect repeat callback function. 336 | * @param arg: contain the ip link information 337 | * @retval None 338 | */ 339 | void ICACHE_FLASH_ATTR 340 | mqtt_tcpclient_recon_cb(void *arg, sint8 errType) 341 | { 342 | struct espconn *pCon = (struct espconn *)arg; 343 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 344 | 345 | INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port); 346 | 347 | client->connState = TCP_RECONNECT_REQ; 348 | 349 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 350 | 351 | } 352 | 353 | /** 354 | * @brief MQTT publish function. 355 | * @param client: MQTT_Client reference 356 | * @param topic: string topic will publish to 357 | * @param data: buffer data send point to 358 | * @param data_length: length of data 359 | * @param qos: qos 360 | * @param retain: retain 361 | * @retval TRUE if success queue 362 | */ 363 | BOOL ICACHE_FLASH_ATTR 364 | MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain) 365 | { 366 | mqtt_message_t* outbound_message; 367 | 368 | 369 | outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, 370 | topic, data, data_length, 371 | qos, retain, 372 | &client->mqtt_state.pending_msg_id); 373 | INFO("MQTT: queuing publish, length: %d...\r\n", outbound_message->length); 374 | if(QUEUE_Puts(&client->msgQueue, outbound_message->data, outbound_message->length) == -1){ 375 | INFO("MQTT: Exceed the amount of queues\r\n"); 376 | return FALSE; 377 | } 378 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 379 | return TRUE; 380 | } 381 | 382 | /** 383 | * @brief MQTT subscibe function. 384 | * @param client: MQTT_Client reference 385 | * @param topic: string topic will subscribe 386 | * @param qos: qos 387 | * @retval TRUE if success queue 388 | */ 389 | BOOL ICACHE_FLASH_ATTR 390 | MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) 391 | { 392 | 393 | 394 | client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, 395 | topic, 0, 396 | &client->mqtt_state.pending_msg_id); 397 | INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n",topic, client->mqtt_state.pending_msg_id); 398 | if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ 399 | INFO("MQTT: Exceed the amount of queues\r\n"); 400 | return FALSE; 401 | } 402 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 403 | return TRUE; 404 | } 405 | 406 | void ICACHE_FLASH_ATTR 407 | MQTT_Task(os_event_t *e) 408 | { 409 | MQTT_Client* client = (MQTT_Client*)e->par; 410 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 411 | uint16_t dataLen; 412 | switch(client->connState){ 413 | 414 | case TCP_RECONNECT_REQ: 415 | break; 416 | case TCP_RECONNECT: 417 | MQTT_Connect(client); 418 | INFO("TCP:Reconect to: %s:%d\r\n", client->host, client->port); 419 | client->connState = TCP_CONNECTING; 420 | break; 421 | case MQTT_DATA: 422 | if(QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) { 423 | break; 424 | } 425 | if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0){ 426 | client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); 427 | client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); 428 | 429 | 430 | client->sendTimeout = MQTT_SEND_TIMOUT; 431 | INFO("MQTT: Sending, type: %d, id: %04X\r\n",client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); 432 | if(client->security){ 433 | espconn_secure_sent(client->pCon, dataBuffer, dataLen); 434 | } 435 | else{ 436 | espconn_sent(client->pCon, dataBuffer, dataLen); 437 | } 438 | 439 | client->mqtt_state.outbound_message = NULL; 440 | break; 441 | } 442 | break; 443 | } 444 | } 445 | 446 | /** 447 | * @brief MQTT initialization connection function 448 | * @param client: MQTT_Client reference 449 | * @param host: Domain or IP string 450 | * @param port: Port to connect 451 | * @param security: 1 for ssl, 0 for none 452 | * @retval None 453 | */ 454 | void MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security) 455 | { 456 | uint32_t temp; 457 | INFO("MQTT_InitConnection\r\n"); 458 | os_memset(mqttClient, 0, sizeof(MQTT_Client)); 459 | 460 | mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); 461 | mqttClient->pCon->type = ESPCONN_TCP; 462 | mqttClient->pCon->state = ESPCONN_NONE; 463 | mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); 464 | mqttClient->pCon->proto.tcp->local_port = espconn_port(); 465 | mqttClient->pCon->proto.tcp->remote_port = port; 466 | temp = os_strlen(host); 467 | mqttClient->host = (uint8_t*)os_zalloc(temp + 1); 468 | os_strcpy(mqttClient->host, host); 469 | mqttClient->host[temp] = 0; 470 | mqttClient->port = port; 471 | mqttClient->security = security; 472 | mqttClient->pCon->reverse = mqttClient; 473 | espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); 474 | espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); 475 | } 476 | 477 | /** 478 | * @brief MQTT initialization mqtt client function 479 | * @param client: MQTT_Client reference 480 | * @param clientid: MQTT client id 481 | * @param client_user:MQTT client user 482 | * @param client_pass:MQTT client password 483 | * @param client_pass:MQTT keep alive timer, in second 484 | * @retval None 485 | */ 486 | void MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession) 487 | { 488 | uint32_t temp; 489 | INFO("MQTT_InitClient\r\n"); 490 | 491 | os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); 492 | 493 | temp = os_strlen(client_id); 494 | mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1); 495 | os_strcpy(mqttClient->connect_info.client_id, client_id); 496 | mqttClient->connect_info.client_id[temp] = 0; 497 | 498 | temp = os_strlen(client_user); 499 | mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1); 500 | os_strcpy(mqttClient->connect_info.username, client_user); 501 | mqttClient->connect_info.username[temp] = 0; 502 | 503 | temp = os_strlen(client_pass); 504 | mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1); 505 | os_strcpy(mqttClient->connect_info.password, client_pass); 506 | mqttClient->connect_info.password[temp] = 0; 507 | 508 | 509 | mqttClient->connect_info.keepalive = keepAliveTime; 510 | mqttClient->connect_info.clean_session = cleanSession; 511 | 512 | mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); 513 | mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; 514 | mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); 515 | mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; 516 | mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; 517 | mqttClient->keepAliveTick = 0; 518 | mqttClient->reconnectTick = 0; 519 | 520 | os_timer_disarm(&mqttClient->mqttTimer); 521 | os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); 522 | os_timer_arm(&mqttClient->mqttTimer, 1000, 1); 523 | 524 | QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); 525 | 526 | system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); 527 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); 528 | } 529 | void MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) 530 | { 531 | uint32_t temp; 532 | temp = os_strlen(will_topic); 533 | mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1); 534 | os_strcpy(mqttClient->connect_info.will_topic, will_topic); 535 | mqttClient->connect_info.will_topic[temp] = 0; 536 | 537 | temp = os_strlen(will_msg); 538 | mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1); 539 | os_strcpy(mqttClient->connect_info.will_message, will_msg); 540 | mqttClient->connect_info.will_message[temp] = 0; 541 | 542 | 543 | mqttClient->connect_info.will_qos = will_qos; 544 | mqttClient->connect_info.will_retain = will_retain; 545 | } 546 | /** 547 | * @brief Begin connect to MQTT broker 548 | * @param client: MQTT_Client reference 549 | * @retval None 550 | */ 551 | void MQTT_Connect(MQTT_Client *mqttClient) 552 | { 553 | if(UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) { 554 | INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port); 555 | if(mqttClient->security){ 556 | espconn_secure_connect(mqttClient->pCon); 557 | } 558 | else { 559 | espconn_connect(mqttClient->pCon); 560 | } 561 | } 562 | else { 563 | INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port); 564 | espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found); 565 | } 566 | mqttClient->connState = TCP_CONNECTING; 567 | } 568 | 569 | void MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) 570 | { 571 | mqttClient->connectedCb = connectedCb; 572 | } 573 | 574 | void MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) 575 | { 576 | mqttClient->disconnectedCb = disconnectedCb; 577 | } 578 | 579 | void MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) 580 | { 581 | mqttClient->dataCb = dataCb; 582 | } 583 | 584 | void MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) 585 | { 586 | mqttClient->publishedCb = publishedCb; 587 | } 588 | -------------------------------------------------------------------------------- /mqtt/mqtt.h: -------------------------------------------------------------------------------- 1 | /* mqtt.h 2 | * 3 | * Copyright (c) 2014-2015, Tuan PM 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | #ifndef USER_AT_MQTT_H_ 31 | #define USER_AT_MQTT_H_ 32 | #include "mqtt_msg.h" 33 | #include "user_interface.h" 34 | 35 | #include "queue.h" 36 | typedef struct mqtt_event_data_t 37 | { 38 | uint8_t type; 39 | const char* topic; 40 | const char* data; 41 | uint16_t topic_length; 42 | uint16_t data_length; 43 | uint16_t data_offset; 44 | } mqtt_event_data_t; 45 | 46 | typedef struct mqtt_state_t 47 | { 48 | uint16_t port; 49 | int auto_reconnect; 50 | mqtt_connect_info_t* connect_info; 51 | uint8_t* in_buffer; 52 | uint8_t* out_buffer; 53 | int in_buffer_length; 54 | int out_buffer_length; 55 | uint16_t message_length; 56 | uint16_t message_length_read; 57 | mqtt_message_t* outbound_message; 58 | mqtt_connection_t mqtt_connection; 59 | uint16_t pending_msg_id; 60 | int pending_msg_type; 61 | int pending_publish_qos; 62 | } mqtt_state_t; 63 | 64 | typedef enum { 65 | WIFI_INIT, 66 | WIFI_CONNECTING, 67 | WIFI_CONNECTING_ERROR, 68 | WIFI_CONNECTED, 69 | DNS_RESOLVE, 70 | TCP_DISCONNECTED, 71 | TCP_RECONNECT_REQ, 72 | TCP_RECONNECT, 73 | TCP_CONNECTING, 74 | TCP_CONNECTING_ERROR, 75 | TCP_CONNECTED, 76 | MQTT_CONNECT_SEND, 77 | MQTT_CONNECT_SENDING, 78 | MQTT_SUBSCIBE_SEND, 79 | MQTT_SUBSCIBE_SENDING, 80 | MQTT_DATA, 81 | MQTT_PUBLISH_RECV, 82 | MQTT_PUBLISHING 83 | } tConnState; 84 | 85 | typedef void (*MqttCallback)(uint32_t *args); 86 | typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh); 87 | 88 | typedef struct { 89 | struct espconn *pCon; 90 | uint8_t security; 91 | uint8_t* host; 92 | uint32_t port; 93 | ip_addr_t ip; 94 | mqtt_state_t mqtt_state; 95 | mqtt_connect_info_t connect_info; 96 | MqttCallback connectedCb; 97 | MqttCallback disconnectedCb; 98 | MqttCallback publishedCb; 99 | MqttDataCallback dataCb; 100 | ETSTimer mqttTimer; 101 | uint32_t keepAliveTick; 102 | uint32_t reconnectTick; 103 | uint32_t sendTimeout; 104 | tConnState connState; 105 | QUEUE msgQueue; 106 | } MQTT_Client; 107 | 108 | #define SEC_NONSSL 0 109 | #define SEC_SSL 1 110 | 111 | #define MQTT_FLAG_CONNECTED 1 112 | #define MQTT_FLAG_READY 2 113 | #define MQTT_FLAG_EXIT 4 114 | 115 | #define MQTT_EVENT_TYPE_NONE 0 116 | #define MQTT_EVENT_TYPE_CONNECTED 1 117 | #define MQTT_EVENT_TYPE_DISCONNECTED 2 118 | #define MQTT_EVENT_TYPE_SUBSCRIBED 3 119 | #define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 120 | #define MQTT_EVENT_TYPE_PUBLISH 5 121 | #define MQTT_EVENT_TYPE_PUBLISHED 6 122 | #define MQTT_EVENT_TYPE_EXITED 7 123 | #define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 124 | 125 | void MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security); 126 | void MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession); 127 | void MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain); 128 | void MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); 129 | void MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); 130 | void MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); 131 | void MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); 132 | BOOL MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); 133 | void MQTT_Connect(MQTT_Client *mqttClient); 134 | BOOL MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain); 135 | 136 | #endif /* USER_AT_MQTT_H_ */ 137 | -------------------------------------------------------------------------------- /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 | 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 | uint8_t magic[6]; 51 | uint8_t version; 52 | uint8_t flags; 53 | uint8_t keepaliveMsb; 54 | uint8_t keepaliveLsb; 55 | }; 56 | 57 | static int append_string(mqtt_connection_t* connection, const char* string, int len) 58 | { 59 | if(connection->message.length + len + 2 > connection->buffer_length) 60 | return -1; 61 | 62 | connection->buffer[connection->message.length++] = len >> 8; 63 | connection->buffer[connection->message.length++] = len & 0xff; 64 | memcpy(connection->buffer + connection->message.length, string, len); 65 | connection->message.length += len; 66 | 67 | return len + 2; 68 | } 69 | 70 | static uint16_t append_message_id(mqtt_connection_t* connection, uint16_t message_id) 71 | { 72 | // If message_id is zero then we should assign one, otherwise 73 | // we'll use the one supplied by the caller 74 | while(message_id == 0) 75 | message_id = ++connection->message_id; 76 | 77 | if(connection->message.length + 2 > connection->buffer_length) 78 | return 0; 79 | 80 | connection->buffer[connection->message.length++] = message_id >> 8; 81 | connection->buffer[connection->message.length++] = message_id & 0xff; 82 | 83 | return message_id; 84 | } 85 | 86 | static int init_message(mqtt_connection_t* connection) 87 | { 88 | connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; 89 | return MQTT_MAX_FIXED_HEADER_SIZE; 90 | } 91 | 92 | static mqtt_message_t* fail_message(mqtt_connection_t* connection) 93 | { 94 | connection->message.data = connection->buffer; 95 | connection->message.length = 0; 96 | return &connection->message; 97 | } 98 | 99 | static mqtt_message_t* fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) 100 | { 101 | int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; 102 | 103 | if(remaining_length > 127) 104 | { 105 | connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); 106 | connection->buffer[1] = 0x80 | (remaining_length % 128); 107 | connection->buffer[2] = remaining_length / 128; 108 | connection->message.length = remaining_length + 3; 109 | connection->message.data = connection->buffer; 110 | } 111 | else 112 | { 113 | connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); 114 | connection->buffer[2] = remaining_length; 115 | connection->message.length = remaining_length + 2; 116 | connection->message.data = connection->buffer + 1; 117 | } 118 | 119 | return &connection->message; 120 | } 121 | 122 | void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) 123 | { 124 | memset(connection, 0, sizeof(connection)); 125 | connection->buffer = buffer; 126 | connection->buffer_length = buffer_length; 127 | } 128 | 129 | int mqtt_get_total_length(uint8_t* buffer, uint16_t length) 130 | { 131 | int i; 132 | int totlen = 0; 133 | 134 | for(i = 1; i < length; ++i) 135 | { 136 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 137 | if((buffer[i] & 0x80) == 0) 138 | { 139 | ++i; 140 | break; 141 | } 142 | } 143 | totlen += i; 144 | 145 | return totlen; 146 | } 147 | 148 | const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) 149 | { 150 | int i; 151 | int totlen = 0; 152 | int topiclen; 153 | 154 | for(i = 1; i < *length; ++i) 155 | { 156 | totlen += (buffer[i] & 0x7f) << (7 * (i -1)); 157 | if((buffer[i] & 0x80) == 0) 158 | { 159 | ++i; 160 | break; 161 | } 162 | } 163 | totlen += i; 164 | 165 | if(i + 2 >= *length) 166 | return NULL; 167 | topiclen = buffer[i++] << 8; 168 | topiclen |= buffer[i++]; 169 | 170 | if(i + topiclen > *length) 171 | return NULL; 172 | 173 | *length = topiclen; 174 | return (const char*)(buffer + i); 175 | } 176 | 177 | const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) 178 | { 179 | int i; 180 | int totlen = 0; 181 | int topiclen; 182 | 183 | for(i = 1; i < *length; ++i) 184 | { 185 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 186 | if((buffer[i] & 0x80) == 0) 187 | { 188 | ++i; 189 | break; 190 | } 191 | } 192 | totlen += i; 193 | 194 | if(i + 2 >= *length) 195 | return NULL; 196 | topiclen = buffer[i++] << 8; 197 | topiclen |= buffer[i++]; 198 | 199 | if(i + topiclen >= *length){ 200 | *length = 0; 201 | return NULL; 202 | } 203 | i += topiclen; 204 | 205 | if(mqtt_get_qos(buffer) > 0) 206 | { 207 | if(i + 2 >= *length) 208 | return NULL; 209 | i += 2; 210 | } 211 | 212 | if(totlen < i) 213 | return NULL; 214 | 215 | if(totlen <= *length) 216 | *length = totlen - i; 217 | else 218 | *length = *length - i; 219 | return (const char*)(buffer + i); 220 | } 221 | 222 | uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length) 223 | { 224 | if(length < 1) 225 | return 0; 226 | 227 | switch(mqtt_get_type(buffer)) 228 | { 229 | case MQTT_MSG_TYPE_PUBLISH: 230 | { 231 | int i; 232 | int topiclen; 233 | 234 | for(i = 1; i < length; ++i) 235 | { 236 | if((buffer[i] & 0x80) == 0) 237 | { 238 | ++i; 239 | break; 240 | } 241 | } 242 | 243 | if(i + 2 >= length) 244 | return 0; 245 | topiclen = buffer[i++] << 8; 246 | topiclen |= buffer[i++]; 247 | 248 | if(i + topiclen >= length) 249 | return 0; 250 | i += topiclen; 251 | 252 | if(mqtt_get_qos(buffer) > 0) 253 | { 254 | if(i + 2 >= length) 255 | return 0; 256 | //i += 2; 257 | } else { 258 | return 0; 259 | } 260 | 261 | return (buffer[i] << 8) | buffer[i + 1]; 262 | } 263 | case MQTT_MSG_TYPE_PUBACK: 264 | case MQTT_MSG_TYPE_PUBREC: 265 | case MQTT_MSG_TYPE_PUBREL: 266 | case MQTT_MSG_TYPE_PUBCOMP: 267 | case MQTT_MSG_TYPE_SUBACK: 268 | case MQTT_MSG_TYPE_UNSUBACK: 269 | case MQTT_MSG_TYPE_SUBSCRIBE: 270 | { 271 | // This requires the remaining length to be encoded in 1 byte, 272 | // which it should be. 273 | if(length >= 4 && (buffer[1] & 0x80) == 0) 274 | return (buffer[2] << 8) | buffer[3]; 275 | else 276 | return 0; 277 | } 278 | 279 | default: 280 | return 0; 281 | } 282 | } 283 | 284 | mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) 285 | { 286 | struct mqtt_connect_variable_header* variable_header; 287 | 288 | init_message(connection); 289 | 290 | if(connection->message.length + sizeof(*variable_header) > connection->buffer_length) 291 | return fail_message(connection); 292 | variable_header = (void*)(connection->buffer + connection->message.length); 293 | connection->message.length += sizeof(*variable_header); 294 | 295 | variable_header->lengthMsb = 0; 296 | variable_header->lengthLsb = 6; 297 | memcpy(variable_header->magic, "MQIsdp", 6); 298 | variable_header->version = 3; 299 | variable_header->flags = 0; 300 | variable_header->keepaliveMsb = info->keepalive >> 8; 301 | variable_header->keepaliveLsb = info->keepalive & 0xff; 302 | 303 | if(info->clean_session) 304 | variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; 305 | 306 | if(info->client_id != NULL && info->client_id[0] != '\0') 307 | { 308 | if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) 309 | return fail_message(connection); 310 | } 311 | else 312 | return fail_message(connection); 313 | 314 | if(info->will_topic != NULL && info->will_topic[0] != '\0') 315 | { 316 | if(append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) 317 | return fail_message(connection); 318 | 319 | if(append_string(connection, info->will_message, strlen(info->will_message)) < 0) 320 | return fail_message(connection); 321 | 322 | variable_header->flags |= MQTT_CONNECT_FLAG_WILL; 323 | if(info->will_retain) 324 | variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; 325 | variable_header->flags |= (info->will_qos & 3) << 4; 326 | } 327 | 328 | if(info->username != NULL && info->username[0] != '\0') 329 | { 330 | if(append_string(connection, info->username, strlen(info->username)) < 0) 331 | return fail_message(connection); 332 | 333 | variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; 334 | } 335 | 336 | if(info->password != NULL && info->password[0] != '\0') 337 | { 338 | if(append_string(connection, info->password, strlen(info->password)) < 0) 339 | return fail_message(connection); 340 | 341 | variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; 342 | } 343 | 344 | return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); 345 | } 346 | 347 | mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) 348 | { 349 | init_message(connection); 350 | 351 | if(topic == NULL || topic[0] == '\0') 352 | return fail_message(connection); 353 | 354 | if(append_string(connection, topic, strlen(topic)) < 0) 355 | return fail_message(connection); 356 | 357 | if(qos > 0) 358 | { 359 | if((*message_id = append_message_id(connection, 0)) == 0) 360 | return fail_message(connection); 361 | } 362 | else 363 | *message_id = 0; 364 | 365 | if(connection->message.length + data_length > connection->buffer_length) 366 | return fail_message(connection); 367 | memcpy(connection->buffer + connection->message.length, data, data_length); 368 | connection->message.length += data_length; 369 | 370 | return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); 371 | } 372 | 373 | mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) 374 | { 375 | init_message(connection); 376 | if(append_message_id(connection, message_id) == 0) 377 | return fail_message(connection); 378 | return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); 379 | } 380 | 381 | mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) 382 | { 383 | init_message(connection); 384 | if(append_message_id(connection, message_id) == 0) 385 | return fail_message(connection); 386 | return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); 387 | } 388 | 389 | mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) 390 | { 391 | init_message(connection); 392 | if(append_message_id(connection, message_id) == 0) 393 | return fail_message(connection); 394 | return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); 395 | } 396 | 397 | mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) 398 | { 399 | init_message(connection); 400 | if(append_message_id(connection, message_id) == 0) 401 | return fail_message(connection); 402 | return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); 403 | } 404 | 405 | mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) 406 | { 407 | init_message(connection); 408 | 409 | if(topic == NULL || topic[0] == '\0') 410 | return fail_message(connection); 411 | 412 | if((*message_id = append_message_id(connection, 0)) == 0) 413 | return fail_message(connection); 414 | 415 | if(append_string(connection, topic, strlen(topic)) < 0) 416 | return fail_message(connection); 417 | 418 | if(connection->message.length + 1 > connection->buffer_length) 419 | return fail_message(connection); 420 | connection->buffer[connection->message.length++] = qos; 421 | 422 | return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); 423 | } 424 | 425 | mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) 426 | { 427 | init_message(connection); 428 | 429 | if(topic == NULL || topic[0] == '\0') 430 | return fail_message(connection); 431 | 432 | if((*message_id = append_message_id(connection, 0)) == 0) 433 | return fail_message(connection); 434 | 435 | if(append_string(connection, topic, strlen(topic)) < 0) 436 | return fail_message(connection); 437 | 438 | return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); 439 | } 440 | 441 | mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection) 442 | { 443 | init_message(connection); 444 | return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); 445 | } 446 | 447 | mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection) 448 | { 449 | init_message(connection); 450 | return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); 451 | } 452 | 453 | mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection) 454 | { 455 | init_message(connection); 456 | return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); 457 | } 458 | -------------------------------------------------------------------------------- /mqtt/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 mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } 101 | static inline int mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } 102 | static inline int mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } 103 | static inline int mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } 104 | 105 | void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); 106 | int mqtt_get_total_length(uint8_t* buffer, uint16_t length); 107 | const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); 108 | const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); 109 | uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length); 110 | 111 | mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); 112 | mqtt_message_t* 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* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); 114 | mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); 115 | mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); 116 | mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); 117 | mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); 118 | mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); 119 | mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection); 120 | mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection); 121 | mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection); 122 | 123 | 124 | #ifdef __cplusplus 125 | } 126 | #endif 127 | 128 | #endif /* MQTT_MSG_H */ 129 | 130 | -------------------------------------------------------------------------------- /mqtt/proto.c: -------------------------------------------------------------------------------- 1 | #include "proto.h" 2 | #include "ringbuf.h" 3 | I8 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 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 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 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 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 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/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 PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize); 26 | I8 PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len); 27 | I16 PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize); 28 | I16 PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len); 29 | I8 PROTO_ParseByte(PROTO_PARSER *parser, U8 value); 30 | I16 PROTO_ParseRb(RINGBUF *rb, U8 *bufOut, U16* len, U16 maxBufLen); 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /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 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 QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len) 43 | { 44 | return PROTO_AddRb(&queue->rb, buffer, len); 45 | } 46 | int32_t 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 QUEUE_IsEmpty(QUEUE *queue) 53 | { 54 | if(queue->rb.fill_cnt<=0) 55 | return TRUE; 56 | return FALSE; 57 | } 58 | -------------------------------------------------------------------------------- /mqtt/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 QUEUE_Init(QUEUE *queue, int bufferSize); 41 | int32_t QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len); 42 | int32_t QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); 43 | BOOL QUEUE_IsEmpty(QUEUE *queue); 44 | #endif /* USER_QUEUE_H_ */ 45 | -------------------------------------------------------------------------------- /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 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 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 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/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 RINGBUF_Init(RINGBUF *r, U8* buf, I32 size); 17 | I16 RINGBUF_Put(RINGBUF *r, U8 c); 18 | I16 RINGBUF_Get(RINGBUF *r, U8* c); 19 | #endif 20 | -------------------------------------------------------------------------------- /mqtt/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/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 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 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 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 | -------------------------------------------------------------------------------- /mqtt/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H_ 2 | #define _UTILS_H_ 3 | 4 | #include "c_types.h" 5 | 6 | uint32_t UTILS_Atoh(const int8_t *s); 7 | uint8_t UTILS_StrToIP(const int8_t* str, void *ip); 8 | uint8_t UTILS_IsIPV4 (int8_t *str); 9 | char *ULTILS_StrReplace(char *orig, char *rep, char *with); 10 | #endif 11 | -------------------------------------------------------------------------------- /mqtt/wifi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * wifi.c 3 | * 4 | * Created on: Dec 30, 2014 5 | * Author: Minh 6 | */ 7 | #include "wifi.h" 8 | #include "user_interface.h" 9 | #include "osapi.h" 10 | #include "espconn.h" 11 | #include "os_type.h" 12 | #include "mem.h" 13 | #include "mqtt_msg.h" 14 | #include "debug.h" 15 | #include "user_config.h" 16 | #include "config.h" 17 | 18 | static ETSTimer WiFiLinker; 19 | WifiCallback wifiCb = NULL; 20 | static uint8_t wifiStatus = STATION_IDLE, lastWifiStatus = STATION_IDLE; 21 | static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg) 22 | { 23 | struct ip_info ipConfig; 24 | 25 | os_timer_disarm(&WiFiLinker); 26 | wifi_get_ip_info(STATION_IF, &ipConfig); 27 | wifiStatus = wifi_station_get_connect_status(); 28 | if (wifiStatus == STATION_GOT_IP && ipConfig.ip.addr != 0) 29 | { 30 | 31 | os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); 32 | os_timer_arm(&WiFiLinker, 2000, 0); 33 | 34 | 35 | } 36 | else 37 | { 38 | if(wifi_station_get_connect_status() == STATION_WRONG_PASSWORD) 39 | { 40 | 41 | INFO("STATION_WRONG_PASSWORD\r\n"); 42 | wifi_station_connect(); 43 | 44 | 45 | } 46 | else if(wifi_station_get_connect_status() == STATION_NO_AP_FOUND) 47 | { 48 | 49 | INFO("STATION_NO_AP_FOUND\r\n"); 50 | wifi_station_connect(); 51 | 52 | 53 | } 54 | else if(wifi_station_get_connect_status() == STATION_CONNECT_FAIL) 55 | { 56 | 57 | INFO("STATION_CONNECT_FAIL\r\n"); 58 | wifi_station_connect(); 59 | 60 | } 61 | else 62 | { 63 | INFO("STATION_IDLE\r\n"); 64 | } 65 | 66 | os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); 67 | os_timer_arm(&WiFiLinker, 500, 0); 68 | } 69 | if(wifiStatus != lastWifiStatus){ 70 | lastWifiStatus = wifiStatus; 71 | if(wifiCb) 72 | wifiCb(wifiStatus); 73 | } 74 | } 75 | 76 | void WIFI_Connect(uint8_t* ssid, uint8_t* pass, WifiCallback cb) 77 | { 78 | struct station_config stationConf; 79 | 80 | INFO("WIFI_INIT\r\n"); 81 | wifi_set_opmode(STATION_MODE); 82 | wifi_station_set_auto_connect(FALSE); 83 | wifiCb = cb; 84 | 85 | os_memset(&stationConf, 0, sizeof(struct station_config)); 86 | 87 | os_sprintf(stationConf.ssid, "%s", ssid); 88 | os_sprintf(stationConf.password, "%s", pass); 89 | 90 | wifi_station_set_config(&stationConf); 91 | 92 | os_timer_disarm(&WiFiLinker); 93 | os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); 94 | os_timer_arm(&WiFiLinker, 1000, 0); 95 | 96 | wifi_station_set_auto_connect(TRUE); 97 | wifi_station_connect(); 98 | } 99 | 100 | -------------------------------------------------------------------------------- /mqtt/wifi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * wifi.h 3 | * 4 | * Created on: Dec 30, 2014 5 | * Author: Minh 6 | */ 7 | 8 | #ifndef USER_WIFI_H_ 9 | #define USER_WIFI_H_ 10 | #include "os_type.h" 11 | typedef void (*WifiCallback)(uint8_t); 12 | void WIFI_Connect(uint8_t* ssid, uint8_t* pass, WifiCallback cb); 13 | 14 | 15 | #endif /* USER_WIFI_H_ */ 16 | -------------------------------------------------------------------------------- /user/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ############################################################# 3 | # Required variables for each makefile 4 | # Discard this section from all parent makefiles 5 | # Expected variables (with automatic defaults): 6 | # CSRCS (all "C" files in the dir) 7 | # SUBDIRS (all subdirs with a Makefile) 8 | # GEN_LIBS - list of libs to be generated () 9 | # GEN_IMAGES - list of images to be generated () 10 | # COMPONENTS_xxx - a list of libs/objs in the form 11 | # subdir/lib to be extracted and rolled up into 12 | # a generated lib/image xxx.a () 13 | # 14 | ifndef PDIR 15 | GEN_LIBS = libuser.a 16 | endif 17 | 18 | 19 | ############################################################# 20 | # Configuration i.e. compile options etc. 21 | # Target specific stuff (defines etc.) goes in here! 22 | # Generally values applying to a tree are captured in the 23 | # makefile at its root level - these are then overridden 24 | # for a subtree within the makefile rooted therein 25 | # 26 | #DEFINES += 27 | 28 | ############################################################# 29 | # Recursion Magic - Don't touch this!! 30 | # 31 | # Each subtree potentially has an include directory 32 | # corresponding to the common APIs applicable to modules 33 | # rooted at that subtree. Accordingly, the INCLUDE PATH 34 | # of a module can only contain the include directories up 35 | # its parent path, and not its siblings 36 | # 37 | # Required for each makefile to inherit from the parent 38 | # 39 | 40 | INCLUDES := $(INCLUDES) -I $(PDIR)include 41 | INCLUDES += -I ./ 42 | INCLUDES += -I ../../rom/include 43 | INCLUDES += -I ../../include/ets 44 | PDIR := ../$(PDIR) 45 | sinclude $(PDIR)Makefile 46 | 47 | -------------------------------------------------------------------------------- /user/httpclient.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ---------------------------------------------------------------------------- 3 | * "THE BEER-WARE LICENSE" (Revision 42): 4 | * Martin d'Allens wrote this file. As long as you retain 5 | * this notice you can do whatever you want with this stuff. If we meet some day, 6 | * and you think this stuff is worth it, you can buy me a beer in return. 7 | * ---------------------------------------------------------------------------- 8 | */ 9 | 10 | // FIXME: sprintf->snprintf everywhere. 11 | // FIXME: support null characters in responses. 12 | 13 | #include "osapi.h" 14 | #include "user_interface.h" 15 | #include "espconn.h" 16 | #include "mem.h" 17 | #include "httpclient.h" 18 | #include "debug.h" 19 | 20 | 21 | // Debug output. 22 | #if 0 23 | #define PRINTF(...) os_printf(__VA_ARGS__) 24 | #else 25 | #define PRINTF(...) 26 | #endif 27 | 28 | // Internal state. 29 | typedef struct { 30 | char * path; 31 | int port; 32 | char * post_data; 33 | char * hostname; 34 | char * buffer; 35 | int buffer_size; 36 | http_callback user_callback; 37 | } request_args; 38 | 39 | static char * esp_strdup(const char * str) 40 | { 41 | if (str == NULL) { 42 | return NULL; 43 | } 44 | char * new_str = (char *)os_malloc(os_strlen(str) + 1); // 1 for null character 45 | if (new_str == NULL) { 46 | os_printf("esp_strdup: malloc error"); 47 | return NULL; 48 | } 49 | os_strcpy(new_str, str); 50 | return new_str; 51 | } 52 | 53 | static void ICACHE_FLASH_ATTR receive_callback(void * arg, char * buf, unsigned short len) 54 | { 55 | struct espconn * conn = (struct espconn *)arg; 56 | request_args * req = (request_args *)conn->reverse; 57 | 58 | if (req->buffer == NULL) { 59 | return; 60 | } 61 | 62 | // Let's do the equivalent of a realloc(). 63 | const int new_size = req->buffer_size + len; 64 | char * new_buffer; 65 | if (new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *)os_malloc(new_size))) { 66 | os_printf("Response too long %d\n", new_size); 67 | os_free(req->buffer); 68 | req->buffer = NULL; 69 | // TODO: espconn_disconnect(conn) without crashing. 70 | return; 71 | } 72 | 73 | os_memcpy(new_buffer, req->buffer, req->buffer_size); 74 | os_memcpy(new_buffer + req->buffer_size - 1 /*overwrite the null character*/, buf, len); // Append new data. 75 | new_buffer[new_size - 1] = '\0'; // Make sure there is an end of string. 76 | 77 | os_free(req->buffer); 78 | req->buffer = new_buffer; 79 | req->buffer_size = new_size; 80 | } 81 | 82 | static void ICACHE_FLASH_ATTR sent_callback(void * arg) 83 | { 84 | struct espconn * conn = (struct espconn *)arg; 85 | request_args * req = (request_args *)conn->reverse; 86 | 87 | if (req->post_data == NULL) { 88 | PRINTF("All sent\n"); 89 | } 90 | else { 91 | // The headers were sent, now send the contents. 92 | PRINTF("Sending request body\n"); 93 | espconn_sent(conn, (uint8_t *)req->post_data, strlen(req->post_data)); 94 | os_free(req->post_data); 95 | req->post_data = NULL; 96 | } 97 | } 98 | 99 | static void ICACHE_FLASH_ATTR connect_callback(void * arg) 100 | { 101 | PRINTF("Connected\n"); 102 | struct espconn * conn = (struct espconn *)arg; 103 | request_args * req = (request_args *)conn->reverse; 104 | 105 | espconn_regist_recvcb(conn, receive_callback); 106 | espconn_regist_sentcb(conn, sent_callback); 107 | 108 | const char * method = "GET"; 109 | char post_headers[128] = ""; 110 | 111 | if (req->post_data != NULL) { // If there is data this is a POST request. 112 | method = "POST"; 113 | os_sprintf(post_headers, 114 | "Content-Type: application/x-www-form-urlencoded\r\n" 115 | "Content-Length: %d\r\n", strlen(req->post_data)); 116 | } 117 | 118 | char buf[2048]; 119 | int len = os_sprintf(buf, 120 | "%s %s HTTP/1.1\r\n" 121 | "Host: %s:%d\r\n" 122 | "Connection: close\r\n" 123 | "User-Agent: ESP8266\r\n" 124 | "%s" 125 | "\r\n", 126 | method, req->path, req->hostname, req->port, post_headers); 127 | 128 | espconn_sent(conn, (uint8_t *)buf, len); 129 | PRINTF("Sending request header\n"); 130 | } 131 | 132 | static void ICACHE_FLASH_ATTR disconnect_callback(void * arg) 133 | { 134 | PRINTF("Disconnected\n"); 135 | struct espconn *conn = (struct espconn *)arg; 136 | 137 | if(conn == NULL) { 138 | return; 139 | } 140 | 141 | if(conn->proto.tcp != NULL) { 142 | os_free(conn->proto.tcp); 143 | } 144 | if(conn->reverse != NULL) { 145 | request_args * req = (request_args *)conn->reverse; 146 | if (req->buffer != NULL) { 147 | // FIXME: make sure this is not a partial response, using the Content-Length header. 148 | 149 | const char * version = "HTTP/1.1 "; 150 | if (os_strncmp(req->buffer, version, strlen(version)) != 0) { 151 | os_printf("Invalid version in %s\n", req->buffer); 152 | return; 153 | } 154 | int http_status = atoi(req->buffer + strlen(version)); 155 | 156 | char * body = (char *)os_strstr(req->buffer, "\r\n\r\n") + 4; 157 | 158 | if (req->user_callback != NULL) { // Callback is optional. 159 | req->user_callback(body, http_status, req->buffer); 160 | } 161 | os_free(req->buffer); 162 | } 163 | os_free(req->hostname); 164 | os_free(req->path); 165 | os_free(req); 166 | } 167 | os_free(conn); 168 | } 169 | 170 | static void ICACHE_FLASH_ATTR dns_callback(const char * hostname, ip_addr_t * addr, void * arg) 171 | { 172 | request_args * req = (request_args *)arg; 173 | 174 | if (addr == NULL) { 175 | os_printf("DNS failed %s\n", hostname); 176 | } 177 | else { 178 | PRINTF("DNS found %s " IPSTR "\n", hostname, IP2STR(addr)); 179 | 180 | struct espconn * conn = (struct espconn *)os_malloc(sizeof(struct espconn)); 181 | conn->type = ESPCONN_TCP; 182 | conn->state = ESPCONN_NONE; 183 | conn->proto.tcp = (esp_tcp *)os_malloc(sizeof(esp_tcp)); 184 | conn->proto.tcp->local_port = espconn_port(); 185 | conn->proto.tcp->remote_port = req->port; 186 | conn->reverse = req; 187 | 188 | os_memcpy(conn->proto.tcp->remote_ip, addr, 4); 189 | 190 | espconn_regist_connectcb(conn, connect_callback); 191 | espconn_regist_disconcb(conn, disconnect_callback); 192 | 193 | // TODO: consider using espconn_regist_reconcb (for timeouts?) 194 | // cf esp8266_sdk_v0.9.1/examples/at/user/at_ipCmd.c (TCP ARQ retransmission?) 195 | 196 | espconn_connect(conn); 197 | } 198 | } 199 | 200 | void ICACHE_FLASH_ATTR http_raw_request(const char * hostname, int port, const char * path, const char * post_data, http_callback user_callback) 201 | { 202 | PRINTF("DNS request\n"); 203 | 204 | request_args * req = (request_args *)os_malloc(sizeof(request_args)); 205 | req->hostname = esp_strdup(hostname); 206 | req->path = esp_strdup(path); 207 | req->port = port; 208 | req->post_data = esp_strdup(post_data); 209 | req->buffer_size = 1; 210 | req->buffer = (char *)os_malloc(1); 211 | req->buffer[0] = '\0'; // Empty string. 212 | req->user_callback = user_callback; 213 | 214 | ip_addr_t addr; 215 | err_t error = espconn_gethostbyname((struct espconn *)req, // It seems we don't need a real espconn pointer here. 216 | hostname, &addr, dns_callback); 217 | 218 | if (error == ESPCONN_INPROGRESS) { 219 | PRINTF("DNS pending\n"); 220 | } 221 | else if (error == ESPCONN_OK) { 222 | // Already in the local names table (or hostname was an IP address), execute the callback ourselves. 223 | dns_callback(hostname, &addr, req); 224 | } 225 | else if (error == ESPCONN_ARG) { 226 | os_printf("DNS error %s\n", hostname); 227 | } 228 | else { 229 | os_printf("DNS error code %d\n", error); 230 | } 231 | } 232 | 233 | /* 234 | * Parse an URL of the form http://host:port/path 235 | * can be a hostname or an IP address 236 | * is optional 237 | */ 238 | void ICACHE_FLASH_ATTR http_post(const char * url, const char * post_data, http_callback user_callback) 239 | { 240 | // FIXME: handle HTTP auth with http://user:pass@host/ 241 | // FIXME: make https work. 242 | // FIXME: get rid of the #anchor part if present. 243 | 244 | char hostname[128] = ""; 245 | int port = 80; 246 | 247 | if (os_strncmp(url, "http://", strlen("http://")) != 0) { 248 | os_printf("URL is not HTTP %s\n", url); 249 | return; 250 | } 251 | url += strlen("http://"); // Get rid of the protocol. 252 | 253 | char * path = os_strchr(url, '/'); 254 | if (path == NULL) { 255 | path = os_strchr(url, '\0'); // Pointer to end of string. 256 | } 257 | 258 | char * colon = os_strchr(url, ':'); 259 | if (colon > path) { 260 | colon = NULL; // Limit the search to characters before the path. 261 | } 262 | 263 | if (colon == NULL) { // The port is not present. 264 | os_memcpy(hostname, url, path - url); 265 | hostname[path - url] = '\0'; 266 | } 267 | else { 268 | port = atoi(colon + 1); 269 | if (port == 0) { 270 | os_printf("Port error %s\n", url); 271 | return; 272 | } 273 | 274 | os_memcpy(hostname, url, colon - url); 275 | hostname[colon - url] = '\0'; 276 | } 277 | 278 | 279 | if (path[0] == '\0') { // Empty path is not allowed. 280 | path = "/"; 281 | } 282 | 283 | PRINTF("hostname=%s\n", hostname); 284 | PRINTF("port=%d\n", port); 285 | PRINTF("path=%s\n", path); 286 | http_raw_request(hostname, port, path, post_data, user_callback); 287 | } 288 | 289 | void ICACHE_FLASH_ATTR http_get(const char * url, http_callback user_callback) 290 | { 291 | http_post(url, NULL, user_callback); 292 | } 293 | 294 | void http_callback_example(char * response, int http_status, char * full_response) 295 | { 296 | INFO("In http_callback_example\n"); 297 | os_printf("http_status=%d\n", http_status); 298 | os_printf("strlen(response)=%d\n", strlen(response)); 299 | os_printf("strlen(full_response)=%d\n", strlen(full_response)); 300 | os_printf("response=%s\n(end)\n", response); 301 | } 302 | 303 | void http_test() 304 | { 305 | /* Test cases: 306 | http_get("https://google.com"); // Should fail. 307 | http_get("http://google.com/search?q=1"); 308 | http_get("http://google.com"); 309 | http_get("http://portquiz.net:8080/"); 310 | http_raw_request("google.com", 80, "/search?q=2", NULL); 311 | http_get("http://173.194.45.65"); // Fails if not online yet. FIXME: we should wait for DHCP to have finished before connecting. 312 | */ 313 | 314 | http_get("http://wtfismyip.com/text", http_callback_example); 315 | http_post("http://httpbin.org/post", "first_word=hello&second_word=world", http_callback_example); 316 | } 317 | -------------------------------------------------------------------------------- /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 "driver/uart.h" 32 | #include "driver/dht22.h" 33 | #include "osapi.h" 34 | #include "mqtt.h" 35 | #include "wifi.h" 36 | #include "config.h" 37 | #include "debug.h" 38 | #include "gpio.h" 39 | #include "user_interface.h" 40 | #include "httpclient.h" 41 | 42 | MQTT_Client mqttClient; 43 | 44 | #define DELAY 30000 /* milliseconds */ 45 | 46 | LOCAL os_timer_t timer; 47 | LOCAL int relay_state = 0; 48 | 49 | 50 | #define RELAY_GPIO 0 51 | #define RELAY_GPIO_MUX PERIPHS_IO_MUX_GPIO0_U 52 | #define RELAY_GPIO_FUNC FUNC_GPIO0 53 | 54 | // *** CHANGE CONFIG HERE *** 55 | LOCAL char YOUR_THINGSPEAK_API_KEY[]= "xxxxxxxxxxxxxxxxx"; 56 | LOCAL char YOUR_THINGSPEAK_CHANNEL[]= "xxxxxxxxx"; 57 | LOCAL bool bRelayInputInverted = true; 58 | 59 | 60 | LOCAL int timer_count = 0; 61 | 62 | // 63 | // This function checks the response for last field1 value of Thingspeak 64 | // channel. If there is a change in the value, send appropriate MQTT 65 | // message to /esp8266/relay topic to turn on/off relay. 66 | // 67 | void http_get_relay_state_callback(char * response, int http_status, char * full_response) 68 | { 69 | char str[64]; 70 | INFO("In http_get_relay_state_callback... http_status=%d\n", http_status); 71 | // os_printf("strlen(response)=%d\n", strlen(response)); 72 | // os_printf("strlen(full_response)=%d\n", strlen(full_response)); 73 | os_printf("response===%s===\n", response); 74 | 75 | if (ets_strstr(response,"-1.0")) 76 | { 77 | if ((relay_state == 0) || (relay_state == 1)) 78 | { 79 | relay_state = -1; 80 | INFO("Sending off to /esp8266/relay...\n"); 81 | os_sprintf(str,"off"); 82 | MQTT_Publish(&mqttClient, "/esp8266/relay", str, os_strlen(str), 0, 0); 83 | } 84 | } 85 | else if (ets_strstr(response,"1.0")) 86 | { 87 | if ((relay_state == 0) || (relay_state == -1)) 88 | { 89 | relay_state = 1; 90 | INFO("Sending on to /esp8266/relay...\n"); 91 | os_sprintf(str,"on"); 92 | MQTT_Publish(&mqttClient, "/esp8266/relay", str, os_strlen(str), 0, 0); 93 | } 94 | } 95 | } 96 | 97 | void http_post_callback(char * response, int http_status, char * full_response) 98 | { 99 | INFO("In http_post_callback... http_status=%d\n", http_status); 100 | // os_printf("strlen(response)=%d\n", strlen(response)); 101 | // os_printf("strlen(full_response)=%d\n", strlen(full_response)); 102 | os_printf("response===%s===\n", response); 103 | } 104 | 105 | // 106 | // This function gets called every DELAY milliseconds. On the 1st, 3rd, 5th, 7th and 9th 107 | // invocations, it will check the relay state from Thingspeak. That is it does this every 108 | // minute if DELAY is 30000 millseconds. On the 10th invocation, which is every 5 minutes, 109 | // it will send temperature and humidity data to MQTT and Thingspeak. 110 | // 111 | LOCAL void ICACHE_FLASH_ATTR timer_callback(void *arg) 112 | { 113 | // send_data_to_mqtt_and_thingspeak(); 114 | struct dht_sensor_data* r = DHTRead(); 115 | float lastTemp = r->temperature; 116 | float lastHum = r->humidity; 117 | char str_temp[64],str_hum[64]; 118 | char str_url[256]; 119 | 120 | if ((timer_count==1) || (timer_count==3) || (timer_count==5) || (timer_count==7) || (timer_count==9)) 121 | { 122 | // Get last field1 value of Thingspeak channel that corresponds to the relay state 123 | os_sprintf(str_url,"http://api.thingspeak.com/channels/%s/fields/field1/last",YOUR_THINGSPEAK_CHANNEL); 124 | http_get(str_url, http_get_relay_state_callback); 125 | 126 | } 127 | else if (timer_count==10) 128 | { 129 | if(r->success) 130 | { 131 | // Send temperature and humidity data to MQTT broker 132 | INFO("Temperature: %d.%d *C, Humidity: %d.%d %%\r\n", (int)(lastTemp),(int)((lastTemp - (int)lastTemp)*100), (int)(lastHum),(int)((lastHum - (int)lastHum)*100)); 133 | os_sprintf(str_temp,"%d.%d", (int)(lastTemp),(int)((lastTemp - (int)lastTemp)*100)); 134 | MQTT_Publish(&mqttClient, "/esp8266/temperature", str_temp, os_strlen(str_temp), 0, 0); 135 | 136 | os_sprintf(str_hum,"%d.%d", (int)(lastHum),(int)((lastHum - (int)lastHum)*100)); 137 | MQTT_Publish(&mqttClient, "/esp8266/humidity", str_hum, os_strlen(str_hum), 0, 0); 138 | 139 | // Send temperature and humidity data to Thingspeak.com 140 | os_sprintf(str_url,"http://api.thingspeak.com/update?key=%s&field2=%s&field3=%s",YOUR_THINGSPEAK_API_KEY,str_temp,str_hum); 141 | http_post(str_url,"",http_post_callback); 142 | } 143 | else 144 | { 145 | INFO("Error reading temperature and humidity\r\n"); 146 | } 147 | timer_count = 1; 148 | } 149 | 150 | timer_count++; 151 | } 152 | 153 | void wifiConnectCb(uint8_t status) 154 | { 155 | if(status == STATION_GOT_IP){ 156 | MQTT_Connect(&mqttClient); 157 | } 158 | } 159 | 160 | void mqttConnectedCb(uint32_t *args) 161 | { 162 | MQTT_Client* client = (MQTT_Client*)args; 163 | INFO("MQTT: Connected\r\n"); 164 | MQTT_Subscribe(&mqttClient, "/esp8266/temperature", 2); 165 | MQTT_Subscribe(&mqttClient, "/esp8266/humidity", 2); 166 | MQTT_Subscribe(&mqttClient, "/esp8266/relay", 2); 167 | } 168 | 169 | void mqttDisconnectedCb(uint32_t *args) 170 | { 171 | MQTT_Client* client = (MQTT_Client*)args; 172 | INFO("MQTT: Disconnected\r\n"); 173 | } 174 | 175 | void mqttPublishedCb(uint32_t *args) 176 | { 177 | MQTT_Client* client = (MQTT_Client*)args; 178 | INFO("MQTT: Published\r\n"); 179 | } 180 | 181 | // 182 | // This function is called whenever a message is received on any of the subscribed topics 183 | // In our case, only messages on the 'relay' topic are processed and commands sent to 184 | // the GPIO0 pin to turn on/off the relay. 185 | // 186 | void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) 187 | { 188 | char topicBuf[64], dataBuf[64], str_url[256]; 189 | MQTT_Client* client = (MQTT_Client*)args; 190 | 191 | os_memcpy(topicBuf, topic, topic_len); 192 | topicBuf[topic_len] = 0; 193 | 194 | os_memcpy(dataBuf, data, data_len); 195 | dataBuf[data_len] = 0; 196 | 197 | INFO("MQTT topic: %s, data: %s \r\n", topicBuf, dataBuf); 198 | 199 | if (os_strcmp(topicBuf,"/esp8266/relay") == 0) 200 | { 201 | if((os_strcmp(dataBuf,"on") == 0) || (os_strcmp(dataBuf,"ON") == 0)) 202 | { 203 | if(bRelayInputInverted) 204 | { 205 | GPIO_OUTPUT_SET(RELAY_GPIO, 0); 206 | } 207 | else 208 | { 209 | GPIO_OUTPUT_SET(RELAY_GPIO, 1); 210 | } 211 | relay_state = 1; 212 | 213 | // Send relay state to Thingspeak.com 214 | os_sprintf(str_url,"http://api.thingspeak.com/update?key=%s&field1=1.0", YOUR_THINGSPEAK_API_KEY); 215 | http_post(str_url,"",http_post_callback); 216 | } 217 | else if((os_strcmp(dataBuf,"off") == 0) || (os_strcmp(dataBuf,"OFF") == 0)) 218 | { 219 | if(bRelayInputInverted) 220 | { 221 | GPIO_OUTPUT_SET(RELAY_GPIO, 1); 222 | } 223 | else 224 | { 225 | GPIO_OUTPUT_SET(RELAY_GPIO, 0); 226 | } 227 | relay_state = -1; 228 | 229 | // Send relay state to Thingspeak.com 230 | os_sprintf(str_url,"http://api.thingspeak.com/update?key=%s&field1=-1.0", YOUR_THINGSPEAK_API_KEY); 231 | http_post(str_url,"",http_post_callback); 232 | } 233 | } 234 | } 235 | 236 | // 237 | // This is the 'main' function. 238 | // 239 | void user_init(void) 240 | { 241 | // Initialize GPIO0 242 | PIN_FUNC_SELECT(RELAY_GPIO_MUX, RELAY_GPIO_FUNC); 243 | GPIO_OUTPUT_SET(RELAY_GPIO, 1); 244 | 245 | // Initialize DHT11/22 sensor 246 | uart_init(BIT_RATE_115200, BIT_RATE_115200); 247 | DHTInit(DHT22, 2000); 248 | 249 | // Load the WiFi and MQTT login details from /include/user_config.h 250 | CFG_Load(); 251 | 252 | os_delay_us(3000000); 253 | 254 | // Connect to WiFi. 255 | WIFI_Connect(sysCfg.sta_ssid, sysCfg.sta_pwd, wifiConnectCb); 256 | 257 | // Initialize MQTT 258 | MQTT_InitConnection(&mqttClient, sysCfg.mqtt_host, sysCfg.mqtt_port, SEC_NONSSL); 259 | MQTT_InitClient(&mqttClient, sysCfg.device_id, sysCfg.mqtt_user, sysCfg.mqtt_pass, sysCfg.mqtt_keepalive, 1); 260 | MQTT_OnConnected(&mqttClient, mqttConnectedCb); 261 | MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); 262 | MQTT_OnPublished(&mqttClient, mqttPublishedCb); 263 | MQTT_OnData(&mqttClient, mqttDataCb); 264 | 265 | // Start a timer that calls timer_callback every DELAY milliseconds. 266 | os_timer_disarm(&timer); 267 | os_timer_setfn(&timer, (os_timer_func_t *)timer_callback, (void *)0); 268 | os_timer_arm(&timer, DELAY, 1); 269 | 270 | INFO("\r\nSystem started ...\r\n"); 271 | } 272 | --------------------------------------------------------------------------------