You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
9.7 KiB
318 lines
9.7 KiB
/*! \file ELClientSocket.cpp |
|
\brief Constructor and functions for ELClientSocket |
|
\author BeeGee |
|
\version 1.0 |
|
\date 2016 |
|
\copyright GNU Public License. |
|
\warning Needs ESP-LINK V2.4 |
|
*/ |
|
|
|
#include "ELClientSocket.h" |
|
|
|
/*! ELClientSocket(ELClient *e) |
|
@brief Class to send/receive data |
|
@details The ELClientSocket class sends data over a Socket to a remote server or acts as a TCP socket server. |
|
Each instance is used to communicate with one server and multiple instances can be created to send to multiple servers. |
|
The ELClientSocket class does not support concurrent requests to the same server because only a single response can be recevied at a time and the responses of the two requests may arrive out of order. |
|
@param e |
|
Pointer to ELClient. Check ELClient API documentation. |
|
@par Example |
|
@code |
|
ELClientSocket socket(&esp); |
|
@endcode |
|
*/ |
|
ELClientSocket::ELClientSocket(ELClient *e) |
|
{ |
|
_elc = e; |
|
remote_instance = -1; |
|
} |
|
|
|
/*! socketCallback(void *res) |
|
@brief Callback function when data is sent, received or an error occured. |
|
@details The function is called when data is sent or received from the remote server. |
|
If a user callback function (userCb) was defined it is called and the response is sent as an argument. |
|
@note Internal library function |
|
@param res |
|
Pointer to ELClientResponse structure |
|
@warning The content of the response structure is overwritten when the next package arrives! |
|
*/ |
|
void ELClientSocket::socketCallback(void *res) |
|
{ |
|
if (!res) return; |
|
|
|
ELClientResponse *resp = (ELClientResponse *)res; |
|
|
|
#ifdef DEBUG_EN |
|
int argNum = resp->argc(); |
|
Serial.println("Number of arguments: "+String(argNum)); |
|
uint16_t _cmd = resp->cmd(); |
|
Serial.println("Command: "+String(_cmd)); |
|
uint16_t _value = resp->value(); |
|
Serial.println("Value: "+String(_value)); |
|
#endif |
|
|
|
resp->popArg(&_resp_type, 1); |
|
resp->popArg(&_client_num, 1); |
|
resp->popArg(&_len, 2); |
|
#ifdef DEBUG_EN |
|
Serial.print("Type: "); |
|
Serial.print(_resp_type); |
|
Serial.print(" client: "); |
|
Serial.print(_client_num); |
|
Serial.print(" size: "+String(_len)); |
|
#endif |
|
if (_resp_type == 1) |
|
{ |
|
#ifdef DEBUG_EN |
|
int argLen = resp->argLen(); |
|
Serial.print(" data length: "+String(argLen)); |
|
#endif |
|
resp->popArgPtr((void**)&_data); |
|
#ifdef DEBUG_EN |
|
_data[_len] = '\0'; |
|
Serial.print(" data: "+String(_data)); |
|
#endif |
|
} |
|
#ifdef DEBUG_EN |
|
Serial.println(""); |
|
#endif |
|
_status = 1; |
|
if (_hasUserCb) |
|
{ |
|
_userCb(_resp_type, _client_num, _len, _data); |
|
} |
|
} |
|
|
|
/*! begin(const char* host, uint16_t port, uint8_t sock_mode, void (*userCb)(uint8_t resp_type, uint8_t client_num, uint16_t len, char *data)) |
|
@brief Initialize communication to a remote server |
|
@details Initialize communication to a remote server, |
|
this communicates with esp-link but does not open a connection to the remote server. |
|
@param host |
|
Host to be connected. Can be a URL or an IP address in the format of xxx.xxx.xxx.xxx . |
|
@param port |
|
Port to be used to send/receive packets. Port MUST NOT be 80, 23 or 2323, as these ports are already used by EL-CLIENT on the ESP8266 |
|
@param sock_mode |
|
Set socket mode to SOCKET_TCP_CLIENT, SOCKET_TCP_CLIENT_LISTEN, SOCKET_TCP_SERVER or SOCKET_UDP |
|
@param (*userCb)(uint8_t resp_type, uint8_t client_num, uint16_t len, char *data) |
|
(optional) Pointer to callback function that is called if data after data has been sent, received or if an error occured |
|
@warning Port MUST NOT be 80, 23 or 2323, as these ports are already used by EL-CLIENT on the ESP8266. |
|
Max 4 connections are supported! |
|
@par Example1 |
|
@code |
|
// Setup a simple client to send data and disconnect after data was sent |
|
socketConnNum = socket.begin(socketServer, socketPort, SOCKET_TCP_CLIENT, socketCb); |
|
@endcode |
|
@par Example2 |
|
@code |
|
// Setup a client to send data and wait for response from remote server |
|
socketConnNum = socket.begin(socketServer, socketPort, SOCKET_TCP_CLIENT_LISTEN, socketCb); |
|
@endcode |
|
@par Example3 |
|
@code |
|
// Setup a TCP server and wait for a client to connect |
|
socketConnNum = socket.begin(socketServer, socketPort, SOCKET_TCP_SERVER, socketCb); |
|
@endcode |
|
@par Example3 |
|
@code |
|
// Setup a TCP server and wait for a client to connect |
|
socketConnNum = socket.begin(socketServer, socketPort, SOCKET_UDP, socketCb); |
|
@endcode |
|
*/ |
|
int ELClientSocket::begin(const char* host, uint16_t port, uint8_t sock_mode, void (*userCb)(uint8_t resp_type, uint8_t client_num, uint16_t len, char *data)) |
|
{ |
|
if (userCb != 0) |
|
{ |
|
_userCb = userCb; |
|
_hasUserCb = true; |
|
} |
|
|
|
socketCb.attach(this, &ELClientSocket::socketCallback); |
|
|
|
_elc->Request(CMD_SOCKET_SETUP, (uint32_t)&socketCb, 3); |
|
_elc->Request(host, strlen(host)); |
|
_elc->Request(&port, 2); |
|
_elc->Request(&sock_mode, 1); |
|
_elc->Request(); |
|
|
|
ELClientPacket *pkt = _elc->WaitReturn(); |
|
|
|
if (pkt && (int32_t)pkt->value >= 0) |
|
{ |
|
remote_instance = pkt->value; |
|
// return 0; |
|
} |
|
return (int)pkt->value; |
|
} |
|
|
|
/*! send(const char* data, int len) |
|
@brief Send data to the remote server. |
|
@param data |
|
Pointer to SOCKET packet |
|
@param len |
|
Length of SOCKET packet |
|
@par Example |
|
@code |
|
Serial.println("Sending JSON array to SOCKET server"); |
|
char socketPacket = "{"device":"spm","s":622.02,"c":-165.86}" |
|
socket.send(socketPacket, 39); |
|
@endcode |
|
*/ |
|
void ELClientSocket::send(const char* data, int len) |
|
{ |
|
_status = 0; |
|
if (remote_instance < 0) return; |
|
_elc->Request(CMD_SOCKET_SEND, remote_instance, 2); |
|
_elc->Request(data, strlen(data)); |
|
if (data != NULL && len > 0) |
|
{ |
|
_elc->Request(data, len); |
|
} |
|
|
|
_elc->Request(); |
|
} |
|
|
|
/*! send(const char* data) |
|
@brief Send null-terminated data to the remote server. |
|
@param data |
|
Pointer to SOCKET packet, must be null-terminated |
|
@par Example |
|
@code |
|
Serial.println("Sending text message to SOCKET server"); |
|
socket.send("Message from your Arduino Uno WiFi over TCP socket"); |
|
@endcode |
|
*/ |
|
void ELClientSocket::send(const char* data) |
|
{ |
|
send(data, strlen(data)); |
|
} |
|
|
|
/*! getResponse(uint8_t *resp_type, uint8_t *client_num, char* data, uint16_t maxLen) |
|
@brief Retrieve response. |
|
@details Check if a response from the remote server was received, |
|
returns the number of send or received bytes, |
|
0 if no response (may need to wait longer) |
|
@param resp_type |
|
Pointer to response type. Is USERCB_SENT if packet was sent or USERCB_RECV if a packet was received. |
|
@param client_num |
|
Pointer to connection number. Can be used to distinguish between different socket clients. |
|
@param data |
|
Pointer to buffer for received packet |
|
@param maxLen |
|
Size of buffer for received packet. If the received packet is larger than the buffer, the received packet will be truncated. |
|
@return <code>uint16_t</code> |
|
Size of received packet or number of sent bytes or 0 if no response |
|
@par Example |
|
@code |
|
#define BUFLEN 266 |
|
void loop() |
|
{ |
|
// process any callbacks coming from esp_link |
|
esp.Process(); |
|
// Check if we received a packet or if the last send request has finished |
|
char response[BUFLEN]; |
|
memset(response, 0, BUFLEN); |
|
uint8_t resp_type; |
|
uint8_t client_num; |
|
uint16_t len = socket.getResponse(&resp_type, &client_num, response, BUFLEN); |
|
if (len != 0) |
|
{ |
|
if (resp_type == USERCB_SENT) |
|
{ |
|
Serial.println("Sent "+String(len)+" bytes"); |
|
} |
|
else |
|
{ |
|
Serial.print("Received packet: "); |
|
for (int i=0; i<len; i++) |
|
{ |
|
Serial.print(response[i]); |
|
} |
|
Serial.println(""); |
|
} |
|
} |
|
} |
|
@endcode |
|
*/ |
|
uint16_t ELClientSocket::getResponse(uint8_t *resp_type, uint8_t *client_num, char* data, uint16_t maxLen) |
|
{ |
|
if (_status == 0) return 0; |
|
memcpy(data, _data, _len>maxLen?maxLen:_len); |
|
*resp_type = _resp_type; |
|
*client_num = _client_num; |
|
_status = 0; |
|
return _len; |
|
} |
|
|
|
/*! waitResponse(uint8_t *resp_type, uint8_t *client_num, char* data, uint16_t maxLen, uint32_t timeout) |
|
@brief Wait for the response |
|
@details Checks if a response from the remote server has arrived for <code>timeout</code> seconds, |
|
returns the number of send or received bytes, |
|
0 if no response (may need to wait longer) |
|
@warning Blocks the Arduino code for 5 seconds! not recommended to use. Use callback function instead! |
|
Received packet is NOT null-terminated |
|
@param resp_type |
|
Pointer to response type. Is USERCB_SENT if packet was sent or USERCB_RECV if a packet was received. |
|
@param client_num |
|
Pointer to connection number. Can be used to distinguish between different socket clients. |
|
@param data |
|
Pointer to buffer for received packet |
|
@param maxLen |
|
Size of buffer for received packet. If the received packet is larger than the buffer, the received packet will be truncated. |
|
@param timeout |
|
(optional) Timout in milli seconds to wait for a response, defaults to 5000ms |
|
@return <code>uint16_t</code> |
|
Size of received packet or number of sent bytes or 0 if no response |
|
@par Example |
|
@code |
|
#define BUFLEN 266 |
|
bool haveRemoteResponse = true; |
|
void loop() |
|
{ |
|
// process any callbacks coming from esp_link |
|
esp.Process(); |
|
if (haveRemoteResponse) // If last packet was sent, send a new one |
|
{ |
|
Serial.println("Sending JSON array to TCP server"); |
|
char socketPacket = "{"device":"spm","s":622.02,"c":-165.86}" |
|
socket.send(socketPacket, 39); |
|
haveRemoteResponse = false; |
|
} |
|
// Check if we received a packet or if the last send request has finished |
|
char response[BUFLEN]; |
|
memset(response, 0, BUFLEN); |
|
uint8_t resp_type; |
|
uint8_t client_num; |
|
uint16_t len = socket.waitResponse(&resp_type, &client_num, response, BUFLEN); |
|
if (len != 0) |
|
{ |
|
if (resp_type == USERCB_SENT) |
|
{ |
|
Serial.println("Sent "+String(len)+" bytes"); |
|
} |
|
else |
|
{ |
|
Serial.print("Received packet: "); |
|
for (int i=0; i<len; i++) |
|
{ |
|
Serial.print(response[i]); |
|
} |
|
Serial.println(""); |
|
haveRemoteResponse = true; |
|
} |
|
} |
|
} |
|
@endcode |
|
*/ |
|
uint16_t ELClientSocket::waitResponse(uint8_t *resp_type, uint8_t *client_num, char* data, uint16_t maxLen, uint32_t timeout) |
|
{ |
|
uint32_t wait = millis(); |
|
while (_status == 0) { |
|
if ( millis() - wait < timeout) |
|
{ |
|
_elc->Process(); |
|
} else { |
|
return -3; |
|
} |
|
} |
|
return getResponse(resp_type, client_num, data, maxLen); |
|
}
|
|
|