Read Accelerometer Values and store them in SQL Server

Hi all,
since a few days I got stuck with my project.
What I am trying to achieve is the following:
Setup:
Arduino Nano 33 IOT
Node-Red
MS-SQL Server

Idea is to read Accelerometer Values from the Arduino with Node-Red Modbus requests. Afterwards they should be stored in the database.

Where I am having trouble with, is the conversion of the values since modbus is working with integer registers.

Arduino Code:


#include <Arduino_LSM6DS3.h>
#include <SPI.h>
#include <WiFiNINA.h> //


#include <ArduinoRS485.h> // ArduinoModbus depends on the ArduinoRS485 library
#include <ArduinoModbus.h>

#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;                 // your network key Index number (needed only for WEP)

const int ledPin = LED_BUILTIN;

int status = WL_IDLE_STATUS;

WiFiServer wifiServer(502);

ModbusTCPServer modbusTCPServer;

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial);

  Serial.println("Modbus TCP Server IMU Value");

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");

    while (1);
  }

  Serial.print("Accelerometer sample rate = ");
  Serial.print(IMU.accelerationSampleRate());
  Serial.println(" Hz");
  Serial.println();
  Serial.println("Acceleration in g's");
  Serial.println("X\tY\tZ");

  // attempt to connect to WiFi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(5000);
  }

  // you're connected now, so print out the status:
    printWifiStatus();

  // start the server
  wifiServer.begin();

  // start the Modbus TCP server
  if (!modbusTCPServer.begin()) {
    Serial.println("Failed to start Modbus TCP Server!");
    while (1);
  }

  // configure the LED
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  // configure a single coil at address 100
  //modbusTCPServer.configureCoils(100, 1);
  modbusTCPServer.configureHoldingRegisters(0x01, 1);
}

void loop() {
  // listen for incoming clients
  WiFiClient client = wifiServer.available();
  
  if (client) {
    // a new client connected
    Serial.println("new client");

    // let the Modbus TCP accept the connection 
    modbusTCPServer.accept(client);

    while (client.connected()) {
      // poll for Modbus TCP requests, while client connected
      modbusTCPServer.poll();

      // update the LED
      readIMUvalue();
    }

    Serial.println("client disconnected");
  }
}

void readIMUvalue() {
  // read the current value of the coil
  
  float x, y, z;
  uint16_t IMUValue ;

  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(x, y, z);
    IMUValue = z ;
    Serial.println(IMUValue);
  }
  
  
  modbusTCPServer.holdingRegisterWrite(0x01,IMUValue);

}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

My Approach:
So the Arduino reads the IMU value as float in g's (for example: 0.79g). In order to convert the values I use uint16_t to store it in another variable. This value "IMUValue" is then used to write to the holding registers.

At the other end I setup node-red as following

[
    {
        "id": "21d7c6feb6ed492d",
        "type": "modbus-read",
        "z": "4e100d14e53b3afc",
        "name": "Nano 33 IoTp",
        "topic": "Analog Input",
        "showStatusActivities": false,
        "logIOActivities": false,
        "showErrors": false,
        "unitid": "1",
        "dataType": "HoldingRegister",
        "adr": "1",
        "quantity": "1",
        "rate": "500",
        "rateUnit": "ms",
        "delayOnStart": false,
        "startDelayTime": "",
        "server": "d6e277a1fd05509b",
        "useIOFile": false,
        "ioFile": "",
        "useIOForPayload": false,
        "emptyMsgOnFail": false,
        "x": 170,
        "y": 80,
        "wires": [
            [
                "b3ed451bdca4e088"
            ],
            []
        ]
    },
    {
        "id": "f008fd4bb8646cd1",
        "type": "debug",
        "z": "4e100d14e53b3afc",
        "name": "debug 4",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 700,
        "y": 80,
        "wires": []
    },
    {
        "id": "b3ed451bdca4e088",
        "type": "buffer-maker",
        "z": "4e100d14e53b3afc",
        "name": "",
        "specification": "spec",
        "specificationType": "ui",
        "items": [
            {
                "name": "item1",
                "type": "floatle",
                "length": 1,
                "dataType": "msg",
                "data": "payload"
            }
        ],
        "swap1": "swap16",
        "swap2": "",
        "swap3": "",
        "swap1Type": "swap",
        "swap2Type": "swap",
        "swap3Type": "swap",
        "msgProperty": "payload",
        "msgPropertyType": "str",
        "x": 450,
        "y": 80,
        "wires": [
            [
                "f008fd4bb8646cd1"
            ]
        ]
    },
    {
        "id": "d6e277a1fd05509b",
        "type": "modbus-client",
        "name": "Arduino",
        "clienttype": "tcp",
        "bufferCommands": true,
        "stateLogEnabled": true,
        "queueLogEnabled": true,
        "failureLogEnabled": true,
        "tcpHost": "192.168.0.194",
        "tcpPort": "502",
        "tcpType": "DEFAULT",
        "serialPort": "/dev/ttyUSB",
        "serialType": "RTU-BUFFERD",
        "serialBaudrate": "115200",
        "serialDatabits": "8",
        "serialStopbits": "1",
        "serialParity": "none",
        "serialConnectionDelay": "100",
        "serialAsciiResponseStartDelimiter": "0x3A",
        "unit_id": "1",
        "commandDelay": "1",
        "clientTimeout": "1000",
        "reconnectOnTimeout": true,
        "reconnectTimeout": "2000",
        "parallelUnitIdsAllowed": true
    }
]

What I now got as buffer is:
image

But what I would need at the end would be the decimal values again.
Maybe somebody has any advice where I left missing pices.

Thank you in advance!
Greetz ardakus

Is there a reason you are not using mqtt for the Comms?

Thank you for the hint.
Not really....
But what I liked the most for my concept was that with modbus I would get sensor values only when I need/ask for them (server answer from client request). Instead of constanly "streaming" sensor data.

The disadvantage being, of course, that you have to ask for the current value and so will often repeatedly get the same value, instead of being told each time it changes so that you can take appropriate action (logging the new value to a time series db for example).

Is that buffer you show in the debug what is coming direct from the modbus node? Normally the recommended method would be to use node-red-contrib-buffer-parser to turn that back into real values.

I can follow your thoughts, for long term monitoring with long change intervals it makes sense to be informed when the values changes.

To describe my goal further:
I want to meassure the IMU values for a specific duration, lets say 15 seconds. In this period the Acceleration Sensor is performing up, and down movements (aproxx. sine waves). When the period elapsed I stop reading from the sensor. So it is not like a "level monitoring" system it should more act like a real time monitoring for a certain time period.

The buffer from the debug is right after the buffer-maker node (from node-red-contrib-buffer-parser)

Correct me if I am wrong.

You are writing values to a ModBus server on your device. You want Node-RED to poll this server and get the value?

So why are you using a buffer-maker? Surely you should be parsing the buffer received from the device into sensible useable values (using buffer-parser)

What is the output of the ModBus node...
image

(Tip: use the "Copy Value" on the debug output and use the forum toolbar button </> to paste the captured debug value in a code block)

Yes thats right.

image

Debug 6 shows:

{"topic":"Analog Input","payload":[0],"responseBuffer":{"data":[0],"buffer":[0,0]},"input":{"topic":"Analog Input","from":"Nano 33 IoT","payload":{"unitid":"1","fc":3,"address":"1","quantity":"1","messageId":"63823e6899cd17a20fcfa618"},"queueLengthByUnitId":{"unitId":1,"queueLength":0},"queueUnitId":1,"unitId":1},"sendingNodeId":"21d7c6feb6ed492d","_msgid":"4973578e405bf67f"}

Debug 7 shows:

{"topic":"Analog Input","payload":{"data":[0],"buffer":[0,0]},"values":[0],"input":{"topic":"Analog Input","from":"Nano 33 IoT","payload":{"unitid":"1","fc":3,"address":"1","quantity":"1","messageId":"63823e6899cd17a20fcfa618"},"queueLengthByUnitId":{"unitId":1,"queueLength":0},"queueUnitId":1,"unitId":1},"sendingNodeId":"21d7c6feb6ed492d","_msgid":"4973578e405bf67f"}

Hmmm, ok, so that data is basically zeros.

I cant really help you convert it if 1) it is zero and 2) you dont tell me what format the data should be (16bit signed integer? float? 32 bit? etc etc)

You need to provide useful sample data and explain what you see and what you want to see for me to be able to help.

Once we have that down, I can show you how to store in a SQL Server.

You are right I switched back to the arduino code and made some correction. For me it seems I am geeting values now.
image

{"topic":"Analog Input","payload":[1626],"responseBuffer":{"data":[1626],"buffer":[6,90]},"input":{"topic":"Analog Input","from":"Nano 33 IoT","payload":{"unitid":"1","fc":3,"address":"0","quantity":"1","messageId":"63833e2e5fd26869a84349de"},"queueLengthByUnitId":{"unitId":1,"queueLength":0},"queueUnitId":1,"unitId":1},"sendingNodeId":"21d7c6feb6ed492d","_msgid":"f18a633e6fd7a8dd"}

Arduiono Output Plotter of the IMU Sensor

The next thing is the needed format. For the project the total value doesnt matter. I only need to display the sine wave of the values and calculate the amount of maxima.

Further Questions I am investigating:

  • How to store the Values in a database?
  • How to setup nodes that the modbus values requests take place in a timeframe (e.g. 15 seconds) and then stops to request until a new time frame is triggered?

Short Update
I figured out:

  • How to store the values in SQL Server
  • How to trigger modbus requests periodically within a timeframe

Now my problem concerns the join node.

Because I need to have an ID for each meassurement (which summarizes all sensor readings within a timeframe) I need to join the modbus response with the counter node.

Debug Message Error says:

Cannot merge non-object types

image

Is my approach just not configured right or am I completly going in the wrong direction?

THX!

You don't need a join node. Simply copy the counter value into a property of the msg (e.g. msg.counter) and then pick it up again at the SQL-PLUS node using the parameters.

1 Like

Oh ok.
I tried it with following setup

Counter Output msg.count with the value.
The thing is this only happens once. Result in the database ist first value correct with the counter value, afterwards I get "NULL". hmm.... I guess I need to tell the nodes to output the counter value more often.

DB screenshot:
image

You need everthing in series.

The counters msg will ALWAYS arrive at the SQL node at a different point in time to the msg from the ModBus getter. This is a minimum level of understanding required to get the best from node-red. What I was inferring was pass the msg through each node in series (not parallel)

Let me expand a little on this :point_up_2:

  1. Remove that loop node
  2. Copy the counter value into a property of the msg (e.g. msg.counter)
  3. Pass the counter message to the ModBus node
    1. Open the modbus node and ensure the option to Keep msg properties is ticked
      image
  4. Pass the result of modbus to SQL node
  5. In the SQL node, use add a parameter in the params list to grab the msg.count value
1 Like

Yeah I also read about that fact in other posts. But at that moment it made no sense to me. Now I have an example of what it means.

Thank you, "Keep Msg Properties" was the missing thing. I followed your instructions and it is much clearer now to me.
Also picking up the msg property worked flawless.

image

Totally amazed about the functions node-red can do, and also the community around it! THX!

Further functions I need/want to add:

  • parse the values to a chart node in the dashboards --> or is it better to load them from the database afterwards instead of lengthening the processing part?

  • execute calculations on the stored values --> do you think it is better to move to other tools like matlabplotlib? Or is node-red also suitable for calculations for example calculating lokal maxima/minima?

These are questions that would be better in a new separate topic.

Start new topic and give a good title (to attract the right users) & describe what you have and what you want.

You can always link back to this post for reference if necessary.

Please mark the post you believe is the solution to your original question "Read Accelerometer Values and store them in SQL Server"

1 Like

Yes good point, lets do so!

Attached the endresult of the flows:

[
    {
        "id": "44742b670cb8f0fd",
        "type": "modbus-response",
        "z": "4e100d14e53b3afc",
        "name": "",
        "registerShowMax": 20,
        "x": 790,
        "y": 360,
        "wires": []
    },
    {
        "id": "b349e2d85ced3863",
        "type": "modbus-getter",
        "z": "4e100d14e53b3afc",
        "name": "",
        "showStatusActivities": false,
        "showErrors": false,
        "logIOActivities": false,
        "unitid": "1",
        "dataType": "HoldingRegister",
        "adr": "0",
        "quantity": "1",
        "server": "d6e277a1fd05509b",
        "useIOFile": false,
        "ioFile": "",
        "useIOForPayload": false,
        "emptyMsgOnFail": false,
        "keepMsgProperties": true,
        "x": 680,
        "y": 300,
        "wires": [
            [
                "249bcc438f8d4ab6"
            ],
            [
                "44742b670cb8f0fd"
            ]
        ]
    },
    {
        "id": "f9f7535a3f72d09c",
        "type": "controltimer",
        "z": "4e100d14e53b3afc",
        "name": "",
        "timerType": "loop",
        "timerDurationUnit": "millisecond",
        "timerDurationType": "num",
        "timerDuration": 50,
        "timerLoopTimeoutUnit": "second",
        "timerLoopTimeoutType": "num",
        "timerLoopTimeout": 2,
        "loopTimeoutMessageType": "str",
        "loopTimeoutMessage": "LOOP_TIMEOUT",
        "timerMaxLoopIterationsType": "num",
        "timerMaxLoopIterations": 100,
        "loopMaxIterationsMessageType": "str",
        "loopMaxIterationsMessage": "MAX_LOOP_ITERATIONS",
        "isConsecutiveStartActionTimerResetAllowed": false,
        "isRunningTimerProgressVisible": true,
        "outputReceivedMessageOnTimerTrigger": true,
        "outputReceivedMessageOnTimerHalt": false,
        "startTimerOnReceivalOfUnknownMessage": false,
        "resetTimerOnReceivalOfUnknownMessage": false,
        "isDebugModeEnabled": false,
        "timerTriggeredMessageType": "str",
        "timerTriggeredMessage": "TIMER_TRIGGERED",
        "timerHaltedMessageType": "str",
        "timerHaltedMessage": "TIMER_HALTED",
        "isStartActionEnabled": true,
        "isStopActionEnabled": true,
        "isResetActionEnabled": true,
        "isPauseActionEnabled": true,
        "isContinueActionEnabled": true,
        "actionPropertyNameType": "str",
        "actionPropertyName": "timer",
        "startActionNameType": "str",
        "startActionName": "START",
        "stopActionNameType": "str",
        "stopActionName": "STOP",
        "resetActionNameType": "str",
        "resetActionName": "RESET",
        "pauseActionNameType": "str",
        "pauseActionName": "PAUSE",
        "continueActionNameType": "str",
        "continueActionName": "CONTINUE",
        "x": 490,
        "y": 240,
        "wires": [
            [
                "b349e2d85ced3863"
            ],
            []
        ]
    },
    {
        "id": "5e7e3eb79732d377",
        "type": "inject",
        "z": "4e100d14e53b3afc",
        "name": "Start Meassurement",
        "props": [
            {
                "p": "timer",
                "v": "START",
                "vt": "str"
            },
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "0",
        "payloadType": "num",
        "x": 150,
        "y": 60,
        "wires": [
            [
                "ce8d952f.18666"
            ]
        ]
    },
    {
        "id": "249bcc438f8d4ab6",
        "type": "MSSQL",
        "z": "4e100d14e53b3afc",
        "mssqlCN": "5558b0cd5f739949",
        "name": "",
        "outField": "payload",
        "returnType": 0,
        "throwErrors": 1,
        "query": "INSERT INTO  [t_test]\r\n    (timestamp, IMUValue, meassurementID) \r\nVALUES\r\n    (@ts, @v , @id)\r\n",
        "modeOpt": "queryMode",
        "modeOptType": "query",
        "queryOpt": "",
        "queryOptType": "editor",
        "paramsOpt": "",
        "paramsOptType": "editor",
        "rows": "rows",
        "rowsType": "msg",
        "params": [
            {
                "output": false,
                "name": "ts",
                "type": "DateTime2",
                "valueType": "datetime",
                "value": "0",
                "options": {
                    "nullable": true,
                    "primary": false,
                    "identity": false,
                    "readOnly": false
                }
            },
            {
                "output": false,
                "name": "v",
                "type": "int",
                "valueType": "msg",
                "value": "payload",
                "options": {
                    "nullable": true,
                    "primary": false,
                    "identity": false,
                    "readOnly": false
                }
            },
            {
                "output": false,
                "name": "id",
                "type": "Int",
                "valueType": "msg",
                "value": "count",
                "options": {
                    "nullable": true,
                    "primary": false,
                    "identity": false,
                    "readOnly": false
                }
            }
        ],
        "x": 880,
        "y": 240,
        "wires": [
            []
        ]
    },
    {
        "id": "ce8d952f.18666",
        "type": "function",
        "z": "4e100d14e53b3afc",
        "name": "msg object",
        "func": "msg.increment = 1;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 250,
        "y": 120,
        "wires": [
            [
                "ac65bf94.6e609"
            ]
        ]
    },
    {
        "id": "ac65bf94.6e609",
        "type": "counter",
        "z": "4e100d14e53b3afc",
        "name": "",
        "init": "0",
        "step": 1,
        "lower": null,
        "upper": null,
        "mode": "increment",
        "outputs": 1,
        "x": 360,
        "y": 180,
        "wires": [
            [
                "f9f7535a3f72d09c"
            ]
        ]
    },
    {
        "id": "d6e277a1fd05509b",
        "type": "modbus-client",
        "name": "Arduino",
        "clienttype": "tcp",
        "bufferCommands": true,
        "stateLogEnabled": true,
        "queueLogEnabled": true,
        "failureLogEnabled": true,
        "tcpHost": "192.000.0.000",
        "tcpPort": "502",
        "tcpType": "DEFAULT",
        "serialPort": "/dev/ttyUSB",
        "serialType": "RTU-BUFFERD",
        "serialBaudrate": "115200",
        "serialDatabits": "8",
        "serialStopbits": "1",
        "serialParity": "none",
        "serialConnectionDelay": "100",
        "serialAsciiResponseStartDelimiter": "0x3A",
        "unit_id": "0",
        "commandDelay": "1",
        "clientTimeout": "1000",
        "reconnectOnTimeout": true,
        "reconnectTimeout": "2000",
        "parallelUnitIdsAllowed": true
    },
    {
        "id": "5558b0cd5f739949",
        "type": "MSSQL-CN",
        "tdsVersion": "7_4",
        "name": "",
        "server": "localhost",
        "port": "1433",
        "encyption": true,
        "trustServerCertificate": true,
        "database": "Node_Red_TestDB",
        "useUTC": true,
        "connectTimeout": "15000",
        "requestTimeout": "15000",
        "cancelTimeout": "5000",
        "pool": "5",
        "parseJSON": false,
        "enableArithAbort": true
    }
]

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.