// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
//
// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh
# include "esp8266.h"
# include "cmd.h"
# include "crc16.h"
# include "uart.h"
# ifdef CMD_DBG
# define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
static const char * cmd_names [ ] = {
" NULL " , " RESET " , " IS_READY " , " WIFI_CONNECT " ,
" MQTT_SETUP " , " MQTT_CONNECT " , " MQTT_DISCONNECT " ,
" MQTT_PUBLISH " , " MQTT_SUBSCRIBE " , " MQTT_LWT " , " MQTT_EVENTS " ,
" REST_SETUP " , " REST_REQUEST " , " REST_SETHEADER " , " REST_EVENTS " ,
" CB_ADD " , " CB_EVENTS " ,
} ;
# else
# define DBG(format, ...) do { } while(0)
# endif
extern const CmdList commands [ ] ;
//===== ESP -> Serial responses
static void ICACHE_FLASH_ATTR
CMD_ProtoWrite ( uint8_t data ) {
switch ( data ) {
case SLIP_START :
case SLIP_END :
case SLIP_REPL :
uart0_write_char ( SLIP_REPL ) ;
uart0_write_char ( SLIP_ESC ( data ) ) ;
break ;
default :
uart0_write_char ( data ) ;
}
}
static void ICACHE_FLASH_ATTR
CMD_ProtoWriteBuf ( uint8_t * data , short len ) {
while ( len - - ) CMD_ProtoWrite ( * data + + ) ;
}
// Start a response, returns the partial CRC
uint16_t ICACHE_FLASH_ATTR
CMD_ResponseStart ( uint16_t cmd , uint32_t callback , uint32_t _return , uint16_t argc ) {
uint16_t crc = 0 ;
uart0_write_char ( SLIP_START ) ;
CMD_ProtoWriteBuf ( ( uint8_t * ) & cmd , 2 ) ;
crc = crc16_data ( ( uint8_t * ) & cmd , 2 , crc ) ;
CMD_ProtoWriteBuf ( ( uint8_t * ) & callback , 4 ) ;
crc = crc16_data ( ( uint8_t * ) & callback , 4 , crc ) ;
CMD_ProtoWriteBuf ( ( uint8_t * ) & _return , 4 ) ;
crc = crc16_data ( ( uint8_t * ) & _return , 4 , crc ) ;
CMD_ProtoWriteBuf ( ( uint8_t * ) & argc , 2 ) ;
crc = crc16_data ( ( uint8_t * ) & argc , 2 , crc ) ;
return crc ;
}
// Adds data to a response, returns the partial CRC
uint16_t ICACHE_FLASH_ATTR
CMD_ResponseBody ( uint16_t crc_in , uint8_t * data , short len ) {
short pad_len = len + 3 - ( len + 3 ) % 4 ; // round up to multiple of 4
CMD_ProtoWriteBuf ( ( uint8_t * ) & pad_len , 2 ) ;
crc_in = crc16_data ( ( uint8_t * ) & pad_len , 2 , crc_in ) ;
CMD_ProtoWriteBuf ( data , len ) ;
crc_in = crc16_data ( data , len , crc_in ) ;
if ( pad_len > len ) {
uint32_t temp = 0 ;
CMD_ProtoWriteBuf ( ( uint8_t * ) & temp , pad_len - len ) ;
crc_in = crc16_data ( ( uint8_t * ) & temp , pad_len - len , crc_in ) ;
}
return crc_in ;
}
// Ends a response
void ICACHE_FLASH_ATTR
CMD_ResponseEnd ( uint16_t crc ) {
CMD_ProtoWriteBuf ( ( uint8_t * ) & crc , 2 ) ;
uart0_write_char ( SLIP_END ) ;
}
//===== serial -> ESP commands
// Execute a parsed command
static uint32_t ICACHE_FLASH_ATTR
CMD_Exec ( const CmdList * scp , CmdPacket * packet ) {
uint16_t crc = 0 ;
// Iterate through the command table and call the appropriate function
while ( scp - > sc_function ! = NULL ) {
if ( scp - > sc_name = = packet - > cmd ) {
DBG ( " CMD_Exec: Dispatching cmd=%s \n " , cmd_names [ packet - > cmd ] ) ;
// call command function
uint32_t ret = scp - > sc_function ( packet ) ;
// if requestor asked for a response, send it
if ( packet - > _return ) {
DBG ( " CMD_Exec: Response: 0x%lx, cmd: %d \r \n " , ret , packet - > cmd ) ;
crc = CMD_ResponseStart ( packet - > cmd , 0 , ret , 0 ) ;
CMD_ResponseEnd ( crc ) ;
} else {
DBG ( " CMD_Exec: no response (%lu) \n " , packet - > _return ) ;
}
return ret ;
}
scp + + ;
}
DBG ( " CMD_Exec: cmd=%d not found \n " , packet - > cmd ) ;
return 0 ;
}
// Parse a packet and print info about it
void ICACHE_FLASH_ATTR
CMD_parse_packet ( uint8_t * buf , short len ) {
// minimum command length
if ( len < 12 ) return ;
// init pointers into buffer
CmdPacket * packet = ( CmdPacket * ) buf ;
uint8_t * data_ptr = ( uint8_t * ) & packet - > args ;
uint8_t * data_limit = data_ptr + len ;
DBG ( " CMD_parse_packet: cmd=%d(%s) argc=%d cb=%p ret=%lu \n " ,
packet - > cmd ,
cmd_names [ packet - > cmd ] ,
packet - > argc ,
( void * ) packet - > callback ,
packet - > _return
) ;
#if 0
// print out arguments
uint16_t argn = 0 ;
uint16_t argc = packet - > argc ;
while ( data_ptr + 2 < data_limit & & argc - - ) {
short l = * ( uint16_t * ) data_ptr ;
os_printf ( " CMD_parse_packet: arg[%d] len=%d: " , argn + + , l ) ;
data_ptr + = 2 ;
while ( data_ptr < data_limit & & l - - ) {
os_printf ( " %02X " , * data_ptr + + ) ;
}
os_printf ( " \n " ) ;
}
# endif
if ( data_ptr < = data_limit ) {
CMD_Exec ( commands , packet ) ;
} else {
DBG ( " CMD_parse_packet: packet length overrun, parsing arg %d \n " , packet - > argc ) ;
}
}
//===== Helpers to parse a command packet
// Fill out a CmdRequest struct given a CmdPacket
void ICACHE_FLASH_ATTR
CMD_Request ( CmdRequest * req , CmdPacket * cmd ) {
req - > cmd = cmd ;
req - > arg_num = 0 ;
req - > arg_ptr = ( uint8_t * ) & cmd - > args ;
}
// Return the number of arguments given a command struct
uint32_t ICACHE_FLASH_ATTR
CMD_GetArgc ( 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 ICACHE_FLASH_ATTR
CMD_PopArg ( 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
req - > arg_ptr + = 2 ;
os_memcpy ( data , req - > arg_ptr , length ) ;
req - > arg_ptr + = length ;
req - > arg_num + + ;
return 0 ;
}
// Skip the next argument
void ICACHE_FLASH_ATTR
CMD_SkipArg ( CmdRequest * req ) {
uint16_t length ;
if ( req - > arg_num > = req - > cmd - > argc ) return ;
length = * ( uint16_t * ) req - > arg_ptr ;
req - > arg_ptr + = 2 ;
req - > arg_ptr + = length ;
req - > arg_num + + ;
}
// Return the length of the next argument
uint16_t ICACHE_FLASH_ATTR
CMD_ArgLen ( CmdRequest * req ) {
return * ( uint16_t * ) req - > arg_ptr ;
}