diff --git a/src/lib/device.ts b/src/lib/device.ts
index b5ac9753e3262eee5a4f5aa9345bf192be7eac7e..dcb128389b51fb14ef29d0ff33fe2c6fe1b4ccd4 100644
--- a/src/lib/device.ts
+++ b/src/lib/device.ts
@@ -35,7 +35,7 @@ export default abstract class Device extends js.global.EventEmitter {
 	private readyEvent: number;
 	private heartbeatInterval: number;
 	private heartbeatEvent: number;
-	private lastHeartbeat?: number;
+	private lastWrite: number;
 	private initTime: number;
 	private initTimeout: number;
 	private readonly timeoutMs: number = RESPONSE_TIMEOUT * 1000;
@@ -58,8 +58,9 @@ export default abstract class Device extends js.global.EventEmitter {
 		this.ackHandlers = {};
 		this.responseHandlers = {};
 		this.purgeEvent = js.setInterval(this.purgeHandlers, this.timeoutMs, this);
-		this.heartbeatInterval = heartbeatInterval;
+		this.heartbeatInterval = heartbeatInterval * 1000;
 		this.heartbeatEvent = -1;
+		this.lastWrite = Date.now();
 		this.initTime = Date.now();
 		this.initTimeout = CONNECT_TIMEOUT * 1000;
 	}
@@ -126,10 +127,7 @@ export default abstract class Device extends js.global.EventEmitter {
 		if (!this.ready && now - this.initTime > this.initTimeout) this.error(`initialization timeout (configCompleteId not received from device)`);
 		this.read();
 		this.purgeHandlers();
-		if (this.lastHeartbeat === undefined || now - this.lastHeartbeat >= this.heartbeatInterval) {
-			this.lastHeartbeat = now;
-			this.sendHeartbeat();
-		}
+		this.sendHeartbeat();
 		this._cycle();
 	}
 
@@ -187,7 +185,7 @@ export default abstract class Device extends js.global.EventEmitter {
 					if (this.readyEvent > -1) js.clearTimeout(this.readyEvent);
 					this.emit('ready');
 					// @ts-expect-error shut up
-					this.heartbeatEvent = js.setInterval(function () { this.sendHeartbeat(); }, 30000, this)
+					this.heartbeatEvent = js.setInterval(function () { this.sendHeartbeat(); }, this.heartbeatInterval, this)
 				}
 				break;
 			case 'metadata':
@@ -278,7 +276,9 @@ export default abstract class Device extends js.global.EventEmitter {
 		if (data.length > MAXLEN) this.error(`write: packet length ${data.length} exceeds maximum length ${MAXLEN}`);
 		const header = [START1, START2, (data.length>>8)&0xFF, data.length&0xFF];
 		const buffer = new Uint8Array([...header, ...data]);
-		return this.sendToDevice(buffer);
+		const res = this.sendToDevice(buffer);
+		if (res) this.lastWrite = Date.now();
+		return res;
 	}
 
 	/** Assembles a [MeshPacket](https://buf.build/meshtastic/protobufs/docs/main:meshtastic#meshtastic.MeshPacket) based on your parameters and writes it to the device. */
@@ -584,6 +584,7 @@ export default abstract class Device extends js.global.EventEmitter {
 
 	/** You probably don't need to call this directly. It's only really needed for SerialDevice, and is handled there for you even in synchronous scripts. */
 	sendHeartbeat(): void {
+		if (Date.now() - this.lastWrite < this.heartbeatInterval) return;
 		const toRadio: protobuf.Mesh.ToRadio = new protobuf.Mesh.ToRadio({
 			payloadVariant: {
 				case: 'heartbeat',