// 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_END: uart0_write_char(SLIP_ESC); uart0_write_char(SLIP_ESC_END); break; case SLIP_ESC: uart0_write_char(SLIP_ESC); uart0_write_char(SLIP_ESC_ESC); 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_END); 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; }