Arduino UNO voltmeter that keeps readings in ram (no datalogger or other shields available at the moment)

Hello everyone, it's my first time doing a "big" project with arduino, my project v1 is about doing a voltmter with arduino UNO



that read and save reading on it's ram on a global variable of char, i'm at a point wher i have donee the code for reading the data in another sketch that is ready and work, now i've done a pc "test machine" where i've made a similar version that with modifications it will be th final one;
I'm at a point where in the loop() i ask to give a number between 1 and 4 with a switch and there is the problem that the switch works only with the 1 case, when i give the others it will not follow the case and i've tried to put only Serial.println in the case 2,3,4 and it print the message in them but nothing to do with the previous.
I've used MemoryUsage library to get the global space reading to know how much space i'm occuping

Here is the code:

   #include <MemoryUsage.h>

char *sReadings[820];  //global char array for storing readings value
word cRPointer = 0;    //global pointer for number of readings inserted

void setup() {
  Serial.begin(9600);
  while (!Serial) {}
}

void loop() {
  Serial.println(F("Starting state of the memory:"));
  Serial.println();
  FREERAM_PRINT;

  Serial.println("Please type a number to choose and press 'Enter'");
  Serial.println("Select 1 for inserting new data");
  Serial.println("Select 2 for retrieving a particular data");
  Serial.println("Select 3 for retrieving all data");
  Serial.println("Select 4 for flushing all data");

  while (!Serial.available()) {
    Serial.print('.');
    delay(500);
  }
  int value = Serial.parseInt();

  if (value == 0) {
    Serial.println("Choice NOT Valid");
    return;
  }
  Serial.println("Ok");
  Serial.println(value);
  switch (value) {
    case 1:
      //data entry
      Serial.println("Requested inserting new data");
      long time = getLong();
      float volt = getFloat();
      if ((time == 0) || (volt == 0.00)) {
        Serial.println("Data Not Valid");
        break;
      }
      //data elaboration
      char *cReading = elaborateData(time, volt);
      //data storing
      if (!(saveData(cReading))) {
        Serial.println("Value not entered correctly");
        break;
      }
      break;

    case 2:
      Serial.println("Requested Reading a particular data");
      Serial.println("Please insert a index to retrieve the data");
      while (!Serial.available()) {
        Serial.print('.');
        delay(500);
      }
      int i = Serial.parseInt();
      Serial.println("Ok");
      Serial.println(i);
      getOneData(i);
      break;

    case 3:
      Serial.println("Requested Reading all data");
      getAllData();
      break;

    case 4:
      Serial.println("Requested flushing all data");
      flushData();
      break;

    default:
      Serial.println("Unrecognized choice");
      break;
  }
  Serial.println();
  Serial.println(F("Ending state of the memory:"));
  Serial.println();
  FREERAM_PRINT;
}
long getLong() {
  //long for the millis
  Serial.print("Insert a long ");
  while (!Serial.available()) {
    Serial.print('.');
    delay(500);
  }
  long time = Serial.parseInt();
  Serial.print(time);
  Serial.println();
  return time;
}
float getFloat() {
  //float for the voltage in volt
  Serial.print("Insert a float ");
  while (!Serial.available()) {
    Serial.print('.');
    delay(500);
  }
  float volt = Serial.parseFloat();
  Serial.print(volt);
  Serial.println();
  return volt;
}
char *elaborateData(long time, float volt) {
  char *reading; //local char array
  char ltime[8];
  ltoa(time, ltime, 10);
  Serial.print("long value ");
  Serial.println(ltime);

  char lvolt[9];
  dtostrf(volt, 0, 3, lvolt);
  Serial.print("float value ");
  Serial.println(lvolt);

  Serial.print("first cReading value: ");
  reading = ltime;  //First part of the reading
  Serial.println(reading);

  strcat(reading, "-");  //Reading part separator

  strcat(reading, lvolt);  //Second part of the reading
  Serial.print("final cReading value: ");
  Serial.println(reading);

  // example of reading value 86400000-50.000
  return reading;  //return final value
}
bool saveData(char *reading) {

  if (cRPointer < 820) {
    sReadings[cRPointer] = reading;
    cRPointer++;
    return true;
  }
  return false;
}
void getOneData(word i) {
  //searching the particular data with all its informations
  for (int j = 0; j < 15; j++) {
    Serial.print(sReadings[i][j]);
  }
}
void getAllData() {
  //reading all data from the ram
  Serial.println("Full reading started");
  int i;
  Serial.println("Starting Reading data");
  for (i = 0; i < cRPointer; i++) {
    for (int j = 0; j < 15; j++) {
      Serial.print(sReadings[i][j]);
    }
    Serial.println();
  }
  Serial.println("Ending Reading data");
  Serial.print("Total entries: ");
  Serial.println(i);
}
void flushData() {
  //deleting all data on ram
  for (int i = 0; i < cRPointer; i++) {
    sReadings[i] = ((char *)"NULL");
  }
}

and here is the serial monitor result

23:56:44.609 -> Starting state of the memory:

23:56:44.609 ->

23:56:44.640 -> Free Ram Size: 1435

23:56:44.640 -> Please type a number to choose and press 'Enter'

23:56:44.709 -> Select 1 for inserting new data

23:56:44.742 -> Select 2 for retrieving a particular data

23:56:44.776 -> Select 3 for retrieving all data

23:56:44.813 -> Select 4 for flushing all data

23:56:44.813 -> ..............Ok

23:56:52.798 -> 1

23:56:52.798 -> Requested inserting new data

23:56:52.798 -> Insert a long ......100

23:56:56.787 -> Insert a float ..12.00

23:56:58.791 -> long value 100

23:56:58.791 -> float value 12.000

23:56:58.830 -> first cReading value: 100

23:56:58.863 -> final cReading value: 100-12.000

23:56:58.896 ->

23:56:58.896 -> Ending state of the memory:

23:56:58.929 ->

23:56:58.929 -> Free Ram Size: 1435
23:56:58.929 -> Starting state of the memory:

23:56:58.962 ->

23:56:58.962 -> Free Ram Size: 1435

23:56:58.996 -> Please type a number to choose and press 'Enter'

23:56:59.062 -> Select 1 for inserting new data

23:56:59.096 -> Select 2 for retrieving a particular data

23:56:59.129 -> Select 3 for retrieving all data

23:56:59.163 -> Select 4 for flushing all data

23:56:59.163 -> ......Ok

23:57:03.143 -> 2

23:57:03.143 ->

23:57:03.143 -> Ending state of the memory:

23:57:03.182 ->

23:57:03.182 -> Free Ram Size: 1435

23:57:03.182 -> Starting state of the memory:

23:57:03.215 ->

23:57:03.215 -> Free Ram Size: 1435

23:57:03.249 -> Please type a number to choose and press 'Enter'

23:57:03.315 -> Select 1 for inserting new data

23:57:03.348 -> Select 2 for retrieving a particular data

23:57:03.381 -> Select 3 for retrieving all data

23:57:03.420 -> Select 4 for flushing all data

23:57:03.420 -> ...Ok

23:57:05.899 -> 3

23:57:05.899 ->

23:57:05.899 -> Ending state of the memory:

23:57:05.932 ->

23:57:05.932 -> Free Ram Size: 1435

23:57:05.932 -> Starting state of the memory:

23:57:05.967 ->

23:57:05.967 -> Free Ram Size: 1435

23:57:05.999 -> Please type a number to choose and press 'Enter'

23:57:06.066 -> Select 1 for inserting new data

23:57:06.099 -> Select 2 for retrieving a particular data

23:57:06.132 -> Select 3 for retrieving all data

23:57:06.170 -> Select 4 for flushing all data

23:57:06.170 -> ..Ok

23:57:08.152 -> 4

23:57:08.152 ->

23:57:08.152 -> Ending state of the memory:

23:57:08.185 ->

23:57:08.185 -> Free Ram Size: 1435

23:57:08.185 -> Starting state of the memory:

23:57:08.219 ->

23:57:08.219 -> Free Ram Size: 1435

23:57:08.252 -> Please type a number to choose and press 'Enter'

23:57:08.318 -> Select 1 for inserting new data

23:57:08.351 -> Select 2 for retrieving a particular data

23:57:08.385 -> Select 3 for retrieving all data

23:57:08.428 -> Select 4 for flushing all data

23:57:08.428 -> ..Ok

23:57:10.386 -> 5

23:57:10.386 ->

23:57:10.386 -> Ending state of the memory:

23:57:10.433 ->

23:57:10.433 -> Free Ram Size: 1435

23:57:10.433 -> Starting state of the memory:

23:57:10.470 ->

23:57:10.470 -> Free Ram Size: 1435

23:57:10.503 -> Please type a number to choose and press 'Enter'

23:57:10.572 -> Select 1 for inserting new data

23:57:10.603 -> Select 2 for retrieving a particular data

23:57:10.636 -> Select 3 for retrieving all data

23:57:10.679 -> Select 4 for flushing all data

It would be correct, much easier and safer to store integers, rather than using pointers to character variables.

For example:

int readings[100]={0};
for (int i=0; i<100; i++) {
readings[i]=analogRead(A0);
}

for (int i=0; i<100; i++) {
Serial.println(readings[i]);
}

Thanks for the idea but i need to store the time in milliseconds when the reading is taken and the reading itself, so i need a long and a float, in a int there is no space for all that, but that is not my problem, my need is to know why i can't enter in the other cases out of the 1 in the switch onto the loop().

The analog voltage readings are integers. There is no need to convert to float, and it would be a waste of RAM memory to do so (4 bytes for a float, 2 bytes for an int).

Here is how to store the time

int readings[100]={0};
unsigned long times[100]={0};
for (int i=0; i<100; i++) {
times[i]=millis();
readings[i]=analogRead(A0);
}

for (int i=0; i<100; i++) {
Serial.print(times[i]);
Serial.print(", ");
Serial.println(readings[i]);
}
    case 1:
      //data entry
      Serial.println("Requested inserting new data");
      long time = getLong();
      float volt = getFloat();
      if ((time == 0) || (volt == 0.00)) {
        Serial.println("Data Not Valid");
        break;
      }
      //data elaboration
      char *cReading = elaborateData(time, volt);
      //data storing
      if (!(saveData(cReading))) {
        Serial.println("Value not entered correctly");
        break;
      }
      break;

You need to enclose the entire case within brackets braces, otherwise you are not allowed to declare a variable with an initializer inside a case. The scope of the variable is from the point of declaration till the end of the switch statement, and the compiler sees each subsequent case as having an uninitialized variable and will never allow you to reach those cases.

You will have more ram to work with if you use the F() macro for all the printed text literals.

1 Like

A few things about the code.

A function should not return a pointer to a local char array, that array no longer exists after the function ends, and can be overwritten at any time.

Storing the data as characters is extremely wasteful of memory, an array of struct would be my preference.

char *elaborateData(long time, float volt) {
  char *reading; //local char array
  char ltime[8];
  ltoa(time, ltime, 10);
  Serial.print("long value ");
  Serial.println(ltime);

  char lvolt[9];
  dtostrf(volt, 0, 3, lvolt);
  Serial.print("float value ");
  Serial.println(lvolt);

  Serial.print(F("first cReading value: "));
  reading = ltime;  //First part of the reading
  Serial.println(reading);

  strcat(reading, "-");  //Reading part separator

  strcat(reading, lvolt);  //Second part of the reading
  Serial.print(F("final cReading value: "));
  Serial.println(reading);

  // example of reading value 86400000-50.000
  return reading;  //return final value
}

You are going to have serious problems with storing a potential reading of "86400000-50.000" in a char array that can hold only seven characters plus the terminating null. The pointer will points to memory that you have not allocated.

1 Like

It takes six bytes to store a time in milliseconds and an analog voltage reading as binary data, in contrast to the 16 bytes required by this character array: "86400000-50.000".

Having looked over the code while trying to actually get it to compile, the whole concept you are using is wrong. Just the array of 820 character pointers is going to take 1640 bytes, where do you expect to store the actual data being pointed to? All of your pointers are meaningless anyway, because they point to local data from a function. Even it you made the array in the function static, the pointers would all point to the exact same location, which would only contain the last data stored there.

Ok, i need to change the core storage for the data, the type and how it's stored, if you can tell me more about this i will apreciate it, so an array of struct will be better? ;
and even if i need to change that the changes i've done with the F in print is getting me something different now

#include <MemoryUsage.h>

char *sReadings[820];  //global char array for storing readings value
word cRPointer = 0;    //global pointer for number of readings inserted

void setup() {
  Serial.begin(9600);
  while (!Serial) {}
}

void loop() {
  Serial.println(F("Starting state of the memory:"));
  Serial.println();
  FREERAM_PRINT;

  Serial.println(F("Please type a number to choose and press 'Enter'"));
  Serial.println(F("Select 1 for inserting new data"));
  Serial.println(F("Select 2 for retrieving a particular data"));
  Serial.println(F("Select 3 for retrieving all data"));
  Serial.println(F("Select 4 for flushing all data"));

  while (!Serial.available()) {
    Serial.print('.');
    delay(500);
  }
  int value = Serial.parseInt();

  if (value == 0) {
    Serial.println(F("Choice NOT Valid"));
    return;
  }
  Serial.println(F("Ok"));
  Serial.println(value);
  switch (value) {
    case 1:
      //data entry
      {
        Serial.println(F("Requested inserting new data"));
        long time = getLong();
        float volt = getFloat();
        if ((time == 0) || (volt == 0.00)) {
          Serial.println(F("Data Not Valid"));
          break;
        }
        //data elaboration
        char *cReading = elaborateData(time, volt);
        //data storing
        if (!(saveData(cReading))) {
          Serial.println(F("Value not entered correctly"));
          break;
        }
      }
      break;

    case 2:
      {
        Serial.println(F("Requested Reading a particular data"));
        Serial.println(F("Please insert a index to retrieve the data"));
        while (!Serial.available()) {
          Serial.print('.');
          delay(500);
        }
        int i = Serial.parseInt();
        Serial.println(F("Ok"));
        Serial.println(i);
        getOneData(i);
      }
      break;

    case 3:
      {Serial.println(F("Requested Reading all data"));
      getAllData();}
      break;

    case 4:
      {Serial.println(F("Requested flushing all data"));
      flushData();}
      break;

    default:
      {Serial.println(F("Unrecognized choice"));}
      break;
  }
  Serial.println();
  Serial.println(F("Ending state of the memory:"));
  Serial.println();
  FREERAM_PRINT;
}
long getLong() {
  //long for the millis
  Serial.print(F("Insert a long "));
  while (!Serial.available()) {}
  long time = Serial.parseInt();
  Serial.print(time);
  Serial.println();
  return time;
}
float getFloat() {
  //float for the voltage in volt
  Serial.print(F("Insert a float "));
  while (!Serial.available()) {}
  float volt = Serial.parseFloat();
  Serial.print(volt);
  Serial.println();
  return volt;
}
char *elaborateData(long time, float volt) {
  char *reading;  //local char array
  char ltime[8];
  ltoa(time, ltime, 10);
  Serial.print(F("long value "));
  Serial.println(ltime);

  char lvolt[9];
  dtostrf(volt, 0, 3, lvolt);
  Serial.print(F("float value "));
  Serial.println(lvolt);

  Serial.print(F("first cReading value: "));
  reading = ltime;  //First part of the reading
  Serial.println(reading);

  strcat(reading, "-");  //Reading part separator

  strcat(reading, lvolt);  //Second part of the reading
  Serial.print(F("final cReading value: "));
  Serial.println(reading);

  // example of reading value 86400000-50.000
  return reading;  //return final value
}
bool saveData(char *reading) {

  if (cRPointer < 820) {
    sReadings[cRPointer] = reading;
    cRPointer++;
    return true;
  }
  return false;
}
void getOneData(word i) {
  //searching the particular data with all its informations
  for (int j = 0; j < 15; j++) {
    Serial.print(sReadings[i][j]);
  }
}
void getAllData() {
  //reading all data from the ram
  Serial.println(F("Full reading started"));
  int i;
  Serial.println(F("Starting Reading data"));
  for (i = 0; i < cRPointer; i++) {
    for (int j = 0; j < 15; j++) {
      Serial.print(sReadings[i][j]);
    }
    Serial.println();
  }
  Serial.println(F("Ending Reading data"));
  Serial.print(F("Total entries: "));
  Serial.println(i);
}
void flushData() {
  //deleting all data on ram
  for (int i = 0; i < cRPointer; i++) {
    sReadings[i] = ((char *)"NULL");
  }
}

```
01:19:23.715 -> Free Ram Size: 169
01:19:23.748 -> Starting state of the memory:
**01:19:23.780 -> **
01:19:23.780 -> Free Ram Size: 169
01:19:23.814 -> Please type a number to choose and press 'Enter'
01:19:23.849 -> Select 1 for inserting new data
01:19:23.882 -> Select 2 for retrieving a particular data
01:19:23.914 -> Select 3 for retrieving all data
01:19:23.984 -> Select 4 for flushing all data
01:19:24.948 -> Choice NOT Valid
01:19:24.948 -> Starting state of the memory:
**01:19:24.981 -> **
01:19:24.981 -> Free Ram Size: 169
01:19:25.014 -> Please type a number to choose and press 'Enter'
01:19:25.047 -> Select 1 for inserting new data
01:19:25.079 -> Select 2 for retrieving a particular data
01:19:25.147 -> Select 3 for retrieving all data
01:19:25.187 -> Select 4 for flushing all data
01:19:25.187 -> ................Ok
01:19:33.168 -> 4
01:19:33.168 -> Requested flushing all data
**01:19:33.200 -> **
01:19:33.200 -> Ending state of the memory:
**01:19:33.234 -> **
01:19:33.234 -> Free Ram Size: 169
01:19:33.234 -> Starting state of the memory:
**01:19:33.267 -> **
01:19:33.267 -> Free Ram Size: 169
01:19:33.300 -> Please type a number to choose and press 'Enter'
01:19:33.367 -> Select 1 for inserting new data
01:19:33.400 -> Select 2 for retrieving a particular data
01:19:33.431 -> Select 3 for retrieving all data
01:19:33.478 -> Select 4 for flushing all data
01:19:34.423 -> Choice NOT Valid
01:19:34.423 -> Starting state of the memory:
**01:19:34.467 -> **
01:19:34.467 -> Free Ram Size: 169
01:19:34.500 -> Please type a number to choose and press 'Enter'
01:19:34.546 -> Select 1 for inserting new data
01:19:34.579 -> Select 2 for retrieving a particular data
01:19:34.646 -> Select 3 for retrieving all data
01:19:34.686 -> Select 4 for flushing all data
01:19:34.686 -> ..Ok
01:19:35.621 -> 5
01:19:35.621 -> Unrecognized choice
**01:19:35.621 -> **
01:19:35.621 -> Ending state of the memory:
**01:19:35.686 -> **
01:19:35.686 -> Free Ram Size: 169
01:19:35.719 -> Starting state of the memory:
**01:19:35.752 -> **
01:19:35.752 -> Free Ram Size: 169
01:19:35.786 -> Please type a number to choose and press 'Enter'
01:19:35.819 -> Select 1 for inserting new data
01:19:35.852 -> Select 2 for retrieving a particular data
01:19:35.919 -> Select 3 for retrieving all data
01:19:35.963 -> Select 4 for flushing all data
01:19:36.909 -> Choice NOT Valid
01:19:36.909 -> Starting state of the memory:
**01:19:36.954 -> **
01:19:36.954 -> Free Ram Size: 169
01:19:36.989 -> Please type a number to choose and press 'Enter'
01:19:37.022 -> Select 1 for inserting new data
01:19:37.055 -> Select 2 for retrieving a particular data
01:19:37.121 -> Select 3 for retrieving all data
01:19:37.153 -> Select 4 for flushing all data
01:19:37.153 -> ............................................................

```

See post #5 for a simple example of storing binary data. An array of struct would be addressed differently, but would not be "better".

I've fixed the problem for the acting weird of the sketch, now it's functioning normal.
I need to rework the data type and storage.

How many readings do you need to store? Even at six bytes per reading (four for time, two for voltage), storing 250 readings is starting to get risky on an UNO.

i have to monitor voltage to for 12h or 24h, and i wanted to sav only the mayor spikes of voltage i don't need to get 250 readings of the same 12 volts (for example), i need to see if the voltage through a day or half a day goes from 12 to 15 or lower, so I have to store only when there is a difference between the normal voltage (that i will set) and the read voltage, that's why i've don the "flushData" so when i will check on that i can save the data caught on a txt file and i will have new space available (also i need to rework also the function to this in the new format and now it will put only "null" in the array to just mak sure it modify it, it's only an example).

i've modified the way of storage, i'm not sure on the flush data but on the others i think is better than before

#include <MemoryUsage.h>
data sAReadings[220];  //global char array for storing readings value
word cRPointer = 0;    //global pointer for number of readings inserted

struct data  //struct to define the data layout
{
  long time;   //time
  float volt;  //volt
};



void setup() {
  Serial.begin(9600);
  while (!Serial) {}
}

void loop() {
  Serial.println(F("Starting state of the memory:"));
  FREERAM_PRINT;

  Serial.println(F("Please type a number to choose and press 'Enter'"));
  Serial.println(F("Select 1 for inserting new data"));
  Serial.println(F("Select 2 for retrieving a particular data"));
  Serial.println(F("Select 3 for retrieving all data"));
  Serial.println(F("Select 4 for flushing all data"));

  while (!Serial.available()) {
    Serial.print(F("."));
    delay(500);
  }
  int value = Serial.parseInt();

  if (value == 0) {
    Serial.println(F("Choice NOT Valid"));
    return;
  }
  Serial.println(F("Ok"));
  Serial.println(value);
  switch (value) {
    case 1:
      //data entry
      {
        Serial.println(F("Requested inserting new data"));
        long time = getLong();
        float volt = getFloat();
        if ((time == 0) || (volt == 0.00)) {
          Serial.println(F("Data Not Valid"));
          break;
        }
        //data elaboration
        data cReading = elaborateData(time, volt);
        //data storing
        if (!(saveData(cReading))) {
          Serial.println(F("Value not entered correctly"));
          break;
        }
      }
      break;

    case 2:
      {
        Serial.println(F("Requested Reading a particular data"));
        Serial.println(F("Please insert a index to retrieve the data"));
        while (!Serial.available()) {
          Serial.print(F("."));
          delay(500);
        }
        int i = Serial.parseInt();
        Serial.println(F("Ok"));
        Serial.println(i);
        getOneData(i);
      }
      break;

    case 3:
      {
        Serial.println(F("Requested Reading all data"));
        getAllData();
      }
      break;

    case 4:
      {
        Serial.println(F("Requested flushing all data"));
        flushData();
      }
      break;

    default:
      {
        Serial.println(F("Unrecognized choice"));
      }
      break;
  }
  Serial.println(F("Ending state of the memory:"));
  FREERAM_PRINT;
}
long getLong() {
  //long for the millis
  Serial.print(F("Insert a long "));
  while (!Serial.available()) {}
  long time = Serial.parseInt();
  Serial.print(time);
  Serial.println();
  return time;
}
float getFloat() {
  //float for the voltage in volt
  Serial.print(F("Insert a float "));
  while (!Serial.available()) {}
  float volt = Serial.parseFloat();
  Serial.print(volt);
  Serial.println();
  return volt;
}

data elaborateData(long time, float volt) {
  data reading = { time, volt };
  // example of reading value 86400000-50.000
  return reading;  //return final value
}

bool saveData(data reading) {

  if (cRPointer < 820) {
    sAReadings[cRPointer] = reading;
    cRPointer++;
    return true;
  }
  return false;
}

void getOneData(word i) {
  //searching the particular data with all its informations
  Serial.print(sAReadings[i].time);
  Serial.print(F("-"));
  Serial.println(sAReadings[i].volt);
}

void getAllData() {
  //reading all data from the ram
  Serial.println(F("Full reading started"));
  int i;
  Serial.println(F("Starting Reading data"));
  for (i = 0; i < cRPointer; i++) {
    Serial.print(sAReadings[i].time);
    Serial.print(F("-"));
    Serial.print(sAReadings[i].volt);

    Serial.println();
  }
  Serial.println(F("Ending Reading data"));
  Serial.print(F("Total entries: "));
  Serial.println(i);
}

void flushData() {
  //deleting all data on ram
  sAReadings[220] = {};
}

Complete waste of memory in the struct. The measurements are two byte integers, while float takes four bytes.

Yeah, i forgot to make the float a int, now i've done it.

Always use unsigned long for variables associated with millis() and micros().

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