Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement socket_bind and socket_accept #41

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 166 additions & 7 deletions ESP8266/ESP8266.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@

#include "ESP8266.h"

ESP8266::ESP8266(PinName tx, PinName rx, bool debug)
: _serial(tx, rx, 1024), _parser(_serial)
ESP8266::ESP8266(PinName tx, PinName rx, Callback<void(SignalingAction, int)> signalingCallback, bool debug)
: _serial(tx, rx, 1024), _parser(_serial), _signalingCallback(signalingCallback)
, _packets(0), _packets_end(&_packets)
{
_serial.baud(115200);
_parser.debugOn(debug);

_in_server_mode = false;
_ipd_packet = NULL;
_global_socket_counter = 0;
}

int ESP8266::get_firmware_version()
Expand All @@ -30,11 +34,11 @@ int ESP8266::get_firmware_version()
int version;
if(_parser.recv("SDK version:%d", &version) && _parser.recv("OK")) {
return version;
} else {
} else {
// Older firmware versions do not prefix the version with "SDK version: "
return -1;
}

}

bool ESP8266::startup(int mode)
Expand Down Expand Up @@ -211,6 +215,8 @@ bool ESP8266::send(int id, const void *data, uint32_t amount)

void ESP8266::_packet_handler()
{
if (_in_server_mode) return;

int id;
uint32_t amount;

Expand Down Expand Up @@ -241,6 +247,13 @@ void ESP8266::_packet_handler()

int32_t ESP8266::recv(int id, void *data, uint32_t amount)
{
bool exit_when_not_found = false;

// see if the underlying socket changed while in the recv() call
// if you don't do this check it might be that a CLOSED,CONNECT happens on the same esp8266 socket id
// and thus we associate the data with the wrong mbed TCPSocket
uint32_t incoming_socket_id = _incoming_socket_status[id];

while (true) {
// check if any packets are ready for us
for (struct packet **p = &_packets; *p; p = &(*p)->next) {
Expand Down Expand Up @@ -269,10 +282,37 @@ int32_t ESP8266::recv(int id, void *data, uint32_t amount)
}
}

// Wait for inbound packet
if (!_parser.recv("OK")) {
if (exit_when_not_found) {
return -1;
}

// Here's a problem... recv() blocks for forever, but it might be that the underlying socket closes in the meantime.
// We know when it happens (due to monitoring the RX IRQ channel)
// but there's no way of signaling this thread and actually abort the request...

if (incoming_socket_id > 0) {
int timeout = _parser.getTimeout();
_parser.setTimeout(1000);

if (!_parser.recv("OK")) {
_parser.setTimeout(timeout);

// socket gone
if (incoming_socket_id != _incoming_socket_status[id]) return NSAPI_ERROR_NO_SOCKET;

// otherwise, just continue trying to get data...
continue;
}
}
else {
// Wait for inbound packet
if (!_parser.recv("OK")) {
// so this is weird... the message just received by the parser could actually be one of ours (in TCPServer mode)...
// so do one more pass...
exit_when_not_found = true;
continue;
}
}
}
}

Expand Down Expand Up @@ -304,7 +344,7 @@ bool ESP8266::writeable()
return _serial.writeable();
}

void ESP8266::attach(Callback<void()> func)
void ESP8266::attach(Callback<void(int)> func)
{
_serial.attach(func);
}
Expand All @@ -320,3 +360,122 @@ bool ESP8266::recv_ap(nsapi_wifi_ap_t *ap)

return ret;
}

bool ESP8266::bind(const SocketAddress& address)
{
// we need an event queue to dispatch from serial RX IRQ -> non-IRQ thread
event_queue = new EventQueue();
event_thread = new Thread(osPriorityNormal, 2048);
if (!event_queue || !event_thread) {
return NSAPI_ERROR_NO_MEMORY;
}
event_thread->start(callback(event_queue, &EventQueue::dispatch_forever));

// buffer to store RX data in
rx_buffer = (char*)malloc(1024);
rx_ix = 0;
if (!rx_buffer) {
return NSAPI_ERROR_NO_MEMORY;
}

// clear incoming socket status
memset(_incoming_socket_status, 0, sizeof(_incoming_socket_status));

// attach to the serial
_serial.attach(callback(this, &ESP8266::attach_rx));

_in_server_mode = true;

// and start the actual server
return _parser.send("AT+CIPSERVER=1,%d", address.get_port())
&& _parser.recv("OK");
}

void ESP8266::process_command(char* cmd, size_t size) {
if (_ipd_packet) {
memcpy(_ipd_packet_data_ptr, cmd, size);
_ipd_packet_data_ptr += size;

_ipd_packet_data_ptr[0] = '\r';
_ipd_packet_data_ptr[1] = '\n';
_ipd_packet_data_ptr += 2;

if (_ipd_packet_data_ptr == ((char*)(_ipd_packet + 1)) + _ipd_packet->len) {
// append to packet list
*_packets_end = _ipd_packet;
_packets_end = &_ipd_packet->next;

_ipd_packet = NULL;
}
}
else if (size == 9 /* 0,CONNECT */
&& (cmd[0] >= '0' && cmd[0] <= '9')
&& (cmd[1] == ',')
&& (strcmp(&cmd[2], "CONNECT") == 0)) {

_incoming_socket_status[cmd[0] - '0'] = ++_global_socket_counter;

_signalingCallback(ESP8266_SOCKET_CONNECT, cmd[0] - '0');
}
else if (size == 8 /* 0,CLOSED */
&& (cmd[0] >= '0' && cmd[0] <= '9')
&& (cmd[1] == ',')
&& (strcmp(&cmd[2], "CLOSED") == 0)) {

_incoming_socket_status[cmd[0] - '0'] = 0;

_signalingCallback(ESP8266_SOCKET_CLOSE, cmd[0] - '0');
}
else if (cmd[0] == '+' && cmd[1] == 'I' && cmd[2] == 'P' && cmd[3] == 'D') {
int id = cmd[5] - '0';

// parse out the length param...
size_t length_ix = 6;
while (cmd[length_ix] != ':' && length_ix < size) length_ix++;
char* temp_length_buff = (char*)calloc(length_ix - 7 + 1, 1);
if (!temp_length_buff) return;
memcpy(temp_length_buff, cmd + 7, length_ix - 7);
int amount = atoi(temp_length_buff);

// alloc a new packet (and store it in a global var. we'll get the data for this packet straight after this msg)
_ipd_packet = (struct packet*)malloc(
sizeof(struct packet) + amount);
if (!_ipd_packet) {
return;
}

_ipd_packet->id = id;
_ipd_packet->len = amount;
_ipd_packet->next = 0;

_ipd_packet_data_ptr = (char*)(_ipd_packet + 1);

size_t data_len = size - length_ix - 1;
memcpy(_ipd_packet_data_ptr, cmd + length_ix + 1, data_len);
_ipd_packet_data_ptr += data_len;

// re-add the newline \r\n again...
_ipd_packet_data_ptr[0] = '\r';
_ipd_packet_data_ptr[1] = '\n';
_ipd_packet_data_ptr += 2;
}
free(cmd);
}

void ESP8266::attach_rx(int c) {
// store value in buffer
rx_buffer[rx_ix] = c;
rx_buffer[rx_ix + 1] = 0;

if (rx_ix > 0 && c == '\n') {
// got a whole command
char* cmd = (char*)calloc(rx_ix, 1);
memcpy(cmd, rx_buffer, rx_ix - 1);
event_queue->call(callback(this, &ESP8266::process_command), cmd, rx_ix - 1);

rx_ix = 0;
return;
}

rx_ix++;
}
39 changes: 33 additions & 6 deletions ESP8266/ESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,26 @@

#include "ATParser.h"

enum SignalingAction {
ESP8266_SOCKET_CONNECT,
ESP8266_SOCKET_CLOSE
};

/** ESP8266Interface class.
This is an interface to a ESP8266 radio.
*/
class ESP8266
{
public:
ESP8266(PinName tx, PinName rx, bool debug=false);
ESP8266(PinName tx, PinName rx, Callback<void(SignalingAction, int)> signalingCallback, bool debug=false);

/**
* Check firmware version of ESP8266
*
* @return integer firmware version or -1 if firmware query command gives outdated response
*/
int get_firmware_version(void);

/**
* Startup the ESP8266
*
Expand Down Expand Up @@ -97,7 +102,7 @@ class ESP8266

/** Get the local network mask
*
* @return Null-terminated representation of the local network mask
* @return Null-terminated representation of the local network mask
* or null if no network mask has been recieved
*/
const char *getNetmask();
Expand All @@ -123,7 +128,7 @@ class ESP8266
* see @a nsapi_error
*/
int scan(WiFiAccessPoint *res, unsigned limit);

/**Perform a dns query
*
* @param name Hostname to resolve
Expand Down Expand Up @@ -193,7 +198,7 @@ class ESP8266
*
* @param func A pointer to a void function, or 0 to set as none
*/
void attach(Callback<void()> func);
void attach(Callback<void(int)> func);

/**
* Attach a function to call whenever network state has changed
Expand All @@ -203,12 +208,23 @@ class ESP8266
*/
template <typename T, typename M>
void attach(T *obj, M method) {
attach(Callback<void()>(obj, method));
attach(Callback<void(int)>(obj, method));
}

/**
* Start binding to a port
*
* @param address SocketAddress instance (only port is being used)
*/
bool bind(const SocketAddress& address);

private:
void attach_rx(int);
void process_command(char*, size_t);

BufferedSerial _serial;
ATParser _parser;
Callback<void(SignalingAction, int)> _signalingCallback;

struct packet {
struct packet *next;
Expand All @@ -223,6 +239,17 @@ class ESP8266
char _gateway_buffer[16];
char _netmask_buffer[16];
char _mac_buffer[18];

// TCPServer mode needs a separate thread to dispatch RX IRQ commands from
EventQueue* event_queue;
Thread* event_thread;
char* rx_buffer;
size_t rx_ix;
uint32_t _incoming_socket_status[5];
uint32_t _global_socket_counter;
bool _in_server_mode;
struct packet *_ipd_packet;
char* _ipd_packet_data_ptr;
};

#endif
Loading