mirror of https://github.com/lumapu/ahoy.git
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.
505 lines
17 KiB
505 lines
17 KiB
|
|
// 2022 mb for AHOY-UL (Arduino Nano, USB light IF)
|
|
|
|
#include <Arduino.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#include "config.h"
|
|
#include "dbg.h"
|
|
|
|
|
|
|
|
/**
|
|
* Class for serial functions (read, eval, etc...)
|
|
*/
|
|
class SerialUtils {
|
|
public:
|
|
SerialUtils() {
|
|
// some member vars here
|
|
}
|
|
|
|
~SerialUtils() {}
|
|
|
|
char *mSerBuffer;
|
|
char *mTokens[MAX_SER_PARAM]; // this pointer array is used to tokenize the mSerBuffer, the data are kept in serBuffer
|
|
char *mStrOutBuf;
|
|
uint8_t *mByteBuffer;
|
|
|
|
void setup(uint8_t mMAX_IN_CHAR = MAX_SERBYTES, uint8_t mMAX_OUT_CHAR = MAX_STRING_LEN, uint8_t mNUM_CMD_PARA = MAX_SER_PARAM) {
|
|
// mMAX_IN_CHAR = MAX_IN_CHAR;
|
|
// mMAX_OUT_CHAR = MAX_OUT_CHAR;
|
|
// mNUM_CMD_PARA = NUM_CMD_PARA;
|
|
|
|
// allocate serial buffer space
|
|
mSerBuffer = new char[mMAX_IN_CHAR + 1]; // one extra byte for \0 termination
|
|
mByteBuffer = new uint8_t[mMAX_IN_CHAR / 2];
|
|
mStrOutBuf = new char[mMAX_OUT_CHAR + 1];
|
|
// char pointer array for string token
|
|
//mTokens = new char*[mNUM_CMD_PARA];
|
|
|
|
mSerBuffer[mMAX_IN_CHAR] = '\0';
|
|
mStrOutBuf[mMAX_OUT_CHAR] = '\0';
|
|
}
|
|
|
|
char *getStrOutBuf() {
|
|
return mStrOutBuf;
|
|
}
|
|
|
|
char *getInBuf() {
|
|
return mSerBuffer;
|
|
}
|
|
|
|
char **getParamsBuf() {
|
|
return mTokens;
|
|
}
|
|
|
|
/******************************************************************************************************
|
|
* serial read function with timeout
|
|
* With ATMEGA328p it can only detect 64byte at once (serial input buffer size) at the baudrate of 115200baud (~5ms per byte)
|
|
*/
|
|
static int serReadUntil(char _ch, char *_str, int _len, uint16_t _TIMEOUT) {
|
|
static volatile uint16_t _backupTO;
|
|
static volatile int thislen;
|
|
|
|
_backupTO = Serial.getTimeout();
|
|
thislen = 0;
|
|
|
|
Serial.setTimeout(_TIMEOUT);
|
|
thislen = Serial.readBytesUntil(_ch, _str, _len); // can only be used unto max serial input buffer of 64bytes
|
|
_str[thislen] = '\0'; // terminate char* str with '\0', data are available external now
|
|
if (_len > 0) {
|
|
DPRINT(DBG_DEBUG, F("ser Rx "));
|
|
_DPRINT(DBG_DEBUG, _str);
|
|
_DPRINT(DBG_DEBUG, F(" len "));
|
|
_DPRINT(DBG_DEBUG, thislen);
|
|
} else {
|
|
DPRINT(DBG_DEBUG, F("ser TIMEOUT"));
|
|
}
|
|
Serial.setTimeout(_backupTO);
|
|
return thislen;
|
|
} // end serReadUntil()
|
|
|
|
static int c_remove(char _chrm, char *_str, int _len) {
|
|
int _ir = 0;
|
|
int _iw = 0;
|
|
// _ir = 0;
|
|
// _iw = 0;
|
|
while (_str[_ir] && _ir < _len) {
|
|
if (_str[_ir] != _chrm) {
|
|
_str[_iw++] = _str[_ir];
|
|
}
|
|
_ir++;
|
|
}
|
|
_str[_iw] = '\0';
|
|
return _iw;
|
|
} // end c_remove()
|
|
|
|
static void c_replace(char _crpl, char _cnew, char *_str, int _len) {
|
|
int _ir = 0;
|
|
_ir = 0;
|
|
while (_str[_ir] && _ir < _len) {
|
|
if (_str[_ir] == _crpl) {
|
|
_str[_ir] = _cnew;
|
|
}
|
|
_ir++;
|
|
}
|
|
return;
|
|
} // end c_replace
|
|
|
|
static void c_lower(char *_str, int _len) {
|
|
int _ir = 0;
|
|
_ir = 0;
|
|
while (_str[_ir] && _ir < _len) {
|
|
if (_str[_ir] >= 'A' && _str[_ir] < 'Z') {
|
|
_str[_ir] = (char)(_str[_ir] + 0x20);
|
|
}
|
|
_ir++;
|
|
}
|
|
return;
|
|
} // end c_lower
|
|
|
|
// todo: use strtol(const char *str, char **endptr, int base) instead
|
|
/**
|
|
* converts 2 digits hex string to uint8_t byte, the high byte is FF in case of non-hex digit
|
|
*/
|
|
/*
|
|
static uint16_t x2b(char high, char low) {
|
|
static volatile uint16_t onebyte=0;
|
|
onebyte = 0;
|
|
if(high >= '0' && high <= '9') onebyte = (uint16_t) high - '0';
|
|
else if (high >='A' && high <= 'F') onebyte = (uint16_t) high - 'A' + 10;
|
|
else if (high >='a' && high <= 'f') onebyte = (uint16_t) high - 'a' + 10;
|
|
else return 0xF000;
|
|
onebyte = onebyte << 4;
|
|
|
|
if(low >= '0' && low <= '9') onebyte |= (uint16_t) low - '0';
|
|
else if (low >='A' && low <= 'F') onebyte |= (uint16_t) low - 'A' + 10;
|
|
else if (low >='a' && low <= 'f') onebyte |= (uint16_t) low - 'a' + 10;
|
|
else return 0x0F00;
|
|
|
|
return onebyte & 0x00FF;
|
|
} //end x2i()
|
|
*/
|
|
|
|
static uint16_t x2b(char *_hexbyte) {
|
|
uint16_t onebyte = 0;
|
|
if (_hexbyte[0] >= '0' && _hexbyte[0] <= '9')
|
|
onebyte = (uint16_t)_hexbyte[0] - '0';
|
|
else if (_hexbyte[0] >= 'A' && _hexbyte[0] <= 'F')
|
|
onebyte = (uint16_t)_hexbyte[0] - 'A' + 10;
|
|
else if (_hexbyte[0] >= 'a' && _hexbyte[0] <= 'f')
|
|
onebyte = (uint16_t)_hexbyte[0] - 'a' + 10;
|
|
else
|
|
return 0xF000;
|
|
onebyte = onebyte << 4;
|
|
|
|
if (_hexbyte[1] >= '0' && _hexbyte[1] <= '9')
|
|
onebyte |= (uint16_t)_hexbyte[1] - '0';
|
|
else if (_hexbyte[1] >= 'A' && _hexbyte[1] <= 'F')
|
|
onebyte |= (uint16_t)_hexbyte[1] - 'A' + 10;
|
|
else if (_hexbyte[1] >= 'a' && _hexbyte[1] <= 'f')
|
|
onebyte |= (uint16_t)_hexbyte[1] - 'a' + 10;
|
|
else
|
|
return 0x0F00;
|
|
|
|
return onebyte & 0x00FF;
|
|
} // end x2i()
|
|
|
|
/*********************************************************************************************************************************************************************
|
|
*
|
|
*
|
|
* faster serial buffer reading function with timeout, value parsing after reading; function works for more than 64byte UART buffer, only for baudrates <= 57600baud
|
|
* also 57600baud works with atmega 8Mhz as well
|
|
*/
|
|
static int serBlockRead_ms(char *buf, size_t _lenMAX = MAX_SERBYTES, size_t _TIMEOUT_ms = 100) {
|
|
volatile size_t _rlen;
|
|
volatile size_t _len;
|
|
volatile size_t _backupTO = Serial.getTimeout();
|
|
|
|
Serial.setTimeout(_TIMEOUT_ms);
|
|
// fast block reading works for max buffer size e.g. 120bytes @ 57600baud
|
|
// if (Serial.available()) { //no need to wait for available
|
|
_rlen = Serial.readBytes(buf, _lenMAX);
|
|
//}
|
|
buf[_rlen] = '\0';
|
|
Serial.setTimeout(_backupTO);
|
|
return _rlen;
|
|
} // end serReadBytes_ms()
|
|
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* eval the serial command smac<txchannel>:<data>:rc<rxchannel>:, e.g. smac:ch03:958180....:rc40: and puts it to the packet_t structure
|
|
* the data payload is addressed directly to the RF interface with the given channels
|
|
*
|
|
* !!! _serdata is changed by strtok(), it's unsafe to be used afterwards in another parsing function !!!
|
|
*/
|
|
|
|
// static boolean eval_uart_smac_request(char *_serdata, uint8_t _slen, packet_t *packet, uint8_t *rxch) {
|
|
// char *p = NULL;
|
|
// char *m = NULL;
|
|
|
|
// if (_slen < 10) {
|
|
// DPRINT(DBG_ERROR, F("slen low "));
|
|
// _DPRINT(DBG_ERROR, _slen);
|
|
// return false;
|
|
// }
|
|
|
|
// _slen = c_remove(' ', _serdata, _slen);
|
|
// c_lower(_serdata, _slen);
|
|
// p = strtok(_serdata, ":");
|
|
// m = strstr(p, "mac");
|
|
// if (m == NULL) return false;
|
|
// p = strtok(NULL, ":");
|
|
// m = strstr(p, "ch");
|
|
// if (m) {
|
|
// m += 2;
|
|
// packet->rfch = atoi(m);
|
|
// DPRINT(DBG_INFO, F("smac_txch "));
|
|
// _DPRINT(DBG_DEBUG, packet->rfch);
|
|
// p = strtok(NULL, ":");
|
|
// // next section
|
|
// DPRINT(DBG_INFO, F("smac_data "));
|
|
// _DPRINT(DBG_DEBUG, p);
|
|
// _DPRINT(DBG_DEBUG, F(", len "));
|
|
// _DPRINT(DBG_DEBUG, strlen(p));
|
|
// uint8_t _i = 0;
|
|
// for (_i = 0; _i < strlen(p); _i += 2) {
|
|
// packet->data[_i / 2] = (uint8_t) x2b(&p[_i]);
|
|
// } // end for()
|
|
// packet->plen = _i / 2;
|
|
// // eval rx channel input
|
|
// p = strtok(NULL, ":");
|
|
// m = strstr(p, "rc");
|
|
// if (m) {
|
|
// m += 2;
|
|
// *rxch = atoi(m);
|
|
// } else {
|
|
// *rxch = 0;
|
|
// } // end if()
|
|
// DPRINT(DBG_INFO, F("smac_rxch "));
|
|
// _DPRINT(DBG_DEBUG, *rxch);
|
|
// return true;
|
|
// } else {
|
|
// DPRINT(DBG_ERROR, F("smac ERROR"));
|
|
// return false;
|
|
// } // end if()
|
|
// } // end eval_uart_cmd()
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
* takes numerical value of char* serdata with some checks and factor, define MIN and MAX value as response
|
|
*/
|
|
static uint32_t uart_eval_decimal_val(const __FlashStringHelper *_info, char *_serdata, uint8_t _len, uint16_t inMIN, uint32_t inMAX, int outFACTOR) {
|
|
volatile uint32_t _tmp32 = 0L;
|
|
|
|
DISABLE_IRQ;
|
|
if ((_len > 0) && (_len < 12)) {
|
|
//_serdata[_len] = '\0';
|
|
_tmp32 = atol(_serdata);
|
|
if (_tmp32 < inMIN) {
|
|
_tmp32 = inMIN;
|
|
} else if (_tmp32 > inMAX) {
|
|
_tmp32 = inMAX;
|
|
}
|
|
_tmp32 = outFACTOR * _tmp32;
|
|
}
|
|
RESTORE_IRQ;
|
|
|
|
if (_info != NULL) {
|
|
DPRINT(DBG_INFO, _info);
|
|
_DPRINT(DBG_INFO, _tmp32);
|
|
}
|
|
Serial.println();
|
|
return _tmp32;
|
|
} // end eval_simple_cmd()
|
|
|
|
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
* generic uart command parsing
|
|
* return number of char* with start address of each token, usually first is the cmd, _tok** only referes to addresses of mSerBuffer,
|
|
*/
|
|
uint8_t read_uart_cmd_param(char **_tok, const uint8_t _numTok = MAX_SER_PARAM) {
|
|
char *_p = NULL;
|
|
char *_m = NULL;
|
|
volatile uint8_t _plen;
|
|
volatile uint8_t _rlen;
|
|
bool _res = false;
|
|
|
|
_rlen = serBlockRead_ms(mSerBuffer);
|
|
_rlen = c_remove(' ', mSerBuffer, _rlen);
|
|
c_replace('\n', '\0', mSerBuffer, _rlen);
|
|
c_replace('\r', '\0', mSerBuffer, _rlen);
|
|
c_lower(mSerBuffer, _rlen);
|
|
|
|
_p = strtok(mSerBuffer, ":");
|
|
|
|
// // check cmdstr header
|
|
// _m = strstr(_p, _matchstr);
|
|
// if (_m == NULL) {
|
|
// DPRINT(DBG_ERROR, F("wrong cmd "));
|
|
// return 0xff;
|
|
// }
|
|
|
|
volatile uint8_t _i = 0;
|
|
for (_i = 0; _i < _numTok; _i++) {
|
|
if (_p) {
|
|
_tok[_i] = _p;
|
|
DPRINT(DBG_DEBUG, F("para"));
|
|
_DPRINT(DBG_DEBUG, _i);
|
|
_DPRINT(DBG_DEBUG, F(" "));
|
|
_DPRINT(DBG_DEBUG, _tok[_i]);
|
|
} else {
|
|
break;
|
|
}
|
|
_p = strtok(NULL, ":");
|
|
} // end for()
|
|
return _i;
|
|
}// end read_uart_cmd_param()
|
|
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
* parses the uart smac command parameter and writes into the mac-packet sending structure
|
|
*/
|
|
boolean uart_cmd_smac_request_parsing(char **_cmd_params, uint8_t _numPara, packet_t *packet, uint8_t *rxch) {
|
|
|
|
//example cmd "smac:ch03:958180....:rc40:"
|
|
if (_numPara < 2) {
|
|
DPRINT(DBG_ERROR, F(" numPara low"));
|
|
_DPRINT(DBG_ERROR, _numPara);
|
|
return false;
|
|
}
|
|
|
|
//parse tx channel
|
|
char* m;
|
|
m = strstr(_cmd_params[1], "ch");
|
|
if(!m) {
|
|
DPRINT(DBG_ERROR, F("no chan"));
|
|
return false;
|
|
}
|
|
m += 2;
|
|
packet->rfch = atoi(m); //sending channel for request
|
|
|
|
//copy the bytes-string into byte array
|
|
uint8_t _i = 0;
|
|
for (_i = 0; _i < strlen(_cmd_params[2]); _i += 2) {
|
|
packet->data[_i / 2] = (uint8_t)x2b(&_cmd_params[2][_i]);
|
|
} // end for()
|
|
packet->plen = _i / 2;
|
|
|
|
if (DBG_DEBUG <= DEBUG_LEVEL) {
|
|
Serial.print(F("\ndata "));
|
|
print_bytes( packet->data, packet->plen, "", true);
|
|
}
|
|
|
|
// parse rx channel input
|
|
*rxch = 0;
|
|
if (_numPara == 3) {
|
|
m = strstr(_cmd_params[3], "rc");
|
|
if(m) {
|
|
m += 2;
|
|
*rxch = atoi(m);
|
|
} // end if(m)
|
|
} // end if()
|
|
|
|
DPRINT(DBG_DEBUG, F("rxch ")); _DPRINT(DBG_DEBUG, *rxch);
|
|
return true;
|
|
}// end uart_cmd_smac_request_parser()
|
|
|
|
|
|
/**
|
|
*
|
|
*
|
|
* parsing values of adds inverter cmd, preprare to the list of registered inverters
|
|
*
|
|
*/
|
|
bool uart_cmd_add_inverter_parsing(char **_cmd_params, uint8_t _nump, uint8_t *_invIX_p, uint16_t *_invType_p, uint8_t *invID5) {
|
|
// volatile uint8_t _plen;
|
|
// volatile uint8_t _rlen;
|
|
bool _res = false;
|
|
char strtmp5[5] = {'0', '0', '0', '0', '\0'};
|
|
|
|
if (_nump != 0xFF && _nump > 1) {
|
|
DPRINT(DBG_INFO, _cmd_params[0]); // para1, used as index
|
|
DPRINT(DBG_INFO, _cmd_params[1]); // para2, the full inverter id of the plate
|
|
|
|
if (strlen(&(_cmd_params[0][0])) > 0) {
|
|
*_invIX_p = (uint8_t)atoi(_cmd_params[0]); //*invIX_p must point to value storage outside of function!!!!!
|
|
//_rlen = *_invIX_p;
|
|
DPRINT(DBG_INFO, F(" InvIx "));
|
|
_DPRINT(DBG_INFO, *_invIX_p);
|
|
delay(10);
|
|
|
|
if ((*_invIX_p) < MAX_NUM_INVERTERS ) {
|
|
if (strlen(&_cmd_params[1][0]) == 12) {
|
|
memcpy(strtmp5, &_cmd_params[1][0], 4); // copy first 4 char into strtmp5
|
|
*_invType_p = strtol(strtmp5, NULL, 16);
|
|
DPRINT(DBG_INFO, F(" InvType "));
|
|
if (DBG_INFO) {
|
|
sprintf(mStrOutBuf, "0x%04X", *_invType_p);
|
|
Serial.print(mStrOutBuf);
|
|
}
|
|
delay(10);
|
|
|
|
invID5[0] = 0x01; // lowest byte val is used for nrf24 pipe id of 5byte addr, not visible, but needed for nrf24 addr
|
|
for (volatile uint8_t i = 1; i < 5; i++) {
|
|
memcpy(strtmp5, &(_cmd_params[1][2 * i + 2]), 2); // copy two digits used for one byte
|
|
strtmp5[2] = '\0'; // str termination
|
|
invID5[i] = strtol(strtmp5, NULL, 16); // convert 2char to one byte
|
|
}
|
|
|
|
|
|
if (DBG_INFO <= DEBUG_LEVEL) {
|
|
DPRINT(DBG_INFO, F(" InvID5 "));
|
|
print_bytes(&invID5[0], 5, "", true);
|
|
}
|
|
_res = true;
|
|
|
|
} else {
|
|
DPRINT(DBG_ERROR, F(" invID len fail"));
|
|
delay(10);
|
|
} // end if strlen(token[1])
|
|
|
|
} else {
|
|
DPRINT(DBG_ERROR, F(" invIx high"));
|
|
delay(10);
|
|
} // end if(invIX)
|
|
}
|
|
}
|
|
return _res;
|
|
} // end uart_inverter_add()
|
|
|
|
/**
|
|
*
|
|
*
|
|
*/
|
|
static bool uart_cmd_del_inverter_parsing(char **_cmd_params, uint8_t _nump, uint8_t *_invIX_p) {
|
|
bool _res = false;
|
|
|
|
if (_nump != 0xFF && _nump > 0) {
|
|
DPRINT(DBG_INFO, _cmd_params[1]); // para1, index0 is cmd itself
|
|
|
|
if (strlen(_cmd_params[1]) > 0) {
|
|
*_invIX_p = (uint8_t)atoi(_cmd_params[1]); //*invIX_p must point to value storage outside of function, like mSerBuffer !!!!!
|
|
//_rlen = *_invIX_p;
|
|
if ((*_invIX_p) < MAX_NUM_INVERTERS) {
|
|
DPRINT(DBG_INFO, F(" InvIx del "));
|
|
_DPRINT(DBG_INFO, *_invIX_p);
|
|
_res = true;
|
|
}
|
|
} // end if(strlen(_cmd_params[1]))
|
|
} // end if(_plen...)
|
|
Serial.println();
|
|
return _res;
|
|
} // end uart_inverter_del_check()
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// output functions
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* prints the byte array either as 3 digit uint8_t or 2digit hex string
|
|
*
|
|
* hint: for ESP8266 *_sep and *_end must not be NULL, otherwise Crash, Arduino Nano works with NULL
|
|
*/
|
|
void print_bytes(uint8_t *_buf, uint8_t _blen, const char *_sep, bool _asHex, const char *_end = "" ) {
|
|
volatile uint8_t _i = 0;
|
|
|
|
if (_asHex) {
|
|
Serial.print(F("hex "));
|
|
}
|
|
|
|
while (_i < _blen) {
|
|
if (_asHex) {
|
|
sprintf(mStrOutBuf, "%02X%s", _buf[_i], _sep);
|
|
Serial.print(mStrOutBuf);
|
|
} else {
|
|
sprintf(mStrOutBuf, "%03d%s", _buf[_i], _sep);
|
|
Serial.print(mStrOutBuf);
|
|
}
|
|
_i++;
|
|
} // end while()
|
|
|
|
if(*_end) Serial.print(_end);
|
|
|
|
} // end print_bytes
|
|
|
|
private:
|
|
};
|
|
|