Count number of sensors reporting data

We have multiple sensors(16 for now) sending data to node-red. Data is collected via BLE gateway and transferred to raspberry Pi with serial input. This data is stored in InlfuxDB database. till now our flow is working fine. Sometimes few sensors stop sending data. We want to count the number of sensors sending data, data is sent every 20s and I have no idea how to do it. any help will be highly appreciated.

current flow

[
    {
        "id": "68a21d34.8d0b84",
        "type": "tab",
        "label": "Serial",
        "disabled": false,
        "info": ""
    },
    {
        "id": "4833802b.27dee",
        "type": "serial in",
        "z": "68a21d34.8d0b84",
        "name": "Read Serial Port ACM0",
        "serial": "a9f36f3f50bd604c",
        "x": 120,
        "y": 220,
        "wires": [
            [
                "9861f7fa.7a7088",
                "dee6b8e7.b18128"
            ]
        ]
    },
    {
        "id": "9861f7fa.7a7088",
        "type": "debug",
        "z": "68a21d34.8d0b84",
        "name": "Raw Serial Data Debug",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 330,
        "y": 100,
        "wires": []
    },
    {
        "id": "dee6b8e7.b18128",
        "type": "json",
        "z": "68a21d34.8d0b84",
        "name": "JSON Parser",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 330,
        "y": 220,
        "wires": [
            [
                "a6e48650b73e1ba4",
                "d421a66d.07b808"
            ]
        ]
    },
    {
        "id": "3c096111.cb0c7e",
        "type": "delay",
        "z": "68a21d34.8d0b84",
        "name": "",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "20",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 840,
        "y": 160,
        "wires": [
            [
                "b40fb3f0.d07c2",
                "a3f2f50e.ac1dd8",
                "231a5f0c.71f17"
            ]
        ]
    },
    {
        "id": "a3f2f50e.ac1dd8",
        "type": "debug",
        "z": "68a21d34.8d0b84",
        "name": "DB Payload Debug",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 990,
        "y": 320,
        "wires": []
    },
    {
        "id": "d421a66d.07b808",
        "type": "function",
        "z": "68a21d34.8d0b84",
        "name": "Database Mapping",
        "func": "var lookupRoom = [ \n{\"name\":\"officeS01\",\t\"floor\":\"Floor00\",  \"id\":\"5\" },\n{\"name\":\"livingroom\",\t\"floor\":\"Floor00\",  \"id\":\"6\" },\n{\"name\":\"diningroom\",\t\"floor\":\"Floor00\",  \"id\":\"7\" },\n{\"name\":\"kitchen\", \t    \"floor\":\"Floor00\",  \"id\":\"8\" },\n{\"name\":\"laundry\",  \t\"floor\":\"Floor00\",  \"id\":\"9\" },\n{\"name\":\"backdoor\", \t\"floor\":\"Floor00\",  \"id\":\"10\"},\n{\"name\":\"bedroom01\", \t\"floor\":\"Floor01\",  \"id\":\"11\"},\n{\"name\":\"bathroom02\", \t\"floor\":\"Floor01\",  \"id\":\"12\"},\n{\"name\":\"bedroom03\", \t\"floor\":\"Floor01\",  \"id\":\"13\"},\n{\"name\":\"entry\", \t    \"floor\":\"Floor00\",  \"id\":\"14\"},\n{\"name\":\"basement\", \t\"floor\":\"Floor-01\", \"id\":\"a\" },\n{\"name\":\"storageroom\", \t\"floor\":\"Floor00\",  \"id\":\"b\" },\n{\"name\":\"bathroom01\", \t\"floor\":\"Floor01\",  \"id\":\"c\" },\n{\"name\":\"officeS02\", \t\"floor\":\"Floor00\",  \"id\":\"d\" },\n{\"name\":\"upstairs\", \t\"floor\":\"Floor01\",  \"id\":\"e\" },\n{\"name\":\"bedroom02\", \t\"floor\":\"Floor01\",  \"id\":\"f\" },\n];\nvar lookupRoomMatch = lookupRoom.find(el => msg.payload[\"02\"] === el.id);\n\n// var msgTime =  new Date(msg.payload[\"03\"]).getTime() * 1e6;\nvar msgTime =  new Date(msg.payload[\"03\"]);\n\n\n\nif(lookupRoomMatch) {\n   var loc = lookupRoomMatch.name;\n    var floor = lookupRoomMatch.floor;\n}\n\n  msg.payload = [\n       {\n        measurement: \"enviro_data\",\n        fields: {\n            \"temperature\": msg.payload[\"04\"],\n            \"humidity\":  msg.payload[\"05\"],\n            \"pressure\": msg.payload[\"06\"],\n\n            \"gas_resistance\": msg.payload[\"07\"],\n            \"gas_percentage\": msg.payload[\"08\"],\n            \"iaq\":  msg.payload[\"09\"]\n        },\n        tags:{\n        \"ID\": msg.payload[\"02\"],\n        [floor] : loc\n        },\n        \"timestamp\": msgTime\n    }, \n\n        {    \n        measurement: \"energy_data\",\n        fields: {\n            \"light\": msg.payload[\"10\"],\n            \"occupancy\": msg.payload[\"11\"]\n        },\n        \n        tags:{\n        \"ID\": msg.payload[\"02\"],\n        [floor] : loc\n        },\n        \"timestamp\": msgTime\n    }, \n    \n        {\n        measurement: \"sensor_state\",\n        fields: {\n            \"battery\": msg.payload[\"12\"],\n            \"iaq_accuracy\": msg.payload[\"13\"],\n            \"stability\": msg.payload[\"14\"]\n        },\n        \n        tags:{\n        \"ID\": msg.payload[\"02\"],\n        [floor] : loc\n        },\n\n        \"timestamp\": msgTime\n    },\n]\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 630,
        "y": 220,
        "wires": [
            [
                "3c096111.cb0c7e"
            ]
        ],
        "info": "# Database Mapping\n\nThis node has changed substantially. \n\nIt has three primary sections.\n\nThe first maps the Node ID to a human recognizable name e.g. office01, and assigns a building floor number. These are used as tags in the database to allow more useful grouping and identification of different nodes.\n\nThe second section defines some variables, including the message timestamp in Unix Epoch time and the name and floor of the sensor node.\n\n**NOTE:** The database batch write node time precision must be set to *default*.\n\nThe third section defines the measurements being written to the database. We define three different groups, \"enviro_data\",\n(temperature, humidity, pressure, gas resistance, gas percentage, IAQ), \"energy data\" (ambient light and occupancy) and \"sensor state\" (battery, iaq_accuracy, stability)."
    },
    {
        "id": "231a5f0c.71f17",
        "type": "influxdb batch",
        "z": "68a21d34.8d0b84",
        "influxdb": "a02ac971.fe5158",
        "precision": "s",
        "retentionPolicy": "",
        "name": "Batch Write to Cloud",
        "database": "",
        "retentionPolicyV18Flux": "",
        "org": "",
        "bucket": "",
        "x": 1060,
        "y": 220,
        "wires": []
    },
    {
        "id": "b40fb3f0.d07c2",
        "type": "influxdb batch",
        "z": "68a21d34.8d0b84",
        "influxdb": "180e2781.eb4b68",
        "precision": "s",
        "retentionPolicy": "",
        "name": "Batch Write to Local",
        "database": "",
        "retentionPolicyV18Flux": "",
        "org": "",
        "bucket": "",
        "x": 1060,
        "y": 160,
        "wires": []
    },
    {
        "id": "a9822510.912868",
        "type": "catch",
        "z": "68a21d34.8d0b84",
        "name": "Error Reporting",
        "scope": null,
        "uncaught": false,
        "x": 100,
        "y": 600,
        "wires": [
            [
                "60ca9efa.67893"
            ]
        ]
    },
    {
        "id": "60ca9efa.67893",
        "type": "debug",
        "z": "68a21d34.8d0b84",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 310,
        "y": 600,
        "wires": []
    },
    {
        "id": "a6e48650b73e1ba4",
        "type": "debug",
        "z": "68a21d34.8d0b84",
        "name": "Raw JSON Debug",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 510,
        "y": 380,
        "wires": []
    },
    {
        "id": "0bafb4072b5e810f",
        "type": "comment",
        "z": "68a21d34.8d0b84",
        "name": "Click to Read Me",
        "info": "# Error Reporting\n\nThe error reporting node will automatically catch and display any errors as the flow runs.  Errors are displayed in the debug panel. If a message is missing a character, or has an unexpected character, it will show up there.",
        "x": 100,
        "y": 640,
        "wires": []
    },
    {
        "id": "199c97093567a5ef",
        "type": "comment",
        "z": "68a21d34.8d0b84",
        "name": "Click to Read Me",
        "info": "# Database Mapping\n\nThis node has changed substantially. \n\nIt has three primary sections.\n\nThe first maps the Node ID to a human recognizable name e.g. office01, and assigns a building floor number. These are used as tags in the database to allow more useful grouping and identification of different nodes.\n\nThe second section defines some variables, including the message timestamp in Unix Epoch time (nanoseconds) and the name and floor of the sensor node.\n\n**NOTE:** The database batch write node time precision must be set to *default*.\n\nThe third section defines the measurements being written to the database. We define three different groups:\n\n\"enviro data\" : temperature, humidity, pressure, gas resistance, gas percentage, IAQ\n\"energy data\" : ambient light and occupancy\n\"sensor state\" : battery, iaq_accuracy, stability",
        "x": 620,
        "y": 260,
        "wires": []
    },
    {
        "id": "a1b3a56e0cc39d3f",
        "type": "comment",
        "z": "68a21d34.8d0b84",
        "name": "Click to Read Me",
        "info": "# Batch Write to Database\n\nWe are performing dual writes to a cloud instance of InfluxDB and the local instance running on the Raspberry Pi.\n\n**Please make sure the cloud write is disabled**\n",
        "x": 1040,
        "y": 100,
        "wires": []
    },
    {
        "id": "a9f36f3f50bd604c",
        "type": "serial-port",
        "serialport": "/dev/ttyACM0",
        "serialbaud": "57600",
        "databits": "8",
        "parity": "none",
        "stopbits": "1",
        "waitfor": "",
        "dtr": "none",
        "rts": "none",
        "cts": "none",
        "dsr": "none",
        "newline": "\\n",
        "bin": "false",
        "out": "char",
        "addchar": "",
        "responsetimeout": "10000"
    },
    {
        "id": "a02ac971.fe5158",
        "type": "influxdb",
        "hostname": "url",
        "port": "port",
        "protocol": "http",
        "database": "db",
        "name": "Basic 4 Batch",
        "usetls": false,
        "tls": "",
        "influxdbVersion": "1.x",
        "url": "http://localhost:8086",
        "rejectUnauthorized": true
    },
    {
        "id": "180e2781.eb4b68",
        "type": "influxdb",
        "hostname": "influxdb",
        "port": "8086",
        "protocol": "http",
        "database": "sensorLocal",
        "name": "influxdb",
        "usetls": false,
        "tls": "",
        "influxdbVersion": "1.x",
        "url": "http://localhost:8086",
        "rejectUnauthorized": true
    }
]```

Besides that I would assume you should be interested to know which sensors that stopped sending? Just counting them would only give you information that all are ok

What I have done myself in all my flows is to have a trigger node for each sensor. Every time the sensor message arrive the trigger is restarted. As long as this happnes within the defined time frame, nothing happens. If a sensor stops reporting, the time runs out and triggers a message, in my case a message to my Telegram account

Assuming your sensors have some unique id or name, you could do a similar setup

Couldn't you add an extra part to the message which is a number to identify which sensor is sending the data?

yes that is right, our sensors have unique id, can you share your flow? i want to have a look.

Here is a solution for a similar requirement. It should work for you...

... Just change topic to the path of your id

When you are checking if sensors are still alive it is from my perspective just interesting to monitor that they do report within a defined time interval. If you really have the task to count the number of messages, the solution already recommended is fine. In addition it could be of interest to check that the message is a valid message, within reasonable limits etc etc.

In my case I am just interested to know they are still alive. This goes for sensors of all kind as well as for running sw processes. This is an example how you can make a simple monitor using the trigger node

let me try and i will report back with more questions.