Skip to main content

Writing a Connhex Edge service

After connecting your first edge, it's now time to have it send some data.

In this example, we will implement a virtual sensor (a.k.a. mock data) for battery charge state.

We'll then turn the readings into a SenML message and create an update loop to publish them at scheduled intervals. We'll also take care of listening for incoming messages from Connhex Cloud.

SenML format

In this example, we'll be using SenML since it is a great choice for many use cases. Note that it is not strictly required: as discussed in depth here, Connhex natively supports multiple message formats.

Finally, we'll use an heartbeat message to notify the Connhex Edge agent that our service is up and running. Let's get started!


This simple example is available in JavaScript and Python: this means your device needs to have Node.js or Python installed.

You'll also need to install the NATS package:

npm i nats
Not supported?

If your device cannot run Node.js or Python, porting this service to any other language for which a NATS client is available should be straightforward.

const { connect, JSONCodec } = require('nats');

const jc = JSONCodec();
const SERVICE_NAME = 'diagnostic';

// Sends a heartbeat message every 10s to notify
// connhex-edge-agent that this service is working properly.
// By default the connhex-edge-agent checks that all services have sent
// a heartbeat message within the last 10s (customizable).
// If a service didn't, it is marked as offline.
const startHeartbeat = (nc) => {
const intervalId = setInterval(() => {
console.log('Publishing heartbeat...');
// Any custom logic that checks that everything is ok should be added here.
}, 10000);

return {
stop: () => clearInterval(intervalId),

const startUpdateLoop = (nc) => {
const intervalId = setInterval(() => {
// Build a SenML message
const msg = [
t: / 1000,
n: `urn:cpt:${SERVICE_NAME}:battery-charge`,
u: '%EL',
v: Math.floor(Math.random() * (100 + 1)),
console.log(`Sending message: ${JSON.stringify(msg)}`);

nc.publish(``, jc.encode(msg));
}, 60000);

return {
stop: () => clearInterval(intervalId),

(async () => {
const handleExit = () => {
.then(() => updateLoop.stop())
.then(() => heartbeat.stop())
.then(() => process.exit(0));
process.on('SIGINT', handleExit);
process.on('SIGQUIT', handleExit);
process.on('SIGTERM', handleExit);

const nc = await connect();
console.log('NATS connected');

const updateLoop = startUpdateLoop(nc);
const heartbeat = startHeartbeat(nc);

// Subscribe to NATS subject and listen for commands received.
// ">" is used as wildcard, check
sub = nc.subscribe(`commands.${SERVICE_NAME}.>`);
for await (const m of sub) {
`Received command: [${m.subject}] ${JSON.stringify(jc.decode(}`

Go back to Connhex Control and navigate to the device detail page. You should see the diagnostic service detected as online and the raw messages printed in the Last messages section.

Connhex Control: custom service running

Sending custom commands to the service

We can interact with the diagnostic service by sending custom commands directly from Connhex Control.

Connhex Edge listens for messages addressed to custom services in the MQTT services subtopic. These messages are then mapped internally to the commands NATS subject (you can find more details here).

For example, to target our service listening on the commands.diagnostic.> NATS subject, we can send our MQTT messages to the services.diagnostic.test subtopic.

Connhex Control: custom commands

If we check the console of the device running the example, we should see the following output, indicating that the message was correctly received and processed by the service!

Received command: [test] {
name: 'test-command',
value: 'test-value',
chanID: '6d92469d-f16c-4c12-98d6-cdc4d2dc2792',
subtopic: 'services.diagnostic.test',
time: '1705396975448',
valType: 'string'

What's next?

Now that we have a Connhex Edge service ready, let's look at how we can get this ready for production.