aqhome-nodes: send heartbeat message to interface every 2mins.

e02 now expects this message. If for at least 10mins no such msg is
received the USB interface is reset.
This commit is contained in:
Martin Preuss
2026-04-27 16:01:12 +02:00
parent a0360d5373
commit 0ac20ba82c
12 changed files with 217 additions and 26 deletions

View File

@@ -36,6 +36,7 @@
#define CONNCLEAN_INTERVAL_IN_SECS 2 #define CONNCLEAN_INTERVAL_IN_SECS 2
#define CONNCHECK_INTERVAL_IN_SECS 10 #define CONNCHECK_INTERVAL_IN_SECS 10
#define HEARTBEAT_INTERVAL_IN_SECS 120 /* every 2mins */
@@ -133,11 +134,13 @@ void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
int timeout; int timeout;
time_t timeLastConnectionCleanup; time_t timeLastConnectionCleanup;
time_t timeLastConnCheck; time_t timeLastConnCheck;
time_t timeLastHeartbeat;
timeout=AQH_NodeServer_GetTimeout(aqh); timeout=AQH_NodeServer_GetTimeout(aqh);
timeStart=time(NULL); timeStart=time(NULL);
timeLastConnectionCleanup=time(NULL); timeLastConnectionCleanup=timeStart;
timeLastConnCheck=time(NULL); timeLastConnCheck=timeStart;
timeLastHeartbeat=timeStart;
while(!stopService) { while(!stopService) {
time_t now; time_t now;
@@ -162,6 +165,13 @@ void _runService(AQH_OBJECT *aqh, AQH_EVENT_LOOP *eventLoop)
timeLastConnCheck=now; timeLastConnCheck=now;
} }
if (_diffInSeconds(now, timeLastHeartbeat)>HEARTBEAT_INTERVAL_IN_SECS) {
DBG_INFO(NULL, "Sending heartbeat message");
AQH_NodeServer_SendHeartbeat(aqh);
timeLastHeartbeat=now;
}
if (timeout && (_diffInSeconds(now, timeStart)>timeout)) { if (timeout && (_diffInSeconds(now, timeStart)>timeout)) {
DBG_INFO(NULL, "Timeout"); DBG_INFO(NULL, "Timeout");
break; break;

View File

@@ -42,6 +42,7 @@
#include <aqhome/msg/node/m_recvstats.h> #include <aqhome/msg/node/m_recvstats.h>
#include <aqhome/msg/node/m_sendstats.h> #include <aqhome/msg/node/m_sendstats.h>
#include <aqhome/msg/node/m_memstats.h> #include <aqhome/msg/node/m_memstats.h>
#include <aqhome/msg/node/m_heartbeat.h>
#include <aqhome/data/value.h> #include <aqhome/data/value.h>
#include <gwenhywfar/args.h> #include <gwenhywfar/args.h>
@@ -811,6 +812,25 @@ void AQH_NodeServer_HandleTtyMsgs(AQH_OBJECT *o)
void AQH_NodeServer_SendHeartbeat(AQH_OBJECT *o)
{
AQH_NODE_SERVER *xo;
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_NODE_SERVER, o);
if (xo && xo->ttyEndpoint) {
AQH_MESSAGE *nodeMsg;
time_t t;
t=time(0);
nodeMsg=AQH_HeartbeatMessage_new(0xf0, 0x00, AQH_MSG_TYPE_HEARTBEAT, (uint32_t) t);
AQH_Endpoint_AddMsgOut(xo->ttyEndpoint, nodeMsg);
AQH_NodeServer_WriteTtyMsgToLogFile(o, nodeMsg, "sending");
DBG_ERROR(NULL, "Forwarding node message %d to node", AQH_NodeMessage_GetMsgType(nodeMsg));
}
}
void _handleMsgFromTty(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg) void _handleMsgFromTty(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{ {
uint8_t code; uint8_t code;

View File

@@ -57,6 +57,7 @@ void AQH_NodeServer_HandleClientMsgs(AQH_OBJECT *o);
void AQH_NodeServer_HandleBrokerMsgs(AQH_OBJECT *o); void AQH_NodeServer_HandleBrokerMsgs(AQH_OBJECT *o);
void AQH_NodeServer_CheckBrokerConnection(AQH_OBJECT *o); void AQH_NodeServer_CheckBrokerConnection(AQH_OBJECT *o);
void AQH_NodeServer_CheckTtyConnection(AQH_OBJECT *o); void AQH_NodeServer_CheckTtyConnection(AQH_OBJECT *o);
void AQH_NodeServer_SendHeartbeat(AQH_OBJECT *o);
/* getters and setters */ /* getters and setters */

View File

@@ -23,6 +23,7 @@
#include "aqhome/msg/msg_sysstats.h" #include "aqhome/msg/msg_sysstats.h"
#include "aqhome/msg/msg_ping.h" #include "aqhome/msg/msg_ping.h"
#include "aqhome/msg/msg_pong.h" #include "aqhome/msg/msg_pong.h"
#include "aqhome/msg/m_heartbeat.h"
#include "aqhome/msg/msg_needaddr.h" #include "aqhome/msg/msg_needaddr.h"
#include "aqhome/msg/msg_claimaddr.h" #include "aqhome/msg/msg_claimaddr.h"
#include "aqhome/msg/msg_haveaddr.h" #include "aqhome/msg/msg_haveaddr.h"
@@ -80,6 +81,7 @@ void AqHomed_LogTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg)
switch(msgType) { switch(msgType) {
case AQH_MSG_TYPE_PING: AQH_PingMsg_DumpToBuffer(msg, dbuf, "received"); break; case AQH_MSG_TYPE_PING: AQH_PingMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_PONG: AQH_PongMsg_DumpToBuffer(msg, dbuf, "received"); break; case AQH_MSG_TYPE_PONG: AQH_PongMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_HEARTBEAT: AQH_HeartbeatMessage_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_COMSENDSTATS: AQH_SendStatsMsg_DumpToBuffer(msg, dbuf, "received"); break; case AQH_MSG_TYPE_COMSENDSTATS: AQH_SendStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_COMRECVSTATS: AQH_RecvStatsMsg_DumpToBuffer(msg, dbuf, "received"); break; case AQH_MSG_TYPE_COMRECVSTATS: AQH_RecvStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
case AQH_MSG_TYPE_TWIBUSMEMBER: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break; case AQH_MSG_TYPE_TWIBUSMEMBER: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;

View File

@@ -64,6 +64,7 @@
m_flashresponse.h m_flashresponse.h
m_range.h m_range.h
m_time.h m_time.h
m_heartbeat.h
</headers> </headers>
@@ -93,6 +94,7 @@
m_flashresponse.c m_flashresponse.c
m_range.c m_range.c
m_time.c m_time.c
m_heartbeat.c
</sources> </sources>

View File

@@ -0,0 +1,55 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2026 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "aqhome/msg/node/m_heartbeat.h"
#include "aqhome/msg/node/m_node.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/endianfns.h>
#define AQH_MSG_OFFS_HEARTBEAT_TIMESTAMP 0 /* 4 bytes */
AQH_MESSAGE *AQH_HeartbeatMessage_new(uint8_t destAddr, uint8_t srcAddr, uint8_t code, uint32_t timestamp)
{
uint32_t payload;
payload=GWEN_ENDIAN_HTOLE32(timestamp);
return AQH_NodeMessage_new(destAddr, srcAddr, code, sizeof(payload), (const uint8_t*)&payload);
}
uint32_t AQH_HeartbeatMessage_GetTimestamp(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint32At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_HEARTBEAT_TIMESTAMP, 0);
}
void AQH_HeartbeatMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText)
{
GWEN_Buffer_AppendArgs(dbuf,
"0x%02x->0x%02x: HEARTBEAT(%s) %s (uid=0x%08x)\n",
AQH_NodeMessage_GetSourceAddress(msg),
AQH_NodeMessage_GetDestAddress(msg),
AQH_NodeMessage_MsgTypeToChar(AQH_NodeMessage_GetMsgType(msg)),
sText,
(unsigned int) AQH_HeartbeatMessage_GetTimestamp(msg));
}

View File

@@ -0,0 +1,28 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2026 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQH_M_HEARTBEAT_H
#define AQH_M_HEARTBEAT_H
#include <aqhome/api.h>
#include <aqhome/ipc2/message.h>
#include <gwenhywfar/debug.h>
AQHOME_API AQH_MESSAGE *AQH_HeartbeatMessage_new(uint8_t destAddr, uint8_t srcAddr, uint8_t code, uint32_t timestamp);
AQHOME_API uint32_t AQH_HeartbeatMessage_GetTimestamp(const AQH_MESSAGE *msg);
AQHOME_API void AQH_HeartbeatMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText);
#endif

View File

@@ -21,6 +21,7 @@
#include "aqhome/msg/node/m_needaddr.h" #include "aqhome/msg/node/m_needaddr.h"
#include "aqhome/msg/node/m_ping.h" #include "aqhome/msg/node/m_ping.h"
#include "aqhome/msg/node/m_pong.h" #include "aqhome/msg/node/m_pong.h"
#include "aqhome/msg/node/m_heartbeat.h"
#include "aqhome/msg/node/m_reboot.h" #include "aqhome/msg/node/m_reboot.h"
#include "aqhome/msg/node/m_value.h" #include "aqhome/msg/node/m_value.h"
#include "aqhome/msg/node/m_flashstart.h" #include "aqhome/msg/node/m_flashstart.h"
@@ -29,6 +30,7 @@
#include "aqhome/msg/node/m_flashready.h" #include "aqhome/msg/node/m_flashready.h"
#include "aqhome/msg/node/m_flashresponse.h" #include "aqhome/msg/node/m_flashresponse.h"
#include "aqhome/msg/node/m_range.h" #include "aqhome/msg/node/m_range.h"
#include "aqhome/msg/node/m_time.h"
#include <gwenhywfar/text.h> #include <gwenhywfar/text.h>
@@ -202,6 +204,7 @@ const char *AQH_NodeMessage_MsgTypeToChar(uint8_t i)
switch(i) { switch(i) {
case AQH_MSG_TYPE_PING: return "Ping"; case AQH_MSG_TYPE_PING: return "Ping";
case AQH_MSG_TYPE_PONG: return "Pong"; case AQH_MSG_TYPE_PONG: return "Pong";
case AQH_MSG_TYPE_HEARTBEAT: return "Heartbeat";
case AQH_MSG_TYPE_COMSENDSTATS: return "SendStats"; case AQH_MSG_TYPE_COMSENDSTATS: return "SendStats";
case AQH_MSG_TYPE_COMRECVSTATS: return "RecvStats"; case AQH_MSG_TYPE_COMRECVSTATS: return "RecvStats";
case AQH_MSG_TYPE_TWIBUSMEMBER: return "TwiBusMember"; case AQH_MSG_TYPE_TWIBUSMEMBER: return "TwiBusMember";
@@ -245,6 +248,7 @@ void AQH_NodeMessage_DumpSpecificToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *d
switch(AQH_NodeMessage_GetMsgType(msg)) { switch(AQH_NodeMessage_GetMsgType(msg)) {
case AQH_MSG_TYPE_PING: AQH_PingMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_PING: AQH_PingMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_PONG: AQH_PongMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_PONG: AQH_PongMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_HEARTBEAT: AQH_HeartbeatMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_COMSENDSTATS: AQH_SendStatsMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_COMSENDSTATS: AQH_SendStatsMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_COMRECVSTATS: AQH_RecvStatsMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_COMRECVSTATS: AQH_RecvStatsMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_NEED_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break; case AQH_MSG_TYPE_NEED_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break;

View File

@@ -29,6 +29,7 @@
#define AQH_MSG_TYPE_PING 10 #define AQH_MSG_TYPE_PING 10
#define AQH_MSG_TYPE_PONG 11 #define AQH_MSG_TYPE_PONG 11
#define AQH_MSG_TYPE_HEARTBEAT 12
#define AQH_MSG_TYPE_COMSENDSTATS 22 #define AQH_MSG_TYPE_COMSENDSTATS 22
#define AQH_MSG_TYPE_COMRECVSTATS 23 #define AQH_MSG_TYPE_COMRECVSTATS 23
#define AQH_MSG_TYPE_TWIBUSMEMBER 30 #define AQH_MSG_TYPE_TWIBUSMEMBER 30

View File

@@ -108,3 +108,13 @@
.equ USART0_DATAREG = UDR0 .equ USART0_DATAREG = UDR0
.equ USART1_DATAREG = UDR1 .equ USART1_DATAREG = UDR1
; ---------------------------------------------------------------------------
; MCP2221
.equ MCP2221_RESET_DDR = DDRB
.equ MCP2221_RESET_PORT = PORTB
.equ MCP2221_RESET_PINNUM = PORTB0

View File

@@ -72,30 +72,12 @@
#define APP_ROUTER_NO_ADDR_MGR #define APP_ROUTER_NO_ADDR_MGR
#define MODULES_CLOCK #define MODULES_CLOCK
;#define MODULES_COM
;#define MODULES_COM_WITH_ADDR_PROTO
;#define MODULES_LED
#define MODULES_LED_SIMPLE #define MODULES_LED_SIMPLE
;#define MODULES_LED_SIGNAL
;#define MODULES_LED_ACTIVITY
;#define MODULES_TWI_MASTER
;#define MODULES_LCD
;#define LCD_MINIMAL_FONT
;#define MODULES_SI7021
;#define MODULES_STATS
;#define MODULES_CNY70
;#define MODULES_REED
;#define MODULES_OWI_MASTER
;#define MODULES_DS18B20
;#define MODULES_MOTION
#define MODULES_NETWORK #define MODULES_NETWORK
#define MODULES_COM2W #define MODULES_COM2W
;#define MODULES_COMONUART0
;#define MODULES_TTYONUART1
#define MODULES_UARTFD0 #define MODULES_UARTFD0
#define APPS_STATS #define APPS_STATS
;#define APPS_NETWORK
;#define APPS_ROUTER
#define APPS_FORWARDER #define APPS_FORWARDER
@@ -106,14 +88,17 @@
.equ HEARTBEAT_MAXTIME_IN_MINS = 10 ; 10 minutes
.equ LEDMCPRESET_ONTIME = 2
.equ LEDMCPRESET_OFFTIME = 2
.equ LEDMCPRESET_REPEATS = 50 ; 50*0.4=20s
; --------------------------------------------------------------------------- ; ---------------------------------------------------------------------------
; defines for values ; defines for values
.equ VALUE_ID_DS18B20_TEMP = 0x06 ; none
.equ VALUE_ID_LEDSIMPLE_TIMING = 0x88
;.equ VALUE_ID_ROUTER_SETRANGE = 0x89
@@ -187,6 +172,30 @@ onSystemStart:
ldi r16, 0xf0 ldi r16, 0xf0
sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r16 sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r16
sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r16 sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r16
ldi r16, HEARTBEAT_MAXTIME_IN_MINS
sts heartbeatTimer, r16
rcall resetMcp2221
ret
; @end
onEveryMinute:
lds r16, heartbeatTimer
tst r16
breq onEverySecond_ret
dec r16
sts heartbeatTimer, r16
brne onEverySecond_ret
rcall resetMcp2221
ldi r16, HEARTBEAT_MAXTIME_IN_MINS
sts heartbeatTimer, r16
onEverySecond_ret:
ret ret
; @end ; @end
@@ -194,7 +203,6 @@ onSystemStart:
onEvery100ms: onEvery100ms:
onEverySecond: onEverySecond:
onEveryMinute:
onEveryHour: onEveryHour:
onEveryDay: onEveryDay:
ret ret
@@ -218,12 +226,48 @@ onEveryLoop:
; Called on every message received ; Called on every message received
onMessageReceived: onMessageReceived:
adiw xh:xl, NETMSG_OFFS_CMD
ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD
cpi r16, NETMSG_CMD_HEARTBEAT
brne onMessageReceived_ret
ldi r16, HEARTBEAT_MAXTIME_IN_MINS
sts heartbeatTimer, r16
onMessageReceived_ret:
clc clc
ret ret
; @end ; @end
; ---------------------------------------------------------------------------
; @routine resetMcp2221
;
; Reset MCP2221 by pulling /RST low for 10us.
resetMcp2221:
push r15
inr r15, SREG
cli
sbi MCP2221_RESET_DDR, MCP2221_RESET_PINNUM ; out
cbi MCP2221_RESET_PORT, MCP2221_RESET_PINNUM ; set RST low
Utils_WaitNanoSecs 10000, 0, r22 ; minimum is 2us, use 10us to be safe
sbi MCP2221_RESET_PORT, MCP2221_RESET_PINNUM ; set RST high
outr SREG, r15
pop r15
#ifdef MODULES_LED_SIMPLE
ldi r18, LEDMCPRESET_ONTIME
ldi r19, LEDMCPRESET_OFFTIME
ldi r20, LEDMCPRESET_REPEATS
bigcall LedSimple_SetTiming
#endif
ret
; @end
@@ -249,3 +293,16 @@ deviceCodeEnd:
.warning "Code reaches into boot loader!" .warning "Code reaches into boot loader!"
.endif .endif
; ***************************************************************************
; data segment
.dseg
heartbeatTimer: .byte 1

View File

@@ -17,6 +17,7 @@
.equ NETMSG_CMD_PING = 10 .equ NETMSG_CMD_PING = 10
.equ NETMSG_CMD_PONG = 11 .equ NETMSG_CMD_PONG = 11
.equ NETMSG_CMD_HEARTBEAT = 12
.equ NETMSG_CMD_SENDSTATS = 22 .equ NETMSG_CMD_SENDSTATS = 22
.equ NETMSG_CMD_RECVSTATS = 23 .equ NETMSG_CMD_RECVSTATS = 23
.equ NETMSG_CMD_TWIBUSMEMBER = 30 .equ NETMSG_CMD_TWIBUSMEMBER = 30