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
|
||||
env:
|
||||
# Target commit for https://github.com/pfalcon/esp-open-sdk/
|
||||
OPENSDK_COMMIT=cd1d336
|
||||
OPENSDK_COMMIT=9bd3aba
|
||||
CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}"
|
||||
CROSS_BINDIR="${CROSS_ROOT}/bin"
|
||||
ESPTOOL2_COMMIT=ec0e2c7
|
||||
ESPTOOL2_DIR="${HOME}/esptool2-${ESPTOOL2_COMMIT}"
|
||||
PATH=${PATH}:${CROSS_BINDIR}:${ESPTOOL2_DIR}
|
||||
MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples CROSS=\"ccache xtensa-lx106-elf-\""
|
||||
cache:
|
||||
directories:
|
||||
- ${CROSS_ROOT}
|
||||
|
@ -57,5 +58,7 @@ before_install:
|
|||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
# Remove ssid_config requirement for examples
|
||||
- sed -i "s%#warning%//#warning%" include/ssid_config.h
|
||||
- make -C examples/ build-examples CROSS="ccache xtensa-lx106-elf-" V=1
|
||||
- sed -i "s%#error%//#error%" include/ssid_config.h
|
||||
# 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();
|
||||
}
|
||||
|
||||
static bool sdk_compat_initialised;
|
||||
void sdk_compat_initialise(void);
|
||||
|
||||
/*
|
||||
* See header file for description.
|
||||
*/
|
||||
|
@ -189,14 +186,6 @@ portBASE_TYPE xPortStartScheduler( void )
|
|||
_xt_isr_attach(INUM_SOFT, SV_ISR);
|
||||
_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. */
|
||||
_xt_isr_attach(INUM_TICK, sdk__xt_timer_int);
|
||||
_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
|
||||
|
||||
* 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.)
|
||||
|
||||
|
|
17
common.mk
17
common.mk
|
@ -75,6 +75,9 @@ FLAVOR ?= release # or debug
|
|||
# Compiler names, etc. assume gdb
|
||||
CROSS ?= xtensa-lx106-elf-
|
||||
|
||||
# Path to the filteroutput.py tool
|
||||
FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
|
||||
|
||||
AR = $(CROSS)ar
|
||||
CC = $(CROSS)gcc
|
||||
CPP = $(CROSS)cpp
|
||||
|
@ -106,8 +109,14 @@ ENTRY_SYMBOL ?= call_user_start
|
|||
# but compiled code size will come down a small amount.)
|
||||
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++_
|
||||
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
|
||||
CFLAGS ?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
|
||||
# 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)
|
||||
|
||||
ifeq ($(WARNINGS_AS_ERRORS),1)
|
||||
C_CXX_FLAGS += -Werror
|
||||
endif
|
||||
|
||||
ifeq ($(SPLIT_SECTIONS),1)
|
||||
C_CXX_FLAGS += -ffunction-sections -fdata-sections
|
||||
LDFLAGS += -Wl,-gc-sections
|
||||
|
@ -375,7 +388,7 @@ size: $(PROGRAM_OUT)
|
|||
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
|
||||
|
||||
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
|
||||
# environment without causing weird side effects
|
||||
|
|
|
@ -41,8 +41,6 @@ void user_init(void);
|
|||
#define RTCMEM_BACKUP_PHY_VER 31
|
||||
#define RTCMEM_SYSTEM_PP_VER 62
|
||||
|
||||
#define halt() while (1) {}
|
||||
|
||||
extern uint32_t _bss_start;
|
||||
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 init_networking(uint8_t *phy_info, uint8_t *mac_addr);
|
||||
static void init_g_ic(void);
|
||||
static void dump_excinfo(void);
|
||||
static void user_start_phase2(void);
|
||||
static void dump_flash_sector(uint32_t start_sector, uint32_t length);
|
||||
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)) {
|
||||
//FIXME: do we really need this check?
|
||||
printf("Firmware ONLY supports ESP8266!!!\n");
|
||||
halt();
|
||||
abort();
|
||||
}
|
||||
if (otp_id0 == 0 && otp_id1 == 0) {
|
||||
printf("empty otp\n");
|
||||
halt();
|
||||
abort();
|
||||
}
|
||||
if (otp_flags & 0x1000) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// .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) {
|
||||
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) {
|
||||
if (sdk_register_chipv6_phy(phy_info)) {
|
||||
printf("FATAL: sdk_register_chipv6_phy failed");
|
||||
halt();
|
||||
abort();
|
||||
}
|
||||
uart_set_baud(0, 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
|
||||
void sdk_wdt_init(void) {
|
||||
WDT.CTRL &= ~WDT_CTRL_ENABLE;
|
||||
|
@ -408,6 +357,9 @@ void sdk_user_init_task(void *params) {
|
|||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
extern void (*__init_array_start)(void);
|
||||
extern void (*__init_array_end)(void);
|
||||
|
||||
// .Lfunc009 -- .irom0.text+0x5b4
|
||||
static void user_start_phase2(void) {
|
||||
uint8_t *buf;
|
||||
|
@ -445,6 +397,13 @@ static void user_start_phase2(void) {
|
|||
}
|
||||
init_networking(phy_info, sdk_info.sta_mac_addr);
|
||||
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);
|
||||
sdk_wdt_init();
|
||||
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];
|
||||
|
||||
bool esp_in_isr;
|
||||
|
||||
void IRAM _xt_isr_attach(uint8_t i, _xt_isr 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)
|
||||
{
|
||||
esp_in_isr = true;
|
||||
|
||||
/* WDT has highest priority (occasional WDT resets otherwise) */
|
||||
if(intset & BIT(INUM_WDT)) {
|
||||
_xt_clear_ints(BIT(INUM_WDT));
|
||||
|
@ -35,5 +39,7 @@ uint16_t IRAM _xt_isr_handler(uint16_t intset)
|
|||
intset -= mask;
|
||||
}
|
||||
|
||||
esp_in_isr = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -68,7 +68,9 @@ DebugExceptionVector:
|
|||
.type DebugExceptionVector, @function
|
||||
|
||||
wsr a0, excsave2
|
||||
call0 sdk_user_fatal_exception_handler
|
||||
mov a2, a1
|
||||
movi a3, 0
|
||||
call0 fatal_exception_handler
|
||||
rfi 2
|
||||
|
||||
.org VecBase + 0x20
|
||||
|
@ -81,7 +83,9 @@ KernelExceptionVector:
|
|||
.type KernelExceptionVector, @function
|
||||
|
||||
break 1, 0
|
||||
call0 sdk_user_fatal_exception_handler
|
||||
mov a2, a1
|
||||
movi a3, 0
|
||||
call0 fatal_exception_handler
|
||||
rfe
|
||||
|
||||
.org VecBase + 0x50
|
||||
|
@ -98,7 +102,9 @@ DoubleExceptionVector:
|
|||
.type DoubleExceptionVector, @function
|
||||
|
||||
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
|
||||
* vectors on chip reset. */
|
||||
|
@ -251,11 +257,13 @@ LoadStoreErrorHandler:
|
|||
* will have correct values */
|
||||
wsr a0, sar
|
||||
l32i a0, sp, 0
|
||||
l32i a2, sp, 0x08
|
||||
/*l32i a2, sp, 0x08*/
|
||||
l32i a3, sp, 0x0c
|
||||
l32i a4, sp, 0x10
|
||||
rsr a1, excsave1
|
||||
call0 sdk_user_fatal_exception_handler
|
||||
mov a2, a1
|
||||
movi a3, 0
|
||||
call0 fatal_exception_handler
|
||||
|
||||
.balign 4
|
||||
.LSE_assign_a1:
|
||||
|
@ -515,7 +523,9 @@ UserExceptionHandler:
|
|||
.literal_position
|
||||
.LUserFailOtherExceptionCause:
|
||||
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,
|
||||
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 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 */
|
||||
|
|
|
@ -25,7 +25,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
|
|||
if (heap_end + incr > stack_ptr)
|
||||
{
|
||||
_write (1, "_sbrk: Heap collided with stack\n", 32);
|
||||
while(1) {}
|
||||
abort();
|
||||
}
|
||||
*/
|
||||
heap_end += incr;
|
||||
|
|
|
@ -17,20 +17,6 @@ void IRAM *zalloc(size_t 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.
|
||||
*
|
||||
* Not part of published API.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# Simple makefile for simple example
|
||||
PROGRAM=cpp_01_tasks
|
||||
OTA=0
|
||||
EXTRA_COMPONENTS=extras/cpp_support
|
||||
include ../../common.mk
|
||||
|
|
|
@ -19,15 +19,14 @@
|
|||
|
||||
// DS18B20 driver
|
||||
#include "ds18b20/ds18b20.h"
|
||||
// Onewire init
|
||||
#include "onewire/onewire.h"
|
||||
|
||||
void broadcast_temperature(void *pvParameters)
|
||||
{
|
||||
|
||||
uint8_t amount = 0;
|
||||
uint8_t sensors = 2;
|
||||
ds_sensor_t t[sensors];
|
||||
uint8_t sensors = 1;
|
||||
ds18b20_addr_t addrs[sensors];
|
||||
float results[sensors];
|
||||
|
||||
// Use GPIO 13 as one wire pin.
|
||||
uint8_t GPIO_FOR_ONE_WIRE = 13;
|
||||
|
@ -36,8 +35,6 @@ void broadcast_temperature(void *pvParameters)
|
|||
|
||||
// Broadcaster part
|
||||
err_t err;
|
||||
// Initialize one wire bus.
|
||||
onewire_init(GPIO_FOR_ONE_WIRE);
|
||||
|
||||
while(1) {
|
||||
|
||||
|
@ -66,18 +63,17 @@ void broadcast_temperature(void *pvParameters)
|
|||
|
||||
for(;;) {
|
||||
// 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){
|
||||
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;
|
||||
int fraction = (int)((t[i].value - intpart) * 100);
|
||||
// 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);
|
||||
// ("\xC2\xB0" is the degree character (U+00B0) in UTF-8)
|
||||
sprintf(msg, "Sensor %08x%08x reports: %f \xC2\xB0""C\n", (uint32_t)(addrs[i] >> 32), (uint32_t)addrs[i], results[i]);
|
||||
printf("%s", msg);
|
||||
|
||||
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.,
|
||||
*/
|
||||
#include "espressif/esp_common.h"
|
||||
#include "esp/uart.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "timers.h"
|
||||
#include "queue.h"
|
||||
#include "esp/uart.h"
|
||||
|
||||
// DS18B20 driver
|
||||
#include "ds18b20/ds18b20.h"
|
||||
// Onewire init
|
||||
#include "onewire/onewire.h"
|
||||
|
||||
void print_temperature(void *pvParameters)
|
||||
{
|
||||
int delay = 500;
|
||||
uint8_t amount = 0;
|
||||
// Declare amount of sensors
|
||||
uint8_t sensors = 2;
|
||||
ds_sensor_t t[sensors];
|
||||
#define SENSOR_GPIO 13
|
||||
#define MAX_SENSORS 8
|
||||
#define RESCAN_INTERVAL 8
|
||||
#define LOOP_DELAY_MS 250
|
||||
|
||||
// Use GPIO 13 as one wire pin.
|
||||
uint8_t GPIO_FOR_ONE_WIRE = 13;
|
||||
void print_temperature(void *pvParameters) {
|
||||
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) {
|
||||
// Search all DS18B20, return its amount and feed 't' structure with result data.
|
||||
amount = ds18b20_read_all(GPIO_FOR_ONE_WIRE, t);
|
||||
// Every RESCAN_INTERVAL samples, check to see if the sensors connected
|
||||
// to our bus have changed.
|
||||
sensor_count = ds18b20_scan_devices(SENSOR_GPIO, addrs, MAX_SENSORS);
|
||||
|
||||
if (amount < sensors){
|
||||
printf("Something is wrong, I expect to see %d sensors \nbut just %d was detected!\n", sensors, amount);
|
||||
}
|
||||
if (sensor_count < 1) {
|
||||
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)
|
||||
{
|
||||
int intpart = (int)t[i].value;
|
||||
int fraction = (int)((t[i].value - intpart) * 100);
|
||||
// Multiple "" here is just to satisfy compiler and don`t raise 'hex escape sequence out of range' warning.
|
||||
printf("Sensor %d report: %d.%02d ""\xC2""\xB0""C\n",t[i].id, intpart, fraction);
|
||||
// Do a number of temperature samples, and print the results.
|
||||
for (int i = 0; i < RESCAN_INTERVAL; i++) {
|
||||
ds18b20_measure_and_read_multi(SENSOR_GPIO, addrs, sensor_count, temps);
|
||||
for (int j = 0; j < sensor_count; j++) {
|
||||
// The DS18B20 address is a 64-bit integer, but newlib-nano
|
||||
// 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");
|
||||
vTaskDelay(delay / portTICK_RATE_MS);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void user_init(void)
|
||||
{
|
||||
void user_init(void) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -305,7 +305,7 @@ static void test_system_interaction()
|
|||
}
|
||||
uint32_t ticks = xTaskGetTickCount() - start;
|
||||
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
|
||||
|
|
|
@ -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:
|
||||
|
||||
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 <stdint.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"
|
||||
"MIIFNzCCBB+gAwIBAgISAfl7TPw6Mf/F6VCBc5eaHA7kMA0GCSqGSIb3DQEBCwUA\r\n"
|
||||
"MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD\r\n"
|
||||
"ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMTAeFw0xNTEyMzAwNzQ0MDBaFw0x\r\n"
|
||||
"NjAzMjkwNzQ0MDBaMBwxGjAYBgNVBAMTEXd3dy5ob3dzbXlzc2wuY29tMIIBIjAN\r\n"
|
||||
"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKF7WzSrDPinQhd9mVfoW5u46/TC\r\n"
|
||||
"fbYKR3MEryetUSeQuwuXFj2xO6+a/JQ99UC3Eq+9s8uFdx1zFFS6qfW+HXBwGWVf\r\n"
|
||||
"ajKEyIcXUJZCRn7aWpTWZq6cuv4bZSv1QklGViQs8UCZifcN0A/mYrH7zHG2WDn2\r\n"
|
||||
"fxNgA+nJBqbZPr1gP9hqGFCnX+dPR5WxtC9+Dv9Sx+wiOWVz/obVTCygdqqcpa5I\r\n"
|
||||
"3/U9REVgO2VfT+xMty6NZTMCjTJ+GXZuB/BrMe9+ZmgWk0grJyqdrCxOCyK6B4g+\r\n"
|
||||
"Fvs8WFRNTHdQnP3/NT5hPtreZ3nuY2YY7RbGFwUaBcvJwbbIqalpiQ1X7QIDAQAB\r\n"
|
||||
"o4ICQzCCAj8wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr\r\n"
|
||||
"BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQMAe087CSp3PjgqyIYyWTR\r\n"
|
||||
"a2aMnTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBwBggrBgEFBQcB\r\n"
|
||||
"AQRkMGIwLwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmludC14MS5sZXRzZW5jcnlw\r\n"
|
||||
"dC5vcmcvMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDEubGV0c2VuY3J5\r\n"
|
||||
"cHQub3JnLzBNBgNVHREERjBEgg1ob3dzbXlzc2wuY29tgg1ob3dzbXl0bHMuY29t\r\n"
|
||||
"ghF3d3cuaG93c215dGxzLmNvbYIRd3d3Lmhvd3NteXNzbC5jb20wgf4GA1UdIASB\r\n"
|
||||
"9jCB8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpo\r\n"
|
||||
"dHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlz\r\n"
|
||||
"IENlcnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcg\r\n"
|
||||
"UGFydGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmlj\r\n"
|
||||
"YXRlIFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBv\r\n"
|
||||
"c2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEALB2QrIrcxxFr81b6khy9LwVBpthL\r\n"
|
||||
"2LSs0xWhA09gxHmPnVrqim3Wa9HFYRApSqtTQWSx58TO+MERXQ7eDX50k53oem+Q\r\n"
|
||||
"gXn90HVDkR0jbV1CsAD5qL00kKOofAyGkUhFlO2hcRU0CIj7Z9HZB8xpYdZWLUSZ\r\n"
|
||||
"BpgiXdf/YYRIwgx29GRQjhfl/H30fHyawY5SvquJuvAeEr7lxqAmEEg3a7J6ibHL\r\n"
|
||||
"90zf5KMkXGyVsXqxLqrEXQKgTvpUMeP5iYAxE45R5GNmYS2jajyTi4XkWw4nEu4Q\r\n"
|
||||
"dMTLuwxGz87KOwSSFOLU903hO3IIPvFIIMY6aVK2kAgQPNIqk9nFurTF9A==\r\n"
|
||||
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n"
|
||||
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
|
||||
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n"
|
||||
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n"
|
||||
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n"
|
||||
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n"
|
||||
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n"
|
||||
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n"
|
||||
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n"
|
||||
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n"
|
||||
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n"
|
||||
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n"
|
||||
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n"
|
||||
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n"
|
||||
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n"
|
||||
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n"
|
||||
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n"
|
||||
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n"
|
||||
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n"
|
||||
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n"
|
||||
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n"
|
||||
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n"
|
||||
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n"
|
||||
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n"
|
||||
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n"
|
||||
"-----END CERTIFICATE-----\r\n";
|
||||
|
||||
|
||||
|
|
|
@ -41,11 +41,11 @@
|
|||
#include "mbedtls/error.h"
|
||||
#include "mbedtls/certs.h"
|
||||
|
||||
#define WEB_SERVER "howsmyssl.com"
|
||||
#define WEB_SERVER "www.howsmyssl.com"
|
||||
#define WEB_PORT "443"
|
||||
#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 */
|
||||
extern const char *server_root_cert;
|
||||
|
@ -115,7 +115,7 @@ void http_get_task(void *pvParameters)
|
|||
strlen(pers))) != 0)
|
||||
{
|
||||
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||||
while(1) {} /* todo: replace with abort() */
|
||||
abort();
|
||||
}
|
||||
|
||||
printf(" ok\n");
|
||||
|
@ -129,7 +129,7 @@ void http_get_task(void *pvParameters)
|
|||
if(ret < 0)
|
||||
{
|
||||
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);
|
||||
|
@ -138,7 +138,7 @@ void http_get_task(void *pvParameters)
|
|||
if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
|
||||
{
|
||||
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 "ota-tftp.h"
|
||||
#include "rboot-ota.h"
|
||||
#include "rboot-integration.h"
|
||||
#include "rboot.h"
|
||||
#include "rboot-api.h"
|
||||
|
||||
void user_init(void)
|
||||
{
|
||||
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",
|
||||
conf.current_rom, conf.count);
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ void tls_server_task(void *pvParameters)
|
|||
strlen(pers))) != 0)
|
||||
{
|
||||
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||||
while(1) {} /* todo: replace with abort() */
|
||||
abort();
|
||||
}
|
||||
|
||||
printf(" ok\n");
|
||||
|
@ -99,7 +99,7 @@ void tls_server_task(void *pvParameters)
|
|||
if(ret < 0)
|
||||
{
|
||||
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);
|
||||
|
@ -109,7 +109,7 @@ void tls_server_task(void *pvParameters)
|
|||
if(ret != 0)
|
||||
{
|
||||
printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret);
|
||||
while(1) { } /*todo: replace with abort() */
|
||||
abort();
|
||||
}
|
||||
|
||||
printf(" ok\n");
|
||||
|
@ -134,7 +134,7 @@ void tls_server_task(void *pvParameters)
|
|||
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
|
||||
{
|
||||
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);
|
||||
|
|
|
@ -1,43 +1,47 @@
|
|||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "math.h"
|
||||
|
||||
#include "onewire/onewire.h"
|
||||
#include "ds18b20.h"
|
||||
|
||||
#define DS1820_WRITE_SCRATCHPAD 0x4E
|
||||
#define DS1820_READ_SCRATCHPAD 0xBE
|
||||
#define DS1820_COPY_SCRATCHPAD 0x48
|
||||
#define DS1820_READ_EEPROM 0xB8
|
||||
#define DS1820_READ_PWRSUPPLY 0xB4
|
||||
#define DS1820_SEARCHROM 0xF0
|
||||
#define DS1820_SKIP_ROM 0xCC
|
||||
#define DS1820_READROM 0x33
|
||||
#define DS1820_MATCHROM 0x55
|
||||
#define DS1820_ALARMSEARCH 0xEC
|
||||
#define DS1820_CONVERT_T 0x44
|
||||
#define DS18B20_WRITE_SCRATCHPAD 0x4E
|
||||
#define DS18B20_READ_SCRATCHPAD 0xBE
|
||||
#define DS18B20_COPY_SCRATCHPAD 0x48
|
||||
#define DS18B20_READ_EEPROM 0xB8
|
||||
#define DS18B20_READ_PWRSUPPLY 0xB4
|
||||
#define DS18B20_SEARCHROM 0xF0
|
||||
#define DS18B20_SKIP_ROM 0xCC
|
||||
#define DS18B20_READROM 0x33
|
||||
#define DS18B20_MATCHROM 0x55
|
||||
#define DS18B20_ALARMSEARCH 0xEC
|
||||
#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 addr[8];
|
||||
onewire_addr_t addr;
|
||||
onewire_search_t search;
|
||||
uint8_t sensor_id = 0;
|
||||
onewire_reset_search(pin);
|
||||
|
||||
while(onewire_search(pin, addr)){
|
||||
uint8_t crc = onewire_crc8(addr, 7);
|
||||
if (crc != addr[7]){
|
||||
printf("CRC check failed: %02X %02X\n", addr[7], crc);
|
||||
onewire_search_start(&search);
|
||||
|
||||
while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) {
|
||||
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;
|
||||
}
|
||||
|
||||
onewire_reset(pin);
|
||||
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);
|
||||
|
||||
onewire_reset(pin);
|
||||
onewire_select(pin, addr);
|
||||
onewire_write(pin, DS1820_READ_SCRATCHPAD, ONEWIRE_DEFAULT_POWER);
|
||||
onewire_write(pin, DS18B20_READ_SCRATCHPAD);
|
||||
|
||||
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) {
|
||||
|
||||
onewire_reset(pin);
|
||||
onewire_skip_rom(pin);
|
||||
onewire_write(pin, DS18B20_CONVERT_T);
|
||||
|
||||
onewire_write(pin, DS1820_SKIP_ROM, ONEWIRE_DEFAULT_POWER);
|
||||
onewire_write(pin, DS1820_CONVERT_T, ONEWIRE_DEFAULT_POWER);
|
||||
|
||||
onewire_power(pin);
|
||||
vTaskDelay(750 / portTICK_RATE_MS);
|
||||
|
||||
onewire_reset(pin);
|
||||
onewire_write(pin, DS1820_SKIP_ROM, ONEWIRE_DEFAULT_POWER);
|
||||
onewire_write(pin, DS1820_READ_SCRATCHPAD, ONEWIRE_DEFAULT_POWER);
|
||||
onewire_skip_rom(pin);
|
||||
onewire_write(pin, DS18B20_READ_SCRATCHPAD);
|
||||
|
||||
uint8_t get[10];
|
||||
|
||||
|
@ -106,3 +110,114 @@ float ds18b20_read_single(uint8_t pin) {
|
|||
return temperature;
|
||||
//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_
|
||||
#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 {
|
||||
uint8_t id;
|
||||
float value;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 0a0c22e0efcf2f8f71d7e16712f80b8f77326f72
|
||||
Subproject commit a7ffc8f7396573bec401e0afcc073137522d5305
|
|
@ -1,206 +1,207 @@
|
|||
#include "onewire.h"
|
||||
#include "string.h"
|
||||
#include "task.h"
|
||||
#include "esp/gpio.h"
|
||||
|
||||
// global search state
|
||||
static unsigned char ROM_NO[ONEWIRE_NUM][8];
|
||||
static uint8_t LastDiscrepancy[ONEWIRE_NUM];
|
||||
static uint8_t LastFamilyDiscrepancy[ONEWIRE_NUM];
|
||||
static uint8_t LastDeviceFlag[ONEWIRE_NUM];
|
||||
#define ONEWIRE_SELECT_ROM 0x55
|
||||
#define ONEWIRE_SKIP_ROM 0xcc
|
||||
#define ONEWIRE_SEARCH 0xf0
|
||||
|
||||
void onewire_init(uint8_t pin)
|
||||
{
|
||||
gpio_enable(pin, GPIO_INPUT);
|
||||
onewire_reset_search(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
|
||||
// shorted).
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t retries = 125;
|
||||
bool onewire_reset(int pin) {
|
||||
bool r;
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin);
|
||||
interrupts();
|
||||
gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
|
||||
gpio_write(pin, 1);
|
||||
// wait until the wire is high... just in case
|
||||
do {
|
||||
if (--retries == 0) return 0;
|
||||
delayMicroseconds(2);
|
||||
} while ( !DIRECT_READ(pin));
|
||||
if (!_onewire_wait_for_bus(pin, 250)) return false;
|
||||
|
||||
gpio_write(pin, 0);
|
||||
sdk_os_delay_us(480);
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
gpio_write(pin, 1); // allow it to float
|
||||
sdk_os_delay_us(70);
|
||||
r = !gpio_read(pin);
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
// Wait for all devices to finish pulling the bus low before returning
|
||||
if (!_onewire_wait_for_bus(pin, 410)) return false;
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
||||
interrupts();
|
||||
delayMicroseconds(480);
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin); // allow it to float
|
||||
delayMicroseconds(70);
|
||||
r = !DIRECT_READ(pin);
|
||||
interrupts();
|
||||
delayMicroseconds(410);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Write a bit. Port and bit is used to cut lookup time and provide
|
||||
// more certain timing.
|
||||
//
|
||||
static void onewire_write_bit(uint8_t pin, uint8_t v)
|
||||
{
|
||||
if (v & 1) {
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
||||
delayMicroseconds(10);
|
||||
DIRECT_WRITE_HIGH(pin); // drive output high
|
||||
interrupts();
|
||||
delayMicroseconds(55);
|
||||
static bool _onewire_write_bit(int pin, bool v) {
|
||||
if (!_onewire_wait_for_bus(pin, 10)) return false;
|
||||
if (v) {
|
||||
taskENTER_CRITICAL();
|
||||
gpio_write(pin, 0); // drive output low
|
||||
sdk_os_delay_us(10);
|
||||
gpio_write(pin, 1); // allow output high
|
||||
taskEXIT_CRITICAL();
|
||||
sdk_os_delay_us(55);
|
||||
} else {
|
||||
noInterrupts();
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
DIRECT_MODE_OUTPUT(pin); // drive output low
|
||||
delayMicroseconds(65);
|
||||
DIRECT_WRITE_HIGH(pin); // drive output high
|
||||
interrupts();
|
||||
delayMicroseconds(5);
|
||||
taskENTER_CRITICAL();
|
||||
gpio_write(pin, 0); // drive output low
|
||||
sdk_os_delay_us(65);
|
||||
gpio_write(pin, 1); // allow output high
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
sdk_os_delay_us(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read a bit. Port and bit is used to cut lookup time and provide
|
||||
// more certain timing.
|
||||
//
|
||||
static uint8_t onewire_read_bit(uint8_t pin)
|
||||
{
|
||||
uint8_t r;
|
||||
static int _onewire_read_bit(int pin) {
|
||||
int r;
|
||||
|
||||
if (!_onewire_wait_for_bus(pin, 10)) return -1;
|
||||
taskENTER_CRITICAL();
|
||||
gpio_write(pin, 0);
|
||||
sdk_os_delay_us(2);
|
||||
gpio_write(pin, 1); // let pin float, pull up will raise
|
||||
sdk_os_delay_us(11);
|
||||
r = gpio_read(pin); // Must sample within 15us of start
|
||||
taskEXIT_CRITICAL();
|
||||
sdk_os_delay_us(48);
|
||||
|
||||
noInterrupts();
|
||||
DIRECT_MODE_OUTPUT(pin);
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
delayMicroseconds(3);
|
||||
DIRECT_MODE_INPUT(pin); // let pin float, pull up will raise
|
||||
delayMicroseconds(10);
|
||||
r = DIRECT_READ(pin);
|
||||
interrupts();
|
||||
delayMicroseconds(53);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Write a byte. The writing code uses the active drivers to raise the
|
||||
// pin high, if you need power after the write (e.g. DS18S20 in
|
||||
// parasite power mode) then set 'power' to 1, otherwise the pin will
|
||||
// go tri-state at the end of the write to avoid heating in a short or
|
||||
// other mishap.
|
||||
// Write a byte. The writing code uses open-drain mode and expects the pullup
|
||||
// resistor to pull the line high when not driven low. If you need strong
|
||||
// power after the write (e.g. DS18B20 in parasite power mode) then call
|
||||
// onewire_power() after this is complete to actively drive the line high.
|
||||
//
|
||||
void onewire_write(uint8_t pin, uint8_t v, uint8_t power /* = 0 */) {
|
||||
bool onewire_write(int pin, uint8_t v) {
|
||||
uint8_t bitMask;
|
||||
|
||||
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);
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
interrupts();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power /* = 0 */) {
|
||||
uint16_t i;
|
||||
for (i = 0 ; i < count ; i++)
|
||||
onewire_write(pin, buf[i], ONEWIRE_DEFAULT_POWER);
|
||||
if (!power) {
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin);
|
||||
DIRECT_WRITE_LOW(pin);
|
||||
interrupts();
|
||||
bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < count ; i++) {
|
||||
if (!onewire_write(pin, buf[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read a byte
|
||||
//
|
||||
uint8_t onewire_read(uint8_t pin) {
|
||||
int onewire_read(int pin) {
|
||||
uint8_t bitMask;
|
||||
uint8_t r = 0;
|
||||
int r = 0;
|
||||
int bit;
|
||||
|
||||
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
|
||||
if (onewire_read_bit(pin)) r |= bitMask;
|
||||
bit = _onewire_read_bit(pin);
|
||||
if (bit < 0) {
|
||||
return -1;
|
||||
} else if (bit) {
|
||||
r |= bitMask;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count) {
|
||||
uint16_t i;
|
||||
for (i = 0 ; i < count ; i++)
|
||||
buf[i] = onewire_read(pin);
|
||||
bool onewire_read_bytes(int pin, uint8_t *buf, size_t count) {
|
||||
size_t i;
|
||||
int b;
|
||||
|
||||
for (i = 0 ; i < count ; i++) {
|
||||
b = onewire_read(pin);
|
||||
if (b < 0) return false;
|
||||
buf[i] = b;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do a ROM select
|
||||
//
|
||||
void onewire_select(uint8_t pin, const uint8_t rom[8])
|
||||
{
|
||||
bool onewire_select(int pin, onewire_addr_t addr) {
|
||||
uint8_t i;
|
||||
|
||||
onewire_write(pin, 0x55, ONEWIRE_DEFAULT_POWER); // Choose ROM
|
||||
|
||||
for (i = 0; i < 8; i++) onewire_write(pin, rom[i], ONEWIRE_DEFAULT_POWER);
|
||||
if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do a ROM skip
|
||||
//
|
||||
void onewire_skip(uint8_t pin)
|
||||
{
|
||||
onewire_write(pin, 0xCC, ONEWIRE_DEFAULT_POWER); // Skip ROM
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (!onewire_write(pin, addr & 0xff)) {
|
||||
return false;
|
||||
}
|
||||
addr >>= 8;
|
||||
}
|
||||
|
||||
void onewire_depower(uint8_t pin)
|
||||
{
|
||||
noInterrupts();
|
||||
DIRECT_MODE_INPUT(pin);
|
||||
interrupts();
|
||||
return true;
|
||||
}
|
||||
|
||||
// You need to use this function to start a search again from the beginning.
|
||||
// You do not need to do it for the first search, though you could.
|
||||
//
|
||||
void onewire_reset_search(uint8_t pin)
|
||||
{
|
||||
bool onewire_skip_rom(int pin) {
|
||||
return onewire_write(pin, ONEWIRE_SKIP_ROM);
|
||||
}
|
||||
|
||||
bool onewire_power(int pin) {
|
||||
// Make sure the bus is not being held low before driving it high, or we
|
||||
// may end up shorting ourselves out.
|
||||
if (!_onewire_wait_for_bus(pin, 10)) return false;
|
||||
|
||||
gpio_enable(pin, GPIO_OUTPUT);
|
||||
gpio_write(pin, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void onewire_depower(int pin) {
|
||||
gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
|
||||
}
|
||||
|
||||
void onewire_search_start(onewire_search_t *search) {
|
||||
// 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;
|
||||
}
|
||||
memset(search, 0, sizeof(*search));
|
||||
}
|
||||
|
||||
// Setup the search to find the device type 'family_code' on the next call
|
||||
// to search(*newAddr) if it is present.
|
||||
//
|
||||
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;
|
||||
void onewire_search_prefix(onewire_search_t *search, uint8_t 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;
|
||||
|
||||
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 this function returns a '1' then it has
|
||||
// enumerated the next device and you may retrieve the ROM from the
|
||||
// OneWire::address variable. If there are no devices, no further
|
||||
// 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
|
||||
// enumeration then a 0 is returned. If a new device is found then
|
||||
// its address is copied to newAddr. Use OneWire::reset_search() to
|
||||
// enumeration then ONEWIRE_NONE is returned. Use OneWire::reset_search() to
|
||||
// start over.
|
||||
//
|
||||
// --- Replaced by the one from the Dallas Semiconductor web site ---
|
||||
|
@ -210,13 +211,15 @@ void onewire_target_search(uint8_t pin, uint8_t family_code)
|
|||
// Return 1 : device found, ROM number in ROM_NO buffer
|
||||
// 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 last_zero, rom_byte_number, search_result;
|
||||
uint8_t last_zero, search_result;
|
||||
int rom_byte_number;
|
||||
uint8_t id_bit, cmp_id_bit;
|
||||
|
||||
unsigned char rom_byte_mask, search_direction;
|
||||
onewire_addr_t addr;
|
||||
unsigned char rom_byte_mask;
|
||||
bool search_direction;
|
||||
|
||||
// initialize for search
|
||||
id_bit_number = 1;
|
||||
|
@ -226,66 +229,60 @@ uint8_t onewire_search(uint8_t pin, uint8_t *newAddr)
|
|||
search_result = 0;
|
||||
|
||||
// if the last call was not the last one
|
||||
if (!LastDeviceFlag[pin])
|
||||
{
|
||||
if (!search->last_device_found) {
|
||||
// 1-Wire reset
|
||||
if (!onewire_reset(pin))
|
||||
{
|
||||
if (!onewire_reset(pin)) {
|
||||
// reset the search
|
||||
LastDiscrepancy[pin] = 0;
|
||||
LastDeviceFlag[pin] = 0;
|
||||
LastFamilyDiscrepancy[pin] = 0;
|
||||
return 0;
|
||||
search->last_discrepancy = 0;
|
||||
search->last_device_found = false;
|
||||
return ONEWIRE_NONE;
|
||||
}
|
||||
|
||||
// issue the search command
|
||||
onewire_write(pin, 0xF0, ONEWIRE_DEFAULT_POWER);
|
||||
onewire_write(pin, ONEWIRE_SEARCH);
|
||||
|
||||
// loop to do the search
|
||||
do
|
||||
{
|
||||
do {
|
||||
// read a bit and its complement
|
||||
id_bit = onewire_read_bit(pin);
|
||||
cmp_id_bit = onewire_read_bit(pin);
|
||||
id_bit = _onewire_read_bit(pin);
|
||||
cmp_id_bit = _onewire_read_bit(pin);
|
||||
|
||||
// check for no devices on 1-wire
|
||||
if ((id_bit == 1) && (cmp_id_bit == 1))
|
||||
if ((id_bit < 0) || (cmp_id_bit < 0)) {
|
||||
// Read error
|
||||
break;
|
||||
else
|
||||
{
|
||||
} else if ((id_bit == 1) && (cmp_id_bit == 1)) {
|
||||
break;
|
||||
} else {
|
||||
// all devices coupled have 0 or 1
|
||||
if (id_bit != cmp_id_bit)
|
||||
if (id_bit != cmp_id_bit) {
|
||||
search_direction = id_bit; // bit write value for search
|
||||
else
|
||||
{
|
||||
} 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 (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 == LastDiscrepancy[pin]);
|
||||
search_direction = (id_bit_number == search->last_discrepancy);
|
||||
}
|
||||
|
||||
// if 0 was picked then record its position in LastZero
|
||||
if (search_direction == 0)
|
||||
{
|
||||
if (!search_direction) {
|
||||
last_zero = id_bit_number;
|
||||
|
||||
// check for Last discrepancy in family
|
||||
if (last_zero < 9)
|
||||
LastFamilyDiscrepancy[pin] = last_zero;
|
||||
}
|
||||
}
|
||||
|
||||
// set or clear the bit in the ROM byte rom_byte_number
|
||||
// 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;
|
||||
if (search_direction) {
|
||||
search->rom_no[rom_byte_number] |= rom_byte_mask;
|
||||
} else {
|
||||
search->rom_no[rom_byte_number] &= ~rom_byte_mask;
|
||||
}
|
||||
|
||||
// serial number search direction write bit
|
||||
onewire_write_bit(pin, search_direction);
|
||||
_onewire_write_bit(pin, search_direction);
|
||||
|
||||
// increment the byte counter id_bit_number
|
||||
// and shift the mask rom_byte_mask
|
||||
|
@ -293,46 +290,40 @@ uint8_t onewire_search(uint8_t pin, uint8_t *newAddr)
|
|||
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)
|
||||
{
|
||||
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
|
||||
} 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;
|
||||
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 (LastDiscrepancy[pin] == 0)
|
||||
LastDeviceFlag[pin] = 1;
|
||||
if (search->last_discrepancy == 0) {
|
||||
search->last_device_found = true;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!search_result || !search->rom_no[0]) {
|
||||
search->last_discrepancy = 0;
|
||||
search->last_device_found = false;
|
||||
return ONEWIRE_NONE;
|
||||
} else {
|
||||
addr = 0;
|
||||
for (rom_byte_number = 7; rom_byte_number >= 0; rom_byte_number--) {
|
||||
addr = (addr << 8) | search->rom_no[rom_byte_number];
|
||||
}
|
||||
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]);
|
||||
//printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr);
|
||||
}
|
||||
}
|
||||
return search_result;
|
||||
return addr;
|
||||
}
|
||||
|
||||
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
|
||||
|
@ -371,12 +362,11 @@ static const uint8_t dscrc_table[] = {
|
|||
// compared to all those delayMicrosecond() calls. But I got
|
||||
// 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;
|
||||
|
||||
while (len--) {
|
||||
crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
|
||||
crc = pgm_read_byte(dscrc_table + (crc ^ *data++));
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
@ -385,14 +375,12 @@ uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
|
|||
// Compute a Dallas Semiconductor 8 bit CRC directly.
|
||||
// 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;
|
||||
|
||||
while (len--) {
|
||||
uint8_t inbyte = *addr++;
|
||||
uint8_t i;
|
||||
for (i = 8; i; i--) {
|
||||
uint8_t inbyte = *data++;
|
||||
for (int i = 8; i; i--) {
|
||||
uint8_t mix = (crc ^ inbyte) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix) crc ^= 0x8C;
|
||||
|
@ -423,9 +411,8 @@ uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
|
|||
// *not* at a 16-bit integer.
|
||||
// @param crc - The crc starting value (optional)
|
||||
// @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)
|
||||
{
|
||||
crc = ~onewire_crc16(input, len, 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);
|
||||
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 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)
|
||||
{
|
||||
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] =
|
||||
{ 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 "FreeRTOS.h"
|
||||
|
||||
// 1 for keeping the parasitic power on H
|
||||
#define ONEWIRE_DEFAULT_POWER 1
|
||||
/** @file onewire.h
|
||||
*
|
||||
* Routines to access devices using the Dallas Semiconductor 1-Wire(tm)
|
||||
* protocol.
|
||||
*/
|
||||
|
||||
// Maximum number of devices.
|
||||
#define ONEWIRE_NUM 20
|
||||
|
||||
// You can exclude certain features from OneWire. In theory, this
|
||||
// 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.
|
||||
/** Select the table-lookup method of computing the 8-bit CRC
|
||||
* 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
|
||||
* is used.
|
||||
*/
|
||||
#ifndef ONEWIRE_CRC8_TABLE
|
||||
#define ONEWIRE_CRC8_TABLE 0
|
||||
#endif
|
||||
|
||||
// Platform specific I/O definitions
|
||||
#define noInterrupts portDISABLE_INTERRUPTS
|
||||
#define interrupts portENABLE_INTERRUPTS
|
||||
#define delayMicroseconds sdk_os_delay_us
|
||||
/** Type used to hold all 1-Wire device ROM addresses (64-bit) */
|
||||
typedef uint64_t onewire_addr_t;
|
||||
|
||||
#define DIRECT_READ(pin) gpio_read(pin)
|
||||
#define DIRECT_MODE_INPUT(pin) gpio_enable(pin, GPIO_INPUT)
|
||||
#define DIRECT_MODE_OUTPUT(pin) gpio_enable(pin, GPIO_OUTPUT)
|
||||
#define DIRECT_WRITE_LOW(pin) gpio_write(pin, 0)
|
||||
#define DIRECT_WRITE_HIGH(pin) gpio_write(pin, 1)
|
||||
/** Structure to contain the current state for onewire_search_next(), etc */
|
||||
typedef struct {
|
||||
uint8_t rom_no[8];
|
||||
uint8_t last_discrepancy;
|
||||
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
|
||||
// with a presence pulse. Returns 0 if there is no device or the
|
||||
// bus is shorted or otherwise held low for more than 250uS
|
||||
uint8_t onewire_reset(uint8_t pin);
|
||||
/** Perform a 1-Wire reset cycle.
|
||||
*
|
||||
* @param pin The GPIO pin connected to the 1-Wire bus.
|
||||
*
|
||||
* @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.
|
||||
void onewire_select(uint8_t pin, const uint8_t rom[8]);
|
||||
/** Issue a 1-Wire rom select command to select a particular device.
|
||||
*
|
||||
* 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.
|
||||
void onewire_skip(uint8_t pin);
|
||||
/** Issue a 1-Wire "skip ROM" command to select *all* devices on the bus.
|
||||
*
|
||||
* 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
|
||||
// the end for parasitically powered devices. You are responsible
|
||||
// for eventually depowering it by calling depower() or doing
|
||||
// another read or write.
|
||||
void onewire_write(uint8_t pin, uint8_t v, uint8_t power);
|
||||
/** Write a byte on the onewire bus.
|
||||
*
|
||||
* The writing code uses open-drain mode and expects the pullup resistor to
|
||||
* pull the line high when not driven low. If you need strong power after the
|
||||
* 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.
|
||||
uint8_t onewire_read(uint8_t pin);
|
||||
/** Read a byte from a 1-Wire device.
|
||||
*
|
||||
* @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
|
||||
// note in write() about that.
|
||||
// void onewire_write_bit(uint8_t pin, uint8_t v);
|
||||
/** Actively drive the bus high to provide extra power for certain operations
|
||||
* of parasitically-powered devices.
|
||||
*
|
||||
* 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.
|
||||
// uint8_t onewire_read_bit(uint8_t pin);
|
||||
/** Stop forcing power onto the bus.
|
||||
*
|
||||
* 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
|
||||
// you used the 'power' flag to write() or used a write_bit() call
|
||||
// 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
|
||||
// someone shorts your bus.
|
||||
void onewire_depower(uint8_t pin);
|
||||
/** Clear the search state so that it will start from the beginning on the next
|
||||
* call to onewire_search_next().
|
||||
*
|
||||
* @param search The onewire_search_t structure to reset.
|
||||
*/
|
||||
void onewire_search_start(onewire_search_t *search);
|
||||
|
||||
// Clear the search state so that if will start from the beginning again.
|
||||
void onewire_reset_search(uint8_t pin);
|
||||
/** Setup the search to search for devices with the specified "family code".
|
||||
*
|
||||
* @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
|
||||
// to search(*newAddr) if it is present.
|
||||
void onewire_target_search(uint8_t pin, uint8_t family_code);
|
||||
/** Search for the next device on the bus.
|
||||
*
|
||||
* 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
|
||||
// returned. A zero might 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. The order is deterministic. You will always get
|
||||
// the same devices in the same order.
|
||||
uint8_t onewire_search(uint8_t pin, uint8_t *newAddr);
|
||||
/** Compute a Dallas Semiconductor 8 bit CRC.
|
||||
*
|
||||
* These are used in the ROM address and scratchpad registers to verify the
|
||||
* transmitted data is correct.
|
||||
*/
|
||||
uint8_t onewire_crc8(const uint8_t *data, uint8_t len);
|
||||
|
||||
// Compute a Dallas Semiconductor 8 bit CRC, these are used in the
|
||||
// ROM and scratchpad registers.
|
||||
uint8_t onewire_crc8(const uint8_t *addr, uint8_t len);
|
||||
/** Compute the 1-Wire CRC16 and compare it against the received CRC.
|
||||
*
|
||||
* 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.
|
||||
// Example usage (reading a DS2408):
|
||||
// // 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
|
||||
// WriteBytes(net, buf, 3); // Write 3 cmd bytes
|
||||
// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16
|
||||
// if (!CheckCRC16(buf, 11, &buf[11])) {
|
||||
// // Handle error.
|
||||
// }
|
||||
//
|
||||
// @param input - Array of bytes to checksum.
|
||||
// @param len - How many bytes to use.
|
||||
// @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 - 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);
|
||||
/** 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 are in `input`.
|
||||
* @param crc_iv The crc starting value (optional)
|
||||
*
|
||||
* @returns the CRC16, as defined by Dallas Semiconductor.
|
||||
*/
|
||||
uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv);
|
||||
|
||||
#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 "ota-tftp.h"
|
||||
#include "rboot-ota.h"
|
||||
#include "rboot.h"
|
||||
#include "rboot-api.h"
|
||||
|
||||
#define TFTP_FIRMWARE_FILE "firmware.bin"
|
||||
#define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
|
||||
|
@ -112,7 +113,7 @@ static void tftp_task(void *listen_port)
|
|||
netbuf_delete(netbuf);
|
||||
|
||||
/* 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();
|
||||
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 <task.h>
|
||||
|
||||
#include "rboot-ota.h"
|
||||
#include <rboot.h>
|
||||
|
||||
#define ROM_MAGIC_OLD 0xe9
|
||||
#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)) {
|
||||
uint8_t magic;
|
||||
|
@ -35,67 +28,6 @@ typedef struct __attribute__((packed)) {
|
|||
} 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
|
||||
// takes up 'expected_length' bytes.
|
||||
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;
|
||||
|
||||
uint8_t checksum = CHECKSUM_INIT;
|
||||
uint8_t checksum = CHKSUM_INIT;
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
// 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__
|
||||
#define __SSID_CONFIG_H__
|
||||
|
|
45
ld/common.ld
45
ld/common.ld
|
@ -139,13 +139,34 @@ SECTIONS
|
|||
(except for libgcc which is matched above.)
|
||||
*/
|
||||
*(.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.a:*.o(.rodata.* .rodata)
|
||||
/* actual certificate in example (TEMPORARY HACK) */
|
||||
*:cert.o(.rodata.* .rodata)
|
||||
/* Anything explicitly marked as "irom" or "irom0" should go here */
|
||||
*(.irom.* .irom.*.* .irom0.*)
|
||||
_irom0_text_end = ABSOLUTE(.);
|
||||
/* 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)
|
||||
|
||||
} >irom0_0_seg :irom0_0_phdr
|
||||
|
||||
.data : ALIGN(4)
|
||||
|
@ -179,23 +200,6 @@ SECTIONS
|
|||
*(.gnu.version_r)
|
||||
*(.eh_frame)
|
||||
. = (. + 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)
|
||||
*(.gnu.version_d)
|
||||
. = ALIGN(4); /* this table MUST be 4-byte aligned */
|
||||
|
@ -230,7 +234,6 @@ SECTIONS
|
|||
} >dram0_0_seg :dram0_0_bss_phdr
|
||||
/* __stack = 0x3ffc8000; */
|
||||
|
||||
|
||||
.lit4 : ALIGN(4)
|
||||
{
|
||||
_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") \
|
||||
} while(0)
|
||||
#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)) { \
|
||||
printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
|
||||
|
|
|
@ -49,10 +49,18 @@
|
|||
#include "lwip/mem.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
/* Very crude mechanism used to determine if the critical section handling
|
||||
functions are being called from an interrupt context or not. This relies on
|
||||
the interrupt handler setting this variable manually. */
|
||||
portBASE_TYPE xInsideISR = pdFALSE;
|
||||
extern bool esp_in_isr;
|
||||
|
||||
/* Based on the default xInsideISR mechanism to determine
|
||||
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
|
||||
|
@ -145,7 +153,7 @@ err_t sys_mbox_trypost( sys_mbox_t *pxMailBox, void *pxMessageToPost )
|
|||
err_t xReturn;
|
||||
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
if( xInsideISR != pdFALSE )
|
||||
if( is_inside_isr() != pdFALSE )
|
||||
{
|
||||
xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
|
||||
}
|
||||
|
@ -208,7 +216,7 @@ unsigned long ulReturn;
|
|||
|
||||
if( ulTimeOut != 0UL )
|
||||
{
|
||||
configASSERT( xInsideISR == ( portBASE_TYPE ) 0 );
|
||||
configASSERT( is_inside_isr() == ( portBASE_TYPE ) 0 );
|
||||
|
||||
if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_RATE_MS ) )
|
||||
{
|
||||
|
@ -267,7 +275,7 @@ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
|||
ppvBuffer = &pvDummy;
|
||||
}
|
||||
|
||||
if( xInsideISR != pdFALSE )
|
||||
if( is_inside_isr() != pdFALSE )
|
||||
{
|
||||
lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
|
||||
}
|
||||
|
@ -443,7 +451,7 @@ void sys_sem_signal( sys_sem_t *pxSemaphore )
|
|||
{
|
||||
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
if( xInsideISR != pdFALSE )
|
||||
if( is_inside_isr() != pdFALSE )
|
||||
{
|
||||
xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
|
||||
}
|
||||
|
@ -541,7 +549,7 @@ sys_thread_t xReturn;
|
|||
*---------------------------------------------------------------------------*/
|
||||
sys_prot_t sys_arch_protect( void )
|
||||
{
|
||||
if( xInsideISR == pdFALSE )
|
||||
if( is_inside_isr() == pdFALSE )
|
||||
{
|
||||
taskENTER_CRITICAL();
|
||||
}
|
||||
|
@ -562,7 +570,7 @@ sys_prot_t sys_arch_protect( void )
|
|||
void sys_arch_unprotect( sys_prot_t xValue )
|
||||
{
|
||||
(void) xValue;
|
||||
if( xInsideISR == pdFALSE )
|
||||
if( is_inside_isr() == pdFALSE )
|
||||
{
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
|
@ -582,4 +590,3 @@ void sys_assert( const char *pcMessage )
|
|||
/*-------------------------------------------------------------------------*
|
||||
* 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