diff --git a/.gitignore b/.gitignore index 2672ca3..8f81706 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ webpages.espfs espfs/espfstest/*.o espfs/espfstest/espfstest *.DS_Store -html_compressed/ \ No newline at end of file +html_compressed/ +esp-link.tgz +tve-patch/ diff --git a/Makefile b/Makefile index 8c9c450..154a781 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,63 @@ +# +# Makefile for esp-link - https://github.com/jeelabs/esp-link +# +# Start by setting the directories for the toolchain a few lines down +# the default target will build the firmware images +# `make flash` will flash the esp serially +# `make wiflash` will flash the esp over wifi +# `VERBOSE=1 make ...` will print debug info +# `ESP_HOSTNAME=my.esp.example.com make wiflash` is an easy way to override a variable +# +# Makefile heavily adapted to esp-link and wireless flashing by Thorsten von Eicken +# Original from esphttpd and others... + +# --------------- toolchain configuration --------------- + +# Base directory for the compiler. Needs a / at the end. +# Typically you'll install https://github.com/pfalcon/esp-open-sdk +XTENSA_TOOLS_ROOT ?= $(abspath ../esp-open-sdk/xtensa-lx106-elf/bin)/ + +# Base directory of the ESP8266 SDK package, absolute +# Typically you'll download from Espressif's BBS, http://bbs.espressif.com/viewforum.php?f=5 +SDK_BASE ?= $(abspath ../esp_iot_sdk_v1.1.0) + +# Esptool.py path and port, only used for 1-time serial flashing +# Typically you'll use https://github.com/themadinventor/esptool +ESPTOOL ?= $(abspath ../esptool/esptool.py) +ESPPORT ?= /dev/ttyUSB0 +ESPBAUD ?= 460800 + +# --------------- chipset configuration --------------- + +ESP_SPI_SIZE ?= 0 # 0->512KB +ESP_FLASH_MODE ?= 0 # 0->QIO +ESP_FLASH_FREQ_DIV ?= 0 # 0->40Mhz +ESP_FLASH_MAX ?= 241664 # max bin file for 512KB flash: 236KB + +# hostname or IP address for wifi flashing +ESP_HOSTNAME ?= esp8266 + +# The pin assignments below are used when the settings in flash are invalid, they +# can be changed via the web interface +# GPIO pin used to reset attached microcontroller, acative low +MCU_RESET_PIN ?= 12 +# GPIO pin used with reset to reprogram MCU (ISP=in-system-programming, unused with AVRs), active low +MCU_ISP_PIN ?= 13 +# GPIO pin used for "connectivity" LED, active low +LED_CONN_PIN ?= 0 +# GPIO pin used for "serial activity" LED, active low +LED_SERIAL_PIN ?= 2 + +# --------------- esp-link version --------------- + +# This queries git to produce a version string like "esp-link v0.9.0 2015-06-01 34bc76" +# If you don't have a proper git checkout or are on windows, then simply swap for the constant +VERSION := "esp-link custom version" +DATE := $(shell date '+%F %T') +BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +SHA := $(shell if git diff --quiet HEAD; then git symbolic-ref HEAD | cut -d"/" -f 3; \ + else echo "development"; fi) +VERSION := esp-link - $(BRANCH) - $(DATE) - $(SHA) # --------------- esphttpd config options --------------- @@ -30,54 +90,33 @@ YUI-COMPRESSOR ?= /usr/bin/yui-compressor # Because the decompression is done in the esp8266, it does not require any support in the browser. USE_HEATSHRINK ?= yes -# -------------- End of esphttpd config options ------------- - +# -------------- End of config options ------------- # Output directors to store intermediate compiled files # relative to the project directory BUILD_BASE = build FW_BASE = firmware -# Base directory for the compiler. Needs a / at the end; -XTENSA_TOOLS_ROOT ?= $(abspath ../esp-open-sdk/xtensa-lx106-elf/bin)/ - -# Base directory of the ESP8266 SDK package, absolute -SDK_BASE ?= $(abspath ../esp_iot_sdk_v1.1.0) - -#Esptool.py path and port -ESPTOOL ?= esptool.py -ESPPORT ?= /dev/ttyUSB0 -#ESPDELAY indicates seconds to wait between flashing the two binary images -ESPDELAY ?= 3 -ESPBAUD ?= 460800 - # name for the target project TARGET = httpd # espressif tool to concatenate sections for OTA upload using bootloader v1.2+ APPGEN_TOOL ?= gen_appbin.py -ESP_SPI_SIZE ?= 0 # 0->512KB -ESP_FLASH_MODE ?= 0 # 0->QIO -ESP_FLASH_FREQ_DIV ?= 0 # 0->40Mhz -ESP_FLASH_MAX ?= 241664 # max bin file for 512KB flash: 236KB -ESP_HOSTNAME ?= esp8266 - - # which modules (subdirectories) of the project to include in compiling -#MODULES = driver user lwip/api lwip/app lwip/core lwip/core/ipv4 lwip/netif MODULES = espfs httpd user serial EXTRA_INCDIR = include . lib/heatshrink/ # libraries used in this project, mainly provided by the SDK LIBS = c gcc hal phy pp net80211 wpa main lwip - - # compiler flags using during compilation of source files CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -D_STDINT_H \ - -Wno-address -DFIRMWARE_SIZE=$(ESP_FLASH_MAX) + -Wno-address -DFIRMWARE_SIZE=$(ESP_FLASH_MAX) \ + -DMCU_RESET_PIN=$(MCU_RESET_PIN) -DMCU_ISP_PIN=$(MCU_ISP_PIN) \ + -DLED_CONN_PIN=$(LED_CONN_PIN) -DLED_SERIAL_PIN=$(LED_SERIAL_PIN) \ + "-DVERSION=$(VERSION)" # linker flags used to generate the main object file LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static @@ -101,8 +140,6 @@ OBJCP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objcopy OBJDP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objdump -#### -#### no user configurable options below here #### SRC_DIR := $(MODULES) BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) @@ -152,7 +189,12 @@ endef .PHONY: all checkdirs clean webpages.espfs -all: checkdirs $(FW_BASE) firmware/user1.bin firmware/user2.bin +all: echo_version checkdirs $(FW_BASE) firmware/user1.bin firmware/user2.bin + +echo_version: + @echo VERSION: $(VERSION) + +user/version.h: $(TARGET_OUT): $(APP_AR) $(LD_SCRIPT) $(vecho) "LD $@" @@ -209,8 +251,10 @@ $(BUILD_DIR): wiflash: all ./wiflash $(ESP_HOSTNAME) firmware/user1.bin firmware/user2.bin -flash: $(TARGET_OUT) $(FW_BASE) - $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x00000 $(FW_BASE)/0x00000.bin 0x40000 $(FW_BASE)/0x40000.bin +flash: all + $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash \ + 0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \ + 0x7E000 $(SDK_BASE)/bin/blank.bin $(BUILD_BASE)/espfs_img.o: html/ html/wifi/ espfs/mkespfsimage/mkespfsimage ifeq ("$(COMPRESS_W_YUI)","yes") @@ -245,12 +289,16 @@ build/eagle.esphttpd2.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.512.app2.ld -e '/^ irom0_0_seg/ s/2B000/38000/' \ $(SDK_LDDIR)/eagle.app.v6.new.512.app2.ld >$@ -blankflash: - $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x7E000 $(SDK_BASE)/bin/blank.bin - espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/ $(Q) $(MAKE) -C espfs/mkespfsimage USE_HEATSHRINK="$(USE_HEATSHRINK)" GZIP_COMPRESSION="$(GZIP_COMPRESSION)" +release: all + $(Q) rm -rf release; mkdir -p release/esp-link + $(Q) cp firmware/user1.bin firmware/user2.bin $(SDK_BASE)/bin/blank.bin \ + "$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link + $(Q) tar zcf esp-link.tgz -C release esp-link + $(Q) rm -rf release + clean: $(Q) rm -f $(APP_AR) $(Q) rm -f $(TARGET_OUT) diff --git a/html/index.tpl b/html/index.tpl index 3d3c01c..ddb4f7d 100644 --- a/html/index.tpl +++ b/html/index.tpl @@ -3,6 +3,7 @@

esp link

+

%version%

diff --git a/serial/serbridge.c b/serial/serbridge.c index bec6545..0cb5f83 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -8,19 +8,10 @@ #include "uart.h" #include "serbridge.h" +#include "serled.h" #include "console.h" -#if 1 -// GPIO for esp-03 module with gpio12->reset, gpio13->isp, gpio2->"ser" LED -#define MCU_RESET 12 -#define MCU_ISP 13 -#define MCU_LED 2 -#else -// GPIO for esp-01 module with gpio0->reset, gpio2->isp -#define MCU_RESET 0 -#define MCU_ISP 2 -#undef MCU_LED -#endif +static uint8_t mcu_reset_pin, mcu_isp_pin; static struct espconn serbridgeConn; static esp_tcp serbridgeTcp; @@ -88,6 +79,8 @@ static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) { //os_printf("%d ST\n", system_get_time()); conn->readytosend = true; sendtxbuffer(conn); // send possible new data in txbuffer + + serledFlash(50); // short blink on serial LED } // Telnet protocol characters @@ -154,21 +147,21 @@ telnetUnwrap(uint8_t *inBuf, int len, uint8_t state) case TN_setControl: // switch control line and delay a tad switch (c) { case DTR_ON: - os_printf("MCU reset\n"); - GPIO_OUTPUT_SET(MCU_RESET, 0); + os_printf("MCU reset gpio%d\n", mcu_reset_pin); + GPIO_OUTPUT_SET(mcu_reset_pin, 0); os_delay_us(100L); break; case DTR_OFF: - GPIO_OUTPUT_SET(MCU_RESET, 1); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); os_delay_us(100L); break; case RTS_ON: - os_printf("MCU ISP\n"); - GPIO_OUTPUT_SET(MCU_ISP, 0); + os_printf("MCU ISP gpio%d\n", mcu_isp_pin); + GPIO_OUTPUT_SET(mcu_isp_pin, 0); os_delay_us(100L); break; case RTS_OFF: - GPIO_OUTPUT_SET(MCU_ISP, 1); + GPIO_OUTPUT_SET(mcu_isp_pin, 1); os_delay_us(100L); break; } @@ -198,16 +191,16 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh if ((len == 2 && strncmp(data, "0 ", 2) == 0) || (len == 2 && strncmp(data, "?\n", 2) == 0) || (len == 3 && strncmp(data, "?\r\n", 3) == 0)) { - os_printf("MCU Reset=%d ISP=%d\n", MCU_RESET, MCU_ISP); + os_printf("MCU Reset=%d ISP=%d\n", mcu_reset_pin, mcu_isp_pin); os_delay_us(2*1000L); // time for os_printf to happen // send reset to arduino/ARM - GPIO_OUTPUT_SET(MCU_RESET, 0); + GPIO_OUTPUT_SET(mcu_reset_pin, 0); os_delay_us(100L); - GPIO_OUTPUT_SET(MCU_ISP, 0); + GPIO_OUTPUT_SET(mcu_isp_pin, 0); os_delay_us(100L); - GPIO_OUTPUT_SET(MCU_RESET, 1); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); os_delay_us(100L); - GPIO_OUTPUT_SET(MCU_ISP, 1); + GPIO_OUTPUT_SET(mcu_isp_pin, 1); os_delay_us(1000L); conn->conn_mode = cmAVR; @@ -231,6 +224,8 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh } else { uart0_tx_buffer(data, len); } + + serledFlash(50); // short blink on serial LED } // Error callback (it's really poorly named, it's not a "connection reconnected" callback, @@ -253,9 +248,9 @@ static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) { { if (connData[i].conn_mode == cmAVR) { // send reset to arduino/ARM - GPIO_OUTPUT_SET(MCU_RESET, 0); + GPIO_OUTPUT_SET(mcu_reset_pin, 0); os_delay_us(100L); - GPIO_OUTPUT_SET(MCU_RESET, 1); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); } connData[i].conn = NULL; } @@ -303,7 +298,7 @@ serbridgeUartCb(char *buf, int length) { } // Start transparent serial bridge TCP server on specified port (typ. 23) -void ICACHE_FLASH_ATTR serbridgeInit(int port) { +void ICACHE_FLASH_ATTR serbridgeInit(int port, uint8_t reset_pin, uint8_t isp_pin) { int i; for (i = 0; i < MAX_CONN; i++) { connData[i].conn = NULL; @@ -314,13 +309,14 @@ void ICACHE_FLASH_ATTR serbridgeInit(int port) { serbridgeTcp.local_port = port; serbridgeConn.proto.tcp = &serbridgeTcp; - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U , FUNC_GPIO13); - //GPIO_OUTPUT_SET(MCU_ISP, 1); - //GPIO_OUTPUT_SET(MCU_RESET, 0); -#ifdef MCU_LED - GPIO_OUTPUT_SET(MCU_LED, 1); -#endif + mcu_reset_pin = reset_pin; + mcu_isp_pin = isp_pin; + // set both pins to 1 so we don't cause a reset + GPIO_OUTPUT_SET(mcu_isp_pin, 1); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); + // switch pin mux to make these pins GPIO pins + makeGpio(mcu_reset_pin); + makeGpio(mcu_isp_pin); espconn_regist_connectcb(&serbridgeConn, serbridgeConnectCb); espconn_accept(&serbridgeConn); diff --git a/serial/serbridge.h b/serial/serbridge.h index 22dc4a1..095b38a 100644 --- a/serial/serbridge.h +++ b/serial/serbridge.h @@ -32,7 +32,7 @@ struct serbridgeConnData { uint8_t telnet_state; }; -void ICACHE_FLASH_ATTR serbridgeInit(int port); +void ICACHE_FLASH_ATTR serbridgeInit(int port, uint8_t reset_pin, uint8_t isp_pin); void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, int len); #endif /* __SER_BRIDGE_H__ */ diff --git a/serial/serled.c b/serial/serled.c new file mode 100644 index 0000000..82390f8 --- /dev/null +++ b/serial/serled.c @@ -0,0 +1,79 @@ +#include +#include + +static ETSTimer serledTimer; +static uint8_t serledPin; + +static void ICACHE_FLASH_ATTR setSerled(int on) { + // LED is active-low + if (on) { + gpio_output_set(0, (1<cgiData; @@ -48,6 +49,7 @@ int ICACHE_FLASH_ATTR cgiReadFlash(HttpdConnData *connData) { *pos+=1024; if (*pos>=0x40200000+(512*1024)) return HTTPD_CGI_DONE; else return HTTPD_CGI_MORE; } +#endif //===== Cgi to query which firmware needs to be uploaded next int ICACHE_FLASH_ATTR cgiGetFirmwareNext(HttpdConnData *connData) { diff --git a/user/console.c b/user/console.c index c872b55..98cdb51 100644 --- a/user/console.c +++ b/user/console.c @@ -6,20 +6,27 @@ // Microcontroller console capturing the last 1024 characters received on the uart so // they can be shown on a web page +// Buffer to hold concole contents. +// Invariants: +// - console_rd==console_wr <=> buffer empty +// - *console_rd == next char to read +// - *console_wr == next char to write +// - 0 <= console_xx < BUF_MAX +// - (console_wr+1)%BUF_MAX) == console_rd <=> buffer full #define BUF_MAX (1024) static char console_buf[BUF_MAX]; static int console_wr, console_rd; -static int console_pos; // offset since reset of console_rd position +static int console_pos; // offset since reset of buffer static void ICACHE_FLASH_ATTR console_write(char c) { - int wr = (console_wr+1)%BUF_MAX; - if (wr == console_rd) { + console_buf[console_wr] = c; + console_wr = (console_wr+1) % BUF_MAX; + if (console_wr == console_rd) { + // full, we write anyway and loose the oldest char console_rd = (console_rd+1) % BUF_MAX; // full, eat first char console_pos++; } - console_buf[console_wr] = c; - console_wr = wr; } // return previous character in console, 0 if at start @@ -41,11 +48,13 @@ tplConsole(HttpdConnData *connData, char *token, void **arg) { if (token==NULL) return HTTPD_CGI_DONE; if (os_strcmp(token, "console") == 0) { - if (console_wr < console_rd) { + if (console_wr > console_rd) { httpdSend(connData, console_buf+console_rd, console_wr-console_rd); } else if (console_rd != console_wr) { httpdSend(connData, console_buf+console_rd, BUF_MAX-console_rd); httpdSend(connData, console_buf, console_wr); + } else { + httpdSend(connData, "", -1); } } else if (os_strcmp(token, "head")==0) { printHead(connData); diff --git a/user/log.c b/user/log.c index d1fd59b..9b4dd98 100644 --- a/user/log.c +++ b/user/log.c @@ -7,6 +7,7 @@ // The web log has a 1KB circular in-memory buffer which os_printf prints into and // the HTTP handler simply displays the buffer content on a web page. +// see consolse.c for invariants (same here) #define BUF_MAX (1024) static char log_buf[BUF_MAX]; static int log_wr, log_rd; @@ -27,11 +28,10 @@ log_uart(bool enable) { static void ICACHE_FLASH_ATTR log_write(char c) { - int wr = (log_wr+1)%BUF_MAX; - if (wr == log_rd) - log_rd = (log_rd+1) % BUF_MAX; // full, eat first char log_buf[log_wr] = c; - log_wr = wr; + log_wr = (log_wr+1) % BUF_MAX; + if (log_wr == log_rd) + log_rd = (log_rd+1) % BUF_MAX; // full, eat first char } #if 0 @@ -74,6 +74,8 @@ tplLog(HttpdConnData *connData, char *token, void **arg) { } else if (log_rd != log_wr) { httpdSend(connData, log_buf+log_rd, BUF_MAX-log_rd); httpdSend(connData, log_buf, log_wr); + } else { + httpdSend(connData, "", -1); } } else if (os_strcmp(token, "head")==0) { printHead(connData); diff --git a/user/status.h b/user/status.h index 0f89edf..4ce8b02 100644 --- a/user/status.h +++ b/user/status.h @@ -2,7 +2,7 @@ #define STATUS_H void statusWifiUpdate(uint8_t state); -void statusInit(void); +void statusInit(uint8_t pin); #endif diff --git a/user/user_main.c b/user/user_main.c index 219eea4..6892b5c 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -21,6 +21,7 @@ #include "uart.h" #include "serbridge.h" #include "status.h" +#include "serled.h" #include "console.h" #include "log.h" #define MCU_RESET 12 @@ -59,7 +60,7 @@ should be placed above the URLs they protect. */ HttpdBuiltInUrl builtInUrls[]={ {"/", cgiRedirect, "/index.tpl"}, - {"/flash/read", cgiReadFlash, NULL}, + //{"/flash/read", cgiReadFlash, NULL}, {"/flash/next", cgiGetFirmwareNext, NULL}, {"/flash/upload", cgiUploadFirmware, NULL}, {"/flash/reboot", cgiRebootFirmware, NULL}, @@ -110,9 +111,12 @@ void user_init(void) { uart_init(BIT_RATE_115200, BIT_RATE_115200); // say hello (leave some time to cause break in TX after boot loader's msg os_delay_us(10000L); - os_printf("\n\nInitializing esp-link\n"); +# define VERS_STR_STR(V) #V +# define VERS_STR(V) VERS_STR_STR(V) + os_printf("\n\nInitializing esp-link\n" VERS_STR(VERSION) "\n"); // Status LEDs - statusInit(); + statusInit(LED_CONN_PIN); + serledInit(LED_SERIAL_PIN); // Wifi wifiInit(); // init the flash filesystem with the html stuff @@ -121,7 +125,7 @@ void user_init(void) { // mount the http handlers httpdInit(builtInUrls, 80); // init the wifi-serial transparent bridge (port 23) - serbridgeInit(23); + serbridgeInit(23, MCU_RESET_PIN, MCU_ISP_PIN); uart_add_recv_cb(&serbridgeUartCb); #ifdef SHOW_HEAP_USE os_timer_disarm(&prHeapTimer); diff --git a/wiflash b/wiflash index e9f2671..d8c12ea 100755 --- a/wiflash +++ b/wiflash @@ -82,7 +82,7 @@ while true; do next=`curl -m 10 $v -s "http://$hostname/flash/next"` if [[ $? != 0 ]]; then echo "Error retrieving http://$hostname/flash/next" >&2 - break + exit 1 fi case "$next" in user1.bin) @@ -94,8 +94,8 @@ while true; do fw="$user2" break;; *) - echo "Cannot parse result of retrieving http://$hostname/flash/next" >&2 - sleep 2 + echo "Error retrieving or parsing http://$hostname/flash/next" >&2 + exit 1 ;; esac done