#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; }