Skip to content

Commit

Permalink
Initial UART HW flow control support
Browse files Browse the repository at this point in the history
  • Loading branch information
Veijo Pesonen committed Aug 20, 2018
1 parent 4c58514 commit 28552b8
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 24 deletions.
97 changes: 81 additions & 16 deletions ESP8266/ESP8266.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,29 @@
*/

#include "ESP8266.h"
#include "mbed_debug.h"
#include "Callback.h"
#include "mbed_error.h"
#include "nsapi_types.h"
#include "PinNames.h"

#include <cstring>

#define ESP8266_DEFAULT_BAUD_RATE 115200
#define ESP8266_ALL_SOCKET_IDS -1

ESP8266::ESP8266(PinName tx, PinName rx, bool debug)
: _serial(tx, rx, ESP8266_DEFAULT_BAUD_RATE),
_parser(&_serial),
_packets(0),
ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
: _serial(tx, rx, ESP8266_DEFAULT_BAUD_RATE),
_serial_rts(rts),
_serial_cts(cts),
_parser(&_serial),
_packets(0),
_packets_end(&_packets),
_connect_error(0),
_fail(false),
_closed(false),
_socket_open(),
_connection_status(NSAPI_STATUS_DISCONNECTED)
_connection_status(NSAPI_STATUS_DISCONNECTED),
_heap_usage(0)
{
_serial.set_baud( ESP8266_DEFAULT_BAUD_RATE );
_parser.debug_on(debug);
Expand Down Expand Up @@ -70,6 +75,34 @@ int ESP8266::get_firmware_version()
}
}

bool ESP8266::start_uart_hw_flow_ctrl(void)
{
bool done = true;

if (_serial_rts != NC && _serial_cts != NC) {
// Start board's flow control
_serial.set_flow_control(SerialBase::RTSCTS, _serial_rts, _serial_cts);

// Start ESP8266's flow control
done = _parser.send("AT+UART_CUR=%u,8,1,0,3", ESP8266_DEFAULT_BAUD_RATE)
&& _parser.recv("OK\n");

} else if (_serial_rts != NC) {
_serial.set_flow_control(SerialBase::RTS, _serial_rts, NC);

done = _parser.send("AT+UART_CUR=%u,8,1,0,2", ESP8266_DEFAULT_BAUD_RATE)
&& _parser.recv("OK\n");

} else if (_serial_cts != NC) {
done = _parser.send("AT+UART_CUR=%u,8,1,0,1", ESP8266_DEFAULT_BAUD_RATE)
&& _parser.recv("OK\n");

_serial.set_flow_control(SerialBase::CTS, NC, _serial_cts);
}

return done;
}

bool ESP8266::startup(int mode)
{
if (!(mode == WIFIMODE_STATION || mode == WIFIMODE_SOFTAP
Expand Down Expand Up @@ -361,7 +394,10 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
if (_parser.send("AT+CIPSEND=%d,%lu", id, amount)
&& _parser.recv(">")
&& _parser.write((char*)data, (int)amount) >= 0) {
while (_parser.process_oob()); // multiple sends in a row require this
// No flow control, data overrun is possible
if (_serial_rts == NC) {
while (_parser.process_oob()); // Drain USART receive register
}
_smutex.unlock();
return NSAPI_ERROR_OK;
}
Expand All @@ -376,25 +412,37 @@ void ESP8266::_packet_handler()
{
int id;
int amount;
int pdu_len;

// parse out the packet
if (!_parser.recv(",%d,%d:", &id, &amount)) {
return;
}

struct packet *packet = (struct packet*)malloc(
sizeof(struct packet) + amount);
pdu_len = sizeof(struct packet) + amount;

if ((_heap_usage + pdu_len) > MBED_CONF_ESP8266_SOCKET_BUFSIZE) {
MBED_WARNING(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOBUFS), \
"ESP8266::_packet_handler(): \"esp8266.socket-bufsize\"-limit exceeded, packet dropped");
return;
}

struct packet *packet = (struct packet*)malloc(pdu_len);
if (!packet) {
debug("ESP8266: could not allocate memory for RX data\n");
MBED_WARNING(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOMEM), \
"ESP8266::_packet_handler(): Could not allocate memory for RX data");
return;
}
_heap_usage += pdu_len;

packet->id = id;
packet->len = amount;
packet->alloc_len = amount;
packet->next = 0;

if (_parser.read((char*)(packet + 1), amount) < amount) {
free(packet);
_heap_usage -= pdu_len;
return;
}

Expand All @@ -403,17 +451,23 @@ void ESP8266::_packet_handler()
_packets_end = &packet->next;
}

void ESP8266::process_oob(uint32_t timeout, bool all) {
setTimeout(timeout);
// Poll for inbound packets
while (_parser.process_oob() && all) {
}
setTimeout();
}

int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout)
{
_smutex.lock();
setTimeout(timeout);

// Poll for inbound packets
while (_parser.process_oob()) {
// No flow control, drain the USART receive register ASAP to avoid data overrun
if (_serial_rts == NC) {
process_oob(timeout, true);
}

setTimeout();

// check if any packets are ready for us
for (struct packet **p = &_packets; *p; p = &(*p)->next) {
if ((*p)->id == id) {
Expand All @@ -426,10 +480,13 @@ int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout)
_packets_end = p;
}
*p = (*p)->next;

_smutex.unlock();

uint32_t pdu_len = sizeof(struct packet) + q->alloc_len;
uint32_t len = q->len;
free(q);
_heap_usage -= pdu_len;
return len;
} else { // return only partial packet
memcpy(data, q+1, amount);
Expand All @@ -446,6 +503,11 @@ int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout)
_smutex.unlock();
return 0;
}

// Flow control, read from USART receive register only when no more data is buffered, and as little as possible
if (_serial_rts != NC) {
process_oob(timeout, false);
}
_smutex.unlock();

return NSAPI_ERROR_WOULD_BLOCK;
Expand Down Expand Up @@ -477,7 +539,9 @@ int32_t ESP8266::recv_udp(int id, void *data, uint32_t amount, uint32_t timeout)
*p = (*p)->next;
_smutex.unlock();

uint32_t pdu_len = sizeof(struct packet) + q->alloc_len;
free(q);
_heap_usage -= pdu_len;
return len;
}
}
Expand All @@ -493,13 +557,14 @@ void ESP8266::_clear_socket_packets(int id)
while (*p) {
if ((*p)->id == id || id == ESP8266_ALL_SOCKET_IDS) {
struct packet *q = *p;
int pdu_len = sizeof(struct packet) + q->alloc_len;

if (_packets_end == &(*p)->next) {
_packets_end = p; // Set last packet next field/_packets
}
*p = (*p)->next;

free(q);
_heap_usage -= pdu_len;
} else {
// Point to last packet next field
p = &(*p)->next;
Expand Down
16 changes: 14 additions & 2 deletions ESP8266/ESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
class ESP8266
{
public:
ESP8266(PinName tx, PinName rx, bool debug=false);
ESP8266(PinName tx, PinName rx, bool debug=false, PinName rts=NC, PinName cts=NC);

/**
* Check firmware version of ESP8266
Expand Down Expand Up @@ -265,20 +265,30 @@ class ESP8266
*/
nsapi_connection_status_t get_connection_status() const;

/**
* Start board's and ESP8266's UART flow control
*
* @return true if started
*/
bool start_uart_hw_flow_ctrl();

static const int8_t WIFIMODE_STATION = 1;
static const int8_t WIFIMODE_SOFTAP = 2;
static const int8_t WIFIMODE_STATION_SOFTAP = 3;
static const int8_t SOCKET_COUNT = 5;

private:
UARTSerial _serial;
PinName _serial_rts;
PinName _serial_cts;
ATCmdParser _parser;
Mutex _smutex; // Protect serial port access

struct packet {
struct packet *next;
int id;
uint32_t len;
uint32_t len; // Remaining length
uint32_t alloc_len; // Original length
// data follows
} *_packets, **_packets_end;
void _packet_handler();
Expand All @@ -292,6 +302,7 @@ class ESP8266
void _connection_status_handler();
void _oob_socket_close_error();
void _clear_socket_packets(int id);
void process_oob(uint32_t timeout, bool all);

char _ip_buffer[16];
char _gateway_buffer[16];
Expand All @@ -304,6 +315,7 @@ class ESP8266
int _socket_open[SOCKET_COUNT];
nsapi_connection_status_t _connection_status;
Callback<void(nsapi_event_t, intptr_t)> _connection_status_cb;
size_t _heap_usage;
};

#endif
9 changes: 6 additions & 3 deletions ESP8266Interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
#define ESP8266_VERSION 2

ESP8266Interface::ESP8266Interface()
: _esp(MBED_CONF_ESP8266_TX, MBED_CONF_ESP8266_RX, MBED_CONF_ESP8266_DEBUG),
: _esp(MBED_CONF_ESP8266_TX, MBED_CONF_ESP8266_RX, MBED_CONF_ESP8266_DEBUG, MBED_CONF_ESP8266_RTS, MBED_CONF_ESP8266_CTS),
_initialized(false),
_started(false)
{
Expand All @@ -57,8 +57,8 @@ ESP8266Interface::ESP8266Interface()
}

// ESP8266Interface implementation
ESP8266Interface::ESP8266Interface(PinName tx, PinName rx, bool debug)
: _esp(tx, rx, debug),
ESP8266Interface::ESP8266Interface(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
: _esp(tx, rx, debug, rts, cts),
_initialized(false),
_started(false)
{
Expand Down Expand Up @@ -267,6 +267,9 @@ bool ESP8266Interface::_disable_default_softap()
nsapi_error_t ESP8266Interface::_init(void)
{
if (!_initialized) {
if (!_esp.start_uart_hw_flow_ctrl()) {
return NSAPI_ERROR_DEVICE_ERROR;
}
if (!_esp.reset()) {
return NSAPI_ERROR_DEVICE_ERROR;
}
Expand Down
2 changes: 1 addition & 1 deletion ESP8266Interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ESP8266Interface : public NetworkStack, public WiFiInterface
* @param rx RX pin
* @param debug Enable debugging
*/
ESP8266Interface(PinName tx, PinName rx, bool debug = false);
ESP8266Interface(PinName tx, PinName rx, bool debug=false, PinName rts=NC, PinName cts=NC);

/** Start the interface
*
Expand Down
20 changes: 18 additions & 2 deletions mbed_lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@
"config": {
"tx": {
"help": "TX pin for serial connection",
"value": null
"value": "D1"
},
"rx": {
"help": "RX pin for serial connection",
"value": null
"value": "D0"
},
"rts": {
"help": "RTS pin for serial connection",
"value": "NC"
},
"cts": {
"help": "CTS pin for serial connection",
"value": "NC"
},
"debug": {
"help": "Enable debug logs",
Expand All @@ -16,6 +24,10 @@
"provide-default": {
"help": "Provide default WifiInterface. [true/false]",
"value": false
},
"socket-bufsize": {
"help": "Size of the socket data buffer",
"value": 102400
}
},
"target_overrides": {
Expand All @@ -30,6 +42,10 @@
"NUCLEO_F411RE": {
"tx": "D8",
"rx": "D2"
},
"NUCLEO_F429ZI": {
"rts": "NC",
"cts": "NC"
}
}
}

0 comments on commit 28552b8

Please sign in to comment.