From 996bb31db3b0d5b9abd196cb11bfd572d7791943 Mon Sep 17 00:00:00 2001 From: golfi200 Date: Sun, 22 Jan 2023 17:27:05 +0100 Subject: [PATCH] update for to avoid ESP8266-crash on hex output (Nano worked before) --- tools/nano/AhoyUL/FHEM/34_ahoyUL.pm | 18 +- tools/nano/AhoyUL/src/main.cpp | 578 ++++++++++++++------------- tools/nano/AhoyUL/src/utils_serial.h | 9 +- 3 files changed, 327 insertions(+), 278 deletions(-) diff --git a/tools/nano/AhoyUL/FHEM/34_ahoyUL.pm b/tools/nano/AhoyUL/FHEM/34_ahoyUL.pm index 5513c5f6..bc3af83f 100644 --- a/tools/nano/AhoyUL/FHEM/34_ahoyUL.pm +++ b/tools/nano/AhoyUL/FHEM/34_ahoyUL.pm @@ -1,4 +1,13 @@ + + + +### do not use further, location of file moved + + + + + package main; use strict; use warnings; @@ -133,7 +142,7 @@ sub ahoyUL_Read($) #Log3 $name, 5, "ahoyUL ($name) - received: $buf"; my $pandata = $hash->{PARTIAL}; - Log3 $name, 5, "ahoyUL/RAW: $pandata + $buf"; + Log3 $name, 4, "ahoyUL_Read: $pandata + $buf"; $pandata .= $buf; while ( $pandata =~ m/\n/ ) { # while-loop as long as "\n" in $pandata @@ -150,7 +159,7 @@ sub ahoyUL_Read($) sub ahoyUL_Parse($$$$) { my ( $hash, $iohash, $name, $rmsg) = @_; - Log3 $name, 3, "ahoyUL: $rmsg"; + Log3 $name, 3, "ahoyUL_Parse: $rmsg"; $last_hour = $hour; ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(); @@ -179,7 +188,8 @@ sub ahoyUL_Parse($$$$) #stop rmac collection insufficiently if OK missed $hash->{RMAC_started} = 0; $hash->{RMAC_complete} = 0; - } + + }#end if-elsif() } elsif($rmsg =~ m/payload/) { # user payload @@ -189,7 +199,7 @@ sub ahoyUL_Parse($$$$) # decoded message from arduino readingsSingleUpdate($hash, "dec_$1", $rmsg , 1); if ($1 eq "ch00") { - #end + #end of decoding output readingsSingleUpdate($hash, "yield_dc", "day $yield_day Wh total $yield_total kWh" , 1); $yield_day = 0; $yield_total = 0; diff --git a/tools/nano/AhoyUL/src/main.cpp b/tools/nano/AhoyUL/src/main.cpp index aed408ba..73f63dc4 100644 --- a/tools/nano/AhoyUL/src/main.cpp +++ b/tools/nano/AhoyUL/src/main.cpp @@ -12,6 +12,7 @@ // - automode: one REQUEST message is polled periodically and decoded payload is given by serial-IF (@57600baud), some comfig inputs possible // - mac-mode: -> The hoymiles specific REQUEST messages must be given as input via serial-IF (@57600baud) smac-packet // <- The full sorted RESPONSE is given to the serial-IF as rmac-packet (to be used with python, fhem, etc.) -- todo +// - scanmode: get trigger on rx-ch03 and then start scanning on all channels for the next 5sec // #include @@ -34,11 +35,13 @@ typedef HmRadio RadioType; // declaration of functions static void resetSystem(void); -static void loadDefaultConfig(config_t *_mconfig); +static void loadDefaultConfig(config_t *); +void mSwitchCasesSer(char); // utility function static int availableMemory(void); static void swap_bytes(uint8_t *, uint32_t); static uint32_t swap_bytes(uint32_t); +static uint32_t swap_bytes(uint8_t *); static bool check_array(uint8_t *, uint8_t *, uint8_t); // low level packet payload and inverter handling static uint8_t getInvIX(invPayload_t *, uint8_t, uint8_t *); @@ -52,7 +55,7 @@ static bool resetPayload(invPayload_t *); static bool out_uart_smac_resp_OK(invPayload_t *); static bool out_uart_smac_resp_ERR(invPayload_t *); static uint8_t collect_and_return_userPayload(invPayload_t *, uint8_t *, uint8_t); -static void decodePayload(uint8_t, uint8_t *, uint8_t, uint32_t, char *, uint8_t, uint16_t); +static void decode_userPayload(uint8_t, uint8_t *, uint8_t, uint32_t, char *, uint8_t, uint16_t, uint32_t); // interrupt handler static void handleIntr(void); @@ -83,7 +86,7 @@ static RadioType hmRadio; // static uint8_t radio_id[5]; //todo: use the mPayload[].id field ,this defines the radio-id (domain) of the rf24 transmission, will be derived from inverter id // static uint64_t radio_id64 = 0ULL; -#define DEF_VERSION "\n version 2022-12-19 21:35" +#define DEF_VERSION "\n version 2023-01-22 14:55" #define P(x) (__FlashStringHelper *)(x) // PROGMEM-Makro for variables static const char COMPILE_DATE[] PROGMEM = {__DATE__}; static const char COMPILE_TIME[] PROGMEM = {__TIME__}; @@ -106,26 +109,27 @@ static uint8_t rxch; // keeps the current RX channel // volatile static uint32_t current_millis = 0; static volatile uint32_t timer1_millis = 0L; // general loop timer static volatile uint32_t timer2_millis = 0L; // send Request timer -static volatile uint32_t lastRx_millis = 0L; -static volatile uint32_t tcmd_millis = 0L; // timer for smac cmd +static volatile uint32_t lastRx_millis = 0L; // last valid Rx packet fragment timer +static volatile uint32_t tcmd_millis = 0L; // timer for smac-cmd start #define ONE_SECOND (1000L) static uint8_t c_loop = 0; -static volatile int m_sread_len = 0; // UART read length +static volatile int m_sread_len = 0; // UART read length // static volatile bool rxRdy = false; //will be set true on first receive packet during sending interval, reset to false before sending static uint8_t m_inv_ix = 0; static bool payload_used[MAX_NUM_INVERTERS]; -// static bool saveMACPacket = false; // when true the the whole MAC packet with address is kept, remove the MAC for CRC calc -static bool automode = true; -static bool sendNow = false; -static bool doDecode = true; -static bool showMAC = true; -static bool smac_send = false; // cmd smac was send +// static bool saveMACPacket = false; // when true the the whole MAC packet with address is kept, remove the MAC for CRC calc +static bool automode = true; // defines automode +static bool sendNow = false; // defines whether Inverter Request-command shall be send immediatly +static bool doDecode = true; // controls basic decoding +static bool showMAC = true; // controls whether rMAC response is shown +static bool smac_send = false; // flag indicates that sMAC-cmd was send static volatile uint16_t tmp16 = 0; static uint8_t tmp8 = 0; static uint8_t tmp81 = 0; +static uint8_t tmp32 = 0; // static uint32_t min_SEND_SYNC_msec = MIN_SEND_INTERVAL_ms; -static uint8_t mCountdown_noSignal = SEND_REPEAT; +static uint8_t mCountdown_noSignal = SEND_REPEAT; // counter down of number of Inverter Requests if no response received, in dark or no signal static volatile uint32_t mSendInterval_ms = SEND_NOSIGNAL_SHORT * ONE_SECOND; static volatile uint32_t polling_req_msec = SEND_INTERVAL * ONE_SECOND; // will be set via serial interface @@ -216,249 +220,8 @@ void loop() { if (Serial.available()) { // wait char inSer = Serial.read(); - // delay(1); - switch (inSer) { - case (char)'a': { - // enable automode with REQ polling interval via a10 => 10sec, a100 => 100sec or other range 5....3600sec - // extend automode with polling period in sec, inverter ix and id, all numerial value are optional from the back - // cmd: a[[[:]:]:<12digit invID>:] e.g. a:120:1:081511223344:, also polling only a:120: and polling and inv_ix via a:120:1 - automode = true; - m_inv_ix = 0; - uint8_t invIX = 0; - uint16_t invType = 0x4711; - uint8_t invID5[5] = {0x01, 0x44, 0x33, 0x22, 0x11}; - mParams = utSer.getParamsBuf(); - tmp8 = utSer.read_uart_cmd_param(mParams); - - if (tmp8 > 0) { - // get polling interval from first parameter - tmp81 = strlen(&mParams[0][0]); - if (tmp81 > 0 && tmp81 < 5) { - polling_req_msec = 1000 * utSer.uart_eval_decimal_val(F("auto poll sec "), &mParams[0][0], 5, 5, 3600, 1); - } - - if (tmp8 > 2) { - // get inverter index and inverter ID from parameter 2 and 3 - if (utSer.uart_cmd_add_inverter_parsing(&mParams[1], MAX_SER_PARAM, &invIX, &invType, &invID5[0])) { - // write to inverter structure at given index - mPayload[invIX].invType = invType; - memcpy(mPayload[invIX].invId, invID5, 5); - m_inv_ix = invIX; - DPRINT(DBG_INFO, F(" OK")); - // todo: save inverter list to eeprom depending on 4th parameter (e.g ":eep:" ) - } // end if() - - } else if (tmp8 > 1) { - // try to get and set the inverter-index onyl - tmp81 = (uint8_t)(utSer.uart_eval_decimal_val(NULL, &mParams[1][0], tmp8, 0, MAX_NUM_INVERTERS, 1)); - if (mPayload[tmp81].invType != 0x0000) { - m_inv_ix = tmp81; - DPRINT(DBG_INFO, F(" inv_ix ")); - _DPRINT(DBG_INFO, m_inv_ix); - DPRINT(DBG_INFO, F(" OK")); - } else { - DPRINT(DBG_INFO, F(" ERR")); - } // end if-else - } // end if(tmp8 > 2)-else - } // end if(tmp8 > 0) - Serial.println(); - break; - } // end case a - - case (char)'d': { - // trigger decoding, enable periodic decoding via "d1" and disable via "d0" - Serial.print(F("\nd")); - // simple decoding, can only handle the current active inverter index, maybe erased if new data arrive, switch other inverter via interter indexing in automode settings - decodePayload(TX_REQ_INFO + 0x80, user_payload, 42, user_pl_ts, utSer.mStrOutBuf, MAX_STRING_LEN, mPayload[m_inv_ix].invType); - m_sread_len = utSer.serBlockRead_ms(utSer.mSerBuffer); - doDecode = (bool)utSer.uart_eval_decimal_val(F("decoding "), utSer.mSerBuffer, m_sread_len, 0, 255, 1); - break; - } // end case d - - case (char)'s': { - // sending smac commands which are send to the inverter, same format as from inverter, see details in funct. eval_uart_smac_cmd(...) - // e.g. "smac:ch03:958180....:rc40:" -- ch03 for Tx channel, , -- rc40 for rx channel 40, if rx channel is left then default tx_ix+2 - - // todo: not yet tested much - - if (automode == true) { - automode = false; - DPRINT(DBG_INFO, F("automode ")); - _DPRINT(DBG_INFO, automode); - } - - mParams = utSer.getParamsBuf(); - tmp8 = utSer.read_uart_cmd_param(mParams); - if (tmp8 > 0) { - if (strstr(&mParams[0][0], "mac")) { - if (utSer.uart_cmd_smac_request_parsing(mParams, tmp8, &rfTX_packet, &rxch)) { - if (rxch == 0) { - // if rxchannel not given, then set automatically - rxch = hmRadio.getRxChannel(rfTX_packet.rfch); - } - hmRadio.setRxChanIdx(hmRadio.getChanIdx(rxch)); - - // compare inv-id from packet data with all registered inv-id of the payload_t struct array - m_inv_ix = getInvIX(mPayload, MAX_NUM_INVERTERS, &rfTX_packet.data[0]); - if (m_inv_ix == 0xFF) { - DPRINT(DBG_DEBUG, F("inv_id no match")); - m_inv_ix = MAX_NUM_INVERTERS - 1; // use last possition - } - DPRINT(DBG_DEBUG, F("m_inv_ix ")); - _DPRINT(DBG_DEBUG, m_inv_ix); - tcmd_millis = millis(); - smac_send = true; - payload_used[m_inv_ix] = !resetPayload(&mPayload[m_inv_ix]); - mPayload[m_inv_ix].isMACPacket = true; // MAC must be enabled to show the full MAC packet, no need for user_payload - mPayload[m_inv_ix].receive = false; - hmRadio.sendPacket_raw(&mPayload[0].invId[0], &rfTX_packet, rxch); // 2022-10-30: byte array transfer working - mPayload[m_inv_ix].requested = true; - mPayload[m_inv_ix].invType = 0x1111; // used as dummy type, decode works external only with known type - - } // end if(utSer.uart_cmd_smac_request_parser(...)) - } // end if(mac) - } // end if(tmp8) - Serial.println(); - break; - } // end case s - - case (char)'i': { - // inverter handling cmds for automode - uint8_t invIX = 0; - uint16_t invType = 0x4711; - uint8_t invID5[5] = {0x01, 0x44, 0x33, 0x22, 0x11}; - // cmd tokens will be written in mParams-pointer array - mParams = utSer.getParamsBuf(); - tmp8 = utSer.read_uart_cmd_param(mParams); - if (tmp8 > 0) { - if (strstr(&mParams[0][0], "add")) { - // cmd to add a new inverter at index possition - // /> invreg:01:1144 11223344: - register new inverter at index 01 and plate id - // - if (utSer.uart_cmd_add_inverter_parsing(++mParams, tmp8, &invIX, &invType, &invID5[0])) { - // write to inverter structure at given index - mPayload[invIX].invType = invType; - memcpy(mPayload[invIX].invId, invID5, 5); - // todo: extend inverter list to eeprom - } - - } else if (strstr(&mParams[0][0], "lst")) { - // match cmd to list inverter which are registered - // /> invlist: -- list content of all index possitions - // - Serial.print(F("\ninv List:")); - for (uint8_t x = 0; x < MAX_NUM_INVERTERS; x++) { - snprintf_P(utSer.getStrOutBuf(), MAX_STRING_LEN, PSTR("\n %d: %04X "), x, mPayload[x].invType); - Serial.print(utSer.getStrOutBuf()); - utSer.print_bytes(mPayload[x].invId, 5, " ", true); - } // end for() - - } else if (strstr(&mParams[0][0], "del")) { - // cmd to delete inverter from inverter list via given index - // /> invdel:01: -- delete index 01 from list - // - if (utSer.uart_cmd_del_inverter_parsing(mParams, tmp8, &invIX)) { - mPayload[invIX].invType = 0x0000; - } - - } else { - // no cmd match - } - } - Serial.println(); - break; - } // end case i - - case (char)'c': { - // scans all channels for 1bit RDP (RSSI) value and print result e.g c100 scans in a loop of 100 iterations - // Serial.print(F("\nc OK ")); - mParams = utSer.getParamsBuf(); - tmp8 = utSer.read_uart_cmd_param(mParams); - if (tmp8 > 0) { - tmp81 = (uint8_t)(utSer.uart_eval_decimal_val(F("scan "), &mParams[0][0], 5, 1, 255, 1)); - while (tmp81--) { - hmRadio.scanRF(); - if (Serial.available()) { - // wait char - inSer = Serial.read(); - if (inSer >= '\n') break; // from while() - } // end if - } // end while() - } - Serial.println(); - break; - } - - case (char)'m': { - // enable/disable show MACmessages via "m0" or "m1" - Serial.print(F("\nm")); - m_sread_len = utSer.serBlockRead_ms(utSer.mSerBuffer); - showMAC = (bool)utSer.uart_eval_decimal_val(F("showMAC "), utSer.mSerBuffer, m_sread_len, 0, 255, 1); - break; - } // end case m - - case (char)'r': { - mParams = utSer.getParamsBuf(); - tmp8 = utSer.read_uart_cmd_param(mParams, 2); - if (strstr(&mParams[0][0], "xch")) { - // sets rx channel via e.g via cmd rxch:63: - rxch = utSer.uart_eval_decimal_val(F("rx chan "), &mParams[1][0], 3, 0, 125, 1); // use first parameter after cmd - } else { - // query current radio information via "r" - hmRadio.print_radio_details(); - } // end if-else() - break; - } - - case (char)'p': { - // set or query power limit from inverter, parameter must be separated by ":" - // todo: extend para-check to reactive powerlimit "%var" and "var" - - mParams = utSer.getParamsBuf(); - tmp8 = utSer.read_uart_cmd_param(mParams, 3); - if (strstr(&mParams[0][0], "?")) { - // cmd:/ "p?" - // query power setting - // not sure there is a query request - DPRINTLN(DBG_DEBUG, F("query: not yet, break")); - break; - - } else if (strstr(&mParams[0][0], "%")) { - // make non persistent settings only - // set relative power - iv_powerLimit[0] = (uint16_t)utSer.uart_eval_decimal_val(F("Prel "), &mParams[0][0], 4, 0, 100, 1); - iv_powerLimit[1] = RelativNonPersistent; - } - if (strstr(&mParams[0][0], "w")) { - // make non persistent settings only - // set absolute power - iv_powerLimit[0] = (uint16_t)utSer.uart_eval_decimal_val(F("Pabs "), &mParams[0][0], 5, 0, 65000, 1); - iv_powerLimit[1] = AbsolutNonPersistent; - } // end if-else() - - // if (!iv_devControlReq) { - iv_devControlReq = true; // defines type of message, not that something is pending - sendNow = true; - //} - break; - } // end p - - case (char)'t': { - // set the time sec since Jan-01 1970 (UNIX epoch time) as decimal String value e.g. "t1612345678:" for Feb-03 2021 9:47:58 - // timestamp is only used for sending packet timer, but not for the timing of Tx/Rx scheduling etc... - // ther is no need of exact timing, it must only increase (change) in REQ_INFO_CMDs - m_sread_len = utSer.serBlockRead_ms(utSer.getInBuf()); - mTimestamp = utSer.uart_eval_decimal_val(F("time set "), utSer.getInBuf(), m_sread_len, 10 * 3600, 0xFFFFFFFF, 1); - break; - } // end case t - - case (char)'?': { - Serial.println(F("\ncmds: a:, c_, d, iadd:, idel:, ilst:, m_, p:, s, smac:, rxch_, t_, ?")); - break; - } // end case '?' - - } // end switch-case - } // end if serial... + mSwitchCasesSer(inSer); + } // end if serial... // automode RF-Tx-trigger if (automode) { @@ -467,7 +230,7 @@ void loop() { // initial fast send as long as no rx mSendInterval_ms = (SEND_NOSIGNAL_SHORT * ONE_SECOND); - } else if (millis() - lastRx_millis > (4 * polling_req_msec)) { + } else if (millis() - lastRx_millis > (8 * polling_req_msec)) { // no Rx in time reduce Tx polling if (mCountdown_noSignal) { // keep fast Tx on initial no-signal for countdown > 0 @@ -491,6 +254,7 @@ void loop() { // todo: add queue of cmds or schedule simple device control request for power limit values if (sendNow || (millis() - timer2_millis > mSendInterval_ms)) { timer2_millis = millis(); + tcmd_millis = timer2_millis; smac_send |= showMAC; // DISABLE_IRQ; payload_used[m_inv_ix] = !resetPayload(&mPayload[m_inv_ix]); @@ -513,8 +277,7 @@ void loop() { } } - // RF-Rx-loop raw reading - // receives rf data and writes data into circular buffer (packet_Buffer) + // RF-Rx-loop raw reading, receives rf data and writes data into circular buffer (packet_Buffer) hmRadio.loop(); // eval RF-Rx raw data (one single entry per loop to keep receiving new messages from one inverter m_inv_ix (inverter domain is set in nrf24+ via invID) @@ -547,10 +310,12 @@ void loop() { _DPRINT(DBG_DEBUG, packet_Buffer.available()); } // end if() - // handle output of data if ready - // after min 500msec and all packets shall be copied successfully to mPayload structure, run only if requested and some data received after REQ-trigger - if ((millis() - timer2_millis >= 500) && packet_Buffer.empty() && mPayload[m_inv_ix].requested && mPayload[m_inv_ix].receive) { - // process payload some sec after last sending + // handle output of data, if ready, based on tx-timer + // todo: make one timer for tx an rx and make shorter timeout + // todo: don't call this section if listening mode only, because processPayload() triggers retransmission + if ((millis() - timer2_millis >= 450) && packet_Buffer.empty() && mPayload[m_inv_ix].requested && mPayload[m_inv_ix].receive) { + // process payload + // after 450ms all packets shall have been copied successfully to mPayload structure, run only if requested and some data received after REQ-trigger if (false == payload_used[m_inv_ix]) { if (processPayload(&mPayload[m_inv_ix], &mConfig, true)) { @@ -565,14 +330,15 @@ void loop() { user_pl_ts = mPayload[m_inv_ix].ts; if (tmp8 == 42 && doDecode) { - decodePayload(mPayload[m_inv_ix].txId, &user_payload[0], tmp8, user_pl_ts, m_strout_p, MAX_STRING_LEN, mPayload[m_inv_ix].invType); + tmp32 = swap_bytes(&mPayload[m_inv_ix].invId[1]); + decode_userPayload(mPayload[m_inv_ix].txId, &user_payload[0], tmp8, user_pl_ts, m_strout_p, MAX_STRING_LEN, mPayload[m_inv_ix].invType, tmp32); } } } } else { - // no receive to smac cmd within timeout (of about 10 retransmissions) --> command_timeout == 4000 - if (smac_send && (millis() - tcmd_millis > 4000)) { + // no receive to smac cmd within timeout (of about 10 retransmissions) --> command_timeout == 5000ms + if (smac_send && (millis() - tcmd_millis > (DEF_MAX_RETRANS_PER_PYLD * 500))) { smac_send = false; // shows number of retransmits, if rt>0 some response, but incomplete out_uart_smac_resp_ERR(&mPayload[m_inv_ix]); @@ -582,7 +348,9 @@ void loop() { } // end loop() //----------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- static void resetSystem(void) { mTimestamp = 1665740000; // sec since 1970 Jan-01, is Oct-14 2022 ~09:33 @@ -630,6 +398,7 @@ static int availableMemory() { return size; } +// maps byte array to uint32_t (highest order byte to lowest index in byte array) static void swap_bytes(uint8_t *_buf, uint32_t _v) { _buf[0] = ((_v >> 24) & 0xff); _buf[1] = ((_v >> 16) & 0xff); @@ -638,12 +407,25 @@ static void swap_bytes(uint8_t *_buf, uint32_t _v) { return; } +//endian change of uint32_t, works back and forth by doing twice static uint32_t swap_bytes(uint32_t _v) { volatile uint32_t _res; _res = ((_v >> 24) & 0x000000ff) | ((_v >> 8) & 0x0000ff00) | ((_v << 8) & 0x00ff0000) | ((_v << 24) & 0xff000000); return _res; } +// maps a byte array to uint32_t (lowest index to higest byte in uint32_t) +static uint32_t swap_bytes(uint8_t *_inbuf) { + volatile uint32_t _res = 0x00; + volatile uint8_t _i = 0; + for (_i = 0; _i<4; _i++) { + _res = _res << 8; + _res |= _inbuf[_i]; + } + return _res; +} + + ////////////////// handle payload function, maybe later as own class ////////////////// /** * compares the two arrays and returns true when equal @@ -926,7 +708,8 @@ static bool out_uart_smac_resp_OK(invPayload_t *_payload) { Serial.print(_payload->rxChIdx); Serial.println(F(":{")); for (uint8_t i = 0; i < (_payload->maxPackId); i++) { - hmRadio.dumpBuf(NULL, &_payload->data[i][0], _payload->len[i]); + //hmRadio.dumpBuf(NULL, &_payload->data[i][0], _payload->len[i]); + utSer.print_bytes(&_payload->data[i][0], _payload->len[i], "", true); if (i != _payload->maxPackId - 1) Serial.println(F(":")); else @@ -972,15 +755,15 @@ static uint8_t collect_and_return_userPayload(invPayload_t *_payload, uint8_t *_ Serial.print(F("\nrPayload(")); Serial.print(_offs); Serial.print(F("): ")); - hmRadio.dumpBuf(NULL, _user_payload, _offs); - Serial.print(F(":")); + //hmRadio.dumpBuf(NULL, _user_payload, _offs); + utSer.print_bytes(_user_payload, _offs, "", true, ":\n"); return _offs; } // end if returnPaylout /** * simple decoding of 2ch HM-inverter only */ -static void decodePayload(uint8_t _cmd, uint8_t *_user_payload, uint8_t _ulen, uint32_t _ts, char *_strout, uint8_t _strlen, uint16_t _invtype) { +static void decode_userPayload(uint8_t _cmd, uint8_t *_user_payload, uint8_t _ulen, uint32_t _ts, char *_strout, uint8_t _strlen, uint16_t _invtype, uint32_t _plateID) { volatile uint32_t _val = 0L; byteAssign_t _bp; volatile uint8_t _x; @@ -1004,7 +787,7 @@ static void decodePayload(uint8_t _cmd, uint8_t *_user_payload, uint8_t _ulen, u _val = 0L; _end = _bp.start + _bp.num; if (_tmp8 != _bp.ch) { - snprintf_P(_strout, _strlen, PSTR("\nHM800/%04Xxxxxxxxx/ch%02d "), _invtype, _bp.ch); + snprintf_P(_strout, _strlen, PSTR("\nHM800/%04X%08X/ch%02d "), _invtype, _plateID, _bp.ch); Serial.print(_strout); // snprintf_P(_strout, _strlen, PSTR("ch%02d/"), _bp.ch); // Serial.print(_strout); @@ -1049,7 +832,258 @@ static void decodePayload(uint8_t _cmd, uint8_t *_user_payload, uint8_t _ulen, u Serial.print(F("NO DECODER ")); Serial.print(_cmd, HEX); } -} +}//end decodePayload() + + +/////////////////////////////////////////////////////////// +// +// handling of serial commands starting with first char _inSer, it uses many global variables to shorten function parameter handover +// +void mSwitchCasesSer(char _inSer) { + switch (_inSer) { + case (char)'a': { + // enable automode with REQ polling interval via a10 => 10sec, a100 => 100sec or other range 5....3600sec + // extend automode with polling period in sec, inverter ix and id, all numerial value are optional from the back + // cmd: a[[[:]:]:<12digit invID>:] e.g. a:120:1:081511223344:, also polling only a:120: and polling and inv_ix via a:120:1 + automode = true; + m_inv_ix = 0; + uint8_t invIX = 0; + uint16_t invType = 0x4711; + uint8_t invID5[5] = {0x01, 0x44, 0x33, 0x22, 0x11}; + mParams = utSer.getParamsBuf(); + tmp8 = utSer.read_uart_cmd_param(mParams); + + if (tmp8 > 0) { + // get polling interval from first parameter + tmp81 = strlen(&mParams[0][0]); + if (tmp81 > 0 && tmp81 < 5) { + polling_req_msec = 1000 * utSer.uart_eval_decimal_val(F("auto poll sec "), &mParams[0][0], 5, 5, 3600, 1); + } + + if (tmp8 > 2) { + // get inverter index and inverter ID from parameter 2 and 3 + if (utSer.uart_cmd_add_inverter_parsing(&mParams[1], MAX_SER_PARAM, &invIX, &invType, &invID5[0])) { + // write to inverter structure at given index + //mPayload[invIX].invType = invType; + //memcpy(mPayload[invIX].invId, &invID5[0], 5); + m_inv_ix = invIX; + DPRINT(DBG_INFO, F(" OK")); + // todo: save inverter list to eeprom depending on 4th parameter (e.g ":eep:" ) + } // end if() + + } else if (tmp8 > 1) { + // try to get and set the inverter-index only + tmp81 = (uint8_t)(utSer.uart_eval_decimal_val(NULL, &mParams[1][0], tmp8, 0, MAX_NUM_INVERTERS, 1)); + if (mPayload[tmp81].invType != 0x0000) { + m_inv_ix = tmp81; + DPRINT(DBG_INFO, F(" inv_ix ")); + _DPRINT(DBG_INFO, m_inv_ix); + DPRINT(DBG_INFO, F(" OK")); + } else { + DPRINT(DBG_INFO, F(" ERR")); + } // end if-else + } // end if(tmp8 > 2)-else + } // end if(tmp8 > 0) + Serial.println(); + break; + } // end case a + + case (char)'d': { + // trigger decoding, enable periodic decoding via "d1" and disable via "d0" + Serial.print(F("\nd")); + // simple decoding, can only handle the current active inverter index, maybe erased if new data arrive, switch other inverter via interter indexing in automode settings + tmp32 = swap_bytes(&mPayload[m_inv_ix].invId[1]); + decode_userPayload(TX_REQ_INFO + 0x80, user_payload, 42, user_pl_ts, utSer.mStrOutBuf, MAX_STRING_LEN, mPayload[m_inv_ix].invType, tmp32); + m_sread_len = utSer.serBlockRead_ms(utSer.mSerBuffer); + doDecode = (bool)utSer.uart_eval_decimal_val(F("decoding "), utSer.mSerBuffer, m_sread_len, 0, 255, 1); + break; + } // end case d + + case (char)'s': { + // sending smac commands which are send to the inverter, same format as from inverter, see details in funct. eval_uart_smac_cmd(...) + // e.g. "smac:ch03:958180....:rc40:" -- ch03 for Tx channel, , -- rc40 for rx channel 40, if rx channel is left then default tx_ix+2 + + // todo: not yet tested much + + if (automode == true) { + automode = false; + DPRINT(DBG_INFO, F("automode ")); + _DPRINT(DBG_INFO, automode); + } + + mParams = utSer.getParamsBuf(); + tmp8 = utSer.read_uart_cmd_param(mParams); + if (tmp8 > 0) { + if (strstr(&mParams[0][0], "mac")) { + if (utSer.uart_cmd_smac_request_parsing(mParams, tmp8, &rfTX_packet, &rxch)) { + if (rxch == 0) { + // if rxchannel not given, then set automatically + rxch = hmRadio.getRxChannel(rfTX_packet.rfch); + } + hmRadio.setRxChanIdx(hmRadio.getChanIdx(rxch)); + + // compare inv-id from packet data with all registered inv-id of the payload_t struct array + m_inv_ix = getInvIX(mPayload, MAX_NUM_INVERTERS, &rfTX_packet.data[0]); + if (m_inv_ix == 0xFF) { + DPRINT(DBG_DEBUG, F("inv_id no match")); + m_inv_ix = MAX_NUM_INVERTERS - 1; // use last possition + } + DPRINT(DBG_DEBUG, F("m_inv_ix ")); + _DPRINT(DBG_DEBUG, m_inv_ix); + tcmd_millis = millis(); + smac_send = true; + payload_used[m_inv_ix] = !resetPayload(&mPayload[m_inv_ix]); + mPayload[m_inv_ix].isMACPacket = true; // MAC must be enabled to show the full MAC packet, no need for user_payload + mPayload[m_inv_ix].receive = false; + hmRadio.sendPacket_raw(&mPayload[0].invId[0], &rfTX_packet, rxch); // 2022-10-30: byte array transfer working + mPayload[m_inv_ix].requested = true; + mPayload[m_inv_ix].invType = 0x1111; // used as dummy type, decode works external only with known type + + } // end if(utSer.uart_cmd_smac_request_parser(...)) + } // end if(mac) + } // end if(tmp8) + Serial.println(); + break; + } // end case s + + case (char)'i': { + // inverter handling cmds for automode + uint8_t invIX = 0; + uint16_t invType = 0x4711; + uint8_t invID5[5] = {0x01, 0x44, 0x33, 0x22, 0x11}; + // cmd tokens will be written in mParams-pointer array + mParams = utSer.getParamsBuf(); + tmp8 = utSer.read_uart_cmd_param(mParams); + if (tmp8 > 0) { + if (strstr(&mParams[0][0], "add")) { + // cmd to add a new inverter at index possition + // /> iadd:01:1144 11223344: - register new inverter at index 01 and plate id + // + if (utSer.uart_cmd_add_inverter_parsing(++mParams, tmp8, &invIX, &invType, &invID5[0])) { + // write to inverter structure at given index + mPayload[invIX].invType = invType; + memcpy(mPayload[invIX].invId, invID5, 5); + // todo: extend inverter list to eeprom + } + + } else if (strstr(&mParams[0][0], "lst")) { + // match cmd to list inverter which are registered + // /> invlist: -- list content of all index possitions + // + Serial.print(F("\ninv List:")); + for (uint8_t x = 0; x < MAX_NUM_INVERTERS; x++) { + snprintf_P(utSer.getStrOutBuf(), MAX_STRING_LEN, PSTR("\n %d: %04X "), x, mPayload[x].invType); + Serial.print(utSer.getStrOutBuf()); + utSer.print_bytes(mPayload[x].invId, 5, " ", true); + } // end for() + + } else if (strstr(&mParams[0][0], "del")) { + // cmd to delete inverter from inverter list via given index + // /> invdel:01: -- delete index 01 from list + // + if (utSer.uart_cmd_del_inverter_parsing(mParams, tmp8, &invIX)) { + mPayload[invIX].invType = 0x0000; + } + + } else { + // no cmd match + } + } + Serial.println(); + break; + } // end case i + + case (char)'c': { + // scans all channels for 1bit RDP (RSSI) value and print result e.g c100 scans in a loop of 100 iterations + // Serial.print(F("\nc OK ")); + mParams = utSer.getParamsBuf(); + tmp8 = utSer.read_uart_cmd_param(mParams); + if (tmp8 > 0) { + tmp81 = (uint8_t)(utSer.uart_eval_decimal_val(F("scan "), &mParams[0][0], 5, 1, 255, 1)); + while (tmp81--) { + hmRadio.scanRF(); + if (Serial.available()) { + // wait char + _inSer = Serial.read(); + if (_inSer >= '\n') break; // from while() + } // end if + } // end while() + } + Serial.println(); + break; + } + + case (char)'m': { + // enable/disable show MACmessages via "m0" or "m1" + Serial.print(F("\nm")); + m_sread_len = utSer.serBlockRead_ms(utSer.mSerBuffer); + showMAC = (bool)utSer.uart_eval_decimal_val(F("showMAC "), utSer.mSerBuffer, m_sread_len, 0, 255, 1); + break; + } // end case m + + case (char)'r': { + mParams = utSer.getParamsBuf(); + tmp8 = utSer.read_uart_cmd_param(mParams, 2); + if (strstr(&mParams[0][0], "xch")) { + // sets rx channel via e.g via cmd rxch:63: + rxch = utSer.uart_eval_decimal_val(F("rx chan "), &mParams[1][0], 3, 0, 125, 1); // use first parameter after cmd + } else { + // query current radio information via "r" + hmRadio.print_radio_details(); + } // end if-else() + break; + } + + case (char)'p': { + // set or query power limit from inverter, parameter must be separated by ":" + // todo: extend para-check to reactive powerlimit "%var" and "var" + + mParams = utSer.getParamsBuf(); + tmp8 = utSer.read_uart_cmd_param(mParams, 3); + if (strstr(&mParams[0][0], "?")) { + // cmd:/ "p?" + // query power setting + // not sure there is a query request + DPRINTLN(DBG_DEBUG, F("query: not yet, break")); + break; + + } else if (strstr(&mParams[0][0], "%")) { + // make non persistent settings only + // set relative power + iv_powerLimit[0] = (uint16_t)utSer.uart_eval_decimal_val(F("Prel "), &mParams[0][0], 4, 0, 100, 1); + iv_powerLimit[1] = RelativNonPersistent; + } + if (strstr(&mParams[0][0], "w")) { + // make non persistent settings only + // set absolute power + iv_powerLimit[0] = (uint16_t)utSer.uart_eval_decimal_val(F("Pabs "), &mParams[0][0], 5, 0, 65000, 1); + iv_powerLimit[1] = AbsolutNonPersistent; + } // end if-else() + + // if (!iv_devControlReq) { + iv_devControlReq = true; // defines type of message, not that something is pending + sendNow = true; + //} + break; + } // end p + + case (char)'t': { + // set the time sec since Jan-01 1970 (UNIX epoch time) as decimal String value e.g. "t1612345678:" for Feb-03 2021 9:47:58 + // timestamp is only used for sending packet timer, but not for the timing of Tx/Rx scheduling etc... + // ther is no need of exact timing, it must only increase (change) in REQ_INFO_CMDs + m_sread_len = utSer.serBlockRead_ms(utSer.getInBuf()); + mTimestamp = utSer.uart_eval_decimal_val(F("time set "), utSer.getInBuf(), m_sread_len, 10 * 3600, 0xFFFFFFFF, 1); + break; + } // end case t + + case (char)'?': { + Serial.println(F("\ncmds: a:, c_, d, iadd:, idel:, ilst:, m_, p:, s, smac:, rxch_, t_, ?")); + break; + } // end case '?' + + } // end switch-case +}//end mSwitchCases() + /////////////////////////////////////////////////////////// // diff --git a/tools/nano/AhoyUL/src/utils_serial.h b/tools/nano/AhoyUL/src/utils_serial.h index d70c976d..22c06ea9 100644 --- a/tools/nano/AhoyUL/src/utils_serial.h +++ b/tools/nano/AhoyUL/src/utils_serial.h @@ -472,8 +472,10 @@ class SerialUtils { * * * 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) { + void print_bytes(uint8_t *_buf, uint8_t _blen, const char *_sep, bool _asHex, const char *_end = "" ) { volatile uint8_t _i = 0; if (_asHex) { @@ -490,7 +492,10 @@ class SerialUtils { } _i++; } // end while() - } // end print_bytes + + if(*_end) Serial.print(_end); + + } // end print_bytes private: };