0

First a little background on me and my project. Most of my programming experience is with PHP and javascript. I have just recently started looking at C and C++ for this Arduino project. My project is a weather station that feeds its data to a web based database in real time. I will be writing the code for a web site that will access this data base and present the data in realtime. I am currently working on the temperature section of my weather station. It is an RTD coupled to a constant current circuit and feeds a varying voltage to analog pin 5 on my Arduino Uno. My problem is with the code. I have a working code but I decided to make it object oriented so that I could reuse it in other projects. Here is the code that works.

const unsigned int RTD_PIN = A5;
const float MVOLTS_CTS = 4.882813;
const float OHMS_DEGREE = 0.3851;
const unsigned int SAMPLES = 10;
const float MILLI_AMPS = 19.125;
unsigned long currentMillis;
unsigned long oldMillis = 0;
unsigned long timeBetweenSamples = 1000; // milliseconds between samples
int measurement[SAMPLES];
int read_index = 0;
int total = 0;
int average_cts = 0;
boolean initialized = false;
float degC = 0;
float degF = 0;


void setup() {
  Serial.begin(9600);
  for (int i = 0; i < SAMPLES; i++) {
    measurement[i] = 0;
  }
}

void loop() {

  // Reset the counter when we go over the
  // set number of samples
  if (read_index >= SAMPLES) {
    read_index = 0;
    initialized = true;
  }

  // only take a measurement if one second has passed
  currentMillis = millis();

  if (currentMillis - oldMillis >= timeBetweenSamples) {
    // subtract one element from the total
    total = total - measurement[read_index];

    // take a measurement
    measurement[read_index] = analogRead(RTD_PIN);

    // add the new measurement back into the total
    total = total + measurement[read_index];

    // take an average of the readings
    average_cts = total / SAMPLES;

    // calculate degrees
    degC = (average_cts * MVOLTS_CTS / MILLI_AMPS -100) / OHMS_DEGREE;
    degF = degC * 1.8 + 32;

    // print results only if all elements of measurement[] are filled
    if (initialized) {
      Serial.print(degC);
      Serial.print(" degrees C ");
      Serial.print(degF);
      Serial.print(" degrees F ");
      Serial.println(average_cts);
    }

    read_index++;
    oldMillis = currentMillis;


  } 
}

And this is the code that doesn't work.

#include "Arduino.h"
#include "Sensor.h"

    Sensor::Sensor (int analogPin, int numSamplesInAverage, unsigned long sampleRate) {

     sensorPin = analogPin;
     samples = numSamplesInAverage;
    _sampleRate = sampleRate;
     int measurement[samples];

     // initialize the measurement array
     for (int i = 0; i >= samples; i++) {
       measurement[i] = 0;
     }

  }

  int Sensor::update() {

    if (read_index >= samples) {
       read_index = 0;
     }

     // only take a measurement if sample rate has been exceeded
     // and return a rolling average
     currentMillis = millis();

     if (currentMillis - oldMillis >= _sampleRate) {
       //subtract one element from the total
       total = total - measurement[read_index];

       // take a measurement
     measurement[read_index] = analogRead(sensorPin);

       // add the new measurement into the total
       total = total + measurement[read_index];

       // take an average of the readings
       average_cts = total / samples;

       // increment the read_index
       read_index++;
       oldMillis = currentMillis;

      return average_cts;
     }

     // return false if the sample rate has not expired 
     return false;     

  }

It seems like the issue is with this one line of code on line 34.

measurement[read_index] = analogRead(sensorPin);

When this line has not been remarked out the read_index only goes to 4 before it resets to 0 and it does it at the rate of program execution instead of every second like expected.

If this line is remarked out the the read_index is incremented from 0 to 9 once every second as expected.

Here is where the read_index is defined.

#ifndef Sensor_h
#define Sensor_h

#include "Arduino.h"

class Sensor
{
   public:
     Sensor(int analogPin, int numSamplesInAverage, unsigned long sampleRate);
     int update();

   private:
     unsigned int sensorPin;
     unsigned long currentMillis;
     unsigned long oldMillis;
     unsigned long sampleRate; // milliseconds between samples
     int read_index = 0;
     int total = 0;
     int average_cts = 0;
     int samples;
     unsigned long _sampleRate;
     int measurement[];
};

#endif

and this is the Arduino sketch that calls update()

#include <Sensor.h>


Sensor rtd(4, 10, 1000);  // instantiate a Sensor object
int cts;

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

void loop() {

  rtd.update();

  Serial.println(rtd.read_index);
}

can anyone tell me why this works in my procedural code but not the objected oriented code?

  • Where is read_index defined? Please post all your code. – Nick Gammon Sep 28 '15 at 6:40
  • Where is "update" called? Please post all your code. You have posted a complete sketch that works, and snippets that don't work. – Nick Gammon Sep 28 '15 at 7:36
  • Sorry @Nick Gammon it is late. – beewrangler Sep 28 '15 at 7:43
  • How big is measurement in the class version...? – Majenko Sep 28 '15 at 9:46
1

You have made a few fundamental errors with your class.

Firstly you have not provided a storage size (number of elements) for the measurement array class member variable:

class Sensor
{
   public:
     Sensor(int analogPin, int numSamplesInAverage, unsigned long sampleRate);
     int update();

   private:
     // ... cut ...
     int measurement[];           // <<-- How big is this?!
};

Then it seems like you try to dynamically allocate the space for it in your constructor:

Sensor::Sensor (int analogPin, int numSamplesInAverage, unsigned long sampleRate) {

    sensorPin = analogPin;
    samples = numSamplesInAverage;
    _sampleRate = sampleRate;
    int measurement[samples];    // <<-- Here
    // ... cut ...
}

BUT that's not what you are doing. What you are doing there is creating a new local variable (local to just that one single function) called measurement with an array size of samples. As soon as you leave the constructor that variable is thrown away.

You haven't touched the class member variable measurement at all. So, when you do:

measurement[read_index] = analogRead(sensorPin);

you are storing the read value into an array of no size that points nowhere. And of course that then causes a crash because you're writing to memory that you shouldn't be writing to.

If you want to be specifying the number of array elements in the constructor you need to be learning about dynamic memory allocation using malloc() and free() and all the pitfalls that go along with using those on a small microcontroller with very tight RAM constraints.

For an example of how it can be done you should take a look at my Average Library which uses dynamic allocation to allocate the array memory in the constructor and free it in the destructor.

  • Thank you for taking the time to answer this for me. I will have a look into it when time allows. – beewrangler Sep 28 '15 at 21:43
  • Well spotted. I elaborated on how to do that in my reply, because what I wanted to say was too long for a comment. – Nick Gammon Sep 28 '15 at 21:59
0

I was going to add to Majenko's excellent answer, but what I want to say is too long. :)


Change:

int measurement[];

to:

int * measurement;

and then in the constructor do:

measurement = new int [samples];

And check that measurement is not NULL, in case there is not enough memory.


Also, this is wrong in two ways:

// initialize the measurement array
for (int i = 0; i >= samples; i++) {
  measurement[i] = 0;

It should be:

// initialize the measurement array
for (int i = 0; i < samples; i++) {
  measurement[i] = 0;

You need to test less than, and you don't test equals (that would initialize one too many).


You should also have a destructor that frees the memory, ie.

delete [] measurement;

When would this destructor come into play? How and when should I call it?

The destructor is automatically called when an instance of the class is destroyed. This is when you do things like free memory you allocated. You don't call it directly.

An example:

class Sensor
{
public:
  // constructor
  Sensor(int analogPin, int numSamplesInAverage, unsigned long mysampleRate);
  // destructor
  ~Sensor (); 
  // call to update samples
  int update();

private:
  unsigned int sensorPin;
  unsigned long currentMillis;
  unsigned long oldMillis;
  unsigned long sampleRate; // milliseconds between samples
  int read_index;
  int total;
  int average_cts;
  int samples;
  int * measurement;
};

Sensor::Sensor (int analogPin, int numSamplesInAverage, unsigned long mysampleRate) 
  {
  sensorPin = analogPin;
  samples = numSamplesInAverage;
  sampleRate = mysampleRate;
  read_index = 0;
  total = 0;
  average_cts = 0;
  measurement = new int [samples];

  if (measurement == NULL)
    exit (1);  // can't continue

  // initialize the measurement array
  for (int i = 0; i < samples; i++) {
    measurement[i] = 0;
  }

}  // end of constructor

// destructor
Sensor::~Sensor ()
  {
  delete [] measurement; 
  }  // end of destructor

Can you please explain how these two line of code work int * measurement and measurement = new int [samples];

int * measurement creates a pointer to an int (called measurement). However pointers can also point to the start of an array (or indeed, to the middle of an array) of ints. So this variable will hold the start of your array of ints.

measurement = new int [samples]; allocates the memory for this array - in this case "samples" lots of ints.

Once the memory is allocated you can treat the pointer as an array, eg.

measurement [5] = 42;

For more detail about this see So what is meant by the "equivalence of pointers and arrays" in C? and also Google: pointer vs array.

Also Google: new and delete with reference to the C++ language.

  • When would this destructor come into play? How and when should I call it? – beewrangler Sep 28 '15 at 22:03
  • See amended answer. – Nick Gammon Sep 28 '15 at 23:22
  • If the Arduino is constantly calling the update() function it doesnt seem to me that the instance would ever be destroyed. So is a destructor even necessary? – beewrangler Sep 28 '15 at 23:26
  • In your case, with the object at global scope, no it will never be destroyed, so the destructor is not necessary. If you were to put the object instantiation into a function, then it would be necessary. It is a good idea to have a destructor if you allocate memory, otherwise one day you (or someone else) will use your class in a place where it will be needed. – Nick Gammon Sep 28 '15 at 23:30
  • Thank you. I will give this a try later and let you know how it turns out. – beewrangler Sep 28 '15 at 23:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.