First time using an array and need a little help

I have a compass and I’ve got it calibrated.

The output is a little jumpy so I need a way to average a series of reading to smooth it out. In reading the reference material it looks like and “Array” fits the bill.

I’ve added the sample array code to my compass code - but things aren’t going well for me. I’m hoping someone will take a look and get me straightened out.

#include <Wire.h>
#include <LSM303.h>

LSM303 compass;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  compass.init();
  compass.enableDefault();
  //===========================Array variables=================================
  const int numReadings = 36; // number of readings for the smoothing.
  int readings[numReadings];      // the readings from the analog input
  int readIndex = 0;              // the index of the current reading
  int total = 0;                  // the running total
  int average = 0;                // the average
  //=============================================================================
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
  /*
    Calibration values; the default values of +/-32767 for each axis
    lead to an assumed magnetometer bias of 0. Use the Calibrate example
    program to determine appropriate values for your particular unit.
  */
  compass.m_min = (LSM303::vector<int16_t>) {
    -541, -840, -564
  };
  compass.m_max = (LSM303::vector<int16_t>) {
    +657, +423, +477
  };
}

void loop() {
  compass.read();
  /*
    When given no arguments, the heading() function returns the angular
    difference in the horizontal plane between a default vector and
    north, in degrees.

    The default vector is chosen by the library to point along the
    surface of the PCB, in the direction of the top of the text on the
    silkscreen. This is the +X axis on the Pololu LSM303D carrier and
    the -Y axis on the Pololu LSM303DLHC, LSM303DLM, and LSM303DLH
    carriers.

    To use a different vector as a reference, use the version of heading()
    that takes a vector argument; for example, use

    compass.heading((LSM303::vector<int>){0, 0, 1});

    to use the +Z axis as a reference.
  */

  float heading = compass.heading();
    //=========================Smooth readings======================
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = heading;
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  average = total / numReadings;
  // send it to the computer as ASCII digits
  Serial.println(average);
  //=================================end of Array==========================

  Serial.println(heading);
  delay(1000);
}

Thanks

Sounds more like what you want is a circular (FIFO) buffer that will hold your n most recent readings. As a new reading gets inserted, Xn gets dropped and X(n - 1) becomes Xn, a new average is then automatically calculated, sum(X1..Xn)/n. Google moving averages.

RayJorgensen:
things aren't going well for me. I'm hoping someone will take a look and get me straightened out.

In what way are things not going well for you? It looks like the code should work fairly well after the 36 zeroes are pushed out of the buffer. Are you having trouble with the heading being near a roll-over from 359 to 0 or from +180 to -180? That is going to send your average way off.

Here is what I get from the serial monitor with the compass pointed kinda north.

354.28
355.27
354.17
355.06
354.74
354.61
355.95
355.12
356.65
355.88
355.15
356.25
355.16
355.02
356.05
356.98
356.29
355.44
355.73
355.40
355.68
355.61
355.24
355.41
356.04
355.00
356.03
355.82
356.12
355.33
356.40

I’d like the “smooth” these out or average them. The code I included gives me lot’s of errors. They are mostly with;

const int numReadings = 36; // number of readings for the smoothing.
int readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
int total = 0; // the running total
int average = 0; // the average

It’s saying these are not declared in the scope. They are defined as global variables so it’s got me stumped.

I’ve also run across a “moving ave” library which might do the same and better.

How often do you get readings? 36 seems to be a large number is you just want to smooth your output.

I'n thinking about this - it just dawned on that if I could loose the .XX it might solve my problem. (
By just getting rid of the decimal it could "display" a constant heading?

How would I do that? Change float to an int?

Just changed to int and I get a 2 degree reading variance. That won’t work.

The problem is the code won’t verify. Errors.

It’s saying these are not declared in the scope. They are defined as global variables so it’s got me stumped.

No, they are declared in setup and are not global.

void setup() {
  Serial.begin(9600);
  Wire.begin();
  compass.init();
  compass.enableDefault();
  //===========================Array variables=================================
  const int numReadings = 36; // number of readings for the smoothing.
  int readings[numReadings];      // the readings from the analog input
  int readIndex = 0;              // the index of the current reading
  int total = 0;                  // the running total
  int average = 0;                // the average
  //=============================================================================
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
  /*
    Calibration values; the default values of +/-32767 for each axis
    lead to an assumed magnetometer bias of 0. Use the Calibrate example
    program to determine appropriate values for your particular unit.
  */
  compass.m_min = (LSM303::vector<int16_t>) {
    -541, -840, -564
  };
  compass.m_max = (LSM303::vector<int16_t>) {
    +657, +423, +477
  };
}

Cattledog to the rescue!!!!!!!!!!!!!!! Works

I sure didn't expect you to respond but I'm glad you did!!!!!

I need to play with the number of samples, etc. And then get it into a state machine and I'll be good to go.

Please follow this as this will be the first state machine I attempt on my own and most likely have a glitch or 2.

THANKS

The code works great sitting flat on the counter. If I hold it along the side and tilt it the readings change. I need to compensate for the pitch and roll. I have looked high and low for some code for this and all I can come up with is a formula that looks like Martian to me. (I didn't do well with algebra all those years ago in high school!)

If any of you guys know of some code for tilt comp I'd really appreciate it. If anyone can read Martian that might be an option too!!!

As a last resort I'll just build a gimbal and go from there.

If any of you guys know of some code for tilt comp I’d really appreciate it.

#include <LSM303.h>

Are you using the Pololu library from the library manager?

From what I see in the documentation, the function heading() which returns a float is tilt compensated.

float heading(void)
Returns the tilt-compensated heading of a default vector in degrees (the angular difference in the horizontal plane between the default vector and north). The default vector is chosen to point along the surface of the PCB, in the direction of the top of the text on the silkscreen. This is the +X axis on the Pololu LSM303D carrier and the -Y axis on the Pololu LSM303DLHC, LSM303DLM, and LSM303DLH carriers.

I’m not familiar with using the lsm303 or its calibration, so I can’t help you through this, but I would think that all the tools are available in the library you are using.

Alternatively, there is plenty to be found on the Adafruit site on the calibration and there is also a tilt corrected sketch which does not use any library other than Wire.h.’’

Yep Cattledog you are right. It is tilt comp. It just lags quite a bit but it does work. I'm going to mount this on a gimbal and let gravity take care of part of this and the the tilt comp can come in and do it's thing. Might just end up with a pretty stable setup!

So It's time to describe my project.

On the 250 trees I planted this year I did the "by eye" thing and not to bad but I want better. These trees will be around for hundreds of years. So I want "auto steer". Beside that - this is fun!

Using the compass - when I lock in a heading with a button- a motor of some sort ( I have a couple steppers and will use for testing) the motor will keep my little tractor on the locked in heading. Thus driving a straight line.

On a second button bush the Heading would be locked 180 for the return pass. So one state here watching the button state. I will also need another button to cut power to the motor so it will spin free which gets me back to me driving it. This would be a "auto steer" on - off switch - Sate 2. This would be phase 1.

Phase 2.

Assuming I can get phase 1 working - the next part is a button that when pushed would turn the steering motor a determined angel which will allow a circle to be driven giving me a nice circle of spray to be put down around each tree. On the second push of the button I go back to following the current heading.

I know this will work because I can do it by hand - It just takes one time around to find the angel - from there I can drive around and around.

At the same time a servo flow valve would open and a flow meter will start. The starting settings will be set based off manual measurements. But the flow meter is based on time and with that can get the servo flow valve to adjust to give the desired application amount. Each circle done will be recorded in an array and after a few trees we have a pretty good handle on that problem. State 3.

State 1 = heading info

State 2 = engage - disengage auto steer

State 3 = make circles and spray

Do I have this right?