OTA update

This commit is contained in:
Martin Ger
2018-09-11 23:28:29 +02:00
parent 196a5a9acb
commit 2f1b731a2c
17 changed files with 89 additions and 891 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 119 KiB

+20 -10
View File
@@ -53,8 +53,8 @@ LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static -L.
# linker script used for the above linkier step
#LD_SCRIPT = eagle.app.v6.ld
LD_SCRIPT = -Trom0.ld
#LD_SCRIPT = -Trom1.ld
LD_SCRIPT1 = -Trom0.ld
LD_SCRIPT2 = -Trom1.ld
# various paths from the SDK used in this project
SDK_LIBDIR = lib
@@ -63,8 +63,8 @@ SDK_INCDIR = include include/json
# we create two different files for uploading into the flash
# these are the names and options to generate them
FW_FILE_1_ADDR = 0x00000
FW_FILE_2_ADDR = 0x10000
FW_FILE_1_ADDR = 0x02000
FW_FILE_2_ADDR = 0x82000
# select which tools to use as compiler, librarian and linker
CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc
@@ -96,6 +96,7 @@ MODULE_INCDIR := $(addsuffix /include,$(INCDIR))
FW_FILE_1 := $(addprefix $(FW_BASE)/,$(FW_FILE_1_ADDR).bin)
FW_FILE_2 := $(addprefix $(FW_BASE)/,$(FW_FILE_2_ADDR).bin)
RBOOT_FILE := $(addprefix $(FW_BASE)/,0x00000.bin)
V ?= $(VERBOSE)
ifeq ("$(V)","1")
@@ -116,15 +117,21 @@ endef
.PHONY: all checkdirs flash clean
all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2)
#all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2)
all: checkdirs $(TARGET_OUT)
$(FW_BASE)/%.bin: $(TARGET_OUT) | $(FW_BASE)
$(vecho) "FW $(FW_BASE)/"
$(Q) $(ESPTOOL) elf2image --version=2 $(TARGET_OUT)
#$(FW_BASE)/%.bin: $(TARGET_OUT) | $(FW_BASE)
# $(vecho) "FW" $@
# $(Q) $(ESPTOOL) elf2image --version=2 $(TARGET_OUT) -o $@
$(TARGET_OUT): $(APP_AR)
$(vecho) "LD $@"
$(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@
$(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT1) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@
$(Q) $(ESPTOOL) elf2image --version=2 $(TARGET_OUT) -o $(FW_FILE_1)
$(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT2) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@
$(Q) $(ESPTOOL) elf2image --version=2 $(TARGET_OUT) -o $(FW_FILE_2)
cp rboot.bin $(RBOOT_FILE)
sha1sum $(FW_FILE_1) $(FW_FILE_2) $(RBOOT_FILE) > $(FW_BASE)/sha1sums
$(APP_AR): $(OBJ)
$(vecho) "AR $@"
@@ -139,7 +146,10 @@ $(FW_BASE):
$(Q) mkdir -p $@
flash: $(FW_FILE_1) $(FW_FILE_2)
sudo $(ESPTOOL) --port $(ESPPORT) --baud $(ESPTOOLBAUD) write_flash $(ESPTOOLOPTS) $(FW_FILE_1_ADDR) $(FW_FILE_1) $(FW_FILE_2_ADDR) $(FW_FILE_2)
sudo $(ESPTOOL) --port $(ESPPORT) --baud $(ESPTOOLBAUD) write_flash $(ESPTOOLOPTS) 0x00000 $(RBOOT_FILE) $(FW_FILE_1_ADDR) $(FW_FILE_1)
flash1: $(FW_FILE_2)
sudo $(ESPTOOL) --port $(ESPPORT) --baud $(ESPTOOLBAUD) write_flash $(ESPTOOLOPTS) 0x00000 $(RBOOT_FILE) $(FW_FILE_2_ADDR) $(FW_FILE_2)
clean:
$(Q) rm -rf $(FW_BASE) $(BUILD_BASE)
+19 -56
View File
@@ -325,6 +325,22 @@ You can send the ESP to sleep manually once by using the "sleep" command.
Caution: If you save a _vmin_ value higher than the max supply voltage to flash, the repeater will immediately shutdown every time after reboot. Then you have to wipe out the whole config by flashing blank.bin (or any other file) to 0x0c000.
# OTA (Over the air) update support (experimental)
Based on using the rboot lib: https://github.com/raburton/rboot and thanks to the contribution of christianchristensen.
During the build process (and in the firmware directory) there are two copies of the esp_wifi_repeater 0x02000.bin and 0x82000.bin. For an initial installation it is fine just to flash 0x00000.bin (the rboot boot loader) and 0x02000.bin (one copy of the program). The esp_wifi_repeater will work.
If you have at least 1MB of flash you can update that binary with another version OTA (Over the air). For an OTA update you can interactively load a new binary from the CLI and switch over to it. The other binary is loaded to the currently non active memory location (either 0x02000 (rom0) or 0x82000 (rom1)) and started on success. You can also interactively switch between two installed binaries. The current config will be used for both binaries (as long as its format hasn't changed).
Now you can control the OTA features with the following commands:
- set ota_host _hostname_: hostname or IP address of the OTA server (default: "none")
- set ota_port _portno_: port number of the OTA server (default: 80)
- ota update: tries to download a new binary (0x02000.bin or 0x82000.bin) via HTTP from ota_host:ota_port and start it
- ota switch: switches to the other binary (if installed)
Known Issue: currently the download has some quirks and doesn't work for all HTTP-servers.
# ENC28J60 Ethernet Support (experimental)
If you enable the HAVE_ENC28J60 option in user_config.h and recompile the project, you get support for an ENC28J60 Ethernet NIC connected via SPI.
@@ -355,71 +371,18 @@ To build this binary you download and install the esp-open-sdk (https://github.c
Then download this source tree in a separate directory and adjust the BUILD_AREA variable in the Makefile and any desired options in user/user_config.h. Changes of the default configuration can be made in user/config_flash.c. Build the esp_wifi_repeater firmware with "make". "make flash" flashes it onto an esp8266.
The source tree includes a binary version of the liblwip_open plus the required additional includes from my fork of esp-open-lwip. *No additional install action is required for that.* Only if you don't want to use the precompiled library, checkout the sources from https://github.com/martin-ger/esp-open-lwip . Use it to replace the directory "esp-open-lwip" in the esp-open-sdk tree. "make clean" in the esp_open_lwip dir and once again a "make" in the upper esp_open_sdk directory. This will compile a liblwip_open.a that contains the NAT-features. Replace liblwip_open_napt.a with that binary.
The source tree includes a binary version of the liblwip_open plus the required additional includes from my fork of esp-open-lwip and a binary of the rboot tool. *No additional install action is required for that.* Only if you don't want to use the precompiled library, checkout the sources from https://github.com/martin-ger/esp-open-lwip . Use it to replace the directory "esp-open-lwip" in the esp-open-sdk tree. "make clean" in the esp_open_lwip dir and once again a "make" in the upper esp_open_sdk directory. This will compile a liblwip_open.a that contains the NAT-features. Replace liblwip_open_napt.a with that binary. Also you might build the "rboot.bin" binary from https://github.com/raburton/rboot and replace it in the root directory of the project.
If you want to use the complete precompiled firmware binaries you can flash them with "esptool.py --port /dev/ttyUSB0 write_flash -fs 4MB -ff 80m -fm dio 0x00000 firmware/0x00000.bin 0x10000 firmware/0x10000.bin" (use -fs 1MB for an ESP-01). For the esp8285 you must use -fs 1MB and -fm dout.
If you want to use the complete precompiled firmware binaries you can flash them with "esptool.py --port /dev/ttyUSB0 write_flash -fs 4MB -ff 80m -fm dio 0x00000 firmware/0x00000.bin 0x02000 firmware/0x02000.bin" (use -fs 1MB for an ESP-01). For the esp8285 you must use -fs 1MB and -fm dout.
On Windows you can flash it using the "ESP8266 Download Tool" available at https://espressif.com/en/support/download/other-tools. Download the two files 0x00000.bin and 0x10000.bin from the firmware directory. For a generic ESP12, a NodeMCU or a Wemos D1 use the following settings (for an ESP-01 change FLASH SIZE to "8Mbit"):
On Windows you can flash it using the "ESP8266 Download Tool" available at https://espressif.com/en/support/download/other-tools. Download the two files 0x00000.bin and 0x02000.bin from the firmware directory. For a generic ESP12, a NodeMCU or a Wemos D1 use the following settings (for an ESP-01 change FLASH SIZE to "8Mbit"):
<img src="https://raw.githubusercontent.com/martin-ger/esp_wifi_repeater/master/FlashRepeaterWindows.jpg">
If "QIO" mode fails on your device, try "DIO" instead. Also have a look at the "Detected Info" to check size and mode of the flash chip. If your downloaded firmware still doesn't start properly, please check with the enclosed checksums whether the binary files are possibly corrupted.
# OTA (Over the air) update support
Based on using the rboot lib: https://github.com/raburton/rboot
User flash location `FLASH_BLOCK_NO` was updated as `0xc` -> `0x4e` conflicted with the rboot memory locations. Also this is a viable space between the ROM locations written that config can be shared.
Memory mapping updates:
Example flashing update based on new bin files: `$ esptool.py write_flash 0x00000 rboot.bin 0x02000 app-0x02000.bin 0x82000 app-0x82000.bin`
`rboot.bin` is from raburton/rboot "Building" and "Installation" instructions.
Note: "Linking user code" from rboot documentation:
`rom0.ld`
```diff
$ diff -u ../esp-open-sdk/sdk/ld/eagle.app.v6.ld rom0.ld
--- ../esp-open-sdk/sdk/ld/eagle.app.v6.ld 2018-08-19 10:32:30.932083233 -0700
+++ rom0.ld 2018-08-21 20:39:36.838230232 -0700
@@ -5,7 +5,7 @@
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000
- irom0_0_seg : org = 0x40210000, len = 0x5C000
+ irom0_0_seg : org = 0x40202010, len = 0x5C000
}
PHDRS
@@ -227,4 +227,4 @@
}
/* get ROM code address */
-INCLUDE "../ld/eagle.rom.addr.v6.ld"
+INCLUDE "eagle.rom.addr.v6.ld"
```
`rom1.ld`
```diff
- irom0_0_seg : org = 0x40210000, len = 0x5C000
+ irom0_0_seg : org = 0x40282010, len = 0x5C000
```
## OTA API
Based on https://github.com/raburton/rboot-sample
NOTE: `user/rboot-ota.h`
* `OTA_HOST` (HOST OR IP)
* `OTA_PORT`
* `OTA_ROM0`
* `OTA_ROM1`
# Known Issues
- Due to the limitations of the ESP's SoftAP implementation, there is a maximum of 8 simultaniously connected stations.
- The ESP8266 requires a good power supply as it produces current spikes of up to 170 mA during transmit (typical average consumption is around 70 mA when WiFi is on). Check the power supply first, if your ESP runs unstable and reboots from time to time. A large capacitor between Vdd and Gnd can help if you experience problems here.
- All firmware published after 17/Oct/2017 have been built with the patched version of the SDK 2.1.0 from Espressif that mitigates the KRACK (https://www.krackattacks.com/ ) attack.
# Feedback
If you have any projects or applications that use this software, I would be glad to receive any feedback - what it should do, if it performs well, or even if it cannot meet your expectations. Leave an issue here or send me an email.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -2
View File
@@ -1,2 +1,3 @@
2df4dab79508a655464cabda1489d8cc8bd4cfed 0x00000.bin
d60650db4ea160f0a740d4921d20e99f251d408f 0x10000.bin
f35be9cf5af5018d871f12ee70f9aaeeda356b02 firmware/0x02000.bin
7a3cf6d304d3c927461366ff891e43283c570eb1 firmware/0x82000.bin
9bd7d25204d71b3db5f35e0b2def8a6aaa7f765c firmware/0x00000.bin
BIN
View File
Binary file not shown.
+4
View File
@@ -143,6 +143,10 @@ uint32_t reg0, reg1, reg3;
#ifdef ACLS
acl_init(); // initializes the ACLs, written in config during save
#endif
#ifdef OTAUPDATE
os_sprintf(config->ota_host,"%s", "none");
config->ota_port = 80;
#endif
}
int ICACHE_FLASH_ATTR config_load(sysconfig_p config)
+5 -1
View File
@@ -16,7 +16,7 @@
#define FLASH_BLOCK_NO 0x4e
#define MAGIC_NUMBER 0x152435fc
#define MAGIC_NUMBER 0x13f435fc
typedef enum {AUTOMESH_OFF = 0, AUTOMESH_LEARNING, AUTOMESH_OPERATIONAL} automeshmode;
@@ -127,6 +127,10 @@ typedef struct
acl_entry acl[MAX_NO_ACLS][MAX_ACL_ENTRIES]; // ACL entries
uint8_t acl_freep[MAX_NO_ACLS]; // ACL free pointers
#endif
#ifdef OTAUPDATE
uint8_t ota_host[64];
uint16_t ota_port;
#endif
} sysconfig_t, *sysconfig_p;
int config_load(sysconfig_p config);
-795
View File
@@ -1,795 +0,0 @@
#include "c_types.h"
#include "mem.h"
#include "ets_sys.h"
#include "osapi.h"
#include "gpio.h"
#include "os_type.h"
#include "user_interface.h"
#include "espconn.h"
#include "dns_responder.h"
//#define DNS_DEBUG 1
/*
* This software is licensed under the CC0.
*
* This is a _basic_ DNS Server for educational use.
* It does not prevent invalid packets from crashing
* the server.
*
* Originating from:
* https://github.com/mwarning/SimpleDNS
*
*/
LOCAL struct espconn dns_espconn;
struct Message msg;
LOCAL uint8_t dns_mode;
/*
* Masks and constants.
*/
static const uint32_t QR_MASK = 0x8000;
static const uint32_t OPCODE_MASK = 0x7800;
static const uint32_t AA_MASK = 0x0400;
static const uint32_t TC_MASK = 0x0200;
static const uint32_t RD_MASK = 0x0100;
static const uint32_t RA_MASK = 0x8000;
static const uint32_t RCODE_MASK = 0x000F;
/* Response Type */
enum {
Ok_ResponseType = 0,
FormatError_ResponseType = 1,
ServerFailure_ResponseType = 2,
NameError_ResponseType = 3,
NotImplemented_ResponseType = 4,
Refused_ResponseType = 5
};
/* Resource Record Types */
enum {
A_Resource_RecordType = 1,
NS_Resource_RecordType = 2,
CNAME_Resource_RecordType = 5,
SOA_Resource_RecordType = 6,
PTR_Resource_RecordType = 12,
MX_Resource_RecordType = 15,
TXT_Resource_RecordType = 16,
AAAA_Resource_RecordType = 28,
SRV_Resource_RecordType = 33
};
/* Operation Code */
enum {
QUERY_OperationCode = 0, /* standard query */
IQUERY_OperationCode = 1, /* inverse query */
STATUS_OperationCode = 2, /* server status request */
NOTIFY_OperationCode = 4, /* request zone transfer */
UPDATE_OperationCode = 5 /* change resource records */
};
/* Response Code */
enum {
NoError_ResponseCode = 0,
FormatError_ResponseCode = 1,
ServerFailure_ResponseCode = 2,
NameError_ResponseCode = 3
};
/* Query Type */
enum {
IXFR_QueryType = 251,
AXFR_QueryType = 252,
MAILB_QueryType = 253,
MAILA_QueryType = 254,
STAR_QueryType = 255
};
/*
* Types.
*/
/* Question Section */
struct Question {
char *qName;
uint16_t qType;
uint16_t qClass;
struct Question* next; // for linked list
};
/* Data part of a Resource Record */
union ResourceData {
struct {
char *txt_data;
} txt_record;
struct {
uint8_t addr[4];
} a_record;
struct {
char* MName;
char* RName;
uint32_t serial;
uint32_t refresh;
uint32_t retry;
uint32_t expire;
uint32_t minimum;
} soa_record;
struct {
char *name;
} name_server_record;
struct {
char name;
} cname_record;
struct {
char *name;
} ptr_record;
struct {
uint16_t preference;
char *exchange;
} mx_record;
struct {
uint8_t addr[16];
} aaaa_record;
struct {
uint16_t priority;
uint16_t weight;
uint16_t port;
char *target;
} srv_record;
};
/* Resource Record Section */
struct ResourceRecord {
char *name;
uint16_t type;
uint16_t class;
uint16_t ttl;
uint16_t rd_length;
union ResourceData rd_data;
struct ResourceRecord* next; // for linked list
};
struct Message {
uint16_t id; /* Identifier */
/* Flags */
uint16_t qr; /* Query/Response Flag */
uint16_t opcode; /* Operation Code */
uint16_t aa; /* Authoritative Answer Flag */
uint16_t tc; /* Truncation Flag */
uint16_t rd; /* Recursion Desired */
uint16_t ra; /* Recursion Available */
uint16_t rcode; /* Response Code */
uint16_t qdCount; /* Question Count */
uint16_t anCount; /* Answer Record Count */
uint16_t nsCount; /* Authority Record Count */
uint16_t arCount; /* Additional Record Count */
/* At least one question; questions are copied to the response 1:1 */
struct Question* questions;
/*
* Resource records to be send back.
* Every resource record can be in any of the following places.
* But every place has a different semantic.
*/
struct ResourceRecord* answers;
struct ResourceRecord* authorities;
struct ResourceRecord* additionals;
};
/*
int ICACHE_FLASH_ATTR get_A_Record(uint8_t addr[4], const char domain_name[])
{
if (strcmp("foo.bar.com", domain_name) == 0)
{
addr[0] = 192;
addr[1] = 168;
addr[2] = 1;
addr[3] = 1;
return 0;
}
else
{
return -1;
}
}
int ICACHE_FLASH_ATTR get_AAAA_Record(uint8_t addr[16], const char domain_name[])
{
if (strcmp("foo.bar.com", domain_name) == 0)
{
addr[0] = 0xfe;
addr[1] = 0x80;
addr[2] = 0x00;
addr[3] = 0x00;
addr[4] = 0x00;
addr[5] = 0x00;
addr[6] = 0x00;
addr[7] = 0x00;
addr[8] = 0x00;
addr[9] = 0x00;
addr[10] = 0x00;
addr[11] = 0x00;
addr[12] = 0x00;
addr[13] = 0x00;
addr[14] = 0x00;
addr[15] = 0x01;
return 0;
}
else
{
return -1;
}
}
*/
/*
* Debugging functions.
*/
#ifdef DNS_DEBUG
void ICACHE_FLASH_ATTR print_hex(uint8_t* buf, size_t len)
{
int i;
os_printf("%zu bytes:\n", len);
for(i = 0; i < len; ++i)
os_printf("%02x ", buf[i]);
os_printf("\n");
}
void print_resource_record(struct ResourceRecord* rr)
{
int i;
while (rr)
{
os_printf(" ResourceRecord { name '%s', type %u, class %u, ttl %u, rd_length %u, ",
rr->name,
rr->type,
rr->class,
rr->ttl,
rr->rd_length
);
union ResourceData *rd = &rr->rd_data;
switch (rr->type)
{
case A_Resource_RecordType:
os_printf("Address Resource Record { address ");
for(i = 0; i < 4; ++i)
os_printf("%s%u", (i ? "." : ""), rd->a_record.addr[i]);
os_printf(" }");
break;
case NS_Resource_RecordType:
os_printf("Name Server Resource Record { name %s }",
rd->name_server_record.name
);
break;
case CNAME_Resource_RecordType:
os_printf("Canonical Name Resource Record { name %u }",
rd->cname_record.name
);
break;
case SOA_Resource_RecordType:
os_printf("SOA { MName '%s', RName '%s', serial %u, refresh %u, retry %u, expire %u, minimum %u }",
rd->soa_record.MName,
rd->soa_record.RName,
rd->soa_record.serial,
rd->soa_record.refresh,
rd->soa_record.retry,
rd->soa_record.expire,
rd->soa_record.minimum
);
break;
case PTR_Resource_RecordType:
os_printf("Pointer Resource Record { name '%s' }",
rd->ptr_record.name
);
break;
case MX_Resource_RecordType:
os_printf("Mail Exchange Record { preference %u, exchange '%s' }",
rd->mx_record.preference,
rd->mx_record.exchange
);
break;
case TXT_Resource_RecordType:
os_printf("Text Resource Record { txt_data '%s' }",
rd->txt_record.txt_data
);
break;
case AAAA_Resource_RecordType:
os_printf("AAAA Resource Record { address ");
for(i = 0; i < 16; ++i)
os_printf("%s%02x", (i ? ":" : ""), rd->aaaa_record.addr[i]);
os_printf(" }");
break;
default:
os_printf("Unknown Resource Record { ??? }");
}
os_printf("}\n");
rr = rr->next;
}
}
void ICACHE_FLASH_ATTR print_query(struct Message* msg)
{
os_printf("QUERY { ID: %02x", msg->id);
os_printf(". FIELDS: [ QR: %u, OpCode: %u ]", msg->qr, msg->opcode);
os_printf(", QDcount: %u", msg->qdCount);
os_printf(", ANcount: %u", msg->anCount);
os_printf(", NScount: %u", msg->nsCount);
os_printf(", ARcount: %u,\n", msg->arCount);
struct Question* q = msg->questions;
while (q)
{
os_printf(" Question { qName '%s', qType %u, qClass %u }\n",
q->qName,
q->qType,
q->qClass
);
q = q->next;
}
print_resource_record(msg->answers);
print_resource_record(msg->authorities);
print_resource_record(msg->additionals);
os_printf("}\n");
}
#endif
/*
* Basic memory operations.
*/
size_t ICACHE_FLASH_ATTR get16bits(const uint8_t** buffer)
{
uint16_t value;
os_memcpy(&value, *buffer, 2);
*buffer += 2;
return ntohs(value);
}
void ICACHE_FLASH_ATTR put8bits(uint8_t** buffer, uint8_t value)
{
os_memcpy(*buffer, &value, 1);
*buffer += 1;
}
void ICACHE_FLASH_ATTR put16bits(uint8_t** buffer, uint16_t value)
{
value = htons(value);
os_memcpy(*buffer, &value, 2);
*buffer += 2;
}
void ICACHE_FLASH_ATTR put32bits(uint8_t** buffer, uint32_t value)
{
value = htons(value);
os_memcpy(*buffer, &value, 4);
*buffer += 4;
}
/*
* Deconding/Encoding functions.
*/
// 3foo3bar3com0 => foo.bar.com
char* ICACHE_FLASH_ATTR decode_domain_name(const uint8_t** buffer)
{
char name[256];
const uint8_t* buf = *buffer;
int j = 0;
int i = 0;
while (buf[i] != 0)
{
//if (i >= buflen || i > sizeof(name))
// return NULL;
if (i != 0)
{
name[j] = '.';
j += 1;
}
int len = buf[i];
i += 1;
os_memcpy(name+j, buf+i, len);
i += len;
j += len;
}
name[j] = '\0';
*buffer += i + 1; //also jump over the last 0
//return strdup(name);
uint8_t *ret = os_malloc(os_strlen(name)+1);
os_strcpy(ret, name);
return ret;
}
char ICACHE_FLASH_ATTR *_strchr(const char *s, int c) {
while (*s != (char)c)
if (!*s++)
return 0;
return (char *)s;
}
// foo.bar.com => 3foo3bar3com0
void ICACHE_FLASH_ATTR encode_domain_name(uint8_t** buffer, const char* domain)
{
uint8_t* buf = *buffer;
const char* beg = domain;
const char* pos;
int len = 0;
int i = 0;
while ((pos = _strchr(beg, '.')))
{
len = pos - beg;
buf[i] = len;
i += 1;
os_memcpy(buf+i, beg, len);
i += len;
beg = pos + 1;
}
len = os_strlen(domain) - (beg - domain);
buf[i] = len;
i += 1;
os_memcpy(buf + i, beg, len);
i += len;
buf[i] = 0;
i += 1;
*buffer += i;
}
void ICACHE_FLASH_ATTR decode_header(struct Message* msg, const uint8_t** buffer)
{
msg->id = get16bits(buffer);
uint32_t fields = get16bits(buffer);
msg->qr = (fields & QR_MASK) >> 15;
msg->opcode = (fields & OPCODE_MASK) >> 11;
msg->aa = (fields & AA_MASK) >> 10;
msg->tc = (fields & TC_MASK) >> 9;
msg->rd = (fields & RD_MASK) >> 8;
msg->ra = (fields & RA_MASK) >> 7;
msg->rcode = (fields & RCODE_MASK) >> 0;
msg->qdCount = get16bits(buffer);
msg->anCount = get16bits(buffer);
msg->nsCount = get16bits(buffer);
msg->arCount = get16bits(buffer);
}
void ICACHE_FLASH_ATTR encode_header(struct Message* msg, uint8_t** buffer)
{
put16bits(buffer, msg->id);
int fields = 0;
fields |= (msg->qr << 15) & QR_MASK;
fields |= (msg->rcode << 0) & RCODE_MASK;
// TODO: insert the rest of the fields
put16bits(buffer, fields);
put16bits(buffer, msg->qdCount);
put16bits(buffer, msg->anCount);
put16bits(buffer, msg->nsCount);
put16bits(buffer, msg->arCount);
}
int ICACHE_FLASH_ATTR decode_msg(struct Message* msg, const uint8_t* buffer, int size)
{
int i;
decode_header(msg, &buffer);
if (msg->anCount != 0 || msg->nsCount != 0)
{
os_printf("Only questions expected!\n");
return -1;
}
// parse questions
uint32_t qcount = msg->qdCount;
struct Question* qs = msg->questions;
for (i = 0; i < qcount; ++i)
{
struct Question* q = os_malloc(sizeof(struct Question));
q->qName = decode_domain_name(&buffer);
q->qType = get16bits(&buffer);
q->qClass = get16bits(&buffer);
// prepend question to questions list
q->next = qs;
msg->questions = q;
}
// We do not expect any resource records to parse here.
return 0;
}
// For every question in the message add a appropiate resource record
// in either section 'answers', 'authorities' or 'additionals'.
void ICACHE_FLASH_ATTR resolver_process(struct Message* msg)
{
struct ResourceRecord* beg;
struct ResourceRecord* rr;
struct Question* q;
int rc;
// leave most values intact for response
msg->qr = 1; // this is a response
msg->aa = 1; // this server is authoritative
msg->ra = 0; // no recursion available
msg->rcode = Ok_ResponseType;
// should already be 0
msg->anCount = 0;
msg->nsCount = 0;
msg->arCount = 0;
// for every question append resource records
q = msg->questions;
while (q)
{
rr = os_malloc(sizeof(struct ResourceRecord));
os_memset(rr, 0, sizeof(struct ResourceRecord));
//rr->name = strdup(q->qName);
rr->name = os_malloc(os_strlen(q->qName)+1);
os_strcpy(rr->name, q->qName);
rr->type = q->qType;
rr->class = q->qClass;
rr->ttl = 60*60; // in seconds; 0 means no caching
os_printf("Query for '%s'\n", q->qName);
// We only can only answer two question types so far
// and the answer (resource records) will be all put
// into the answers list.
// This behavior is probably non-standard!
switch (q->qType)
{
case A_Resource_RecordType:
rr->rd_length = 4;
rc = get_A_Record(rr->rd_data.a_record.addr, q->qName);
if (rc < 0)
{
os_free(rr->name);
os_free(rr);
goto next;
}
break;
case AAAA_Resource_RecordType:
rr->rd_length = 16;
rc = get_AAAA_Record(rr->rd_data.aaaa_record.addr, q->qName);
if (rc < 0)
{
os_free(rr->name);
os_free(rr);
goto next;
}
break;
/*
case NS_Resource_RecordType:
case CNAME_Resource_RecordType:
case SOA_Resource_RecordType:
case PTR_Resource_RecordType:
case MX_Resource_RecordType:
case TXT_Resource_RecordType:
*/
default:
os_free(rr);
msg->rcode = NotImplemented_ResponseType;
os_printf("Cannot answer question of type %d.\n", q->qType);
goto next;
}
msg->anCount++;
// prepend resource record to answers list
beg = msg->answers;
msg->answers = rr;
rr->next = beg;
// jump here to omit question
next:
// process next question
q = q->next;
}
}
/* @return 0 upon failure, 1 upon success */
int ICACHE_FLASH_ATTR encode_resource_records(struct ResourceRecord* rr, uint8_t** buffer)
{
int i;
while (rr)
{
// Answer questions by attaching resource sections.
encode_domain_name(buffer, rr->name);
put16bits(buffer, rr->type);
put16bits(buffer, rr->class);
put32bits(buffer, rr->ttl);
put16bits(buffer, rr->rd_length);
switch (rr->type)
{
case A_Resource_RecordType:
for(i = 0; i < 4; ++i)
put8bits(buffer, rr->rd_data.a_record.addr[i]);
break;
case AAAA_Resource_RecordType:
for(i = 0; i < 16; ++i)
put8bits(buffer, rr->rd_data.aaaa_record.addr[i]);
break;
default:
os_printf("Unknown type %u. => Ignore resource record.\n", rr->type);
return 1;
}
rr = rr->next;
}
return 0;
}
/* @return 0 upon failure, 1 upon success */
int ICACHE_FLASH_ATTR encode_msg(struct Message* msg, uint8_t** buffer)
{
struct Question* q;
int rc;
encode_header(msg, buffer);
q = msg->questions;
while (q)
{
encode_domain_name(buffer, q->qName);
put16bits(buffer, q->qType);
put16bits(buffer, q->qClass);
q = q->next;
}
rc = 0;
rc |= encode_resource_records(msg->answers, buffer);
rc |= encode_resource_records(msg->authorities, buffer);
rc |= encode_resource_records(msg->additionals, buffer);
return rc;
}
void ICACHE_FLASH_ATTR free_resource_records(struct ResourceRecord* rr)
{
struct ResourceRecord* next;
while (rr) {
os_free(rr->name);
next = rr->next;
os_free(rr);
rr = next;
}
}
void ICACHE_FLASH_ATTR free_questions(struct Question* qq)
{
struct Question* next;
while (qq) {
os_free(qq->qName);
next = qq->next;
os_free(qq);
qq = next;
}
}
LOCAL void ICACHE_FLASH_ATTR
user_udp_recv(void *arg, char *pusrdata, unsigned short length)
{
uint8_t hwaddr[6];
uint8_t buffer[1024];
struct ip_info ipconfig;
struct espconn *pespconn = (struct espconn *)arg;
if (wifi_get_opmode() != STATION_MODE) {
wifi_get_ip_info(SOFTAP_IF, &ipconfig);
wifi_get_macaddr(SOFTAP_IF, hwaddr);
if (!ip_addr_netcmp((struct ip_addr *)pespconn->proto.udp->remote_ip, &ipconfig.ip, &ipconfig.netmask)) {
//udp packet is received from ESP8266 station
if (!(dns_mode & DNS_MODE_STA))
return;
// wifi_get_ip_info(STATION_IF, &ipconfig);
// wifi_get_macaddr(STATION_IF, hwaddr);
} else {
//udp packet is received from ESP8266 softAP
if (!(dns_mode & DNS_MODE_AP))
return;
}
} else {
//udp packet is received from ESP8266 station
if (!(dns_mode & DNS_MODE_STA))
return;
//wifi_get_ip_info(STATION_IF, &ipconfig);
//wifi_get_macaddr(STATION_IF, hwaddr);
}
if (pusrdata == NULL)
return;
free_questions(msg.questions);
free_resource_records(msg.answers);
free_resource_records(msg.authorities);
free_resource_records(msg.additionals);
os_memset(&msg, 0, sizeof(struct Message));
if (decode_msg(&msg, pusrdata, length) != 0) {
return;
}
#ifdef DNS_DEBUG
/* Print query */
print_query(&msg);
#endif
resolver_process(&msg);
#ifdef DNS_DEBUG
/* Print response */
print_query(&msg);
#endif
uint8_t *p = buffer;
if (encode_msg(&msg, &p) != 0) {
return;
}
espconn_sent(pespconn, buffer, p - buffer);
}
void ICACHE_FLASH_ATTR
dns_resp_init(uint8_t mode)
{
dns_mode = mode;
dns_espconn.type = ESPCONN_UDP;
dns_espconn.proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp));
dns_espconn.proto.udp->local_port = 53; // DNS udp port
espconn_regist_recvcb(&dns_espconn, user_udp_recv); // register a udp packet receiving callback
espconn_create(&dns_espconn); // create udp
}
-8
View File
@@ -1,8 +0,0 @@
#define DNS_MODE_STA 0x01
#define DNS_MODE_AP 0x02
#define DNS_MODE_STATIONAP 0x03
void dns_resp_init(uint8_t mode);
int get_A_Record(uint8_t addr[4], const char domain_name[]);
int get_AAAA_Record(uint8_t addr[16], const char domain_name[]);
+8 -5
View File
@@ -13,6 +13,8 @@
#include <osapi.h>
#include "rboot-ota.h"
#include "config_flash.h"
extern sysconfig_t config;
#ifdef __cplusplus
extern "C" {
@@ -186,8 +188,9 @@ static void ICACHE_FLASH_ATTR upgrade_connect_cb(void *arg) {
return;
}
os_sprintf((char*)request,
"GET /%s HTTP/1.0\r\nHost: " OTA_HOST "\r\n" HTTP_HEADER,
(upgrade->rom_slot == FLASH_BY_ADDR ? OTA_FILE : (upgrade->rom_slot == 0 ? OTA_ROM0 : OTA_ROM1)));
"GET /%s HTTP/1.0\r\nHost: %s\r\n%s",
(upgrade->rom_slot == FLASH_BY_ADDR ? OTA_FILE : (upgrade->rom_slot == 0 ? OTA_ROM0 : OTA_ROM1)),
config.ota_host, HTTP_HEADER);
// send the http request, with timeout for reply
os_timer_setfn(&ota_timer, (os_timer_func_t *)rboot_ota_deinit, 0);
@@ -246,7 +249,7 @@ static void ICACHE_FLASH_ATTR upgrade_resolved(const char *name, ip_addr_t *ip,
if (ip == 0) {
to_console("DNS lookup failed for: ");
to_console(OTA_HOST);
to_console(config.ota_host);
to_console("\r\n");
// not connected so don't call disconnect on the connection
// but call our own disconnect callback to do the cleanup
@@ -258,7 +261,7 @@ static void ICACHE_FLASH_ATTR upgrade_resolved(const char *name, ip_addr_t *ip,
upgrade->conn->type = ESPCONN_TCP;
upgrade->conn->state = ESPCONN_NONE;
upgrade->conn->proto.tcp->local_port = espconn_port();
upgrade->conn->proto.tcp->remote_port = OTA_PORT;
upgrade->conn->proto.tcp->remote_port = config.ota_port;
*(ip_addr_t*)upgrade->conn->proto.tcp->remote_ip = *ip;
// set connection call backs
espconn_regist_connectcb(upgrade->conn, upgrade_connect_cb);
@@ -328,7 +331,7 @@ bool ICACHE_FLASH_ATTR rboot_ota_start(ota_callback callback) {
system_upgrade_flag_set(UPGRADE_FLAG_START);
// dns lookup
result = espconn_gethostbyname(upgrade->conn, OTA_HOST, &upgrade->ip, upgrade_resolved);
result = espconn_gethostbyname(upgrade->conn, config.ota_host, &upgrade->ip, upgrade_resolved);
if (result == ESPCONN_OK) {
// hostname is already cached or is actually a dotted decimal ip address
upgrade_resolved(0, &upgrade->ip, upgrade->conn);
+4 -4
View File
@@ -16,10 +16,10 @@ extern "C" {
#endif
// ota server details
#define OTA_HOST "192.168.1.53"
#define OTA_PORT 8000
#define OTA_ROM0 "rom0.bin"
#define OTA_ROM1 "rom1.bin"
//#define OTA_HOST "192.168.178.25"
//#define OTA_PORT 8080
#define OTA_ROM0 "0x02000.bin"
#define OTA_ROM1 "0x82000.bin"
// OTA_FILE is not required, but is part of the example
// code for writing arbitrary files to flash
#define OTA_FILE "file.bin"
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef _USER_CONFIG_
#define _USER_CONFIG_
#define ESP_REPEATER_VERSION "V1.8.11"
#define ESP_REPEATER_VERSION "V2.0.0"
#define LOCAL_ACCESS 0x01
#define REMOTE_ACCESS 0x02
+25 -9
View File
@@ -797,6 +797,7 @@ void ICACHE_FLASH_ATTR Switch() {
rboot_set_current_rom(after);
to_console("Restarting...\r\n\r\n");
system_restart();
while (true);
}
static void ICACHE_FLASH_ATTR OtaUpdate_CallBack(bool result, uint8 rom_slot) {
@@ -811,6 +812,7 @@ static void ICACHE_FLASH_ATTR OtaUpdate_CallBack(bool result, uint8 rom_slot) {
to_console(msg);
rboot_set_current_rom(rom_slot);
system_restart();
while (true);
}
} else {
// fail
@@ -1299,15 +1301,10 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn)
#endif
#ifdef OTAUPDATE
if (nTokens == 2 && strcmp(tokens[1], "ota") == 0) {
to_console("Firmware update: ");
to_console(OTA_HOST);
//to_console(OTA_PORT);
to_console("/");
to_console(OTA_ROM0);
to_console(" OR ");
to_console(OTA_ROM1);
to_console("\r\n");
os_sprintf_flash(response, "Currently running rom %d.\r\n", rboot_get_current_rom());
os_sprintf(response, "Firmware update: %s:%d/%s\r\n", config.ota_host, config.ota_port,
rboot_get_current_rom()?OTA_ROM0:OTA_ROM1);
to_console(response);
os_sprintf_flash(response, "Currently running rom %d\r\n", rboot_get_current_rom());
to_console(response);
goto command_handled_2;
}
@@ -1645,6 +1642,10 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn)
#ifdef OTAUPDATE
if (strcmp(tokens[0], "ota") == 0)
{
if (config.locked) {
os_sprintf(response, INVALID_LOCKED);
goto command_handled;
}
if (nTokens != 2) {
os_sprintf(response, INVALID_NUMARGS);
goto command_handled;
@@ -2011,6 +2012,21 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn)
goto command_handled;
}
#endif
#ifdef OTAUPDATE
if (strcmp(tokens[1], "ota_host") == 0)
{
os_strncpy(config.ota_host, tokens[2], 64);
config.mqtt_host[63] = 0;
os_sprintf_flash(response, "OTA host set\r\n");
goto command_handled;
}
if (strcmp(tokens[1],"ota_port") == 0)
{
config.ota_port = atoi(tokens[2]);
os_sprintf_flash(response, "OTA port set\r\n");
goto command_handled;
}
#endif
#ifdef REMOTE_CONFIG
if (strcmp(tokens[1],"config_port") == 0)
{