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.
esp-link/examples/arduino/libraries/EspLink/EspLink.cpp

208 lines
4.5 KiB

#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 = ((len+3)&~3) - len; // 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;
}