mirror of https://github.com/jeelabs/esp-link.git
parent
fd14d7e337
commit
42ba248831
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 11 KiB |
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> |
<html> |
||||||
<head><title>Esp8266 web server</title> |
<head><title>ESP Link</title> |
||||||
<link rel="stylesheet" type="text/css" href="style.css"> |
<link rel="stylesheet" type="text/css" href="style.css"> |
||||||
</head> |
</head> |
||||||
<body> |
<body> |
||||||
<div id="main"> |
<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> |
<p> |
||||||
If you see this, it means the tiny li'l website in your ESP8266 does actually work. Fyi, this page has |
The ESP Link connects the ESP's serial port to Wifi and it can |
||||||
been loaded <b>%counter%</b> times. |
program microcontrollers over the serial port, in particular Arduinos, AVRs, and |
||||||
|
NXP's LPC800-series ARM processors.</p> |
||||||
|
|
||||||
|
<h1>Status</h1> |
||||||
<ul> |
<ul> |
||||||
<li>If you haven't connected this device to your WLAN network now, you can <a href="/wifi">do so.</a></li> |
<li>This page has been loaded <b>%counter%</b> times</li> |
||||||
<li>You can also control the <a href="led.tpl">LED</a>.</li> |
<li>Manage <a href="/wifi">wifi</a></li> |
||||||
<li>You can download the raw <a href="flash.bin">contents</a> of the SPI flash rom</li> |
<li>Control the <a href="led.tpl">LED</a></li> |
||||||
<li>And because I can, here's a link to my <a href="http://spritesmods.com/?f=esphttpd">website</a></ul> |
|
||||||
</ul> |
</ul> |
||||||
</p> |
</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> |
</div> |
||||||
</body></html> |
</body></html> |
||||||
|
@ -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; |
|
||||||
} |
|
@ -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 |
||||||
|
|
Loading…
Reference in new issue