mirror of https://github.com/jeelabs/esp-link.git
parent
21fbbf6a8a
commit
f75c1acf95
@ -0,0 +1,207 @@ |
||||
#include "EspLink.h" |
||||
|
||||
#define READ_BUF_DFLT_SIZE 64 |
||||
|
||||
// Standard SLIP escape chars from RFC
|
||||
#define SLIP_END 0300 // indicates end of packet
|
||||
#define SLIP_ESC 0333 // indicates byte stuffing
|
||||
#define SLIP_ESC_END 0334 // ESC ESC_END means END data byte
|
||||
#define SLIP_ESC_ESC 0335 // ESC ESC_ESC means ESC data byte
|
||||
|
||||
EspLink::EspLink(Stream &streamIn, CmdRequestCB callback):stream(streamIn),requestCb(callback) |
||||
{ |
||||
readBuf = NULL; |
||||
readLastChar = 0; |
||||
} |
||||
|
||||
EspLink::~EspLink() |
||||
{ |
||||
if( readBuf != NULL ) |
||||
free( readBuf ); |
||||
readBuf = NULL; |
||||
} |
||||
|
||||
void EspLink::writeChar(uint8_t data) |
||||
{ |
||||
switch(data) |
||||
{ |
||||
case SLIP_END: |
||||
stream.write(SLIP_ESC); |
||||
stream.write(SLIP_ESC_END); |
||||
break; |
||||
case SLIP_ESC: |
||||
stream.write(SLIP_ESC); |
||||
stream.write(SLIP_ESC_ESC); |
||||
break; |
||||
default: |
||||
stream.write(data); |
||||
} |
||||
|
||||
crc16_add(data, &crc16_out); |
||||
} |
||||
|
||||
/* CITT CRC16 polynomial ^16 + ^12 + ^5 + 1 */ |
||||
/*---------------------------------------------------------------------------*/ |
||||
void EspLink::crc16_add(uint8_t b, uint16_t *crc) |
||||
{ |
||||
*crc ^= b; |
||||
*crc = (*crc >> 8) | (*crc << 8); |
||||
*crc ^= (*crc & 0xff00) << 4; |
||||
*crc ^= (*crc >> 8) >> 4; |
||||
*crc ^= (*crc & 0xff00) >> 5; |
||||
} |
||||
|
||||
void EspLink::writeBuf(uint8_t * buf, uint16_t len) |
||||
{ |
||||
while(len-- > 0) |
||||
writeChar(*buf++); |
||||
} |
||||
|
||||
void EspLink::sendPacketStart(uint16_t cmd, uint32_t value, uint16_t argc) |
||||
{ |
||||
crc16_out = 0; |
||||
stream.write( SLIP_END ); |
||||
writeBuf((uint8_t*)&cmd, 2); |
||||
writeBuf((uint8_t*)&argc, 2); |
||||
writeBuf((uint8_t*)&value, 4); |
||||
} |
||||
|
||||
void EspLink::sendPacketArg(uint16_t len, uint8_t * data) |
||||
{ |
||||
writeBuf((uint8_t*)&len, 2); |
||||
writeBuf(data, len); |
||||
|
||||
uint16_t pad = (4-((len+2)&3))&3; // get to multiple of 4
|
||||
if (pad > 0) { |
||||
uint32_t temp = 0; |
||||
writeBuf((uint8_t*)&temp, pad); |
||||
} |
||||
} |
||||
|
||||
void EspLink::sendPacketEnd() { |
||||
uint16_t crc = crc16_out; |
||||
writeBuf((uint8_t*)&crc, 2); |
||||
stream.write(SLIP_END); |
||||
} |
||||
|
||||
void EspLink::parseSlipPacket() |
||||
{ |
||||
CmdRequest req; |
||||
req.cmd = (CmdPacket *)readBuf; |
||||
req.arg_num = 0; |
||||
req.arg_ptr = readBuf + sizeof(CmdPacket); |
||||
|
||||
requestCb(&req); |
||||
|
||||
free(readBuf); |
||||
readBuf = NULL; |
||||
} |
||||
|
||||
void EspLink::checkPacket() |
||||
{ |
||||
if( readBufPtr <= 3 ) |
||||
return; |
||||
uint16_t crc = 0; |
||||
for(uint16_t i=0; i < readBufPtr - 2; i++) |
||||
crc16_add(readBuf[i], &crc); |
||||
|
||||
uint16_t crcpacket = *(uint16_t*)(readBuf + readBufPtr - 2); |
||||
|
||||
if( crc == crcpacket ) |
||||
{ |
||||
readBufPtr -= 2; |
||||
parseSlipPacket(); |
||||
} |
||||
} |
||||
|
||||
void EspLink::readLoop() |
||||
{ |
||||
if( stream.available() > 0 ) |
||||
{ |
||||
int byt = stream.read(); |
||||
|
||||
switch(readState) |
||||
{ |
||||
case WAIT_FOR_SLIP_START: |
||||
if( byt == SLIP_END ) |
||||
{ |
||||
if(readBuf != NULL) |
||||
free(readBuf); |
||||
readBufPtr = 0; |
||||
readBufMax = READ_BUF_DFLT_SIZE; |
||||
readBuf = (uint8_t *)malloc(readBufMax); |
||||
readState = READ_SLIP_PACKAGE; |
||||
} |
||||
break; |
||||
case READ_SLIP_PACKAGE: |
||||
if( byt == SLIP_END ) |
||||
{ |
||||
readState = WAIT_FOR_SLIP_START; |
||||
checkPacket(); |
||||
break; |
||||
} |
||||
if( byt == SLIP_ESC ) |
||||
break; |
||||
if( readLastChar == SLIP_ESC && byt == SLIP_ESC_END ) |
||||
byt = SLIP_END; |
||||
else if( readLastChar == SLIP_ESC && byt == SLIP_ESC_ESC ) |
||||
byt = SLIP_ESC; |
||||
|
||||
if( readBufPtr >= readBufMax ) |
||||
{ |
||||
readBufMax = readBufMax + READ_BUF_DFLT_SIZE; |
||||
readBuf = (uint8_t *)realloc(readBuf, readBufMax); |
||||
if( readBuf == NULL ) |
||||
{ |
||||
readState = WAIT_FOR_SLIP_START; // TODO
|
||||
break; |
||||
} |
||||
} |
||||
readBuf[readBufPtr++] = byt; |
||||
break; |
||||
} |
||||
|
||||
readLastChar = byt; |
||||
} |
||||
} |
||||
|
||||
// Return the number of arguments given a command struct
|
||||
uint32_t EspLink::cmdGetArgc(CmdRequest *req) { |
||||
return req->cmd->argc; |
||||
} |
||||
|
||||
// Copy the next argument from a command structure into the data pointer, returns 0 on success
|
||||
// -1 on error
|
||||
int32_t EspLink::cmdPopArg(CmdRequest *req, void *data, uint16_t len) { |
||||
uint16_t length; |
||||
|
||||
if (req->arg_num >= req->cmd->argc) |
||||
return -1; |
||||
|
||||
length = *(uint16_t*)req->arg_ptr; |
||||
if (length != len) return -1; // safety check
|
||||
|
||||
memcpy(data, req->arg_ptr + 2, length); |
||||
req->arg_ptr += (length+5)&~3; // round up to multiple of 4
|
||||
|
||||
req->arg_num ++; |
||||
return 0; |
||||
} |
||||
|
||||
// Skip the next argument
|
||||
void EspLink::cmdSkipArg(CmdRequest *req) { |
||||
uint16_t length; |
||||
|
||||
if (req->arg_num >= req->cmd->argc) return; |
||||
|
||||
length = *(uint16_t*)req->arg_ptr; |
||||
|
||||
req->arg_ptr += (length+5)&~3; |
||||
req->arg_num ++; |
||||
} |
||||
|
||||
// Return the length of the next argument
|
||||
uint16_t EspLink::cmdArgLen(CmdRequest *req) { |
||||
return *(uint16_t*)req->arg_ptr; |
||||
} |
||||
|
@ -0,0 +1,91 @@ |
||||
#ifndef ESP_LINK_H |
||||
#define ESP_LINK_H |
||||
|
||||
#include <inttypes.h> |
||||
#include <Stream.h> |
||||
|
||||
typedef struct __attribute__((__packed__)) { |
||||
uint16_t len; // length of data
|
||||
uint8_t data[0]; // really data[len]
|
||||
} CmdArg; |
||||
|
||||
typedef struct __attribute__((__packed__)) { |
||||
uint16_t cmd; // command to perform, from CmdName enum
|
||||
uint16_t argc; // number of arguments to command
|
||||
uint32_t value; // callback pointer for response or first argument
|
||||
CmdArg args[0]; // really args[argc]
|
||||
} CmdPacket; |
||||
|
||||
typedef struct { |
||||
CmdPacket *cmd; // command packet header
|
||||
uint32_t arg_num; // number of args parsed
|
||||
uint8_t *arg_ptr; // pointer to ??
|
||||
} CmdRequest; |
||||
|
||||
typedef void (* CmdRequestCB)(CmdRequest *); |
||||
|
||||
typedef enum { |
||||
CMD_NULL = 0, |
||||
CMD_SYNC, // synchronize and clear
|
||||
CMD_RESP_V, // response with a value
|
||||
CMD_RESP_CB, // response with a callback
|
||||
CMD_WIFI_STATUS, // get the current wifi status
|
||||
CMD_CB_ADD, |
||||
CMD_CB_EVENTS, |
||||
CMD_GET_TIME, // get current time in seconds since the unix epoch
|
||||
|
||||
CMD_MQTT_SETUP = 10, // set-up callbacks
|
||||
CMD_MQTT_PUBLISH, // publish a message
|
||||
CMD_MQTT_SUBSCRIBE, // subscribe to a topic
|
||||
CMD_MQTT_LWT, // set the last-will-topic and messge
|
||||
|
||||
CMD_REST_SETUP = 20, |
||||
CMD_REST_REQUEST, |
||||
CMD_REST_SETHEADER, |
||||
|
||||
CMD_WEB_DATA = 30, |
||||
CMD_WEB_REQ_CB, |
||||
} CmdName; |
||||
|
||||
typedef enum |
||||
{ |
||||
WAIT_FOR_SLIP_START, |
||||
READ_SLIP_PACKAGE, |
||||
} ReadState; |
||||
|
||||
class EspLink |
||||
{ |
||||
private: |
||||
uint16_t crc16_out; |
||||
Stream &stream; |
||||
ReadState readState; |
||||
uint8_t * readBuf; |
||||
uint16_t readBufPtr; |
||||
uint16_t readBufMax; |
||||
int readLastChar; |
||||
CmdRequestCB requestCb; |
||||
|
||||
void crc16_add(uint8_t b, uint16_t *crc); |
||||
void writeChar(uint8_t chr); |
||||
void writeBuf(uint8_t * buf, uint16_t len); |
||||
void checkPacket(); |
||||
void parseSlipPacket(); |
||||
|
||||
public: |
||||
EspLink(Stream &stream, CmdRequestCB callback); |
||||
~EspLink(); |
||||
|
||||
void sendPacketStart(uint16_t cmd, uint32_t value, uint16_t argc); |
||||
void sendPacketArg(uint16_t len, uint8_t * data); |
||||
void sendPacketEnd(); |
||||
|
||||
void readLoop(); |
||||
|
||||
uint32_t cmdGetArgc(CmdRequest *req); |
||||
int32_t cmdPopArg(CmdRequest *req, void *data, uint16_t len); |
||||
void cmdSkipArg(CmdRequest *req); |
||||
uint16_t cmdArgLen(CmdRequest *req); |
||||
}; |
||||
|
||||
#endif /* ESP_LINK_H */ |
||||
|
@ -0,0 +1,70 @@ |
||||
|
||||
#include "WebServer.h" |
||||
#include "EspLink.h" |
||||
|
||||
void packetReceived(CmdRequest *req); |
||||
|
||||
EspLink espLink(Serial, packetReceived); |
||||
|
||||
void packetReceived(CmdRequest *req) |
||||
{ |
||||
Serial.println("\nReceived\n"); |
||||
uint16_t shrt, port; |
||||
espLink.cmdPopArg(req, &shrt, 2); |
||||
RequestReason reason = (RequestReason)shrt; |
||||
Serial.print("Reason: "); |
||||
Serial.println(reason); |
||||
|
||||
uint8_t ip[4]; |
||||
espLink.cmdPopArg(req, &ip, 4); |
||||
Serial.print("IP: "); |
||||
for(int i=0; i < 4; i++) |
||||
{ |
||||
Serial.print(ip[i], DEC); |
||||
if( i != 3 ) |
||||
Serial.print("."); |
||||
} |
||||
Serial.println(); |
||||
|
||||
espLink.cmdPopArg(req, &port, 2); |
||||
Serial.print("Port: "); |
||||
Serial.println(port); |
||||
|
||||
{ |
||||
uint16_t len = espLink.cmdArgLen(req); |
||||
char bf[len+1]; |
||||
bf[len] = 0; |
||||
espLink.cmdPopArg(req, bf, len); |
||||
Serial.print("Url: "); |
||||
Serial.println(bf); |
||||
} |
||||
|
||||
switch( reason ) |
||||
{ |
||||
case BUTTON: |
||||
{ |
||||
uint16_t len = espLink.cmdArgLen(req); |
||||
char bf[len+1]; |
||||
bf[len] = 0; |
||||
espLink.cmdPopArg(req, bf, len); |
||||
Serial.print("Button: "); |
||||
Serial.println(bf); |
||||
} |
||||
break; |
||||
} |
||||
|
||||
} |
||||
|
||||
void setup() { |
||||
Serial.begin(57600); |
||||
|
||||
delay(10); |
||||
espLink.sendPacketStart(CMD_CB_ADD, 100, 1); |
||||
espLink.sendPacketArg(5, (uint8_t *)"webCb"); |
||||
espLink.sendPacketEnd(); |
||||
} |
||||
|
||||
void loop() { |
||||
espLink.readLoop(); |
||||
} |
||||
|
@ -0,0 +1,11 @@ |
||||
#ifndef WEB_SERVER_H |
||||
#define WEB_SERVER_H |
||||
|
||||
typedef enum { |
||||
LOAD=0, |
||||
REFRESH, |
||||
BUTTON, |
||||
SUBMIT, |
||||
} RequestReason; |
||||
|
||||
#endif /* WEB_SERVER_H */ |
Loading…
Reference in new issue