Merge branch 'feature/better_crash_dumps' into open-libmain
This commit is contained in:
commit
5fa17990dd
62 changed files with 5163 additions and 1355 deletions
|
@ -2,12 +2,13 @@ language: c
|
||||||
sudo: false
|
sudo: false
|
||||||
env:
|
env:
|
||||||
# Target commit for https://github.com/pfalcon/esp-open-sdk/
|
# Target commit for https://github.com/pfalcon/esp-open-sdk/
|
||||||
OPENSDK_COMMIT=cd1d336
|
OPENSDK_COMMIT=9bd3aba
|
||||||
CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}"
|
CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}"
|
||||||
CROSS_BINDIR="${CROSS_ROOT}/bin"
|
CROSS_BINDIR="${CROSS_ROOT}/bin"
|
||||||
ESPTOOL2_COMMIT=ec0e2c7
|
ESPTOOL2_COMMIT=ec0e2c7
|
||||||
ESPTOOL2_DIR="${HOME}/esptool2-${ESPTOOL2_COMMIT}"
|
ESPTOOL2_DIR="${HOME}/esptool2-${ESPTOOL2_COMMIT}"
|
||||||
PATH=${PATH}:${CROSS_BINDIR}:${ESPTOOL2_DIR}
|
PATH=${PATH}:${CROSS_BINDIR}:${ESPTOOL2_DIR}
|
||||||
|
MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples CROSS=\"ccache xtensa-lx106-elf-\""
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- ${CROSS_ROOT}
|
- ${CROSS_ROOT}
|
||||||
|
@ -57,5 +58,7 @@ before_install:
|
||||||
script:
|
script:
|
||||||
- cd ${TRAVIS_BUILD_DIR}
|
- cd ${TRAVIS_BUILD_DIR}
|
||||||
# Remove ssid_config requirement for examples
|
# Remove ssid_config requirement for examples
|
||||||
- sed -i "s%#warning%//#warning%" include/ssid_config.h
|
- sed -i "s%#error%//#error%" include/ssid_config.h
|
||||||
- make -C examples/ build-examples CROSS="ccache xtensa-lx106-elf-" V=1
|
# Try building everything w/ non-verbose output (to avoid 4MB log limit), but if it fails then
|
||||||
|
# rerun with V=1 so the error part is output verbosely
|
||||||
|
- ${MAKE_CMD} || ( ${MAKE_CMD} V=1 )
|
||||||
|
|
|
@ -178,9 +178,6 @@ void xPortSysTickHandle (void)
|
||||||
//OpenNMI();
|
//OpenNMI();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sdk_compat_initialised;
|
|
||||||
void sdk_compat_initialise(void);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See header file for description.
|
* See header file for description.
|
||||||
*/
|
*/
|
||||||
|
@ -189,14 +186,6 @@ portBASE_TYPE xPortStartScheduler( void )
|
||||||
_xt_isr_attach(INUM_SOFT, SV_ISR);
|
_xt_isr_attach(INUM_SOFT, SV_ISR);
|
||||||
_xt_isr_unmask(BIT(INUM_SOFT));
|
_xt_isr_unmask(BIT(INUM_SOFT));
|
||||||
|
|
||||||
/* ENORMOUS HACK: Call the sdk_compat_initialise() function.
|
|
||||||
This can be removed happily once we have open source startup code.
|
|
||||||
*/
|
|
||||||
if(!sdk_compat_initialised) {
|
|
||||||
sdk_compat_initialised = true;
|
|
||||||
sdk_compat_initialise();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize system tick timer interrupt and schedule the first tick. */
|
/* Initialize system tick timer interrupt and schedule the first tick. */
|
||||||
_xt_isr_attach(INUM_TICK, sdk__xt_timer_int);
|
_xt_isr_attach(INUM_TICK, sdk__xt_timer_int);
|
||||||
_xt_isr_unmask(BIT(INUM_TICK));
|
_xt_isr_unmask(BIT(INUM_TICK));
|
||||||
|
|
|
@ -18,7 +18,7 @@ Please note that this project is released with a [Contributor Code of Conduct](h
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
* Install [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk/), build it with `make STANDALONE=n`, then edit your PATH and add the generated toolchain `bin` directory. The path will be something like `/path/to/esp-open-sdk/xtensa-lx106-elf/bin`. (Despite the similar name esp-open-sdk has different maintainers - but we think it's fantastic!)
|
* Install [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk/), build it with `make toolchain esptool libhal STANDALONE=n`, then edit your PATH and add the generated toolchain `bin` directory. The path will be something like `/path/to/esp-open-sdk/xtensa-lx106-elf/bin`. (Despite the similar name esp-open-sdk has different maintainers - but we think it's fantastic!)
|
||||||
|
|
||||||
(Other toolchains may also work, as long as a gcc cross-compiler is available on the PATH and libhal (and libhal headers) are compiled and available to gcc. The proprietary Tensilica "xcc" compiler will probably not work.)
|
(Other toolchains may also work, as long as a gcc cross-compiler is available on the PATH and libhal (and libhal headers) are compiled and available to gcc. The proprietary Tensilica "xcc" compiler will probably not work.)
|
||||||
|
|
||||||
|
|
17
common.mk
17
common.mk
|
@ -75,6 +75,9 @@ FLAVOR ?= release # or debug
|
||||||
# Compiler names, etc. assume gdb
|
# Compiler names, etc. assume gdb
|
||||||
CROSS ?= xtensa-lx106-elf-
|
CROSS ?= xtensa-lx106-elf-
|
||||||
|
|
||||||
|
# Path to the filteroutput.py tool
|
||||||
|
FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
|
||||||
|
|
||||||
AR = $(CROSS)ar
|
AR = $(CROSS)ar
|
||||||
CC = $(CROSS)gcc
|
CC = $(CROSS)gcc
|
||||||
CPP = $(CROSS)cpp
|
CPP = $(CROSS)cpp
|
||||||
|
@ -106,8 +109,14 @@ ENTRY_SYMBOL ?= call_user_start
|
||||||
# but compiled code size will come down a small amount.)
|
# but compiled code size will come down a small amount.)
|
||||||
SPLIT_SECTIONS ?= 1
|
SPLIT_SECTIONS ?= 1
|
||||||
|
|
||||||
|
# Set this to 1 to have all compiler warnings treated as errors (and stop the
|
||||||
|
# build). This is recommended whenever you are working on code which will be
|
||||||
|
# submitted back to the main project, as all submitted code will be expected to
|
||||||
|
# compile without warnings to be accepted.
|
||||||
|
WARNINGS_AS_ERRORS ?= 0
|
||||||
|
|
||||||
# Common flags for both C & C++_
|
# Common flags for both C & C++_
|
||||||
C_CXX_FLAGS ?= -Wall -Werror -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
|
C_CXX_FLAGS ?= -Wall -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
|
||||||
# Flags for C only
|
# Flags for C only
|
||||||
CFLAGS ?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
|
CFLAGS ?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
|
||||||
# Flags for C++ only
|
# Flags for C++ only
|
||||||
|
@ -118,6 +127,10 @@ CPPFLAGS += -mlongcalls -mtext-section-literals
|
||||||
|
|
||||||
LDFLAGS = -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
|
LDFLAGS = -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
|
||||||
|
|
||||||
|
ifeq ($(WARNINGS_AS_ERRORS),1)
|
||||||
|
C_CXX_FLAGS += -Werror
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(SPLIT_SECTIONS),1)
|
ifeq ($(SPLIT_SECTIONS),1)
|
||||||
C_CXX_FLAGS += -ffunction-sections -fdata-sections
|
C_CXX_FLAGS += -ffunction-sections -fdata-sections
|
||||||
LDFLAGS += -Wl,-gc-sections
|
LDFLAGS += -Wl,-gc-sections
|
||||||
|
@ -375,7 +388,7 @@ size: $(PROGRAM_OUT)
|
||||||
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
|
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
|
||||||
|
|
||||||
test: flash
|
test: flash
|
||||||
screen $(ESPPORT) 115200
|
$(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT)
|
||||||
|
|
||||||
# the rebuild target is written like this so it can be run in a parallel build
|
# the rebuild target is written like this so it can be run in a parallel build
|
||||||
# environment without causing weird side effects
|
# environment without causing weird side effects
|
||||||
|
|
|
@ -41,8 +41,6 @@ void user_init(void);
|
||||||
#define RTCMEM_BACKUP_PHY_VER 31
|
#define RTCMEM_BACKUP_PHY_VER 31
|
||||||
#define RTCMEM_SYSTEM_PP_VER 62
|
#define RTCMEM_SYSTEM_PP_VER 62
|
||||||
|
|
||||||
#define halt() while (1) {}
|
|
||||||
|
|
||||||
extern uint32_t _bss_start;
|
extern uint32_t _bss_start;
|
||||||
extern uint32_t _bss_end;
|
extern uint32_t _bss_end;
|
||||||
|
|
||||||
|
@ -85,7 +83,6 @@ static void IRAM set_spi0_divisor(uint32_t divisor);
|
||||||
static void zero_bss(void);
|
static void zero_bss(void);
|
||||||
static void init_networking(uint8_t *phy_info, uint8_t *mac_addr);
|
static void init_networking(uint8_t *phy_info, uint8_t *mac_addr);
|
||||||
static void init_g_ic(void);
|
static void init_g_ic(void);
|
||||||
static void dump_excinfo(void);
|
|
||||||
static void user_start_phase2(void);
|
static void user_start_phase2(void);
|
||||||
static void dump_flash_sector(uint32_t start_sector, uint32_t length);
|
static void dump_flash_sector(uint32_t start_sector, uint32_t length);
|
||||||
static void dump_flash_config_sectors(uint32_t start_sector);
|
static void dump_flash_config_sectors(uint32_t start_sector);
|
||||||
|
@ -102,11 +99,11 @@ static void IRAM get_otp_mac_address(uint8_t *buf) {
|
||||||
if (!(otp_flags & 0x8000)) {
|
if (!(otp_flags & 0x8000)) {
|
||||||
//FIXME: do we really need this check?
|
//FIXME: do we really need this check?
|
||||||
printf("Firmware ONLY supports ESP8266!!!\n");
|
printf("Firmware ONLY supports ESP8266!!!\n");
|
||||||
halt();
|
abort();
|
||||||
}
|
}
|
||||||
if (otp_id0 == 0 && otp_id1 == 0) {
|
if (otp_id0 == 0 && otp_id1 == 0) {
|
||||||
printf("empty otp\n");
|
printf("empty otp\n");
|
||||||
halt();
|
abort();
|
||||||
}
|
}
|
||||||
if (otp_flags & 0x1000) {
|
if (otp_flags & 0x1000) {
|
||||||
// If bit 12 is set, it indicates that the vendor portion of the MAC
|
// If bit 12 is set, it indicates that the vendor portion of the MAC
|
||||||
|
@ -147,23 +144,6 @@ static void IRAM set_spi0_divisor(uint32_t divisor) {
|
||||||
SPI(0).CTRL0 = SET_FIELD(SPI(0).CTRL0, SPI_CTRL0_CLOCK, clkdiv);
|
SPI(0).CTRL0 = SET_FIELD(SPI(0).CTRL0, SPI_CTRL0_CLOCK, clkdiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// .text+0x148
|
|
||||||
void IRAM sdk_user_fatal_exception_handler(void) {
|
|
||||||
if (!sdk_NMIIrqIsOn) {
|
|
||||||
vPortEnterCritical();
|
|
||||||
do {
|
|
||||||
DPORT.DPORT0 &= 0xffffffe0;
|
|
||||||
} while (DPORT.DPORT0 & 0x00000001);
|
|
||||||
}
|
|
||||||
Cache_Read_Disable();
|
|
||||||
Cache_Read_Enable(0, 0, 1);
|
|
||||||
dump_excinfo();
|
|
||||||
uart_flush_txfifo(0);
|
|
||||||
uart_flush_txfifo(1);
|
|
||||||
sdk_system_restart_in_nmi();
|
|
||||||
halt();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void IRAM default_putc(char c) {
|
static void IRAM default_putc(char c) {
|
||||||
uart_putc(0, c);
|
uart_putc(0, c);
|
||||||
|
@ -277,7 +257,7 @@ static void zero_bss(void) {
|
||||||
static void init_networking(uint8_t *phy_info, uint8_t *mac_addr) {
|
static void init_networking(uint8_t *phy_info, uint8_t *mac_addr) {
|
||||||
if (sdk_register_chipv6_phy(phy_info)) {
|
if (sdk_register_chipv6_phy(phy_info)) {
|
||||||
printf("FATAL: sdk_register_chipv6_phy failed");
|
printf("FATAL: sdk_register_chipv6_phy failed");
|
||||||
halt();
|
abort();
|
||||||
}
|
}
|
||||||
uart_set_baud(0, 74906);
|
uart_set_baud(0, 74906);
|
||||||
uart_set_baud(1, 74906);
|
uart_set_baud(1, 74906);
|
||||||
|
@ -333,37 +313,6 @@ static void init_g_ic(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .Lfunc008 -- .irom0.text+0x2a0
|
|
||||||
static void dump_excinfo(void) {
|
|
||||||
uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1;
|
|
||||||
uint32_t excinfo[8];
|
|
||||||
|
|
||||||
RSR(exccause, exccause);
|
|
||||||
printf("Fatal exception (%d): \n", (int)exccause);
|
|
||||||
RSR(epc1, epc1);
|
|
||||||
RSR(epc2, epc2);
|
|
||||||
RSR(epc3, epc3);
|
|
||||||
RSR(excvaddr, excvaddr);
|
|
||||||
RSR(depc, depc);
|
|
||||||
RSR(excsave1, excsave1);
|
|
||||||
printf("%s=0x%08x\n", "epc1", epc1);
|
|
||||||
printf("%s=0x%08x\n", "epc2", epc2);
|
|
||||||
printf("%s=0x%08x\n", "epc3", epc3);
|
|
||||||
printf("%s=0x%08x\n", "excvaddr", excvaddr);
|
|
||||||
printf("%s=0x%08x\n", "depc", depc);
|
|
||||||
printf("%s=0x%08x\n", "excsave1", excsave1);
|
|
||||||
sdk_system_rtc_mem_read(0, excinfo, 32); // Why?
|
|
||||||
excinfo[0] = 2;
|
|
||||||
excinfo[1] = exccause;
|
|
||||||
excinfo[2] = epc1;
|
|
||||||
excinfo[3] = epc2;
|
|
||||||
excinfo[4] = epc3;
|
|
||||||
excinfo[5] = excvaddr;
|
|
||||||
excinfo[6] = depc;
|
|
||||||
excinfo[7] = excsave1;
|
|
||||||
sdk_system_rtc_mem_write(0, excinfo, 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
// .irom0.text+0x398
|
// .irom0.text+0x398
|
||||||
void sdk_wdt_init(void) {
|
void sdk_wdt_init(void) {
|
||||||
WDT.CTRL &= ~WDT_CTRL_ENABLE;
|
WDT.CTRL &= ~WDT_CTRL_ENABLE;
|
||||||
|
@ -408,6 +357,9 @@ void sdk_user_init_task(void *params) {
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void (*__init_array_start)(void);
|
||||||
|
extern void (*__init_array_end)(void);
|
||||||
|
|
||||||
// .Lfunc009 -- .irom0.text+0x5b4
|
// .Lfunc009 -- .irom0.text+0x5b4
|
||||||
static void user_start_phase2(void) {
|
static void user_start_phase2(void) {
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
|
@ -445,6 +397,13 @@ static void user_start_phase2(void) {
|
||||||
}
|
}
|
||||||
init_networking(phy_info, sdk_info.sta_mac_addr);
|
init_networking(phy_info, sdk_info.sta_mac_addr);
|
||||||
free(phy_info);
|
free(phy_info);
|
||||||
|
|
||||||
|
// Call gcc constructor functions
|
||||||
|
void (**ctor)(void);
|
||||||
|
for ( ctor = &__init_array_start; ctor != &__init_array_end; ++ctor) {
|
||||||
|
(*ctor)();
|
||||||
|
}
|
||||||
|
|
||||||
tcpip_init(NULL, NULL);
|
tcpip_init(NULL, NULL);
|
||||||
sdk_wdt_init();
|
sdk_wdt_init();
|
||||||
xTaskCreate(sdk_user_init_task, (signed char *)"uiT", 1024, 0, 14, &sdk_xUserTaskHandle);
|
xTaskCreate(sdk_user_init_task, (signed char *)"uiT", 1024, 0, 14, &sdk_xUserTaskHandle);
|
||||||
|
|
175
core/debug_dumps.c
Normal file
175
core/debug_dumps.c
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
/* Code for dumping status/debug output/etc, including fatal
|
||||||
|
* exception handling & abort implementation.
|
||||||
|
*
|
||||||
|
* Part of esp-open-rtos
|
||||||
|
*
|
||||||
|
* Partially reverse engineered from MIT licensed Espressif RTOS SDK Copyright (C) Espressif Systems.
|
||||||
|
* Additions Copyright (C) 2015 Superhouse Automation Pty Ltd
|
||||||
|
* BSD Licensed as described in the file LICENSE
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <task.h>
|
||||||
|
|
||||||
|
#include "debug_dumps.h"
|
||||||
|
#include "common_macros.h"
|
||||||
|
#include "xtensa_ops.h"
|
||||||
|
#include "esp/rom.h"
|
||||||
|
#include "esp/uart.h"
|
||||||
|
#include "espressif/esp_common.h"
|
||||||
|
#include "sdk_internal.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static void IRAM fatal_handler_prelude(void);
|
||||||
|
/* Inner parts of crash handlers marked noinline to ensure they don't inline into IRAM. */
|
||||||
|
static void __attribute__((noinline)) __attribute__((noreturn)) fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack);
|
||||||
|
static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_inner(uint32_t *caller, uint32_t *sp);
|
||||||
|
|
||||||
|
/* fatal_exception_handler called from any unhandled user exception
|
||||||
|
*
|
||||||
|
* (similar to a hard fault on other processor architectures)
|
||||||
|
*
|
||||||
|
* This function is run from IRAM, but the majority of the handler
|
||||||
|
* runs from flash after fatal_handler_prelude ensures it is mapped
|
||||||
|
* safely.
|
||||||
|
*/
|
||||||
|
void IRAM __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack) {
|
||||||
|
fatal_handler_prelude();
|
||||||
|
fatal_exception_handler_inner(sp, registers_saved_on_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Abort implementation
|
||||||
|
*
|
||||||
|
* Replaces the weak-linked abort implementation provided by newlib libc.
|
||||||
|
*
|
||||||
|
* Disable interrupts, enable flash mapping, dump stack & caller
|
||||||
|
* address, restart.
|
||||||
|
*
|
||||||
|
* This function is run from IRAM, but the majority of the abort
|
||||||
|
* handler runs from flash after fatal_handler_prelude ensures it is
|
||||||
|
* mapped safely.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void IRAM abort(void) {
|
||||||
|
uint32_t *sp, *caller;
|
||||||
|
RETADDR(caller);
|
||||||
|
/* abort() caller is one instruction before our return address */
|
||||||
|
caller = (uint32_t *)((intptr_t)caller - 3);
|
||||||
|
SP(sp);
|
||||||
|
fatal_handler_prelude();
|
||||||
|
abort_handler_inner(caller, sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump exception information from special function registers */
|
||||||
|
static void dump_excinfo(void) {
|
||||||
|
uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1;
|
||||||
|
uint32_t excinfo[8];
|
||||||
|
|
||||||
|
RSR(exccause, exccause);
|
||||||
|
printf("Fatal exception (%d): \n", (int)exccause);
|
||||||
|
RSR(epc1, epc1);
|
||||||
|
RSR(epc2, epc2);
|
||||||
|
RSR(epc3, epc3);
|
||||||
|
RSR(excvaddr, excvaddr);
|
||||||
|
RSR(depc, depc);
|
||||||
|
RSR(excsave1, excsave1);
|
||||||
|
printf("%s=0x%08x\n", "epc1", epc1);
|
||||||
|
printf("%s=0x%08x\n", "epc2", epc2);
|
||||||
|
printf("%s=0x%08x\n", "epc3", epc3);
|
||||||
|
printf("%s=0x%08x\n", "excvaddr", excvaddr);
|
||||||
|
printf("%s=0x%08x\n", "depc", depc);
|
||||||
|
printf("%s=0x%08x\n", "excsave1", excsave1);
|
||||||
|
sdk_system_rtc_mem_read(0, excinfo, 32); // Why?
|
||||||
|
excinfo[0] = 2;
|
||||||
|
excinfo[1] = exccause;
|
||||||
|
excinfo[2] = epc1;
|
||||||
|
excinfo[3] = epc2;
|
||||||
|
excinfo[4] = epc3;
|
||||||
|
excinfo[5] = excvaddr;
|
||||||
|
excinfo[6] = depc;
|
||||||
|
excinfo[7] = excsave1;
|
||||||
|
sdk_system_rtc_mem_write(0, excinfo, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dump stack memory (frames above sp) to stdout
|
||||||
|
|
||||||
|
There's a lot of smart stuff we could do while dumping stack
|
||||||
|
but for now we just dump what looks like our stack region.
|
||||||
|
|
||||||
|
Probably dumps more memory than it needs to, the first instance of
|
||||||
|
0xa5a5a5a5 probably constitutes the end of our stack.
|
||||||
|
*/
|
||||||
|
void dump_stack(uint32_t *sp) {
|
||||||
|
printf("\nStack: SP=%p\n", sp);
|
||||||
|
for(uint32_t *p = sp; p < sp + 32; p += 4) {
|
||||||
|
if((intptr_t)p >= 0x3fffc000) {
|
||||||
|
break; /* approximate end of RAM */
|
||||||
|
}
|
||||||
|
printf("%p: %08x %08x %08x %08x\n", p, p[0], p[1], p[2], p[3]);
|
||||||
|
if(p[0] == 0xa5a5a5a5 && p[1] == 0xa5a5a5a5
|
||||||
|
&& p[2] == 0xa5a5a5a5 && p[3] == 0xa5a5a5a5) {
|
||||||
|
break; /* FreeRTOS uses this pattern to mark untouched stack space */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump normal registers that were stored above 'sp'
|
||||||
|
by the exception handler preamble
|
||||||
|
*/
|
||||||
|
void dump_registers_in_exception_handler(uint32_t *sp) {
|
||||||
|
uint32_t excsave1;
|
||||||
|
uint32_t *saved = sp - (0x50 / sizeof(uint32_t));
|
||||||
|
printf("Registers:\n");
|
||||||
|
RSR(excsave1, excsave1);
|
||||||
|
printf("a0 %08x ", excsave1);
|
||||||
|
printf("a1 %08x ", (intptr_t)sp);
|
||||||
|
for(int a = 2; a < 14; a++) {
|
||||||
|
printf("a%-2d %08x%c", a, saved[a+3], a == 3 || a == 7 || a == 11 ? '\n':' ');
|
||||||
|
}
|
||||||
|
printf("SAR %08x\n", saved[0x13]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Prelude ensures exceptions/NMI off and flash is mapped, allowing
|
||||||
|
calls to non-IRAM functions.
|
||||||
|
*/
|
||||||
|
static void IRAM fatal_handler_prelude(void) {
|
||||||
|
if (!sdk_NMIIrqIsOn) {
|
||||||
|
vPortEnterCritical();
|
||||||
|
do {
|
||||||
|
DPORT.DPORT0 &= 0xffffffe0;
|
||||||
|
} while (DPORT.DPORT0 & 0x00000001);
|
||||||
|
}
|
||||||
|
Cache_Read_Disable();
|
||||||
|
Cache_Read_Enable(0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main part of fatal exception handler, is run from flash to save
|
||||||
|
some IRAM.
|
||||||
|
*/
|
||||||
|
static void fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) {
|
||||||
|
dump_excinfo();
|
||||||
|
if (sp) {
|
||||||
|
if (registers_saved_on_stack) {
|
||||||
|
dump_registers_in_exception_handler(sp);
|
||||||
|
}
|
||||||
|
dump_stack(sp);
|
||||||
|
}
|
||||||
|
uart_flush_txfifo(0);
|
||||||
|
uart_flush_txfifo(1);
|
||||||
|
sdk_system_restart_in_nmi();
|
||||||
|
while(1) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main part of abort handler, can be run from flash to save some
|
||||||
|
IRAM.
|
||||||
|
*/
|
||||||
|
static void abort_handler_inner(uint32_t *caller, uint32_t *sp) {
|
||||||
|
printf("abort() invoked at %p.\n", caller);
|
||||||
|
dump_stack(sp);
|
||||||
|
uart_flush_txfifo(0);
|
||||||
|
uart_flush_txfifo(1);
|
||||||
|
sdk_system_restart_in_nmi();
|
||||||
|
while(1) {}
|
||||||
|
}
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
_xt_isr isr[16];
|
_xt_isr isr[16];
|
||||||
|
|
||||||
|
bool esp_in_isr;
|
||||||
|
|
||||||
void IRAM _xt_isr_attach(uint8_t i, _xt_isr func)
|
void IRAM _xt_isr_attach(uint8_t i, _xt_isr func)
|
||||||
{
|
{
|
||||||
isr[i] = func;
|
isr[i] = func;
|
||||||
|
@ -20,6 +22,8 @@ void IRAM _xt_isr_attach(uint8_t i, _xt_isr func)
|
||||||
*/
|
*/
|
||||||
uint16_t IRAM _xt_isr_handler(uint16_t intset)
|
uint16_t IRAM _xt_isr_handler(uint16_t intset)
|
||||||
{
|
{
|
||||||
|
esp_in_isr = true;
|
||||||
|
|
||||||
/* WDT has highest priority (occasional WDT resets otherwise) */
|
/* WDT has highest priority (occasional WDT resets otherwise) */
|
||||||
if(intset & BIT(INUM_WDT)) {
|
if(intset & BIT(INUM_WDT)) {
|
||||||
_xt_clear_ints(BIT(INUM_WDT));
|
_xt_clear_ints(BIT(INUM_WDT));
|
||||||
|
@ -35,5 +39,7 @@ uint16_t IRAM _xt_isr_handler(uint16_t intset)
|
||||||
intset -= mask;
|
intset -= mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_in_isr = false;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,9 @@ DebugExceptionVector:
|
||||||
.type DebugExceptionVector, @function
|
.type DebugExceptionVector, @function
|
||||||
|
|
||||||
wsr a0, excsave2
|
wsr a0, excsave2
|
||||||
call0 sdk_user_fatal_exception_handler
|
mov a2, a1
|
||||||
|
movi a3, 0
|
||||||
|
call0 fatal_exception_handler
|
||||||
rfi 2
|
rfi 2
|
||||||
|
|
||||||
.org VecBase + 0x20
|
.org VecBase + 0x20
|
||||||
|
@ -81,7 +83,9 @@ KernelExceptionVector:
|
||||||
.type KernelExceptionVector, @function
|
.type KernelExceptionVector, @function
|
||||||
|
|
||||||
break 1, 0
|
break 1, 0
|
||||||
call0 sdk_user_fatal_exception_handler
|
mov a2, a1
|
||||||
|
movi a3, 0
|
||||||
|
call0 fatal_exception_handler
|
||||||
rfe
|
rfe
|
||||||
|
|
||||||
.org VecBase + 0x50
|
.org VecBase + 0x50
|
||||||
|
@ -98,7 +102,9 @@ DoubleExceptionVector:
|
||||||
.type DoubleExceptionVector, @function
|
.type DoubleExceptionVector, @function
|
||||||
|
|
||||||
break 1, 4
|
break 1, 4
|
||||||
call0 sdk_user_fatal_exception_handler
|
mov a2, a1
|
||||||
|
movi a3, 0
|
||||||
|
call0 fatal_exception_handler
|
||||||
|
|
||||||
/* Reset vector at offset 0x80 is unused, as vecbase gets reset to mask ROM
|
/* Reset vector at offset 0x80 is unused, as vecbase gets reset to mask ROM
|
||||||
* vectors on chip reset. */
|
* vectors on chip reset. */
|
||||||
|
@ -251,11 +257,13 @@ LoadStoreErrorHandler:
|
||||||
* will have correct values */
|
* will have correct values */
|
||||||
wsr a0, sar
|
wsr a0, sar
|
||||||
l32i a0, sp, 0
|
l32i a0, sp, 0
|
||||||
l32i a2, sp, 0x08
|
/*l32i a2, sp, 0x08*/
|
||||||
l32i a3, sp, 0x0c
|
l32i a3, sp, 0x0c
|
||||||
l32i a4, sp, 0x10
|
l32i a4, sp, 0x10
|
||||||
rsr a1, excsave1
|
rsr a1, excsave1
|
||||||
call0 sdk_user_fatal_exception_handler
|
mov a2, a1
|
||||||
|
movi a3, 0
|
||||||
|
call0 fatal_exception_handler
|
||||||
|
|
||||||
.balign 4
|
.balign 4
|
||||||
.LSE_assign_a1:
|
.LSE_assign_a1:
|
||||||
|
@ -515,7 +523,9 @@ UserExceptionHandler:
|
||||||
.literal_position
|
.literal_position
|
||||||
.LUserFailOtherExceptionCause:
|
.LUserFailOtherExceptionCause:
|
||||||
break 1, 1
|
break 1, 1
|
||||||
call0 sdk_user_fatal_exception_handler
|
addi a2, a1, 0x50 /* UserExceptionHandler pushes stack down 0x50 */
|
||||||
|
movi a3, 1
|
||||||
|
call0 fatal_exception_handler
|
||||||
|
|
||||||
/* _xt_user_exit is pushed onto the stack as part of the user exception handler,
|
/* _xt_user_exit is pushed onto the stack as part of the user exception handler,
|
||||||
restores same set registers which were saved there and returns from exception */
|
restores same set registers which were saved there and returns from exception */
|
||||||
|
|
22
core/include/debug_dumps.h
Normal file
22
core/include/debug_dumps.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/* Functions for dumping status/debug output/etc, including fatal
|
||||||
|
* exception handling.
|
||||||
|
*
|
||||||
|
* Part of esp-open-rtos
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2016 Superhouse Automation Pty Ltd
|
||||||
|
* BSD Licensed as described in the file LICENSE
|
||||||
|
*/
|
||||||
|
#ifndef _DEBUG_DUMPS_H
|
||||||
|
#define _DEBUG_DUMPS_H
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Dump stack memory starting from stack pointer address sp. */
|
||||||
|
void dump_stack(uint32_t *sp);
|
||||||
|
|
||||||
|
/* Called from exception_vectors.S when a fatal exception occurs.
|
||||||
|
|
||||||
|
Probably not useful to be called in other contexts.
|
||||||
|
*/
|
||||||
|
void __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack);
|
||||||
|
|
||||||
|
#endif
|
|
@ -25,4 +25,19 @@
|
||||||
#define ESYNC() asm volatile ( "esync" )
|
#define ESYNC() asm volatile ( "esync" )
|
||||||
#define DSYNC() asm volatile ( "dsync" )
|
#define DSYNC() asm volatile ( "dsync" )
|
||||||
|
|
||||||
|
|
||||||
|
/* Read stack pointer to variable.
|
||||||
|
*
|
||||||
|
* Note that the compiler will push a stack frame (minimum 16 bytes)
|
||||||
|
* in the prelude of a C function that calls any other functions.
|
||||||
|
*/
|
||||||
|
#define SP(var) asm volatile ("mov %0, a1" : "=r" (var));
|
||||||
|
|
||||||
|
/* Read the function return address to a variable.
|
||||||
|
*
|
||||||
|
* Depends on the containing function being simple enough that a0 is
|
||||||
|
* being used as a working register.
|
||||||
|
*/
|
||||||
|
#define RETADDR(var) asm volatile ("mov %0, a0" : "=r" (var))
|
||||||
|
|
||||||
#endif /* _XTENSA_OPS_H */
|
#endif /* _XTENSA_OPS_H */
|
||||||
|
|
|
@ -25,7 +25,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
|
||||||
if (heap_end + incr > stack_ptr)
|
if (heap_end + incr > stack_ptr)
|
||||||
{
|
{
|
||||||
_write (1, "_sbrk: Heap collided with stack\n", 32);
|
_write (1, "_sbrk: Heap collided with stack\n", 32);
|
||||||
while(1) {}
|
abort();
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
heap_end += incr;
|
heap_end += incr;
|
||||||
|
|
|
@ -17,20 +17,6 @@ void IRAM *zalloc(size_t nbytes)
|
||||||
return calloc(1, nbytes);
|
return calloc(1, nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void (*__init_array_start)(void);
|
|
||||||
extern void (*__init_array_end)(void);
|
|
||||||
|
|
||||||
/* Do things which should be done as part of the SDK startup code, but aren't.
|
|
||||||
TODO: Move into app_main.c
|
|
||||||
*/
|
|
||||||
void sdk_compat_initialise()
|
|
||||||
{
|
|
||||||
/* Call C++ constructors or C functions marked with __attribute__((constructor)) */
|
|
||||||
void (**p)(void);
|
|
||||||
for ( p = &__init_array_start; p != &__init_array_end; ++p)
|
|
||||||
(*p)();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* UART RX function from Espressif SDK internals.
|
/* UART RX function from Espressif SDK internals.
|
||||||
*
|
*
|
||||||
* Not part of published API.
|
* Not part of published API.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# Simple makefile for simple example
|
# Simple makefile for simple example
|
||||||
PROGRAM=cpp_01_tasks
|
PROGRAM=cpp_01_tasks
|
||||||
OTA=0
|
|
||||||
EXTRA_COMPONENTS=extras/cpp_support
|
EXTRA_COMPONENTS=extras/cpp_support
|
||||||
include ../../common.mk
|
include ../../common.mk
|
||||||
|
|
|
@ -19,15 +19,14 @@
|
||||||
|
|
||||||
// DS18B20 driver
|
// DS18B20 driver
|
||||||
#include "ds18b20/ds18b20.h"
|
#include "ds18b20/ds18b20.h"
|
||||||
// Onewire init
|
|
||||||
#include "onewire/onewire.h"
|
|
||||||
|
|
||||||
void broadcast_temperature(void *pvParameters)
|
void broadcast_temperature(void *pvParameters)
|
||||||
{
|
{
|
||||||
|
|
||||||
uint8_t amount = 0;
|
uint8_t amount = 0;
|
||||||
uint8_t sensors = 2;
|
uint8_t sensors = 1;
|
||||||
ds_sensor_t t[sensors];
|
ds18b20_addr_t addrs[sensors];
|
||||||
|
float results[sensors];
|
||||||
|
|
||||||
// Use GPIO 13 as one wire pin.
|
// Use GPIO 13 as one wire pin.
|
||||||
uint8_t GPIO_FOR_ONE_WIRE = 13;
|
uint8_t GPIO_FOR_ONE_WIRE = 13;
|
||||||
|
@ -36,8 +35,6 @@ void broadcast_temperature(void *pvParameters)
|
||||||
|
|
||||||
// Broadcaster part
|
// Broadcaster part
|
||||||
err_t err;
|
err_t err;
|
||||||
// Initialize one wire bus.
|
|
||||||
onewire_init(GPIO_FOR_ONE_WIRE);
|
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
|
|
||||||
|
@ -66,18 +63,17 @@ void broadcast_temperature(void *pvParameters)
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
// Search all DS18B20, return its amount and feed 't' structure with result data.
|
// Search all DS18B20, return its amount and feed 't' structure with result data.
|
||||||
amount = ds18b20_read_all(GPIO_FOR_ONE_WIRE, t);
|
amount = ds18b20_scan_devices(GPIO_FOR_ONE_WIRE, addrs, sensors);
|
||||||
|
|
||||||
if (amount < sensors){
|
if (amount < sensors){
|
||||||
printf("Something is wrong, I expect to see %d sensors \nbut just %d was detected!\n", sensors, amount);
|
printf("Something is wrong, I expect to see %d sensors \nbut just %d was detected!\n", sensors, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < amount; ++i)
|
ds18b20_measure_and_read_multi(GPIO_FOR_ONE_WIRE, addrs, sensors, results);
|
||||||
|
for (int i = 0; i < sensors; ++i)
|
||||||
{
|
{
|
||||||
int intpart = (int)t[i].value;
|
// ("\xC2\xB0" is the degree character (U+00B0) in UTF-8)
|
||||||
int fraction = (int)((t[i].value - intpart) * 100);
|
sprintf(msg, "Sensor %08x%08x reports: %f \xC2\xB0""C\n", (uint32_t)(addrs[i] >> 32), (uint32_t)addrs[i], results[i]);
|
||||||
// Multiple "" here is just to satisfy compiler and don`t raise 'hex escape sequence out of range' warning.
|
|
||||||
sprintf(msg, "Sensor %d report: %d.%02d ""\xC2""\xB0""C\n",t[i].id, intpart, fraction);
|
|
||||||
printf("%s", msg);
|
printf("%s", msg);
|
||||||
|
|
||||||
struct netbuf* buf = netbuf_new();
|
struct netbuf* buf = netbuf_new();
|
||||||
|
|
|
@ -1,59 +1,78 @@
|
||||||
/* ds18b20 - Retrieves temperature from ds18b20 sensors and print it out.
|
/* ds18b20_onewire.c - Retrieves readings from one or more DS18B20 temperature
|
||||||
|
* sensors, and prints the results to stdout.
|
||||||
*
|
*
|
||||||
* This sample code is in the public domain.,
|
* This sample code is in the public domain.,
|
||||||
*/
|
*/
|
||||||
#include "espressif/esp_common.h"
|
|
||||||
#include "esp/uart.h"
|
|
||||||
|
|
||||||
#include "FreeRTOS.h"
|
#include "FreeRTOS.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include "timers.h"
|
#include "esp/uart.h"
|
||||||
#include "queue.h"
|
|
||||||
|
|
||||||
// DS18B20 driver
|
|
||||||
#include "ds18b20/ds18b20.h"
|
#include "ds18b20/ds18b20.h"
|
||||||
// Onewire init
|
|
||||||
#include "onewire/onewire.h"
|
|
||||||
|
|
||||||
void print_temperature(void *pvParameters)
|
#define SENSOR_GPIO 13
|
||||||
{
|
#define MAX_SENSORS 8
|
||||||
int delay = 500;
|
#define RESCAN_INTERVAL 8
|
||||||
uint8_t amount = 0;
|
#define LOOP_DELAY_MS 250
|
||||||
// Declare amount of sensors
|
|
||||||
uint8_t sensors = 2;
|
|
||||||
ds_sensor_t t[sensors];
|
|
||||||
|
|
||||||
// Use GPIO 13 as one wire pin.
|
void print_temperature(void *pvParameters) {
|
||||||
uint8_t GPIO_FOR_ONE_WIRE = 13;
|
ds18b20_addr_t addrs[MAX_SENSORS];
|
||||||
|
float temps[MAX_SENSORS];
|
||||||
|
int sensor_count;
|
||||||
|
|
||||||
onewire_init(GPIO_FOR_ONE_WIRE);
|
// There is no special initialization required before using the ds18b20
|
||||||
|
// routines. However, we make sure that the internal pull-up resistor is
|
||||||
|
// enabled on the GPIO pin so that one can connect up a sensor without
|
||||||
|
// needing an external pull-up (Note: The internal (~47k) pull-ups of the
|
||||||
|
// ESP8266 do appear to work, at least for simple setups (one or two sensors
|
||||||
|
// connected with short leads), but do not technically meet the pull-up
|
||||||
|
// requirements from the DS18B20 datasheet and may not always be reliable.
|
||||||
|
// For a real application, a proper 4.7k external pull-up resistor is
|
||||||
|
// recommended instead!)
|
||||||
|
|
||||||
|
gpio_set_pullup(SENSOR_GPIO, true, true);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
// Search all DS18B20, return its amount and feed 't' structure with result data.
|
// Every RESCAN_INTERVAL samples, check to see if the sensors connected
|
||||||
amount = ds18b20_read_all(GPIO_FOR_ONE_WIRE, t);
|
// to our bus have changed.
|
||||||
|
sensor_count = ds18b20_scan_devices(SENSOR_GPIO, addrs, MAX_SENSORS);
|
||||||
|
|
||||||
if (amount < sensors){
|
if (sensor_count < 1) {
|
||||||
printf("Something is wrong, I expect to see %d sensors \nbut just %d was detected!\n", sensors, amount);
|
printf("\nNo sensors detected!\n");
|
||||||
}
|
} else {
|
||||||
|
printf("\n%d sensors detected:\n", sensor_count);
|
||||||
|
// If there were more sensors found than we have space to handle,
|
||||||
|
// just report the first MAX_SENSORS..
|
||||||
|
if (sensor_count > MAX_SENSORS) sensor_count = MAX_SENSORS;
|
||||||
|
|
||||||
for (int i = 0; i < amount; ++i)
|
// Do a number of temperature samples, and print the results.
|
||||||
{
|
for (int i = 0; i < RESCAN_INTERVAL; i++) {
|
||||||
int intpart = (int)t[i].value;
|
ds18b20_measure_and_read_multi(SENSOR_GPIO, addrs, sensor_count, temps);
|
||||||
int fraction = (int)((t[i].value - intpart) * 100);
|
for (int j = 0; j < sensor_count; j++) {
|
||||||
// Multiple "" here is just to satisfy compiler and don`t raise 'hex escape sequence out of range' warning.
|
// The DS18B20 address is a 64-bit integer, but newlib-nano
|
||||||
printf("Sensor %d report: %d.%02d ""\xC2""\xB0""C\n",t[i].id, intpart, fraction);
|
// printf does not support printing 64-bit values, so we
|
||||||
|
// split it up into two 32-bit integers and print them
|
||||||
|
// back-to-back to make it look like one big hex number.
|
||||||
|
uint32_t addr0 = addrs[j] >> 32;
|
||||||
|
uint32_t addr1 = addrs[j];
|
||||||
|
float temp_c = temps[j];
|
||||||
|
float temp_f = (temp_c * 1.8) + 32;
|
||||||
|
printf(" Sensor %08x%08x reports %f deg C (%f deg F)\n", addr0, addr1, temp_c, temp_f);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Wait for a little bit between each sample (note that the
|
||||||
|
// ds18b20_measure_and_read_multi operation already takes at
|
||||||
|
// least 750ms to run, so this is on top of that delay).
|
||||||
|
vTaskDelay(LOOP_DELAY_MS / portTICK_RATE_MS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
printf("\n");
|
|
||||||
vTaskDelay(delay / portTICK_RATE_MS);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void user_init(void)
|
void user_init(void) {
|
||||||
{
|
|
||||||
uart_set_baud(0, 115200);
|
uart_set_baud(0, 115200);
|
||||||
|
|
||||||
printf("SDK version:%s\n", sdk_system_get_sdk_version());
|
|
||||||
|
|
||||||
xTaskCreate(&print_temperature, (signed char *)"print_temperature", 256, NULL, 2, NULL);
|
xTaskCreate(&print_temperature, (signed char *)"print_temperature", 256, NULL, 2, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -305,7 +305,7 @@ static void test_system_interaction()
|
||||||
}
|
}
|
||||||
uint32_t ticks = xTaskGetTickCount() - start;
|
uint32_t ticks = xTaskGetTickCount() - start;
|
||||||
printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_RATE_MS);
|
printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_RATE_MS);
|
||||||
while(1) {}
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The following "sanity tests" are designed to try to execute every code path
|
/* The following "sanity tests" are designed to try to execute every code path
|
||||||
|
|
|
@ -1,43 +1,44 @@
|
||||||
/* This is the root certificate for the CA trust chain of
|
/* This is the CA certificate for the CA trust chain of
|
||||||
www.howsmyssl.com in PEM format, as dumped via:
|
www.howsmyssl.com in PEM format, as dumped via:
|
||||||
|
|
||||||
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
|
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
|
||||||
|
|
||||||
The root cert is the last cert in the chain output by the server.
|
The CA cert is the last cert in the chain output by the server.
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
|
||||||
|
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
|
||||||
|
*/
|
||||||
const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n"
|
const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n"
|
||||||
"MIIFNzCCBB+gAwIBAgISAfl7TPw6Mf/F6VCBc5eaHA7kMA0GCSqGSIb3DQEBCwUA\r\n"
|
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n"
|
||||||
"MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD\r\n"
|
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
|
||||||
"ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMTAeFw0xNTEyMzAwNzQ0MDBaFw0x\r\n"
|
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n"
|
||||||
"NjAzMjkwNzQ0MDBaMBwxGjAYBgNVBAMTEXd3dy5ob3dzbXlzc2wuY29tMIIBIjAN\r\n"
|
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n"
|
||||||
"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKF7WzSrDPinQhd9mVfoW5u46/TC\r\n"
|
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n"
|
||||||
"fbYKR3MEryetUSeQuwuXFj2xO6+a/JQ99UC3Eq+9s8uFdx1zFFS6qfW+HXBwGWVf\r\n"
|
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n"
|
||||||
"ajKEyIcXUJZCRn7aWpTWZq6cuv4bZSv1QklGViQs8UCZifcN0A/mYrH7zHG2WDn2\r\n"
|
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n"
|
||||||
"fxNgA+nJBqbZPr1gP9hqGFCnX+dPR5WxtC9+Dv9Sx+wiOWVz/obVTCygdqqcpa5I\r\n"
|
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n"
|
||||||
"3/U9REVgO2VfT+xMty6NZTMCjTJ+GXZuB/BrMe9+ZmgWk0grJyqdrCxOCyK6B4g+\r\n"
|
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n"
|
||||||
"Fvs8WFRNTHdQnP3/NT5hPtreZ3nuY2YY7RbGFwUaBcvJwbbIqalpiQ1X7QIDAQAB\r\n"
|
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n"
|
||||||
"o4ICQzCCAj8wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr\r\n"
|
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n"
|
||||||
"BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQMAe087CSp3PjgqyIYyWTR\r\n"
|
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n"
|
||||||
"a2aMnTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBwBggrBgEFBQcB\r\n"
|
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n"
|
||||||
"AQRkMGIwLwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmludC14MS5sZXRzZW5jcnlw\r\n"
|
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n"
|
||||||
"dC5vcmcvMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDEubGV0c2VuY3J5\r\n"
|
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n"
|
||||||
"cHQub3JnLzBNBgNVHREERjBEgg1ob3dzbXlzc2wuY29tgg1ob3dzbXl0bHMuY29t\r\n"
|
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n"
|
||||||
"ghF3d3cuaG93c215dGxzLmNvbYIRd3d3Lmhvd3NteXNzbC5jb20wgf4GA1UdIASB\r\n"
|
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n"
|
||||||
"9jCB8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpo\r\n"
|
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n"
|
||||||
"dHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlz\r\n"
|
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n"
|
||||||
"IENlcnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcg\r\n"
|
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n"
|
||||||
"UGFydGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmlj\r\n"
|
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n"
|
||||||
"YXRlIFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBv\r\n"
|
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n"
|
||||||
"c2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEALB2QrIrcxxFr81b6khy9LwVBpthL\r\n"
|
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n"
|
||||||
"2LSs0xWhA09gxHmPnVrqim3Wa9HFYRApSqtTQWSx58TO+MERXQ7eDX50k53oem+Q\r\n"
|
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n"
|
||||||
"gXn90HVDkR0jbV1CsAD5qL00kKOofAyGkUhFlO2hcRU0CIj7Z9HZB8xpYdZWLUSZ\r\n"
|
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n"
|
||||||
"BpgiXdf/YYRIwgx29GRQjhfl/H30fHyawY5SvquJuvAeEr7lxqAmEEg3a7J6ibHL\r\n"
|
|
||||||
"90zf5KMkXGyVsXqxLqrEXQKgTvpUMeP5iYAxE45R5GNmYS2jajyTi4XkWw4nEu4Q\r\n"
|
|
||||||
"dMTLuwxGz87KOwSSFOLU903hO3IIPvFIIMY6aVK2kAgQPNIqk9nFurTF9A==\r\n"
|
|
||||||
"-----END CERTIFICATE-----\r\n";
|
"-----END CERTIFICATE-----\r\n";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,11 +41,11 @@
|
||||||
#include "mbedtls/error.h"
|
#include "mbedtls/error.h"
|
||||||
#include "mbedtls/certs.h"
|
#include "mbedtls/certs.h"
|
||||||
|
|
||||||
#define WEB_SERVER "howsmyssl.com"
|
#define WEB_SERVER "www.howsmyssl.com"
|
||||||
#define WEB_PORT "443"
|
#define WEB_PORT "443"
|
||||||
#define WEB_URL "https://www.howsmyssl.com/a/check"
|
#define WEB_URL "https://www.howsmyssl.com/a/check"
|
||||||
|
|
||||||
#define GET_REQUEST "GET "WEB_URL" HTTP/1.1\n\n"
|
#define GET_REQUEST "GET "WEB_URL" HTTP/1.1\nHost: "WEB_SERVER"\n\n"
|
||||||
|
|
||||||
/* Root cert for howsmyssl.com, stored in cert.c */
|
/* Root cert for howsmyssl.com, stored in cert.c */
|
||||||
extern const char *server_root_cert;
|
extern const char *server_root_cert;
|
||||||
|
@ -115,7 +115,7 @@ void http_get_task(void *pvParameters)
|
||||||
strlen(pers))) != 0)
|
strlen(pers))) != 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok\n");
|
printf(" ok\n");
|
||||||
|
@ -129,7 +129,7 @@ void http_get_task(void *pvParameters)
|
||||||
if(ret < 0)
|
if(ret < 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok (%d skipped)\n", ret);
|
printf(" ok (%d skipped)\n", ret);
|
||||||
|
@ -138,7 +138,7 @@ void http_get_task(void *pvParameters)
|
||||||
if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
|
if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
|
printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
3
examples/mqtt_client/Makefile
Normal file
3
examples/mqtt_client/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
PROGRAM=mqtt_client
|
||||||
|
EXTRA_COMPONENTS = extras/paho_mqtt_c
|
||||||
|
include ../../common.mk
|
221
examples/mqtt_client/mqtt_client.c
Normal file
221
examples/mqtt_client/mqtt_client.c
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
#include "espressif/esp_common.h"
|
||||||
|
#include "esp/uart.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <task.h>
|
||||||
|
#include <ssid_config.h>
|
||||||
|
|
||||||
|
#include <espressif/esp_sta.h>
|
||||||
|
#include <espressif/esp_wifi.h>
|
||||||
|
|
||||||
|
#include <paho_mqtt_c/MQTTESP8266.h>
|
||||||
|
#include <paho_mqtt_c/MQTTClient.h>
|
||||||
|
|
||||||
|
#include <semphr.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* You can use http://test.mosquitto.org/ to test mqtt_client instead
|
||||||
|
* of setting up your own MQTT server */
|
||||||
|
#define MQTT_HOST ("test.mosquitto.org")
|
||||||
|
#define MQTT_PORT 1883
|
||||||
|
|
||||||
|
#define MQTT_USER NULL
|
||||||
|
#define MQTT_PASS NULL
|
||||||
|
|
||||||
|
xSemaphoreHandle wifi_alive;
|
||||||
|
xQueueHandle publish_queue;
|
||||||
|
#define PUB_MSG_LEN 16
|
||||||
|
|
||||||
|
static void beat_task(void *pvParameters)
|
||||||
|
{
|
||||||
|
portTickType xLastWakeTime = xTaskGetTickCount();
|
||||||
|
char msg[PUB_MSG_LEN];
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
vTaskDelayUntil(&xLastWakeTime, 10000 / portTICK_RATE_MS);
|
||||||
|
printf("beat\r\n");
|
||||||
|
snprintf(msg, PUB_MSG_LEN, "Beat %d\r\n", count++);
|
||||||
|
if (xQueueSend(publish_queue, (void *)msg, 0) == pdFALSE) {
|
||||||
|
printf("Publish queue overflow.\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void topic_received(MessageData *md)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
MQTTMessage *message = md->message;
|
||||||
|
printf("Received: ");
|
||||||
|
for( i = 0; i < md->topic->lenstring.len; ++i)
|
||||||
|
printf("%c", md->topic->lenstring.data[ i ]);
|
||||||
|
|
||||||
|
printf(" = ");
|
||||||
|
for( i = 0; i < (int)message->payloadlen; ++i)
|
||||||
|
printf("%c", ((char *)(message->payload))[i]);
|
||||||
|
|
||||||
|
printf("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * get_my_id(void)
|
||||||
|
{
|
||||||
|
// Use MAC address for Station as unique ID
|
||||||
|
static char my_id[13];
|
||||||
|
static bool my_id_done = false;
|
||||||
|
int8_t i;
|
||||||
|
uint8_t x;
|
||||||
|
if (my_id_done)
|
||||||
|
return my_id;
|
||||||
|
if (!sdk_wifi_get_macaddr(STATION_IF, (uint8_t *)my_id))
|
||||||
|
return NULL;
|
||||||
|
for (i = 5; i >= 0; --i)
|
||||||
|
{
|
||||||
|
x = my_id[i] & 0x0F;
|
||||||
|
if (x > 9) x += 7;
|
||||||
|
my_id[i * 2 + 1] = x + '0';
|
||||||
|
x = my_id[i] >> 4;
|
||||||
|
if (x > 9) x += 7;
|
||||||
|
my_id[i * 2] = x + '0';
|
||||||
|
}
|
||||||
|
my_id[12] = '\0';
|
||||||
|
my_id_done = true;
|
||||||
|
return my_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mqtt_task(void *pvParameters)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct Network network;
|
||||||
|
MQTTClient client = DefaultClient;
|
||||||
|
char mqtt_client_id[20];
|
||||||
|
uint8_t mqtt_buf[100];
|
||||||
|
uint8_t mqtt_readbuf[100];
|
||||||
|
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||||
|
|
||||||
|
NewNetwork( &network );
|
||||||
|
memset(mqtt_client_id, 0, sizeof(mqtt_client_id));
|
||||||
|
strcpy(mqtt_client_id, "ESP-");
|
||||||
|
strcat(mqtt_client_id, get_my_id());
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
xSemaphoreTake(wifi_alive, portMAX_DELAY);
|
||||||
|
printf("%s: started\n\r", __func__);
|
||||||
|
printf("%s: (Re)connecting to MQTT server %s ... ",__func__,
|
||||||
|
MQTT_HOST);
|
||||||
|
ret = ConnectNetwork(&network, MQTT_HOST, MQTT_PORT);
|
||||||
|
if( ret ){
|
||||||
|
printf("error: %d\n\r", ret);
|
||||||
|
taskYIELD();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("done\n\r");
|
||||||
|
NewMQTTClient(&client, &network, 5000, mqtt_buf, 100,
|
||||||
|
mqtt_readbuf, 100);
|
||||||
|
|
||||||
|
data.willFlag = 0;
|
||||||
|
data.MQTTVersion = 3;
|
||||||
|
data.clientID.cstring = mqtt_client_id;
|
||||||
|
data.username.cstring = MQTT_USER;
|
||||||
|
data.password.cstring = MQTT_PASS;
|
||||||
|
data.keepAliveInterval = 10;
|
||||||
|
data.cleansession = 0;
|
||||||
|
printf("Send MQTT connect ... ");
|
||||||
|
ret = MQTTConnect(&client, &data);
|
||||||
|
if(ret){
|
||||||
|
printf("error: %d\n\r", ret);
|
||||||
|
DisconnectNetwork(&network);
|
||||||
|
taskYIELD();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("done\r\n");
|
||||||
|
MQTTSubscribe(&client, "/esptopic", QOS1, topic_received);
|
||||||
|
xQueueReset(publish_queue);
|
||||||
|
|
||||||
|
while(1){
|
||||||
|
|
||||||
|
char msg[PUB_MSG_LEN - 1] = "\0";
|
||||||
|
while(xQueueReceive(publish_queue, (void *)msg, 0) ==
|
||||||
|
pdTRUE){
|
||||||
|
printf("got message to publish\r\n");
|
||||||
|
MQTTMessage message;
|
||||||
|
message.payload = msg;
|
||||||
|
message.payloadlen = PUB_MSG_LEN;
|
||||||
|
message.dup = 0;
|
||||||
|
message.qos = QOS1;
|
||||||
|
message.retained = 0;
|
||||||
|
ret = MQTTPublish(&client, "/beat", &message);
|
||||||
|
if (ret != SUCCESS ){
|
||||||
|
printf("error while publishing message: %d\n", ret );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = MQTTYield(&client, 1000);
|
||||||
|
if (ret == DISCONNECTED)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("Connection dropped, request restart\n\r");
|
||||||
|
taskYIELD();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wifi_task(void *pvParameters)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
uint8_t retries = 30;
|
||||||
|
struct sdk_station_config config = {
|
||||||
|
.ssid = WIFI_SSID,
|
||||||
|
.password = WIFI_PASS,
|
||||||
|
};
|
||||||
|
|
||||||
|
printf("WiFi: connecting to WiFi\n\r");
|
||||||
|
sdk_wifi_set_opmode(STATION_MODE);
|
||||||
|
sdk_wifi_station_set_config(&config);
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
while ((status != STATION_GOT_IP) && (retries)){
|
||||||
|
status = sdk_wifi_station_get_connect_status();
|
||||||
|
printf("%s: status = %d\n\r", __func__, status );
|
||||||
|
if( status == STATION_WRONG_PASSWORD ){
|
||||||
|
printf("WiFi: wrong password\n\r");
|
||||||
|
break;
|
||||||
|
} else if( status == STATION_NO_AP_FOUND ) {
|
||||||
|
printf("WiFi: AP not found\n\r");
|
||||||
|
break;
|
||||||
|
} else if( status == STATION_CONNECT_FAIL ) {
|
||||||
|
printf("WiFi: connection failed\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vTaskDelay( 1000 / portTICK_RATE_MS );
|
||||||
|
--retries;
|
||||||
|
}
|
||||||
|
if (status == STATION_GOT_IP) {
|
||||||
|
printf("WiFi: Connected\n\r");
|
||||||
|
xSemaphoreGive( wifi_alive );
|
||||||
|
taskYIELD();
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((status = sdk_wifi_station_get_connect_status()) == STATION_GOT_IP) {
|
||||||
|
xSemaphoreGive( wifi_alive );
|
||||||
|
taskYIELD();
|
||||||
|
}
|
||||||
|
printf("WiFi: disconnected\n\r");
|
||||||
|
sdk_wifi_station_disconnect();
|
||||||
|
vTaskDelay( 1000 / portTICK_RATE_MS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void user_init(void)
|
||||||
|
{
|
||||||
|
uart_set_baud(0, 115200);
|
||||||
|
printf("SDK version:%s\n", sdk_system_get_sdk_version());
|
||||||
|
|
||||||
|
vSemaphoreCreateBinary(wifi_alive);
|
||||||
|
publish_queue = xQueueCreate(3, PUB_MSG_LEN);
|
||||||
|
xTaskCreate(&wifi_task, (int8_t *)"wifi_task", 256, NULL, 2, NULL);
|
||||||
|
xTaskCreate(&beat_task, (int8_t *)"beat_task", 256, NULL, 3, NULL);
|
||||||
|
xTaskCreate(&mqtt_task, (int8_t *)"mqtt_task", 1024, NULL, 4, NULL);
|
||||||
|
}
|
|
@ -14,13 +14,15 @@
|
||||||
#include "ssid_config.h"
|
#include "ssid_config.h"
|
||||||
|
|
||||||
#include "ota-tftp.h"
|
#include "ota-tftp.h"
|
||||||
#include "rboot-ota.h"
|
#include "rboot-integration.h"
|
||||||
|
#include "rboot.h"
|
||||||
|
#include "rboot-api.h"
|
||||||
|
|
||||||
void user_init(void)
|
void user_init(void)
|
||||||
{
|
{
|
||||||
uart_set_baud(0, 115200);
|
uart_set_baud(0, 115200);
|
||||||
|
|
||||||
rboot_config_t conf = rboot_get_config();
|
rboot_config conf = rboot_get_config();
|
||||||
printf("\r\n\r\nOTA Basic demo.\r\nCurrently running on flash slot %d / %d.\r\n\r\n",
|
printf("\r\n\r\nOTA Basic demo.\r\nCurrently running on flash slot %d / %d.\r\n\r\n",
|
||||||
conf.current_rom, conf.count);
|
conf.current_rom, conf.count);
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ void tls_server_task(void *pvParameters)
|
||||||
strlen(pers))) != 0)
|
strlen(pers))) != 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok\n");
|
printf(" ok\n");
|
||||||
|
@ -99,7 +99,7 @@ void tls_server_task(void *pvParameters)
|
||||||
if(ret < 0)
|
if(ret < 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok (%d skipped)\n", ret);
|
printf(" ok (%d skipped)\n", ret);
|
||||||
|
@ -109,7 +109,7 @@ void tls_server_task(void *pvParameters)
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret);
|
printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret);
|
||||||
while(1) { } /*todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok\n");
|
printf(" ok\n");
|
||||||
|
@ -134,7 +134,7 @@ void tls_server_task(void *pvParameters)
|
||||||
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
|
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
|
||||||
{
|
{
|
||||||
printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret );
|
printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret );
|
||||||
while(1) { }
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||||
|
|
|
@ -1,43 +1,47 @@
|
||||||
#include "FreeRTOS.h"
|
#include "FreeRTOS.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
#include "math.h"
|
||||||
|
|
||||||
#include "onewire/onewire.h"
|
|
||||||
#include "ds18b20.h"
|
#include "ds18b20.h"
|
||||||
|
|
||||||
#define DS1820_WRITE_SCRATCHPAD 0x4E
|
#define DS18B20_WRITE_SCRATCHPAD 0x4E
|
||||||
#define DS1820_READ_SCRATCHPAD 0xBE
|
#define DS18B20_READ_SCRATCHPAD 0xBE
|
||||||
#define DS1820_COPY_SCRATCHPAD 0x48
|
#define DS18B20_COPY_SCRATCHPAD 0x48
|
||||||
#define DS1820_READ_EEPROM 0xB8
|
#define DS18B20_READ_EEPROM 0xB8
|
||||||
#define DS1820_READ_PWRSUPPLY 0xB4
|
#define DS18B20_READ_PWRSUPPLY 0xB4
|
||||||
#define DS1820_SEARCHROM 0xF0
|
#define DS18B20_SEARCHROM 0xF0
|
||||||
#define DS1820_SKIP_ROM 0xCC
|
#define DS18B20_SKIP_ROM 0xCC
|
||||||
#define DS1820_READROM 0x33
|
#define DS18B20_READROM 0x33
|
||||||
#define DS1820_MATCHROM 0x55
|
#define DS18B20_MATCHROM 0x55
|
||||||
#define DS1820_ALARMSEARCH 0xEC
|
#define DS18B20_ALARMSEARCH 0xEC
|
||||||
#define DS1820_CONVERT_T 0x44
|
#define DS18B20_CONVERT_T 0x44
|
||||||
|
|
||||||
|
#define os_sleep_ms(x) vTaskDelay(((x) + portTICK_RATE_MS - 1) / portTICK_RATE_MS)
|
||||||
|
|
||||||
uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) {
|
uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) {
|
||||||
|
onewire_addr_t addr;
|
||||||
uint8_t addr[8];
|
onewire_search_t search;
|
||||||
uint8_t sensor_id = 0;
|
uint8_t sensor_id = 0;
|
||||||
onewire_reset_search(pin);
|
|
||||||
|
|
||||||
while(onewire_search(pin, addr)){
|
onewire_search_start(&search);
|
||||||
uint8_t crc = onewire_crc8(addr, 7);
|
|
||||||
if (crc != addr[7]){
|
while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) {
|
||||||
printf("CRC check failed: %02X %02X\n", addr[7], crc);
|
uint8_t crc = onewire_crc8((uint8_t *)&addr, 7);
|
||||||
|
if (crc != (addr >> 56)){
|
||||||
|
printf("CRC check failed: %02X %02X\n", (unsigned)(addr >> 56), crc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
onewire_reset(pin);
|
onewire_reset(pin);
|
||||||
onewire_select(pin, addr);
|
onewire_select(pin, addr);
|
||||||
onewire_write(pin, DS1820_CONVERT_T, ONEWIRE_DEFAULT_POWER);
|
onewire_write(pin, DS18B20_CONVERT_T);
|
||||||
|
|
||||||
|
onewire_power(pin);
|
||||||
vTaskDelay(750 / portTICK_RATE_MS);
|
vTaskDelay(750 / portTICK_RATE_MS);
|
||||||
|
|
||||||
onewire_reset(pin);
|
onewire_reset(pin);
|
||||||
onewire_select(pin, addr);
|
onewire_select(pin, addr);
|
||||||
onewire_write(pin, DS1820_READ_SCRATCHPAD, ONEWIRE_DEFAULT_POWER);
|
onewire_write(pin, DS18B20_READ_SCRATCHPAD);
|
||||||
|
|
||||||
uint8_t get[10];
|
uint8_t get[10];
|
||||||
|
|
||||||
|
@ -71,15 +75,15 @@ uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) {
|
||||||
float ds18b20_read_single(uint8_t pin) {
|
float ds18b20_read_single(uint8_t pin) {
|
||||||
|
|
||||||
onewire_reset(pin);
|
onewire_reset(pin);
|
||||||
|
onewire_skip_rom(pin);
|
||||||
|
onewire_write(pin, DS18B20_CONVERT_T);
|
||||||
|
|
||||||
onewire_write(pin, DS1820_SKIP_ROM, ONEWIRE_DEFAULT_POWER);
|
onewire_power(pin);
|
||||||
onewire_write(pin, DS1820_CONVERT_T, ONEWIRE_DEFAULT_POWER);
|
|
||||||
|
|
||||||
vTaskDelay(750 / portTICK_RATE_MS);
|
vTaskDelay(750 / portTICK_RATE_MS);
|
||||||
|
|
||||||
onewire_reset(pin);
|
onewire_reset(pin);
|
||||||
onewire_write(pin, DS1820_SKIP_ROM, ONEWIRE_DEFAULT_POWER);
|
onewire_skip_rom(pin);
|
||||||
onewire_write(pin, DS1820_READ_SCRATCHPAD, ONEWIRE_DEFAULT_POWER);
|
onewire_write(pin, DS18B20_READ_SCRATCHPAD);
|
||||||
|
|
||||||
uint8_t get[10];
|
uint8_t get[10];
|
||||||
|
|
||||||
|
@ -106,3 +110,114 @@ float ds18b20_read_single(uint8_t pin) {
|
||||||
return temperature;
|
return temperature;
|
||||||
//printf("Got a DS18B20 Reading: %d.%02d\n", (int)temperature, (int)(temperature - (int)temperature) * 100);
|
//printf("Got a DS18B20 Reading: %d.%02d\n", (int)temperature, (int)(temperature - (int)temperature) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ds18b20_measure(int pin, ds18b20_addr_t addr, bool wait) {
|
||||||
|
if (!onewire_reset(pin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (addr == DS18B20_ANY) {
|
||||||
|
onewire_skip_rom(pin);
|
||||||
|
} else {
|
||||||
|
onewire_select(pin, addr);
|
||||||
|
}
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
onewire_write(pin, DS18B20_CONVERT_T);
|
||||||
|
// For parasitic devices, power must be applied within 10us after issuing
|
||||||
|
// the convert command.
|
||||||
|
onewire_power(pin);
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
os_sleep_ms(750);
|
||||||
|
onewire_depower(pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ds18b20_read_scratchpad(int pin, ds18b20_addr_t addr, uint8_t *buffer) {
|
||||||
|
uint8_t crc;
|
||||||
|
uint8_t expected_crc;
|
||||||
|
|
||||||
|
if (!onewire_reset(pin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (addr == DS18B20_ANY) {
|
||||||
|
onewire_skip_rom(pin);
|
||||||
|
} else {
|
||||||
|
onewire_select(pin, addr);
|
||||||
|
}
|
||||||
|
onewire_write(pin, DS18B20_READ_SCRATCHPAD);
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
buffer[i] = onewire_read(pin);
|
||||||
|
}
|
||||||
|
crc = onewire_read(pin);
|
||||||
|
|
||||||
|
expected_crc = onewire_crc8(buffer, 8);
|
||||||
|
if (crc != expected_crc) {
|
||||||
|
printf("CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ds18b20_read_temperature(int pin, ds18b20_addr_t addr) {
|
||||||
|
uint8_t scratchpad[8];
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
if (!ds18b20_read_scratchpad(pin, addr, scratchpad)) {
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = scratchpad[1] << 8 | scratchpad[0];
|
||||||
|
|
||||||
|
return ((float)temp * 625.0)/10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ds18b20_measure_and_read(int pin, ds18b20_addr_t addr) {
|
||||||
|
if (!ds18b20_measure(pin, addr, true)) {
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
return ds18b20_read_temperature(pin, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ds18b20_measure_and_read_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list) {
|
||||||
|
if (!ds18b20_measure(pin, DS18B20_ANY, true)) {
|
||||||
|
for (int i=0; i < addr_count; i++) {
|
||||||
|
result_list[i] = NAN;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ds18b20_read_temp_multi(pin, addr_list, addr_count, result_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ds18b20_scan_devices(int pin, ds18b20_addr_t *addr_list, int addr_count) {
|
||||||
|
onewire_search_t search;
|
||||||
|
onewire_addr_t addr;
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
onewire_search_start(&search);
|
||||||
|
while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) {
|
||||||
|
if (found < addr_count) {
|
||||||
|
addr_list[found] = addr;
|
||||||
|
}
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ds18b20_read_temp_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list) {
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < addr_count; i++) {
|
||||||
|
result_list[i] = ds18b20_read_temperature(pin, addr_list[i]);
|
||||||
|
if (isnan(result_list[i])) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,139 @@
|
||||||
#ifndef DRIVER_DS18B20_H_
|
#ifndef DRIVER_DS18B20_H_
|
||||||
#define DRIVER_DS18B20_H_
|
#define DRIVER_DS18B20_H_
|
||||||
|
|
||||||
|
#include "onewire/onewire.h"
|
||||||
|
|
||||||
|
/** @file ds18b20.h
|
||||||
|
*
|
||||||
|
* Communicate with the DS18B20 family of one-wire temperature sensor ICs.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef onewire_addr_t ds18b20_addr_t;
|
||||||
|
|
||||||
|
/** An address value which can be used to indicate "any device on the bus" */
|
||||||
|
#define DS18B20_ANY ONEWIRE_NONE
|
||||||
|
|
||||||
|
/** Find the addresses of all DS18B20 devices on the bus.
|
||||||
|
*
|
||||||
|
* Scans the bus for all devices and places their addresses in the supplied
|
||||||
|
* array. If there are more than `addr_count` devices on the bus, only the
|
||||||
|
* first `addr_count` are recorded.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the DS18B20 bus
|
||||||
|
* @param addr_list A pointer to an array of ds18b20_addr_t values. This
|
||||||
|
* will be populated with the addresses of the found
|
||||||
|
* devices.
|
||||||
|
* @param addr_count Number of slots in the `addr_list` array. At most this
|
||||||
|
* many addresses will be returned.
|
||||||
|
*
|
||||||
|
* @returns The number of devices found. Note that this may be less than,
|
||||||
|
* equal to, or more than `addr_count`, depending on how many DS18B20 devices
|
||||||
|
* are attached to the bus.
|
||||||
|
*/
|
||||||
|
int ds18b20_scan_devices(int pin, ds18b20_addr_t *addr_list, int addr_count);
|
||||||
|
|
||||||
|
/** Tell one or more sensors to perform a temperature measurement and
|
||||||
|
* conversion (CONVERT_T) operation. This operation can take up to 750ms to
|
||||||
|
* complete.
|
||||||
|
*
|
||||||
|
* If `wait=true`, this routine will automatically drive the pin high for the
|
||||||
|
* necessary 750ms after issuing the command to ensure parasitically-powered
|
||||||
|
* devices have enough power to perform the conversion operation (for
|
||||||
|
* non-parasitically-powered devices, this is not necessary but does not
|
||||||
|
* hurt). If `wait=false`, this routine will drive the pin high, but will
|
||||||
|
* then return immediately. It is up to the caller to wait the requisite time
|
||||||
|
* and then depower the bus using onewire_depower() or by issuing another
|
||||||
|
* command once conversion is done.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the DS18B20 device
|
||||||
|
* @param addr The 64-bit address of the device on the bus. This can be set
|
||||||
|
* to ::DS18B20_ANY to send the command to all devices on the bus
|
||||||
|
* at the same time.
|
||||||
|
* @param wait Whether to wait for the necessary 750ms for the DS18B20 to
|
||||||
|
* finish performing the conversion before returning to the
|
||||||
|
* caller (You will normally want to do this).
|
||||||
|
*
|
||||||
|
* @returns `true` if the command was successfully issued, or `false` on error.
|
||||||
|
*/
|
||||||
|
bool ds18b20_measure(int pin, ds18b20_addr_t addr, bool wait);
|
||||||
|
|
||||||
|
/** Read the value from the last CONVERT_T operation.
|
||||||
|
*
|
||||||
|
* This should be called after ds18b20_measure() to fetch the result of the
|
||||||
|
* temperature measurement.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the DS18B20 device
|
||||||
|
* @param addr The 64-bit address of the device to read. This can be set
|
||||||
|
* to ::DS18B20_ANY to read any device on the bus (but note
|
||||||
|
* that this will only work if there is exactly one device
|
||||||
|
* connected, or they will corrupt each others' transmissions)
|
||||||
|
*
|
||||||
|
* @returns The temperature in degrees Celsius, or NaN if there was an error.
|
||||||
|
*/
|
||||||
|
float ds18b20_read_temperature(int pin, ds18b20_addr_t addr);
|
||||||
|
|
||||||
|
/** Read the value from the last CONVERT_T operation for multiple devices.
|
||||||
|
*
|
||||||
|
* This should be called after ds18b20_measure() to fetch the result of the
|
||||||
|
* temperature measurement.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the DS18B20 bus
|
||||||
|
* @param addr_list A list of addresses for devices to read.
|
||||||
|
* @param addr_count The number of entries in `addr_list`.
|
||||||
|
* @param result_list An array of floats to hold the returned temperature
|
||||||
|
* values. It should have at least `addr_count` entries.
|
||||||
|
*
|
||||||
|
* @returns `true` if all temperatures were fetched successfully, or `false`
|
||||||
|
* if one or more had errors (the temperature for erroring devices will be
|
||||||
|
* returned as NaN).
|
||||||
|
*/
|
||||||
|
bool ds18b20_read_temp_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list);
|
||||||
|
|
||||||
|
/** Perform a ds18b20_measure() followed by ds18b20_read_temperature()
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the DS18B20 device
|
||||||
|
* @param addr The 64-bit address of the device to read. This can be set
|
||||||
|
* to ::DS18B20_ANY to read any device on the bus (but note
|
||||||
|
* that this will only work if there is exactly one device
|
||||||
|
* connected, or they will corrupt each others' transmissions)
|
||||||
|
*
|
||||||
|
* @returns The temperature in degrees Celsius, or NaN if there was an error.
|
||||||
|
*/
|
||||||
|
float ds18b20_measure_and_read(int pin, ds18b20_addr_t addr);
|
||||||
|
|
||||||
|
/** Perform a ds18b20_measure() followed by ds18b20_read_temp_multi()
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the DS18B20 bus
|
||||||
|
* @param addr_list A list of addresses for devices to read.
|
||||||
|
* @param addr_count The number of entries in `addr_list`.
|
||||||
|
* @param result_list An array of floats to hold the returned temperature
|
||||||
|
* values. It should have at least `addr_count` entries.
|
||||||
|
*
|
||||||
|
* @returns `true` if all temperatures were fetched successfully, or `false`
|
||||||
|
* if one or more had errors (the temperature for erroring devices will be
|
||||||
|
* returned as NaN).
|
||||||
|
*/
|
||||||
|
bool ds18b20_measure_and_read_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list);
|
||||||
|
|
||||||
|
/** Read the scratchpad data for a particular DS18B20 device.
|
||||||
|
*
|
||||||
|
* This is not generally necessary to do directly. It is done automatically
|
||||||
|
* as part of ds18b20_read_temperature().
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the DS18B20 device
|
||||||
|
* @param addr The 64-bit address of the device to read. This can be set
|
||||||
|
* to ::DS18B20_ANY to read any device on the bus (but note
|
||||||
|
* that this will only work if there is exactly one device
|
||||||
|
* connected, or they will corrupt each others' transmissions)
|
||||||
|
* @param buffer An 8-byte buffer to hold the read data.
|
||||||
|
*
|
||||||
|
* @returns `true` if the data was read successfully, or `false` on error.
|
||||||
|
*/
|
||||||
|
bool ds18b20_read_scratchpad(int pin, ds18b20_addr_t addr, uint8_t *buffer);
|
||||||
|
|
||||||
|
// The following are obsolete/deprecated APIs
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t id;
|
uint8_t id;
|
||||||
float value;
|
float value;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0a0c22e0efcf2f8f71d7e16712f80b8f77326f72
|
Subproject commit a7ffc8f7396573bec401e0afcc073137522d5305
|
|
@ -1,206 +1,207 @@
|
||||||
#include "onewire.h"
|
#include "onewire.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "esp/gpio.h"
|
||||||
|
|
||||||
// global search state
|
#define ONEWIRE_SELECT_ROM 0x55
|
||||||
static unsigned char ROM_NO[ONEWIRE_NUM][8];
|
#define ONEWIRE_SKIP_ROM 0xcc
|
||||||
static uint8_t LastDiscrepancy[ONEWIRE_NUM];
|
#define ONEWIRE_SEARCH 0xf0
|
||||||
static uint8_t LastFamilyDiscrepancy[ONEWIRE_NUM];
|
|
||||||
static uint8_t LastDeviceFlag[ONEWIRE_NUM];
|
|
||||||
|
|
||||||
void onewire_init(uint8_t pin)
|
// Waits up to `max_wait` microseconds for the specified pin to go high.
|
||||||
{
|
// Returns true if successful, false if the bus never comes high (likely
|
||||||
gpio_enable(pin, GPIO_INPUT);
|
// shorted).
|
||||||
onewire_reset_search(pin);
|
static inline bool _onewire_wait_for_bus(int pin, int max_wait) {
|
||||||
|
bool state;
|
||||||
|
for (int i = 0; i < ((max_wait + 4) / 5); i++) {
|
||||||
|
if (gpio_read(pin)) break;
|
||||||
|
sdk_os_delay_us(5);
|
||||||
|
}
|
||||||
|
state = gpio_read(pin);
|
||||||
|
// Wait an extra 1us to make sure the devices have an adequate recovery
|
||||||
|
// time before we drive things low again.
|
||||||
|
sdk_os_delay_us(1);
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the onewire reset function. We will wait up to 250uS for
|
// Perform the onewire reset function. We will wait up to 250uS for
|
||||||
// the bus to come high, if it doesn't then it is broken or shorted
|
// the bus to come high, if it doesn't then it is broken or shorted
|
||||||
// and we return a 0;
|
// and we return false;
|
||||||
//
|
//
|
||||||
// Returns 1 if a device asserted a presence pulse, 0 otherwise.
|
// Returns true if a device asserted a presence pulse, false otherwise.
|
||||||
//
|
//
|
||||||
uint8_t onewire_reset(uint8_t pin)
|
bool onewire_reset(int pin) {
|
||||||
{
|
bool r;
|
||||||
uint8_t r;
|
|
||||||
uint8_t retries = 125;
|
|
||||||
|
|
||||||
noInterrupts();
|
gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
|
||||||
DIRECT_MODE_INPUT(pin);
|
gpio_write(pin, 1);
|
||||||
interrupts();
|
// wait until the wire is high... just in case
|
||||||
// wait until the wire is high... just in case
|
if (!_onewire_wait_for_bus(pin, 250)) return false;
|
||||||
do {
|
|
||||||
if (--retries == 0) return 0;
|
|
||||||
delayMicroseconds(2);
|
|
||||||
} while ( !DIRECT_READ(pin));
|
|
||||||
|
|
||||||
noInterrupts();
|
gpio_write(pin, 0);
|
||||||
DIRECT_WRITE_LOW(pin);
|
sdk_os_delay_us(480);
|
||||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
|
||||||
interrupts();
|
taskENTER_CRITICAL();
|
||||||
delayMicroseconds(480);
|
gpio_write(pin, 1); // allow it to float
|
||||||
noInterrupts();
|
sdk_os_delay_us(70);
|
||||||
DIRECT_MODE_INPUT(pin); // allow it to float
|
r = !gpio_read(pin);
|
||||||
delayMicroseconds(70);
|
taskEXIT_CRITICAL();
|
||||||
r = !DIRECT_READ(pin);
|
|
||||||
interrupts();
|
// Wait for all devices to finish pulling the bus low before returning
|
||||||
delayMicroseconds(410);
|
if (!_onewire_wait_for_bus(pin, 410)) return false;
|
||||||
return r;
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a bit. Port and bit is used to cut lookup time and provide
|
static bool _onewire_write_bit(int pin, bool v) {
|
||||||
// more certain timing.
|
if (!_onewire_wait_for_bus(pin, 10)) return false;
|
||||||
//
|
if (v) {
|
||||||
static void onewire_write_bit(uint8_t pin, uint8_t v)
|
taskENTER_CRITICAL();
|
||||||
{
|
gpio_write(pin, 0); // drive output low
|
||||||
if (v & 1) {
|
sdk_os_delay_us(10);
|
||||||
noInterrupts();
|
gpio_write(pin, 1); // allow output high
|
||||||
DIRECT_WRITE_LOW(pin);
|
taskEXIT_CRITICAL();
|
||||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
sdk_os_delay_us(55);
|
||||||
delayMicroseconds(10);
|
} else {
|
||||||
DIRECT_WRITE_HIGH(pin); // drive output high
|
taskENTER_CRITICAL();
|
||||||
interrupts();
|
gpio_write(pin, 0); // drive output low
|
||||||
delayMicroseconds(55);
|
sdk_os_delay_us(65);
|
||||||
} else {
|
gpio_write(pin, 1); // allow output high
|
||||||
noInterrupts();
|
taskEXIT_CRITICAL();
|
||||||
DIRECT_WRITE_LOW(pin);
|
}
|
||||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
sdk_os_delay_us(1);
|
||||||
delayMicroseconds(65);
|
|
||||||
DIRECT_WRITE_HIGH(pin); // drive output high
|
return true;
|
||||||
interrupts();
|
|
||||||
delayMicroseconds(5);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a bit. Port and bit is used to cut lookup time and provide
|
static int _onewire_read_bit(int pin) {
|
||||||
// more certain timing.
|
int r;
|
||||||
//
|
|
||||||
static uint8_t onewire_read_bit(uint8_t pin)
|
|
||||||
{
|
|
||||||
uint8_t r;
|
|
||||||
|
|
||||||
noInterrupts();
|
if (!_onewire_wait_for_bus(pin, 10)) return -1;
|
||||||
DIRECT_MODE_OUTPUT(pin);
|
taskENTER_CRITICAL();
|
||||||
DIRECT_WRITE_LOW(pin);
|
gpio_write(pin, 0);
|
||||||
delayMicroseconds(3);
|
sdk_os_delay_us(2);
|
||||||
DIRECT_MODE_INPUT(pin); // let pin float, pull up will raise
|
gpio_write(pin, 1); // let pin float, pull up will raise
|
||||||
delayMicroseconds(10);
|
sdk_os_delay_us(11);
|
||||||
r = DIRECT_READ(pin);
|
r = gpio_read(pin); // Must sample within 15us of start
|
||||||
interrupts();
|
taskEXIT_CRITICAL();
|
||||||
delayMicroseconds(53);
|
sdk_os_delay_us(48);
|
||||||
return r;
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a byte. The writing code uses the active drivers to raise the
|
// Write a byte. The writing code uses open-drain mode and expects the pullup
|
||||||
// pin high, if you need power after the write (e.g. DS18S20 in
|
// resistor to pull the line high when not driven low. If you need strong
|
||||||
// parasite power mode) then set 'power' to 1, otherwise the pin will
|
// power after the write (e.g. DS18B20 in parasite power mode) then call
|
||||||
// go tri-state at the end of the write to avoid heating in a short or
|
// onewire_power() after this is complete to actively drive the line high.
|
||||||
// other mishap.
|
|
||||||
//
|
//
|
||||||
void onewire_write(uint8_t pin, uint8_t v, uint8_t power /* = 0 */) {
|
bool onewire_write(int pin, uint8_t v) {
|
||||||
uint8_t bitMask;
|
uint8_t bitMask;
|
||||||
|
|
||||||
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
||||||
onewire_write_bit(pin, (bitMask & v)?1:0);
|
if (!_onewire_write_bit(pin, (bitMask & v))) {
|
||||||
}
|
return false;
|
||||||
if ( !power) {
|
}
|
||||||
noInterrupts();
|
}
|
||||||
DIRECT_MODE_INPUT(pin);
|
return true;
|
||||||
DIRECT_WRITE_LOW(pin);
|
|
||||||
interrupts();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power /* = 0 */) {
|
bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count) {
|
||||||
uint16_t i;
|
size_t i;
|
||||||
for (i = 0 ; i < count ; i++)
|
|
||||||
onewire_write(pin, buf[i], ONEWIRE_DEFAULT_POWER);
|
for (i = 0 ; i < count ; i++) {
|
||||||
if (!power) {
|
if (!onewire_write(pin, buf[i])) {
|
||||||
noInterrupts();
|
return false;
|
||||||
DIRECT_MODE_INPUT(pin);
|
}
|
||||||
DIRECT_WRITE_LOW(pin);
|
}
|
||||||
interrupts();
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a byte
|
// Read a byte
|
||||||
//
|
//
|
||||||
uint8_t onewire_read(uint8_t pin) {
|
int onewire_read(int pin) {
|
||||||
uint8_t bitMask;
|
uint8_t bitMask;
|
||||||
uint8_t r = 0;
|
int r = 0;
|
||||||
|
int bit;
|
||||||
|
|
||||||
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
||||||
if (onewire_read_bit(pin)) r |= bitMask;
|
bit = _onewire_read_bit(pin);
|
||||||
}
|
if (bit < 0) {
|
||||||
return r;
|
return -1;
|
||||||
|
} else if (bit) {
|
||||||
|
r |= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count) {
|
bool onewire_read_bytes(int pin, uint8_t *buf, size_t count) {
|
||||||
uint16_t i;
|
size_t i;
|
||||||
for (i = 0 ; i < count ; i++)
|
int b;
|
||||||
buf[i] = onewire_read(pin);
|
|
||||||
|
for (i = 0 ; i < count ; i++) {
|
||||||
|
b = onewire_read(pin);
|
||||||
|
if (b < 0) return false;
|
||||||
|
buf[i] = b;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a ROM select
|
bool onewire_select(int pin, onewire_addr_t addr) {
|
||||||
//
|
|
||||||
void onewire_select(uint8_t pin, const uint8_t rom[8])
|
|
||||||
{
|
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
onewire_write(pin, 0x55, ONEWIRE_DEFAULT_POWER); // Choose ROM
|
if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) onewire_write(pin, rom[i], ONEWIRE_DEFAULT_POWER);
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (!onewire_write(pin, addr & 0xff)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
addr >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a ROM skip
|
bool onewire_skip_rom(int pin) {
|
||||||
//
|
return onewire_write(pin, ONEWIRE_SKIP_ROM);
|
||||||
void onewire_skip(uint8_t pin)
|
|
||||||
{
|
|
||||||
onewire_write(pin, 0xCC, ONEWIRE_DEFAULT_POWER); // Skip ROM
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onewire_depower(uint8_t pin)
|
bool onewire_power(int pin) {
|
||||||
{
|
// Make sure the bus is not being held low before driving it high, or we
|
||||||
noInterrupts();
|
// may end up shorting ourselves out.
|
||||||
DIRECT_MODE_INPUT(pin);
|
if (!_onewire_wait_for_bus(pin, 10)) return false;
|
||||||
interrupts();
|
|
||||||
|
gpio_enable(pin, GPIO_OUTPUT);
|
||||||
|
gpio_write(pin, 1);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// You need to use this function to start a search again from the beginning.
|
void onewire_depower(int pin) {
|
||||||
// You do not need to do it for the first search, though you could.
|
gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
|
||||||
//
|
|
||||||
void onewire_reset_search(uint8_t pin)
|
|
||||||
{
|
|
||||||
// reset the search state
|
|
||||||
LastDiscrepancy[pin] = 0;
|
|
||||||
LastDeviceFlag[pin] = 0;
|
|
||||||
LastFamilyDiscrepancy[pin] = 0;
|
|
||||||
int i;
|
|
||||||
for(i = 7; ; i--) {
|
|
||||||
ROM_NO[pin][i] = 0;
|
|
||||||
if ( i == 0) break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the search to find the device type 'family_code' on the next call
|
void onewire_search_start(onewire_search_t *search) {
|
||||||
// to search(*newAddr) if it is present.
|
// reset the search state
|
||||||
//
|
memset(search, 0, sizeof(*search));
|
||||||
void onewire_target_search(uint8_t pin, uint8_t family_code)
|
|
||||||
{
|
|
||||||
// set the search state to find SearchFamily type devices
|
|
||||||
ROM_NO[pin][0] = family_code;
|
|
||||||
uint8_t i;
|
|
||||||
for (i = 1; i < 8; i++)
|
|
||||||
ROM_NO[pin][i] = 0;
|
|
||||||
LastDiscrepancy[pin] = 64;
|
|
||||||
LastFamilyDiscrepancy[pin] = 0;
|
|
||||||
LastDeviceFlag[pin] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform a search. If this function returns a '1' then it has
|
void onewire_search_prefix(onewire_search_t *search, uint8_t family_code) {
|
||||||
// enumerated the next device and you may retrieve the ROM from the
|
uint8_t i;
|
||||||
// OneWire::address variable. If there are no devices, no further
|
|
||||||
|
search->rom_no[0] = family_code;
|
||||||
|
for (i = 1; i < 8; i++) {
|
||||||
|
search->rom_no[i] = 0;
|
||||||
|
}
|
||||||
|
search->last_discrepancy = 64;
|
||||||
|
search->last_device_found = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a search. If the next device has been successfully enumerated, its
|
||||||
|
// ROM address will be returned. If there are no devices, no further
|
||||||
// devices, or something horrible happens in the middle of the
|
// devices, or something horrible happens in the middle of the
|
||||||
// enumeration then a 0 is returned. If a new device is found then
|
// enumeration then ONEWIRE_NONE is returned. Use OneWire::reset_search() to
|
||||||
// its address is copied to newAddr. Use OneWire::reset_search() to
|
|
||||||
// start over.
|
// start over.
|
||||||
//
|
//
|
||||||
// --- Replaced by the one from the Dallas Semiconductor web site ---
|
// --- Replaced by the one from the Dallas Semiconductor web site ---
|
||||||
|
@ -210,129 +211,119 @@ void onewire_target_search(uint8_t pin, uint8_t family_code)
|
||||||
// Return 1 : device found, ROM number in ROM_NO buffer
|
// Return 1 : device found, ROM number in ROM_NO buffer
|
||||||
// 0 : device not found, end of search
|
// 0 : device not found, end of search
|
||||||
//
|
//
|
||||||
uint8_t onewire_search(uint8_t pin, uint8_t *newAddr)
|
onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) {
|
||||||
{
|
//TODO: add more checking for read/write errors
|
||||||
uint8_t id_bit_number;
|
uint8_t id_bit_number;
|
||||||
uint8_t last_zero, rom_byte_number, search_result;
|
uint8_t last_zero, search_result;
|
||||||
uint8_t id_bit, cmp_id_bit;
|
int rom_byte_number;
|
||||||
|
uint8_t id_bit, cmp_id_bit;
|
||||||
|
onewire_addr_t addr;
|
||||||
|
unsigned char rom_byte_mask;
|
||||||
|
bool search_direction;
|
||||||
|
|
||||||
unsigned char rom_byte_mask, search_direction;
|
// initialize for search
|
||||||
|
id_bit_number = 1;
|
||||||
|
last_zero = 0;
|
||||||
|
rom_byte_number = 0;
|
||||||
|
rom_byte_mask = 1;
|
||||||
|
search_result = 0;
|
||||||
|
|
||||||
// initialize for search
|
// if the last call was not the last one
|
||||||
id_bit_number = 1;
|
if (!search->last_device_found) {
|
||||||
last_zero = 0;
|
// 1-Wire reset
|
||||||
rom_byte_number = 0;
|
if (!onewire_reset(pin)) {
|
||||||
rom_byte_mask = 1;
|
// reset the search
|
||||||
search_result = 0;
|
search->last_discrepancy = 0;
|
||||||
|
search->last_device_found = false;
|
||||||
|
return ONEWIRE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
// if the last call was not the last one
|
// issue the search command
|
||||||
if (!LastDeviceFlag[pin])
|
onewire_write(pin, ONEWIRE_SEARCH);
|
||||||
{
|
|
||||||
// 1-Wire reset
|
|
||||||
if (!onewire_reset(pin))
|
|
||||||
{
|
|
||||||
// reset the search
|
|
||||||
LastDiscrepancy[pin] = 0;
|
|
||||||
LastDeviceFlag[pin] = 0;
|
|
||||||
LastFamilyDiscrepancy[pin] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// issue the search command
|
// loop to do the search
|
||||||
onewire_write(pin, 0xF0, ONEWIRE_DEFAULT_POWER);
|
do {
|
||||||
|
// read a bit and its complement
|
||||||
|
id_bit = _onewire_read_bit(pin);
|
||||||
|
cmp_id_bit = _onewire_read_bit(pin);
|
||||||
|
|
||||||
// loop to do the search
|
// check for no devices on 1-wire
|
||||||
do
|
if ((id_bit < 0) || (cmp_id_bit < 0)) {
|
||||||
{
|
// Read error
|
||||||
// read a bit and its complement
|
break;
|
||||||
id_bit = onewire_read_bit(pin);
|
} else if ((id_bit == 1) && (cmp_id_bit == 1)) {
|
||||||
cmp_id_bit = onewire_read_bit(pin);
|
break;
|
||||||
|
} else {
|
||||||
|
// all devices coupled have 0 or 1
|
||||||
|
if (id_bit != cmp_id_bit) {
|
||||||
|
search_direction = id_bit; // bit write value for search
|
||||||
|
} else {
|
||||||
|
// if this discrepancy if before the Last Discrepancy
|
||||||
|
// on a previous next then pick the same as last time
|
||||||
|
if (id_bit_number < search->last_discrepancy) {
|
||||||
|
search_direction = ((search->rom_no[rom_byte_number] & rom_byte_mask) > 0);
|
||||||
|
} else {
|
||||||
|
// if equal to last pick 1, if not then pick 0
|
||||||
|
search_direction = (id_bit_number == search->last_discrepancy);
|
||||||
|
}
|
||||||
|
|
||||||
// check for no devices on 1-wire
|
// if 0 was picked then record its position in LastZero
|
||||||
if ((id_bit == 1) && (cmp_id_bit == 1))
|
if (!search_direction) {
|
||||||
break;
|
last_zero = id_bit_number;
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
// all devices coupled have 0 or 1
|
|
||||||
if (id_bit != cmp_id_bit)
|
|
||||||
search_direction = id_bit; // bit write value for search
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if this discrepancy if before the Last Discrepancy
|
|
||||||
// on a previous next then pick the same as last time
|
|
||||||
if (id_bit_number < LastDiscrepancy[pin])
|
|
||||||
search_direction = ((ROM_NO[pin][rom_byte_number] & rom_byte_mask) > 0);
|
|
||||||
else
|
|
||||||
// if equal to last pick 1, if not then pick 0
|
|
||||||
search_direction = (id_bit_number == LastDiscrepancy[pin]);
|
|
||||||
|
|
||||||
// if 0 was picked then record its position in LastZero
|
// set or clear the bit in the ROM byte rom_byte_number
|
||||||
if (search_direction == 0)
|
// with mask rom_byte_mask
|
||||||
{
|
if (search_direction) {
|
||||||
last_zero = id_bit_number;
|
search->rom_no[rom_byte_number] |= rom_byte_mask;
|
||||||
|
} else {
|
||||||
|
search->rom_no[rom_byte_number] &= ~rom_byte_mask;
|
||||||
|
}
|
||||||
|
|
||||||
// check for Last discrepancy in family
|
// serial number search direction write bit
|
||||||
if (last_zero < 9)
|
_onewire_write_bit(pin, search_direction);
|
||||||
LastFamilyDiscrepancy[pin] = last_zero;
|
|
||||||
}
|
// increment the byte counter id_bit_number
|
||||||
|
// and shift the mask rom_byte_mask
|
||||||
|
id_bit_number++;
|
||||||
|
rom_byte_mask <<= 1;
|
||||||
|
|
||||||
|
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
|
||||||
|
if (rom_byte_mask == 0) {
|
||||||
|
rom_byte_number++;
|
||||||
|
rom_byte_mask = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (rom_byte_number < 8); // loop until through all ROM bytes 0-7
|
||||||
|
|
||||||
|
// if the search was successful then
|
||||||
|
if (!(id_bit_number < 65)) {
|
||||||
|
// search successful so set last_discrepancy,last_device_found,search_result
|
||||||
|
search->last_discrepancy = last_zero;
|
||||||
|
|
||||||
|
// check for last device
|
||||||
|
if (search->last_discrepancy == 0) {
|
||||||
|
search->last_device_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set or clear the bit in the ROM byte rom_byte_number
|
search_result = 1;
|
||||||
// with mask rom_byte_mask
|
}
|
||||||
if (search_direction == 1)
|
}
|
||||||
ROM_NO[pin][rom_byte_number] |= rom_byte_mask;
|
|
||||||
else
|
|
||||||
ROM_NO[pin][rom_byte_number] &= ~rom_byte_mask;
|
|
||||||
|
|
||||||
// serial number search direction write bit
|
// if no device found then reset counters so next 'search' will be like a first
|
||||||
onewire_write_bit(pin, search_direction);
|
if (!search_result || !search->rom_no[0]) {
|
||||||
|
search->last_discrepancy = 0;
|
||||||
// increment the byte counter id_bit_number
|
search->last_device_found = false;
|
||||||
// and shift the mask rom_byte_mask
|
return ONEWIRE_NONE;
|
||||||
id_bit_number++;
|
} else {
|
||||||
rom_byte_mask <<= 1;
|
addr = 0;
|
||||||
|
for (rom_byte_number = 7; rom_byte_number >= 0; rom_byte_number--) {
|
||||||
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
|
addr = (addr << 8) | search->rom_no[rom_byte_number];
|
||||||
if (rom_byte_mask == 0)
|
}
|
||||||
{
|
//printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr);
|
||||||
rom_byte_number++;
|
}
|
||||||
rom_byte_mask = 1;
|
return addr;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
|
|
||||||
|
|
||||||
// if the search was successful then
|
|
||||||
if (!(id_bit_number < 65))
|
|
||||||
{
|
|
||||||
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
|
|
||||||
LastDiscrepancy[pin] = last_zero;
|
|
||||||
|
|
||||||
// check for last device
|
|
||||||
if (LastDiscrepancy[pin] == 0)
|
|
||||||
LastDeviceFlag[pin] = 1;
|
|
||||||
|
|
||||||
search_result = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no device found then reset counters so next 'search' will be like a first
|
|
||||||
if (!search_result || !ROM_NO[pin][0])
|
|
||||||
{
|
|
||||||
LastDiscrepancy[pin] = 0;
|
|
||||||
LastDeviceFlag[pin] = 0;
|
|
||||||
LastFamilyDiscrepancy[pin] = 0;
|
|
||||||
search_result = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (rom_byte_number = 0; rom_byte_number < 8; rom_byte_number++)
|
|
||||||
{
|
|
||||||
newAddr[rom_byte_number] = ROM_NO[pin][rom_byte_number];
|
|
||||||
//printf("Ok I found something at %d - %x...\n",rom_byte_number, newAddr[rom_byte_number]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return search_result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
|
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
|
||||||
|
@ -371,41 +362,38 @@ static const uint8_t dscrc_table[] = {
|
||||||
// compared to all those delayMicrosecond() calls. But I got
|
// compared to all those delayMicrosecond() calls. But I got
|
||||||
// confused, so I use this table from the examples.)
|
// confused, so I use this table from the examples.)
|
||||||
//
|
//
|
||||||
uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
|
uint8_t onewire_crc8(const uint8_t *data, uint8_t len) {
|
||||||
{
|
uint8_t crc = 0;
|
||||||
uint8_t crc = 0;
|
|
||||||
|
|
||||||
while (len--) {
|
while (len--) {
|
||||||
crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
|
crc = pgm_read_byte(dscrc_table + (crc ^ *data++));
|
||||||
}
|
}
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
//
|
//
|
||||||
// Compute a Dallas Semiconductor 8 bit CRC directly.
|
// Compute a Dallas Semiconductor 8 bit CRC directly.
|
||||||
// this is much slower, but much smaller, than the lookup table.
|
// this is much slower, but much smaller, than the lookup table.
|
||||||
//
|
//
|
||||||
uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
|
uint8_t onewire_crc8(const uint8_t *data, uint8_t len) {
|
||||||
{
|
uint8_t crc = 0;
|
||||||
uint8_t crc = 0;
|
|
||||||
|
|
||||||
while (len--) {
|
while (len--) {
|
||||||
uint8_t inbyte = *addr++;
|
uint8_t inbyte = *data++;
|
||||||
uint8_t i;
|
for (int i = 8; i; i--) {
|
||||||
for (i = 8; i; i--) {
|
uint8_t mix = (crc ^ inbyte) & 0x01;
|
||||||
uint8_t mix = (crc ^ inbyte) & 0x01;
|
crc >>= 1;
|
||||||
crc >>= 1;
|
if (mix) crc ^= 0x8C;
|
||||||
if (mix) crc ^= 0x8C;
|
inbyte >>= 1;
|
||||||
inbyte >>= 1;
|
}
|
||||||
}
|
}
|
||||||
}
|
return crc;
|
||||||
return crc;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Compute the 1-Wire CRC16 and compare it against the received CRC.
|
// Compute the 1-Wire CRC16 and compare it against the received CRC.
|
||||||
// Example usage (reading a DS2408):
|
// Example usage (reading a DS2408):
|
||||||
// // Put everything in a buffer so we can compute the CRC easily.
|
// // Put everything in a buffer so we can compute the CRC easily.
|
||||||
// uint8_t buf[13];
|
// uint8_t buf[13];
|
||||||
// buf[0] = 0xF0; // Read PIO Registers
|
// buf[0] = 0xF0; // Read PIO Registers
|
||||||
// buf[1] = 0x88; // LSB address
|
// buf[1] = 0x88; // LSB address
|
||||||
|
@ -423,9 +411,8 @@ uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
|
||||||
// *not* at a 16-bit integer.
|
// *not* at a 16-bit integer.
|
||||||
// @param crc - The crc starting value (optional)
|
// @param crc - The crc starting value (optional)
|
||||||
// @return 1, iff the CRC matches.
|
// @return 1, iff the CRC matches.
|
||||||
bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc)
|
bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv) {
|
||||||
{
|
uint16_t crc = ~onewire_crc16(input, len, crc_iv);
|
||||||
crc = ~onewire_crc16(input, len, crc);
|
|
||||||
return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1];
|
return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,8 +428,8 @@ bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inve
|
||||||
// @param len - How many bytes to use.
|
// @param len - How many bytes to use.
|
||||||
// @param crc - The crc starting value (optional)
|
// @param crc - The crc starting value (optional)
|
||||||
// @return The CRC16, as defined by Dallas Semiconductor.
|
// @return The CRC16, as defined by Dallas Semiconductor.
|
||||||
uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc)
|
uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv) {
|
||||||
{
|
uint16_t crc = crc_iv;
|
||||||
static const uint8_t oddparity[16] =
|
static const uint8_t oddparity[16] =
|
||||||
{ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
|
{ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
|
||||||
|
|
||||||
|
|
|
@ -4,135 +4,232 @@
|
||||||
#include <espressif/esp_misc.h> // sdk_os_delay_us
|
#include <espressif/esp_misc.h> // sdk_os_delay_us
|
||||||
#include "FreeRTOS.h"
|
#include "FreeRTOS.h"
|
||||||
|
|
||||||
// 1 for keeping the parasitic power on H
|
/** @file onewire.h
|
||||||
#define ONEWIRE_DEFAULT_POWER 1
|
*
|
||||||
|
* Routines to access devices using the Dallas Semiconductor 1-Wire(tm)
|
||||||
|
* protocol.
|
||||||
|
*/
|
||||||
|
|
||||||
// Maximum number of devices.
|
/** Select the table-lookup method of computing the 8-bit CRC
|
||||||
#define ONEWIRE_NUM 20
|
* by setting this to 1 during compilation. The lookup table enlarges code
|
||||||
|
* size by about 250 bytes. By default, a slower but very compact algorithm
|
||||||
// You can exclude certain features from OneWire. In theory, this
|
* is used.
|
||||||
// might save some space. In practice, the compiler automatically
|
*/
|
||||||
// removes unused code (technically, the linker, using -fdata-sections
|
|
||||||
// and -ffunction-sections when compiling, and Wl,--gc-sections
|
|
||||||
// when linking), so most of these will not result in any code size
|
|
||||||
// reduction. Well, unless you try to use the missing features
|
|
||||||
// and redesign your program to not need them! ONEWIRE_CRC8_TABLE
|
|
||||||
// is the exception, because it selects a fast but large algorithm
|
|
||||||
// or a small but slow algorithm.
|
|
||||||
|
|
||||||
// Select the table-lookup method of computing the 8-bit CRC
|
|
||||||
// by setting this to 1. The lookup table enlarges code size by
|
|
||||||
// about 250 bytes. It does NOT consume RAM (but did in very
|
|
||||||
// old versions of OneWire). If you disable this, a slower
|
|
||||||
// but very compact algorithm is used.
|
|
||||||
#ifndef ONEWIRE_CRC8_TABLE
|
#ifndef ONEWIRE_CRC8_TABLE
|
||||||
#define ONEWIRE_CRC8_TABLE 0
|
#define ONEWIRE_CRC8_TABLE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Platform specific I/O definitions
|
/** Type used to hold all 1-Wire device ROM addresses (64-bit) */
|
||||||
#define noInterrupts portDISABLE_INTERRUPTS
|
typedef uint64_t onewire_addr_t;
|
||||||
#define interrupts portENABLE_INTERRUPTS
|
|
||||||
#define delayMicroseconds sdk_os_delay_us
|
|
||||||
|
|
||||||
#define DIRECT_READ(pin) gpio_read(pin)
|
/** Structure to contain the current state for onewire_search_next(), etc */
|
||||||
#define DIRECT_MODE_INPUT(pin) gpio_enable(pin, GPIO_INPUT)
|
typedef struct {
|
||||||
#define DIRECT_MODE_OUTPUT(pin) gpio_enable(pin, GPIO_OUTPUT)
|
uint8_t rom_no[8];
|
||||||
#define DIRECT_WRITE_LOW(pin) gpio_write(pin, 0)
|
uint8_t last_discrepancy;
|
||||||
#define DIRECT_WRITE_HIGH(pin) gpio_write(pin, 1)
|
bool last_device_found;
|
||||||
|
} onewire_search_t;
|
||||||
|
|
||||||
void onewire_init(uint8_t pin);
|
/** ::ONEWIRE_NONE is an invalid ROM address that will never occur in a device
|
||||||
|
* (CRC mismatch), and so can be useful as an indicator for "no-such-device",
|
||||||
|
* etc.
|
||||||
|
*/
|
||||||
|
#define ONEWIRE_NONE ((onewire_addr_t)(0xffffffffffffffffLL))
|
||||||
|
|
||||||
// Perform a 1-Wire reset cycle. Returns 1 if a device responds
|
/** Perform a 1-Wire reset cycle.
|
||||||
// with a presence pulse. Returns 0 if there is no device or the
|
*
|
||||||
// bus is shorted or otherwise held low for more than 250uS
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
uint8_t onewire_reset(uint8_t pin);
|
*
|
||||||
|
* @returns `true` if at least one device responds with a presence pulse,
|
||||||
|
* `false` if no devices were detected (or the bus is shorted, etc)
|
||||||
|
*/
|
||||||
|
bool onewire_reset(int pin);
|
||||||
|
|
||||||
// Issue a 1-Wire rom select command, you do the reset first.
|
/** Issue a 1-Wire rom select command to select a particular device.
|
||||||
void onewire_select(uint8_t pin, const uint8_t rom[8]);
|
*
|
||||||
|
* It is necessary to call onewire_reset() before calling this function.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
|
* @param addr The ROM address of the device to select
|
||||||
|
*
|
||||||
|
* @returns `true` if the "ROM select" command could be succesfully issued,
|
||||||
|
* `false` if there was an error.
|
||||||
|
*/
|
||||||
|
bool onewire_select(int pin, const onewire_addr_t addr);
|
||||||
|
|
||||||
// Issue a 1-Wire rom skip command, to address all on bus.
|
/** Issue a 1-Wire "skip ROM" command to select *all* devices on the bus.
|
||||||
void onewire_skip(uint8_t pin);
|
*
|
||||||
|
* It is necessary to call onewire_reset() before calling this function.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
|
*
|
||||||
|
* @returns `true` if the "skip ROM" command could be succesfully issued,
|
||||||
|
* `false` if there was an error.
|
||||||
|
*/
|
||||||
|
bool onewire_skip_rom(int pin);
|
||||||
|
|
||||||
// Write a byte. If 'power' is one then the wire is held high at
|
/** Write a byte on the onewire bus.
|
||||||
// the end for parasitically powered devices. You are responsible
|
*
|
||||||
// for eventually depowering it by calling depower() or doing
|
* The writing code uses open-drain mode and expects the pullup resistor to
|
||||||
// another read or write.
|
* pull the line high when not driven low. If you need strong power after the
|
||||||
void onewire_write(uint8_t pin, uint8_t v, uint8_t power);
|
* write (e.g. DS18B20 in parasite power mode) then call onewire_power() after
|
||||||
|
* this is complete to actively drive the line high.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
|
* @param v The byte value to write
|
||||||
|
*
|
||||||
|
* @returns `true` if successful, `false` on error.
|
||||||
|
*/
|
||||||
|
bool onewire_write(int pin, uint8_t v);
|
||||||
|
|
||||||
void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power);
|
/** Write multiple bytes on the 1-Wire bus.
|
||||||
|
*
|
||||||
|
* See onewire_write() for more info.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
|
* @param buf A pointer to the buffer of bytes to be written
|
||||||
|
* @param count Number of bytes to write
|
||||||
|
*
|
||||||
|
* @returns `true` if all bytes written successfully, `false` on error.
|
||||||
|
*/
|
||||||
|
bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count);
|
||||||
|
|
||||||
// Read a byte.
|
/** Read a byte from a 1-Wire device.
|
||||||
uint8_t onewire_read(uint8_t pin);
|
*
|
||||||
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
|
*
|
||||||
|
* @returns the read byte on success, negative value on error.
|
||||||
|
*/
|
||||||
|
int onewire_read(int pin);
|
||||||
|
|
||||||
void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count);
|
/** Read multiple bytes from a 1-Wire device.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
|
* @param buf A pointer to the buffer to contain the read bytes
|
||||||
|
* @param count Number of bytes to read
|
||||||
|
*
|
||||||
|
* @returns `true` on success, `false` on error.
|
||||||
|
*/
|
||||||
|
bool onewire_read_bytes(int pin, uint8_t *buf, size_t count);
|
||||||
|
|
||||||
// Write a bit. The bus is always left powered at the end, see
|
/** Actively drive the bus high to provide extra power for certain operations
|
||||||
// note in write() about that.
|
* of parasitically-powered devices.
|
||||||
// void onewire_write_bit(uint8_t pin, uint8_t v);
|
*
|
||||||
|
* For parasitically-powered devices which need more power than can be
|
||||||
|
* provided via the normal pull-up resistor, it may be necessary for some
|
||||||
|
* operations to drive the bus actively high. This function can be used to
|
||||||
|
* perform that operation.
|
||||||
|
*
|
||||||
|
* The bus can be depowered once it is no longer needed by calling
|
||||||
|
* onewire_depower(), or it will be depowered automatically the next time
|
||||||
|
* onewire_reset() is called to start another command.
|
||||||
|
*
|
||||||
|
* Note: Make sure the device(s) you are powering will not pull more current
|
||||||
|
* than the ESP8266 is able to supply via its GPIO pins (this is especially
|
||||||
|
* important when multiple devices are on the same bus and they are all
|
||||||
|
* performing a power-intensive operation at the same time (i.e. multiple
|
||||||
|
* DS18B20 sensors, which have all been given a "convert T" operation by using
|
||||||
|
* onewire_skip_rom())).
|
||||||
|
*
|
||||||
|
* Note: This routine will check to make sure that the bus is already high
|
||||||
|
* before driving it, to make sure it doesn't attempt to drive it high while
|
||||||
|
* something else is pulling it low (which could cause a reset or damage the
|
||||||
|
* ESP8266).
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
|
*
|
||||||
|
* @returns `true` on success, `false` on error.
|
||||||
|
*/
|
||||||
|
bool onewire_power(int pin);
|
||||||
|
|
||||||
// Read a bit.
|
/** Stop forcing power onto the bus.
|
||||||
// uint8_t onewire_read_bit(uint8_t pin);
|
*
|
||||||
|
* You only need to do this if you previously called onewire_power() to drive
|
||||||
|
* the bus high and now want to allow it to float instead. Note that
|
||||||
|
* onewire_reset() will also automatically depower the bus first, so you do
|
||||||
|
* not need to call this first if you just want to start a new operation.
|
||||||
|
*
|
||||||
|
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||||
|
*/
|
||||||
|
void onewire_depower(int pin);
|
||||||
|
|
||||||
// Stop forcing power onto the bus. You only need to do this if
|
/** Clear the search state so that it will start from the beginning on the next
|
||||||
// you used the 'power' flag to write() or used a write_bit() call
|
* call to onewire_search_next().
|
||||||
// and aren't about to do another read or write. You would rather
|
*
|
||||||
// not leave this powered if you don't have to, just in case
|
* @param search The onewire_search_t structure to reset.
|
||||||
// someone shorts your bus.
|
*/
|
||||||
void onewire_depower(uint8_t pin);
|
void onewire_search_start(onewire_search_t *search);
|
||||||
|
|
||||||
// Clear the search state so that if will start from the beginning again.
|
/** Setup the search to search for devices with the specified "family code".
|
||||||
void onewire_reset_search(uint8_t pin);
|
*
|
||||||
|
* @param search The onewire_search_t structure to update.
|
||||||
|
* @param family_code The "family code" to search for.
|
||||||
|
*/
|
||||||
|
void onewire_search_prefix(onewire_search_t *search, uint8_t family_code);
|
||||||
|
|
||||||
// Setup the search to find the device type 'family_code' on the next call
|
/** Search for the next device on the bus.
|
||||||
// to search(*newAddr) if it is present.
|
*
|
||||||
void onewire_target_search(uint8_t pin, uint8_t family_code);
|
* The order of returned device addresses is deterministic. You will always
|
||||||
|
* get the same devices in the same order.
|
||||||
|
*
|
||||||
|
* @returns the address of the next device on the bus, or ::ONEWIRE_NONE if
|
||||||
|
* there is no next address. ::ONEWIRE_NONE might also mean that the bus is
|
||||||
|
* shorted, there are no devices, or you have already retrieved all of them.
|
||||||
|
*
|
||||||
|
* It might be a good idea to check the CRC to make sure you didn't get
|
||||||
|
* garbage.
|
||||||
|
*/
|
||||||
|
onewire_addr_t onewire_search_next(onewire_search_t *search, int pin);
|
||||||
|
|
||||||
// Look for the next device. Returns 1 if a new address has been
|
/** Compute a Dallas Semiconductor 8 bit CRC.
|
||||||
// returned. A zero might mean that the bus is shorted, there are
|
*
|
||||||
// no devices, or you have already retrieved all of them. It
|
* These are used in the ROM address and scratchpad registers to verify the
|
||||||
// might be a good idea to check the CRC to make sure you didn't
|
* transmitted data is correct.
|
||||||
// get garbage. The order is deterministic. You will always get
|
*/
|
||||||
// the same devices in the same order.
|
uint8_t onewire_crc8(const uint8_t *data, uint8_t len);
|
||||||
uint8_t onewire_search(uint8_t pin, uint8_t *newAddr);
|
|
||||||
|
|
||||||
// Compute a Dallas Semiconductor 8 bit CRC, these are used in the
|
/** Compute the 1-Wire CRC16 and compare it against the received CRC.
|
||||||
// ROM and scratchpad registers.
|
*
|
||||||
uint8_t onewire_crc8(const uint8_t *addr, uint8_t len);
|
* Example usage (reading a DS2408):
|
||||||
|
* @code
|
||||||
|
* // Put everything in a buffer so we can compute the CRC easily.
|
||||||
|
* uint8_t buf[13];
|
||||||
|
* buf[0] = 0xF0; // Read PIO Registers
|
||||||
|
* buf[1] = 0x88; // LSB address
|
||||||
|
* buf[2] = 0x00; // MSB address
|
||||||
|
* onewire_write_bytes(pin, buf, 3); // Write 3 cmd bytes
|
||||||
|
* onewire_read_bytes(pin, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16
|
||||||
|
* if (!onewire_check_crc16(buf, 11, &buf[11])) {
|
||||||
|
* // TODO: Handle error.
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @param input Array of bytes to checksum.
|
||||||
|
* @param len Number of bytes in `input`
|
||||||
|
* @param inverted_crc The two CRC16 bytes in the received data.
|
||||||
|
* This should just point into the received data,
|
||||||
|
* *not* at a 16-bit integer.
|
||||||
|
* @param crc_iv The crc starting value (optional)
|
||||||
|
*
|
||||||
|
* @returns `true` if the CRC matches, `false` otherwise.
|
||||||
|
*/
|
||||||
|
bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv);
|
||||||
|
|
||||||
// Compute the 1-Wire CRC16 and compare it against the received CRC.
|
/** Compute a Dallas Semiconductor 16 bit CRC.
|
||||||
// Example usage (reading a DS2408):
|
*
|
||||||
// // Put everything in a buffer so we can compute the CRC easily.
|
* This is required to check the integrity of data received from many 1-Wire
|
||||||
// uint8_t buf[13];
|
* devices. Note that the CRC computed here is *not* what you'll get from the
|
||||||
// buf[0] = 0xF0; // Read PIO Registers
|
* 1-Wire network, for two reasons:
|
||||||
// buf[1] = 0x88; // LSB address
|
* 1. The CRC is transmitted bitwise inverted.
|
||||||
// buf[2] = 0x00; // MSB address
|
* 2. Depending on the endian-ness of your processor, the binary
|
||||||
// WriteBytes(net, buf, 3); // Write 3 cmd bytes
|
* representation of the two-byte return value may have a different
|
||||||
// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16
|
* byte order than the two bytes you get from 1-Wire.
|
||||||
// if (!CheckCRC16(buf, 11, &buf[11])) {
|
*
|
||||||
// // Handle error.
|
* @param input Array of bytes to checksum.
|
||||||
// }
|
* @param len How many bytes are in `input`.
|
||||||
//
|
* @param crc_iv The crc starting value (optional)
|
||||||
// @param input - Array of bytes to checksum.
|
*
|
||||||
// @param len - How many bytes to use.
|
* @returns the CRC16, as defined by Dallas Semiconductor.
|
||||||
// @param inverted_crc - The two CRC16 bytes in the received data.
|
*/
|
||||||
// This should just point into the received data,
|
uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv);
|
||||||
// *not* at a 16-bit integer.
|
|
||||||
// @param crc - The crc starting value (optional)
|
|
||||||
// @return True, iff the CRC matches.
|
|
||||||
bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc);
|
|
||||||
|
|
||||||
// Compute a Dallas Semiconductor 16 bit CRC. This is required to check
|
|
||||||
// the integrity of data received from many 1-Wire devices. Note that the
|
|
||||||
// CRC computed here is *not* what you'll get from the 1-Wire network,
|
|
||||||
// for two reasons:
|
|
||||||
// 1) The CRC is transmitted bitwise inverted.
|
|
||||||
// 2) Depending on the endian-ness of your processor, the binary
|
|
||||||
// representation of the two-byte return value may have a different
|
|
||||||
// byte order than the two bytes you get from 1-Wire.
|
|
||||||
// @param input - Array of bytes to checksum.
|
|
||||||
// @param len - How many bytes to use.
|
|
||||||
// @param crc - The crc starting value (optional)
|
|
||||||
// @return The CRC16, as defined by Dallas Semiconductor.
|
|
||||||
uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
32
extras/paho_mqtt_c/LICENSE.txt
Normal file
32
extras/paho_mqtt_c/LICENSE.txt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
Software License Agreement (BSD License)
|
||||||
|
|
||||||
|
Copyright (c) 2015, Baoshi Zhu (www.ba0sh1.com)
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Baoshi Zhu nor the names of its contributors may be
|
||||||
|
used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission from me.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
Part of this project is based on Eclipse Paho which is copyrighted by IBM corp.
|
||||||
|
Refer to the licensing information at the begining of corresponding source files.
|
551
extras/paho_mqtt_c/MQTTClient.c
Normal file
551
extras/paho_mqtt_c/MQTTClient.c
Normal file
|
@ -0,0 +1,551 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
*******************************************************************************/
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include <lwip/arch.h>
|
||||||
|
#include "MQTTClient.h"
|
||||||
|
|
||||||
|
void NewMessageData(MessageData* md, MQTTString* aTopicName, MQTTMessage* aMessgage) {
|
||||||
|
md->topic = aTopicName;
|
||||||
|
md->message = aMessgage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int getNextPacketId(MQTTClient *c) {
|
||||||
|
return c->next_packetid = (c->next_packetid == MAX_PACKET_ID) ? 1 : c->next_packetid + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sendPacket(MQTTClient* c, int length, Timer* timer)
|
||||||
|
{
|
||||||
|
int rc = FAILURE,
|
||||||
|
sent = 0;
|
||||||
|
|
||||||
|
while (sent < length && !expired(timer))
|
||||||
|
{
|
||||||
|
rc = c->ipstack->mqttwrite(c->ipstack, &c->buf[sent], length, left_ms(timer));
|
||||||
|
if (rc < 0) // there was an error writing the data
|
||||||
|
break;
|
||||||
|
sent += rc;
|
||||||
|
}
|
||||||
|
if (sent == length)
|
||||||
|
{
|
||||||
|
countdown(&(c->ping_timer), c->keepAliveInterval); // record the fact that we have successfully sent the packet
|
||||||
|
rc = SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rc = FAILURE;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int decodePacket(MQTTClient* c, int* value, int timeout)
|
||||||
|
{
|
||||||
|
unsigned char i;
|
||||||
|
int multiplier = 1;
|
||||||
|
int len = 0;
|
||||||
|
const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4;
|
||||||
|
|
||||||
|
*value = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int rc = MQTTPACKET_READ_ERROR;
|
||||||
|
|
||||||
|
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||||
|
{
|
||||||
|
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
rc = c->ipstack->mqttread(c->ipstack, &i, 1, timeout);
|
||||||
|
if (rc != 1)
|
||||||
|
goto exit;
|
||||||
|
*value += (i & 127) * multiplier;
|
||||||
|
multiplier *= 128;
|
||||||
|
} while ((i & 128) != 0);
|
||||||
|
exit:
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int readPacket(MQTTClient* c, Timer* timer)
|
||||||
|
{
|
||||||
|
int rc = FAILURE;
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
int len = 0;
|
||||||
|
int rem_len = 0;
|
||||||
|
|
||||||
|
/* 1. read the header byte. This has the packet type in it */
|
||||||
|
if (c->ipstack->mqttread(c->ipstack, c->readbuf, 1, left_ms(timer)) != 1)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
len = 1;
|
||||||
|
/* 2. read the remaining length. This is variable in itself */
|
||||||
|
decodePacket(c, &rem_len, left_ms(timer));
|
||||||
|
len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
|
||||||
|
|
||||||
|
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
|
||||||
|
if (rem_len > 0 && (c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, left_ms(timer)) != rem_len))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
header.byte = c->readbuf[0];
|
||||||
|
rc = header.bits.type;
|
||||||
|
exit:
|
||||||
|
//dmsg_printf("readPacket=%d\r\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// assume topic filter and name is in correct format
|
||||||
|
// # can only be at end
|
||||||
|
// + and # can only be next to separator
|
||||||
|
char isTopicMatched(char* topicFilter, MQTTString* topicName)
|
||||||
|
{
|
||||||
|
char* curf = topicFilter;
|
||||||
|
char* curn = topicName->lenstring.data;
|
||||||
|
char* curn_end = curn + topicName->lenstring.len;
|
||||||
|
|
||||||
|
while (*curf && curn < curn_end)
|
||||||
|
{
|
||||||
|
if (*curn == '/' && *curf != '/')
|
||||||
|
break;
|
||||||
|
if (*curf != '+' && *curf != '#' && *curf != *curn)
|
||||||
|
break;
|
||||||
|
if (*curf == '+')
|
||||||
|
{ // skip until we meet the next separator, or end of string
|
||||||
|
char* nextpos = curn + 1;
|
||||||
|
while (nextpos < curn_end && *nextpos != '/')
|
||||||
|
nextpos = ++curn + 1;
|
||||||
|
}
|
||||||
|
else if (*curf == '#')
|
||||||
|
curn = curn_end - 1; // skip until end of string
|
||||||
|
curf++;
|
||||||
|
curn++;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (curn == curn_end) && (*curf == '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int deliverMessage(MQTTClient* c, MQTTString* topicName, MQTTMessage* message)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int rc = FAILURE;
|
||||||
|
|
||||||
|
// we have to find the right message handler - indexed by topic
|
||||||
|
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||||
|
{
|
||||||
|
if (c->messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(topicName, (char*)c->messageHandlers[i].topicFilter) ||
|
||||||
|
isTopicMatched((char*)c->messageHandlers[i].topicFilter, topicName)))
|
||||||
|
{
|
||||||
|
if (c->messageHandlers[i].fp != NULL)
|
||||||
|
{
|
||||||
|
MessageData md;
|
||||||
|
NewMessageData(&md, topicName, message);
|
||||||
|
c->messageHandlers[i].fp(&md);
|
||||||
|
rc = SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == FAILURE && c->defaultMessageHandler != NULL)
|
||||||
|
{
|
||||||
|
MessageData md;
|
||||||
|
NewMessageData(&md, topicName, message);
|
||||||
|
c->defaultMessageHandler(&md);
|
||||||
|
rc = SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int keepalive(MQTTClient* c)
|
||||||
|
{
|
||||||
|
int rc = SUCCESS;
|
||||||
|
|
||||||
|
if (c->keepAliveInterval == 0)
|
||||||
|
{
|
||||||
|
rc = SUCCESS;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expired(&(c->ping_timer)))
|
||||||
|
{
|
||||||
|
if (c->ping_outstanding)
|
||||||
|
{
|
||||||
|
// if ping failure accumulated above MAX_FAIL_ALLOWED, the connection is broken
|
||||||
|
++(c->fail_count);
|
||||||
|
if (c->fail_count >= MAX_FAIL_ALLOWED)
|
||||||
|
{
|
||||||
|
rc = DISCONNECTED;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Timer timer;
|
||||||
|
InitTimer(&timer);
|
||||||
|
countdown_ms(&timer, 1000);
|
||||||
|
c->ping_outstanding = 1;
|
||||||
|
int len = MQTTSerialize_pingreq(c->buf, c->buf_size);
|
||||||
|
if (len > 0)
|
||||||
|
sendPacket(c, len, &timer);
|
||||||
|
}
|
||||||
|
// re-arm ping counter
|
||||||
|
countdown(&(c->ping_timer), c->keepAliveInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int cycle(MQTTClient* c, Timer* timer)
|
||||||
|
{
|
||||||
|
// read the socket, see what work is due
|
||||||
|
unsigned short packet_type = readPacket(c, timer);
|
||||||
|
|
||||||
|
int len = 0,
|
||||||
|
rc = SUCCESS;
|
||||||
|
|
||||||
|
switch (packet_type)
|
||||||
|
{
|
||||||
|
case CONNACK:
|
||||||
|
case PUBACK:
|
||||||
|
case SUBACK:
|
||||||
|
break;
|
||||||
|
case PUBLISH:
|
||||||
|
{
|
||||||
|
MQTTString topicName;
|
||||||
|
MQTTMessage msg;
|
||||||
|
if (MQTTDeserialize_publish((unsigned char*)&msg.dup, (int*)&msg.qos, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName,
|
||||||
|
(unsigned char**)&msg.payload, (int*)&msg.payloadlen, c->readbuf, c->readbuf_size) != 1)
|
||||||
|
goto exit;
|
||||||
|
deliverMessage(c, &topicName, &msg);
|
||||||
|
if (msg.qos != QOS0)
|
||||||
|
{
|
||||||
|
if (msg.qos == QOS1)
|
||||||
|
len = MQTTSerialize_ack(c->buf, c->buf_size, PUBACK, 0, msg.id);
|
||||||
|
else if (msg.qos == QOS2)
|
||||||
|
len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREC, 0, msg.id);
|
||||||
|
if (len <= 0)
|
||||||
|
rc = FAILURE;
|
||||||
|
else
|
||||||
|
rc = sendPacket(c, len, timer);
|
||||||
|
if (rc == FAILURE)
|
||||||
|
goto exit; // there was a problem
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PUBREC:
|
||||||
|
{
|
||||||
|
unsigned short mypacketid;
|
||||||
|
unsigned char dup, type;
|
||||||
|
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
|
||||||
|
rc = FAILURE;
|
||||||
|
else if ((len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREL, 0, mypacketid)) <= 0)
|
||||||
|
rc = FAILURE;
|
||||||
|
else if ((rc = sendPacket(c, len, timer)) != SUCCESS) // send the PUBREL packet
|
||||||
|
rc = FAILURE; // there was a problem
|
||||||
|
if (rc == FAILURE)
|
||||||
|
goto exit; // there was a problem
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PUBCOMP:
|
||||||
|
break;
|
||||||
|
case PINGRESP:
|
||||||
|
{
|
||||||
|
c->ping_outstanding = 0;
|
||||||
|
c->fail_count = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c->isconnected)
|
||||||
|
rc = keepalive(c);
|
||||||
|
exit:
|
||||||
|
if (rc == SUCCESS)
|
||||||
|
rc = packet_type;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void NewMQTTClient(MQTTClient* c, Network* network, unsigned int command_timeout_ms, unsigned char* buf, size_t buf_size, unsigned char* readbuf, size_t readbuf_size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
c->ipstack = network;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||||
|
c->messageHandlers[i].topicFilter = 0;
|
||||||
|
c->command_timeout_ms = command_timeout_ms;
|
||||||
|
c->buf = buf;
|
||||||
|
c->buf_size = buf_size;
|
||||||
|
c->readbuf = readbuf;
|
||||||
|
c->readbuf_size = readbuf_size;
|
||||||
|
c->isconnected = 0;
|
||||||
|
c->ping_outstanding = 0;
|
||||||
|
c->fail_count = 0;
|
||||||
|
c->defaultMessageHandler = NULL;
|
||||||
|
InitTimer(&(c->ping_timer));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTYield(MQTTClient* c, int timeout_ms)
|
||||||
|
{
|
||||||
|
int rc = SUCCESS;
|
||||||
|
Timer timer;
|
||||||
|
|
||||||
|
InitTimer(&timer);
|
||||||
|
countdown_ms(&timer, timeout_ms);
|
||||||
|
while (!expired(&timer))
|
||||||
|
{
|
||||||
|
rc = cycle(c, &timer);
|
||||||
|
// cycle could return 0 or packet_type or 65535 if nothing is read
|
||||||
|
// cycle returns DISCONNECTED only if keepalive() fails.
|
||||||
|
if (rc == DISCONNECTED)
|
||||||
|
break;
|
||||||
|
rc = SUCCESS;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// only used in single-threaded mode where one command at a time is in process
|
||||||
|
int waitfor(MQTTClient* c, int packet_type, Timer* timer)
|
||||||
|
{
|
||||||
|
int rc = FAILURE;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (expired(timer))
|
||||||
|
break; // we timed out
|
||||||
|
}
|
||||||
|
while ((rc = cycle(c, timer)) != packet_type);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTConnect(MQTTClient* c, MQTTPacket_connectData* options)
|
||||||
|
{
|
||||||
|
Timer connect_timer;
|
||||||
|
int rc = FAILURE;
|
||||||
|
MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
InitTimer(&connect_timer);
|
||||||
|
countdown_ms(&connect_timer, c->command_timeout_ms);
|
||||||
|
|
||||||
|
if (c->isconnected) // don't send connect packet again if we are already connected
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (options == 0)
|
||||||
|
options = &default_options; // set default options if none were supplied
|
||||||
|
|
||||||
|
c->keepAliveInterval = options->keepAliveInterval;
|
||||||
|
countdown(&(c->ping_timer), c->keepAliveInterval);
|
||||||
|
|
||||||
|
if ((len = MQTTSerialize_connect(c->buf, c->buf_size, options)) <= 0)
|
||||||
|
goto exit;
|
||||||
|
if ((rc = sendPacket(c, len, &connect_timer)) != SUCCESS) // send the connect packet
|
||||||
|
goto exit; // there was a problem
|
||||||
|
|
||||||
|
// this will be a blocking call, wait for the connack
|
||||||
|
if (waitfor(c, CONNACK, &connect_timer) == CONNACK)
|
||||||
|
{
|
||||||
|
unsigned char connack_rc = 255;
|
||||||
|
char sessionPresent = 0;
|
||||||
|
if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, c->readbuf, c->readbuf_size) == 1)
|
||||||
|
rc = connack_rc;
|
||||||
|
else
|
||||||
|
rc = FAILURE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rc = FAILURE;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (rc == SUCCESS)
|
||||||
|
c->isconnected = 1;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTSubscribe(MQTTClient* c, const char* topic, enum QoS qos, messageHandler handler)
|
||||||
|
{
|
||||||
|
int rc = FAILURE;
|
||||||
|
Timer timer;
|
||||||
|
int len = 0;
|
||||||
|
MQTTString topicStr = MQTTString_initializer;
|
||||||
|
topicStr.cstring = (char *)topic;
|
||||||
|
|
||||||
|
InitTimer(&timer);
|
||||||
|
countdown_ms(&timer, c->command_timeout_ms);
|
||||||
|
if (!c->isconnected)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
len = MQTTSerialize_subscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topicStr, (int*)&qos);
|
||||||
|
if (len <= 0)
|
||||||
|
goto exit;
|
||||||
|
if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
|
||||||
|
{
|
||||||
|
goto exit; // there was a problem
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitfor(c, SUBACK, &timer) == SUBACK) // wait for suback
|
||||||
|
{
|
||||||
|
int count = 0, grantedQoS = -1;
|
||||||
|
unsigned short mypacketid;
|
||||||
|
if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, c->readbuf, c->readbuf_size) == 1)
|
||||||
|
rc = grantedQoS; // 0, 1, 2 or 0x80
|
||||||
|
if (rc != 0x80)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||||
|
{
|
||||||
|
if (c->messageHandlers[i].topicFilter == 0)
|
||||||
|
{
|
||||||
|
c->messageHandlers[i].topicFilter = topic;
|
||||||
|
c->messageHandlers[i].fp = handler;
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rc = FAILURE;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTUnsubscribe(MQTTClient* c, const char* topicFilter)
|
||||||
|
{
|
||||||
|
int rc = FAILURE;
|
||||||
|
Timer timer;
|
||||||
|
MQTTString topic = MQTTString_initializer;
|
||||||
|
topic.cstring = (char *)topicFilter;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
InitTimer(&timer);
|
||||||
|
countdown_ms(&timer, c->command_timeout_ms);
|
||||||
|
|
||||||
|
if (!c->isconnected)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if ((len = MQTTSerialize_unsubscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic)) <= 0)
|
||||||
|
goto exit;
|
||||||
|
if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
|
||||||
|
goto exit; // there was a problem
|
||||||
|
|
||||||
|
if (waitfor(c, UNSUBACK, &timer) == UNSUBACK)
|
||||||
|
{
|
||||||
|
unsigned short mypacketid; // should be the same as the packetid above
|
||||||
|
if (MQTTDeserialize_unsuback(&mypacketid, c->readbuf, c->readbuf_size) == 1)
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rc = FAILURE;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTPublish(MQTTClient* c, const char* topic, MQTTMessage* message)
|
||||||
|
{
|
||||||
|
int rc = FAILURE;
|
||||||
|
Timer timer;
|
||||||
|
MQTTString topicStr = MQTTString_initializer;
|
||||||
|
topicStr.cstring = (char *)topic;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
InitTimer(&timer);
|
||||||
|
countdown_ms(&timer, c->command_timeout_ms);
|
||||||
|
|
||||||
|
if (!c->isconnected)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (message->qos == QOS1 || message->qos == QOS2)
|
||||||
|
message->id = getNextPacketId(c);
|
||||||
|
|
||||||
|
len = MQTTSerialize_publish(c->buf, c->buf_size, 0, message->qos, message->retained, message->id,
|
||||||
|
topicStr, (unsigned char*)message->payload, message->payloadlen);
|
||||||
|
if (len <= 0)
|
||||||
|
goto exit;
|
||||||
|
if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
|
||||||
|
{
|
||||||
|
goto exit; // there was a problem
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message->qos == QOS1)
|
||||||
|
{
|
||||||
|
if (waitfor(c, PUBACK, &timer) == PUBACK)
|
||||||
|
{
|
||||||
|
// We still can receive from broker, treat as recoverable
|
||||||
|
c->fail_count = 0;
|
||||||
|
unsigned short mypacketid;
|
||||||
|
unsigned char dup, type;
|
||||||
|
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
|
||||||
|
rc = FAILURE;
|
||||||
|
else
|
||||||
|
rc = SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (message->qos == QOS2)
|
||||||
|
{
|
||||||
|
if (waitfor(c, PUBCOMP, &timer) == PUBCOMP)
|
||||||
|
{
|
||||||
|
// We still can receive from broker, treat as recoverable
|
||||||
|
c->fail_count = 0;
|
||||||
|
unsigned short mypacketid;
|
||||||
|
unsigned char dup, type;
|
||||||
|
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
|
||||||
|
rc = FAILURE;
|
||||||
|
else
|
||||||
|
rc = SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTDisconnect(MQTTClient* c)
|
||||||
|
{
|
||||||
|
int rc = FAILURE;
|
||||||
|
Timer timer; // we might wait for incomplete incoming publishes to complete
|
||||||
|
int len = MQTTSerialize_disconnect(c->buf, c->buf_size);
|
||||||
|
|
||||||
|
InitTimer(&timer);
|
||||||
|
countdown_ms(&timer, c->command_timeout_ms);
|
||||||
|
|
||||||
|
if (len > 0)
|
||||||
|
rc = sendPacket(c, len, &timer); // send the disconnect packet
|
||||||
|
|
||||||
|
c->isconnected = 0;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
91
extras/paho_mqtt_c/MQTTClient.h
Normal file
91
extras/paho_mqtt_c/MQTTClient.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef __MQTT_CLIENT_C_
|
||||||
|
#define __MQTT_CLIENT_C_
|
||||||
|
|
||||||
|
#include "MQTTPacket.h"
|
||||||
|
#include "MQTTESP8266.h"
|
||||||
|
|
||||||
|
#define MAX_PACKET_ID 65535
|
||||||
|
#define MAX_MESSAGE_HANDLERS 5
|
||||||
|
#define MAX_FAIL_ALLOWED 2
|
||||||
|
|
||||||
|
enum QoS { QOS0, QOS1, QOS2 };
|
||||||
|
|
||||||
|
// all failure return codes must be negative
|
||||||
|
enum returnCode {DISCONNECTED = -3, BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 };
|
||||||
|
|
||||||
|
void NewTimer(Timer*);
|
||||||
|
|
||||||
|
typedef struct _MQTTMessage
|
||||||
|
{
|
||||||
|
enum QoS qos;
|
||||||
|
char retained;
|
||||||
|
char dup;
|
||||||
|
unsigned short id;
|
||||||
|
void *payload;
|
||||||
|
size_t payloadlen;
|
||||||
|
} MQTTMessage;
|
||||||
|
|
||||||
|
typedef struct _MessageData
|
||||||
|
{
|
||||||
|
MQTTString* topic;
|
||||||
|
MQTTMessage* message;
|
||||||
|
} MessageData;
|
||||||
|
|
||||||
|
typedef void (*messageHandler)(MessageData*);
|
||||||
|
|
||||||
|
struct _MQTTClient
|
||||||
|
{
|
||||||
|
unsigned int next_packetid;
|
||||||
|
unsigned int command_timeout_ms;
|
||||||
|
size_t buf_size, readbuf_size;
|
||||||
|
unsigned char *buf;
|
||||||
|
unsigned char *readbuf;
|
||||||
|
unsigned int keepAliveInterval;
|
||||||
|
char ping_outstanding;
|
||||||
|
int fail_count;
|
||||||
|
int isconnected;
|
||||||
|
|
||||||
|
struct MessageHandlers
|
||||||
|
{
|
||||||
|
const char* topicFilter;
|
||||||
|
void (*fp) (MessageData*);
|
||||||
|
} messageHandlers[MAX_MESSAGE_HANDLERS]; // Message handlers are indexed by subscription topic
|
||||||
|
|
||||||
|
void (*defaultMessageHandler) (MessageData*);
|
||||||
|
|
||||||
|
Network* ipstack;
|
||||||
|
Timer ping_timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _MQTTClient MQTTClient;
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTConnect(MQTTClient* c, MQTTPacket_connectData* options);
|
||||||
|
int MQTTPublish(MQTTClient* c, const char* topic, MQTTMessage* message);
|
||||||
|
int MQTTSubscribe(MQTTClient* c, const char* topic, enum QoS qos, messageHandler handler);
|
||||||
|
int MQTTUnsubscribe(MQTTClient* c, const char* topic);
|
||||||
|
int MQTTDisconnect(MQTTClient* c);
|
||||||
|
int MQTTYield(MQTTClient* c, int timeout_ms);
|
||||||
|
|
||||||
|
void NewMQTTClient(MQTTClient*, Network*, unsigned int, unsigned char*, size_t, unsigned char*, size_t);
|
||||||
|
|
||||||
|
#define DefaultClient {0, 0, 0, 0, NULL, NULL, 0, 0, 0}
|
||||||
|
|
||||||
|
#endif
|
136
extras/paho_mqtt_c/MQTTConnect.h
Normal file
136
extras/paho_mqtt_c/MQTTConnect.h
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MQTTCONNECT_H_
|
||||||
|
#define MQTTCONNECT_H_
|
||||||
|
|
||||||
|
#if !defined(DLLImport)
|
||||||
|
#define DLLImport
|
||||||
|
#endif
|
||||||
|
#if !defined(DLLExport)
|
||||||
|
#define DLLExport
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
unsigned char all; /**< all connect flags */
|
||||||
|
#if defined(REVERSED)
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int username : 1; /**< 3.1 user name */
|
||||||
|
unsigned int password : 1; /**< 3.1 password */
|
||||||
|
unsigned int willRetain : 1; /**< will retain setting */
|
||||||
|
unsigned int willQoS : 2; /**< will QoS value */
|
||||||
|
unsigned int will : 1; /**< will flag */
|
||||||
|
unsigned int cleansession : 1; /**< clean session flag */
|
||||||
|
unsigned int : 1; /**< unused */
|
||||||
|
} bits;
|
||||||
|
#else
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int : 1; /**< unused */
|
||||||
|
unsigned int cleansession : 1; /**< cleansession flag */
|
||||||
|
unsigned int will : 1; /**< will flag */
|
||||||
|
unsigned int willQoS : 2; /**< will QoS value */
|
||||||
|
unsigned int willRetain : 1; /**< will retain setting */
|
||||||
|
unsigned int password : 1; /**< 3.1 password */
|
||||||
|
unsigned int username : 1; /**< 3.1 user name */
|
||||||
|
} bits;
|
||||||
|
#endif
|
||||||
|
} MQTTConnectFlags; /**< connect flags byte */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the MQTT "Last Will and Testament" (LWT) settings for
|
||||||
|
* the connect packet.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/** The eyecatcher for this structure. must be MQTW. */
|
||||||
|
char struct_id[4];
|
||||||
|
/** The version number of this structure. Must be 0 */
|
||||||
|
int struct_version;
|
||||||
|
/** The LWT topic to which the LWT message will be published. */
|
||||||
|
MQTTString topicName;
|
||||||
|
/** The LWT payload. */
|
||||||
|
MQTTString message;
|
||||||
|
/**
|
||||||
|
* The retained flag for the LWT message (see MQTTAsync_message.retained).
|
||||||
|
*/
|
||||||
|
unsigned char retained;
|
||||||
|
/**
|
||||||
|
* The quality of service setting for the LWT message (see
|
||||||
|
* MQTTAsync_message.qos and @ref qos).
|
||||||
|
*/
|
||||||
|
char qos;
|
||||||
|
} MQTTPacket_willOptions;
|
||||||
|
|
||||||
|
|
||||||
|
#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 }
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/** The eyecatcher for this structure. must be MQTC. */
|
||||||
|
char struct_id[4];
|
||||||
|
/** The version number of this structure. Must be 0 */
|
||||||
|
int struct_version;
|
||||||
|
/** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1
|
||||||
|
*/
|
||||||
|
unsigned char MQTTVersion;
|
||||||
|
MQTTString clientID;
|
||||||
|
unsigned short keepAliveInterval;
|
||||||
|
unsigned char cleansession;
|
||||||
|
unsigned char willFlag;
|
||||||
|
MQTTPacket_willOptions will;
|
||||||
|
MQTTString username;
|
||||||
|
MQTTString password;
|
||||||
|
} MQTTPacket_connectData;
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
unsigned char all; /**< all connack flags */
|
||||||
|
#if defined(REVERSED)
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int sessionpresent : 1; /**< session present flag */
|
||||||
|
unsigned int : 7; /**< unused */
|
||||||
|
} bits;
|
||||||
|
#else
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int : 7; /**< unused */
|
||||||
|
unsigned int sessionpresent : 1; /**< session present flag */
|
||||||
|
} bits;
|
||||||
|
#endif
|
||||||
|
} MQTTConnackFlags; /**< connack flags byte */
|
||||||
|
|
||||||
|
#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \
|
||||||
|
MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} }
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options);
|
||||||
|
DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len);
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent);
|
||||||
|
DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen);
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen);
|
||||||
|
DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen);
|
||||||
|
|
||||||
|
#endif /* MQTTCONNECT_H_ */
|
214
extras/paho_mqtt_c/MQTTConnectClient.c
Normal file
214
extras/paho_mqtt_c/MQTTConnectClient.c
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
*******************************************************************************/
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include "MQTTPacket.h"
|
||||||
|
#include "StackTrace.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
|
||||||
|
* @param options the options to be used to build the connect packet
|
||||||
|
* @return the length of buffer needed to contain the serialized version of the packet
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_connectLength(MQTTPacket_connectData* options)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
|
||||||
|
if (options->MQTTVersion == 3)
|
||||||
|
len = 12; /* variable depending on MQTT or MQIsdp */
|
||||||
|
else if (options->MQTTVersion == 4)
|
||||||
|
len = 10;
|
||||||
|
|
||||||
|
len += MQTTstrlen(options->clientID)+2;
|
||||||
|
if (options->willFlag)
|
||||||
|
len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2;
|
||||||
|
if (options->username.cstring || options->username.lenstring.data)
|
||||||
|
len += MQTTstrlen(options->username)+2;
|
||||||
|
if (options->password.cstring || options->password.lenstring.data)
|
||||||
|
len += MQTTstrlen(options->password)+2;
|
||||||
|
|
||||||
|
FUNC_EXIT_RC(len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the connect options into the buffer.
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param len the length in bytes of the supplied buffer
|
||||||
|
* @param options the options to be used to build the connect packet
|
||||||
|
* @return serialized length, or error if 0
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
|
||||||
|
{
|
||||||
|
unsigned char *ptr = buf;
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
MQTTConnectFlags flags = {0};
|
||||||
|
int len = 0;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen)
|
||||||
|
{
|
||||||
|
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.byte = 0;
|
||||||
|
header.bits.type = CONNECT;
|
||||||
|
writeChar(&ptr, header.byte); /* write header */
|
||||||
|
|
||||||
|
ptr += MQTTPacket_encode(ptr, len); /* write remaining length */
|
||||||
|
|
||||||
|
if (options->MQTTVersion == 4)
|
||||||
|
{
|
||||||
|
writeCString(&ptr, "MQTT");
|
||||||
|
writeChar(&ptr, (char) 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeCString(&ptr, "MQIsdp");
|
||||||
|
writeChar(&ptr, (char) 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
flags.all = 0;
|
||||||
|
flags.bits.cleansession = options->cleansession;
|
||||||
|
flags.bits.will = (options->willFlag) ? 1 : 0;
|
||||||
|
if (flags.bits.will)
|
||||||
|
{
|
||||||
|
flags.bits.willQoS = options->will.qos;
|
||||||
|
flags.bits.willRetain = options->will.retained;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options->username.cstring || options->username.lenstring.data)
|
||||||
|
flags.bits.username = 1;
|
||||||
|
if (options->password.cstring || options->password.lenstring.data)
|
||||||
|
flags.bits.password = 1;
|
||||||
|
|
||||||
|
writeChar(&ptr, flags.all);
|
||||||
|
writeInt(&ptr, options->keepAliveInterval);
|
||||||
|
writeMQTTString(&ptr, options->clientID);
|
||||||
|
if (options->willFlag)
|
||||||
|
{
|
||||||
|
writeMQTTString(&ptr, options->will.topicName);
|
||||||
|
writeMQTTString(&ptr, options->will.message);
|
||||||
|
}
|
||||||
|
if (flags.bits.username)
|
||||||
|
writeMQTTString(&ptr, options->username);
|
||||||
|
if (flags.bits.password)
|
||||||
|
writeMQTTString(&ptr, options->password);
|
||||||
|
|
||||||
|
rc = ptr - buf;
|
||||||
|
|
||||||
|
exit: FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the supplied (wire) buffer into connack data - return code
|
||||||
|
* @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
|
||||||
|
* @param connack_rc returned integer value of the connack return code
|
||||||
|
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||||
|
* @param len the length in bytes of the data in the supplied buffer
|
||||||
|
* @return error code. 1 is success, 0 is failure
|
||||||
|
*/
|
||||||
|
int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
|
||||||
|
{
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
unsigned char* curdata = buf;
|
||||||
|
unsigned char* enddata = NULL;
|
||||||
|
int rc = 0;
|
||||||
|
int mylen;
|
||||||
|
MQTTConnackFlags flags = {0};
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
header.byte = readChar(&curdata);
|
||||||
|
if (header.bits.type != CONNACK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||||
|
enddata = curdata + mylen;
|
||||||
|
if (enddata - curdata < 2)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
flags.all = readChar(&curdata);
|
||||||
|
*sessionPresent = flags.bits.sessionpresent;
|
||||||
|
*connack_rc = readChar(&curdata);
|
||||||
|
|
||||||
|
rc = 1;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||||
|
* @param packettype the message type
|
||||||
|
* @return serialized length, or error if 0
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
|
||||||
|
{
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
int rc = -1;
|
||||||
|
unsigned char *ptr = buf;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
if (buflen < 2)
|
||||||
|
{
|
||||||
|
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
header.byte = 0;
|
||||||
|
header.bits.type = packettype;
|
||||||
|
writeChar(&ptr, header.byte); /* write header */
|
||||||
|
|
||||||
|
ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */
|
||||||
|
rc = ptr - buf;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||||
|
* @return serialized length, or error if 0
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_disconnect(unsigned char* buf, int buflen)
|
||||||
|
{
|
||||||
|
return MQTTSerialize_zero(buf, buflen, DISCONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||||
|
* @return serialized length, or error if 0
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_pingreq(unsigned char* buf, int buflen)
|
||||||
|
{
|
||||||
|
return MQTTSerialize_zero(buf, buflen, PINGREQ);
|
||||||
|
}
|
107
extras/paho_mqtt_c/MQTTDeserializePublish.c
Normal file
107
extras/paho_mqtt_c/MQTTDeserializePublish.c
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
*******************************************************************************/
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include "StackTrace.h"
|
||||||
|
#include "MQTTPacket.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define min(a, b) ((a < b) ? 1 : 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the supplied (wire) buffer into publish data
|
||||||
|
* @param dup returned integer - the MQTT dup flag
|
||||||
|
* @param qos returned integer - the MQTT QoS value
|
||||||
|
* @param retained returned integer - the MQTT retained flag
|
||||||
|
* @param packetid returned integer - the MQTT packet identifier
|
||||||
|
* @param topicName returned MQTTString - the MQTT topic in the publish
|
||||||
|
* @param payload returned byte buffer - the MQTT publish payload
|
||||||
|
* @param payloadlen returned integer - the length of the MQTT payload
|
||||||
|
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||||
|
* @param buflen the length in bytes of the data in the supplied buffer
|
||||||
|
* @return error code. 1 is success
|
||||||
|
*/
|
||||||
|
int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
|
||||||
|
unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen)
|
||||||
|
{
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
unsigned char* curdata = buf;
|
||||||
|
unsigned char* enddata = NULL;
|
||||||
|
int rc = 0;
|
||||||
|
int mylen = 0;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
header.byte = readChar(&curdata);
|
||||||
|
if (header.bits.type != PUBLISH)
|
||||||
|
goto exit;
|
||||||
|
*dup = header.bits.dup;
|
||||||
|
*qos = header.bits.qos;
|
||||||
|
*retained = header.bits.retain;
|
||||||
|
|
||||||
|
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||||
|
enddata = curdata + mylen;
|
||||||
|
|
||||||
|
if (!readMQTTLenString(topicName, &curdata, enddata) ||
|
||||||
|
enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (*qos > 0)
|
||||||
|
*packetid = readInt(&curdata);
|
||||||
|
|
||||||
|
*payloadlen = enddata - curdata;
|
||||||
|
*payload = curdata;
|
||||||
|
rc = 1;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the supplied (wire) buffer into an ack
|
||||||
|
* @param packettype returned integer - the MQTT packet type
|
||||||
|
* @param dup returned integer - the MQTT dup flag
|
||||||
|
* @param packetid returned integer - the MQTT packet identifier
|
||||||
|
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||||
|
* @param buflen the length in bytes of the data in the supplied buffer
|
||||||
|
* @return error code. 1 is success, 0 is failure
|
||||||
|
*/
|
||||||
|
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen)
|
||||||
|
{
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
unsigned char* curdata = buf;
|
||||||
|
unsigned char* enddata = NULL;
|
||||||
|
int rc = 0;
|
||||||
|
int mylen;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
header.byte = readChar(&curdata);
|
||||||
|
*dup = header.bits.dup;
|
||||||
|
*packettype = header.bits.type;
|
||||||
|
|
||||||
|
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||||
|
enddata = curdata + mylen;
|
||||||
|
|
||||||
|
if (enddata - curdata < 2)
|
||||||
|
goto exit;
|
||||||
|
*packetid = readInt(&curdata);
|
||||||
|
|
||||||
|
rc = 1;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
187
extras/paho_mqtt_c/MQTTESP8266.c
Normal file
187
extras/paho_mqtt_c/MQTTESP8266.c
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* @file MQTTESP8266.c
|
||||||
|
* @author Baoshi <mail(at)ba0sh1(dot)com>
|
||||||
|
* @version 0.1
|
||||||
|
* @date Sep 9, 2015
|
||||||
|
* @brief Eclipse Paho ported to ESP8266 RTOS
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
* @copyright
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015, Baoshi Zhu. All rights reserved.
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE.txt file.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTY. IN NO EVENT WILL THE AUTHOR(S) BE HELD LIABLE FOR ANY DAMAGES
|
||||||
|
* ARISING FROM THE USE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include <lwip/sockets.h>
|
||||||
|
#include <lwip/inet.h>
|
||||||
|
#include <lwip/netdb.h>
|
||||||
|
#include <lwip/sys.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "MQTTESP8266.h"
|
||||||
|
|
||||||
|
char expired(Timer* timer)
|
||||||
|
{
|
||||||
|
portTickType now = xTaskGetTickCount();
|
||||||
|
int32_t left = timer->end_time - now;
|
||||||
|
return (left < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void countdown_ms(Timer* timer, unsigned int timeout)
|
||||||
|
{
|
||||||
|
portTickType now = xTaskGetTickCount();
|
||||||
|
timer->end_time = now + timeout / portTICK_RATE_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void countdown(Timer* timer, unsigned int timeout)
|
||||||
|
{
|
||||||
|
countdown_ms(timer, timeout * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int left_ms(Timer* timer)
|
||||||
|
{
|
||||||
|
portTickType now = xTaskGetTickCount();
|
||||||
|
int32_t left = timer->end_time - now;
|
||||||
|
return (left < 0) ? 0 : left / portTICK_RATE_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InitTimer(Timer* timer)
|
||||||
|
{
|
||||||
|
timer->end_time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int mqtt_esp_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
fd_set fdset;
|
||||||
|
int rc = 0;
|
||||||
|
int rcvd = 0;
|
||||||
|
FD_ZERO(&fdset);
|
||||||
|
FD_SET(n->my_socket, &fdset);
|
||||||
|
// It seems tv_sec actually means FreeRTOS tick
|
||||||
|
tv.tv_sec = timeout_ms / portTICK_RATE_MS;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
rc = select(n->my_socket + 1, &fdset, 0, 0, &tv);
|
||||||
|
if ((rc > 0) && (FD_ISSET(n->my_socket, &fdset)))
|
||||||
|
{
|
||||||
|
rcvd = recv(n->my_socket, buffer, len, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// select fail
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return rcvd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int mqtt_esp_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
fd_set fdset;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&fdset);
|
||||||
|
FD_SET(n->my_socket, &fdset);
|
||||||
|
// It seems tv_sec actually means FreeRTOS tick
|
||||||
|
tv.tv_sec = timeout_ms / portTICK_RATE_MS;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
rc = select(n->my_socket + 1, 0, &fdset, 0, &tv);
|
||||||
|
if ((rc > 0) && (FD_ISSET(n->my_socket, &fdset)))
|
||||||
|
{
|
||||||
|
rc = send(n->my_socket, buffer, len, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// select fail
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void NewNetwork(Network* n)
|
||||||
|
{
|
||||||
|
n->my_socket = -1;
|
||||||
|
n->mqttread = mqtt_esp_read;
|
||||||
|
n->mqttwrite = mqtt_esp_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int host2addr(const char *hostname , struct in_addr *in)
|
||||||
|
{
|
||||||
|
struct addrinfo hints, *servinfo, *p;
|
||||||
|
struct sockaddr_in *h;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
rv = getaddrinfo(hostname, 0 , &hints , &servinfo);
|
||||||
|
if (rv != 0)
|
||||||
|
{
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop through all the results and get the first resolve
|
||||||
|
for (p = servinfo; p != 0; p = p->ai_next)
|
||||||
|
{
|
||||||
|
h = (struct sockaddr_in *)p->ai_addr;
|
||||||
|
in->s_addr = h->sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
freeaddrinfo(servinfo); // all done with this structure
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ConnectNetwork(Network* n, const char* host, int port)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (host2addr(host, &(addr.sin_addr)) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
n->my_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if( n->my_socket < 0 )
|
||||||
|
{
|
||||||
|
// error
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ret = connect(n->my_socket, ( struct sockaddr *)&addr, sizeof(struct sockaddr_in));
|
||||||
|
if( ret < 0 )
|
||||||
|
{
|
||||||
|
// error
|
||||||
|
close(n->my_socket);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int DisconnectNetwork(Network* n)
|
||||||
|
{
|
||||||
|
close(n->my_socket);
|
||||||
|
n->my_socket = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
59
extras/paho_mqtt_c/MQTTESP8266.h
Normal file
59
extras/paho_mqtt_c/MQTTESP8266.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* @file MQTTESP8266.h
|
||||||
|
* @author Baoshi <mail(at)ba0sh1(dot)com>
|
||||||
|
* @version 0.1
|
||||||
|
* @date Sep 9, 2015
|
||||||
|
* @brief Eclipse Paho ported to ESP8266 RTOS
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
* @copyright
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015, Baoshi Zhu. All rights reserved.
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE.txt file.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTY. IN NO EVENT WILL THE AUTHOR(S) BE HELD LIABLE FOR ANY DAMAGES
|
||||||
|
* ARISING FROM THE USE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _MQTT_ESP8266_H_
|
||||||
|
#define _MQTT_ESP8266_H_
|
||||||
|
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <portmacro.h>
|
||||||
|
|
||||||
|
typedef struct Timer Timer;
|
||||||
|
|
||||||
|
struct Timer
|
||||||
|
{
|
||||||
|
portTickType end_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Network Network;
|
||||||
|
|
||||||
|
struct Network
|
||||||
|
{
|
||||||
|
int my_socket;
|
||||||
|
int (*mqttread) (Network*, unsigned char*, int, int);
|
||||||
|
int (*mqttwrite) (Network*, unsigned char*, int, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
char expired(Timer*);
|
||||||
|
void countdown_ms(Timer*, unsigned int);
|
||||||
|
void countdown(Timer*, unsigned int);
|
||||||
|
int left_ms(Timer*);
|
||||||
|
|
||||||
|
void InitTimer(Timer*);
|
||||||
|
|
||||||
|
int mqtt_esp_read(Network*, unsigned char*, int, int);
|
||||||
|
int mqtt_esp_write(Network*, unsigned char*, int, int);
|
||||||
|
void mqtt_esp_disconnect(Network*);
|
||||||
|
|
||||||
|
void NewNetwork(Network* n);
|
||||||
|
int ConnectNetwork(Network* n, const char* host, int port);
|
||||||
|
int DisconnectNetwork(Network* n);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _MQTT_ESP8266_H_ */
|
37
extras/paho_mqtt_c/MQTTFormat.h
Normal file
37
extras/paho_mqtt_c/MQTTFormat.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(MQTTFORMAT_H)
|
||||||
|
#define MQTTFORMAT_H
|
||||||
|
|
||||||
|
#include "StackTrace.h"
|
||||||
|
#include "MQTTPacket.h"
|
||||||
|
|
||||||
|
const char* MQTTPacket_getName(unsigned short packetid);
|
||||||
|
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data);
|
||||||
|
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent);
|
||||||
|
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
|
||||||
|
unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen);
|
||||||
|
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid);
|
||||||
|
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
|
||||||
|
MQTTString topicFilters[], int requestedQoSs[]);
|
||||||
|
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs);
|
||||||
|
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
|
||||||
|
int count, MQTTString topicFilters[]);
|
||||||
|
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
|
||||||
|
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
|
||||||
|
|
||||||
|
#endif
|
409
extras/paho_mqtt_c/MQTTPacket.c
Normal file
409
extras/paho_mqtt_c/MQTTPacket.c
Normal file
|
@ -0,0 +1,409 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
* Sergio R. Caprile - non-blocking packet read functions for stream transport
|
||||||
|
*******************************************************************************/
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include "StackTrace.h"
|
||||||
|
#include "MQTTPacket.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the message length according to the MQTT algorithm
|
||||||
|
* @param buf the buffer into which the encoded data is written
|
||||||
|
* @param length the length to be encoded
|
||||||
|
* @return the number of bytes written to buffer
|
||||||
|
*/
|
||||||
|
int MQTTPacket_encode(unsigned char* buf, int length)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
char d = length % 128;
|
||||||
|
length /= 128;
|
||||||
|
/* if there are more digits to encode, set the top bit of this digit */
|
||||||
|
if (length > 0)
|
||||||
|
d |= 0x80;
|
||||||
|
buf[rc++] = d;
|
||||||
|
} while (length > 0);
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the message length according to the MQTT algorithm
|
||||||
|
* @param getcharfn pointer to function to read the next character from the data source
|
||||||
|
* @param value the decoded length returned
|
||||||
|
* @return the number of bytes read from the socket
|
||||||
|
*/
|
||||||
|
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value)
|
||||||
|
{
|
||||||
|
unsigned char c;
|
||||||
|
int multiplier = 1;
|
||||||
|
int len = 0;
|
||||||
|
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
*value = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int rc = MQTTPACKET_READ_ERROR;
|
||||||
|
|
||||||
|
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||||
|
{
|
||||||
|
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
rc = (*getcharfn)(&c, 1);
|
||||||
|
if (rc != 1)
|
||||||
|
goto exit;
|
||||||
|
*value += (c & 127) * multiplier;
|
||||||
|
multiplier *= 128;
|
||||||
|
} while ((c & 128) != 0);
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTPacket_len(int rem_len)
|
||||||
|
{
|
||||||
|
rem_len += 1; /* header byte */
|
||||||
|
|
||||||
|
/* now remaining_length field */
|
||||||
|
if (rem_len < 128)
|
||||||
|
rem_len += 1;
|
||||||
|
else if (rem_len < 16384)
|
||||||
|
rem_len += 2;
|
||||||
|
else if (rem_len < 2097151)
|
||||||
|
rem_len += 3;
|
||||||
|
else
|
||||||
|
rem_len += 4;
|
||||||
|
return rem_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned char* bufptr;
|
||||||
|
|
||||||
|
int bufchar(unsigned char* c, int count)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
*c = *bufptr++;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MQTTPacket_decodeBuf(unsigned char* buf, int* value)
|
||||||
|
{
|
||||||
|
bufptr = buf;
|
||||||
|
return MQTTPacket_decode(bufchar, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates an integer from two bytes read from the input buffer
|
||||||
|
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||||
|
* @return the integer value calculated
|
||||||
|
*/
|
||||||
|
int readInt(unsigned char** pptr)
|
||||||
|
{
|
||||||
|
unsigned char* ptr = *pptr;
|
||||||
|
int len = 256*(*ptr) + (*(ptr+1));
|
||||||
|
*pptr += 2;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads one character from the input buffer.
|
||||||
|
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||||
|
* @return the character read
|
||||||
|
*/
|
||||||
|
char readChar(unsigned char** pptr)
|
||||||
|
{
|
||||||
|
char c = **pptr;
|
||||||
|
(*pptr)++;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes one character to an output buffer.
|
||||||
|
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||||
|
* @param c the character to write
|
||||||
|
*/
|
||||||
|
void writeChar(unsigned char** pptr, char c)
|
||||||
|
{
|
||||||
|
**pptr = c;
|
||||||
|
(*pptr)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an integer as 2 bytes to an output buffer.
|
||||||
|
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||||
|
* @param anInt the integer to write
|
||||||
|
*/
|
||||||
|
void writeInt(unsigned char** pptr, int anInt)
|
||||||
|
{
|
||||||
|
**pptr = (unsigned char)(anInt / 256);
|
||||||
|
(*pptr)++;
|
||||||
|
**pptr = (unsigned char)(anInt % 256);
|
||||||
|
(*pptr)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a "UTF" string to an output buffer. Converts C string to length-delimited.
|
||||||
|
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||||
|
* @param string the C string to write
|
||||||
|
*/
|
||||||
|
void writeCString(unsigned char** pptr, const char* string)
|
||||||
|
{
|
||||||
|
int len = strlen(string);
|
||||||
|
writeInt(pptr, len);
|
||||||
|
memcpy(*pptr, string, len);
|
||||||
|
*pptr += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int getLenStringLen(char* ptr)
|
||||||
|
{
|
||||||
|
int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring)
|
||||||
|
{
|
||||||
|
if (mqttstring.lenstring.len > 0)
|
||||||
|
{
|
||||||
|
writeInt(pptr, mqttstring.lenstring.len);
|
||||||
|
memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len);
|
||||||
|
*pptr += mqttstring.lenstring.len;
|
||||||
|
}
|
||||||
|
else if (mqttstring.cstring)
|
||||||
|
writeCString(pptr, mqttstring.cstring);
|
||||||
|
else
|
||||||
|
writeInt(pptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mqttstring the MQTTString structure into which the data is to be read
|
||||||
|
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||||
|
* @param enddata pointer to the end of the data: do not read beyond
|
||||||
|
* @return 1 if successful, 0 if not
|
||||||
|
*/
|
||||||
|
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
/* the first two bytes are the length of the string */
|
||||||
|
if (enddata - (*pptr) > 1) /* enough length to read the integer? */
|
||||||
|
{
|
||||||
|
mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */
|
||||||
|
if (&(*pptr)[mqttstring->lenstring.len] <= enddata)
|
||||||
|
{
|
||||||
|
mqttstring->lenstring.data = (char*)*pptr;
|
||||||
|
*pptr += mqttstring->lenstring.len;
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mqttstring->cstring = NULL;
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string
|
||||||
|
* @param mqttstring the string to return the length of
|
||||||
|
* @return the length of the string
|
||||||
|
*/
|
||||||
|
int MQTTstrlen(MQTTString mqttstring)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (mqttstring.cstring)
|
||||||
|
rc = strlen(mqttstring.cstring);
|
||||||
|
else
|
||||||
|
rc = mqttstring.lenstring.len;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares an MQTTString to a C string
|
||||||
|
* @param a the MQTTString to compare
|
||||||
|
* @param bptr the C string to compare
|
||||||
|
* @return boolean - equal or not
|
||||||
|
*/
|
||||||
|
int MQTTPacket_equals(MQTTString* a, char* bptr)
|
||||||
|
{
|
||||||
|
int alen = 0,
|
||||||
|
blen = 0;
|
||||||
|
char *aptr;
|
||||||
|
|
||||||
|
if (a->cstring)
|
||||||
|
{
|
||||||
|
aptr = a->cstring;
|
||||||
|
alen = strlen(a->cstring);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aptr = a->lenstring.data;
|
||||||
|
alen = a->lenstring.len;
|
||||||
|
}
|
||||||
|
blen = strlen(bptr);
|
||||||
|
|
||||||
|
return (alen == blen) && (strncmp(aptr, bptr, alen) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to read packet data from some source into a buffer
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer
|
||||||
|
* @param getfn pointer to a function which will read any number of bytes from the needed source
|
||||||
|
* @return integer MQTT packet type, or -1 on error
|
||||||
|
* @note the whole message must fit into the caller's buffer
|
||||||
|
*/
|
||||||
|
int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int))
|
||||||
|
{
|
||||||
|
int rc = -1;
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
int len = 0;
|
||||||
|
int rem_len = 0;
|
||||||
|
|
||||||
|
/* 1. read the header byte. This has the packet type in it */
|
||||||
|
if ((*getfn)(buf, 1) != 1)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
len = 1;
|
||||||
|
/* 2. read the remaining length. This is variable in itself */
|
||||||
|
MQTTPacket_decode(getfn, &rem_len);
|
||||||
|
len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */
|
||||||
|
|
||||||
|
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
|
||||||
|
if((rem_len + len) > buflen)
|
||||||
|
goto exit;
|
||||||
|
if ((*getfn)(buf + len, rem_len) != rem_len)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
header.byte = buf[0];
|
||||||
|
rc = header.bits.type;
|
||||||
|
exit:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the message length according to the MQTT algorithm, non-blocking
|
||||||
|
* @param trp pointer to a transport structure holding what is needed to solve getting data from it
|
||||||
|
* @param value the decoded length returned
|
||||||
|
* @return integer the number of bytes read from the socket, 0 for call again, or -1 on error
|
||||||
|
*/
|
||||||
|
static int MQTTPacket_decodenb(MQTTTransport *trp)
|
||||||
|
{
|
||||||
|
unsigned char c;
|
||||||
|
int rc = MQTTPACKET_READ_ERROR;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
if(trp->len == 0){ /* initialize on first call */
|
||||||
|
trp->multiplier = 1;
|
||||||
|
trp->rem_len = 0;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
int frc;
|
||||||
|
if (++(trp->len) > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||||
|
goto exit;
|
||||||
|
if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1)
|
||||||
|
goto exit;
|
||||||
|
if (frc == 0){
|
||||||
|
rc = 0;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
trp->rem_len += (c & 127) * trp->multiplier;
|
||||||
|
trp->multiplier *= 128;
|
||||||
|
} while ((c & 128) != 0);
|
||||||
|
rc = trp->len;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to read packet data from some source into a buffer, non-blocking
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer
|
||||||
|
* @param trp pointer to a transport structure holding what is needed to solve getting data from it
|
||||||
|
* @return integer MQTT packet type, 0 for call again, or -1 on error
|
||||||
|
* @note the whole message must fit into the caller's buffer
|
||||||
|
*/
|
||||||
|
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp)
|
||||||
|
{
|
||||||
|
int rc = -1, frc;
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
|
||||||
|
switch(trp->state){
|
||||||
|
default:
|
||||||
|
trp->state = 0;
|
||||||
|
/*FALLTHROUGH*/
|
||||||
|
case 0:
|
||||||
|
/* read the header byte. This has the packet type in it */
|
||||||
|
if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1)
|
||||||
|
goto exit;
|
||||||
|
if (frc == 0)
|
||||||
|
return 0;
|
||||||
|
trp->len = 0;
|
||||||
|
++trp->state;
|
||||||
|
/*FALLTHROUGH*/
|
||||||
|
/* read the remaining length. This is variable in itself */
|
||||||
|
case 1:
|
||||||
|
if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR)
|
||||||
|
goto exit;
|
||||||
|
if(frc == 0)
|
||||||
|
return 0;
|
||||||
|
trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */
|
||||||
|
if((trp->rem_len + trp->len) > buflen)
|
||||||
|
goto exit;
|
||||||
|
++trp->state;
|
||||||
|
/*FALLTHROUGH*/
|
||||||
|
case 2:
|
||||||
|
/* read the rest of the buffer using a callback to supply the rest of the data */
|
||||||
|
if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1)
|
||||||
|
goto exit;
|
||||||
|
if (frc == 0)
|
||||||
|
return 0;
|
||||||
|
trp->rem_len -= frc;
|
||||||
|
trp->len += frc;
|
||||||
|
if(trp->rem_len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
header.byte = buf[0];
|
||||||
|
rc = header.bits.type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
trp->state = 0;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
133
extras/paho_mqtt_c/MQTTPacket.h
Normal file
133
extras/paho_mqtt_c/MQTTPacket.h
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MQTTPACKET_H_
|
||||||
|
#define MQTTPACKET_H_
|
||||||
|
|
||||||
|
#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(WIN32_DLL) || defined(WIN64_DLL)
|
||||||
|
#define DLLImport __declspec(dllimport)
|
||||||
|
#define DLLExport __declspec(dllexport)
|
||||||
|
#elif defined(LINUX_SO)
|
||||||
|
#define DLLImport extern
|
||||||
|
#define DLLExport __attribute__ ((visibility ("default")))
|
||||||
|
#else
|
||||||
|
#define DLLImport
|
||||||
|
#define DLLExport
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum errors
|
||||||
|
{
|
||||||
|
MQTTPACKET_BUFFER_TOO_SHORT = -2,
|
||||||
|
MQTTPACKET_READ_ERROR = -1,
|
||||||
|
MQTTPACKET_READ_COMPLETE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum msgTypes
|
||||||
|
{
|
||||||
|
CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
|
||||||
|
PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
|
||||||
|
PINGREQ, PINGRESP, DISCONNECT
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitfields for the MQTT header byte.
|
||||||
|
*/
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
unsigned char byte; /**< the whole byte */
|
||||||
|
#if defined(REVERSED)
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int type : 4; /**< message type nibble */
|
||||||
|
unsigned int dup : 1; /**< DUP flag bit */
|
||||||
|
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||||
|
unsigned int retain : 1; /**< retained flag bit */
|
||||||
|
} bits;
|
||||||
|
#else
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int retain : 1; /**< retained flag bit */
|
||||||
|
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||||
|
unsigned int dup : 1; /**< DUP flag bit */
|
||||||
|
unsigned int type : 4; /**< message type nibble */
|
||||||
|
} bits;
|
||||||
|
#endif
|
||||||
|
} MQTTHeader;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
char* data;
|
||||||
|
} MQTTLenString;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char* cstring;
|
||||||
|
MQTTLenString lenstring;
|
||||||
|
} MQTTString;
|
||||||
|
|
||||||
|
#define MQTTString_initializer {NULL, {0, NULL}}
|
||||||
|
|
||||||
|
int MQTTstrlen(MQTTString mqttstring);
|
||||||
|
|
||||||
|
#include "MQTTConnect.h"
|
||||||
|
#include "MQTTPublish.h"
|
||||||
|
#include "MQTTSubscribe.h"
|
||||||
|
#include "MQTTUnsubscribe.h"
|
||||||
|
#include "MQTTFormat.h"
|
||||||
|
|
||||||
|
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid);
|
||||||
|
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen);
|
||||||
|
|
||||||
|
int MQTTPacket_len(int rem_len);
|
||||||
|
int MQTTPacket_equals(MQTTString* a, char* b);
|
||||||
|
|
||||||
|
int MQTTPacket_encode(unsigned char* buf, int length);
|
||||||
|
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value);
|
||||||
|
int MQTTPacket_decodeBuf(unsigned char* buf, int* value);
|
||||||
|
|
||||||
|
int readInt(unsigned char** pptr);
|
||||||
|
char readChar(unsigned char** pptr);
|
||||||
|
void writeChar(unsigned char** pptr, char c);
|
||||||
|
void writeInt(unsigned char** pptr, int anInt);
|
||||||
|
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata);
|
||||||
|
void writeCString(unsigned char** pptr, const char* string);
|
||||||
|
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring);
|
||||||
|
|
||||||
|
DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int));
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */
|
||||||
|
void *sck; /* pointer to whatever the system may use to identify the transport */
|
||||||
|
int multiplier;
|
||||||
|
int rem_len;
|
||||||
|
int len;
|
||||||
|
char state;
|
||||||
|
}MQTTTransport;
|
||||||
|
|
||||||
|
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp);
|
||||||
|
|
||||||
|
#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MQTTPACKET_H_ */
|
38
extras/paho_mqtt_c/MQTTPublish.h
Normal file
38
extras/paho_mqtt_c/MQTTPublish.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MQTTPUBLISH_H_
|
||||||
|
#define MQTTPUBLISH_H_
|
||||||
|
|
||||||
|
#if !defined(DLLImport)
|
||||||
|
#define DLLImport
|
||||||
|
#endif
|
||||||
|
#if !defined(DLLExport)
|
||||||
|
#define DLLExport
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
|
||||||
|
MQTTString topicName, unsigned char* payload, int payloadlen);
|
||||||
|
|
||||||
|
DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
|
||||||
|
unsigned char** payload, int* payloadlen, unsigned char* buf, int len);
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid);
|
||||||
|
DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid);
|
||||||
|
DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid);
|
||||||
|
|
||||||
|
#endif /* MQTTPUBLISH_H_ */
|
169
extras/paho_mqtt_c/MQTTSerializePublish.c
Normal file
169
extras/paho_mqtt_c/MQTTSerializePublish.c
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
* Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144
|
||||||
|
*******************************************************************************/
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include "MQTTPacket.h"
|
||||||
|
#include "StackTrace.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the length of the MQTT publish packet that would be produced using the supplied parameters
|
||||||
|
* @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0)
|
||||||
|
* @param topicName the topic name to be used in the publish
|
||||||
|
* @param payloadlen the length of the payload to be sent
|
||||||
|
* @return the length of buffer needed to contain the serialized version of the packet
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
len += 2 + MQTTstrlen(topicName) + payloadlen;
|
||||||
|
if (qos > 0)
|
||||||
|
len += 2; /* packetid */
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the supplied publish data into the supplied buffer, ready for sending
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer
|
||||||
|
* @param dup integer - the MQTT dup flag
|
||||||
|
* @param qos integer - the MQTT QoS value
|
||||||
|
* @param retained integer - the MQTT retained flag
|
||||||
|
* @param packetid integer - the MQTT packet identifier
|
||||||
|
* @param topicName MQTTString - the MQTT topic in the publish
|
||||||
|
* @param payload byte buffer - the MQTT publish payload
|
||||||
|
* @param payloadlen integer - the length of the MQTT payload
|
||||||
|
* @return the length of the serialized data. <= 0 indicates error
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
|
||||||
|
MQTTString topicName, unsigned char* payload, int payloadlen)
|
||||||
|
{
|
||||||
|
unsigned char *ptr = buf;
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
int rem_len = 0;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen)
|
||||||
|
{
|
||||||
|
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.bits.type = PUBLISH;
|
||||||
|
header.bits.dup = dup;
|
||||||
|
header.bits.qos = qos;
|
||||||
|
header.bits.retain = retained;
|
||||||
|
writeChar(&ptr, header.byte); /* write header */
|
||||||
|
|
||||||
|
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||||
|
|
||||||
|
writeMQTTString(&ptr, topicName);
|
||||||
|
|
||||||
|
if (qos > 0)
|
||||||
|
writeInt(&ptr, packetid);
|
||||||
|
|
||||||
|
memcpy(ptr, payload, payloadlen);
|
||||||
|
ptr += payloadlen;
|
||||||
|
|
||||||
|
rc = ptr - buf;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the ack packet into the supplied buffer.
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer
|
||||||
|
* @param type the MQTT packet type
|
||||||
|
* @param dup the MQTT dup flag
|
||||||
|
* @param packetid the MQTT packet identifier
|
||||||
|
* @return serialized length, or error if 0
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
|
||||||
|
{
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
int rc = 0;
|
||||||
|
unsigned char *ptr = buf;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
if (buflen < 4)
|
||||||
|
{
|
||||||
|
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
header.bits.type = packettype;
|
||||||
|
header.bits.dup = dup;
|
||||||
|
header.bits.qos = (packettype == PUBREL) ? 1 : 0;
|
||||||
|
writeChar(&ptr, header.byte); /* write header */
|
||||||
|
|
||||||
|
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||||
|
writeInt(&ptr, packetid);
|
||||||
|
rc = ptr - buf;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a puback packet into the supplied buffer.
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer
|
||||||
|
* @param packetid integer - the MQTT packet identifier
|
||||||
|
* @return serialized length, or error if 0
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid)
|
||||||
|
{
|
||||||
|
return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a pubrel packet into the supplied buffer.
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer
|
||||||
|
* @param dup integer - the MQTT dup flag
|
||||||
|
* @param packetid integer - the MQTT packet identifier
|
||||||
|
* @return serialized length, or error if 0
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid)
|
||||||
|
{
|
||||||
|
return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a pubrel packet into the supplied buffer.
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied buffer
|
||||||
|
* @param packetid integer - the MQTT packet identifier
|
||||||
|
* @return serialized length, or error if 0
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid)
|
||||||
|
{
|
||||||
|
return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
39
extras/paho_mqtt_c/MQTTSubscribe.h
Normal file
39
extras/paho_mqtt_c/MQTTSubscribe.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MQTTSUBSCRIBE_H_
|
||||||
|
#define MQTTSUBSCRIBE_H_
|
||||||
|
|
||||||
|
#if !defined(DLLImport)
|
||||||
|
#define DLLImport
|
||||||
|
#endif
|
||||||
|
#if !defined(DLLExport)
|
||||||
|
#define DLLExport
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||||
|
int count, MQTTString topicFilters[], int requestedQoSs[]);
|
||||||
|
|
||||||
|
DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid,
|
||||||
|
int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len);
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs);
|
||||||
|
|
||||||
|
DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MQTTSUBSCRIBE_H_ */
|
137
extras/paho_mqtt_c/MQTTSubscribeClient.c
Normal file
137
extras/paho_mqtt_c/MQTTSubscribeClient.c
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
*******************************************************************************/
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include "MQTTPacket.h"
|
||||||
|
#include "StackTrace.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters
|
||||||
|
* @param count the number of topic filter strings in topicFilters
|
||||||
|
* @param topicFilters the array of topic filter strings to be used in the publish
|
||||||
|
* @return the length of buffer needed to contain the serialized version of the packet
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int len = 2; /* packetid */
|
||||||
|
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the supplied subscribe data into the supplied buffer, ready for sending
|
||||||
|
* @param buf the buffer into which the packet will be serialized
|
||||||
|
* @param buflen the length in bytes of the supplied bufferr
|
||||||
|
* @param dup integer - the MQTT dup flag
|
||||||
|
* @param packetid integer - the MQTT packet identifier
|
||||||
|
* @param count - number of members in the topicFilters and reqQos arrays
|
||||||
|
* @param topicFilters - array of topic filter names
|
||||||
|
* @param requestedQoSs - array of requested QoS
|
||||||
|
* @return the length of the serialized data. <= 0 indicates error
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count,
|
||||||
|
MQTTString topicFilters[], int requestedQoSs[])
|
||||||
|
{
|
||||||
|
unsigned char *ptr = buf;
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
int rem_len = 0;
|
||||||
|
int rc = 0;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen)
|
||||||
|
{
|
||||||
|
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.byte = 0;
|
||||||
|
header.bits.type = SUBSCRIBE;
|
||||||
|
header.bits.dup = dup;
|
||||||
|
header.bits.qos = 1;
|
||||||
|
writeChar(&ptr, header.byte); /* write header */
|
||||||
|
|
||||||
|
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||||
|
|
||||||
|
writeInt(&ptr, packetid);
|
||||||
|
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
writeMQTTString(&ptr, topicFilters[i]);
|
||||||
|
writeChar(&ptr, requestedQoSs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ptr - buf;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the supplied (wire) buffer into suback data
|
||||||
|
* @param packetid returned integer - the MQTT packet identifier
|
||||||
|
* @param maxcount - the maximum number of members allowed in the grantedQoSs array
|
||||||
|
* @param count returned integer - number of members in the grantedQoSs array
|
||||||
|
* @param grantedQoSs returned array of integers - the granted qualities of service
|
||||||
|
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||||
|
* @param buflen the length in bytes of the data in the supplied buffer
|
||||||
|
* @return error code. 1 is success, 0 is failure
|
||||||
|
*/
|
||||||
|
int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen)
|
||||||
|
{
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
unsigned char* curdata = buf;
|
||||||
|
unsigned char* enddata = NULL;
|
||||||
|
int rc = 0;
|
||||||
|
int mylen;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
header.byte = readChar(&curdata);
|
||||||
|
if (header.bits.type != SUBACK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||||
|
enddata = curdata + mylen;
|
||||||
|
if (enddata - curdata < 2)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
*packetid = readInt(&curdata);
|
||||||
|
|
||||||
|
*count = 0;
|
||||||
|
while (curdata < enddata)
|
||||||
|
{
|
||||||
|
if (*count > maxcount)
|
||||||
|
{
|
||||||
|
rc = -1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
grantedQoSs[(*count)++] = readChar(&curdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = 1;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
38
extras/paho_mqtt_c/MQTTUnsubscribe.h
Normal file
38
extras/paho_mqtt_c/MQTTUnsubscribe.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MQTTUNSUBSCRIBE_H_
|
||||||
|
#define MQTTUNSUBSCRIBE_H_
|
||||||
|
|
||||||
|
#if !defined(DLLImport)
|
||||||
|
#define DLLImport
|
||||||
|
#endif
|
||||||
|
#if !defined(DLLExport)
|
||||||
|
#define DLLExport
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||||
|
int count, MQTTString topicFilters[]);
|
||||||
|
|
||||||
|
DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[],
|
||||||
|
unsigned char* buf, int len);
|
||||||
|
|
||||||
|
DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid);
|
||||||
|
|
||||||
|
DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len);
|
||||||
|
|
||||||
|
#endif /* MQTTUNSUBSCRIBE_H_ */
|
106
extras/paho_mqtt_c/MQTTUnsubscribeClient.c
Normal file
106
extras/paho_mqtt_c/MQTTUnsubscribeClient.c
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
*******************************************************************************/
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include "MQTTPacket.h"
|
||||||
|
#include "StackTrace.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters
|
||||||
|
* @param count the number of topic filter strings in topicFilters
|
||||||
|
* @param topicFilters the array of topic filter strings to be used in the publish
|
||||||
|
* @return the length of buffer needed to contain the serialized version of the packet
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int len = 2; /* packetid */
|
||||||
|
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the supplied unsubscribe data into the supplied buffer, ready for sending
|
||||||
|
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||||
|
* @param buflen the length in bytes of the data in the supplied buffer
|
||||||
|
* @param dup integer - the MQTT dup flag
|
||||||
|
* @param packetid integer - the MQTT packet identifier
|
||||||
|
* @param count - number of members in the topicFilters array
|
||||||
|
* @param topicFilters - array of topic filter names
|
||||||
|
* @return the length of the serialized data. <= 0 indicates error
|
||||||
|
*/
|
||||||
|
int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||||
|
int count, MQTTString topicFilters[])
|
||||||
|
{
|
||||||
|
unsigned char *ptr = buf;
|
||||||
|
MQTTHeader header = {0};
|
||||||
|
int rem_len = 0;
|
||||||
|
int rc = -1;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen)
|
||||||
|
{
|
||||||
|
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.byte = 0;
|
||||||
|
header.bits.type = UNSUBSCRIBE;
|
||||||
|
header.bits.dup = dup;
|
||||||
|
header.bits.qos = 1;
|
||||||
|
writeChar(&ptr, header.byte); /* write header */
|
||||||
|
|
||||||
|
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||||
|
|
||||||
|
writeInt(&ptr, packetid);
|
||||||
|
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
writeMQTTString(&ptr, topicFilters[i]);
|
||||||
|
|
||||||
|
rc = ptr - buf;
|
||||||
|
exit:
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the supplied (wire) buffer into unsuback data
|
||||||
|
* @param packetid returned integer - the MQTT packet identifier
|
||||||
|
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||||
|
* @param buflen the length in bytes of the data in the supplied buffer
|
||||||
|
* @return error code. 1 is success, 0 is failure
|
||||||
|
*/
|
||||||
|
int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen)
|
||||||
|
{
|
||||||
|
unsigned char type = 0;
|
||||||
|
unsigned char dup = 0;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
FUNC_ENTRY;
|
||||||
|
rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen);
|
||||||
|
if (type == UNSUBACK)
|
||||||
|
rc = 1;
|
||||||
|
FUNC_EXIT_RC(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
77
extras/paho_mqtt_c/StackTrace.h
Normal file
77
extras/paho_mqtt_c/StackTrace.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 IBM Corp.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||||
|
*
|
||||||
|
* The Eclipse Public License is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||||
|
* Ian Craggs - fix for bug #434081
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef STACKTRACE_H_
|
||||||
|
#define STACKTRACE_H_
|
||||||
|
|
||||||
|
#define NOSTACKTRACE 1
|
||||||
|
|
||||||
|
#if defined(NOSTACKTRACE)
|
||||||
|
#define FUNC_ENTRY
|
||||||
|
#define FUNC_ENTRY_NOLOG
|
||||||
|
#define FUNC_ENTRY_MED
|
||||||
|
#define FUNC_ENTRY_MAX
|
||||||
|
#define FUNC_EXIT
|
||||||
|
#define FUNC_EXIT_NOLOG
|
||||||
|
#define FUNC_EXIT_MED
|
||||||
|
#define FUNC_EXIT_MAX
|
||||||
|
#define FUNC_EXIT_RC(x)
|
||||||
|
#define FUNC_EXIT_MED_RC(x)
|
||||||
|
#define FUNC_EXIT_MAX_RC(x)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined(WIN32)
|
||||||
|
#define inline __inline
|
||||||
|
#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM)
|
||||||
|
#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1)
|
||||||
|
#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM)
|
||||||
|
#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM)
|
||||||
|
#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM)
|
||||||
|
#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1)
|
||||||
|
#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM)
|
||||||
|
#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM)
|
||||||
|
#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM)
|
||||||
|
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM)
|
||||||
|
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM)
|
||||||
|
#else
|
||||||
|
#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM)
|
||||||
|
#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1)
|
||||||
|
#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM)
|
||||||
|
#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM)
|
||||||
|
#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM)
|
||||||
|
#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1)
|
||||||
|
#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM)
|
||||||
|
#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM)
|
||||||
|
#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM)
|
||||||
|
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM)
|
||||||
|
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM)
|
||||||
|
|
||||||
|
void StackTrace_entry(const char* name, int line, int trace);
|
||||||
|
void StackTrace_exit(const char* name, int line, void* return_value, int trace);
|
||||||
|
|
||||||
|
void StackTrace_printStack(FILE* dest);
|
||||||
|
char* StackTrace_get(unsigned long);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* STACKTRACE_H_ */
|
9
extras/paho_mqtt_c/component.mk
Normal file
9
extras/paho_mqtt_c/component.mk
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Component makefile for extras/paho_mqtt_c
|
||||||
|
|
||||||
|
# expected anyone using bmp driver includes it as 'paho_mqtt_c/MQTT*.h'
|
||||||
|
INC_DIRS += $(paho_mqtt_c_ROOT)..
|
||||||
|
|
||||||
|
# args for passing into compile rule generation
|
||||||
|
paho_mqtt_c_SRC_DIR = $(paho_mqtt_c_ROOT)
|
||||||
|
|
||||||
|
$(eval $(call component_compile_rules,paho_mqtt_c))
|
|
@ -22,7 +22,8 @@
|
||||||
#include <espressif/esp_system.h>
|
#include <espressif/esp_system.h>
|
||||||
|
|
||||||
#include "ota-tftp.h"
|
#include "ota-tftp.h"
|
||||||
#include "rboot-ota.h"
|
#include "rboot.h"
|
||||||
|
#include "rboot-api.h"
|
||||||
|
|
||||||
#define TFTP_FIRMWARE_FILE "firmware.bin"
|
#define TFTP_FIRMWARE_FILE "firmware.bin"
|
||||||
#define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
|
#define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
|
||||||
|
@ -112,7 +113,7 @@ static void tftp_task(void *listen_port)
|
||||||
netbuf_delete(netbuf);
|
netbuf_delete(netbuf);
|
||||||
|
|
||||||
/* Find next free slot - this requires flash unmapping so best done when no packets in flight */
|
/* Find next free slot - this requires flash unmapping so best done when no packets in flight */
|
||||||
rboot_config_t conf;
|
rboot_config conf;
|
||||||
conf = rboot_get_config();
|
conf = rboot_get_config();
|
||||||
int slot = (conf.current_rom + 1) % conf.count;
|
int slot = (conf.current_rom + 1) % conf.count;
|
||||||
|
|
||||||
|
|
214
extras/rboot-ota/rboot-api.c
Normal file
214
extras/rboot-ota/rboot-api.c
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// rBoot OTA and config API for ESP8266.
|
||||||
|
// Copyright 2015 Richard A Burton
|
||||||
|
// richardaburton@gmail.com
|
||||||
|
// See license.txt for license terms.
|
||||||
|
// OTA code based on SDK sample from Espressif.
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <rboot.h>
|
||||||
|
#include <string.h>
|
||||||
|
//#include <c_types.h>
|
||||||
|
//#include <spi_flash.h>
|
||||||
|
|
||||||
|
// detect rtos sdk (not ideal method!)
|
||||||
|
#ifdef IRAM_ATTR
|
||||||
|
#define os_free(s) vPortFree(s)
|
||||||
|
#define os_malloc(s) pvPortMalloc(s)
|
||||||
|
#else
|
||||||
|
#include <mem.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RBOOT_INTEGRATION
|
||||||
|
#include <rboot-integration.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "rboot-api.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOT_CONFIG_CHKSUM) || defined(BOOT_RTC_ENABLED)
|
||||||
|
// calculate checksum for block of data
|
||||||
|
// from start up to (but excluding) end
|
||||||
|
static uint8 calc_chksum(uint8 *start, uint8 *end) {
|
||||||
|
uint8 chksum = CHKSUM_INIT;
|
||||||
|
while(start < end) {
|
||||||
|
chksum ^= *start;
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
return chksum;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// get the rboot config
|
||||||
|
rboot_config ICACHE_FLASH_ATTR rboot_get_config(void) {
|
||||||
|
rboot_config conf;
|
||||||
|
spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)&conf, sizeof(rboot_config));
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the rboot config
|
||||||
|
// preserves the contents of the rest of the sector,
|
||||||
|
// so the rest of the sector can be used to store user data
|
||||||
|
// updates checksum automatically (if enabled)
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf) {
|
||||||
|
uint8 *buffer;
|
||||||
|
buffer = (uint8*)os_malloc(SECTOR_SIZE);
|
||||||
|
if (!buffer) {
|
||||||
|
//os_printf("No ram!\r\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BOOT_CONFIG_CHKSUM
|
||||||
|
conf->chksum = calc_chksum((uint8*)conf, (uint8*)&conf->chksum);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)((void*)buffer), SECTOR_SIZE);
|
||||||
|
memcpy(buffer, conf, sizeof(rboot_config));
|
||||||
|
vPortEnterCritical();
|
||||||
|
spi_flash_erase_sector(BOOT_CONFIG_SECTOR);
|
||||||
|
vPortExitCritical();
|
||||||
|
taskYIELD();
|
||||||
|
vPortEnterCritical();
|
||||||
|
//spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)((void*)buffer), SECTOR_SIZE);
|
||||||
|
spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)((void*)buffer), SECTOR_SIZE);
|
||||||
|
vPortExitCritical();
|
||||||
|
os_free(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current boot rom
|
||||||
|
uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void) {
|
||||||
|
rboot_config conf;
|
||||||
|
conf = rboot_get_config();
|
||||||
|
return conf.current_rom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set current boot rom
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom) {
|
||||||
|
rboot_config conf;
|
||||||
|
conf = rboot_get_config();
|
||||||
|
if (rom >= conf.count) return false;
|
||||||
|
conf.current_rom = rom;
|
||||||
|
return rboot_set_config(&conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the write status struct, based on supplied start address
|
||||||
|
rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr) {
|
||||||
|
rboot_write_status status = {0};
|
||||||
|
status.start_addr = start_addr;
|
||||||
|
status.start_sector = start_addr / SECTOR_SIZE;
|
||||||
|
status.last_sector_erased = status.start_sector - 1;
|
||||||
|
//status.max_sector_count = 200;
|
||||||
|
//os_printf("init addr: 0x%08x\r\n", start_addr);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to do the actual writing to flash
|
||||||
|
// call repeatedly with more data (max len per write is the flash sector size (4k))
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len) {
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
uint8 *buffer;
|
||||||
|
int32 lastsect;
|
||||||
|
|
||||||
|
if (data == NULL || len == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a buffer
|
||||||
|
buffer = (uint8 *)os_malloc(len + status->extra_count);
|
||||||
|
if (!buffer) {
|
||||||
|
//os_printf("No ram!\r\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy in any remaining bytes from last chunk
|
||||||
|
memcpy(buffer, status->extra_bytes, status->extra_count);
|
||||||
|
// copy in new data
|
||||||
|
memcpy(buffer + status->extra_count, data, len);
|
||||||
|
|
||||||
|
// calculate length, must be multiple of 4
|
||||||
|
// save any remaining bytes for next go
|
||||||
|
len += status->extra_count;
|
||||||
|
status->extra_count = len % 4;
|
||||||
|
len -= status->extra_count;
|
||||||
|
memcpy(status->extra_bytes, buffer + len, status->extra_count);
|
||||||
|
|
||||||
|
// check data will fit
|
||||||
|
//if (status->start_addr + len < (status->start_sector + status->max_sector_count) * SECTOR_SIZE) {
|
||||||
|
|
||||||
|
// erase any additional sectors needed by this chunk
|
||||||
|
lastsect = ((status->start_addr + len) - 1) / SECTOR_SIZE;
|
||||||
|
while (lastsect > status->last_sector_erased) {
|
||||||
|
status->last_sector_erased++;
|
||||||
|
spi_flash_erase_sector(status->last_sector_erased);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write current chunk
|
||||||
|
//os_printf("write addr: 0x%08x, len: 0x%04x\r\n", status->start_addr, len);
|
||||||
|
if (spi_flash_write(status->start_addr, (uint32 *)((void*)buffer), len) == SPI_FLASH_RESULT_OK) {
|
||||||
|
ret = true;
|
||||||
|
status->start_addr += len;
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
|
os_free(buffer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BOOT_RTC_ENABLED
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_get_rtc_data(rboot_rtc_data *rtc) {
|
||||||
|
if (system_rtc_mem_read(RBOOT_RTC_ADDR, rtc, sizeof(rboot_rtc_data))) {
|
||||||
|
return (rtc->chksum == calc_chksum((uint8*)rtc, (uint8*)&rtc->chksum));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_set_rtc_data(rboot_rtc_data *rtc) {
|
||||||
|
// calculate checksum
|
||||||
|
rtc->chksum = calc_chksum((uint8*)rtc, (uint8*)&rtc->chksum);
|
||||||
|
return system_rtc_mem_write(RBOOT_RTC_ADDR, rtc, sizeof(rboot_rtc_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_set_temp_rom(uint8 rom) {
|
||||||
|
rboot_rtc_data rtc;
|
||||||
|
// invalid data in rtc?
|
||||||
|
if (!rboot_get_rtc_data(&rtc)) {
|
||||||
|
// set basics
|
||||||
|
rtc.magic = RBOOT_RTC_MAGIC;
|
||||||
|
rtc.last_mode = MODE_STANDARD;
|
||||||
|
rtc.last_rom = 0;
|
||||||
|
}
|
||||||
|
// set next boot to temp mode with specified rom
|
||||||
|
rtc.next_mode = MODE_TEMP_ROM;
|
||||||
|
rtc.temp_rom = rom;
|
||||||
|
|
||||||
|
return rboot_set_rtc_data(&rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_get_last_boot_rom(uint8 *rom) {
|
||||||
|
rboot_rtc_data rtc;
|
||||||
|
if (rboot_get_rtc_data(&rtc)) {
|
||||||
|
*rom = rtc.last_rom;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode) {
|
||||||
|
rboot_rtc_data rtc;
|
||||||
|
if (rboot_get_rtc_data(&rtc)) {
|
||||||
|
*mode = rtc.last_mode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
136
extras/rboot-ota/rboot-api.h
Normal file
136
extras/rboot-ota/rboot-api.h
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
#ifndef __RBOOT_API_H__
|
||||||
|
#define __RBOOT_API_H__
|
||||||
|
|
||||||
|
/** @defgroup rboot rBoot API
|
||||||
|
* @brief rBoot for ESP8266 API allows runtime code to access the rBoot configuration.
|
||||||
|
* Configuration may be read to use within the main firmware or updated to
|
||||||
|
* affect next boot behavior.
|
||||||
|
* @copyright 2015 Richard A Burton
|
||||||
|
* @author richardaburton@gmail.com
|
||||||
|
* @author OTA code based on SDK sample from Espressif
|
||||||
|
* @license See licence.txt for license terms.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <rboot.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Structure defining flash write status
|
||||||
|
* @note The user application should not modify the contents of this
|
||||||
|
* structure.
|
||||||
|
* @see rboot_write_flash
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32 start_addr;
|
||||||
|
uint32 start_sector;
|
||||||
|
//uint32 max_sector_count;
|
||||||
|
int32 last_sector_erased;
|
||||||
|
uint8 extra_count;
|
||||||
|
uint8 extra_bytes[4];
|
||||||
|
} rboot_write_status;
|
||||||
|
|
||||||
|
/** @brief Read rBoot configuration from flash
|
||||||
|
* @retval rboot_config Copy of the rBoot configuration
|
||||||
|
* @note Returns rboot_config (defined in rboot.h) allowing you to modify any values
|
||||||
|
* in it, including the ROM layout.
|
||||||
|
*/
|
||||||
|
rboot_config ICACHE_FLASH_ATTR rboot_get_config(void);
|
||||||
|
|
||||||
|
/** @brief Write rBoot configuration to flash memory
|
||||||
|
* @param conf pointer to a rboot_config structure containing configuration to save
|
||||||
|
* @retval bool True on success
|
||||||
|
* @note Saves the rboot_config structure back to configuration sector (BOOT_CONFIG_SECTOR)
|
||||||
|
* of the flash, while maintaining the contents of the rest of the sector.
|
||||||
|
* You can use the rest of this sector for your app settings, as long as you
|
||||||
|
* protect this structure when you do so.
|
||||||
|
*/
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf);
|
||||||
|
|
||||||
|
/** @brief Get index of current ROM
|
||||||
|
* @retval uint8 Index of the current ROM
|
||||||
|
* @note Get the currently selected boot ROM (this will be the currently
|
||||||
|
* running ROM, as long as you haven't changed it since boot or rBoot
|
||||||
|
* booted the rom in temporary boot mode, see rboot_get_last_boot_rom).
|
||||||
|
*/
|
||||||
|
uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void);
|
||||||
|
|
||||||
|
/** @brief Set the index of current ROM
|
||||||
|
* @param rom The index of the ROM to use on next boot
|
||||||
|
* @retval bool True on success
|
||||||
|
* @note Set the current boot ROM, which will be used when next restarted.
|
||||||
|
* @note This function re-writes the whole configuration to flash memory (not just the current ROM index)
|
||||||
|
*/
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom);
|
||||||
|
|
||||||
|
/** @brief Initialise flash write process
|
||||||
|
* @param start_addr Address on the SPI flash to begin write to
|
||||||
|
* @note Call once before starting to pass data to write to flash memory with rboot_write_flash function.
|
||||||
|
* start_addr is the address on the SPI flash to write from. Returns a status structure which
|
||||||
|
* must be passed back on each write. The contents of the structure should not
|
||||||
|
* be modified by the calling code.
|
||||||
|
*/
|
||||||
|
rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr);
|
||||||
|
|
||||||
|
/** @brief Write data to flash memory
|
||||||
|
* @param status Pointer to rboot_write_status structure defining the write status
|
||||||
|
* @param data Pointer to a block of uint8 data elements to be written to flash
|
||||||
|
* @param len Quantity of uint8 data elements to write to flash
|
||||||
|
* @note Call repeatedly to write data to the flash, starting at the address
|
||||||
|
* specified on the prior call to rboot_write_init. Current write position is
|
||||||
|
* tracked automatically. This method is likely to be called each time a packet
|
||||||
|
* of OTA data is received over the network.
|
||||||
|
* @note Call rboot_write_init before calling this function to get the rboot_write_status structure
|
||||||
|
*/
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len);
|
||||||
|
|
||||||
|
#ifdef BOOT_RTC_ENABLED
|
||||||
|
/** @brief Get rBoot status/control data from RTC data area
|
||||||
|
* @param rtc Pointer to a rboot_rtc_data structure to be populated
|
||||||
|
* @retval bool True on success, false if no data/invalid checksum (in which
|
||||||
|
* case do not use the contents of the structure)
|
||||||
|
*/
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_get_rtc_data(rboot_rtc_data *rtc);
|
||||||
|
|
||||||
|
/** @brief Set rBoot status/control data in RTC data area
|
||||||
|
* @param rtc pointer to a rboot_rtc_data structure
|
||||||
|
* @retval bool True on success
|
||||||
|
* @note The checksum will be calculated automatically for you.
|
||||||
|
*/
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_set_rtc_data(rboot_rtc_data *rtc);
|
||||||
|
|
||||||
|
/** @brief Set temporary rom for next boot
|
||||||
|
* @param rom Rom slot number for next boot
|
||||||
|
* @retval bool True on success
|
||||||
|
* @note This call will tell rBoot to temporarily boot the specified rom on
|
||||||
|
* the next boot. This is does not update the stored rBoot config on
|
||||||
|
* the flash, so after another reset it will boot back to the original
|
||||||
|
* rom.
|
||||||
|
*/
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_set_temp_rom(uint8 rom);
|
||||||
|
|
||||||
|
/** @brief Get the last booted rom slot number
|
||||||
|
* @param rom Pointer to rom slot number variable to populate
|
||||||
|
* @retval bool True on success, false if no data/invalid checksum
|
||||||
|
* @note This will find the currently running rom, even if booted as a
|
||||||
|
* temporary rom.
|
||||||
|
*/
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_get_last_boot_rom(uint8 *rom);
|
||||||
|
|
||||||
|
/** @brief Get the last boot mode
|
||||||
|
* @param mode Pointer to mode variable to populate
|
||||||
|
* @retval bool True on success, false if no data/invalid checksum
|
||||||
|
* @note This will indicate the type of boot: MODE_STANDARD, MODE_GPIO_ROM or
|
||||||
|
* MODE_TEMP_ROM.
|
||||||
|
*/
|
||||||
|
bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
#endif
|
|
@ -10,17 +10,10 @@
|
||||||
#include <FreeRTOS.h>
|
#include <FreeRTOS.h>
|
||||||
#include <task.h>
|
#include <task.h>
|
||||||
|
|
||||||
#include "rboot-ota.h"
|
#include <rboot.h>
|
||||||
|
|
||||||
#define ROM_MAGIC_OLD 0xe9
|
#define ROM_MAGIC_OLD 0xe9
|
||||||
#define ROM_MAGIC_NEW 0xea
|
#define ROM_MAGIC_NEW 0xea
|
||||||
#define CHECKSUM_INIT 0xef
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
#define RBOOT_DEBUG(f_, ...) printf((f_), __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define RBOOT_DEBUG(f_, ...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
uint8_t magic;
|
uint8_t magic;
|
||||||
|
@ -35,67 +28,6 @@ typedef struct __attribute__((packed)) {
|
||||||
} section_header_t;
|
} section_header_t;
|
||||||
|
|
||||||
|
|
||||||
// get the rboot config
|
|
||||||
rboot_config_t rboot_get_config() {
|
|
||||||
rboot_config_t conf;
|
|
||||||
sdk_spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)&conf, sizeof(rboot_config_t));
|
|
||||||
return conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write the rboot config
|
|
||||||
// preserves contents of rest of sector, so rest
|
|
||||||
// of sector can be used to store user data
|
|
||||||
// updates checksum automatically, if enabled
|
|
||||||
bool rboot_set_config(rboot_config_t *conf) {
|
|
||||||
uint8_t *buffer;
|
|
||||||
#ifdef RBOOT_CONFIG_CHKSUM
|
|
||||||
uint8_t chksum;
|
|
||||||
uint8_t *ptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
buffer = (uint8_t*)malloc(SECTOR_SIZE);
|
|
||||||
if (!buffer) {
|
|
||||||
printf("rboot_set_config: Failed to allocate sector buffer\r\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BOOT_CONFIG_CHKSUM
|
|
||||||
chksum = CHKSUM_INIT;
|
|
||||||
for (ptr = (uint8_t*)conf; ptr < &conf->chksum; ptr++) {
|
|
||||||
chksum ^= *ptr;
|
|
||||||
}
|
|
||||||
conf->chksum = chksum;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sdk_spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)buffer, SECTOR_SIZE);
|
|
||||||
memcpy(buffer, conf, sizeof(rboot_config_t));
|
|
||||||
vPortEnterCritical();
|
|
||||||
sdk_spi_flash_erase_sector(BOOT_CONFIG_SECTOR);
|
|
||||||
vPortExitCritical();
|
|
||||||
taskYIELD();
|
|
||||||
vPortEnterCritical();
|
|
||||||
sdk_spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)buffer, SECTOR_SIZE);
|
|
||||||
vPortExitCritical();
|
|
||||||
free(buffer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current boot rom
|
|
||||||
uint8_t rboot_get_current_rom() {
|
|
||||||
rboot_config_t conf;
|
|
||||||
conf = rboot_get_config();
|
|
||||||
return conf.current_rom;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set current boot rom
|
|
||||||
bool rboot_set_current_rom(uint8_t rom) {
|
|
||||||
rboot_config_t conf;
|
|
||||||
conf = rboot_get_config();
|
|
||||||
if (rom >= conf.count) return false;
|
|
||||||
conf.current_rom = rom;
|
|
||||||
return rboot_set_config(&conf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that a valid-looking rboot image is found at this offset on the flash, and
|
// Check that a valid-looking rboot image is found at this offset on the flash, and
|
||||||
// takes up 'expected_length' bytes.
|
// takes up 'expected_length' bytes.
|
||||||
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message)
|
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message)
|
||||||
|
@ -120,7 +52,7 @@ bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **
|
||||||
|
|
||||||
int remaining_sections = image_header.section_count;
|
int remaining_sections = image_header.section_count;
|
||||||
|
|
||||||
uint8_t checksum = CHECKSUM_INIT;
|
uint8_t checksum = CHKSUM_INIT;
|
||||||
|
|
||||||
while(remaining_sections > 0 && offset < end_offset)
|
while(remaining_sections > 0 && offset < end_offset)
|
||||||
{
|
{
|
34
extras/rboot-ota/rboot-integration.h
Normal file
34
extras/rboot-ota/rboot-integration.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// The rboot project provides this file for making rboot fit other projects
|
||||||
|
|
||||||
|
#ifndef __RBOOT_INTEGRATION_H__
|
||||||
|
#define __RBOOT_INTEGRATION_H__
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <espressif/spi_flash.h>
|
||||||
|
#include <espressif/esp_system.h>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <task.h>
|
||||||
|
|
||||||
|
#define uint8 uint8_t
|
||||||
|
#define uint16 uint16_t
|
||||||
|
#define uint32 uint32_t
|
||||||
|
#define int32 int32_t
|
||||||
|
#define ICACHE_FLASH_ATTR
|
||||||
|
#define spi_flash_read sdk_spi_flash_read
|
||||||
|
#define spi_flash_erase_sector sdk_spi_flash_erase_sector
|
||||||
|
#define spi_flash_write sdk_spi_flash_write
|
||||||
|
#define os_malloc malloc
|
||||||
|
#define os_free free
|
||||||
|
#define system_rtc_mem_read sdk_system_rtc_mem_read
|
||||||
|
#define system_rtc_mem_write sdk_system_rtc_mem_write
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define RBOOT_DEBUG(f_, ...) printf((f_), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define RBOOT_DEBUG(f_, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that a valid-looking rboot image is found at this offset on the flash, and
|
||||||
|
// takes up 'expected_length' bytes.
|
||||||
|
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message);
|
||||||
|
#endif // __RBOOT_INTEGRATION_H__
|
|
@ -1,55 +0,0 @@
|
||||||
#ifndef __RBOOT_OTA_H__
|
|
||||||
#define __RBOOT_OTA_H__
|
|
||||||
/* rboot-ota client API
|
|
||||||
*
|
|
||||||
* Ported from https://github.com/raburton/esp8266/ to esp-open-rtos
|
|
||||||
*
|
|
||||||
* BSD Licensed as per the file LICENSE in the top-level directory.
|
|
||||||
* Copyright (c) 2015 Richard A Burton & SuperHouse Pty Ltd
|
|
||||||
*/
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <rboot-config.h>
|
|
||||||
|
|
||||||
/* rboot config block structure (stored in flash offset 0x1000)
|
|
||||||
*
|
|
||||||
* Structure taken from rboot.h revision a4724ede22
|
|
||||||
* The version of rboot you're using has to match this structure
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint8_t magic; // our magic
|
|
||||||
uint8_t version; // config struct version
|
|
||||||
uint8_t mode; // boot loader mode
|
|
||||||
uint8_t current_rom; // currently selected rom
|
|
||||||
uint8_t gpio_rom; // rom to use for gpio boot
|
|
||||||
uint8_t count; // number of roms in use
|
|
||||||
uint8_t unused[2]; // padding
|
|
||||||
uint32_t roms[RBOOT_MAX_ROMS]; // flash addresses of the roms
|
|
||||||
#ifdef RBOOT_CONFIG_CHKSUM
|
|
||||||
uint8_t chksum; // config chksum
|
|
||||||
#endif
|
|
||||||
} rboot_config_t;
|
|
||||||
|
|
||||||
|
|
||||||
#define SECTOR_SIZE 0x1000
|
|
||||||
#define BOOT_CONFIG_SECTOR 1
|
|
||||||
|
|
||||||
// timeout for the initial connect (in ms)
|
|
||||||
#define OTA_CONNECT_TIMEOUT 10000
|
|
||||||
|
|
||||||
// timeout for the download and flash to complete (in ms), once connected
|
|
||||||
#define OTA_DOWNLOAD_TIMEOUT 20000
|
|
||||||
|
|
||||||
#define UPGRADE_FLAG_IDLE 0x00
|
|
||||||
#define UPGRADE_FLAG_START 0x01
|
|
||||||
#define UPGRADE_FLAG_FINISH 0x02
|
|
||||||
|
|
||||||
#define FLASH_BY_ADDR 0xff
|
|
||||||
|
|
||||||
rboot_config_t rboot_get_config();
|
|
||||||
bool rboot_set_config(rboot_config_t *conf);
|
|
||||||
uint8_t rboot_get_current_rom();
|
|
||||||
bool rboot_set_current_rom(uint8_t rom);
|
|
||||||
bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message);
|
|
||||||
|
|
||||||
#endif
|
|
111
extras/rboot-ota/rboot.h
Normal file
111
extras/rboot-ota/rboot.h
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
#ifndef __RBOOT_H__
|
||||||
|
#define __RBOOT_H__
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// rBoot open source boot loader for ESP8266.
|
||||||
|
// Copyright 2015 Richard A Burton
|
||||||
|
// richardaburton@gmail.com
|
||||||
|
// See license.txt for license terms.
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RBOOT_INTEGRATION
|
||||||
|
|
||||||
|
// uncomment to use only c code
|
||||||
|
// if you aren't using gcc you may need to do this
|
||||||
|
//#define BOOT_NO_ASM
|
||||||
|
|
||||||
|
// uncomment to have a checksum on the boot config
|
||||||
|
//#define BOOT_CONFIG_CHKSUM
|
||||||
|
|
||||||
|
// uncomment to enable big flash support (>1MB)
|
||||||
|
#define BOOT_BIG_FLASH
|
||||||
|
|
||||||
|
// uncomment to enable 2 way communication between
|
||||||
|
// rBoot and the user app via the esp rtc data area
|
||||||
|
#define BOOT_RTC_ENABLED
|
||||||
|
|
||||||
|
// uncomment to enable GPIO booting
|
||||||
|
//#define BOOT_GPIO_ENABLED
|
||||||
|
|
||||||
|
// uncomment to include .irom0.text section in the checksum
|
||||||
|
// roms must be built with esptool2 using -iromchksum option
|
||||||
|
//#define BOOT_IROM_CHKSUM
|
||||||
|
|
||||||
|
// uncomment to add a boot delay, allows you time to connect
|
||||||
|
// a terminal before rBoot starts to run and output messages
|
||||||
|
// value is in microseconds
|
||||||
|
//#define BOOT_DELAY_MICROS 2000000
|
||||||
|
|
||||||
|
// increase if required
|
||||||
|
#define MAX_ROMS 4
|
||||||
|
|
||||||
|
#define CHKSUM_INIT 0xef
|
||||||
|
|
||||||
|
#define SECTOR_SIZE 0x1000
|
||||||
|
#define BOOT_CONFIG_SECTOR 1
|
||||||
|
|
||||||
|
#define BOOT_CONFIG_MAGIC 0xe1
|
||||||
|
#define BOOT_CONFIG_VERSION 0x01
|
||||||
|
|
||||||
|
#define MODE_STANDARD 0x00
|
||||||
|
#define MODE_GPIO_ROM 0x01
|
||||||
|
#define MODE_TEMP_ROM 0x02
|
||||||
|
|
||||||
|
#define RBOOT_RTC_MAGIC 0x2334ae68
|
||||||
|
#define RBOOT_RTC_READ 1
|
||||||
|
#define RBOOT_RTC_WRITE 0
|
||||||
|
#define RBOOT_RTC_ADDR 64
|
||||||
|
|
||||||
|
#ifdef RBOOT_INTEGRATION
|
||||||
|
#include <rboot-integration.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Structure containing rBoot configuration
|
||||||
|
* @note ROM addresses must be multiples of 0x1000 (flash sector aligned).
|
||||||
|
* Without BOOT_BIG_FLASH only the first 8Mbit (1MB) of the chip will
|
||||||
|
* be memory mapped so ROM slots containing .irom0.text sections must
|
||||||
|
* remain below 0x100000. Slots beyond this will only be accessible via
|
||||||
|
* spi read calls, so use these for stored resources, not code. With
|
||||||
|
* BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit (1MB), so
|
||||||
|
* ROMs can be anywhere, but must not straddle two 8MBit (1MB) blocks.
|
||||||
|
* @ingroup rboot
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8 magic; ///< Our magic, identifies rBoot configuration - should be BOOT_CONFIG_MAGIC
|
||||||
|
uint8 version; ///< Version of configuration structure - should be BOOT_CONFIG_VERSION
|
||||||
|
uint8 mode; ///< Boot loader mode (MODE_STANDARD | MODE_GPIO_ROM)
|
||||||
|
uint8 current_rom; ///< Currently selected ROM (will be used for next standard boot)
|
||||||
|
uint8 gpio_rom; ///< ROM to use for GPIO boot (hardware switch) with mode set to MODE_GPIO_ROM
|
||||||
|
uint8 count; ///< Quantity of ROMs available to boot
|
||||||
|
uint8 unused[2]; ///< Padding (not used)
|
||||||
|
uint32 roms[MAX_ROMS]; ///< Flash addresses of each ROM
|
||||||
|
#ifdef BOOT_CONFIG_CHKSUM
|
||||||
|
uint8 chksum; ///< Checksum of this configuration structure (if BOOT_CONFIG_CHKSUM defined)
|
||||||
|
#endif
|
||||||
|
} rboot_config;
|
||||||
|
|
||||||
|
#ifdef BOOT_RTC_ENABLED
|
||||||
|
/** @brief Structure containing rBoot status/control data
|
||||||
|
* @note This structure is used to, optionally, communicate between rBoot and
|
||||||
|
* the user app. It is stored in the ESP RTC data area.
|
||||||
|
* @ingroup rboot
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32 magic; ///< Magic, identifies rBoot RTC data - should be RBOOT_RTC_MAGIC
|
||||||
|
uint8 next_mode; ///< The next boot mode, defaults to MODE_STANDARD - can be set to MODE_TEMP_ROM
|
||||||
|
uint8 last_mode; ///< The last (this) boot mode - can be MODE_STANDARD, MODE_GPIO_ROM or MODE_TEMP_ROM
|
||||||
|
uint8 last_rom; ///< The last (this) boot rom number
|
||||||
|
uint8 temp_rom; ///< The next boot rom number when next_mode set to MODE_TEMP_ROM
|
||||||
|
uint8 chksum; ///< Checksum of this structure this will be updated for you passed to the API
|
||||||
|
} rboot_rtc_data;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,4 +1,4 @@
|
||||||
*NOTE: This rboot-ota and the TFTP server ota-tftp.h are specific to esp-open-rtos. The below Makefile is from the upstream rboot-ota project.*
|
*NOTE: This rboot-ota and the TFTP server ota-tftp.h are specific to esp-open-rtos. The below Makefile is from the upstream rboot-ota project and the rboot code is taken from #75ca33b.*
|
||||||
|
|
||||||
For more details on OTA in esp-open-rtos, see https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration
|
For more details on OTA in esp-open-rtos, see https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
// https://www.kernel.org/pub/software/scm/git/docs/git-update-index.html
|
// https://www.kernel.org/pub/software/scm/git/docs/git-update-index.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#warning "You need to enter your wifi credentials in this file and follow the instructions here to keep the password safe from Github commits."
|
#error "You need to enter your wifi credentials in this file and follow the instructions here to keep the password safe from Github commits."
|
||||||
|
|
||||||
#ifndef __SSID_CONFIG_H__
|
#ifndef __SSID_CONFIG_H__
|
||||||
#define __SSID_CONFIG_H__
|
#define __SSID_CONFIG_H__
|
||||||
|
|
45
ld/common.ld
45
ld/common.ld
|
@ -139,13 +139,34 @@ SECTIONS
|
||||||
(except for libgcc which is matched above.)
|
(except for libgcc which is matched above.)
|
||||||
*/
|
*/
|
||||||
*(.literal .text .literal.* .text.*)
|
*(.literal .text .literal.* .text.*)
|
||||||
|
/* Anything explicitly marked as "irom" or "irom0" should go here */
|
||||||
|
*(.irom.* .irom.*.* .irom0.*)
|
||||||
|
_irom0_text_end = ABSOLUTE(.);
|
||||||
|
|
||||||
|
/* Temporary .rodata hacks start here, eventually all rodata will
|
||||||
|
be in irom by default */
|
||||||
/* mbedtls rodata */
|
/* mbedtls rodata */
|
||||||
*mbedtls.a:*.o(.rodata.* .rodata)
|
*mbedtls.a:*.o(.rodata.* .rodata)
|
||||||
/* actual certificate in example (TEMPORARY HACK) */
|
/* actual certificate in example (TEMPORARY HACK) */
|
||||||
*:cert.o(.rodata.* .rodata)
|
*:cert.o(.rodata.* .rodata)
|
||||||
/* Anything explicitly marked as "irom" or "irom0" should go here */
|
/* C++ constructor and destructor tables, properly ordered: */
|
||||||
*(.irom.* .irom.*.* .irom0.*)
|
__init_array_start = ABSOLUTE(.);
|
||||||
_irom0_text_end = ABSOLUTE(.);
|
KEEP (*crtbegin.o(.ctors))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||||
|
KEEP (*(SORT(.ctors.*)))
|
||||||
|
KEEP (*(.ctors))
|
||||||
|
__init_array_end = ABSOLUTE(.);
|
||||||
|
KEEP (*crtbegin.o(.dtors))
|
||||||
|
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||||
|
KEEP (*(SORT(.dtors.*)))
|
||||||
|
KEEP (*(.dtors))
|
||||||
|
/* C++ exception handlers table: */
|
||||||
|
__XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
|
||||||
|
*(.xt_except_desc)
|
||||||
|
*(.gnu.linkonce.h.*)
|
||||||
|
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
|
||||||
|
*(.xt_except_desc_end)
|
||||||
|
|
||||||
} >irom0_0_seg :irom0_0_phdr
|
} >irom0_0_seg :irom0_0_phdr
|
||||||
|
|
||||||
.data : ALIGN(4)
|
.data : ALIGN(4)
|
||||||
|
@ -179,23 +200,6 @@ SECTIONS
|
||||||
*(.gnu.version_r)
|
*(.gnu.version_r)
|
||||||
*(.eh_frame)
|
*(.eh_frame)
|
||||||
. = (. + 3) & ~ 3;
|
. = (. + 3) & ~ 3;
|
||||||
/* C++ constructor and destructor tables, properly ordered: */
|
|
||||||
__init_array_start = ABSOLUTE(.);
|
|
||||||
KEEP (*crtbegin.o(.ctors))
|
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
|
||||||
KEEP (*(SORT(.ctors.*)))
|
|
||||||
KEEP (*(.ctors))
|
|
||||||
__init_array_end = ABSOLUTE(.);
|
|
||||||
KEEP (*crtbegin.o(.dtors))
|
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
|
||||||
KEEP (*(SORT(.dtors.*)))
|
|
||||||
KEEP (*(.dtors))
|
|
||||||
/* C++ exception handlers table: */
|
|
||||||
__XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
|
|
||||||
*(.xt_except_desc)
|
|
||||||
*(.gnu.linkonce.h.*)
|
|
||||||
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
|
|
||||||
*(.xt_except_desc_end)
|
|
||||||
*(.dynamic)
|
*(.dynamic)
|
||||||
*(.gnu.version_d)
|
*(.gnu.version_d)
|
||||||
. = ALIGN(4); /* this table MUST be 4-byte aligned */
|
. = ALIGN(4); /* this table MUST be 4-byte aligned */
|
||||||
|
@ -230,7 +234,6 @@ SECTIONS
|
||||||
} >dram0_0_seg :dram0_0_bss_phdr
|
} >dram0_0_seg :dram0_0_bss_phdr
|
||||||
/* __stack = 0x3ffc8000; */
|
/* __stack = 0x3ffc8000; */
|
||||||
|
|
||||||
|
|
||||||
.lit4 : ALIGN(4)
|
.lit4 : ALIGN(4)
|
||||||
{
|
{
|
||||||
_lit4_start = ABSOLUTE(.);
|
_lit4_start = ABSOLUTE(.);
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -94,7 +94,7 @@ typedef int sys_prot_t;
|
||||||
_Pragma("GCC diagnostic pop") \
|
_Pragma("GCC diagnostic pop") \
|
||||||
} while(0)
|
} while(0)
|
||||||
#define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \
|
#define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \
|
||||||
x, __LINE__, __FILE__); while(1) {} } while(0)
|
x, __LINE__, __FILE__); abort(); } while(0)
|
||||||
|
|
||||||
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
|
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
|
||||||
printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
|
printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
|
||||||
|
|
387
lwip/sys_arch.c
387
lwip/sys_arch.c
|
@ -49,10 +49,18 @@
|
||||||
#include "lwip/mem.h"
|
#include "lwip/mem.h"
|
||||||
#include "lwip/stats.h"
|
#include "lwip/stats.h"
|
||||||
|
|
||||||
/* Very crude mechanism used to determine if the critical section handling
|
extern bool esp_in_isr;
|
||||||
functions are being called from an interrupt context or not. This relies on
|
|
||||||
the interrupt handler setting this variable manually. */
|
/* Based on the default xInsideISR mechanism to determine
|
||||||
portBASE_TYPE xInsideISR = pdFALSE;
|
if an ISR is running.
|
||||||
|
|
||||||
|
Doesn't support the possibility that LWIP functions are called from the NMI
|
||||||
|
handler (none are called from NMI when using current/SDK implementation.)
|
||||||
|
*/
|
||||||
|
static inline bool is_inside_isr()
|
||||||
|
{
|
||||||
|
return esp_in_isr;
|
||||||
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
* Routine: sys_mbox_new
|
* Routine: sys_mbox_new
|
||||||
|
@ -66,17 +74,17 @@ portBASE_TYPE xInsideISR = pdFALSE;
|
||||||
*---------------------------------------------------------------------------*/
|
*---------------------------------------------------------------------------*/
|
||||||
err_t sys_mbox_new( sys_mbox_t *pxMailBox, int iSize )
|
err_t sys_mbox_new( sys_mbox_t *pxMailBox, int iSize )
|
||||||
{
|
{
|
||||||
err_t xReturn = ERR_MEM;
|
err_t xReturn = ERR_MEM;
|
||||||
|
|
||||||
*pxMailBox = xQueueCreate( iSize, sizeof( void * ) );
|
*pxMailBox = xQueueCreate( iSize, sizeof( void * ) );
|
||||||
|
|
||||||
if( *pxMailBox != NULL )
|
if( *pxMailBox != NULL )
|
||||||
{
|
{
|
||||||
xReturn = ERR_OK;
|
xReturn = ERR_OK;
|
||||||
SYS_STATS_INC_USED( mbox );
|
SYS_STATS_INC_USED( mbox );
|
||||||
}
|
}
|
||||||
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,21 +104,21 @@ void sys_mbox_free( sys_mbox_t *pxMailBox )
|
||||||
{
|
{
|
||||||
unsigned long ulMessagesWaiting;
|
unsigned long ulMessagesWaiting;
|
||||||
|
|
||||||
ulMessagesWaiting = uxQueueMessagesWaiting( *pxMailBox );
|
ulMessagesWaiting = uxQueueMessagesWaiting( *pxMailBox );
|
||||||
configASSERT( ( ulMessagesWaiting == 0 ) );
|
configASSERT( ( ulMessagesWaiting == 0 ) );
|
||||||
|
|
||||||
#if SYS_STATS
|
#if SYS_STATS
|
||||||
{
|
{
|
||||||
if( ulMessagesWaiting != 0UL )
|
if( ulMessagesWaiting != 0UL )
|
||||||
{
|
{
|
||||||
SYS_STATS_INC( mbox.err );
|
SYS_STATS_INC( mbox.err );
|
||||||
}
|
}
|
||||||
|
|
||||||
SYS_STATS_DEC( mbox.used );
|
SYS_STATS_DEC( mbox.used );
|
||||||
}
|
}
|
||||||
#endif /* SYS_STATS */
|
#endif /* SYS_STATS */
|
||||||
|
|
||||||
vQueueDelete( *pxMailBox );
|
vQueueDelete( *pxMailBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -124,7 +132,7 @@ unsigned long ulMessagesWaiting;
|
||||||
*---------------------------------------------------------------------------*/
|
*---------------------------------------------------------------------------*/
|
||||||
void sys_mbox_post( sys_mbox_t *pxMailBox, void *pxMessageToPost )
|
void sys_mbox_post( sys_mbox_t *pxMailBox, void *pxMessageToPost )
|
||||||
{
|
{
|
||||||
while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
|
while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -145,27 +153,27 @@ err_t sys_mbox_trypost( sys_mbox_t *pxMailBox, void *pxMessageToPost )
|
||||||
err_t xReturn;
|
err_t xReturn;
|
||||||
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
if( xInsideISR != pdFALSE )
|
if( is_inside_isr() != pdFALSE )
|
||||||
{
|
{
|
||||||
xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
|
xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( portTickType ) 0 );
|
xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( portTickType ) 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( xReturn == pdPASS )
|
if( xReturn == pdPASS )
|
||||||
{
|
{
|
||||||
xReturn = ERR_OK;
|
xReturn = ERR_OK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* The queue was already full. */
|
/* The queue was already full. */
|
||||||
xReturn = ERR_MEM;
|
xReturn = ERR_MEM;
|
||||||
SYS_STATS_INC( mbox.err );
|
SYS_STATS_INC( mbox.err );
|
||||||
}
|
}
|
||||||
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -199,46 +207,46 @@ void *pvDummy;
|
||||||
portTickType xStartTime, xEndTime, xElapsed;
|
portTickType xStartTime, xEndTime, xElapsed;
|
||||||
unsigned long ulReturn;
|
unsigned long ulReturn;
|
||||||
|
|
||||||
xStartTime = xTaskGetTickCount();
|
xStartTime = xTaskGetTickCount();
|
||||||
|
|
||||||
if( NULL == ppvBuffer )
|
if( NULL == ppvBuffer )
|
||||||
{
|
{
|
||||||
ppvBuffer = &pvDummy;
|
ppvBuffer = &pvDummy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ulTimeOut != 0UL )
|
if( ulTimeOut != 0UL )
|
||||||
{
|
{
|
||||||
configASSERT( xInsideISR == ( portBASE_TYPE ) 0 );
|
configASSERT( is_inside_isr() == ( portBASE_TYPE ) 0 );
|
||||||
|
|
||||||
if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_RATE_MS ) )
|
if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_RATE_MS ) )
|
||||||
{
|
{
|
||||||
xEndTime = xTaskGetTickCount();
|
xEndTime = xTaskGetTickCount();
|
||||||
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
|
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
|
||||||
|
|
||||||
ulReturn = xElapsed;
|
ulReturn = xElapsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Timed out. */
|
/* Timed out. */
|
||||||
*ppvBuffer = NULL;
|
*ppvBuffer = NULL;
|
||||||
ulReturn = SYS_ARCH_TIMEOUT;
|
ulReturn = SYS_ARCH_TIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) );
|
while( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) );
|
||||||
xEndTime = xTaskGetTickCount();
|
xEndTime = xTaskGetTickCount();
|
||||||
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
|
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
|
||||||
|
|
||||||
if( xElapsed == 0UL )
|
if( xElapsed == 0UL )
|
||||||
{
|
{
|
||||||
xElapsed = 1UL;
|
xElapsed = 1UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulReturn = xElapsed;
|
ulReturn = xElapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ulReturn;
|
return ulReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -262,30 +270,30 @@ unsigned long ulReturn;
|
||||||
long lResult;
|
long lResult;
|
||||||
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
if( ppvBuffer== NULL )
|
if( ppvBuffer== NULL )
|
||||||
{
|
{
|
||||||
ppvBuffer = &pvDummy;
|
ppvBuffer = &pvDummy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( xInsideISR != pdFALSE )
|
if( is_inside_isr() != pdFALSE )
|
||||||
{
|
{
|
||||||
lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
|
lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lResult = xQueueReceive( *pxMailBox, &( *ppvBuffer ), 0UL );
|
lResult = xQueueReceive( *pxMailBox, &( *ppvBuffer ), 0UL );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( lResult == pdPASS )
|
if( lResult == pdPASS )
|
||||||
{
|
{
|
||||||
ulReturn = ERR_OK;
|
ulReturn = ERR_OK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ulReturn = SYS_MBOX_EMPTY;
|
ulReturn = SYS_MBOX_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ulReturn;
|
return ulReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -305,24 +313,24 @@ err_t sys_sem_new( sys_sem_t *pxSemaphore, u8_t ucCount )
|
||||||
{
|
{
|
||||||
err_t xReturn = ERR_MEM;
|
err_t xReturn = ERR_MEM;
|
||||||
|
|
||||||
vSemaphoreCreateBinary( ( *pxSemaphore ) );
|
vSemaphoreCreateBinary( ( *pxSemaphore ) );
|
||||||
|
|
||||||
if( *pxSemaphore != NULL )
|
if( *pxSemaphore != NULL )
|
||||||
{
|
{
|
||||||
if( ucCount == 0U )
|
if( ucCount == 0U )
|
||||||
{
|
{
|
||||||
xSemaphoreTake( *pxSemaphore, 1UL );
|
xSemaphoreTake( *pxSemaphore, 1UL );
|
||||||
}
|
}
|
||||||
|
|
||||||
xReturn = ERR_OK;
|
xReturn = ERR_OK;
|
||||||
SYS_STATS_INC_USED( sem );
|
SYS_STATS_INC_USED( sem );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SYS_STATS_INC( sem.err );
|
SYS_STATS_INC( sem.err );
|
||||||
}
|
}
|
||||||
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -353,36 +361,36 @@ u32_t sys_arch_sem_wait( sys_sem_t *pxSemaphore, u32_t ulTimeout )
|
||||||
portTickType xStartTime, xEndTime, xElapsed;
|
portTickType xStartTime, xEndTime, xElapsed;
|
||||||
unsigned long ulReturn;
|
unsigned long ulReturn;
|
||||||
|
|
||||||
xStartTime = xTaskGetTickCount();
|
xStartTime = xTaskGetTickCount();
|
||||||
|
|
||||||
if( ulTimeout != 0UL )
|
if( ulTimeout != 0UL )
|
||||||
{
|
{
|
||||||
if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_RATE_MS ) == pdTRUE )
|
if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_RATE_MS ) == pdTRUE )
|
||||||
{
|
{
|
||||||
xEndTime = xTaskGetTickCount();
|
xEndTime = xTaskGetTickCount();
|
||||||
xElapsed = (xEndTime - xStartTime) * portTICK_RATE_MS;
|
xElapsed = (xEndTime - xStartTime) * portTICK_RATE_MS;
|
||||||
ulReturn = xElapsed;
|
ulReturn = xElapsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ulReturn = SYS_ARCH_TIMEOUT;
|
ulReturn = SYS_ARCH_TIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE );
|
while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE );
|
||||||
xEndTime = xTaskGetTickCount();
|
xEndTime = xTaskGetTickCount();
|
||||||
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
|
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
|
||||||
|
|
||||||
if( xElapsed == 0UL )
|
if( xElapsed == 0UL )
|
||||||
{
|
{
|
||||||
xElapsed = 1UL;
|
xElapsed = 1UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulReturn = xElapsed;
|
ulReturn = xElapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ulReturn;
|
return ulReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a new mutex
|
/** Create a new mutex
|
||||||
|
@ -392,33 +400,33 @@ err_t sys_mutex_new( sys_mutex_t *pxMutex )
|
||||||
{
|
{
|
||||||
err_t xReturn = ERR_MEM;
|
err_t xReturn = ERR_MEM;
|
||||||
|
|
||||||
*pxMutex = xSemaphoreCreateMutex();
|
*pxMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
if( *pxMutex != NULL )
|
if( *pxMutex != NULL )
|
||||||
{
|
{
|
||||||
xReturn = ERR_OK;
|
xReturn = ERR_OK;
|
||||||
SYS_STATS_INC_USED( mutex );
|
SYS_STATS_INC_USED( mutex );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SYS_STATS_INC( mutex.err );
|
SYS_STATS_INC( mutex.err );
|
||||||
}
|
}
|
||||||
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Lock a mutex
|
/** Lock a mutex
|
||||||
* @param mutex the mutex to lock */
|
* @param mutex the mutex to lock */
|
||||||
void sys_mutex_lock( sys_mutex_t *pxMutex )
|
void sys_mutex_lock( sys_mutex_t *pxMutex )
|
||||||
{
|
{
|
||||||
while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS );
|
while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Unlock a mutex
|
/** Unlock a mutex
|
||||||
* @param mutex the mutex to unlock */
|
* @param mutex the mutex to unlock */
|
||||||
void sys_mutex_unlock(sys_mutex_t *pxMutex )
|
void sys_mutex_unlock(sys_mutex_t *pxMutex )
|
||||||
{
|
{
|
||||||
xSemaphoreGive( *pxMutex );
|
xSemaphoreGive( *pxMutex );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -426,8 +434,8 @@ void sys_mutex_unlock(sys_mutex_t *pxMutex )
|
||||||
* @param mutex the mutex to delete */
|
* @param mutex the mutex to delete */
|
||||||
void sys_mutex_free( sys_mutex_t *pxMutex )
|
void sys_mutex_free( sys_mutex_t *pxMutex )
|
||||||
{
|
{
|
||||||
SYS_STATS_DEC( mutex.used );
|
SYS_STATS_DEC( mutex.used );
|
||||||
vQueueDelete( *pxMutex );
|
vQueueDelete( *pxMutex );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -443,14 +451,14 @@ void sys_sem_signal( sys_sem_t *pxSemaphore )
|
||||||
{
|
{
|
||||||
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
if( xInsideISR != pdFALSE )
|
if( is_inside_isr() != pdFALSE )
|
||||||
{
|
{
|
||||||
xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
|
xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
xSemaphoreGive( *pxSemaphore );
|
xSemaphoreGive( *pxSemaphore );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -463,8 +471,8 @@ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
||||||
*---------------------------------------------------------------------------*/
|
*---------------------------------------------------------------------------*/
|
||||||
void sys_sem_free( sys_sem_t *pxSemaphore )
|
void sys_sem_free( sys_sem_t *pxSemaphore )
|
||||||
{
|
{
|
||||||
SYS_STATS_DEC(sem.used);
|
SYS_STATS_DEC(sem.used);
|
||||||
vQueueDelete( *pxSemaphore );
|
vQueueDelete( *pxSemaphore );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -479,7 +487,7 @@ void sys_init(void)
|
||||||
|
|
||||||
u32_t sys_now(void)
|
u32_t sys_now(void)
|
||||||
{
|
{
|
||||||
return xTaskGetTickCount();
|
return xTaskGetTickCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -506,18 +514,18 @@ xTaskHandle xCreatedTask;
|
||||||
portBASE_TYPE xResult;
|
portBASE_TYPE xResult;
|
||||||
sys_thread_t xReturn;
|
sys_thread_t xReturn;
|
||||||
|
|
||||||
xResult = xTaskCreate( pxThread, ( signed char * ) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
|
xResult = xTaskCreate( pxThread, ( signed char * ) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
|
||||||
|
|
||||||
if( xResult == pdPASS )
|
if( xResult == pdPASS )
|
||||||
{
|
{
|
||||||
xReturn = xCreatedTask;
|
xReturn = xCreatedTask;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
xReturn = NULL;
|
xReturn = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return xReturn;
|
return xReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -541,11 +549,11 @@ sys_thread_t xReturn;
|
||||||
*---------------------------------------------------------------------------*/
|
*---------------------------------------------------------------------------*/
|
||||||
sys_prot_t sys_arch_protect( void )
|
sys_prot_t sys_arch_protect( void )
|
||||||
{
|
{
|
||||||
if( xInsideISR == pdFALSE )
|
if( is_inside_isr() == pdFALSE )
|
||||||
{
|
{
|
||||||
taskENTER_CRITICAL();
|
taskENTER_CRITICAL();
|
||||||
}
|
}
|
||||||
return ( sys_prot_t ) 1;
|
return ( sys_prot_t ) 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*
|
/*---------------------------------------------------------------------------*
|
||||||
|
@ -561,11 +569,11 @@ sys_prot_t sys_arch_protect( void )
|
||||||
*---------------------------------------------------------------------------*/
|
*---------------------------------------------------------------------------*/
|
||||||
void sys_arch_unprotect( sys_prot_t xValue )
|
void sys_arch_unprotect( sys_prot_t xValue )
|
||||||
{
|
{
|
||||||
(void) xValue;
|
(void) xValue;
|
||||||
if( xInsideISR == pdFALSE )
|
if( is_inside_isr() == pdFALSE )
|
||||||
{
|
{
|
||||||
taskEXIT_CRITICAL();
|
taskEXIT_CRITICAL();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -573,13 +581,12 @@ void sys_arch_unprotect( sys_prot_t xValue )
|
||||||
*/
|
*/
|
||||||
void sys_assert( const char *pcMessage )
|
void sys_assert( const char *pcMessage )
|
||||||
{
|
{
|
||||||
(void) pcMessage;
|
(void) pcMessage;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*-------------------------------------------------------------------------*
|
/*-------------------------------------------------------------------------*
|
||||||
* End of File: sys_arch.c
|
* End of File: sys_arch.c
|
||||||
*-------------------------------------------------------------------------*/
|
*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
105
utils/filteroutput.py
Executable file
105
utils/filteroutput.py
Executable file
|
@ -0,0 +1,105 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# A thin Python wrapper around addr2line, can monitor esp-open-rtos
|
||||||
|
# output and uses gdb to convert any suitable looking hex numbers
|
||||||
|
# found in the output into function and line numbers.
|
||||||
|
#
|
||||||
|
# Works with a serial port if the --port option is supplied.
|
||||||
|
# Otherwise waits for input on stdin.
|
||||||
|
#
|
||||||
|
import serial
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import subprocess
|
||||||
|
import termios
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Try looking up anything in the executable address space
|
||||||
|
RE_EXECADDR = r"(0x)?40([0-9]|[a-z]){6}"
|
||||||
|
|
||||||
|
def find_elf_file():
|
||||||
|
out_files = []
|
||||||
|
for top,_,files in os.walk('.', followlinks=False):
|
||||||
|
for f in files:
|
||||||
|
if f.endswith(".out"):
|
||||||
|
out_files.append(os.path.join(top,f))
|
||||||
|
if len(out_files) == 1:
|
||||||
|
return out_files[0]
|
||||||
|
elif len(out_files) > 1:
|
||||||
|
print("Found multiple .out files: %s. Please specify one with the --elf option." % out_files)
|
||||||
|
else:
|
||||||
|
print("No .out file found under current directory. Please specify one with the --elf option.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='esp-open-rtos output filter tool', prog='filteroutput')
|
||||||
|
parser.add_argument(
|
||||||
|
'--elf', '-e',
|
||||||
|
help="ELF file (*.out file) to load symbols from (if not supplied, will search for one)"),
|
||||||
|
parser.add_argument(
|
||||||
|
'--port', '-p',
|
||||||
|
help='Serial port to monitor (will monitor stdin if None)',
|
||||||
|
default=None)
|
||||||
|
parser.add_argument(
|
||||||
|
'--baud', '-b',
|
||||||
|
help='Baud rate for serial port',
|
||||||
|
type=int,
|
||||||
|
default=74880)
|
||||||
|
parser.add_argument(
|
||||||
|
'--reset-on-connect', '-r',
|
||||||
|
help='Reset ESP8266 (via DTR) on serial connect',
|
||||||
|
action='store_true')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.elf is None:
|
||||||
|
args.elf = find_elf_file()
|
||||||
|
elif not os.path.exists(args.elf):
|
||||||
|
print("ELF file '%s' not found" % args.elf)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.port is not None:
|
||||||
|
print("Opening %s at %dbps..." % (args.port, args.baud))
|
||||||
|
port = serial.Serial(args.port, baudrate=args.baud)
|
||||||
|
if args.reset_on_connect:
|
||||||
|
print("Resetting...")
|
||||||
|
port.setDTR(False)
|
||||||
|
time.sleep(0.1)
|
||||||
|
port.setDTR(True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Reading from stdin...")
|
||||||
|
port = sys.stdin
|
||||||
|
# disable echo
|
||||||
|
try:
|
||||||
|
old_attr = termios.tcgetattr(sys.stdin.fileno())
|
||||||
|
attr = termios.tcgetattr(sys.stdin.fileno())
|
||||||
|
attr[3] = attr[3] & ~termios.ECHO
|
||||||
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, attr)
|
||||||
|
except termios.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
line = port.readline()
|
||||||
|
if line == '':
|
||||||
|
break
|
||||||
|
print(line.strip())
|
||||||
|
for match in re.finditer(RE_EXECADDR, line, re.IGNORECASE):
|
||||||
|
addr = match.group(0)
|
||||||
|
if not addr.startswith("0x"):
|
||||||
|
addr = "0x"+addr
|
||||||
|
# keeping addr2line and feeding it addresses on stdin didn't seem to work smoothly
|
||||||
|
addr2line = subprocess.check_output(["xtensa-lx106-elf-addr2line","-pfia","-e","%s" % args.elf, addr], cwd=".").strip()
|
||||||
|
if not addr2line.endswith(": ?? ??:0"):
|
||||||
|
print("\n%s\n" % addr2line.strip())
|
||||||
|
finally:
|
||||||
|
if args.port is None:
|
||||||
|
# restore echo
|
||||||
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old_attr)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in a new issue