Browse Source

faster sync after long time no signal and fix negative temperature decoding

pull/478/head
golfi200 3 years ago
parent
commit
df58f1b2e7
  1. 48
      tools/nano/AhoyUL/FHEM/34_ahoyUL.pm
  2. 6
      tools/nano/AhoyUL/src/config.h
  3. 249
      tools/nano/AhoyUL/src/main.cpp

48
tools/nano/AhoyUL/FHEM/34_ahoyUL.pm

@ -1,9 +1,10 @@
package main; package main;
use strict; use strict;
use warnings; use warnings;
use DevIo; # load DevIo.pm if not already loaded use DevIo; # load DevIo.pm if not already loaded
use Time::HiRes qw(gettimeofday);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
my $last_hour=0; my $last_hour=0;
@ -19,7 +20,7 @@ my %sets = (
); );
my %Inv = { "1141xxxxxxxx", }; #my %Inv = { "1141xxxxxxxx" };
my $yield_day = 0; #todo make per inverter my $yield_day = 0; #todo make per inverter
my $yield_total = 0; my $yield_total = 0;
@ -46,7 +47,9 @@ sub ahoyUL_Define($$)
# set the device to open # set the device to open
$hash->{DeviceName} = $dev; $hash->{DeviceName} = $dev;
# open connection with custom init function # open connection with custom init function
my $ret = DevIo_OpenDev($hash, 0, "ahoyUL_Init"); #my $ret = DevIo_OpenDev($hash, 0, "ahoyUL_Init");
my $ret = DevIo_OpenDev($hash, 0, undef);
InternalTimer(gettimeofday()+5, "ahoyUL_Init", $hash);
# start periodic reading # start periodic reading
InternalTimer(gettimeofday()+15, "ahoyUL_GetUpdate", $hash); InternalTimer(gettimeofday()+15, "ahoyUL_GetUpdate", $hash);
@ -54,13 +57,12 @@ sub ahoyUL_Define($$)
} }
# will be executed upon successful connection establishment (see DevIo_OpenDev()) # will be executed upon successful connection establishment (see DevIo_OpenDev())
sub ahoyUL_Init($) sub ahoyUL_Init($)
{ {
my ($hash) = @_; my ($hash) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3 $name, 3, "ahoy device Init() called ..."; Log3 $name, 2, "ahoy device Init() called ...";
# send init to device, here e.g. enable automode to send DevInfoReq (0x15 ... 0x0B ....) every 120sec and enable simple decoding in ahoy-nano # send init to device, here e.g. enable automode to send DevInfoReq (0x15 ... 0x0B ....) every 120sec and enable simple decoding in ahoy-nano
DevIo_SimpleWrite($hash, "a120:\nd1\r\n", 2); DevIo_SimpleWrite($hash, "a120:\nd1\r\n", 2);
@ -73,16 +75,24 @@ sub ahoyUL_Init($)
sub ahoyUL_Initialize($) sub ahoyUL_Initialize($)
{ {
my ($hash) = @_; my ($hash) = @_;
my $name = $hash->{NAME};
$hash->{DefFn} = "ahoyUL_Define"; $hash->{DefFn} = "ahoyUL_Define";
$hash->{UndefFn} = "ahoyUL_Undef"; $hash->{UndefFn} = "ahoyUL_Undef";
$hash->{SetFn} = "ahoyUL_Set"; $hash->{SetFn} = "ahoyUL_Set";
$hash->{ReadFn} = "ahoyUL_Read"; $hash->{ReadFn} = "ahoyUL_Read";
$hash->{ReadyFn} = "ahoyUL_Ready"; $hash->{ReadyFn} = "ahoyUL_Ready";
$hash->{ParseFn} = "ahoyUL_Parse"; $hash->{ParseFn} = "ahoyUL_Parse";
$hash->{ParseFn} = "ahoyUL_GetUpdate"; #to be initialized in X_Define with a period $hash->{ParseFn} = "ahoyUL_GetUpdate"; #to be initialized in X_Define with a period
#$hash->{AttrFn} = "ahoyUL_Attr";
$hash->{AttrList} = "disable:0,1 " .
"header " .
"inv_polling_sec " .
"$readingFnAttributes ";
Log3 $name, 2, "ahoy_Initialize called";
return undef;
} }
@ -90,11 +100,11 @@ sub ahoyUL_GetUpdate($)
{ {
my ($hash) = @_; my ($hash) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3 $name, 3, "ahoy_GetUpdate called ..."; Log3 $name, 4, "ahoy_GetUpdate called ... todo later";
# neuen Timer starten in einem konfigurierten Interval. # neuen Timer starten in einem konfigurierten Interval.
#InternalTimer(gettimeofday()+$hash->{cmdInterval}, "ahoyUL_GetUpdate", $hash); #InternalTimer(gettimeofday()+$hash->{cmdInterval}, "ahoyUL_GetUpdate", $hash);
InternalTimer(gettimeofday()+120, "ahoyUL_GetUpdate", $hash); InternalTimer(gettimeofday() + 1800, "ahoyUL_GetUpdate", $hash);
#todo: call cmd sender method or do it right here #todo: call cmd sender method or do it right here
} }
@ -259,4 +269,26 @@ sub ahoyUL_Undef($$)
return undef; return undef;
} }
# default sub to set and del attributes, maybe used later
sub ahoyUL_Attr($$$$)
{
my ( $cmd, $name, $aName, $aValue ) = @_;
# $cmd - Vorgangsart - kann die Werte "del" (löschen) oder "set" (setzen) annehmen
# $name - Gerätename
# $aName/$aValue sind Attribut-Name und Attribut-Wert
if ($cmd eq "set") {
if ($aName eq "Regex") {
eval { qr/$aValue/ };
if ($@) {
Log3 $name, 3, "X ($name) - Invalid regex in attr $name $aName $aValue: $@";
return "Invalid Regex $aValue: $@";
}
}
}
return undef;
}
1; 1;

6
tools/nano/AhoyUL/src/config.h

@ -43,8 +43,10 @@
#define SERIAL_INTERVAL 5 #define SERIAL_INTERVAL 5
// default send interval // default send interval
#define SEND_INTERVAL 60 //send interval if Rx OK #define SEND_INTERVAL (60) //send interval if Rx OK
#define SEND_INTERVAL_MIN 10 //send interval if no RX (used initial sync or signal loss), todo: shall be disabled over night #define SEND_NOSIGNAL_SHORT (10) //short send interval if no RX (used initial sync or signal loss)
#define SEND_REPEAT (5) //number of tries of short send interval to sync faster to inverter after night
#define SEND_NOSIGNAL_LONG (20*60) //long TX interval whne no SIGNAL for long time, e.g. over night
// maximum human readable inverter name length // maximum human readable inverter name length
#define MAX_NAME_LENGTH 16 #define MAX_NAME_LENGTH 16

249
tools/nano/AhoyUL/src/main.cpp

@ -82,39 +82,33 @@ 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 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; // static uint64_t radio_id64 = 0ULL;
#define DEF_VERSION "\n version 2022-12-15 23:00"
#define DEF_VERSION "\n version 2022-12-06 21:30"
#define P(x) (__FlashStringHelper *)(x) // PROGMEM-Makro for variables #define P(x) (__FlashStringHelper *)(x) // PROGMEM-Makro for variables
static const char COMPILE_DATE[] PROGMEM = {__DATE__}; static const char COMPILE_DATE[] PROGMEM = {__DATE__};
static const char COMPILE_TIME[] PROGMEM = {__TIME__}; static const char COMPILE_TIME[] PROGMEM = {__TIME__};
static const char NAME[] PROGMEM = {DEF_DEVICE_NAME}; static const char NAME[] PROGMEM = {DEF_DEVICE_NAME};
static const char VERSION[] PROGMEM = {DEF_VERSION}; static const char VERSION[] PROGMEM = {DEF_VERSION};
#define USER_PAYLOAD_MAXLEN 80 // user_payload buffer size, so far a max output length is 62 for 4ch Inverter #define USER_PAYLOAD_MAXLEN 80 // user_payload buffer size, so far a max output length is 62 for 4ch Inverter
static uint8_t user_payload[USER_PAYLOAD_MAXLEN]; // buffer used for simple decoding and output only, one inverter only static uint8_t user_payload[USER_PAYLOAD_MAXLEN]; // buffer used for simple decoding and output only, one inverter only
static uint32_t user_pl_ts = 0; static uint32_t user_pl_ts = 0;
static SerialUtils utSer; // class reference of serial utils static SerialUtils utSer; // class reference of serial utils
static char inSer; // one char input-buffer for most simple uart-cmds static char inSer; // one char input-buffer for most simple uart-cmds
static char *m_strout_p; // pointer of output buffer static char *m_strout_p; // pointer of output buffer
static char **mParams; // pointer to char arrays used for input buffer str tokenizer output, array must get initialized with physical space!!! static char **mParams; // pointer to char arrays used for input buffer str tokenizer output, array must get initialized with physical space!!!
//static char* mParam[MAX_SER_PARAM]; // optionally: array of pointers to char* // static char* mParam[MAX_SER_PARAM]; // optionally: array of pointers to char*
static packet_t rfTX_packet; // structure of one received radio packet static packet_t rfTX_packet; // structure of one received radio packet
static uint8_t rxch; // keeps the current RX channel static uint8_t rxch; // keeps the current RX channel
// volatile static uint32_t current_millis = 0; // volatile static uint32_t current_millis = 0;
static volatile uint32_t timer1_millis = 0L; // general loop timer static volatile uint32_t timer1_millis = 0L; // general loop timer
static volatile uint32_t timer2_millis = 0L; // send Request timer static volatile uint32_t timer2_millis = 0L; // send Request timer
static volatile uint32_t lastRx_millis = 0L; static volatile uint32_t lastRx_millis = 0L;
static volatile uint32_t tcmd_millis = 0L; //timer for smac cmd static volatile uint32_t tcmd_millis = 0L; // timer for smac cmd
#define ONE_SECOND (1000L) #define ONE_SECOND (1000L)
#define ONE_MINUTE (60L * ONE_SECOND)
#define QUARTER_HOUR (15L * ONE_MINUTE)
#define SEND_INTERVAL_ms (ONE_SECOND * SEND_INTERVAL)
#define MIN_SEND_INTERVAL_ms (ONE_SECOND * MIN_SEND_INTERVAL)
static uint8_t c_loop = 0; static uint8_t c_loop = 0;
static volatile int sread_len = 0; // UART read length static volatile int 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 volatile bool rxRdy = false; //will be set true on first receive packet during sending interval, reset to false before sending
@ -125,30 +119,36 @@ static bool automode = true;
static bool sendNow = false; static bool sendNow = false;
static bool doDecode = true; static bool doDecode = true;
static bool showMAC = true; static bool showMAC = true;
static bool smac_send = false; //cmd smac was send static bool smac_send = false; // cmd smac was send
static volatile uint32_t polling_inv_msec = SEND_INTERVAL_ms;
static volatile uint16_t tmp16 = 0; static volatile uint16_t tmp16 = 0;
static uint8_t tmp8 = 0; static uint8_t tmp8 = 0;
static uint8_t tmp81 = 0; static uint8_t tmp81 = 0;
static uint32_t min_SEND_SYNC_msec = MIN_SEND_INTERVAL_ms; // static uint32_t min_SEND_SYNC_msec = MIN_SEND_INTERVAL_ms;
static uint8_t mCountdown_noSignal = SEND_REPEAT;
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
static bool iv_devControlReq = false; //this is one kind of requests, see devControlCmds static bool iv_devControlReq = false; // this is one kind of requests, see devControlCmds
static uint8_t iv_devControlCmd = ActivePowerContr; //for now the only devControlCmd static uint8_t iv_devControlCmd = ActivePowerContr; // for now the only devControlCmd
static uint16_t iv_powerLimit[2]; // limit power output static uint16_t iv_powerLimit[2]; // limit power output
/** /**
* *
* *
* setup() * setup()
* *
***********************************************************************************************/ ***********************************************************************************************/
void setup() { void setup() {
// Serial.begin(115200); // Serial.begin(115200);
Serial.begin(57600); Serial.begin(57600);
printf_begin(); // I guess, need for printf(), tocheck with F() and PROGMEM printf_begin(); // I guess, need for printf(), tocheck with F() and PROGMEM
Serial.flush(); Serial.flush();
Serial.print(P(NAME)); Serial.print(P(NAME));
Serial.print(P(VERSION)); Serial.print(F(" /compiled ")); Serial.print(P(COMPILE_DATE)); Serial.print(F(" ")); Serial.print(P(COMPILE_TIME)); Serial.print(P(VERSION));
Serial.print(F(" /compiled "));
Serial.print(P(COMPILE_DATE));
Serial.print(F(" "));
Serial.print(P(COMPILE_TIME));
mSerialTicker = 0xffff; mSerialTicker = 0xffff;
resetSystem(); // reset allocated mPayload buffer resetSystem(); // reset allocated mPayload buffer
@ -179,11 +179,13 @@ void setup() {
// todo: load Inverter decoder depending on InvID // todo: load Inverter decoder depending on InvID
m_inv_ix = 0; //first inverter index m_inv_ix = 0; // first inverter index
//global var for all inverter // global var for all inverter
iv_powerLimit[1] = AbsolutNonPersistent; iv_powerLimit[1] = AbsolutNonPersistent;
iv_powerLimit[0] = 0xFFFF; //unlimited iv_powerLimit[0] = 0xFFFF; // unlimited
if (automode) sendNow = true; // trigger initial first send until millis() counter is valid
} // end setup() } // end setup()
/** /**
@ -191,7 +193,7 @@ void setup() {
* *
* loop() * loop()
* *
**********************************************************************************************************************/ **********************************************************************************************************************/
void loop() { void loop() {
// the inverter id/ix I shall be handled in a loop if more than one inverter is registered for periodic polling // the inverter id/ix I shall be handled in a loop if more than one inverter is registered for periodic polling
@ -213,12 +215,12 @@ void loop() {
if (Serial.available()) { if (Serial.available()) {
// wait char // wait char
inSer = Serial.read(); inSer = Serial.read();
//delay(1); // delay(1);
switch (inSer) { switch (inSer) {
case (char)'a': { case (char)'a': {
// enable automode with REQ polling interval via a10 => 10sec, a100 => 100sec or other range 5....3600sec // 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 // extend automode with polling period in sec, inverter ix and id, all numerial value are optional from the back
//cmd: a[[[:<poll_sec>]:<invIX>]:<12digit invID>:] e.g. a:120:1:081511223344:, also polling only a:120: and polling and inv_ix via a:120:1 // cmd: a[[[:<poll_sec>]:<invIX>]:<12digit invID>:] e.g. a:120:1:081511223344:, also polling only a:120: and polling and inv_ix via a:120:1
automode = true; automode = true;
m_inv_ix = 0; m_inv_ix = 0;
uint8_t invIX = 0; uint8_t invIX = 0;
@ -228,39 +230,39 @@ void loop() {
tmp8 = utSer.read_uart_cmd_param(mParams); tmp8 = utSer.read_uart_cmd_param(mParams);
if (tmp8 > 0) { if (tmp8 > 0) {
//get polling interval from first parameter // get polling interval from first parameter
tmp81 = strlen(&mParams[0][0]); tmp81 = strlen(&mParams[0][0]);
if (tmp81 > 0 && tmp81 < 5) { if (tmp81 > 0 && tmp81 < 5) {
polling_inv_msec = 1000 * utSer.uart_eval_decimal_val(F("auto poll sec "), &mParams[0][0], 5, 5, 3600, 1); polling_req_msec = 1000 * utSer.uart_eval_decimal_val(F("auto poll sec "), &mParams[0][0], 5, 5, 3600, 1);
} }
if (tmp8 > 2) { if (tmp8 > 2) {
//get inverter index and inverter ID from parameter 2 and 3 // 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])) { if (utSer.uart_cmd_add_inverter_parsing(&mParams[1], MAX_SER_PARAM, &invIX, &invType, &invID5[0])) {
// write to inverter structure at given index // write to inverter structure at given index
mPayload[invIX].invType = invType; mPayload[invIX].invType = invType;
memcpy(mPayload[invIX].invId, invID5, 5); memcpy(mPayload[invIX].invId, invID5, 5);
m_inv_ix = invIX; m_inv_ix = invIX;
DPRINT(DBG_INFO, F(" OK")); DPRINT(DBG_INFO, F(" OK"));
// todo: save inverter list to eeprom depending on 4th parameter (e.g ":eep:" ) // todo: save inverter list to eeprom depending on 4th parameter (e.g ":eep:" )
} //end if() } // end if()
} else if (tmp8 > 1) { } else if (tmp8 > 1) {
//try to get and set the inverter-index onyl // 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)); tmp81 = (uint8_t)(utSer.uart_eval_decimal_val(NULL, &mParams[1][0], tmp8, 0, MAX_NUM_INVERTERS, 1));
if (mPayload[tmp81].invType != 0x0000) { if (mPayload[tmp81].invType != 0x0000) {
m_inv_ix = tmp81; m_inv_ix = tmp81;
DPRINT(DBG_INFO, F(" inv_ix ")); DPRINT(DBG_INFO, F(" inv_ix "));
_DPRINT(DBG_INFO, m_inv_ix); _DPRINT(DBG_INFO, m_inv_ix);
DPRINT(DBG_INFO, F(" OK")); DPRINT(DBG_INFO, F(" OK"));
} else { } else {
DPRINT(DBG_INFO, F(" ERR")); DPRINT(DBG_INFO, F(" ERR"));
} //end if-else } // end if-else
}//end if(tmp8 > 2)-else } // end if(tmp8 > 2)-else
} //end if(tmp8 > 0) } // end if(tmp8 > 0)
Serial.println(); Serial.println();
break; break;
} // end case a } // end case a
case (char)'d': { case (char)'d': {
// trigger decoding, enable periodic decoding via "d1" and disable via "d0" // trigger decoding, enable periodic decoding via "d1" and disable via "d0"
@ -276,11 +278,12 @@ void loop() {
// sending smac commands which are send to the inverter, same format as from inverter, see details in funct. eval_uart_smac_cmd(...) // 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, <raw data with crc8>, -- rc40 for rx channel 40, if rx channel is left then default tx_ix+2 // e.g. "smac:ch03:958180....:rc40:" -- ch03 for Tx channel, <raw data with crc8>, -- rc40 for rx channel 40, if rx channel is left then default tx_ix+2
//todo: not yet tested much // todo: not yet tested much
if (automode == true) { if (automode == true) {
automode = false; automode = false;
DPRINT(DBG_INFO, F("automode ")); _DPRINT(DBG_INFO, automode); DPRINT(DBG_INFO, F("automode "));
_DPRINT(DBG_INFO, automode);
} }
mParams = utSer.getParamsBuf(); mParams = utSer.getParamsBuf();
@ -298,21 +301,22 @@ void loop() {
m_inv_ix = getInvIX(mPayload, MAX_NUM_INVERTERS, &rfTX_packet.data[0]); m_inv_ix = getInvIX(mPayload, MAX_NUM_INVERTERS, &rfTX_packet.data[0]);
if (m_inv_ix == 0xFF) { if (m_inv_ix == 0xFF) {
DPRINT(DBG_DEBUG, F("inv_id no match")); DPRINT(DBG_DEBUG, F("inv_id no match"));
m_inv_ix = MAX_NUM_INVERTERS - 1; //use last possition m_inv_ix = MAX_NUM_INVERTERS - 1; // use last possition
} }
DPRINT(DBG_DEBUG, F("m_inv_ix ")); _DPRINT(DBG_DEBUG, m_inv_ix); DPRINT(DBG_DEBUG, F("m_inv_ix "));
_DPRINT(DBG_DEBUG, m_inv_ix);
tcmd_millis = millis(); tcmd_millis = millis();
smac_send = true; smac_send = true;
payload_used[m_inv_ix] = !resetPayload(&mPayload[m_inv_ix]); 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].isMACPacket = true; // MAC must be enabled to show the full MAC packet, no need for user_payload
mPayload[m_inv_ix].receive = false; mPayload[m_inv_ix].receive = false;
hmRadio.sendPacket_raw(&mPayload[0].invId[0], &rfTX_packet, rxch); // 2022-10-30: byte array transfer working 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].requested = true;
mPayload[m_inv_ix].invType = 0x1111; //used as dummy type, decode works external only with known type 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(utSer.uart_cmd_smac_request_parser(...))
} // end if(mac) } // end if(mac)
} // end if(tmp8) } // end if(tmp8)
Serial.println(); Serial.println();
break; break;
} // end case s } // end case s
@ -370,15 +374,15 @@ void loop() {
mParams = utSer.getParamsBuf(); mParams = utSer.getParamsBuf();
tmp8 = utSer.read_uart_cmd_param(mParams); tmp8 = utSer.read_uart_cmd_param(mParams);
if (tmp8 > 0) { if (tmp8 > 0) {
tmp81 = (uint8_t) (utSer.uart_eval_decimal_val(F("scan "), &mParams[0][0], 5, 1, 255, 1)); tmp81 = (uint8_t)(utSer.uart_eval_decimal_val(F("scan "), &mParams[0][0], 5, 1, 255, 1));
while (tmp81--) { while (tmp81--) {
hmRadio.scanRF(); hmRadio.scanRF();
if (Serial.available()) { if (Serial.available()) {
// wait char // wait char
inSer = Serial.read(); inSer = Serial.read();
if (inSer >= '\n') break; //from while() if (inSer >= '\n') break; // from while()
}//end if } // end if
}//end while() } // end while()
} }
Serial.println(); Serial.println();
break; break;
@ -394,98 +398,116 @@ void loop() {
case (char)'r': { case (char)'r': {
mParams = utSer.getParamsBuf(); mParams = utSer.getParamsBuf();
tmp8 = utSer.read_uart_cmd_param(mParams,2); tmp8 = utSer.read_uart_cmd_param(mParams, 2);
if (strstr(&mParams[0][0], "xch")) { if (strstr(&mParams[0][0], "xch")) {
// sets rx channel via e.g via cmd rxch:63: // 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 rxch = utSer.uart_eval_decimal_val(F("rx chan "), &mParams[1][0], 3, 0, 125, 1); // use first parameter after cmd
} else { } else {
// query current radio information via "r" // query current radio information via "r"
hmRadio.print_radio_details(); hmRadio.print_radio_details();
}//end if-else() } // end if-else()
break; break;
} }
case (char)'p': { case (char)'p': {
//set or query power limit from inverter, parameter must be separated by ":" // set or query power limit from inverter, parameter must be separated by ":"
//todo: extend para-check to reactive powerlimit "%var" and "var" // todo: extend para-check to reactive powerlimit "%var" and "var"
mParams = utSer.getParamsBuf(); mParams = utSer.getParamsBuf();
tmp8 = utSer.read_uart_cmd_param(mParams,3); tmp8 = utSer.read_uart_cmd_param(mParams, 3);
if (strstr(&mParams[0][0], "?")) { if (strstr(&mParams[0][0], "?")) {
// cmd:/ "p?" // cmd:/ "p?"
// query power setting // query power setting
//not sure there is a query request // not sure there is a query request
DPRINTLN(DBG_DEBUG, F("query: not yet, break")); DPRINTLN(DBG_DEBUG, F("query: not yet, break"));
break; break;
} else if (strstr(&mParams[0][0], "%")) { } else if (strstr(&mParams[0][0], "%")) {
//make non persistent settings only // make non persistent settings only
// set relative power // set relative power
iv_powerLimit[0] = (uint16_t) utSer.uart_eval_decimal_val(F("Prel "), &mParams[0][0], 4, 0, 100, 1); iv_powerLimit[0] = (uint16_t)utSer.uart_eval_decimal_val(F("Prel "), &mParams[0][0], 4, 0, 100, 1);
iv_powerLimit[1] = RelativNonPersistent; iv_powerLimit[1] = RelativNonPersistent;
}
} if (strstr(&mParams[0][0], "w")) { if (strstr(&mParams[0][0], "w")) {
//make non persistent settings only // make non persistent settings only
// set absolute power // set absolute power
iv_powerLimit[0] = (uint16_t) utSer.uart_eval_decimal_val(F("Pabs "), &mParams[0][0], 5, 0, 65000, 1); iv_powerLimit[0] = (uint16_t)utSer.uart_eval_decimal_val(F("Pabs "), &mParams[0][0], 5, 0, 65000, 1);
iv_powerLimit[1] = AbsolutNonPersistent; iv_powerLimit[1] = AbsolutNonPersistent;
}//end if-else() } // end if-else()
//if (!iv_devControlReq) { // if (!iv_devControlReq) {
iv_devControlReq = true; //defines type of message, not that something is pending iv_devControlReq = true; // defines type of message, not that something is pending
sendNow = true; sendNow = true;
//} //}
break; break;
}//end p } // end p
case (char)'t': { 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 // 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... // 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 // ther is no need of exact timing, it must only increase (change) in REQ_INFO_CMDs
sread_len = utSer.serBlockRead_ms(utSer.getInBuf()); sread_len = utSer.serBlockRead_ms(utSer.getInBuf());
mTimestamp = utSer.uart_eval_decimal_val(F("time set "), utSer.getInBuf(), sread_len, 10 * 3600, 0xFFFFFFFF, 1); mTimestamp = utSer.uart_eval_decimal_val(F("time set "), utSer.getInBuf(), sread_len, 10 * 3600, 0xFFFFFFFF, 1);
break; break;
} // end case t } // end case t
case (char)'?':{ case (char)'?': {
Serial.println(F("\ncmds: a:, c_, d, iadd:, idel:, ilst:, m_, p:, s, smac:, rxch_, t_, ?")); Serial.println(F("\ncmds: a:, c_, d, iadd:, idel:, ilst:, m_, p:, s, smac:, rxch_, t_, ?"));
break; break;
} //end case '?' } // end case '?'
} // end switch-case
} // end if serial...
} // end switch-case
} // end if serial...
// automode RF-Tx-trigger // automode RF-Tx-trigger
if (automode) { if (automode) {
// slow down the sending if no Rx for long time // slow down the sending if no Rx for long time
if (millis() - lastRx_millis > QUARTER_HOUR) { if (millis() < (SEND_INTERVAL * ONE_SECOND) && !lastRx_millis) {
min_SEND_SYNC_msec = QUARTER_HOUR; // initial fast send as long as no rx
mSendInterval_ms = (SEND_NOSIGNAL_SHORT * ONE_SECOND);
} else if (millis() - lastRx_millis > (4 * polling_req_msec)) {
// no Rx in time reduce Tx polling
if (mCountdown_noSignal) {
// keep fast Tx on initial no-signal for countdown > 0
mSendInterval_ms = (SEND_NOSIGNAL_SHORT * ONE_SECOND);
} else {
// large no signal period
mSendInterval_ms = (SEND_NOSIGNAL_LONG * ONE_SECOND);
}
} else { } else {
min_SEND_SYNC_msec = MIN_SEND_INTERVAL_ms; // rx OK keep default Tx polling
mSendInterval_ms = polling_req_msec;
mCountdown_noSignal = SEND_REPEAT;
}
// trigger Tx if long period not send
if ((millis() - timer2_millis >= (SEND_NOSIGNAL_LONG * ONE_SECOND))) {
// no send for long time, trigger fast send for countdown time
mCountdown_noSignal = SEND_REPEAT;
} }
// todo: add queue of cmds or schedule simple device control request for power limit values // todo: add queue of cmds or schedule simple device control request for power limit values
if (millis() - timer2_millis >= min_SEND_SYNC_msec && ((millis() - lastRx_millis > polling_inv_msec) || millis() < 60000 || sendNow)) { if (sendNow || (millis() - timer2_millis > mSendInterval_ms)) {
timer2_millis = millis(); timer2_millis = millis();
// DISABLE_IRQ; // DISABLE_IRQ;
payload_used[m_inv_ix] = !resetPayload(&mPayload[m_inv_ix]); payload_used[m_inv_ix] = !resetPayload(&mPayload[m_inv_ix]);
mPayload[m_inv_ix].isMACPacket = true; mPayload[m_inv_ix].isMACPacket = true;
mPayload[m_inv_ix].receive = false; mPayload[m_inv_ix].receive = false;
mPayload[m_inv_ix].requested = true; // assume the previous sending is finished because of scheduled timing, therefore no check if still true mPayload[m_inv_ix].requested = true; // assume the previous sending is finished because of scheduled timing, therefore no check if still true
sendNow = false;
if (mCountdown_noSignal) mCountdown_noSignal--;
if (iv_devControlReq) { if (iv_devControlReq) {
//send devControlReq // send devControlReq
hmRadio.sendControlPacket(&mPayload[m_inv_ix].invId[0], iv_devControlCmd, iv_powerLimit); hmRadio.sendControlPacket(&mPayload[m_inv_ix].invId[0], iv_devControlCmd, iv_powerLimit);
iv_devControlReq = false; //todo: use timer that it is not send to quick (e.g. max once per 5sec) iv_devControlReq = false; // todo: use timer that it is not send to quick (e.g. max once per 5sec)
sendNow = false;
} else { } else {
//send regular info request // send regular info request
hmRadio.sendTimePacket(&mPayload[m_inv_ix].invId[0], 0x0B, mTimestamp, 0x0000); hmRadio.sendTimePacket(&mPayload[m_inv_ix].invId[0], 0x0B, mTimestamp, 0x0000);
} }
// RESTORE_IRQ; // RESTORE_IRQ;
} }
} }
@ -546,13 +568,14 @@ void loop() {
} }
} else { } else {
//no receive to smac cmd within timeout (of about 10 retransmissions) --> command_timeout == 4000 // no receive to smac cmd within timeout (of about 10 retransmissions) --> command_timeout == 4000
if (smac_send && (millis()-tcmd_millis > 4000)) { if (smac_send && (millis() - tcmd_millis > 4000)) {
smac_send = false; smac_send = false;
//shows number of retransmits, if rt>0 some response, but incomplete // shows number of retransmits, if rt>0 some response, but incomplete
Serial.print(F("rmac:rt")); Serial.print(mPayload[m_inv_ix].retransmits); Serial.println(F(":ERR")); Serial.print(F("rmac:rt"));
}//end if() Serial.print(mPayload[m_inv_ix].retransmits);
Serial.println(F(":ERR"));
} // end if()
} }
} // end loop() } // end loop()
@ -926,7 +949,7 @@ static uint8_t collect_and_return_userPayload(invPayload_t *_payload, uint8_t *_
_ixpl = 10; // index of position of payload start after pid _ixpl = 10; // index of position of payload start after pid
} // end if() } // end if()
if (_payload->maxPackId < 1) return 0xff; //false, assume no valid data in payload if (_payload->maxPackId < 1) return 0xff; // false, assume no valid data in payload
for (uint8_t i = 0; i < (_payload->maxPackId); i++) { for (uint8_t i = 0; i < (_payload->maxPackId); i++) {
memcpy(&_user_payload[_offs], &_payload->data[i][_ixpl], (_payload->len[i] - _ixpl - 1)); memcpy(&_user_payload[_offs], &_payload->data[i][_ixpl], (_payload->len[i] - _ixpl - 1));
@ -960,8 +983,8 @@ static void decodePayload(uint8_t _cmd, uint8_t *_user_payload, uint8_t _ulen, u
if (_cmd == 0x95 and _ulen == 42) { if (_cmd == 0x95 and _ulen == 42) {
//!!! simple HM600/700/800 2ch decoding for cmd=0x95 only !!!! //!!! simple HM600/700/800 2ch decoding for cmd=0x95 only !!!!
//snprintf_P(_strout, _strlen, PSTR("\nHM800/%04Xxxxxxxxx/"), _invtype); // snprintf_P(_strout, _strlen, PSTR("\nHM800/%04Xxxxxxxxx/"), _invtype);
//Serial.print(_strout); // Serial.print(_strout);
for (_x = 0; _x < HM2CH_LIST_LEN; _x++) { for (_x = 0; _x < HM2CH_LIST_LEN; _x++) {
// read values from given positions in payload // read values from given positions in payload
@ -971,8 +994,8 @@ static void decodePayload(uint8_t _cmd, uint8_t *_user_payload, uint8_t _ulen, u
if (_tmp8 != _bp.ch) { if (_tmp8 != _bp.ch) {
snprintf_P(_strout, _strlen, PSTR("\nHM800/%04Xxxxxxxxx/ch%02d "), _invtype, _bp.ch); snprintf_P(_strout, _strlen, PSTR("\nHM800/%04Xxxxxxxxx/ch%02d "), _invtype, _bp.ch);
Serial.print(_strout); Serial.print(_strout);
//snprintf_P(_strout, _strlen, PSTR("ch%02d/"), _bp.ch); // snprintf_P(_strout, _strlen, PSTR("ch%02d/"), _bp.ch);
//Serial.print(_strout); // Serial.print(_strout);
} }
_tmp8 = _bp.ch; _tmp8 = _bp.ch;
@ -991,7 +1014,7 @@ static void decodePayload(uint8_t _cmd, uint8_t *_user_payload, uint8_t _ulen, u
_val <<= 8; _val <<= 8;
_val |= _user_payload[_bp.start]; _val |= _user_payload[_bp.start];
} while (++_bp.start != _end); } while (++_bp.start != _end);
_fval = _val / (float)_bp.div; _fval = (int)_val / (float)_bp.div;
if (_bp.unitId == UNIT_NONE) { if (_bp.unitId == UNIT_NONE) {
Serial.print(_val); Serial.print(_val);

Loading…
Cancel
Save