From 243115491954aa5087f62b376fc518caabfa9c86 Mon Sep 17 00:00:00 2001 From: Diego Date: Sat, 14 Apr 2018 23:35:50 -0300 Subject: [PATCH] V0.3 Large rewrite for better memory managment. Use char* instead of String TO(RE)DO : Multicomand messages, string parameters --- .../Ethernet_Instrument.ino | 190 +++++++++++ examples/SCPI_Dimmer/SCPI_Dimmer.ino | 26 +- src/Vrekrer_scpi_parser.cpp | 311 +++++++++--------- src/Vrekrer_scpi_parser.h | 58 ++-- 4 files changed, 392 insertions(+), 193 deletions(-) create mode 100644 examples/Ethernet_Instrument/Ethernet_Instrument.ino diff --git a/examples/Ethernet_Instrument/Ethernet_Instrument.ino b/examples/Ethernet_Instrument/Ethernet_Instrument.ino new file mode 100644 index 0000000..6650b15 --- /dev/null +++ b/examples/Ethernet_Instrument/Ethernet_Instrument.ino @@ -0,0 +1,190 @@ +#include "Arduino.h"; +#include "EEPROM.h"; +#include "EtherCard.h"; +#include "Vrekrer_scpi_parser.h" + +const int eeprom_eth_data_start = 0; +const static byte dns[] = {0, 0, 0, 0}; +const static byte mask[] = {255, 255, 255, 0}; +byte mac[6] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 }; +byte ip[4] = {192, 168, 10, 7}; +byte gw[4] = {192, 168, 10, 1}; +byte Ethernet::buffer[128]; + +const byte csPin = 2; //ChipSelect Pin + +SCPI_Parser my_instrument; +boolean fromSerial = true; + +void setup() { + Serial.begin(9600); + while (!Serial); + my_instrument.RegisterCommand("*IDN?", &Identify); + my_instrument.SetCommandTreeBase("SYSTem:COMMunicate:LAN"); + my_instrument.RegisterCommand(":ADDRess", &SetIP); + my_instrument.RegisterCommand(":ADDRess?", &GetIP); + my_instrument.RegisterCommand(":DGATeway", &SetGW); + my_instrument.RegisterCommand(":DGATeway?", &GetGW); + my_instrument.RegisterCommand(":MAC", &SetMAC); + my_instrument.RegisterCommand(":MAC?", &GetMAC); + + int eeprom_address = eeprom_eth_data_start; + if (EEPROM.read(eeprom_address) == 'V') { //Already initialized + //Serial.println("EEPROM OK"); + ++eeprom_address; + EEPROM.get(eeprom_address, mac); + eeprom_address += sizeof(mac); + EEPROM.get(eeprom_address, ip); + eeprom_address += sizeof(ip); + EEPROM.get(eeprom_address, gw); + } else { //Write default values to EEPROM + EEPROM.write(eeprom_address, 'V'); + ++eeprom_address; + EEPROM.put(eeprom_address, mac); + eeprom_address += sizeof(mac); + EEPROM.put(eeprom_address, ip); + eeprom_address += sizeof(ip); + EEPROM.put(eeprom_address, gw); + } + + ether.hisport = 5025; //SCPI PORT + + boolean eth_enabled = false; + if (ether.begin(sizeof Ethernet::buffer, mac, csPin)) + eth_enabled = ether.staticSetup(ip, gw, dns, mask); + if (!eth_enabled) ether.powerDown(); +} + +void loop() { + fromSerial = true; + my_instrument.ProcessInput(Serial, "\n"); + + if ( ether.isLinkUp() ) { + fromSerial = false; + my_instrument.Execute(GetEthMsg(), Serial); + } +} + +/* SCPI FUNCTIONS */ + +void Identify(SCPI_C commands, SCPI_P parameters, Stream &interface) { + char IDN[] = "Vrekrer,SCPI Ethernet Instrument,#00,v0.3\n"; + PrintToInterface(IDN); +} + +void SetIP(SCPI_C commands, SCPI_P parameters, Stream &interface) { + SaveIP(parameters.First(), eeprom_eth_data_start + 7 ); +} + +void GetIP(SCPI_C commands, SCPI_P parameters, Stream &interface) { + char ip_str[16]; + IpToString(ether.myip, ip_str); + PrintToInterface( ip_str ); +} + +void SetGW(SCPI_C commands, SCPI_P parameters, Stream &interface) { + SaveIP(parameters.First(), eeprom_eth_data_start + 11 ); +} + +void GetGW(SCPI_C commands, SCPI_P parameters, Stream &interface) { + char ip_str[16]; + IpToString(ether.gwip, ip_str); + PrintToInterface( ip_str ); +} + +void SetMAC(SCPI_C commands, SCPI_P parameters, Stream &interface) { + int this_mac[6]; + int mac_ok = 0; + if (parameters.Size() == 6) { + for (uint8_t i = 0; i < 6; i++) { + mac_ok += sscanf(parameters[i], "%x", &this_mac[i]); + } + } + int eeprom_address = eeprom_eth_data_start + 1; + if (mac_ok == 6) { + for (uint8_t i = 0; i < 6; i++) { + EEPROM.write(eeprom_address, byte(this_mac[i])); + ++eeprom_address; + } + } +} + +void GetMAC(SCPI_C commands, SCPI_P parameters, Stream &interface) { + char mac_str[ ] = "0x##, 0x##, 0x##, 0x##, 0x##, 0x##\n"; + for (int i = 0; i < 6; ++i) { + char u = ether.mymac[i] / 16; + char l = ether.mymac[i] % 16; + mac_str[6 * i + 2] = u < 10 ? u + '0' : u + 'A' - 10; + mac_str[6 * i + 3] = l < 10 ? l + '0' : l + 'A' - 10; + } + PrintToInterface(mac_str); +} + + +/* HELPER FUNCTIONS */ + +char* GetEthMsg() { + word pos = ether.packetLoop(ether.packetReceive()); + if (pos) { + ether.httpServerReplyAck(); + char* msg = Ethernet::buffer + pos; + msg = strtok(msg, "\n"); //Remove termination char + return msg; + } else { + return NULL; + } +} + +void WriteEthMsg(char* msg) { + delayMicroseconds(20); //Delay needed for next package + BufferFiller bfill = ether.tcpOffset(); + bfill.emit_raw(msg, strlen(msg)); + ether.httpServerReply_with_flags(bfill.position(), TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V); +} + +void PrintToInterface(char* print_str) { + if (fromSerial) + Serial.write(print_str, strlen(print_str)); + else + WriteEthMsg(print_str); +} + +void SaveIP(char* ip_str, int eeprom_address) { + int this_ip[4]; + int ipOk = sscanf(ip_str, "%d.%d.%d.%d", &this_ip[0], &this_ip[1], &this_ip[2], &this_ip[3]); + if (ipOk == 4) { + for (uint8_t i = 0; i < 4; i++) { + EEPROM.write(eeprom_address, byte(this_ip[i])); + ++eeprom_address; + } + } +} + +void IpToString(byte* IP, char* ip_str) { + sprintf(ip_str, "%d.%d.%d.%d\n", IP[0], IP[1], IP[2], IP[3]); + return ip_str; +} + +/* Example code in python + +import visa +rm = visa.ResourceManager('@py') +# use rm.list_resources() to get the resource names + +SerialInstrument = rm.get_instrument('ASRL/dev/ttyACM0::INSTR') +SerialInstrument.write_termination = '\n' +SerialInstrument.read_termination = '\n' +# Change mac, ip and default gateway +SerialInstrument.write('SYST:COMM:LAN:MAC 0x74, 0x69, 0x69, 0x2D, 0x30, 0x32') +SerialInstrument.write('SYST:COMM:LAN:ADDR 192.168.0.177') +SerialInstrument.write('SYST:COMM:LAN:DGAT 192.168.0.1') + +# **Reset the instrument +# And now connect by ethernet with the new ip. +EthInstrument = rm.get_instrument('TCPIP0::192.168.0.177::5025::SOCKET') +EthInstrument.write_termination = '\n' +EthInstrument.read_termination = '\n' +#Test connection reading mac +print( EthInstrument.query('SYST:COMM:LAN:MAC?') ) + +*/ diff --git a/examples/SCPI_Dimmer/SCPI_Dimmer.ino b/examples/SCPI_Dimmer/SCPI_Dimmer.ino index 7b96f90..6d4ddcc 100644 --- a/examples/SCPI_Dimmer/SCPI_Dimmer.ino +++ b/examples/SCPI_Dimmer/SCPI_Dimmer.ino @@ -23,35 +23,27 @@ void setup() void loop() { - char line_buffer[256]; - unsigned char read_length; - - // Read in a line and execute it - read_length = Serial.readBytesUntil('\n', line_buffer, 256); - if(read_length > 0) { - line_buffer[read_length] = '\0'; //Strip the terminator char - my_instrument.Execute(String(line_buffer)); - } + my_instrument.ProcessInput(Serial, "\n"); } -void Identify(SCPI_Commands commands, SCPI_Parameters parameters){ - Serial.println("Vrekrer,Arduino SCPI Dimmer,#00,v0.2"); +void Identify(SCPI_C commands, SCPI_P parameters, Stream& interface) { + interface.println("Vrekrer,Arduino SCPI Dimmer,#00,v0.3"); } -void SetBrightness(SCPI_Commands commands, SCPI_Parameters parameters){ +void SetBrightness(SCPI_C commands, SCPI_P parameters, Stream& interface) { // For simplicity no bad parameter check is done. if (parameters.Size() > 0) { - brightness = constrain(parameters[0].toInt(), 0, 10); + brightness = constrain(String(parameters[0]).toInt(), 0, 10); analogWrite(ledPin, intensity[brightness]); } } -void GetBrightness(SCPI_Commands commands, SCPI_Parameters parameters) { - Serial.println(String(brightness, DEC)); +void GetBrightness(SCPI_C commands, SCPI_P parameters, Stream& interface) { + interface.println(String(brightness, DEC)); } -void IncDecBrightness(SCPI_Commands commands, SCPI_Parameters parameters){ - String last_header = commands[commands.Size() - 1]; +void IncDecBrightness(SCPI_C commands, SCPI_P parameters, Stream& interface) { + String last_header = String(commands.Last()); last_header.toUpperCase(); if (last_header.startsWith("INC")) { brightness = constrain(brightness + 1, 0, 10); diff --git a/src/Vrekrer_scpi_parser.cpp b/src/Vrekrer_scpi_parser.cpp index befbc4d..fcc6b18 100644 --- a/src/Vrekrer_scpi_parser.cpp +++ b/src/Vrekrer_scpi_parser.cpp @@ -3,41 +3,47 @@ // SCPI_String_Array member functions -String SCPI_String_Array::operator[](const byte index) { +SCPI_String_Array::SCPI_String_Array() {} + +SCPI_String_Array::~SCPI_String_Array() {} + +char* SCPI_String_Array::operator[](const uint8_t index) { return values_[index]; } -void SCPI_String_Array::Append(String value) { - values_[size_] = value; - size_++; +void SCPI_String_Array::Append(char* value) { + if (size_ < SCPI_ARRAY_SYZE) { + values_[size_] = value; + size_++; + } } -String SCPI_String_Array::Pop() { +char* SCPI_String_Array::Pop() { if (size_ > 0) { size_--; return values_[size_]; } else { - return String(""); + return NULL; } } -String SCPI_String_Array::First() { +char* SCPI_String_Array::First() { if (size_ > 0) { - return values_[size_]; + return values_[0]; } else { - return String(""); + return NULL; } } -String SCPI_String_Array::Last() { +char* SCPI_String_Array::Last() { if (size_ > 0) { - return values_[size_ -1]; + return values_[size_ - 1]; } else { - return String(""); + return NULL; } } -byte SCPI_String_Array::Size() { +uint8_t SCPI_String_Array::Size() { return size_; } @@ -45,26 +51,23 @@ byte SCPI_String_Array::Size() { SCPI_Commands::SCPI_Commands() {} -SCPI_Commands::SCPI_Commands(String message) { - int i; +SCPI_Commands::SCPI_Commands(char *message) { + char* token = message; + // Trim leading spaces + while (isspace(*token)) token++; // Discard parameters and multicommands - message.trim(); - i = message.indexOf(' '); - if (i != -1) message.remove(i); - i = message.indexOf(';'); - if (i != -1) message.remove(i); + not_processed_message = strpbrk(token, " \t;"); + if (not_processed_message != NULL) { + not_processed_message += 1; + token = strtok(token, " \t;"); + token = strtok(token, ":"); + } else { + token = strtok(token, ":"); + } // Strip using ':' - while (true) { - i = message.indexOf(':'); - if (i == 0) { //leading ":" - message.remove(0, 1); - } else if (i != -1) { - this->Append(message.substring(0, i)); - message.remove(0, i + 1); - } else { //last command - this->Append(message); - break; - } + while (token != NULL) { + this->Append(token); + token = strtok(NULL, ":"); } } @@ -72,160 +75,166 @@ SCPI_Commands::SCPI_Commands(String message) { SCPI_Parameters::SCPI_Parameters() {} -SCPI_Parameters::SCPI_Parameters(String message) { - message.trim(); - int i = message.indexOf(' '); - int j = message.indexOf(';'); - - //remove commands before ' ' or ';' - if ((j != -1) && (i != -1) && (j < i)) { //';' before ' ' - message.remove(0, j); - } else if (i != -1) { - message.remove(0, i + 1); - } else if (j != -1) { - message.remove(0, j); - } else { // No parameters - return; +SCPI_Parameters::SCPI_Parameters(char* message) { + char* parameter = message; + // Discard parameters and multicommands + not_processed_message = strpbrk(parameter, ";"); + if (not_processed_message != NULL) { + not_processed_message += 1; + parameter = strtok(parameter, ";"); + parameter = strtok(parameter, ","); + } else { + parameter = strtok(parameter, ","); } - - // Strip using parameter separator ',' - // if it is multicommand message put next commands in last parameter - while (message.length()) { - message.trim(); - //Check for string parameters enclosed by "" or '' - if ((message.charAt(0) == '"') || (message.charAt(0) == '\'')) { - i = message.indexOf(message.charAt(0), 1); - if (i == -1) { //Bad unterminated string - this->Append(message.substring(0, 1)); - break; - } else { //String found - this->Append(message.substring(0, i+1)); - message.remove(0, i+1); - } - message.trim(); - if (message.charAt(0) == ',') //remove next ',' if any - message.remove(0, 1); - continue; - } - //Split on "," or ";" - i = message.indexOf(','); - j = message.indexOf(';'); - if ((j == 0) && (message.length() == 1)) { //just terminator - break; - } else if (j == 0) { // This is a new commmand - this->Append(message); //Pass it as last parameter - break; - } else if ((j != -1) && (i != -1) && (j < i)) { //';' before ' ' - this->Append(message.substring(0, j)); - message.remove(0, j); - } else if (i != -1) { //Normal Parameter - this->Append(message.substring(0, i)); - message.remove(0, i+1); - } else if (j != -1) { //Parameter before new command - this->Append(message.substring(0, j)); - message.remove(0, j); - } else { // Last parameter - this->Append(message); - break; - } + // Strip using ':' + while (parameter != NULL) { + while(isspace(*parameter)) parameter++; + this->Append(parameter); + parameter = strtok(NULL, ","); } + //TODO add support for strings parameters } + //SCPI_Registered_Commands member functions -void SCPI_Parser::AddToken(String token) { +void SCPI_Parser::AddToken(char *token) { //Strip '?' from end if needed - if (token.endsWith("?")) - token.remove(token.length() -1); + size_t original_size = strlen(token); + char *mytoken = strtok(token, "?"); //add the token bool allready_added = false; - for (int i = 0; i < tokens_size_; i++) - allready_added ^= token.equals(tokens_[i]); + for (uint8_t i = 0; i < tokens_size_; i++) + allready_added ^= (strcmp(mytoken, tokens_[i]) == 0); if (!allready_added) { - tokens_[tokens_size_] = token; - tokens_size_++; + if (tokens_size_ < SCPI_MAX_TOKENS) { + char *stored_token = new char [strlen(mytoken) + 1]; + strcpy(stored_token, mytoken); + tokens_[tokens_size_] = stored_token; + tokens_size_++; + } } + //Restore ? if needed + if (original_size > strlen(token)) token[original_size-1] = '?'; } -String SCPI_Parser::GetCommandCode(SCPI_Commands commands) { - char code[10]; //TODO Max Command nesting? +uint32_t SCPI_Parser::GetCommandCode(SCPI_Commands& commands) { + uint32_t code = tree_code_ - 1; + char* header; for (int i = 0; i < commands.Size(); i++) { - code[i] = char(33); //Default value ch(33) =! - String header = commands[i]; - header.toUpperCase(); - byte offset = 65; //ch(65) = A - if (header.endsWith("?")) { - byte offset = 97; //ch(65) = a - header.remove(header.length(), 1); - } - for (int j = 0; j < tokens_size_; j++) { - String short_token = tokens_[j]; - for (int t = 0; t < short_token.length(); t++) - if (isLowerCase(short_token.charAt(t))) { - short_token.remove(t); - break; - } - String long_token = tokens_[j]; - long_token.toUpperCase(); - if (header.equals(short_token) || header.equals(long_token)) - code[i] = char(offset + j); + code *= SCPI_MAX_TOKENS; + header = commands[i]; + bool isToken; + for (uint8_t j = 0; j < tokens_size_; j++) { + size_t ss = 0; //Token Short size + while (isupper(tokens_[j][ss])) ss++; + size_t ls = strlen(tokens_[j]); //Token Long size + size_t hs = strlen(header); //Header size + + isToken = true; + if ((hs == ss) | (hs == ss+1)) { //short token + for (uint8_t k = 0; k < ss; k++) + isToken &= (toupper(header[k]) == tokens_[j][k]); + } else if ((hs == ls) | (hs == ls+1)) { //long token + for (uint8_t k = 0; k < ls; k++) + isToken &= (toupper(header[k]) == toupper(tokens_[j][k])); + } else { + isToken = false; + } + if (isToken) { + code += j; + break; + } } + if (!isToken) return 0; } - code[commands.Size()] = '\0'; - return String(code); + if (header[strlen(header) - 1] == '?') code ^= 0x80000000; + return code+1; } -void SCPI_Parser::SetCommandTreeBase(String tree_base) { +void SCPI_Parser::SetCommandTreeBase(char* tree_base) { SCPI_Commands tree_tokens(tree_base); - for (int i = 0; i < tree_tokens.Size(); i++) + for (uint8_t i = 0; i < tree_tokens.Size(); i++) this->AddToken(tree_tokens[i]); + tree_code_ = 1; tree_code_ = this->GetCommandCode(tree_tokens); } -void SCPI_Parser::RegisterCommand(String command, SCPI_caller_t caller) { +void SCPI_Parser::RegisterCommand(char* command, SCPI_caller_t caller) { SCPI_Commands command_tokens(command); - for (int i = 0; i < command_tokens.Size(); i++) + for (uint8_t i = 0; i < command_tokens.Size(); i++) this->AddToken(command_tokens[i]); - String code = this->GetCommandCode(command_tokens); - valid_codes_[codes_size_] = tree_code_ + code; + uint32_t code = this->GetCommandCode(command_tokens); + valid_codes_[codes_size_] = code; callers_[codes_size_] = caller; codes_size_++; } -String SCPI_Parser::Execute_(String message) { +void SCPI_Parser::Execute(char* message, Stream &interface) { + tree_code_ = 1; SCPI_Commands commands(message); - SCPI_Parameters parameters(message); - - String not_processed_message = ""; - if (parameters.Last().startsWith(";")) { //It is a command not a param - not_processed_message = parameters.Pop(); - not_processed_message.remove(0, 1); - } + SCPI_Parameters parameters(commands.not_processed_message); + uint32_t code = this->GetCommandCode(commands); + for (uint8_t i = 0; i < codes_size_; i++) + if (valid_codes_[i] == code) + (*callers_[i])(commands, parameters, interface); +} + +char* SCPI_Parser::GetMessage(Stream& interface, char* term_chars) { + uint8_t msg_counter = 0; + msg_buffer[msg_counter] = '\0'; + + bool continous_data = true; + unsigned long last_data_millis = millis(); + while (continous_data) { + if (interface.available()) { + last_data_millis = millis(); + msg_buffer[msg_counter] = interface.read(); - String code = this->GetCommandCode(commands); - String full_code = execute_scope_ + code; - int i; - for (i = 0; i < codes_size_; i++) - if (valid_codes_[i].equals(code)) { - code.remove(code.length() - 1); - execute_scope_ = code; - break; - } else if (valid_codes_[i].equals(full_code)) { - full_code.remove(full_code.length() - 1); - execute_scope_ = full_code; - break; + //TODO check msg_counter overflow + ++msg_counter; + msg_buffer[msg_counter] = '\0'; + + if (strstr(msg_buffer, term_chars) != NULL) { + msg_buffer[msg_counter - strlen(term_chars)] = '\0'; + break; + } + } else { //No chars aviable jet + if ((millis() - last_data_millis) > 10) // 10 ms without new data + continous_data = false; } - if (callers_[i] && (i < codes_size_)) { - (*callers_[i])(commands, parameters); - } else { - execute_scope_ = String(""); } - return not_processed_message; + if (continous_data) + return msg_buffer; + else + return NULL; } -void SCPI_Parser::Execute(String message) { - execute_scope_ = String(""); - while (message.length()) { - message = this->Execute_(message); +void SCPI_Parser::ProcessInput(Stream& interface, char* term_chars) { + char* message = this->GetMessage(interface, term_chars); + if (message != NULL) { + this->Execute(message, interface); + } +} + +void SCPI_Parser::PrintDebugInfo() { + Serial.println(F("*** DEBUG INFO ***")); + Serial.println(); + Serial.print(F("TOKENS :")); + Serial.println(tokens_size_); + for (uint8_t i = 0; i < tokens_size_; i++) { + Serial.print(F(" ")); + Serial.println(String(tokens_[i])); + Serial.flush(); + } + Serial.println(); + Serial.println(F("VALID CODES :")); + for (uint8_t i = 0; i < codes_size_; i++) { + Serial.print(F(" ")); + Serial.println(valid_codes_[i]); + Serial.flush(); } + Serial.println(); + Serial.println(F("*******************")); + Serial.println(); } diff --git a/src/Vrekrer_scpi_parser.h b/src/Vrekrer_scpi_parser.h index 9d206db..fbabb21 100644 --- a/src/Vrekrer_scpi_parser.h +++ b/src/Vrekrer_scpi_parser.h @@ -10,7 +10,7 @@ #define SCPI_MAX_TOKENS 15 #endif -// Maximun number of register commands +// Maximun number of registered commands #ifndef SCPI_MAX_COMMANDS #define SCPI_MAX_COMMANDS 20 #endif @@ -19,47 +19,55 @@ class SCPI_String_Array { public: - String operator[](const byte index); - void Append(String value); - String Pop(); - String First(); - String Last(); - byte Size(); + SCPI_String_Array(); + ~SCPI_String_Array(); + char* operator[](const byte index); + void Append(char* value); + char* Pop(); + char* First(); + char* Last(); + uint8_t Size(); protected: - byte size_ = 0; - String values_[SCPI_ARRAY_SYZE]; + uint8_t size_ = 0; + char* values_[SCPI_ARRAY_SYZE]; }; class SCPI_Commands : public SCPI_String_Array { public: SCPI_Commands(); - SCPI_Commands(String message); + SCPI_Commands(char* message); + char* not_processed_message; }; class SCPI_Parameters : public SCPI_String_Array { public: SCPI_Parameters(); - SCPI_Parameters(String message); + SCPI_Parameters(char *message); + char* not_processed_message; }; -typedef void (*SCPI_caller_t)(SCPI_Commands, SCPI_Parameters); +typedef SCPI_Commands SCPI_C; +typedef SCPI_Parameters SCPI_P; +typedef void (*SCPI_caller_t)(SCPI_C, SCPI_P, Stream&); class SCPI_Parser { public: - void SetCommandTreeBase(String tree_base); - void RegisterCommand(String command, SCPI_caller_t caller); - void Execute(String message); + void SetCommandTreeBase(char* tree_base); + void RegisterCommand(char* command, SCPI_caller_t caller); + void Execute(char* message, Stream& interface); + void ProcessInput(Stream &interface, char* term_chars); + char* GetMessage(Stream& interface, char* term_chars); + void PrintDebugInfo(); protected: - void AddToken(String token); - String Execute_(String message); - String GetCommandCode(SCPI_Commands commands); - byte tokens_size_ = 0; - String tokens_[SCPI_MAX_TOKENS]; - byte codes_size_ = 0; - String valid_codes_[SCPI_MAX_COMMANDS]; + void AddToken(char* token); + uint32_t GetCommandCode(SCPI_Commands& commands); + uint8_t tokens_size_ = 0; + char *tokens_[SCPI_MAX_TOKENS]; + uint8_t codes_size_ = 0; + uint32_t valid_codes_[SCPI_MAX_COMMANDS]; SCPI_caller_t callers_[SCPI_MAX_COMMANDS]; - String execute_scope_ = ""; - String tree_code_ = ""; + uint32_t tree_code_ = 1; + char msg_buffer[64]; //TODO BUFFER_LENGTH }; -#endif +#endif