rename to esp-link; add serial bridge code; remove jpgs

v0.9.0
Thorsten von Eicken 10 years ago
parent fd14d7e337
commit 42ba248831
  1. 2
      Makefile
  2. 18
      README
  3. BIN
      html/cats/cross-eyed-cat.jpg-
  4. BIN
      html/cats/junge-katze-iv.jpg-
  5. BIN
      html/cats/junge-katze-iv_01.jpg
  6. BIN
      html/cats/kitten-loves-toy.jpg-
  7. 72
      html/help.tpl
  8. 26
      html/index.tpl
  9. 4
      html/led.tpl
  10. 10
      html/style.css
  11. 3
      html/wifi/connecting.html
  12. 24
      html/wifi/style.css
  13. 11
      html/wifi/wifi.tpl
  14. 32
      include/uart_hw.h
  15. 224
      serial/serbridge.c
  16. 37
      serial/serbridge.h
  17. 271
      serial/uart.c
  18. 21
      serial/uart.h
  19. 30
      user/status.c
  20. 7
      user/status.h
  21. 33
      user/user_main.c

@ -63,7 +63,7 @@ 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
MODULES = espfs httpd user serial
EXTRA_INCDIR = include \
. \
lib/heatshrink/

@ -1,5 +1,5 @@
ESP8266 - Bridge
================
ESP-LINK
========
This firmware implements a transparent bridge between Wifi and serial using an ESP8266 module.
It also provides support for flash-programming Arduino/AVR microcontrollers as well as
@ -36,9 +36,9 @@ Wifi configuration
After you have serially flashed the module it will create a wifi access point (AP) with an
SSID of the form `ESP_012ABC` where 012ABC is a piece of the module's MAC address.
Using a laptop, phone, or tablet connect to this SSID and then open a browser pointed at
http://192.168.0.1, you should them see the esp8266-bridge web site.
http://192.168.0.1, you should them see the esp-link web site.
Now configure the wifi. The typical desired configuration is for the esp-bridge to be a
Now configure the wifi. The typical desired configuration is for the esp-link to be a
station on your local wifi network so can communicate with it from all your computers.
To make this happen, navigate to the wifi page and hit the "change to STA+AP mode" button.
@ -48,16 +48,16 @@ reconnect to the ESP_123ABC wifi network and refres the wifi settings page.
At this point you should see a list of detected networks on the web page and you can select
yours. Enter a password if your network is secure (recommended...) and hit the connect button.
You should now see that the esp-bridge has connected to your network and it should show you
You should now see that the esp-link has connected to your network and it should show you
its IP address. Write it down and then follow the provided link (you may have to switch your
laptop, phone, or tablet back to your network before you can actually connect).
At this point the esp-bridge will have switched to STA mode and be just a station on your
At this point the esp-link will have switched to STA mode and be just a station on your
wifi network. These settings are stored in flash and thereby remembered through resets and
power cycles. They are also remembered when you flash new firmware. Only flashing `blank.bin`
as indicated above will reset the wifi settings.
There is a fail-safe, which is that after a reset (need details) the esp-bridge will revert
There is a fail-safe, which is that after a reset (need details) the esp-link will revert
back to AP+STA mode and thus both present its ESP_012ABC-style network and try to connect to
the requested network, which will presumably not work or it wouldn't be in fail-safe mode
in the first place. You can then connect to the network and reconfigure the station part.
@ -81,7 +81,7 @@ Actual setup of the SDK and toolchain is out of the scope of this document, so I
enough to set up your own if you haven't already.
If you have that, you can clone out the source code:
git clone http://github.com/jeelabs/esp-bridge
git clone http://github.com/jeelabs/esp-link
This project makes use of heatshrink, which is a git submodule. To fetch the code:
cd esphttpd
@ -95,7 +95,7 @@ Flashing the firmware
---------------------
This firmware supports over-the-air (OTA) flashing, so you do not have to deal with serial
flashing again after the initial one! The recommended way to flash is to use `make wiflash`,
which assumes that you set ESP_HOSTNAME to the hostname or IP address of your esp-bridge.
which assumes that you set ESP_HOSTNAME to the hostname or IP address of your esp-link
The flashing, restart, and re-associating with your wireless network takes about 15 seconds
and is fully automatic. The 512KB flash are divided into two 236KB partitions allowing for new

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

@ -0,0 +1,72 @@
<html>
<head><title>Help - ESP Link</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="main">
<p><a href="/index.tpl">Home</a> | <a href="/wifi/wifi.tpl">Wifi</a> |
Serial | <a href="/led.tpl">LED</a></p>
<h1>ESP Link Help</h1>
The ESP Link functions in two wifi modes: Station+AccessPoint (STA+AP) and Station (STA).
In the STA+AP mode it presents a network called esp8266 that you can connect to using the
password jeelabs8266. This mode is intended for initial configuration, but it is
fully functional. Typically the easiest way to connect to the esp8266 network is using a phone,
tablet, or laptop.</p>
<p>The recommended next step is to configure the ESP Link to connect to your "normal"
Wifi network so you can access it from any of your machines. Once you have connected the ESP Link
to your network and pointed your browser at it successfully, you should
switch the ESP Link to STA mode, which is more secure (no canned password).<p>
<p>In STA mode the ESP Link connects to the configured network (the info is saved in flash).
If, after a reset, it cannot connect for one minute, it automatically reverts to STA+AP mode
allowing you to reconnect to the esp8266 network to change configuration.</p>
<p>In STA mode the most tricky part usually is the IP address. On most networks, the ESP Link
will get a dynamic IP address assigned via DHCP and you now need to enter that IP address into
your browser to connect. The good news is that after you reset your ESP Link it will continue to
have the same IP address. However, if you leave it off for the week-end it will most likely get a
fresh IP the next time it starts up. On many Wifi routers you can enter a fixed mapping from
the ESP Link's hardware MAC address to a static IP address so it always gets the same IP
address. This is the recommended method of operation.</p>
<h1>Using your ESP Serial Programmer</h1>
The ESP Programmer can used in several distinct ways:
<ul>
<li>as a transparent bridge between TCP port 23 and the serial port</li>
<li>as a web console to see input from the serial port</li>
<li>as an Arduino, AVR, or ARM processor programmer using serial-over-TCP</li>
<li>as an Arduino, AVR, or ARM processor programmer by uploading HEX files (not yet functional)</li>
</ul>
<h2>Transparent bridge</h2>
<p>The ESP accepts TCP connections to port 23 and "connects" through to the serial port.
Up to 5 simultaneous TCP connections are supported and characters coming in on the serial
port get passed through to all connections. Characters coming in on a connection get copied
through to the serial port.</p>
<p>When using Linux a simple way to use this is <tt>nc esp8266 23</tt></p>
<h2>Programmer using serial-over-TCP</h2>
<p>By hooking up the ESP's GPIO lines to the reset line of an Arduino (or AVR in general) that is
preloaded with the Optiboot bootloader/flasher it is possible to reprogram these processors over
Wifi. The way is works is that the ESP toggles the reset line each time a connection is established
and the first characters are the flash programming synchronization sequence.</p>
<p>When using Linux avrdude can be instructed to program an AVR over TCP by using a special syntax
for the serial port: <tt>-Pnet:esp8266:23</tt>, where <tt>esp8266</tt> is the hostname of the ESP
Serial Programmer (an IP address could have been used instead).</p>
<p>NXP's LPC800-serial ARM processors can be programmed similarly by hooking up GPIO pins to the
ARM's reset and ISP lines. The ESP Serial Programmer issues the correct reset/isp pulses to put
the ARM chip into firmware programming mode.</p>
<h2>Web Console</h2>
<p>The output of an attached Arduino/AVR/ARM can also be monitored via the console web page.
When connecting, it shows the most recent 10KB of characters received on the serial port and
then continues to print everything that comes in on the serial port. Eventually the page refreshes
when it gets very long. (Yes, this could be improved with some javascript...)</p>
<h2>Programmer using HEX upload</h2>
<p><i>(Not yet functional)</i> Instead of using the wifi-to-serial bridge to program
microcontrollers it is often faster to upload the HEX file to the ESP Serial Programmer and
have it perform the actual programming protocol.</p>
</div>
</body></html>

@ -1,25 +1,25 @@
<html>
<head><title>Esp8266 web server</title>
<head><title>ESP Link</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="main">
<h1>It Works</h1>
<p><a href="/index.tpl">Home</a> | <a href="/wifi/wifi.tpl">Wifi</a> |
Serial | <a href="/led.tpl">LED</a> | <a href="/help.tpl">Help</a></p>
<h1>ESP Link</h1>
<p>
If you see this, it means the tiny li'l website in your ESP8266 does actually work. Fyi, this page has
been loaded <b>%counter%</b> times.
The ESP Link connects the ESP's serial port to Wifi and it can
program microcontrollers over the serial port, in particular Arduinos, AVRs, and
NXP's LPC800-series ARM processors.</p>
<h1>Status</h1>
<ul>
<li>If you haven't connected this device to your WLAN network now, you can <a href="/wifi">do so.</a></li>
<li>You can also control the <a href="led.tpl">LED</a>.</li>
<li>You can download the raw <a href="flash.bin">contents</a> of the SPI flash rom</li>
<li>And because I can, here's a link to my <a href="http://spritesmods.com/?f=esphttpd">website</a></ul>
<li>This page has been loaded <b>%counter%</b> times</li>
<li>Manage <a href="/wifi">wifi</a></li>
<li>Control the <a href="led.tpl">LED</a></li>
</ul>
</p>
<p>And because we're on the Internets now, here are the required pictures of cats:<br />
<!--img src="cats/cross-eyed-cat.jpg"><br /-->
<img src="cats/junge-katze-iv_01.jpg"><br />
<!--img src="cats/kitten-loves-toy.jpg"><br /-->
</p>
</div>
</body></html>

@ -1,9 +1,9 @@
<html><head><title>Test</title>
<html><head><title>LED test - ESP Link</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="main">
<h1>The LED</h1>
<h1>ESP Link - LED test</h1>
<p>
If there's a LED connected to GPIO2, it's now %ledstate%. You can change that using the buttons below.
</p>

@ -2,10 +2,11 @@
body {
background-color: #404040;
font-family: sans-serif;
font-color: #663300;
}
#main {
background-color: #d0d0FF;
background-color: #FFFFCC;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
@ -15,3 +16,10 @@ body {
padding: 20px
}
.lock-icon {
background-image: url("/wifi/icons.png");
background-color: transparent;
width: 32px;
height: 32px;
display: inline-block;
}

@ -1,4 +1,4 @@
<html><head><title>Connecting...</title>
<html><head><title>Connecting... - ESP Link</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="140medley.min.js"></script>
<script type="text/javascript">
@ -34,6 +34,7 @@ window.onload=function(e) {
</head>
<body>
<div id="main">
<h1>ESP Link - Connecting</h1>
<h2>Connecting to AP...</h2>
<p>Status:<br />
<div id="status">...</div>

@ -1,24 +0,0 @@
body {
background-color: #404040;
font-family: sans-serif;
}
#main {
background-color: #d0d0FF;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
border: 2px solid #000000;
width: 800px;
margin: 0 auto;
padding: 20px
}
.icon {
background-image: url("icons.png");
background-color: transparent;
width: 32px;
height: 32px;
display: inline-block;
}

@ -1,5 +1,5 @@
<html><head><title>WiFi connection</title>
<link rel="stylesheet" type="text/css" href="style.css">
<html><head><title>WiFi connection - ESP Link</title>
<link rel="stylesheet" type="text/css" href="/style.css">
<script type="text/javascript" src="140medley.min.js"></script>
<script type="text/javascript">
@ -12,13 +12,13 @@ function createInputForAp(ap) {
div.id="apdiv";
var rssi=document.createElement("div");
var rssiVal=-Math.floor(ap.rssi/51)*32;
rssi.className="icon";
rssi.className="lock-icon";
rssi.style.backgroundPosition="0px "+rssiVal+"px";
var encrypt=document.createElement("div");
var encVal="-64"; //assume wpa/wpa2
if (ap.enc=="0") encVal="0"; //open
if (ap.enc=="1") encVal="-32"; //wep
encrypt.className="icon";
encrypt.className="lock-icon";
encrypt.style.backgroundPosition="-32px "+encVal+"px";
var input=document.createElement("input");
input.type="radio";
@ -74,6 +74,9 @@ window.onload=function(e) {
</head>
<body>
<div id="main">
<p><a href="/index.tpl">Home</a> | Wifi | Serial | <a href="/led.tpl">LED</a></p>
<h1>ESP Link - Wifi Configuration</h1>
<p>
Current WiFi mode: %WiFiMode%
</p>

@ -6,6 +6,7 @@
#ifndef UART_REGISTER_H_INCLUDED
#define UART_REGISTER_H_INCLUDED
#define REG_UART_BASE( i ) (0x60000000+(i)*0xf00)
//version value:32'h062000
@ -125,13 +126,18 @@
#define UART_DATE( i ) (REG_UART_BASE( i ) + 0x78)
#define UART_ID( i ) (REG_UART_BASE( i ) + 0x7C)
#endif // UART_REGISTER_H_INCLUDED
#define RX_BUFF_SIZE 256
#define TX_BUFF_SIZE 100
#define UART0 0
#define UART1 1
//calc bit 0..5 for UART_CONF0 register
#define CALC_UARTMODE(data_bits,parity,stop_bits) \
(((parity == NONE_BITS) ? 0x0 : (UART_PARITY_EN | (parity & UART_PARITY))) | \
((stop_bits & UART_STOP_BIT_NUM) << UART_STOP_BIT_NUM_S) | \
((data_bits & UART_BIT_NUM) << UART_BIT_NUM_S))
typedef enum {
FIVE_BITS = 0x0,
SIX_BITS = 0x1,
@ -180,6 +186,15 @@ typedef enum {
WRITE_OVER
} RcvMsgBuffState;
typedef struct {
uint32 RcvBuffSize;
uint8 *pRcvMsgBuff;
uint8 *pWritePos;
uint8 *pReadPos;
uint8 TrigLvl; //JLU: may need to pad
RcvMsgBuffState BuffState;
} RcvMsgBuff;
typedef struct {
uint32 TrxBuffSize;
uint8 *pTrxBuff;
@ -193,3 +208,18 @@ typedef enum {
RCV_ESC_CHAR,
} RcvMsgState;
typedef struct {
UartBautRate baut_rate;
UartBitsNum4Char data_bits;
UartExistParity exist_parity;
UartParityMode parity;
UartStopBitsNum stop_bits;
UartFlowCtrl flow_ctrl;
RcvMsgBuff rcv_buff;
TrxMsgBuff trx_buff;
RcvMsgState rcv_state;
int received;
int buff_uart_no; //indicate which uart use tx/rx buffer
} UartDevice;
#endif // UART_REGISTER_H_INCLUDED

@ -0,0 +1,224 @@
#include "espmissingincludes.h"
#include "c_types.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"
#include "osapi.h"
#include "gpio.h"
#include "uart.h"
#include "serbridge.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 struct espconn serbridgeConn;
static esp_tcp serbridgeTcp;
sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len);
// Connection pool
serbridgeConnData connData[MAX_CONN];
// Transmit buffers for the connection pool
static char txbuffer[MAX_CONN][MAX_TXBUFFER];
// Given a pointer to an espconn struct find the connection that correcponds to it
static serbridgeConnData ICACHE_FLASH_ATTR *serbridgeFindConnData(void *arg) {
for (int i=0; i<MAX_CONN; i++) {
if (connData[i].conn == (struct espconn *)arg) {
return &connData[i];
}
}
//os_printf("FindConnData: Huh? Couldn't find connection for %p\n", arg);
return NULL; // not found, may be closed already...
}
// Send all data in conn->txbuffer
// returns result from espconn_sent if data in buffer or ESPCONN_OK (0)
// Use only internally from espbuffsend and serbridgeSentCb
static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) {
sint8 result = ESPCONN_OK;
if (conn->txbufferlen != 0) {
//os_printf("%d TX %d\n", system_get_time(), conn->txbufferlen);
conn->readytosend = false;
result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen);
conn->txbufferlen = 0;
if (result != ESPCONN_OK) {
os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn);
}
}
return result;
}
// espbuffsend adds data to the send buffer. If the previous send was completed it calls
// sendtxbuffer and espconn_sent.
// Returns ESPCONN_OK (0) for success, -128 if buffer is full or error from espconn_sent
// Use espbuffsend instead of espconn_sent as it solves the problem that espconn_sent must
// only be called *after* receiving an espconn_sent_callback for the previous packet.
sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) {
if (conn->txbufferlen + len > MAX_TXBUFFER) {
os_printf("espbuffsend: txbuffer full on conn %p\n", conn);
return -128;
}
os_memcpy(conn->txbuffer + conn->txbufferlen, data, len);
conn->txbufferlen += len;
if (conn->readytosend) {
return sendtxbuffer(conn);
} else {
//os_printf("%d QU %d\n", system_get_time(), conn->txbufferlen);
}
return ESPCONN_OK;
}
//callback after the data are sent
static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) {
serbridgeConnData *conn = serbridgeFindConnData(arg);
//os_printf("Sent callback on conn %p\n", conn);
if (conn == NULL) return;
//os_printf("%d ST\n", system_get_time());
conn->readytosend = true;
sendtxbuffer(conn); // send possible new data in txbuffer
}
// Receive callback
static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) {
serbridgeConnData *conn = serbridgeFindConnData(arg);
//os_printf("Receive callback on conn %p\n", conn);
if (conn == NULL) return;
// at the start of a connection we're in cmInit mode and we wait for the first few characters
// to arrive in order to decide what type of connection this is.. The following if statements
// do this dispatch. An issue here is that we assume that the first few characters all arrive
// in the same TCP packet, which is true if the sender is a program, but not necessarily
// if the sender is a person typing (although in that case the line-oriented TTY input seems
// to make it work too). If this becomes a problem we need to buffer the first few chars...
if (conn->conn_mode == cmInit) {
// If the connection starts with the Arduino or ARM reset sequence we perform a RESET
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);
// send reset to arduino/ARM
GPIO_OUTPUT_SET(MCU_RESET, 0);
os_delay_us(100L);
GPIO_OUTPUT_SET(MCU_ISP, 0);
os_delay_us(1000L);
GPIO_OUTPUT_SET(MCU_RESET, 1);
os_delay_us(100L);
GPIO_OUTPUT_SET(MCU_ISP, 1);
os_delay_us(1000L);
//uart0_tx_buffer(data, len);
//conn->skip_chars = 2;
conn->conn_mode = cmAVR;
//return;
} else {
conn->conn_mode = cmTransparent;
}
}
uart0_tx_buffer(data, len);
}
// Error callback (it's really poorly named, it's not a "connection reconnected" callback,
// it's really a "connection broken, please reconnect" callback)
static void ICACHE_FLASH_ATTR serbridgeReconCb(void *arg, sint8 err) {
serbridgeConnData *conn=serbridgeFindConnData(arg);
if (conn == NULL) return;
// Close the connection
espconn_disconnect(conn->conn);
conn->conn = NULL;
}
// Disconnection callback
static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) {
// Iterate through all the connections and deallocate the ones that are in a state that
// indicates that they're closed
for (int i=0; i<MAX_CONN; i++) {
if (connData[i].conn != NULL &&
(connData[i].conn->state == ESPCONN_NONE || connData[i].conn->state == ESPCONN_CLOSE))
{
connData[i].conn = NULL;
}
}
}
// New connection callback, use one of the connection descriptors, if we have one left.
static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) {
struct espconn *conn = arg;
//Find empty conndata in pool
int i;
for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break;
os_printf("Accept port 23, conn=%p, pool slot %d\n", conn, i);
if (i==MAX_CONN) {
os_printf("Aiee, conn pool overflow!\n");
espconn_disconnect(conn);
return;
}
connData[i].conn=conn;
connData[i].txbufferlen = 0;
connData[i].readytosend = true;
connData[i].skip_chars = 0;
connData[i].conn_mode = cmInit;
espconn_regist_recvcb(conn, serbridgeRecvCb);
espconn_regist_reconcb(conn, serbridgeReconCb);
espconn_regist_disconcb(conn, serbridgeDisconCb);
espconn_regist_sentcb(conn, serbridgeSentCb);
}
// callback with a buffer of characters that have arrived on the uart
void ICACHE_FLASH_ATTR
serbridgeUartCb(char *buf, int length) {
// push the buffer into each open connection
int s = 0;
for (int i = 0; i < MAX_CONN; ++i) {
if (connData[i].conn) {
s++;
if (connData[i].skip_chars == 0) {
espbuffsend(&connData[i], buf, length);
} else if (connData[i].skip_chars >= length) {
connData[i].skip_chars -= length;
} else { // connData[i].skip_chars < length
espbuffsend(&connData[i], buf+connData[i].skip_chars, length-connData[i].skip_chars);
connData[i].skip_chars = 0;
}
}
}
}
// Start transparent serial bridge TCP server on specified port (typ. 23)
void ICACHE_FLASH_ATTR serbridgeInit(int port) {
int i;
for (i = 0; i < MAX_CONN; i++) {
connData[i].conn = NULL;
connData[i].txbuffer = txbuffer[i];
}
serbridgeConn.type = ESPCONN_TCP;
serbridgeConn.state = ESPCONN_NONE;
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
espconn_regist_connectcb(&serbridgeConn, serbridgeConnectCb);
espconn_accept(&serbridgeConn);
espconn_regist_time(&serbridgeConn, SER_BRIDGE_TIMEOUT, 0);
}

@ -0,0 +1,37 @@
#ifndef __SER_BRIDGE_H__
#define __SER_BRIDGE_H__
#include <ip_addr.h>
#include <c_types.h>
#include <espconn.h>
#define MAX_CONN 5
#define SER_BRIDGE_TIMEOUT 28799
//Max send buffer len
#define MAX_TXBUFFER 1024
typedef struct serbridgeConnData serbridgeConnData;
enum connModes {
cmInit = 0, // initialization mode: nothing received yet
cmTransparent, // transparent mode
cmAVR, // Arduino/AVR programming mode
cmARM, // ARM (LPC8xx) programming
cmEcho, // simply echo characters (used for debugging latency)
cmCommand, // AT command mode
};
struct serbridgeConnData {
struct espconn *conn;
enum connModes conn_mode; // connection mode
char *txbuffer; // buffer for the data to send
uint16 txbufferlen; // length of data in txbuffer
bool readytosend; // true, if txbuffer can send by espconn_sent
uint8 skip_chars; // number of chars to skip from uart, used in Arduino reset sequence
};
void ICACHE_FLASH_ATTR serbridgeInit(int port);
void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, int len);
#endif /* __SER_BRIDGE_H__ */

@ -0,0 +1,271 @@
/*
* File : uart.c
* This file is part of Espressif's AT+ command set program.
* Copyright (C) 2013 - 2016, Espressif Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of version 3 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "espmissingincludes.h"
#include "ets_sys.h"
#include "osapi.h"
#include "user_interface.h"
#include "uart.h"
#define recvTaskPrio 0
#define recvTaskQueueLen 64
// UartDev is defined and initialized in rom code.
extern UartDevice UartDev;
os_event_t recvTaskQueue[recvTaskQueueLen];
#define MAX_CB 4
static UartRecv_cb uart_recv_cb[4];
static void uart0_rx_intr_handler(void *para);
/******************************************************************************
* FunctionName : uart_config
* Description : Internal used function
* UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled
* UART1 just used for debug output
* Parameters : uart_no, use UART0 or UART1 defined ahead
* Returns : NONE
*******************************************************************************/
static void ICACHE_FLASH_ATTR
uart_config(uint8 uart_no)
{
if (uart_no == UART1) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
PIN_PULLDWN_DIS(PERIPHS_IO_MUX_GPIO2_U);
PIN_PULLUP_DIS(PERIPHS_IO_MUX_GPIO2_U);
} else {
/* rcv_buff size if 0x100 */
ETS_UART_INTR_ATTACH(uart0_rx_intr_handler, &(UartDev.rcv_buff));
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS);
}
uart_div_modify(uart_no, UART_CLK_FREQ / (UartDev.baut_rate));
if (uart_no == UART1) //UART 1 always 8 N 1
WRITE_PERI_REG(UART_CONF0(uart_no),
CALC_UARTMODE(EIGHT_BITS, NONE_BITS, ONE_STOP_BIT));
else
WRITE_PERI_REG(UART_CONF0(uart_no),
CALC_UARTMODE(UartDev.data_bits, UartDev.parity, UartDev.stop_bits));
//clear rx and tx fifo,not ready
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
if (uart_no == UART0) {
// Configure RX interrupt conditions as follows: trigger rx-full when there are 80 characters
// in the buffer (allows for 64-byte HEX records), trigger rx-timeout when the fifo is
// non-empty and nothing further has been received for 4 character period. Also set the
// hardware flow-control to trigger when the FIFO holds 100 characters, although we don't
// really expect the signals to actually be wired up to anything. It doesn't hurt to set
// the threshold here...
WRITE_PERI_REG(UART_CONF1(uart_no),
((80 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) |
((100 & UART_RX_FLOW_THRHD) << UART_RX_FLOW_THRHD_S) |
UART_RX_FLOW_EN |
(4 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S |
UART_RX_TOUT_EN);
SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA |
UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA);
} else {
WRITE_PERI_REG(UART_CONF1(uart_no),
((UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S));
}
//clear all interrupt
WRITE_PERI_REG(UART_INT_CLR(uart_no), 0xffff);
}
/******************************************************************************
* FunctionName : uart1_tx_one_char
* Description : Internal used function
* Use uart1 interface to transfer one char
* Parameters : uint8 TxChar - character to tx
* Returns : OK
*******************************************************************************/
static STATUS
uart_tx_one_char(uint8 uart, uint8 c)
{
//Wait until there is room in the FIFO
while (((READ_PERI_REG(UART_STATUS(uart))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=100) ;
//Send the character
WRITE_PERI_REG(UART_FIFO(uart), c);
return OK;
}
/******************************************************************************
* FunctionName : uart1_write_char
* Description : Internal used function
* Do some special deal while tx char is '\r' or '\n'
* Parameters : char c - character to tx
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
uart1_write_char(char c)
{
if (c == '\n') uart_tx_one_char(UART1, '\r');
uart_tx_one_char(UART1, c);
}
void ICACHE_FLASH_ATTR
uart0_write_char(char c)
{
if (c == '\n') uart_tx_one_char(UART0, '\r');
uart_tx_one_char(UART0, c);
}
/******************************************************************************
* FunctionName : uart0_tx_buffer
* Description : use uart0 to transfer buffer
* Parameters : uint8 *buf - point to send buffer
* uint16 len - buffer len
* Returns :
*******************************************************************************/
void ICACHE_FLASH_ATTR
uart0_tx_buffer(char *buf, uint16 len)
{
uint16 i;
for (i = 0; i < len; i++)
{
uart_tx_one_char(UART0, buf[i]);
}
}
/******************************************************************************
* FunctionName : uart0_sendStr
* Description : use uart0 to transfer buffer
* Parameters : uint8 *buf - point to send buffer
* uint16 len - buffer len
* Returns :
*******************************************************************************/
void ICACHE_FLASH_ATTR
uart0_sendStr(const char *str)
{
while(*str)
{
uart_tx_one_char(UART0, *str++);
}
}
/******************************************************************************
* FunctionName : uart0_rx_intr_handler
* Description : Internal used function
* UART0 interrupt handler, add self handle code inside
* Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg
* Returns : NONE
*******************************************************************************/
static void // must not use ICACHE_FLASH_ATTR !
uart0_rx_intr_handler(void *para)
{
/* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2,
* bit0 represents uart1 and uart0 respectively */
uint8 uart_no = UART0;//UartDev.buff_uart_no;
if(UART_FRM_ERR_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_FRM_ERR_INT_ST))
{
os_printf("FRM_ERR\r\n");
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR);
}
if(UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST))
{
//os_printf("fifo full\r\n");
ETS_UART_INTR_DISABLE();
system_os_post(recvTaskPrio, 0, 0);
}
else if(UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST))
{
ETS_UART_INTR_DISABLE();
//os_printf("stat:%02X",*(uint8 *)UART_INT_ENA(uart_no));
system_os_post(recvTaskPrio, 0, 0);
}
}
/******************************************************************************
* FunctionName : uart_recvTask
* Description : system task triggered on receive interrupt, empties FIFO and calls callbacks
*******************************************************************************/
static void ICACHE_FLASH_ATTR
uart_recvTask(os_event_t *events)
{
while (READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
//WRITE_PERI_REG(0X60000914, 0x73); //WTD // commented out by TvE
// read a buffer-full from the uart
uint16 length = 0;
char buf[128];
while ((READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) &&
(length < 128)) {
buf[length++] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
}
//os_printf("%d ix %d\n", system_get_time(), length);
for (int i=0; i<MAX_CB; i++) {
if (uart_recv_cb[i] != NULL) (uart_recv_cb[i])(buf, length);
}
}
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR|UART_RXFIFO_TOUT_INT_CLR);
ETS_UART_INTR_ENABLE();
}
/******************************************************************************
* FunctionName : uart_init
* Description : user interface for init uart
* Parameters : UartBautRate uart0_br - uart0 bautrate
* UartBautRate uart1_br - uart1 bautrate
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
uart_init(UartBautRate uart0_br, UartBautRate uart1_br)
{
// rom use 74880 baut_rate, here reinitialize
UartDev.baut_rate = uart0_br;
uart_config(UART0);
UartDev.baut_rate = uart1_br;
uart_config(UART1);
for (int i=0; i<4; i++) uart_tx_one_char(UART1, '\n');
for (int i=0; i<4; i++) uart_tx_one_char(UART0, '\n');
ETS_UART_INTR_ENABLE();
// install uart1 putc callback
os_install_putc1((void *)uart0_write_char);
system_os_task(uart_recvTask, recvTaskPrio, recvTaskQueue, recvTaskQueueLen);
}
void ICACHE_FLASH_ATTR
uart_add_recv_cb(UartRecv_cb cb) {
for (int i=0; i<MAX_CB; i++) {
if (uart_recv_cb[i] == NULL) {
uart_recv_cb[i] = cb;
return;
}
}
os_printf("UART: max cb count exceeded\n");
}
void ICACHE_FLASH_ATTR
uart_reattach()
{
uart_init(BIT_RATE_74880, BIT_RATE_74880);
// ETS_UART_INTR_ATTACH(uart_rx_intr_handler_ssc, &(UartDev.rcv_buff));
// ETS_UART_INTR_ENABLE();
}

@ -0,0 +1,21 @@
#ifndef __UART_H__
#define __UART_H__
#include "uart_hw.h"
// Receive callback function signature
typedef void (*UartRecv_cb)(char *buf, int len);
// Initialize UARTs to the provided baud rates (115200 recommended). This also makes the os_printf
// calls use uart1 for output (for debugging purposes)
void ICACHE_FLASH_ATTR uart_init(UartBautRate uart0_br, UartBautRate uart1_br);
// Transmit a buffer of characters on UART0
void ICACHE_FLASH_ATTR uart0_tx_buffer(char *buf, uint16 len);
// 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
// with all new characters.
void ICACHE_FLASH_ATTR uart_add_recv_cb(UartRecv_cb cb);
#endif /* __UART_H__ */

@ -0,0 +1,30 @@
#include <esp8266.h>
#define LEDGPIO 0
static ETSTimer ledTimer;
void ICACHE_FLASH_ATTR setLed(int on) {
if (on) {
gpio_output_set((1<<LEDGPIO), 0, (1<<LEDGPIO), 0);
} else {
gpio_output_set(0, (1<<LEDGPIO), (1<<LEDGPIO), 0);
}
}
static uint8_t ledState = 0;
static void ICACHE_FLASH_ATTR ledTimerCb(void *arg) {
//os_printf("Timer Hello\n");
setLed((ledState++)&1);
}
void statusInit() {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0);
gpio_output_set(0, 0, (1<<LEDGPIO), 0);
os_timer_disarm(&ledTimer);
os_timer_setfn(&ledTimer, ledTimerCb, NULL);
os_timer_arm(&ledTimer, 500, 1);
}

@ -0,0 +1,7 @@
#ifndef STATUS_H
#define STATUS_H
void statusInit(void);
#endif

@ -20,6 +20,12 @@
#include "stdout.h"
#include "auth.h"
#include "espfs.h"
#include "uart.h"
#include "serbridge.h"
#include "status.h"
#define MCU_RESET 12
#define MCU_ISP 13
#include <gpio.h>
//#define SHOW_HEAP_USE
@ -88,25 +94,34 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) {
#endif
// address of espfs binary blob
extern uint8_t _binary_espfs_img_start;
extern uint32_t _binary_espfs_img_start;
//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done.
void user_init(void) {
stdoutInit();
ioInit();
// init gpio pins used to reset&reprogram attached microcontrollers
gpio_init();
GPIO_OUTPUT_SET(MCU_ISP, 1);
GPIO_OUTPUT_SET(MCU_RESET, 0);
// init UART
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 esphttpd\n");
// 0x40200000 is the base address for spi flash memory mapping, ESPFS_POS is the position
// where image is written in flash that is defined in Makefile.
//EspFsInitResult res = espFsInit((void*)(0x40200000 + ESPFS_POS));
os_printf("\n\nInitializing esp-link\n");
// Status LEDs
statusInit();
// init the flash filesystem with the html stuff
os_delay_us(100000L);
EspFsInitResult res = espFsInit(&_binary_espfs_img_start);
os_printf("espFsInit(0x%08lx) returned %d\n", (uint32_t)&_binary_espfs_img_start, res);
// mount the http handlers
httpdInit(builtInUrls, 80);
// init the wifi-serial transparent bridge (port 23)
serbridgeInit(23);
uart_add_recv_cb(&serbridgeUartCb);
#ifdef SHOW_HEAP_USE
os_timer_disarm(&prHeapTimer);
os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL);
os_timer_arm(&prHeapTimer, 3000, 1);
#endif
os_printf("\nReady\n");
os_printf("** esp-link ready\n");
}

Loading…
Cancel
Save