mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-25 20:02:37 +03:00
commit
212e189e59
@ -25,20 +25,20 @@
|
||||
|
||||
#define CONT_STACKGUARD 0xfeefeffe
|
||||
|
||||
void cont_init(cont_t* cont) {
|
||||
void ICACHE_RAM_ATTR cont_init(cont_t* cont) {
|
||||
cont->stack_guard1 = CONT_STACKGUARD;
|
||||
cont->stack_guard2 = CONT_STACKGUARD;
|
||||
cont->stack_end = cont->stack + (sizeof(cont->stack) / 4);
|
||||
cont->struct_start = (unsigned*) cont;
|
||||
}
|
||||
|
||||
int cont_check(cont_t* cont) {
|
||||
int ICACHE_RAM_ATTR cont_check(cont_t* cont) {
|
||||
if(cont->stack_guard1 != CONT_STACKGUARD || cont->stack_guard2 != CONT_STACKGUARD) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cont_can_yield(cont_t* cont) {
|
||||
bool ICACHE_RAM_ATTR cont_can_yield(cont_t* cont) {
|
||||
return !ETS_INTR_WITHINISR() &&
|
||||
cont->pc_ret != 0 && cont->pc_yield == 0;
|
||||
}
|
||||
|
@ -129,8 +129,12 @@ static void do_global_ctors(void) {
|
||||
(*p)();
|
||||
}
|
||||
|
||||
extern "C" void __gdb_init() {}
|
||||
extern "C" void gdb_init(void) __attribute__ ((weak, alias("__gdb_init")));
|
||||
|
||||
void init_done() {
|
||||
system_set_os_print(1);
|
||||
gdb_init();
|
||||
do_global_ctors();
|
||||
esp_schedule();
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ extern void __real_system_restart_local();
|
||||
extern cont_t g_cont;
|
||||
|
||||
|
||||
static void uart_write_char_d(char c);
|
||||
void uart_write_char_d(char c);
|
||||
static void uart0_write_char_d(char c);
|
||||
static void uart1_write_char_d(char c);
|
||||
static void print_stack(uint32_t start, uint32_t end);
|
||||
@ -133,8 +133,8 @@ void uart_write_char_d(char c) {
|
||||
uart1_write_char_d(c);
|
||||
}
|
||||
|
||||
void uart0_write_char_d(char c) {
|
||||
while (((USS(0) >> USTXC) & 0xff) >= 0x7e) { }
|
||||
static void uart0_write_char_d(char c) {
|
||||
while (((USS(0) >> USTXC) & 0xff)) { }
|
||||
|
||||
if (c == '\n') {
|
||||
USF(0) = '\r';
|
||||
@ -142,7 +142,7 @@ void uart0_write_char_d(char c) {
|
||||
USF(0) = c;
|
||||
}
|
||||
|
||||
void uart1_write_char_d(char c) {
|
||||
static void uart1_write_char_d(char c) {
|
||||
while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { }
|
||||
|
||||
if (c == '\n') {
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
/*
|
||||
timer.c - Timer1 library for esp8266
|
||||
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
@ -29,13 +29,13 @@
|
||||
|
||||
static volatile timercallback timer1_user_cb = NULL;
|
||||
|
||||
void timer1_isr_handler(void *para){
|
||||
void ICACHE_RAM_ATTR timer1_isr_handler(void *para){
|
||||
if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable
|
||||
T1I = 0;
|
||||
if (timer1_user_cb) {
|
||||
// to make ISR compatible to Arduino AVR model where interrupts are disabled
|
||||
// we disable them before we call the client ISR
|
||||
uint32_t savedPS = xt_rsil(15); // stop other interrupts
|
||||
uint32_t savedPS = xt_rsil(15); // stop other interrupts
|
||||
timer1_user_cb();
|
||||
xt_wsr_ps(savedPS);
|
||||
}
|
||||
@ -61,7 +61,7 @@ void timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload){
|
||||
T1I = 0;
|
||||
}
|
||||
|
||||
void timer1_write(uint32_t ticks){
|
||||
void ICACHE_RAM_ATTR timer1_write(uint32_t ticks){
|
||||
T1L = ((ticks)& 0x7FFFFF);
|
||||
if ((T1C & (1 << TCIT)) == 0) TEIE |= TEIE1;//edge int enable
|
||||
}
|
||||
@ -76,7 +76,7 @@ void timer1_disable(){
|
||||
|
||||
static volatile timercallback timer0_user_cb = NULL;
|
||||
|
||||
void timer0_isr_handler(void* para){
|
||||
void ICACHE_RAM_ATTR timer0_isr_handler(void* para){
|
||||
if (timer0_user_cb) {
|
||||
// to make ISR compatible to Arduino AVR model where interrupts are disabled
|
||||
// we disable them before we call the client ISR
|
||||
@ -99,6 +99,3 @@ void timer0_detachInterrupt() {
|
||||
timer0_user_cb = NULL;
|
||||
ETS_CCOMPARE0_DISABLE();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
/*
|
||||
pwm.c - analogWrite implementation for esp8266
|
||||
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
@ -88,7 +88,7 @@ void prep_pwm_steps(){
|
||||
ETS_FRC1_INTR_ENABLE();
|
||||
}
|
||||
|
||||
void pwm_timer_isr(){
|
||||
void ICACHE_RAM_ATTR pwm_timer_isr(){
|
||||
static uint8_t current_step = 0;
|
||||
static uint8_t stepcount = 0;
|
||||
static uint16_t steps[17];
|
||||
|
@ -39,25 +39,25 @@
|
||||
#include "user_interface.h"
|
||||
#include "debug.h"
|
||||
|
||||
void* malloc(size_t size) {
|
||||
void* ICACHE_RAM_ATTR malloc(size_t size) {
|
||||
size = ((size + 3) & ~((size_t)0x3));
|
||||
return os_malloc(size);
|
||||
}
|
||||
|
||||
void free(void* ptr) {
|
||||
void ICACHE_RAM_ATTR free(void* ptr) {
|
||||
os_free(ptr);
|
||||
}
|
||||
|
||||
void* realloc(void* ptr, size_t size) {
|
||||
void* ICACHE_RAM_ATTR realloc(void* ptr, size_t size) {
|
||||
size = ((size + 3) & ~((size_t)0x3));
|
||||
return os_realloc(ptr, size);
|
||||
}
|
||||
|
||||
int puts(const char * str) {
|
||||
int ICACHE_RAM_ATTR puts(const char * str) {
|
||||
return os_printf("%s", str);
|
||||
}
|
||||
|
||||
int printf(const char* format, ...) {
|
||||
int ICACHE_RAM_ATTR printf(const char* format, ...) {
|
||||
int ret;
|
||||
va_list arglist;
|
||||
va_start(arglist, format);
|
||||
@ -66,7 +66,7 @@ int printf(const char* format, ...) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sprintf(char* buffer, const char* format, ...) {
|
||||
int ICACHE_RAM_ATTR sprintf(char* buffer, const char* format, ...) {
|
||||
int ret;
|
||||
va_list arglist;
|
||||
va_start(arglist, format);
|
||||
@ -75,7 +75,7 @@ int sprintf(char* buffer, const char* format, ...) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int snprintf(char* buffer, size_t size, const char* format, ...) {
|
||||
int ICACHE_RAM_ATTR snprintf(char* buffer, size_t size, const char* format, ...) {
|
||||
int ret;
|
||||
va_list arglist;
|
||||
va_start(arglist, format);
|
||||
@ -84,22 +84,22 @@ int snprintf(char* buffer, size_t size, const char* format, ...) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vprintf(const char * format, va_list arg) {
|
||||
int ICACHE_RAM_ATTR vprintf(const char * format, va_list arg) {
|
||||
return ets_vprintf(format, arg);
|
||||
}
|
||||
|
||||
int vsnprintf(char * buffer, size_t size, const char * format, va_list arg) {
|
||||
int ICACHE_RAM_ATTR vsnprintf(char * buffer, size_t size, const char * format, va_list arg) {
|
||||
return ets_vsnprintf(buffer, size, format, arg);
|
||||
}
|
||||
|
||||
size_t ICACHE_FLASH_ATTR strnlen(const char *s, size_t len) {
|
||||
size_t strnlen(const char *s, size_t len) {
|
||||
// there is no ets_strnlen
|
||||
const char *cp;
|
||||
for (cp = s; len != 0 && *cp != '\0'; cp++, len--);
|
||||
return (size_t)(cp - s);
|
||||
}
|
||||
|
||||
char* ICACHE_FLASH_ATTR strchr(const char * str, int character) {
|
||||
char* strchr(const char * str, int character) {
|
||||
while(1) {
|
||||
if(*str == 0x00) {
|
||||
return NULL;
|
||||
@ -111,7 +111,7 @@ char* ICACHE_FLASH_ATTR strchr(const char * str, int character) {
|
||||
}
|
||||
}
|
||||
|
||||
char * ICACHE_FLASH_ATTR strrchr(const char * str, int character) {
|
||||
char* strrchr(const char * str, int character) {
|
||||
char * ret = NULL;
|
||||
while(1) {
|
||||
if(*str == 0x00) {
|
||||
@ -124,11 +124,11 @@ char * ICACHE_FLASH_ATTR strrchr(const char * str, int character) {
|
||||
}
|
||||
}
|
||||
|
||||
char* ICACHE_FLASH_ATTR strcat(char * dest, const char * src) {
|
||||
char* strcat(char * dest, const char * src) {
|
||||
return strncat(dest, src, strlen(src));
|
||||
}
|
||||
|
||||
char* ICACHE_FLASH_ATTR strncat(char * dest, const char * src, size_t n) {
|
||||
char* strncat(char * dest, const char * src, size_t n) {
|
||||
size_t i;
|
||||
size_t offset = strlen(dest);
|
||||
for(i = 0; i < n && src[i]; i++) {
|
||||
@ -138,7 +138,7 @@ char* ICACHE_FLASH_ATTR strncat(char * dest, const char * src, size_t n) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
char* ICACHE_FLASH_ATTR strtok_r(char* s, const char* delim, char** last) {
|
||||
char* strtok_r(char* s, const char* delim, char** last) {
|
||||
const char* spanp;
|
||||
char* tok;
|
||||
char c;
|
||||
@ -193,13 +193,13 @@ cont:
|
||||
// NOTREACHED EVER
|
||||
}
|
||||
|
||||
char* ICACHE_FLASH_ATTR strtok(char* s, const char* delim) {
|
||||
char* strtok(char* s, const char* delim) {
|
||||
static char* last;
|
||||
|
||||
return (strtok_r(s, delim, &last));
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR strcasecmp(const char * str1, const char * str2) {
|
||||
int strcasecmp(const char * str1, const char * str2) {
|
||||
int d = 0;
|
||||
while(1) {
|
||||
int c1 = tolower(*str1++);
|
||||
@ -211,7 +211,7 @@ int ICACHE_FLASH_ATTR strcasecmp(const char * str1, const char * str2) {
|
||||
return d;
|
||||
}
|
||||
|
||||
char* ICACHE_FLASH_ATTR strdup(const char *str) {
|
||||
char* strdup(const char *str) {
|
||||
size_t len = strlen(str) + 1;
|
||||
char *cstr = malloc(len);
|
||||
if(cstr) {
|
||||
@ -222,7 +222,7 @@ char* ICACHE_FLASH_ATTR strdup(const char *str) {
|
||||
|
||||
// based on Source:
|
||||
// https://github.com/anakod/Sming/blob/master/Sming/system/stringconversion.cpp#L93
|
||||
double ICACHE_FLASH_ATTR strtod(const char* str, char** endptr) {
|
||||
double strtod(const char* str, char** endptr) {
|
||||
double result = 0.0;
|
||||
double factor = 1.0;
|
||||
bool decimals = false;
|
||||
@ -388,7 +388,7 @@ int isblank(int c) {
|
||||
|
||||
static int errno_var = 0;
|
||||
|
||||
int* ICACHE_FLASH_ATTR __errno(void) {
|
||||
int* __errno(void) {
|
||||
// DEBUGV("__errno is called last error: %d (not current)\n", errno_var);
|
||||
return &errno_var;
|
||||
}
|
||||
@ -440,7 +440,7 @@ time_t mktime(struct tm *timp) {
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
size_t ICACHE_FLASH_ATTR strlcpy(char* dst, const char* src, size_t size) {
|
||||
size_t strlcpy(char* dst, const char* src, size_t size) {
|
||||
const char *s = src;
|
||||
size_t n = size;
|
||||
|
||||
@ -486,8 +486,8 @@ size_t ICACHE_FLASH_ATTR strlcpy(char* dst, const char* src, size_t size) {
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
@ -505,134 +505,134 @@ size_t ICACHE_FLASH_ATTR strlcpy(char* dst, const char* src, size_t size) {
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
long ICACHE_FLASH_ATTR strtol(const char *nptr, char **endptr, int base) {
|
||||
const unsigned char *s = (const unsigned char *)nptr;
|
||||
unsigned long acc;
|
||||
int c;
|
||||
unsigned long cutoff;
|
||||
int neg = 0, any, cutlim;
|
||||
long strtol(const char *nptr, char **endptr, int base) {
|
||||
const unsigned char *s = (const unsigned char *)nptr;
|
||||
unsigned long acc;
|
||||
int c;
|
||||
unsigned long cutoff;
|
||||
int neg = 0, any, cutlim;
|
||||
|
||||
/*
|
||||
* Skip white space and pick up leading +/- sign if any.
|
||||
* If base is 0, allow 0x for hex and 0 for octal, else
|
||||
* assume decimal; if base is already 16, allow 0x.
|
||||
*/
|
||||
do {
|
||||
c = *s++;
|
||||
} while (isspace(c));
|
||||
if (c == '-') {
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
} else if (c == '+')
|
||||
c = *s++;
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X')) {
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
/*
|
||||
* Skip white space and pick up leading +/- sign if any.
|
||||
* If base is 0, allow 0x for hex and 0 for octal, else
|
||||
* assume decimal; if base is already 16, allow 0x.
|
||||
*/
|
||||
do {
|
||||
c = *s++;
|
||||
} while (isspace(c));
|
||||
if (c == '-') {
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
} else if (c == '+')
|
||||
c = *s++;
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X')) {
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
|
||||
/*
|
||||
* Compute the cutoff value between legal numbers and illegal
|
||||
* numbers. That is the largest legal value, divided by the
|
||||
* base. An input number that is greater than this value, if
|
||||
* followed by a legal input character, is too big. One that
|
||||
* is equal to this value may be valid or not; the limit
|
||||
* between valid and invalid numbers is then based on the last
|
||||
* digit. For instance, if the range for longs is
|
||||
* [-2147483648..2147483647] and the input base is 10,
|
||||
* cutoff will be set to 214748364 and cutlim to either
|
||||
* 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
|
||||
* a value > 214748364, or equal but the next digit is > 7 (or 8),
|
||||
* the number is too big, and we will return a range error.
|
||||
*
|
||||
* Set any if any `digits' consumed; make it negative to indicate
|
||||
* overflow.
|
||||
*/
|
||||
cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
|
||||
cutlim = cutoff % (unsigned long)base;
|
||||
cutoff /= (unsigned long)base;
|
||||
for (acc = 0, any = 0;; c = *s++) {
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
/*
|
||||
* Compute the cutoff value between legal numbers and illegal
|
||||
* numbers. That is the largest legal value, divided by the
|
||||
* base. An input number that is greater than this value, if
|
||||
* followed by a legal input character, is too big. One that
|
||||
* is equal to this value may be valid or not; the limit
|
||||
* between valid and invalid numbers is then based on the last
|
||||
* digit. For instance, if the range for longs is
|
||||
* [-2147483648..2147483647] and the input base is 10,
|
||||
* cutoff will be set to 214748364 and cutlim to either
|
||||
* 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
|
||||
* a value > 214748364, or equal but the next digit is > 7 (or 8),
|
||||
* the number is too big, and we will return a range error.
|
||||
*
|
||||
* Set any if any `digits' consumed; make it negative to indicate
|
||||
* overflow.
|
||||
*/
|
||||
cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
|
||||
cutlim = cutoff % (unsigned long)base;
|
||||
cutoff /= (unsigned long)base;
|
||||
for (acc = 0, any = 0;; c = *s++) {
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
|
||||
any = -1;
|
||||
else {
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
if (any < 0) {
|
||||
acc = neg ? LONG_MIN : LONG_MAX;
|
||||
errno = ERANGE;
|
||||
} else if (neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = (char *) (any ? (char *)s - 1 : nptr);
|
||||
return (acc);
|
||||
any = -1;
|
||||
else {
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
if (any < 0) {
|
||||
acc = neg ? LONG_MIN : LONG_MAX;
|
||||
errno = ERANGE;
|
||||
} else if (neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = (char *) (any ? (char *)s - 1 : nptr);
|
||||
return (acc);
|
||||
}
|
||||
|
||||
unsigned long ICACHE_FLASH_ATTR strtoul(const char *nptr, char **endptr, int base)
|
||||
unsigned long strtoul(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
const unsigned char *s = (const unsigned char *)nptr;
|
||||
unsigned long acc;
|
||||
int c;
|
||||
unsigned long cutoff;
|
||||
int neg = 0, any, cutlim;
|
||||
const unsigned char *s = (const unsigned char *)nptr;
|
||||
unsigned long acc;
|
||||
int c;
|
||||
unsigned long cutoff;
|
||||
int neg = 0, any, cutlim;
|
||||
|
||||
/*
|
||||
* See strtol for comments as to the logic used.
|
||||
*/
|
||||
do {
|
||||
c = *s++;
|
||||
} while (isspace(c));
|
||||
if (c == '-') {
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
} else if (c == '+')
|
||||
c = *s++;
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X')) {
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
|
||||
cutlim = (unsigned long)ULONG_MAX % (unsigned long)base;
|
||||
for (acc = 0, any = 0;; c = *s++) {
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
/*
|
||||
* See strtol for comments as to the logic used.
|
||||
*/
|
||||
do {
|
||||
c = *s++;
|
||||
} while (isspace(c));
|
||||
if (c == '-') {
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
} else if (c == '+')
|
||||
c = *s++;
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X')) {
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
|
||||
cutlim = (unsigned long)ULONG_MAX % (unsigned long)base;
|
||||
for (acc = 0, any = 0;; c = *s++) {
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
|
||||
any = -1;
|
||||
else {
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
if (any < 0) {
|
||||
acc = ULONG_MAX;
|
||||
errno = ERANGE;
|
||||
} else if (neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = (char *) (any ? (char *)s - 1 : nptr);
|
||||
return (acc);
|
||||
any = -1;
|
||||
else {
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
if (any < 0) {
|
||||
acc = ULONG_MAX;
|
||||
errno = ERANGE;
|
||||
} else if (neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = (char *) (any ? (char *)s - 1 : nptr);
|
||||
return (acc);
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ void handleWifi() {
|
||||
Serial.println("scan done");
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
server.sendContent(String() + "\r\n<tr><td>SSID " + String(WiFi.SSID(i)) + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":" *") + " (" + WiFi.RSSI(i) + ")</td></tr>");
|
||||
server.sendContent(String() + "\r\n<tr><td>SSID " + WiFi.SSID(i) + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":" *") + " (" + WiFi.RSSI(i) + ")</td></tr>");
|
||||
}
|
||||
} else {
|
||||
server.sendContent(String() + "<tr><td>No WLAN found</td></tr>");
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
To upload through terminal you can use: curl -F "image=@firmware.bin" esp8266-webupdate.local/update
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ESP8266HTTPUpdateServer.h>
|
||||
|
||||
const char* host = "esp8266-webupdate";
|
||||
const char* ssid = "........";
|
||||
const char* password = "........";
|
||||
|
||||
ESP8266WebServer httpServer(80);
|
||||
ESP8266HTTPUpdateServer httpUpdater;
|
||||
|
||||
void setup(void){
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Booting Sketch...");
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while(WiFi.waitForConnectResult() != WL_CONNECTED){
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("WiFi failed, retrying.");
|
||||
}
|
||||
|
||||
MDNS.begin(host);
|
||||
|
||||
httpUpdater.setup(&httpServer);
|
||||
httpServer.begin();
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
|
||||
}
|
||||
|
||||
void loop(void){
|
||||
httpServer.handleClient();
|
||||
delay(1);
|
||||
}
|
20
libraries/ESP8266HTTPUpdateServer/keywords.txt
Normal file
20
libraries/ESP8266HTTPUpdateServer/keywords.txt
Normal file
@ -0,0 +1,20 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For HTTPUpdateServer
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
ESP8266HTTPUpdateServer KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
setup KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
9
libraries/ESP8266HTTPUpdateServer/library.properties
Normal file
9
libraries/ESP8266HTTPUpdateServer/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=ESP8266HTTPUpdateServer
|
||||
version=1.0
|
||||
author=Ivan Grokhotkov, Miguel Ángel Ajo
|
||||
maintainer=Ivan Grokhtkov <ivan@esp8266.com>
|
||||
sentence=Simple HTTP Update server based on the ESP8266WebServer
|
||||
paragraph=The library accepts HTTP post requests to the /update url, and updates the ESP8266 firmware.
|
||||
category=Communication
|
||||
url=
|
||||
architectures=esp8266
|
@ -0,0 +1,75 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include "ESP8266HTTPUpdateServer.h"
|
||||
|
||||
|
||||
const char* ESP8266HTTPUpdateServer::_serverIndex =
|
||||
R"(<html><body><form method='POST' action='/update' enctype='multipart/form-data'>
|
||||
<input type='file' name='update'>
|
||||
<input type='submit' value='Update'>
|
||||
</form>
|
||||
</body></html>)";
|
||||
|
||||
ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug)
|
||||
{
|
||||
_serial_output = serial_debug;
|
||||
_server = NULL;
|
||||
}
|
||||
|
||||
void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server)
|
||||
{
|
||||
_server = server;
|
||||
|
||||
// handler for the /update form page
|
||||
_server->on("/update", HTTP_GET, [&](){
|
||||
_server->sendHeader("Connection", "close");
|
||||
_server->sendHeader("Access-Control-Allow-Origin", "*");
|
||||
_server->send(200, "text/html", _serverIndex);
|
||||
});
|
||||
|
||||
// handler for the /update form POST (once file upload finishes)
|
||||
_server->on("/update", HTTP_POST, [&](){
|
||||
_server->sendHeader("Connection", "close");
|
||||
_server->sendHeader("Access-Control-Allow-Origin", "*");
|
||||
_server->send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
|
||||
ESP.restart();
|
||||
});
|
||||
|
||||
// handler for the file upload, get's the sketch bytes, and writes
|
||||
// them through the Update object.
|
||||
_server->onFileUpload([&](){
|
||||
if(_server->uri() != "/update") return;
|
||||
HTTPUpload& upload = _server->upload();
|
||||
if(upload.status == UPLOAD_FILE_START){
|
||||
if (_serial_output)
|
||||
Serial.setDebugOutput(true);
|
||||
WiFiUDP::stopAll();
|
||||
if (_serial_output)
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
if(!Update.begin(maxSketchSpace)){//start with max available size
|
||||
if (_serial_output) Update.printError(Serial);
|
||||
}
|
||||
} else if(upload.status == UPLOAD_FILE_WRITE){
|
||||
if (_serial_output) Serial.printf(".");
|
||||
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
|
||||
if (_serial_output) Update.printError(Serial);
|
||||
|
||||
}
|
||||
} else if(upload.status == UPLOAD_FILE_END){
|
||||
if(Update.end(true)){ //true to set the size to the current progress
|
||||
if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||
} else {
|
||||
if (_serial_output) Update.printError(Serial);
|
||||
}
|
||||
if (_serial_output) Serial.setDebugOutput(false);
|
||||
} else if(upload.status == UPLOAD_FILE_ABORTED){
|
||||
Update.end();
|
||||
if (_serial_output) Serial.println("Update was aborted");
|
||||
}
|
||||
yield();
|
||||
});
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
#ifndef __HTTP_UPDATE_SERVER_H
|
||||
#define __HTTP_UPDATE_SERVER_H
|
||||
|
||||
class ESP8266WebServer;
|
||||
|
||||
class ESP8266HTTPUpdateServer
|
||||
{
|
||||
private:
|
||||
bool _serial_output;
|
||||
ESP8266WebServer *_server;
|
||||
static const char *_serverIndex;
|
||||
public:
|
||||
ESP8266HTTPUpdateServer(bool serial_debug=false);
|
||||
void setup(ESP8266WebServer *server=NULL);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -16,6 +16,7 @@ HTTPMethod KEYWORD1
|
||||
begin KEYWORD2
|
||||
handleClient KEYWORD2
|
||||
on KEYWORD2
|
||||
addHandler KEYWORD2
|
||||
uri KEYWORD2
|
||||
method KEYWORD2
|
||||
client KEYWORD2
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "WiFiClient.h"
|
||||
#include "ESP8266WebServer.h"
|
||||
#include "FS.h"
|
||||
#include "detail/RequestHandler.h"
|
||||
#include "detail/RequestHandlersImpl.h"
|
||||
// #define DEBUG
|
||||
#define DEBUG_OUTPUT Serial
|
||||
|
||||
@ -45,7 +45,7 @@ ESP8266WebServer::~ESP8266WebServer() {
|
||||
return;
|
||||
RequestHandler* handler = _firstHandler;
|
||||
while (handler) {
|
||||
RequestHandler* next = handler->next;
|
||||
RequestHandler* next = handler->next();
|
||||
delete handler;
|
||||
handler = next;
|
||||
}
|
||||
@ -63,13 +63,17 @@ void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::
|
||||
_addRequestHandler(new FunctionRequestHandler(fn, uri, method));
|
||||
}
|
||||
|
||||
void ESP8266WebServer::addHandler(RequestHandler* handler) {
|
||||
_addRequestHandler(handler);
|
||||
}
|
||||
|
||||
void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) {
|
||||
if (!_lastHandler) {
|
||||
_firstHandler = handler;
|
||||
_lastHandler = handler;
|
||||
}
|
||||
else {
|
||||
_lastHandler->next = handler;
|
||||
_lastHandler->next(handler);
|
||||
_lastHandler = handler;
|
||||
}
|
||||
}
|
||||
@ -293,7 +297,7 @@ void ESP8266WebServer::onNotFound(THandlerFunction fn) {
|
||||
|
||||
void ESP8266WebServer::_handleRequest() {
|
||||
RequestHandler* handler;
|
||||
for (handler = _firstHandler; handler; handler = handler->next) {
|
||||
for (handler = _firstHandler; handler; handler = handler->next()) {
|
||||
if (handler->handle(*this, _currentMethod, _currentUri))
|
||||
break;
|
||||
}
|
||||
|
@ -27,7 +27,8 @@
|
||||
#include <functional>
|
||||
|
||||
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS };
|
||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END };
|
||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
||||
UPLOAD_FILE_ABORTED };
|
||||
|
||||
#define HTTP_DOWNLOAD_UNIT_SIZE 1460
|
||||
#define HTTP_UPLOAD_BUFLEN 2048
|
||||
@ -37,7 +38,9 @@ enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END };
|
||||
#define CONTENT_LENGTH_UNKNOWN ((size_t) -1)
|
||||
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
|
||||
|
||||
class RequestHandler;
|
||||
class ESP8266WebServer;
|
||||
|
||||
#include "detail/RequestHandler.h"
|
||||
|
||||
namespace fs {
|
||||
class FS;
|
||||
@ -65,6 +68,7 @@ public:
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
void on(const char* uri, THandlerFunction handler);
|
||||
void on(const char* uri, HTTPMethod method, THandlerFunction fn);
|
||||
void addHandler(RequestHandler* handler);
|
||||
void serveStatic(const char* uri, fs::FS& fs, const char* path);
|
||||
void onNotFound(THandlerFunction fn); //called when handler is not assigned
|
||||
void onFileUpload(THandlerFunction fn); //handle file uploads
|
||||
@ -116,6 +120,7 @@ protected:
|
||||
void _parseArguments(String data);
|
||||
static const char* _responseCodeToString(int code);
|
||||
bool _parseForm(WiFiClient& client, String boundary, uint32_t len);
|
||||
bool _parseFormUploadAborted();
|
||||
void _uploadWriteByte(uint8_t b);
|
||||
uint8_t _uploadReadByte(WiFiClient& client);
|
||||
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
|
||||
|
@ -274,7 +274,7 @@ void ESP8266WebServer::_uploadWriteByte(uint8_t b){
|
||||
uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){
|
||||
int res = client.read();
|
||||
if(res == -1){
|
||||
while(!client.available())
|
||||
while(!client.available() && client.connected())
|
||||
yield();
|
||||
res = client.read();
|
||||
}
|
||||
@ -387,13 +387,16 @@ bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
|
||||
uint8_t argByte = _uploadReadByte(client);
|
||||
readfile:
|
||||
while(argByte != 0x0D){
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
_uploadWriteByte(argByte);
|
||||
argByte = _uploadReadByte(client);
|
||||
}
|
||||
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if (argByte == 0x0A){
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-'){
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
@ -401,6 +404,7 @@ readfile:
|
||||
goto readfile;
|
||||
} else {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (!client.connected()) return _parseFormUploadAborted();
|
||||
if ((char)argByte != '-'){
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
@ -481,3 +485,9 @@ readfile:
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ESP8266WebServer::_parseFormUploadAborted(){
|
||||
_currentUpload.status = UPLOAD_FILE_ABORTED;
|
||||
if (_fileUploadHandler) _fileUploadHandler();
|
||||
return false;
|
||||
}
|
||||
|
@ -3,109 +3,13 @@
|
||||
|
||||
class RequestHandler {
|
||||
public:
|
||||
RequestHandler(const char* uri, HTTPMethod method)
|
||||
: _uri(uri)
|
||||
, _method(method)
|
||||
, next(NULL)
|
||||
{
|
||||
}
|
||||
virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) { return false; }
|
||||
|
||||
virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) = 0;
|
||||
RequestHandler* next() { return _next; }
|
||||
void next(RequestHandler* r) { _next = r; }
|
||||
|
||||
RequestHandler* next;
|
||||
|
||||
protected:
|
||||
String _uri;
|
||||
HTTPMethod _method;
|
||||
};
|
||||
|
||||
|
||||
class FunctionRequestHandler : public RequestHandler {
|
||||
typedef RequestHandler base;
|
||||
|
||||
public:
|
||||
FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method)
|
||||
: _fn(fn)
|
||||
, base(uri, method)
|
||||
{
|
||||
}
|
||||
|
||||
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
|
||||
if (_method != HTTP_ANY && _method != requestMethod)
|
||||
return false;
|
||||
|
||||
if (requestUri != _uri)
|
||||
return false;
|
||||
|
||||
_fn();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
ESP8266WebServer::THandlerFunction _fn;
|
||||
};
|
||||
|
||||
class StaticRequestHandler : public RequestHandler {
|
||||
typedef RequestHandler base;
|
||||
|
||||
public:
|
||||
StaticRequestHandler(FS& fs, const char* path, const char* uri)
|
||||
: _fs(fs)
|
||||
, base(uri, HTTP_GET)
|
||||
, _path(path)
|
||||
{
|
||||
_isFile = fs.exists(path);
|
||||
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d\r\n", path, uri, _isFile);
|
||||
_baseUriLength = _uri.length();
|
||||
}
|
||||
|
||||
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
|
||||
if (requestMethod != _method)
|
||||
return false;
|
||||
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
|
||||
if (!requestUri.startsWith(_uri))
|
||||
return false;
|
||||
|
||||
String path(_path);
|
||||
if (!_isFile) {
|
||||
// Base URI doesn't point to a file. Append whatever follows this
|
||||
// URI in request to get the file path.
|
||||
path += requestUri.substring(_baseUriLength);
|
||||
}
|
||||
else if (requestUri != _uri) {
|
||||
// Base URI points to a file but request doesn't match this URI exactly
|
||||
return false;
|
||||
}
|
||||
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
|
||||
File f = _fs.open(path, "r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
server.streamFile(f, getContentType(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
static String getContentType(const String& path) {
|
||||
if (path.endsWith(".html")) return "text/html";
|
||||
else if (path.endsWith(".htm")) return "text/html";
|
||||
else if (path.endsWith(".css")) return "text/css";
|
||||
else if (path.endsWith(".txt")) return "text/plain";
|
||||
else if (path.endsWith(".js")) return "application/javascript";
|
||||
else if (path.endsWith(".png")) return "image/png";
|
||||
else if (path.endsWith(".gif")) return "image/gif";
|
||||
else if (path.endsWith(".jpg")) return "image/jpeg";
|
||||
else if (path.endsWith(".ico")) return "image/x-icon";
|
||||
else if (path.endsWith(".xml")) return "text/xml";
|
||||
else if (path.endsWith(".pdf")) return "application/pdf";
|
||||
else if (path.endsWith(".zip")) return "application/zip";
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
protected:
|
||||
FS _fs;
|
||||
String _path;
|
||||
bool _isFile;
|
||||
size_t _baseUriLength;
|
||||
private:
|
||||
RequestHandler* _next = nullptr;
|
||||
};
|
||||
|
||||
#endif //REQUESTHANDLER_H
|
||||
|
95
libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h
Normal file
95
libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h
Normal file
@ -0,0 +1,95 @@
|
||||
#ifndef REQUESTHANDLERSIMPL_H
|
||||
#define REQUESTHANDLERSIMPL_H
|
||||
|
||||
#include "RequestHandler.h"
|
||||
|
||||
class FunctionRequestHandler : public RequestHandler {
|
||||
public:
|
||||
FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method)
|
||||
: _fn(fn)
|
||||
, _uri(uri)
|
||||
, _method(method)
|
||||
{
|
||||
}
|
||||
|
||||
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
|
||||
if (_method != HTTP_ANY && _method != requestMethod)
|
||||
return false;
|
||||
|
||||
if (requestUri != _uri)
|
||||
return false;
|
||||
|
||||
_fn();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
String _uri;
|
||||
HTTPMethod _method;
|
||||
ESP8266WebServer::THandlerFunction _fn;
|
||||
};
|
||||
|
||||
class StaticRequestHandler : public RequestHandler {
|
||||
public:
|
||||
StaticRequestHandler(FS& fs, const char* path, const char* uri)
|
||||
: _fs(fs)
|
||||
, _uri(uri)
|
||||
, _path(path)
|
||||
{
|
||||
_isFile = fs.exists(path);
|
||||
DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d\r\n", path, uri, _isFile);
|
||||
_baseUriLength = _uri.length();
|
||||
}
|
||||
|
||||
bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override {
|
||||
if (requestMethod != HTTP_GET)
|
||||
return false;
|
||||
DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
|
||||
if (!requestUri.startsWith(_uri))
|
||||
return false;
|
||||
|
||||
String path(_path);
|
||||
if (!_isFile) {
|
||||
// Base URI doesn't point to a file. Append whatever follows this
|
||||
// URI in request to get the file path.
|
||||
path += requestUri.substring(_baseUriLength);
|
||||
}
|
||||
else if (requestUri != _uri) {
|
||||
// Base URI points to a file but request doesn't match this URI exactly
|
||||
return false;
|
||||
}
|
||||
DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
|
||||
File f = _fs.open(path, "r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
server.streamFile(f, getContentType(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
static String getContentType(const String& path) {
|
||||
if (path.endsWith(".html")) return "text/html";
|
||||
else if (path.endsWith(".htm")) return "text/html";
|
||||
else if (path.endsWith(".css")) return "text/css";
|
||||
else if (path.endsWith(".txt")) return "text/plain";
|
||||
else if (path.endsWith(".js")) return "application/javascript";
|
||||
else if (path.endsWith(".png")) return "image/png";
|
||||
else if (path.endsWith(".gif")) return "image/gif";
|
||||
else if (path.endsWith(".jpg")) return "image/jpeg";
|
||||
else if (path.endsWith(".ico")) return "image/x-icon";
|
||||
else if (path.endsWith(".xml")) return "text/xml";
|
||||
else if (path.endsWith(".pdf")) return "application/pdf";
|
||||
else if (path.endsWith(".zip")) return "application/zip";
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
protected:
|
||||
FS _fs;
|
||||
String _uri;
|
||||
String _path;
|
||||
bool _isFile;
|
||||
size_t _baseUriLength;
|
||||
};
|
||||
|
||||
|
||||
#endif //REQUESTHANDLERSIMPL_H
|
@ -536,13 +536,13 @@ void * ESP8266WiFiClass::_getScanInfoByIndex(int i)
|
||||
return reinterpret_cast<bss_info*>(ESP8266WiFiClass::_scanResult) + i;
|
||||
}
|
||||
|
||||
const char* ESP8266WiFiClass::SSID(uint8_t i)
|
||||
String ESP8266WiFiClass::SSID(uint8_t i)
|
||||
{
|
||||
struct bss_info* it = reinterpret_cast<struct bss_info*>(_getScanInfoByIndex(i));
|
||||
if (!it)
|
||||
return 0;
|
||||
return "";
|
||||
|
||||
return reinterpret_cast<const char*>(it->ssid);
|
||||
return String(reinterpret_cast<const char*>(it->ssid));
|
||||
}
|
||||
|
||||
uint8_t * ESP8266WiFiClass::BSSID(uint8_t i)
|
||||
|
@ -239,7 +239,7 @@ public:
|
||||
*
|
||||
* return: ssid string of the specified item on the networks scanned list
|
||||
*/
|
||||
const char* SSID(uint8_t networkItem);
|
||||
String SSID(uint8_t networkItem);
|
||||
|
||||
/*
|
||||
* Return the encryption type of the networks discovered during the scanNetworks
|
||||
|
@ -51,7 +51,7 @@ public:
|
||||
protected:
|
||||
int _connectSSL();
|
||||
|
||||
SSLContext* _ssl;
|
||||
SSLContext* _ssl = nullptr;
|
||||
};
|
||||
|
||||
#endif //wificlientsecure_h
|
||||
|
49
libraries/GDBStub/README.md
Normal file
49
libraries/GDBStub/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
## Using GDB stub
|
||||
|
||||
- Add `#include <GDBStub.h>` to the sketch
|
||||
- Upload the sketch
|
||||
- Redirect serial port to TCP port:
|
||||
```
|
||||
tcp_serial_redirect.py -p /dev/tty.SLAB_USBtoUART -b 115200 --spy -P 9980 --rts=0 --dtr=0
|
||||
```
|
||||
Change port and baud rate as necessary. This command requires python and pyserial.
|
||||
- Observe serial output:
|
||||
```
|
||||
nc localhost 9980
|
||||
```
|
||||
- When crash happens, `Trap %d: pc=%p va=%p` line will appear in serial output.
|
||||
- Close nc and start gdb:
|
||||
```
|
||||
xtensa-lx106-elf-gdb /path/to/Sketch.cpp.elf -ex "target remote :9980"
|
||||
```
|
||||
- Use gdb to inspect program state at the point of an exception.
|
||||
|
||||
## Tips and tricks
|
||||
|
||||
- To halt the target when software WDT fires, add
|
||||
```
|
||||
((int*)0) = 0;
|
||||
```
|
||||
at the top of `__wrap_system_restart_local` in core_esp8266_postmortem.c.
|
||||
|
||||
## License
|
||||
|
||||
GDB Server stub by Marko Mikulicic was taken from Cesanta's smart.js
|
||||
|
||||
https://github.com/cesanta/smart.js
|
||||
|
||||
Copyright (c) 2013-2014 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
This software is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see <http://www.gnu.org/licenses>.
|
||||
|
||||
You are free to use this software under the terms of the GNU General
|
||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
Alternatively, you can license this software under a commercial
|
||||
license, as set out in <https://www.cesanta.com/license>.
|
9
libraries/GDBStub/library.properties
Normal file
9
libraries/GDBStub/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=GDBStub
|
||||
version=0.1
|
||||
author=Marko Mikulicic (Cesanta)
|
||||
maintainer=Ivan Grokhotkov <ivan@esp8266.com>
|
||||
sentence=GDB server stub from Cesanta's Smart.js
|
||||
paragraph=GDB server stub helps debug crashes when JTAG isn't an option.
|
||||
category=Uncategorized
|
||||
url=https://github.com/cesanta/smart.js
|
||||
architectures=esp8266
|
355
libraries/GDBStub/src/GDBStub.c
Normal file
355
libraries/GDBStub/src/GDBStub.c
Normal file
@ -0,0 +1,355 @@
|
||||
/* GDB Server stub by Marko Mikulicic (Cesanta)
|
||||
|
||||
Copyright (c) 2013-2014 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
This software is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see <http://www.gnu.org/licenses>.
|
||||
|
||||
You are free to use this software under the terms of the GNU General
|
||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
Alternatively, you can license this software under a commercial
|
||||
license, as set out in <https://www.cesanta.com/license>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ets_sys.h"
|
||||
#include "user_interface.h"
|
||||
#include "esp8266_peri.h"
|
||||
|
||||
#include "xtensa/corebits.h"
|
||||
#include "xtensa/specreg.h"
|
||||
|
||||
#define __stringify_1(x...) #x
|
||||
#define __stringify(x...) __stringify_1(x)
|
||||
#define RSR(sr) \
|
||||
({ \
|
||||
uint32_t r; \
|
||||
asm volatile("rsr %0,"__stringify(sr) : "=a"(r)); \
|
||||
r; \
|
||||
})
|
||||
|
||||
|
||||
/*
|
||||
* the saved registers begin at a fixed position in the xtos
|
||||
* low-level exception handler. I don't know if 0x100 it's just an
|
||||
* artifact of the actual xtos build ESP8266EX is using (although this nice
|
||||
* round number looks deliberate). The exception handler is burned on rom
|
||||
* so it should work on future SDK updates, but not necessarily on future
|
||||
* revisions of the chip.
|
||||
*/
|
||||
#define V7_GDB_SP_OFFSET 0x100
|
||||
|
||||
/*
|
||||
* Addresses in this range are guaranteed to be readable without faulting.
|
||||
* Contains ranges that are unmapped but innocuous.
|
||||
*
|
||||
* Putative ESP8266 memory map at:
|
||||
* https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map
|
||||
*/
|
||||
#define ESP_LOWER_VALID_ADDRESS 0x20000000
|
||||
#define ESP_UPPER_VALID_ADDRESS 0x60000000
|
||||
|
||||
/*
|
||||
* Constructed by xtos.
|
||||
*
|
||||
* There is a UserFrame structure in
|
||||
* ./esp_iot_rtos_sdk/extra_include/xtensa/xtruntime-frames.h
|
||||
*/
|
||||
struct xtos_saved_regs {
|
||||
uint32_t pc; /* instruction causing the trap */
|
||||
uint32_t ps;
|
||||
uint32_t sar;
|
||||
uint32_t vpri; /* current xtos virtual priority */
|
||||
uint32_t a0; /* when __XTENSA_CALL0_ABI__ is true */
|
||||
uint32_t a[16]; /* a2 - a15 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Register file in the format lx106 gdb port expects it.
|
||||
*
|
||||
* Inspired by gdb/regformats/reg-xtensa.dat from
|
||||
* https://github.com/jcmvbkbc/crosstool-NG/blob/lx106-g%2B%2B/overlays/xtensa_lx106.tar
|
||||
*/
|
||||
struct regfile {
|
||||
uint32_t a[16];
|
||||
uint32_t pc;
|
||||
uint32_t sar;
|
||||
uint32_t litbase;
|
||||
uint32_t sr176;
|
||||
uint32_t sr208;
|
||||
uint32_t ps;
|
||||
};
|
||||
|
||||
#define printf ets_printf
|
||||
extern void uart_write_char_d(char c);
|
||||
|
||||
/* TODO(mkm): not sure if gdb guarantees lowercase hex digits */
|
||||
#define fromhex(c) \
|
||||
(((c) &0x40) ? ((c) &0x20 ? (c) - 'a' + 10 : (c) - 'A' + 10) : (c) - '0')
|
||||
#define hexdigit(n) (((n) < 10) ? '0' + (n) : 'a' + ((n) -10))
|
||||
|
||||
static struct regfile regs = {0};
|
||||
static uint8_t gdb_send_checksum;
|
||||
|
||||
int gdb_read_uart() {
|
||||
static char buf[512];
|
||||
static char pos = 0;
|
||||
|
||||
if (pos == 0) {
|
||||
size_t rx_count;
|
||||
while ((rx_count = (USS(0) >> USRXC) & 0xff)>0 && pos < sizeof(buf)) {
|
||||
buf[pos++] = U0F;
|
||||
}
|
||||
}
|
||||
if (pos == 0) {
|
||||
return -1;
|
||||
}
|
||||
return buf[--pos];
|
||||
}
|
||||
|
||||
|
||||
uint8_t read_unaligned_byte(uint8_t *addr) {
|
||||
uint32_t *base = (uint32_t *) ((uintptr_t) addr & ~0x3);
|
||||
uint32_t word;
|
||||
|
||||
word = *base;
|
||||
return (uint8_t)(word >> 8 * ((uintptr_t) addr & 0x3));
|
||||
}
|
||||
|
||||
void gdb_nack() {
|
||||
printf("-");
|
||||
}
|
||||
|
||||
void gdb_ack() {
|
||||
printf("+");
|
||||
}
|
||||
|
||||
void gdb_begin_packet() {
|
||||
printf("$");
|
||||
gdb_send_checksum = 0;
|
||||
}
|
||||
|
||||
void gdb_end_packet() {
|
||||
printf("#%c%c", hexdigit(gdb_send_checksum >> 4),
|
||||
hexdigit(gdb_send_checksum & 0xF));
|
||||
}
|
||||
|
||||
void gdb_putchar(char ch) {
|
||||
gdb_send_checksum += (uint8_t) ch;
|
||||
printf("%c", ch);
|
||||
}
|
||||
|
||||
/* output a string while computing the checksum */
|
||||
void gdb_putstr(char *str) {
|
||||
while (*str) gdb_putchar(*str++);
|
||||
}
|
||||
|
||||
void gdb_putbyte(uint8_t val) {
|
||||
gdb_putchar(hexdigit(val >> 4));
|
||||
gdb_putchar(hexdigit(val & 0xF));
|
||||
}
|
||||
|
||||
/* 32-bit integer in native byte order */
|
||||
void gdb_putint(uint32_t val) {
|
||||
int i;
|
||||
uint8_t *v = (uint8_t *) &val;
|
||||
for (i = 0; i < 4; i++) {
|
||||
gdb_putbyte(v[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* send a gdb packet with checksum */
|
||||
void gdb_send_packet(char *str) {
|
||||
gdb_begin_packet();
|
||||
gdb_putstr(str);
|
||||
gdb_end_packet();
|
||||
}
|
||||
|
||||
uint8_t gdb_read_unaligned(uint8_t *addr) {
|
||||
if (addr < (uint8_t *) ESP_LOWER_VALID_ADDRESS ||
|
||||
addr >= (uint8_t *) ESP_UPPER_VALID_ADDRESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return read_unaligned_byte(addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles the GDB server protocol.
|
||||
* We currently support only the simple command set.
|
||||
*
|
||||
* Data is exchanged in packets like `$Cxxxxx#cc`
|
||||
* where `C` is a single letter command name, `xxxx` is some data payload,
|
||||
* and `cc` is a two digit hex checksum of the packet body.
|
||||
* Replies follow the same structure except that they lack the command symbol.
|
||||
*
|
||||
* For a more complete description of the protocol, see
|
||||
* https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html
|
||||
*/
|
||||
void gdb_handle_char(int ch) {
|
||||
static enum {
|
||||
GDB_JUNK,
|
||||
GDB_DATA,
|
||||
GDB_CHECKSUM,
|
||||
GDB_CHECKSUM2
|
||||
} state = GDB_JUNK;
|
||||
static char data[128];
|
||||
static int pos = 0;
|
||||
static uint8_t checksum;
|
||||
|
||||
switch (state) {
|
||||
case GDB_JUNK:
|
||||
if (ch == '$') {
|
||||
checksum = 0;
|
||||
state = GDB_DATA;
|
||||
}
|
||||
break;
|
||||
case GDB_DATA:
|
||||
if (ch == '#') {
|
||||
state = GDB_CHECKSUM;
|
||||
break;
|
||||
}
|
||||
/* ignore too long commands, by acking and sending empty response */
|
||||
if (pos > sizeof(data)) {
|
||||
state = GDB_JUNK;
|
||||
gdb_ack();
|
||||
gdb_send_packet("");
|
||||
break;
|
||||
}
|
||||
checksum += (uint8_t) ch;
|
||||
data[pos++] = ch;
|
||||
break;
|
||||
case GDB_CHECKSUM:
|
||||
if (fromhex(ch) != (checksum >> 4)) {
|
||||
gdb_nack();
|
||||
state = GDB_JUNK;
|
||||
} else {
|
||||
state = GDB_CHECKSUM2;
|
||||
}
|
||||
break;
|
||||
case GDB_CHECKSUM2:
|
||||
state = GDB_JUNK;
|
||||
if (fromhex(ch) != (checksum & 0xF)) {
|
||||
gdb_nack();
|
||||
pos = 0;
|
||||
break;
|
||||
}
|
||||
gdb_ack();
|
||||
|
||||
/* process commands */
|
||||
switch (data[0]) {
|
||||
case '?':
|
||||
/* stop status */
|
||||
gdb_send_packet("S09"); /* TRAP */
|
||||
break;
|
||||
case 'm': {
|
||||
/* read memory */
|
||||
int i;
|
||||
uint32_t addr = 0;
|
||||
uint32_t num = 0;
|
||||
for (i = 1; i < pos && data[i] != ','; i++) {
|
||||
addr <<= 4;
|
||||
addr |= fromhex(data[i]);
|
||||
}
|
||||
for (i++; i < pos; i++) {
|
||||
num <<= 4;
|
||||
num |= fromhex(data[i]); /* should be decimal */
|
||||
}
|
||||
gdb_begin_packet();
|
||||
for (i = 0; i < num; i++) {
|
||||
gdb_putbyte(gdb_read_unaligned(((uint8_t *) addr) + i));
|
||||
}
|
||||
gdb_end_packet();
|
||||
break;
|
||||
}
|
||||
case 'g': {
|
||||
/* dump registers */
|
||||
int i;
|
||||
gdb_begin_packet();
|
||||
for (i = 0; i < sizeof(regs); i++) {
|
||||
gdb_putbyte(((uint8_t *) ®s)[i]);
|
||||
}
|
||||
gdb_end_packet();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gdb_send_packet("");
|
||||
break;
|
||||
}
|
||||
pos = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* The user should detach and let gdb do the talkin' */
|
||||
void gdb_server() {
|
||||
printf("waiting for gdb\n");
|
||||
/*
|
||||
* polling since we cannot wait for interrupts inside
|
||||
* an interrupt handler of unknown level.
|
||||
*
|
||||
* Interrupts disabled so that the user (or v7 prompt)
|
||||
* uart interrupt handler doesn't interfere.
|
||||
*/
|
||||
xthal_set_intenable(0);
|
||||
for (;;) {
|
||||
int ch = gdb_read_uart();
|
||||
if (ch != -1) gdb_handle_char(ch);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* xtos low level exception handler (in rom)
|
||||
* populates an xtos_regs structure with (most) registers
|
||||
* present at the time of the exception and passes it to the
|
||||
* high-level handler.
|
||||
*
|
||||
* Note that the a1 (sp) register is clobbered (bug? necessity?),
|
||||
* however the original stack pointer can be inferred from the address
|
||||
* of the saved registers area, since the exception handler uses the same
|
||||
* user stack. This might be different in other execution modes on the
|
||||
* quite variegated xtensa platform family, but that's how it works on ESP8266.
|
||||
*/
|
||||
void ICACHE_RAM_ATTR gdb_exception_handler(struct xtos_saved_regs *frame) {
|
||||
ESP8266_REG(0x900) &= 0x7e; // Disable WDT
|
||||
int i;
|
||||
uint32_t cause = RSR(EXCCAUSE);
|
||||
uint32_t vaddr = RSR(EXCVADDR);
|
||||
memcpy(®s.a[2], frame->a, sizeof(frame->a));
|
||||
|
||||
regs.a[0] = frame->a0;
|
||||
regs.a[1] = (uint32_t) frame + V7_GDB_SP_OFFSET;
|
||||
regs.pc = frame->pc;
|
||||
regs.sar = frame->sar;
|
||||
regs.ps = frame->ps;
|
||||
regs.litbase = RSR(LITBASE);
|
||||
|
||||
U0IE = 0;
|
||||
ets_install_putc1(&uart_write_char_d);
|
||||
|
||||
printf("\nTrap %d: pc=%p va=%p\n", cause, frame->pc, vaddr);
|
||||
gdb_server();
|
||||
|
||||
_ResetVector();
|
||||
}
|
||||
|
||||
void gdb_init() {
|
||||
char causes[] = {EXCCAUSE_ILLEGAL, EXCCAUSE_INSTR_ERROR,
|
||||
EXCCAUSE_LOAD_STORE_ERROR, EXCCAUSE_DIVIDE_BY_ZERO,
|
||||
EXCCAUSE_UNALIGNED, EXCCAUSE_INSTR_PROHIBITED,
|
||||
EXCCAUSE_LOAD_PROHIBITED, EXCCAUSE_STORE_PROHIBITED};
|
||||
int i;
|
||||
for (i = 0; i < (int) sizeof(causes); i++) {
|
||||
_xtos_set_exception_handler(causes[i], gdb_exception_handler);
|
||||
}
|
||||
}
|
6
libraries/GDBStub/src/GDBStub.h
Normal file
6
libraries/GDBStub/src/GDBStub.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef GDBSTUB_H
|
||||
#define GDBSTUB_H
|
||||
|
||||
// this header is intentionally left blank
|
||||
|
||||
#endif //GDBSTUB_H
|
@ -16,18 +16,18 @@ compiler.sdk.path={runtime.platform.path}/tools/sdk
|
||||
compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include"
|
||||
|
||||
compiler.c.cmd=xtensa-lx106-elf-gcc
|
||||
compiler.c.flags=-c -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=gnu99
|
||||
compiler.c.flags=-c -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=gnu99 -ffunction-sections -fdata-sections
|
||||
|
||||
compiler.S.cmd=xtensa-lx106-elf-gcc
|
||||
compiler.S.flags=-c -g -x assembler-with-cpp -MMD
|
||||
|
||||
compiler.c.elf.flags=-g -Os -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" "-T{build.flash_ld}" -Wl,-wrap,system_restart_local -Wl,-wrap,register_chipv6_phy
|
||||
compiler.c.elf.flags=-g -Os -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,register_chipv6_phy
|
||||
|
||||
compiler.c.elf.cmd=xtensa-lx106-elf-gcc
|
||||
compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lnet80211 -llwip -lwpa -lmain -lpp -lsmartconfig -lwps -lcrypto -laxtls
|
||||
|
||||
compiler.cpp.cmd=xtensa-lx106-elf-g++
|
||||
compiler.cpp.flags=-c -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD
|
||||
compiler.cpp.flags=-c -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections
|
||||
|
||||
compiler.as.cmd=xtensa-lx106-elf-as
|
||||
|
||||
|
@ -67,7 +67,7 @@ typedef enum {
|
||||
|
||||
#ifdef ICACHE_FLASH
|
||||
#define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text")))
|
||||
#define ICACHE_RAM_ATTR __attribute__((section(".text")))
|
||||
#define ICACHE_RAM_ATTR __attribute__((section(".iram.text")))
|
||||
#define ICACHE_RODATA_ATTR __attribute__((section(".irom.text")))
|
||||
#else
|
||||
#define ICACHE_FLASH_ATTR
|
||||
|
@ -150,12 +150,11 @@ SECTIONS
|
||||
.irom0.text : ALIGN(4)
|
||||
{
|
||||
_irom0_text_start = ABSOLUTE(.);
|
||||
*core_esp8266_*.o(.literal*, .text*)
|
||||
*spiffs*.o(.literal*, .text*)
|
||||
*.c.o(.literal*, .text*)
|
||||
*.cpp.o(.literal*, .text*)
|
||||
*libm.a:(.literal .text .literal.* .text.*)
|
||||
*libsmartconfig.a:(.literal .text .literal.* .text.*)
|
||||
*(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text)
|
||||
*(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text .irom.text.*)
|
||||
_irom0_text_end = ABSOLUTE(.);
|
||||
_flash_code_end = ABSOLUTE(.);
|
||||
} >irom0_0_seg :irom0_0_phdr
|
||||
@ -192,6 +191,8 @@ SECTIONS
|
||||
*(.init.literal)
|
||||
*(.init)
|
||||
*(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
|
||||
*.cpp.o(.iram.text)
|
||||
*.c.o(.iram.text)
|
||||
*(.fini.literal)
|
||||
*(.fini)
|
||||
*(.gnu.version)
|
||||
|
Loading…
x
Reference in New Issue
Block a user