Merge branch 'feature/better_crash_dumps' into open-libmain

This commit is contained in:
Angus Gratton 2016-05-09 12:07:34 +10:00
commit 5fa17990dd
62 changed files with 5163 additions and 1355 deletions

View file

@ -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 )

View file

@ -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));

View file

@ -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.)

View file

@ -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

View file

@ -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
View 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) {}
}

View file

@ -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;
}

View file

@ -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 */

View 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

View file

@ -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 */

View file

@ -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;

View file

@ -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.

View file

@ -1,5 +1,4 @@
# Simple makefile for simple example
PROGRAM=cpp_01_tasks
OTA=0
EXTRA_COMPONENTS=extras/cpp_support
include ../../common.mk

View file

@ -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();

View file

@ -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");
// Wait for a little bit between each sample (note that the
// ds18b20_measure_and_read_multi operation already takes at
// least 750ms to run, so this is on top of that delay).
vTaskDelay(LOOP_DELAY_MS / portTICK_RATE_MS);
}
}
printf("\n");
vTaskDelay(delay / portTICK_RATE_MS);
}
}
void user_init(void)
{
void user_init(void) {
uart_set_baud(0, 115200);
printf("SDK version:%s\n", sdk_system_get_sdk_version());
xTaskCreate(&print_temperature, (signed char *)"print_temperature", 256, NULL, 2, NULL);
}

View file

@ -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

View file

@ -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";

View file

@ -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();
}
/*

View file

@ -0,0 +1,3 @@
PROGRAM=mqtt_client
EXTRA_COMPONENTS = extras/paho_mqtt_c
include ../../common.mk

View 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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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();
// wait until the wire is high... just in case
do {
if (--retries == 0) return 0;
delayMicroseconds(2);
} while ( !DIRECT_READ(pin));
gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
gpio_write(pin, 1);
// wait until the wire is high... just in case
if (!_onewire_wait_for_bus(pin, 250)) 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;
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;
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);
} 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);
}
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 {
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;
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;
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);
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 */) {
uint8_t bitMask;
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 ( !power) {
noInterrupts();
DIRECT_MODE_INPUT(pin);
DIRECT_WRITE_LOW(pin);
interrupts();
}
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
if (!_onewire_write_bit(pin, (bitMask & v))) {
return false;
}
}
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) {
uint8_t bitMask;
uint8_t r = 0;
int onewire_read(int pin) {
uint8_t bitMask;
int r = 0;
int bit;
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
if (onewire_read_bit(pin)) r |= bitMask;
}
return r;
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
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
if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) {
return false;
}
for (i = 0; i < 8; i++) onewire_write(pin, rom[i], ONEWIRE_DEFAULT_POWER);
for (i = 0; i < 8; i++) {
if (!onewire_write(pin, addr & 0xff)) {
return false;
}
addr >>= 8;
}
return true;
}
// Do a ROM skip
//
void onewire_skip(uint8_t pin)
{
onewire_write(pin, 0xCC, ONEWIRE_DEFAULT_POWER); // Skip ROM
bool onewire_skip_rom(int pin) {
return onewire_write(pin, ONEWIRE_SKIP_ROM);
}
void onewire_depower(uint8_t pin)
{
noInterrupts();
DIRECT_MODE_INPUT(pin);
interrupts();
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;
}
// 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)
{
// 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;
}
void onewire_depower(int pin) {
gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
}
// 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;
uint8_t i;
for (i = 1; i < 8; i++)
ROM_NO[pin][i] = 0;
LastDiscrepancy[pin] = 64;
LastFamilyDiscrepancy[pin] = 0;
LastDeviceFlag[pin] = 0;
void onewire_search_start(onewire_search_t *search) {
// reset the search state
memset(search, 0, sizeof(*search));
}
// 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
void onewire_search_prefix(onewire_search_t *search, uint8_t family_code) {
uint8_t i;
search->rom_no[0] = family_code;
for (i = 1; i < 8; i++) {
search->rom_no[i] = 0;
}
search->last_discrepancy = 64;
search->last_device_found = false;
}
// Perform a search. If the next device has been successfully enumerated, its
// ROM address will be returned. If there are no devices, no further
// devices, or something horrible happens in the middle of the
// 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,129 +211,119 @@ 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)
{
uint8_t id_bit_number;
uint8_t last_zero, rom_byte_number, search_result;
uint8_t id_bit, cmp_id_bit;
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, search_result;
int rom_byte_number;
uint8_t id_bit, cmp_id_bit;
onewire_addr_t addr;
unsigned char rom_byte_mask;
bool search_direction;
unsigned char rom_byte_mask, search_direction;
// initialize for search
id_bit_number = 1;
last_zero = 0;
rom_byte_number = 0;
rom_byte_mask = 1;
search_result = 0;
// initialize for search
id_bit_number = 1;
last_zero = 0;
rom_byte_number = 0;
rom_byte_mask = 1;
search_result = 0;
// if the last call was not the last one
if (!search->last_device_found) {
// 1-Wire reset
if (!onewire_reset(pin)) {
// reset the search
search->last_discrepancy = 0;
search->last_device_found = false;
return ONEWIRE_NONE;
}
// if the last call was not the last one
if (!LastDeviceFlag[pin])
{
// 1-Wire reset
if (!onewire_reset(pin))
{
// reset the search
LastDiscrepancy[pin] = 0;
LastDeviceFlag[pin] = 0;
LastFamilyDiscrepancy[pin] = 0;
return 0;
}
// issue the search command
onewire_write(pin, ONEWIRE_SEARCH);
// issue the search command
onewire_write(pin, 0xF0, ONEWIRE_DEFAULT_POWER);
// loop to do the search
do {
// read a bit and its complement
id_bit = _onewire_read_bit(pin);
cmp_id_bit = _onewire_read_bit(pin);
// loop to do the search
do
{
// read a bit and its complement
id_bit = onewire_read_bit(pin);
cmp_id_bit = onewire_read_bit(pin);
// check for no devices on 1-wire
if ((id_bit < 0) || (cmp_id_bit < 0)) {
// Read error
break;
} else if ((id_bit == 1) && (cmp_id_bit == 1)) {
break;
} else {
// all devices coupled have 0 or 1
if (id_bit != cmp_id_bit) {
search_direction = id_bit; // bit write value for search
} else {
// if this discrepancy if before the Last Discrepancy
// on a previous next then pick the same as last time
if (id_bit_number < search->last_discrepancy) {
search_direction = ((search->rom_no[rom_byte_number] & rom_byte_mask) > 0);
} else {
// if equal to last pick 1, if not then pick 0
search_direction = (id_bit_number == search->last_discrepancy);
}
// check for no devices on 1-wire
if ((id_bit == 1) && (cmp_id_bit == 1))
break;
else
{
// all devices coupled have 0 or 1
if (id_bit != cmp_id_bit)
search_direction = id_bit; // bit write value for search
else
{
// if this discrepancy if before the Last Discrepancy
// on a previous next then pick the same as last time
if (id_bit_number < LastDiscrepancy[pin])
search_direction = ((ROM_NO[pin][rom_byte_number] & rom_byte_mask) > 0);
else
// if equal to last pick 1, if not then pick 0
search_direction = (id_bit_number == LastDiscrepancy[pin]);
// if 0 was picked then record its position in LastZero
if (!search_direction) {
last_zero = id_bit_number;
}
}
// if 0 was picked then record its position in LastZero
if (search_direction == 0)
{
last_zero = id_bit_number;
// set or clear the bit in the ROM byte rom_byte_number
// with mask 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;
}
// check for Last discrepancy in family
if (last_zero < 9)
LastFamilyDiscrepancy[pin] = last_zero;
}
// serial number search direction write bit
_onewire_write_bit(pin, search_direction);
// increment the byte counter id_bit_number
// and shift the mask rom_byte_mask
id_bit_number++;
rom_byte_mask <<= 1;
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
if (rom_byte_mask == 0) {
rom_byte_number++;
rom_byte_mask = 1;
}
}
} while (rom_byte_number < 8); // loop until through all ROM bytes 0-7
// if the search was successful then
if (!(id_bit_number < 65)) {
// search successful so set last_discrepancy,last_device_found,search_result
search->last_discrepancy = last_zero;
// check for last device
if (search->last_discrepancy == 0) {
search->last_device_found = true;
}
// set or clear the bit in the ROM byte rom_byte_number
// 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;
search_result = 1;
}
}
// serial number search direction write bit
onewire_write_bit(pin, search_direction);
// increment the byte counter id_bit_number
// and shift the mask rom_byte_mask
id_bit_number++;
rom_byte_mask <<= 1;
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
if (rom_byte_mask == 0)
{
rom_byte_number++;
rom_byte_mask = 1;
}
}
}
while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
// if the search was successful then
if (!(id_bit_number < 65))
{
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
LastDiscrepancy[pin] = last_zero;
// check for last device
if (LastDiscrepancy[pin] == 0)
LastDeviceFlag[pin] = 1;
search_result = 1;
}
}
// if no device found then reset counters so next 'search' will be like a first
if (!search_result || !ROM_NO[pin][0])
{
LastDiscrepancy[pin] = 0;
LastDeviceFlag[pin] = 0;
LastFamilyDiscrepancy[pin] = 0;
search_result = 0;
}
else
{
for (rom_byte_number = 0; rom_byte_number < 8; rom_byte_number++)
{
newAddr[rom_byte_number] = ROM_NO[pin][rom_byte_number];
//printf("Ok I found something at %d - %x...\n",rom_byte_number, newAddr[rom_byte_number]);
}
}
return search_result;
// if no device found then reset counters so next 'search' will be like a first
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];
}
//printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr);
}
return addr;
}
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
@ -371,41 +362,38 @@ static const uint8_t dscrc_table[] = {
// compared to all those delayMicrosecond() calls. But I got
// confused, so I use this table from the examples.)
//
uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
{
uint8_t crc = 0;
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++));
}
return crc;
while (len--) {
crc = pgm_read_byte(dscrc_table + (crc ^ *data++));
}
return crc;
}
#else
//
// 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 crc = 0;
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 mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix) crc ^= 0x8C;
inbyte >>= 1;
}
}
return crc;
while (len--) {
uint8_t inbyte = *data++;
for (int i = 8; i; i--) {
uint8_t mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix) crc ^= 0x8C;
inbyte >>= 1;
}
}
return crc;
}
#endif
// 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.
// // 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
@ -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 };

View file

@ -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

View 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.

View 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;
}

View 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

View 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_ */

View 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);
}

View 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;
}

View 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;
}

View 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_ */

View 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

View 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;
}

View 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_ */

View 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_ */

View 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);
}

View 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_ */

View 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;
}

View 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_ */

View 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;
}

View 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_ */

View 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))

View file

@ -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;

View 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

View 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

View file

@ -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)
{

View 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__

View file

@ -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
View 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

View file

@ -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

View file

@ -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__

View file

@ -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.

View file

@ -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__); \

View 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
@ -66,17 +74,17 @@ portBASE_TYPE xInsideISR = pdFALSE;
*---------------------------------------------------------------------------*/
err_t sys_mbox_new( sys_mbox_t *pxMailBox, int iSize )
{
err_t xReturn = ERR_MEM;
err_t xReturn = ERR_MEM;
*pxMailBox = xQueueCreate( iSize, sizeof( void * ) );
*pxMailBox = xQueueCreate( iSize, sizeof( void * ) );
if( *pxMailBox != NULL )
{
xReturn = ERR_OK;
SYS_STATS_INC_USED( mbox );
}
if( *pxMailBox != NULL )
{
xReturn = ERR_OK;
SYS_STATS_INC_USED( mbox );
}
return xReturn;
return xReturn;
}
@ -96,21 +104,21 @@ void sys_mbox_free( sys_mbox_t *pxMailBox )
{
unsigned long ulMessagesWaiting;
ulMessagesWaiting = uxQueueMessagesWaiting( *pxMailBox );
configASSERT( ( ulMessagesWaiting == 0 ) );
ulMessagesWaiting = uxQueueMessagesWaiting( *pxMailBox );
configASSERT( ( ulMessagesWaiting == 0 ) );
#if SYS_STATS
{
if( ulMessagesWaiting != 0UL )
{
SYS_STATS_INC( mbox.err );
}
#if SYS_STATS
{
if( ulMessagesWaiting != 0UL )
{
SYS_STATS_INC( mbox.err );
}
SYS_STATS_DEC( mbox.used );
}
#endif /* SYS_STATS */
SYS_STATS_DEC( mbox.used );
}
#endif /* SYS_STATS */
vQueueDelete( *pxMailBox );
vQueueDelete( *pxMailBox );
}
/*---------------------------------------------------------------------------*
@ -124,7 +132,7 @@ unsigned long ulMessagesWaiting;
*---------------------------------------------------------------------------*/
void sys_mbox_post( sys_mbox_t *pxMailBox, void *pxMessageToPost )
{
while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
}
/*---------------------------------------------------------------------------*
@ -145,27 +153,27 @@ err_t sys_mbox_trypost( sys_mbox_t *pxMailBox, void *pxMessageToPost )
err_t xReturn;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if( xInsideISR != pdFALSE )
{
xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
}
else
{
xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( portTickType ) 0 );
}
if( is_inside_isr() != pdFALSE )
{
xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
}
else
{
xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( portTickType ) 0 );
}
if( xReturn == pdPASS )
{
xReturn = ERR_OK;
}
else
{
/* The queue was already full. */
xReturn = ERR_MEM;
SYS_STATS_INC( mbox.err );
}
if( xReturn == pdPASS )
{
xReturn = ERR_OK;
}
else
{
/* The queue was already full. */
xReturn = ERR_MEM;
SYS_STATS_INC( mbox.err );
}
return xReturn;
return xReturn;
}
/*---------------------------------------------------------------------------*
@ -199,46 +207,46 @@ void *pvDummy;
portTickType xStartTime, xEndTime, xElapsed;
unsigned long ulReturn;
xStartTime = xTaskGetTickCount();
xStartTime = xTaskGetTickCount();
if( NULL == ppvBuffer )
{
ppvBuffer = &pvDummy;
}
if( NULL == ppvBuffer )
{
ppvBuffer = &pvDummy;
}
if( ulTimeOut != 0UL )
{
configASSERT( xInsideISR == ( portBASE_TYPE ) 0 );
if( ulTimeOut != 0UL )
{
configASSERT( is_inside_isr() == ( portBASE_TYPE ) 0 );
if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_RATE_MS ) )
{
xEndTime = xTaskGetTickCount();
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_RATE_MS ) )
{
xEndTime = xTaskGetTickCount();
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
ulReturn = xElapsed;
}
else
{
/* Timed out. */
*ppvBuffer = NULL;
ulReturn = SYS_ARCH_TIMEOUT;
}
}
else
{
while( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) );
xEndTime = xTaskGetTickCount();
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
ulReturn = xElapsed;
}
else
{
/* Timed out. */
*ppvBuffer = NULL;
ulReturn = SYS_ARCH_TIMEOUT;
}
}
else
{
while( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) );
xEndTime = xTaskGetTickCount();
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
if( xElapsed == 0UL )
{
xElapsed = 1UL;
}
if( xElapsed == 0UL )
{
xElapsed = 1UL;
}
ulReturn = xElapsed;
}
ulReturn = xElapsed;
}
return ulReturn;
return ulReturn;
}
/*---------------------------------------------------------------------------*
@ -262,30 +270,30 @@ unsigned long ulReturn;
long lResult;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if( ppvBuffer== NULL )
{
ppvBuffer = &pvDummy;
}
if( ppvBuffer== NULL )
{
ppvBuffer = &pvDummy;
}
if( xInsideISR != pdFALSE )
{
lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
}
else
{
lResult = xQueueReceive( *pxMailBox, &( *ppvBuffer ), 0UL );
}
if( is_inside_isr() != pdFALSE )
{
lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
}
else
{
lResult = xQueueReceive( *pxMailBox, &( *ppvBuffer ), 0UL );
}
if( lResult == pdPASS )
{
ulReturn = ERR_OK;
}
else
{
ulReturn = SYS_MBOX_EMPTY;
}
if( lResult == pdPASS )
{
ulReturn = ERR_OK;
}
else
{
ulReturn = SYS_MBOX_EMPTY;
}
return ulReturn;
return ulReturn;
}
/*---------------------------------------------------------------------------*
@ -305,24 +313,24 @@ err_t sys_sem_new( sys_sem_t *pxSemaphore, u8_t ucCount )
{
err_t xReturn = ERR_MEM;
vSemaphoreCreateBinary( ( *pxSemaphore ) );
vSemaphoreCreateBinary( ( *pxSemaphore ) );
if( *pxSemaphore != NULL )
{
if( ucCount == 0U )
{
xSemaphoreTake( *pxSemaphore, 1UL );
}
if( *pxSemaphore != NULL )
{
if( ucCount == 0U )
{
xSemaphoreTake( *pxSemaphore, 1UL );
}
xReturn = ERR_OK;
SYS_STATS_INC_USED( sem );
}
else
{
SYS_STATS_INC( sem.err );
}
xReturn = ERR_OK;
SYS_STATS_INC_USED( sem );
}
else
{
SYS_STATS_INC( sem.err );
}
return xReturn;
return xReturn;
}
/*---------------------------------------------------------------------------*
@ -353,36 +361,36 @@ u32_t sys_arch_sem_wait( sys_sem_t *pxSemaphore, u32_t ulTimeout )
portTickType xStartTime, xEndTime, xElapsed;
unsigned long ulReturn;
xStartTime = xTaskGetTickCount();
xStartTime = xTaskGetTickCount();
if( ulTimeout != 0UL )
{
if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_RATE_MS ) == pdTRUE )
{
xEndTime = xTaskGetTickCount();
xElapsed = (xEndTime - xStartTime) * portTICK_RATE_MS;
ulReturn = xElapsed;
}
else
{
ulReturn = SYS_ARCH_TIMEOUT;
}
}
else
{
while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE );
xEndTime = xTaskGetTickCount();
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
if( ulTimeout != 0UL )
{
if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_RATE_MS ) == pdTRUE )
{
xEndTime = xTaskGetTickCount();
xElapsed = (xEndTime - xStartTime) * portTICK_RATE_MS;
ulReturn = xElapsed;
}
else
{
ulReturn = SYS_ARCH_TIMEOUT;
}
}
else
{
while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE );
xEndTime = xTaskGetTickCount();
xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
if( xElapsed == 0UL )
{
xElapsed = 1UL;
}
if( xElapsed == 0UL )
{
xElapsed = 1UL;
}
ulReturn = xElapsed;
}
ulReturn = xElapsed;
}
return ulReturn;
return ulReturn;
}
/** Create a new mutex
@ -392,33 +400,33 @@ err_t sys_mutex_new( sys_mutex_t *pxMutex )
{
err_t xReturn = ERR_MEM;
*pxMutex = xSemaphoreCreateMutex();
*pxMutex = xSemaphoreCreateMutex();
if( *pxMutex != NULL )
{
xReturn = ERR_OK;
SYS_STATS_INC_USED( mutex );
}
else
{
SYS_STATS_INC( mutex.err );
}
if( *pxMutex != NULL )
{
xReturn = ERR_OK;
SYS_STATS_INC_USED( mutex );
}
else
{
SYS_STATS_INC( mutex.err );
}
return xReturn;
return xReturn;
}
/** Lock a mutex
* @param mutex the mutex to lock */
void sys_mutex_lock( sys_mutex_t *pxMutex )
{
while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS );
while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS );
}
/** Unlock a mutex
* @param mutex the mutex to unlock */
void sys_mutex_unlock(sys_mutex_t *pxMutex )
{
xSemaphoreGive( *pxMutex );
xSemaphoreGive( *pxMutex );
}
@ -426,8 +434,8 @@ void sys_mutex_unlock(sys_mutex_t *pxMutex )
* @param mutex the mutex to delete */
void sys_mutex_free( sys_mutex_t *pxMutex )
{
SYS_STATS_DEC( mutex.used );
vQueueDelete( *pxMutex );
SYS_STATS_DEC( mutex.used );
vQueueDelete( *pxMutex );
}
@ -443,14 +451,14 @@ void sys_sem_signal( sys_sem_t *pxSemaphore )
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if( xInsideISR != pdFALSE )
{
xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
}
else
{
xSemaphoreGive( *pxSemaphore );
}
if( is_inside_isr() != pdFALSE )
{
xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
}
else
{
xSemaphoreGive( *pxSemaphore );
}
}
/*---------------------------------------------------------------------------*
@ -463,8 +471,8 @@ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
*---------------------------------------------------------------------------*/
void sys_sem_free( sys_sem_t *pxSemaphore )
{
SYS_STATS_DEC(sem.used);
vQueueDelete( *pxSemaphore );
SYS_STATS_DEC(sem.used);
vQueueDelete( *pxSemaphore );
}
/*---------------------------------------------------------------------------*
@ -479,7 +487,7 @@ void sys_init(void)
u32_t sys_now(void)
{
return xTaskGetTickCount();
return xTaskGetTickCount();
}
/*---------------------------------------------------------------------------*
@ -506,18 +514,18 @@ xTaskHandle xCreatedTask;
portBASE_TYPE xResult;
sys_thread_t xReturn;
xResult = xTaskCreate( pxThread, ( signed char * ) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
xResult = xTaskCreate( pxThread, ( signed char * ) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
if( xResult == pdPASS )
{
xReturn = xCreatedTask;
}
else
{
xReturn = NULL;
}
if( xResult == pdPASS )
{
xReturn = xCreatedTask;
}
else
{
xReturn = NULL;
}
return xReturn;
return xReturn;
}
/*---------------------------------------------------------------------------*
@ -541,11 +549,11 @@ sys_thread_t xReturn;
*---------------------------------------------------------------------------*/
sys_prot_t sys_arch_protect( void )
{
if( xInsideISR == pdFALSE )
{
taskENTER_CRITICAL();
}
return ( sys_prot_t ) 1;
if( is_inside_isr() == pdFALSE )
{
taskENTER_CRITICAL();
}
return ( sys_prot_t ) 1;
}
/*---------------------------------------------------------------------------*
@ -561,11 +569,11 @@ sys_prot_t sys_arch_protect( void )
*---------------------------------------------------------------------------*/
void sys_arch_unprotect( sys_prot_t xValue )
{
(void) xValue;
if( xInsideISR == pdFALSE )
{
taskEXIT_CRITICAL();
}
(void) xValue;
if( is_inside_isr() == pdFALSE )
{
taskEXIT_CRITICAL();
}
}
/*
@ -573,13 +581,12 @@ void sys_arch_unprotect( sys_prot_t xValue )
*/
void sys_assert( const char *pcMessage )
{
(void) pcMessage;
(void) pcMessage;
for (;;)
{
}
for (;;)
{
}
}
/*-------------------------------------------------------------------------*
* End of File: sys_arch.c
*-------------------------------------------------------------------------*/

105
utils/filteroutput.py Executable file
View 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()