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. 77
      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

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

@ -82,15 +82,13 @@ 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;
@ -104,17 +102,13 @@ static char **mParams; // pointer to char arrays used for input buffer st
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
@ -126,11 +120,13 @@ 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
@ -148,7 +144,11 @@ void setup() {
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
@ -184,6 +184,8 @@ void setup() {
// 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()
/** /**
@ -231,7 +233,7 @@ void loop() {
// 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) {
@ -280,7 +282,8 @@ void loop() {
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();
@ -300,7 +303,8 @@ void loop() {
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]);
@ -423,8 +427,8 @@ void loop() {
// 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);
@ -455,37 +459,55 @@ void loop() {
} // end switch-case } // end switch-case
} // end if serial... } // 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 { } else {
min_SEND_SYNC_msec = MIN_SEND_INTERVAL_ms; // large no signal period
mSendInterval_ms = (SEND_NOSIGNAL_LONG * ONE_SECOND);
}
} else {
// 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;
} }
} }
@ -550,9 +572,10 @@ void loop() {
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"));
Serial.print(mPayload[m_inv_ix].retransmits);
Serial.println(F(":ERR"));
} // end if() } // end if()
} }
} // end loop() } // end loop()
@ -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