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