added built-in AVR/optiboot programmer

pull/69/head
Thorsten von Eicken 9 years ago
parent 8867c8e9b6
commit adb0706349
  1. 110
      avrflash
  2. 3
      esp-link/cgi.c
  3. 1
      esp-link/cgi.h
  4. 580
      esp-link/cgioptiboot.c
  5. 11
      esp-link/cgioptiboot.h
  6. 2
      esp-link/cgipins.c
  7. 19
      esp-link/log.c
  8. 3
      esp-link/main.c
  9. 44
      esp-link/stk500.h
  10. 17
      httpd/httpd.c
  11. 1
      include/espmissingincludes.h
  12. 12
      serial/serbridge.c
  13. 3
      serial/serbridge.h
  14. 1
      serial/serled.c
  15. 17
      serial/uart.c
  16. 15
      serial/uart.h

@ -0,0 +1,110 @@
#! /bin/bash
#
# Flash an AVR with optiboot using the esp-link built-in programmer
# Basically we first reset the AVR and get in sync, and then send the hex file
#
# ----------------------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# Thorsten von Eicken wrote this file. As long as you retain
# this notice you can do whatever you want with this stuff. If we meet some day,
# and you think this stuff is worth it, you can buy me a beer in return.
# ----------------------------------------------------------------------------
show_help() {
cat <<EOT
Usage: ${0##*/} [-options...] hostname sketch.hex
Flash the AVR running optiboot attached to esp-link with the sketch.
-v Be verbose
-h show this help
Example: ${0##*/} -v esp-link mysketch.hex
${0##*/} 192.168.4.1 mysketch.hex
EOT
}
if ! which curl >/dev/null; then
echo "ERROR: Cannot find curl: it is required for this script." >&2
exit 1
fi
start=`date +%s`
# ===== Parse arguments
verbose=
while getopts "hvx:" opt; do
case "$opt" in
h) show_help; exit 0 ;;
v) verbose=1 ;;
x) foo="$OPTARG" ;;
'?') show_help >&2; exit 1 ;;
esac
done
# Shift off the options and optional --.
shift "$((OPTIND-1))"
# Get the fixed arguments
if [[ $# != 2 ]]; then
show_help >&2
exit 1
fi
hostname=$1
hex=$2
re='[-A-Za-z0-9.]+'
if [[ ! "$hostname" =~ $re ]]; then
echo "ERROR: hostname ${hostname} is not a valid hostname or ip address" >&2
exit 1
fi
if [[ ! -r "$hex" ]]; then
echo "ERROR: cannot read hex file ($hex)" >&2
exit 1
fi
# ===== Get AVR in sync
[[ -n "$verbose" ]] && echo "Resetting AVR with http://$hostname/pgm/sync" >&2
v=; [[ -n "$verbose" ]] && v=-v
sync=`curl -m 10 $v -s -w '%{http_code}' -XPOST "http://$hostname/pgm/sync"`
if [[ $? != 0 || "$sync" != 204 ]]; then
echo "Error resetting AVR" >&2
exit 1
fi
while true; do
sync=`curl -m 10 $v -s "http://$hostname/pgm/sync"`
if [[ $? != 0 ]]; then
echo "Error checking sync" >&2
exit 1
fi
case "$sync" in
SYNC*)
echo "AVR in $sync" >&2
break;;
"NOT READY"*)
[[ -n "$verbose" ]] && echo " Waiting for sync..." >&2
;;
*)
echo "Error checking sync: $sync" >&2
exit 1
;;
esac
sleep 0.1
done
# ===== Send HEX file
[[ -n "$verbose" ]] && echo "Sending HEX file for programming" >&2
sync=`curl -m 10 $v -s -g -d "@$hex" "http://$hostname/pgm/upload"`
echo $sync
if [[ $? != 0 || ! "$sync" =~ ^Success ]]; then
echo "Error programming AVR" >&2
exit 1
fi
sec=$(( `date +%s` - $start ))
echo "Success, took $sec seconds" >&2
exit 0

@ -17,7 +17,8 @@ Some random cgi routines.
#include <esp8266.h> #include <esp8266.h>
#include "cgi.h" #include "cgi.h"
void noCacheHeaders(HttpdConnData *connData, int code) { void ICACHE_FLASH_ATTR
noCacheHeaders(HttpdConnData *connData, int code) {
httpdStartResponse(connData, code); httpdStartResponse(connData, code);
httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate");
httpdHeader(connData, "Pragma", "no-cache"); httpdHeader(connData, "Pragma", "no-cache");

@ -4,6 +4,7 @@
#include <esp8266.h> #include <esp8266.h>
#include "httpd.h" #include "httpd.h"
void noCacheHeaders(HttpdConnData *connData, int code);
void jsonHeader(HttpdConnData *connData, int code); void jsonHeader(HttpdConnData *connData, int code);
void errorResponse(HttpdConnData *connData, int code, char *message); void errorResponse(HttpdConnData *connData, int code, char *message);

@ -0,0 +1,580 @@
// Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo
#include <esp8266.h>
#include <osapi.h>
#include "cgi.h"
#include "cgioptiboot.h"
#include "config.h"
#include "uart.h"
#include "stk500.h"
#include "serbridge.h"
#include "serled.h"
#define SYNC_TIMEOUT (2000) // to achieve sync, in milliseconds
#define PGM_TIMEOUT (20000) // when sync is achieved, in milliseconds
#define OPTIBOOT_DBG
#undef DBG
#ifdef OPTIBOOT_DBG
#define DBG(format, ...) os_printf(format, ## __VA_ARGS__)
#else
#define DBG(format, ...) do { } while(0)
#endif
//===== global state
static ETSTimer optibootTimer;
static enum { // overall programming states
stateSync = 0, // trying to get sync
stateGetSig, // reading device signature
stateGetVersLo, // reading optiboot version, low bits
stateGetVersHi, // reading optiboot version, high bits
stateProg, // programming...
} progState;
static short syncCnt; // counter & timeout for sync attempts
static short ackWait; // counter of expected ACKs
static uint16_t optibootVers;
#define RESP_SZ 64
static char responseBuf[RESP_SZ]; // buffer to accumulate responses from optiboot
static short responseLen = 0; // amount accumulated so far
#define ERR_MAX 128
static char errMessage[ERR_MAX]; // error message
#define MAX_PAGE_SZ 512 // max flash page size supported
#define MAX_SAVED 512 // max chars in saved buffer
// structure used to remember request details from one callback to the next
// allocated dynamically so we don't burn so much static RAM
static struct optibootData {
char *saved; // buffer for saved incomplete hex records
char *pageBuf; // buffer for received data to be sent to AVR
uint16_t pageLen; // number of bytes in pageBuf
uint16_t pgmSz; // size of flash page to be programmed at a time
uint16_t pgmDone; // number of bytes programmed
uint32_t address; // address to write next page to
uint32_t startTime; // time of program POST request
HttpdConnData *conn; // request doing the programming, so we can cancel it
bool eof; // got EOF record
} *optibootData;
// forward function references
static void optibootTimerCB(void *);
static void optibootUartRecv(char *buffer, short length);
static bool processRecord(char *buf, short len);
static bool programPage(void);
static void ICACHE_FLASH_ATTR armTimer();
static void ICACHE_FLASH_ATTR optibootInit() {
progState = stateSync;
syncCnt = 0;
ackWait = 0;
errMessage[0] = 0;
responseLen = 0;
programmingCB = optibootUartRecv;
if (optibootData != NULL) {
if (optibootData->conn != NULL)
optibootData->conn->cgiPrivData = (void *)-1; // signal that request has been aborted
if (optibootData->pageBuf) os_free(optibootData->pageBuf);
if (optibootData->saved) os_free(optibootData->saved);
os_free(optibootData);
optibootData = NULL;
}
os_timer_disarm(&optibootTimer);
DBG("OB init\n");
}
//===== Cgi to reset AVR and get Optiboot into sync
int ICACHE_FLASH_ATTR cgiOptibootSync(HttpdConnData *connData) {
if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
// check that we know the reset pin, else error out with that
if (flashConfig.reset_pin < 0) {
errorResponse(connData, 400, "No reset pin defined");
} else if (connData->requestType == HTTPD_METHOD_POST) {
// issue reset
optibootInit();
serbridgeReset();
makeGpio(5);
gpio_output_set(0, (1<<5), (1<<5), 0); // output 0
// start sync timer
os_timer_disarm(&optibootTimer);
os_timer_setfn(&optibootTimer, optibootTimerCB, NULL);
os_timer_arm(&optibootTimer, 50, 0); // fire in 50ms and don't recur
// respond with optimistic OK
noCacheHeaders(connData, 204);
httpdEndHeaders(connData);
httpdSend(connData, "", 0);
} else if (connData->requestType == HTTPD_METHOD_GET) {
noCacheHeaders(connData, 200);
httpdEndHeaders(connData);
if (!errMessage[0] && progState >= stateProg) {
DBG("OB got sync\n");
char buf[32];
os_sprintf(buf, "SYNC : Optiboot %d.%d", optibootVers>>8, optibootVers&0xff);
httpdSend(connData, buf, -1);
} else if (errMessage[0] && progState == stateSync) {
DBG("OB lost sync\n");
char buf[RESP_SZ+64];
os_sprintf(buf, "FAILED to SYNC: %s, got <%s>", errMessage, responseBuf);
httpdSend(connData, buf, -1);
} else {
httpdSend(connData, errMessage[0] ? errMessage : "NOT READY", -1);
}
} else {
errorResponse(connData, 404, "Only GET and POST supported");
}
return HTTPD_CGI_DONE;
}
// verify that N chars are hex characters
static bool ICACHE_FLASH_ATTR checkHex(char *buf, short len) {
while (len--) {
char c = *buf++;
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
continue;
DBG("OB non-hex\n");
os_sprintf(errMessage, "Non hex char in POST record: '%c'/0x%02x", c, c);
return false;
}
return true;
}
// get hex value of some hex characters
static uint32_t ICACHE_FLASH_ATTR getHexValue(char *buf, short len) {
uint32_t v = 0;
while (len--) {
v = (v<<4) | (uint32_t)(*buf & 0xf);
if (*buf > '9') v += 9;
buf++;
}
return v;
}
//===== Cgi to write firmware to Optiboot, requires prior sync call
int ICACHE_FLASH_ATTR cgiOptibootData(HttpdConnData *connData) {
if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
DBG("OB pgm: state=%d PrivData=%p postLen=%d\n", progState, connData->cgiPrivData, connData->post->len);
// check that we have sync
if (errMessage[0] || progState < stateProg) {
DBG("OB not in sync\n");
errorResponse(connData, 400, errMessage[0] ? errMessage : "Optiboot not in sync");
return HTTPD_CGI_DONE;
}
// check that we don't have two concurrent programming requests going on
if (connData->cgiPrivData == (void *)-1) {
DBG("OB aborted\n");
errorResponse(connData, 400, "Request got aborted by a concurrent sync request");
return HTTPD_CGI_DONE;
}
// allocate data structure to track programming
if (!optibootData) {
optibootData = os_zalloc(sizeof(struct optibootData));
char *saved = os_zalloc(MAX_SAVED+1); // need space for string terminator
char *pageBuf = os_zalloc(MAX_PAGE_SZ+MAX_SAVED/2);
if (!optibootData || !pageBuf || !saved) {
errorResponse(connData, 400, "Out of memory");
return HTTPD_CGI_DONE;
}
optibootData->pageBuf = pageBuf;
optibootData->saved = saved;
optibootData->startTime = system_get_time();
optibootData->pgmSz = 128; // hard coded for 328p for now, should be query string param
DBG("OB data alloc\n");
}
// iterate through the data received and program the AVR one block at a time
HttpdPostData *post = connData->post;
char *saved = optibootData->saved;
while (post->buffLen > 0) {
// first fill-up the saved buffer
short saveLen = strlen(saved);
if (saveLen < MAX_SAVED) {
short cpy = MAX_SAVED-saveLen;
if (cpy > post->buffLen) cpy = post->buffLen;
os_memcpy(saved+saveLen, post->buff, cpy);
saveLen += cpy;
saved[saveLen] = 0; // string terminator
os_memmove(post->buff, post->buff+cpy, post->buffLen-cpy);
post->buffLen -= cpy;
//DBG("OB cp %d buff->saved\n", cpy);
}
// process HEX records
while (saveLen >= 11) { // 11 is minimal record length
// skip any CR/LF
short skip = 0;
while (skip < saveLen && (saved[skip] == '\n' || saved[skip] == '\r'))
skip++;
if (skip > 0) {
// shift out cr/lf (keep terminating \0)
os_memmove(saved, saved+skip, saveLen+1-skip);
saveLen -= skip;
if (saveLen < 11) break;
DBG("OB skip %d cr/lf\n", skip);
}
// inspect whether we have a proper record start
if (saved[0] != ':') {
DBG("OB found non-: start\n");
os_sprintf(errMessage, "Expected start of record in POST data, got %s", saved);
errorResponse(connData, 400, errMessage);
optibootInit();
return HTTPD_CGI_DONE;
}
if (!checkHex(saved+1, 2)) {
errorResponse(connData, 400, errMessage);
optibootInit();
return HTTPD_CGI_DONE;
}
uint8_t recLen = getHexValue(saved+1, 2);
//DBG("OB record %d\n", recLen);
// process record
if (saveLen >= 11+recLen*2) {
if (!processRecord(saved, 11+recLen*2)) {
DBG("OB process err %s\n", errMessage);
errorResponse(connData, 400, errMessage);
optibootInit();
return HTTPD_CGI_DONE;
}
short shift = 11+recLen*2;
os_memmove(saved, saved+shift, saveLen+1-shift);
saveLen -= shift;
//DBG("OB %d byte record\n", shift);
} else {
break;
}
}
}
short code;
if (post->received < post->len) {
//DBG("OB pgm need more\n");
return HTTPD_CGI_MORE;
}
if (optibootData->eof) {
// tell optiboot to reboot into the sketch
uart0_write_char(STK_LEAVE_PROGMODE);
uart0_write_char(CRC_EOP);
code = 200;
// calculate some stats
float dt = ((system_get_time() - optibootData->startTime)/1000)/1000.0; // in seconds
uint16_t pgmDone = optibootData->pgmDone;
optibootInit();
os_sprintf(errMessage, "Success. %d bytes in %d.%ds, %dB/s %d%% efficient",
pgmDone, (int)dt, (int)(dt*10)%10, (int)(pgmDone/dt),
(int)(100.0*(10.0*pgmDone/flashConfig.baud_rate)/dt));
} else {
code = 400;
optibootInit();
os_strcpy(errMessage, "Improperly terminated POST data");
}
DBG("OB pgm done: %d -- %s\n", code, errMessage);
noCacheHeaders(connData, code);
httpdEndHeaders(connData);
httpdSend(connData, errMessage, -1);
errMessage[0] = 0;
return HTTPD_CGI_DONE;
}
// verify checksum
static bool ICACHE_FLASH_ATTR verifyChecksum(char *buf, short len) {
uint8_t sum = 0;
while (len >= 2) {
sum += (uint8_t)getHexValue(buf, 2);
buf += 2;
len -= 2;
}
return sum == 0;
}
// Process a hex record -- assumes that the records starts with ':' & hex length
static bool ICACHE_FLASH_ATTR processRecord(char *buf, short len) {
buf++; len--; // skip leading ':'
// check we have all hex chars
if (!checkHex(buf, len)) return false;
// verify checksum
if (!verifyChecksum(buf, len)) {
buf[len] = 0;
os_sprintf(errMessage, "Invalid checksum for record %s", buf);
return false;
}
// dispatch based on record type
uint8_t type = getHexValue(buf+6, 2);
switch (type) {
case 0x00: { // data
//DBG("OB REC data %ld pglen=%d\n", getHexValue(buf, 2), optibootData->pageLen);
uint32_t addr = getHexValue(buf+2, 4);
// check whether we need to program previous record(s)
if (optibootData->pageLen > 0 &&
addr != ((optibootData->address+optibootData->pageLen)&0xffff)) {
//DBG("OB addr chg\n");
if (!programPage()) return false;
}
// set address, unless we're adding to the end (programPage may have changed pageLen)
if (optibootData->pageLen == 0) {
optibootData->address = (optibootData->address & 0xffff0000) | addr;
//DBG("OB set-addr 0x%lx\n", optibootData->address);
}
// append record
uint16_t recLen = getHexValue(buf, 2);
for (uint16_t i=0; i<recLen; i++)
optibootData->pageBuf[optibootData->pageLen++] = getHexValue(buf+8+2*i, 2);
// program page, if we have a full page
if (optibootData->pageLen >= optibootData->pgmSz) {
//DBG("OB full\n");
if (!programPage()) return false;
}
break; }
case 0x01: // EOF
DBG("OB EOF\n");
// program any remaining partial page
if (optibootData->pageLen > 0)
if (!programPage()) return false;
optibootData->eof = true;
break;
case 0x04: // address
DBG("OB address 0x%lx\n", getHexValue(buf+8, 4) << 16);
// program any remaining partial page
if (optibootData->pageLen > 0)
if (!programPage()) return false;
optibootData->address = getHexValue(buf+8, 4) << 16;
break;
case 0x05: // start address
// ignore, there's no way to tell optiboot that...
break;
default:
DBG("OB bad record type\n");
os_sprintf(errMessage, "Invalid/unknown record type: 0x%02x", type);
return false;
}
return true;
}
// Poll UART for ACKs, max 50ms
static bool pollAck() {
char recv[16];
uint16_t need = ackWait*2;
uint16_t got = uart0_rx_poll(recv, need, 50000);
gpio_output_set(0, (1<<5), (1<<5), 0); // output 0
if (got < need) {
os_strcpy(errMessage, "Timeout waiting for flash page to be programmed");
return false;
}
ackWait = 0;
if (recv[0] == STK_INSYNC && recv[1] == STK_OK)
return true;
os_sprintf(errMessage, "Did not get ACK after programming cmd: %x02x %x02x", recv[0], recv[1]);
return false;
}
// Program a flash page
static bool ICACHE_FLASH_ATTR programPage(void) {
if (optibootData->pageLen == 0) return true;
armTimer(); // keep the timerCB out of the picture
if (ackWait > 7) {
os_sprintf(errMessage, "Lost sync while programming\n");
return false;
}
uint16_t pgmLen = optibootData->pageLen;
if (pgmLen > optibootData->pgmSz) pgmLen = optibootData->pgmSz;
DBG("OB pgm %d@0x%lx ackWait=%d\n", pgmLen, optibootData->address, ackWait);
// send address to optiboot (little endian format)
gpio_output_set((1<<5), 0, (1<<5), 0); // output 1
ackWait++;
uart0_write_char(STK_LOAD_ADDRESS);
uint16_t addr = optibootData->address >> 1; // word address
uart0_write_char(addr & 0xff);
uart0_write_char(addr >> 8);
uart0_write_char(CRC_EOP);
armTimer();
if (!pollAck()) {
DBG("OB pgm failed in load address\n");
return false;
}
armTimer();
// send page length (big-endian format, go figure...)
gpio_output_set((1<<5), 0, (1<<5), 0); // output 1
ackWait++;
uart0_write_char(STK_PROG_PAGE);
uart0_write_char(pgmLen>>8);
uart0_write_char(pgmLen&0xff);
uart0_write_char('F'); // we're writing flash
// send page content
for (short i=0; i<pgmLen; i++)
uart0_write_char(optibootData->pageBuf[i]);
uart0_write_char(CRC_EOP);
armTimer();
bool ok = pollAck();
armTimer();
if (!ok) {
DBG("OB pgm failed in prog page\n");
return false;
}
// shift data out of buffer
os_memmove(optibootData->pageBuf, optibootData->pageBuf+pgmLen, optibootData->pageLen-pgmLen);
optibootData->pageLen -= pgmLen;
optibootData->address += pgmLen;
optibootData->pgmDone += pgmLen;
//DBG("OB pgm OK\n");
return true;
}
//===== Rebooting and getting sync
static void ICACHE_FLASH_ATTR armTimer() {
os_timer_disarm(&optibootTimer);
os_timer_arm(&optibootTimer, 100, 0); // 100ms
}
static void ICACHE_FLASH_ATTR optibootTimerCB(void *arg) {
// see whether we've issued so many sync in a row that it's time to give up
syncCnt++;
if ((progState == stateSync && syncCnt > SYNC_TIMEOUT/50) ||
syncCnt > PGM_TIMEOUT/50) {
optibootInit();
strcpy(errMessage, "abandoned after timeout");
return;
}
switch (progState) {
case stateSync: // we're trying to get sync, all we do here is send a sync request
uart0_write_char(STK_GET_SYNC);
uart0_write_char(CRC_EOP);
break;
case stateProg: // we're programming and we timed-out of inaction
uart0_write_char(STK_GET_SYNC);
uart0_write_char(CRC_EOP);
ackWait++; // we now expect an ACK
break;
default: // we're trying to get some info from optiboot and it should have responded!
optibootInit(); // abort
os_sprintf(errMessage, "No response in state %d\n", progState);
return; // do not re-arm timer
}
// we need to come back...
armTimer();
}
// skip in-sync responses
static short ICACHE_FLASH_ATTR skipInSync(char *buf, short length) {
while (length > 1 && buf[0] == STK_INSYNC && buf[1] == STK_OK) {
// not the most efficient, oh well...
os_memcpy(buf, buf+2, length-2);
length -= 2;
}
return length;
}
// receive response from optiboot, we only store the last response
static void ICACHE_FLASH_ATTR optibootUartRecv(char *buf, short length) {
// append what we got to what we have accumulated
if (responseLen < RESP_SZ-1) {
short cpy = RESP_SZ-1-responseLen;
if (cpy > length) cpy = length;
os_memcpy(responseBuf+responseLen, buf, cpy);
responseLen += cpy;
responseBuf[responseLen] = 0; // string terminator
}
// dispatch based the current state
switch (progState) {
case stateSync: // we're trying to get a sync response
// look for STK_INSYNC+STK_OK at end of buffer
if (responseLen > 0 && responseBuf[responseLen-1] == STK_INSYNC) {
// missing STK_OK after STK_INSYNC, shift stuff out and try again
responseBuf[0] = STK_INSYNC;
responseLen = 1;
} else if (responseLen > 1 && responseBuf[responseLen-2] == STK_INSYNC &&
responseBuf[responseLen-1] == STK_OK) {
// got sync response, send signature request
progState++;
os_memcpy(responseBuf, responseBuf+2, responseLen-2);
responseLen -= 2;
uart0_write_char(STK_READ_SIGN);
uart0_write_char(CRC_EOP);
armTimer(); // reset timer
} else {
// nothing useful, keep at most half the buffer for error message purposes
if (responseLen > RESP_SZ/2) {
os_memcpy(responseBuf, responseBuf+responseLen-RESP_SZ/2, RESP_SZ/2);
responseLen = RESP_SZ/2;
responseBuf[responseLen] = 0; // string terminator
}
}
break;
case stateGetSig: // expecting signature
responseLen = skipInSync(responseBuf, responseLen);
if (responseLen >= 5 && responseBuf[0] == STK_INSYNC && responseBuf[4] == STK_OK) {
if (responseBuf[1] == 0x1e && responseBuf[2] == 0x95 && responseBuf[3] == 0x0f) {
// right on... ask for optiboot version
progState++;
uart0_write_char(STK_GET_PARAMETER);
uart0_write_char(0x82);
uart0_write_char(CRC_EOP);
armTimer(); // reset timer
} else {
optibootInit(); // abort
os_sprintf(errMessage, "Bad programmer signature: 0x%02x 0x%02x 0x%02x\n",
responseBuf[1], responseBuf[2], responseBuf[3]);
}
os_memcpy(responseBuf, responseBuf+5, responseLen-5);
responseLen -= 5;
}
break;
case stateGetVersLo: // expecting version
if (responseLen >= 3 && responseBuf[0] == STK_INSYNC && responseBuf[2] == STK_OK) {
optibootVers = responseBuf[1];
progState++;
os_memcpy(responseBuf, responseBuf+3, responseLen-3);
responseLen -= 3;
uart0_write_char(STK_GET_PARAMETER);
uart0_write_char(0x81);
uart0_write_char(CRC_EOP);
armTimer(); // reset timer
}
break;
case stateGetVersHi: // expecting version
if (responseLen >= 3 && responseBuf[0] == STK_INSYNC && responseBuf[2] == STK_OK) {
optibootVers |= responseBuf[1]<<8;
progState++;
os_memcpy(responseBuf, responseBuf+3, responseLen-3);
responseLen -= 3;
armTimer(); // reset timer
ackWait = 0;
}
break;
case stateProg: // count down expected sync responses
//DBG("UART recv %d\n", length);
while (responseLen >= 2 && responseBuf[0] == STK_INSYNC && responseBuf[1] == STK_OK) {
if (ackWait > 0) ackWait--;
os_memmove(responseBuf, responseBuf+2, responseLen-2);
responseLen -= 2;
}
armTimer(); // reset timer
default:
break;
}
}

@ -0,0 +1,11 @@
// Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo
#ifndef OPTIBOOT_H
#define OPTIBOOT_H
#include <httpd.h>
int ICACHE_FLASH_ATTR cgiOptibootSync(HttpdConnData *connData);
int ICACHE_FLASH_ATTR cgiOptibootData(HttpdConnData *connData);
#endif

@ -17,7 +17,7 @@ static int8_t map_asn[][5] = {
{ 0, -1, 2, -1, 0 }, // esp-01(AVR) { 0, -1, 2, -1, 0 }, // esp-01(AVR)
{ 0, 2, -1, -1, 0 }, // esp-01(ARM) { 0, 2, -1, -1, 0 }, // esp-01(ARM)
{ 13, 12, 14, 0, 0 }, // esp-br-rev -- for test purposes { 13, 12, 14, 0, 0 }, // esp-br-rev -- for test purposes
{ 3, 1, 0, 2, 1 }, // esp-link-12 { 1, 3, 0, 2, 1 }, // esp-link-12
}; };
static const int num_map_names = sizeof(map_names)/sizeof(char*); static const int num_map_names = sizeof(map_names)/sizeof(char*);
static const int num_map_func = sizeof(map_func)/sizeof(char*); static const int num_map_func = sizeof(map_func)/sizeof(char*);

@ -18,6 +18,8 @@ static int log_pos;
static bool log_no_uart; // start out printing to uart static bool log_no_uart; // start out printing to uart
static bool log_newline; // at start of a new line static bool log_newline; // at start of a new line
#define uart0_write_char uart1_write_char
// called from wifi reset timer to turn UART on when we loose wifi and back off // called from wifi reset timer to turn UART on when we loose wifi and back off
// when we connect to wifi AP. Here this is gated by the flash setting // when we connect to wifi AP. Here this is gated by the flash setting
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -64,20 +66,21 @@ log_read(void) {
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
log_write_char(char c) { log_write_char(char c) {
// Uart output unless disabled // log timestamp
if (!log_no_uart) {
if (log_newline) { if (log_newline) {
char buff[16]; char buff[16];
int l = os_sprintf(buff, "%6d> ", (system_get_time()/1000)%1000000); int l = os_sprintf(buff, "%6d> ", (system_get_time()/1000)%1000000);
for (int i=0; i<l; i++) if (!log_no_uart)
uart0_write_char(buff[i]); for (int i=0; i<l; i++) uart0_write_char(buff[i]);
if (1)
for (int i=0; i<l; i++) log_write(buff[i]);
log_newline = false; log_newline = false;
} }
if (c == '\n') log_newline = true;
// Uart output unless disabled
if (!log_no_uart) {
if (c == '\n') uart0_write_char('\r');
uart0_write_char(c); uart0_write_char(c);
if (c == '\n') {
log_newline = true;
uart0_write_char('\r');
}
} }
// Store in log buffer // Store in log buffer
if (c == '\n') log_write('\r'); if (c == '\n') log_write('\r');

@ -19,6 +19,7 @@
#include "cgitcp.h" #include "cgitcp.h"
#include "cgimqtt.h" #include "cgimqtt.h"
#include "cgiflash.h" #include "cgiflash.h"
#include "cgioptiboot.h"
#include "auth.h" #include "auth.h"
#include "espfs.h" #include "espfs.h"
#include "uart.h" #include "uart.h"
@ -46,6 +47,8 @@ HttpdBuiltInUrl builtInUrls[] = {
{ "/flash/next", cgiGetFirmwareNext, NULL }, { "/flash/next", cgiGetFirmwareNext, NULL },
{ "/flash/upload", cgiUploadFirmware, NULL }, { "/flash/upload", cgiUploadFirmware, NULL },
{ "/flash/reboot", cgiRebootFirmware, NULL }, { "/flash/reboot", cgiRebootFirmware, NULL },
{ "/pgm/sync", cgiOptibootSync, NULL },
{ "/pgm/upload", cgiOptibootData, NULL },
{ "/log/text", ajaxLog, NULL }, { "/log/text", ajaxLog, NULL },
{ "/log/dbg", ajaxLogDbg, NULL }, { "/log/dbg", ajaxLogDbg, NULL },
{ "/console/reset", ajaxConsoleReset, NULL }, { "/console/reset", ajaxConsoleReset, NULL },

@ -0,0 +1,44 @@
/* STK500 constants list, from AVRDUDE
*
* Trivial set of constants derived from Atmel App Note AVR061
* Not copyrighted. Released to the public domain.
*/
#define STK_OK 0x10
#define STK_FAILED 0x11 // Not used
#define STK_UNKNOWN 0x12 // Not used
#define STK_NODEVICE 0x13 // Not used
#define STK_INSYNC 0x14 // ' '
#define STK_NOSYNC 0x15 // Not used
#define ADC_CHANNEL_ERROR 0x16 // Not used
#define ADC_MEASURE_OK 0x17 // Not used
#define PWM_CHANNEL_ERROR 0x18 // Not used
#define PWM_ADJUST_OK 0x19 // Not used
#define CRC_EOP 0x20 // 'SPACE'
#define STK_GET_SYNC 0x30 // '0'
#define STK_GET_SIGN_ON 0x31 // '1'
#define STK_SET_PARAMETER 0x40 // '@'
#define STK_GET_PARAMETER 0x41 // 'A'
#define STK_SET_DEVICE 0x42 // 'B'
#define STK_SET_DEVICE_EXT 0x45 // 'E'
#define STK_ENTER_PROGMODE 0x50 // 'P'
#define STK_LEAVE_PROGMODE 0x51 // 'Q'
#define STK_CHIP_ERASE 0x52 // 'R'
#define STK_CHECK_AUTOINC 0x53 // 'S'
#define STK_LOAD_ADDRESS 0x55 // 'U'
#define STK_UNIVERSAL 0x56 // 'V'
#define STK_PROG_FLASH 0x60 // '`'
#define STK_PROG_DATA 0x61 // 'a'
#define STK_PROG_FUSE 0x62 // 'b'
#define STK_PROG_LOCK 0x63 // 'c'
#define STK_PROG_PAGE 0x64 // 'd'
#define STK_PROG_FUSE_EXT 0x65 // 'e'
#define STK_READ_FLASH 0x70 // 'p'
#define STK_READ_DATA 0x71 // 'q'
#define STK_READ_FUSE 0x72 // 'r'
#define STK_READ_LOCK 0x73 // 's'
#define STK_READ_PAGE 0x74 // 't'
#define STK_READ_SIGN 0x75 // 'u'
#define STK_READ_OSCCAL 0x76 // 'v'
#define STK_READ_FUSE_EXT 0x77 // 'w'
#define STK_READ_OSCCAL_EXT 0x78 // 'x'

@ -111,9 +111,15 @@ static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) {
uint32 dt = conn->startTime; uint32 dt = conn->startTime;
if (dt > 0) dt = (system_get_time() - dt) / 1000; if (dt > 0) dt = (system_get_time() - dt) / 1000;
if (conn->conn && conn->url) if (conn->conn && conn->url)
#if 0
os_printf("HTTP %s %s from %s -> %d in %ums, heap=%ld\n", os_printf("HTTP %s %s from %s -> %d in %ums, heap=%ld\n",
conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, conn->priv->from, conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, conn->priv->from,
conn->priv->code, dt, (unsigned long)system_get_free_heap_size()); conn->priv->code, dt, (unsigned long)system_get_free_heap_size());
#else
os_printf("HTTP %s %s: %d, %ums, h=%ld\n",
conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url,
conn->priv->code, dt, (unsigned long)system_get_free_heap_size());
#endif
#endif #endif
conn->conn = NULL; // don't try to send anything, the SDK crashes... conn->conn = NULL; // don't try to send anything, the SDK crashes...
@ -285,7 +291,8 @@ static void ICACHE_FLASH_ATTR xmitSendBuff(HttpdConnData *conn) {
sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen); sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen);
if (status != 0) { if (status != 0) {
#ifdef HTTPD_DBG #ifdef HTTPD_DBG
os_printf("%sERROR! espconn_sent returned %d\n", connStr, status); os_printf("%sERROR! espconn_sent returned %d, trying to send %d to %s\n",
connStr, status, conn->priv->sendBuffLen, conn->url);
#endif #endif
} }
conn->priv->sendBuffLen = 0; conn->priv->sendBuffLen = 0;
@ -341,6 +348,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
//See if we can find a CGI that's happy to handle the request. //See if we can find a CGI that's happy to handle the request.
while (1) { while (1) {
//Look up URL in the built-in URL table. //Look up URL in the built-in URL table.
if (conn->cgi == NULL) {
while (builtInUrls[i].url != NULL) { while (builtInUrls[i].url != NULL) {
int match = 0; int match = 0;
//See if there's a literal match //See if there's a literal match
@ -365,9 +373,11 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
#endif #endif
httpdSend(conn, httpNotFoundHeader, -1); httpdSend(conn, httpNotFoundHeader, -1);
xmitSendBuff(conn); xmitSendBuff(conn);
conn->cgi = NULL; //mark for destruction conn->cgi = NULL; //mark for destruction.
if (conn->post) conn->post->len = 0; // skip any remaining receives
return; return;
} }
}
//Okay, we have a CGI function that matches the URL. See if it wants to handle the //Okay, we have a CGI function that matches the URL. See if it wants to handle the
//particular URL we're supposed to handle. //particular URL we're supposed to handle.
@ -380,7 +390,8 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
else if (r == HTTPD_CGI_DONE) { else if (r == HTTPD_CGI_DONE) {
//Yep, it's happy to do so and already is done sending data. //Yep, it's happy to do so and already is done sending data.
xmitSendBuff(conn); xmitSendBuff(conn);
conn->cgi = NULL; //mark conn for destruction conn->cgi = NULL; //mark for destruction.
if (conn->post) conn->post->len = 0; // skip any remaining receives
return; return;
} }
else if (r == HTTPD_CGI_NOTFOUND || r == HTTPD_CGI_AUTHENTICATED) { else if (r == HTTPD_CGI_NOTFOUND || r == HTTPD_CGI_AUTHENTICATED) {

@ -20,6 +20,7 @@ void ets_isr_unmask(unsigned intr);
int ets_memcmp(const void *s1, const void *s2, size_t n); int ets_memcmp(const void *s1, const void *s2, size_t n);
void *ets_memcpy(void *dest, const void *src, size_t n); void *ets_memcpy(void *dest, const void *src, size_t n);
void *ets_memmove(void *dest, const void *src, size_t n);
void *ets_memset(void *s, int c, size_t n); void *ets_memset(void *s, int c, size_t n);
int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3))); int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
int ets_str2macaddr(void *, void *); int ets_str2macaddr(void *, void *);

@ -20,6 +20,8 @@ static int8_t mcu_reset_pin, mcu_isp_pin;
extern uint8_t slip_disabled; // disable slip to allow flashing of attached MCU extern uint8_t slip_disabled; // disable slip to allow flashing of attached MCU
void (*programmingCB)(char *buffer, short length) = NULL;
// Connection pool // Connection pool
serbridgeConnData connData[MAX_CONN]; serbridgeConnData connData[MAX_CONN];
@ -97,7 +99,7 @@ telnetUnwrap(uint8_t *inBuf, int len, uint8_t state)
os_delay_us(100L); os_delay_us(100L);
} }
#ifdef SERBR_DBG #ifdef SERBR_DBG
else os_printf("MCU reset: no pin\n"); else { os_printf("MCU reset: no pin\n"); }
#endif #endif
break; break;
case DTR_OFF: case DTR_OFF:
@ -115,7 +117,7 @@ telnetUnwrap(uint8_t *inBuf, int len, uint8_t state)
os_delay_us(100L); os_delay_us(100L);
} }
#ifdef SERBR_DBG #ifdef SERBR_DBG
else os_printf("MCU isp: no pin\n"); else { os_printf("MCU isp: no pin\n"); }
#endif #endif
slip_disabled++; slip_disabled++;
break; break;
@ -147,7 +149,7 @@ serbridgeReset()
GPIO_OUTPUT_SET(mcu_reset_pin, 1); GPIO_OUTPUT_SET(mcu_reset_pin, 1);
} }
#ifdef SERBR_DBG #ifdef SERBR_DBG
else os_printf("MCU reset: no pin\n"); else { os_printf("MCU reset: no pin\n"); }
#endif #endif
} }
@ -346,7 +348,9 @@ console_process(char *buf, short len)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
serbridgeUartCb(char *buf, short length) serbridgeUartCb(char *buf, short length)
{ {
if (!flashConfig.slip_enable || slip_disabled > 0) { if (programmingCB) {
programmingCB(buf, length);
} else if (!flashConfig.slip_enable || slip_disabled > 0) {
//os_printf("SLIP: disabled got %d\n", length); //os_printf("SLIP: disabled got %d\n", length);
console_process(buf, length); console_process(buf, length);
} else { } else {

@ -36,4 +36,7 @@ void ICACHE_FLASH_ATTR serbridgeInitPins(void);
void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, short len); void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, short len);
void ICACHE_FLASH_ATTR serbridgeReset(); void ICACHE_FLASH_ATTR serbridgeReset();
// callback when receiving UART chars when in programming mode
extern void (*programmingCB)(char *buffer, short length);
#endif /* __SER_BRIDGE_H__ */ #endif /* __SER_BRIDGE_H__ */

@ -29,6 +29,7 @@ void ICACHE_FLASH_ATTR serledFlash(int duration) {
} }
void ICACHE_FLASH_ATTR serledInit(void) { void ICACHE_FLASH_ATTR serledInit(void) {
return;
int8_t pin = flashConfig.ser_led_pin; int8_t pin = flashConfig.ser_led_pin;
if (pin >= 0) { if (pin >= 0) {
makeGpio(pin); makeGpio(pin);

@ -242,6 +242,23 @@ uart_recvTask(os_event_t *events)
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();
} }
// Turn UART interrupts off and poll for nchars or until timeout hits
uint16_t ICACHE_FLASH_ATTR
uart0_rx_poll(char *buff, uint16_t nchars, uint32_t timeout_us) {
ETS_UART_INTR_DISABLE();
uint16_t got = 0;
uint32_t start = system_get_time(); // time in us
while (system_get_time()-start < timeout_us) {
while (READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
buff[got++] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
if (got == nchars) goto done;
}
}
done:
ETS_UART_INTR_ENABLE();
return got;
}
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
uart0_baud(int rate) { uart0_baud(int rate) {
os_printf("UART %d baud\n", rate); os_printf("UART %d baud\n", rate);

@ -8,19 +8,24 @@ typedef void (*UartRecv_cb)(char *buf, short len);
// Initialize UARTs to the provided baud rates (115200 recommended). This also makes the os_printf // Initialize UARTs to the provided baud rates (115200 recommended). This also makes the os_printf
// calls use uart1 for output (for debugging purposes) // calls use uart1 for output (for debugging purposes)
void ICACHE_FLASH_ATTR uart_init(UartBautRate uart0_br, UartBautRate uart1_br); void uart_init(UartBautRate uart0_br, UartBautRate uart1_br);
// Transmit a buffer of characters on UART0 // Transmit a buffer of characters on UART0
void ICACHE_FLASH_ATTR uart0_tx_buffer(char *buf, uint16 len); void uart0_tx_buffer(char *buf, uint16 len);
void ICACHE_FLASH_ATTR uart0_write_char(char c); void uart0_write_char(char c);
STATUS uart_tx_one_char(uint8 uart, uint8 c); STATUS uart_tx_one_char(uint8 uart, uint8 c);
void uart1_write_char(char c);
// Add a receive callback function, this is called on the uart receive task each time a chunk // Add a receive callback function, this is called on the uart receive task each time a chunk
// of bytes are received. A small number of callbacks can be added and they are all called // of bytes are received. A small number of callbacks can be added and they are all called
// with all new characters. // with all new characters.
void ICACHE_FLASH_ATTR uart_add_recv_cb(UartRecv_cb cb); void uart_add_recv_cb(UartRecv_cb cb);
// Turn UART interrupts off and poll for nchars or until timeout hits
uint16_t uart0_rx_poll(char *buff, uint16_t nchars, uint32_t timeout_us);
void ICACHE_FLASH_ATTR uart0_baud(int rate); void uart0_baud(int rate);
#endif /* __UART_H__ */ #endif /* __UART_H__ */

Loading…
Cancel
Save