Skip to content
This repository was archived by the owner on Jul 10, 2025. It is now read-only.

Commit 715e35d

Browse files
authored
Merge pull request #115 from benbrown/fix-serial-heartbeat
Add serial heartbeat to keep alive
2 parents c6c26f1 + 70c301a commit 715e35d

File tree

2 files changed

+36
-0
lines changed

2 files changed

+36
-0
lines changed

src/adapters/serialConnection.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ export class SerialConnection extends MeshDevice {
2525
* through a transform stream (https://stackoverflow.com/questions/71262432) */
2626
private pipePromise?: Promise<void>;
2727

28+
/* Reference for the heartbeat ping interval so it can be canceled on disconnect. */
29+
private heartbeatInterval?: ReturnType<typeof setInterval> | undefined;
30+
2831
/**
2932
* Fires when `disconnect()` is called, used to instruct serial port and
3033
* readers to release there locks
@@ -44,6 +47,7 @@ export class SerialConnection extends MeshDevice {
4447
this.transformer = undefined;
4548
this.onReleaseEvent = new SimpleEventDispatcher<boolean>();
4649
this.preventLock = false;
50+
this.heartbeatInterval = undefined;
4751

4852
this.log.debug(
4953
Types.Emitter[Types.Emitter.Constructor],
@@ -125,6 +129,7 @@ export class SerialConnection extends MeshDevice {
125129
});
126130

127131
this.preventLock = false;
132+
128133
/** Connect to device */
129134
await this.port
130135
.open({
@@ -151,6 +156,14 @@ export class SerialConnection extends MeshDevice {
151156
this.configure().catch(() => {
152157
// TODO: FIX, workaround for `wantConfigId` not getting acks.
153158
});
159+
160+
// Set up an interval to send a heartbeat ping once every minute.
161+
// The firmware requires at least one ping per 15 minutes, so this should be more than enough.
162+
this.heartbeatInterval = setInterval(() => {
163+
this.heartbeat().catch((err) => {
164+
console.error('Heartbeat error', err);
165+
});
166+
}, 60*1000);
154167
} else {
155168
console.log("not readable or writable");
156169
}
@@ -180,6 +193,12 @@ export class SerialConnection extends MeshDevice {
180193
if (this.port?.readable) {
181194
await this.port?.close();
182195
}
196+
197+
// stop the interval when disconnecting.
198+
if (this.heartbeatInterval) {
199+
clearInterval(this.heartbeatInterval);
200+
this.heartbeatInterval = undefined;
201+
}
183202
// -------
184203
this.updateDeviceStatus(Types.DeviceStatusEnum.DeviceDisconnected);
185204
this.complete();

src/meshDevice.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,23 @@ export abstract class MeshDevice {
735735
return this.sendRaw(toBinary(Protobuf.Mesh.ToRadioSchema, toRadio));
736736
}
737737

738+
/** Serial connection requires a heartbeat ping to stay connected, otherwise times out after 15 minutes */
739+
public heartbeat(): Promise<number> {
740+
this.log.debug(
741+
Types.Emitter[Types.Emitter.Ping],
742+
"❤️ Send heartbeat ping to radio",
743+
);
744+
745+
const toRadio = create(Protobuf.Mesh.ToRadioSchema, {
746+
payloadVariant: {
747+
case: "heartbeat",
748+
value: {},
749+
},
750+
});
751+
752+
return this.sendRaw(toBinary(Protobuf.Mesh.ToRadioSchema, toRadio));
753+
}
754+
738755
/** Sends a trace route packet to the designated node */
739756
public async traceRoute(destination: number): Promise<number> {
740757
const routeDiscovery = create(Protobuf.Mesh.RouteDiscoverySchema, {

0 commit comments

Comments
 (0)