ArduinoJson serialize floats and reduce number of decimal places

Hello together...

In the following sketch i am reading two 4- 20 mA industrial sensor. One for range, one for force.
I want to send the values over wifi to a App with a Json file using the AduinoJson Library. The problem that i have is, how i can write to the Json the floating values only with 1 or 2 decimal places. Right now it writes until 9 decimal points.
I tried as follow to reduce the decimal points but it does not work.

  range = (int)(unroude_range * 100 + 0.5) / 100.0;

  force = (int)(unroude_force * 100 + 0.5) / 100.0;

Follows the complete funktion where i am reading the sensors writing and sending the Json.
Thanks in advance for some help.

void Read_Scale() {

  String jsonString = "";

  StaticJsonDocument<1000> doc;
  static int scale_counter = 0;

  if (millis() - scale_previousMillis >= scale_interval) {  // read analog value every 500ms

    scale_previousMillis = millis(); // reset the timer

    rawADCValue_range = 0;
    rawADCValue_force = 0;
    for (int i = 0; i < 100; ) {
      if (millis() - range_t  >= 1) {
        rawADCValue_range += analogRead(ADC_Range_Pin);
        rawADCValue_force += analogRead(ADC_Force_Pin);
        i++;
        range_t = millis();
      }
    }

    ADCVoltage_range = (float)(rawADCValue_range / 100.0) * (3.3 / 4095.0);
    Current_range = mapfloat(ADCVoltage_range, 0.8, 3.3, 4, 20);
    unroude_range = mapfloat(Current_range, 4, 20, 0, 50);
    range = (int)(unroude_range * 100 + 0.5) / 100.0;

    ADCVoltage_force = (float)(rawADCValue_force / 100.0) * (3.3 / 4095.0);
    Current_force = mapfloat(ADCVoltage_force, 0.8, 3.3, 4, 20);
    unroude_force = mapfloat(Current_force, 4, 20, 0, 20);
    force = (int)(unroude_force * 100 + 0.5) / 100.0;

    buffer_range[scale_counter] = range ; // store the range value in the buffer
    buffer_force[scale_counter] = force ; // store the force value in the buffer

    scale_counter ++;

    doc["count_json"] = daten.zyklus;
    JsonArray rangeValues = doc.createNestedArray("Range");
    JsonArray forceValues = doc.createNestedArray("Force");

    if (scale_counter >= NUM_SAMPLES)
    {
      for (int i = 0; i < NUM_SAMPLES; i++){
        rangeValues.add(buffer_range[i]);
        forceValues.add(buffer_force[i]);
      }

      scale_counter = 0;

      serializeJson(doc, jsonString);
      webSocket.broadcastTXT(jsonString);
      Serial.println(jsonString);

    }
  }
}

Check this tips from the author of library:

1 Like

@cotestatnt thanks for the reply.

I saw this tip from the author. So. I tried

 range = (int)(unroude_range * 100 + 0.5) / 100.0;

 force = (int)(unroude_force * 100 + 0.5) / 100.0;

but the serializeJson continues having 9 digits instead of 2

I've done a quick test with a small example and it works as stated from the author of library.

1 Like

I will take a look on your example and review my code.

OK.... Ramdomly i realized something.

If i transfer my code to the esp32 using the Arduino IDE, on the Serial Monitor and on my App i see the Json arriving with 2 decimal points.
But if i transfer the code using Visual Studio Code with Platform IO i see the Json with 9 decimal points.


Your example @cotestatnt, i tested also and in both situations using Arduino or Platform IO the values inside the Json are displayed correctly.

I do not undestand , why my code using different IDE´s the result is different?!?!?!?!
Follows my code that i used to test the result showed in the pictures that i posted.

#include <Arduino.h>
#include <ArduinoJson.h>

#define ADC_Range_Pin  35  // Pin Range Sensor
#define ADC_Force_Pin  34  // Pin Force Sensor

unsigned long scale_previousMillis = 0;
const long scale_interval = 100; // Interval 
const byte NUM_SAMPLES = 20;
float buffer_range [NUM_SAMPLES];
float buffer_force [NUM_SAMPLES];

// Range Sensor Instanzen-----------------------------
float Current_range = 0; //4-20 mA
float rawADCValue_range = 0;
float ADCVoltage_range = 0; //0-3,3 V
float unroude_range = 0; //0-50mm
float range = 0; //0-50mm
unsigned long range_t;

// Force Sensor Instanzen-----------------------------
float Current_force = 0; //4-20 mA
float rawADCValue_force = 0; //0-4095
float ADCVoltage_force = 0; //0-3,3 V
float unroude_force = 0; //0-20 Newton
float force = 0; //0-20 Newton
unsigned long force_t;

float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void Read_Scale() {
  
  StaticJsonDocument<1000> doc;
  static int scale_counter = 0;

  if (millis() - scale_previousMillis >= scale_interval) {  // read analog value every 500ms

    scale_previousMillis = millis(); // reset the timer

    rawADCValue_range = 0;
    rawADCValue_force = 0;
    for (int i = 0; i < 100; ) {
      if (millis() - range_t  >= 1) {
        rawADCValue_range += analogRead(ADC_Range_Pin);
        rawADCValue_force += analogRead(ADC_Force_Pin);
        i++;
        range_t = millis();
      }
    }

    ADCVoltage_range = (float)(rawADCValue_range / 100.0) * (3.3 / 4095.0);
    Current_range = mapfloat(ADCVoltage_range, 0.8, 3.3, 4, 20);
    unroude_range = mapfloat(Current_range, 4, 20, 0, 50);
    range = (int)(unroude_range * 100 + 0.5) / 100.0;

    ADCVoltage_force = (float)(rawADCValue_force / 100.0) * (3.3 / 4095.0);
    Current_force = mapfloat(ADCVoltage_force, 0.8, 3.3, 4, 20);
    unroude_force = mapfloat(Current_force, 4, 20, 0, 20);
    force = (int)(unroude_force * 100 + 0.5) / 100.0;
    
    buffer_range[scale_counter] = range ; // store the range value in the buffer
    buffer_force[scale_counter] = force ; // store the force value in the buffer

    scale_counter ++;
    
    JsonArray rangeValues = doc.createNestedArray("Range");
    JsonArray forceValues = doc.createNestedArray("Force");
    
    if (scale_counter >= NUM_SAMPLES)
    {
      for (int i = 0; i < NUM_SAMPLES; i++)
      {
        rangeValues.add(buffer_range[i]);
        forceValues.add(buffer_force[i]);
      }
      serializeJson(doc, Serial);
      Serial.println();
      scale_counter = 0;
      Serial.println();
      
    }
  }
}

void setup() {
  Serial.begin(115200);
}

void loop() {
  Read_Scale();
}

@cotestatnt... Ones more again. Thanks for your help.
I defined the rounding function round_range(). I round the numbers that I am putting into the array within the JSON document and it is working.


double round_range(double value) {
  return (int)(value * 100 + 0.5) / 100.0;
}


rangeValues.add(round_range(buffer_range[i]))

why don't you use:

char char_array[10];
snprintf(char_array,sizeof(char_array),"%0.2f",my_float);
payload["value"] = char_array;

this way you can do proper formatting for every payload - not limited to one function
btw the above function: does it work properly for negative values (rounding)?

I have to try out your approach.
Yes it is working properly at me for negative values

1 Like

The printf functions are relatively resource expensive on AVR based boards. On those boards, the printf functions don't support floating point.

I'm not sure which board te OP is using.

I am using a ESP32 NodeMCU board. ESP32 NodeMCU Module WLAN WiFi Dev Kit C Development Board mit CP2102 – AZ-Delivery

@sterretje @zyghom , i have a issued in my code that i do not know how to manage.

The project consist of ,

  1. Pneumatic cylinder.
  2. 4- 20 mA Position transmitter attached to the cylinder to control the rage. https://www.festo.com/cms/en-ae_ae/52260.htm
  3. 250Kg Loadcell to measure the force connected to a Load Cell Amplifier - HX711. METTLER TOLEDO load cell for suspended tank and hopper weighing
  4. ESP32 NodeMCU as controller.

What i want to do is to compress with the cylinder a steel spring. With the position transmitter i catch the values of the " Range".
The spring is mounted between the cylinder and the Loadcell.
With the Loadcell i catch the values of the "Force", when the cylinder compress the spring.

All this is working and i get the values.
The problem that i have is how i can shorten the values between sample readings.

Follows the code .

For reading the Position transmitter

void Read_Range_Sensor()
{
  rawADCValue_range = 0;

  for (int i = 0; i < 6;)
  {
    if (millis() - range_t >= 0.1)
    {
      rawADCValue_range += analogRead(ADC_Range_Pin);
      i++;
      range_t = millis();
    }
  }

  ADCVoltage_range = (float)(rawADCValue_range / 6.0) * (3.3 / 4095.0);
  Current_range = mapfloat(ADCVoltage_range, 0.8, 3.3, 4, 20);
  range_reading = mapfloat(Current_range, 4, 20, 0, 50);

  round_range = int(range_reading + 0.5);
  cil_back = ((round_range >= daten.VarData[3]) || (round_range >= daten.VarData[3] - 2)) && (round_range <= daten.VarData[3] + 2);
  cil_front = (round_range >= daten.VarData[4] - 2) && (round_range <= daten.VarData[4] + 2);
}

For reading the Loadcell

void Read_Loadcell_Sensor()
{
  scale.set_scale(calibration_factor);
  force_reading = scale.get_units(), 1;
}

void WebSocket_Send_Array()
{
    // Buffer Sensor Reading Instanzen--------------------
    unsigned long scale_previousMillis = 0;
    const long scale_interval = 0.1; // Interval
    const byte NUM_SAMPLES = 16; // Number of Reading
    float buffer_range[NUM_SAMPLES];
    float buffer_force[NUM_SAMPLES];

    String jsonString = "";
    StaticJsonDocument<2048> doc;
    static int scale_counter = 0;

    if (millis() - scale_previousMillis >= scale_interval)
    { // read analog value every 500ms

      scale_previousMillis = millis(); // reset the timer

      buffer_range[scale_counter] = range_reading; // store the range value in the buffer
      buffer_force[scale_counter] = force_reading * NEWTON_CONSTANT, 1; // store the range value in the buffer

      scale_counter++;

      doc["count_array"] = daten.zyklus;
      JsonArray rangeValues = doc.createNestedArray("range_array");
      JsonArray forceValues = doc.createNestedArray("force_array");


      if (scale_counter >= NUM_SAMPLES)
      {
        for (int i = 0; i < NUM_SAMPLES; i++)
        {
          rangeValues.add(round_forcerange(buffer_range[i]));
          forceValues.add(round_forcerange(buffer_force[i]));
        }
        //serializeJson(doc, Serial);
        scale_counter = 0;
        serializeJson(doc, jsonString);
        webSocket.broadcastTXT(jsonString);
        wifi_send_ready = 1;
      }
    }
  }

with this i get values like as follow.

{"count":3375,
"range_array":[41.8,33.1,28.2,24.6,21.5,18.8,16.1,13.7,11.2,8.9,6.7,4.6,2.6,0.8,0.5,0.1],
"force_array":[-8.5,-13.8,-52.1,-107.5,-149.9,-184.8,-215.7,-244.3,-271.3,-296.6,-320.4,-341.5,-357.6,-367.6,-400.6,-509.7]}

But i would like to shorten the catched values.
For example the values in the range_array.
Index 0 and 1 are far away from each other.

Index 0: 41.8
Index 1: 33.1
Index 2: 28.2
.
.
.

I would need that the catche the values and be more close to each other: For example.

Index 0: 41.8
Index 1: 40.8
Index 2: 39.8
.
.
.

It does not matter if i change the _interval or the number of readings. I get always values far from each other.

    const long scale_interval = 0.1; // Interval
    const byte NUM_SAMPLES = 16; // Number of Reading

I guess that that means that you want to do more readings.

I have no experience with any of your components; nor with websockets. The only advice would be to add timing statements in a number of parts of the code to see where the bottle neck is.

  uint32_t startTime = micros();
  for (int i = 0; i < 6;)
  {
    if (millis() - range_t >= 0.1)
    {
      rawADCValue_range += analogRead(ADC_Range_Pin);
      i++;
      range_t = millis();
    }
  }
  uint32_t duration = millis() - startTime;
  Serial.print("Measurement duration = ");
  Serial.println(duration);

and

  uint32_t startTime = micros();
  webSocket.broadcastTXT(jsonString);
  uint32_t duration = millis() - startTime;
  Serial.print("websocket duration = ");
  Serial.println(duration);

Use serial monitor to check durations.

I don't know what rang_t is. But it feels strange that you use a floating point (0.1) as millis() will never be more accurate than an integer.

That's all that I can advise.

Note:
There might be other parts in your code that slow it down. As you did not post all your code, we will never know.

@sterretje thanks for your reply.
I will follow your advice .

if (millis() - range_t >= 0.1)

I don't know what rang_t is. But it feels strange that you use a floating point (0.1) as millis() will never be more accurate than an integer.

Yes you are right.... It was me trying to make it a little bit faster.....:slight_smile:
The same is here also

 const long scale_interval = 0.1; // Interval

i will put it as a Integer.

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