Go Down

Topic: variables do not always update (Read 371 times) previous topic - next topic

hanjione

Dec 09, 2018, 01:48 am Last Edit: Dec 09, 2018, 01:50 am by hanjione
I have been monitoring temperature, etc in my garden for several years using a slave Leonardo connected to sensors and providing data via I2C to an SBC master. The sketch would poll data from sensors within the void loop() and the SBC would poll data from slave every 15 minutes. I need to monitor the rain gauge continuously but prefer to only poll the sensors once every 15 minutes. I adapted my original sketch to use an interrupt (triggered by the SBC) to call a function get_data(). This works for a while and then the variables are no longer updated. Below is a snippet from the data csv file
Code: [Select]
09/12/2018,06:30,23.6,48.9,12.2,n/a,1019.5,0.0
09/12/2018,06:45,22.8,49.2,11.6,n/a,1020.0,0.0
09/12/2018,07:00,20.5,57.1,11.7,n/a,1020.0,0.0
09/12/2018,07:15,19.2,62.1,11.8,n/a,1019.7,0.0
09/12/2018,07:30,18.9,63.4,11.8,n/a,1020.2,0.0
09/12/2018,07:45,19.0,63.3,11.9,n/a,1020.2,0.0
09/12/2018,08:00,19.3,62.3,11.9,n/a,1019.7,0.0
09/12/2018,08:15,19.5,61.3,11.8,n/a,1019.6,0.0
09/12/2018,08:30,19.5,61.3,11.8,n/a,1019.6,0.0
09/12/2018,08:45,19.5,61.3,11.8,n/a,1019.6,0.0
09/12/2018,08:48,19.5,61.3,11.8,n/a,1019.6,0.0
09/12/2018,09:00,19.5,61.3,11.8,n/a,1019.6,0.0

I've spent much time on the net trying to identify what is wrong with my sketch but haven't yet sorted out the problem. I could turn power to the arduino and sensor circuit on/off periodically from the SBC  but the time period between updating and not updating variables is irregular. I would prefer not constantly poll the sensors in void loop(). Perhaps someone can see what I've missed or am doing wrong in this sketch.
Code: [Select]
#include "Wire.h"                         // Wire.h library required for I2C communication.
#include "BaroSensor.h"                   // Freetronics library for its MS5637 air pressure module.
#include "cactus_io_HTU21D.h"             // I use cactus.io library for HTU21D. It calculates heat index.
#define slave_address 0x04                // LeoStick's I2C address.                                     
HTU21D htu;                               // Create instance of object to be used in cactus.io function names.

const byte tip_pin = 4;
const byte poll_interrupt_pin = 7;
volatile int poll_data_state = 0;
volatile float temperature;               // Temperature reading from HTU21D module.
volatile float humidity;                  // Humidity reading from HTU21D module.
volatile float heat_index;                // Calculated heat index.
volatile float air_pressure;              // Barometric pressure reading from air pressure module.
volatile float value_to_send;             // Variable assigned in loop to data sent via I2C.
volatile float rain_guage_counter;        // Set to 0 at start reset to 0 again after each data poll.
int rain_guage_state = 0;                 // Current state of the button.
int debounce_time = 150;


void setup()
{
  pinMode(tip_pin, INPUT);              // Set tip pin as INPUT
  pinMode(poll_interrupt_pin, INPUT);   // Set poll interrup pin as Input
  attachInterrupt(digitalPinToInterrupt(poll_interrupt_pin), poll_isr, LOW);
  Wire.begin(slave_address);            // Initialise Leostick as i2c slave
  Wire.onReceive(receive_data);         // Callbacks for i2c communication
  Wire.onRequest(send_data); 
  get_data();                           // When sketch boots up all data variables will be 0.0
                                        // on first polling unless get_data() iss run first
  Serial.begin(9600);                   // Only required for debugging with serial monitor
}

void loop()
{
  // If rain guage bucket tips rain guage state goes low. Increment 1 bucket tip.
  rain_guage_state = digitalRead(tip_pin);
  if (rain_guage_state == LOW)
    {
      delay(debounce_time);
      rain_guage_counter++;
    }
 
  if (poll_data_state == 1)
    {
      get_data();
      // print_data();                  // Uncomment for debugging in serial monitor
      poll_data_state = 0;
    }
}

// Function for polling HTU21D and Baro Sensor.
void poll_isr()
  {
    poll_data_state = 1;
  }

// Function for polling HTU21D and Baro Sensor for weather data.
void get_data()
  {
    BaroSensor.begin();          // Initialise the pressure sensor module
    air_pressure = BaroSensor.getPressure();
    htu.readSensor();           // Initialise temp/Rh sensor                     
    temperature = htu.getTemperature_C();
    humidity = htu.getHumidity();
    heat_index = htu.getHeatIndex_C();
  }

// Function used to provide data to I2C master on request.
void receive_data(int byte_count)
  {
    while(Wire.available())
     {
      int number_command = Wire.read();

      if(number_command==1)
        {
          value_to_send = rain_guage_counter;
          rain_guage_counter = 0;    // Reset counter to 0 for next 15 minute period
        }
      if(number_command==2)
        {
          value_to_send = temperature;
         }
      if(number_command==3)
        {
          value_to_send = humidity;
        }
      if(number_command==4)
        {
          value_to_send = heat_index;
        }
      if(number_command==5)
        {
          value_to_send = air_pressure;
        }
     }
  }
// Function for sending data via I2C after converting float values into vectors of 4 bytes.
void send_data()
  {
    char vector_to_send[4];       
    memcpy(vector_to_send, (char*) & (value_to_send), 4);
    Wire.write(vector_to_send, 4);
  }

// Function for printing data to serial monitor. Not required after debugging.
void print_data()
  {
    Serial.print(rain_guage_counter);//
    Serial.print("  ");
    Serial.print(temperature);
    Serial.print("  ");
    Serial.print(humidity);
    Serial.print("  ");
    Serial.print(heat_index);
    Serial.print("  ");
    Serial.println(air_pressure);
  }

Coding Badly


stowite

#2
Dec 09, 2018, 02:29 am Last Edit: Dec 09, 2018, 02:30 am by stowite
There is quite a lot I don't like about that sketch but the the only really worrying thing is that you call
Code: [Select]

   BaroSensor.begin();          // Initialise the pressure sensor module

every time you read the data. I would expect that call to be made just once in setup() .
   

hanjione

#3
Dec 09, 2018, 02:29 am Last Edit: Dec 09, 2018, 03:05 am by hanjione
The sketch currently running out in my garden shed polls sensors within the main loop (as in code below) I now want to poll the sensors once every 15 minutes (only) but continuously monitor the rain guage.
Code: [Select]
void loop() {
  // check_rain_guage
  button_state = digitalRead(tip_pin);    // check_rain_guage
  if (button_state == LOW) {
    delay(debounce_time);
    rain_guage_counter++;
  }
 
  htu.readSensor();                       // check HTU21D module
  temperature = htu.getTemperature_C();
  humidity = htu.getHumidity();
  heat_index = htu.getHeatIndex_C();
  air_pressure = BaroSensor.getPressure();// check MS5637 pressure module
 
  //Serial.print(rain_guage_counter);     // Use the following Serial.print lines for debugging
  //Serial.print("  ");                   // They can be deleted or commented out later.
  //Serial.print(temperature);
  //Serial.print("  ");
  //Serial.print(humidity);
  //Serial.print("  ");
  //Serial.print(heat_index);
  //Serial.print("  ");
  //Serial.println(air_pressure);
}

hanjione

Quote
There is quite a lot I don't like about that sketch but the the only really worrying thing is that you call
Code: [Select]

   BaroSensor.begin();          // Initialise the pressure sensor module
Yes that's where it normally is but was having a play around this AM as that sensor was sometimes returning 'nan'. As far as the sketch, it is derived from the sketch that I have been using  for around 4 years and it has done what I wanted it too. 24X7. I don't claim to be clever with arduinos. I'm basically a BASH hacker with a little experince with C that wanted to monitor conditions in my garden.

stowite

Yes that's where it normally is but was having a play around this AM as that sensor was sometimes returning 'nan'. As far as the sketch, it is derived from the sketch that I have been using  for around 4 years and it has done what I wanted it too. 24X7.

But did the sketch that sometimes returned NaN call that begin() every time it took a reading ?


hanjione

I'm not quite sure what your looking for but if begin() is in setup it would be called once at boot, if in the get_data function it would be called once every fifteen minutes when the interrupt triggers a call for get_data. This circuit is bread boarded. When connected to the SBC I use a GPIO photomos in the circuit to trigger an interrupt. Watching the serial monitor while manually creating a short to trigger an interrupt with on the breadboard wiith the SBC powered down I was frequently getting 'nan'.

May I ask do you think this may have something to do with why all of the variables sometimes don't update? If so I can easily remove the pressure sensor from my code and circuit and test if you think that will make a difference.

sterretje

Outside my area of expertise, but the problem might be that you're using the Wire library for both slave (listening to the SBC) and master (talking to the MS5637); BaroSensor.begin() calls Wire.begin() (without an address, so the Leonardo as a master).

This should however already have been a problem in your original sketch. Can you post your complete original sketch for reference?
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

hanjione

#8
Dec 09, 2018, 06:09 am Last Edit: Dec 09, 2018, 06:16 am by hanjione
Just to clarify things. The setup out in the garden shed is set up with a DHT22 measuring temp and Rh and collected via one wire with the Leonardo. and I2C to SBC. The MSL Hpa is parsed from a web page at a nearby airport in the script on the SBC. This setup was legacy from a previous TI and Honeywell chip circuit interfacing with owserver on the SBC and parsed MSL Hpa in the script. When I wrote about never having the stale variable problem on my current setup I was talking about between my leonardo and SBC via I2C. The the the HTU21 and MS637 only joined the party on this breadboard.

I can see what you are getting at having just had a squiz in the BaroSensor library. How that might affect my stale variable problem I don't know (yet). Here's a sketch that is very close to whats out in the shed (that sketch has gone walkabout) but upgraded for current sensors. Below the sketch is output from the C program on the SBC that collects the data.via I2C from the Leonardo.
Code: [Select]
#include "Wire.h"                         // Wire.h library required for I2C comm with
                                          // BananaPro.
#include "BaroSensor.h"                   // Use Freetronics library for its MS5637 air
                                          // pressure module.
#include "cactus_io_HTU21D.h"             // Use the cactus.io library for HTU21D module
                                          // this library has a function that calculates
                                          // the heat index.

#define slave_address 0x04                // The Leostick's I2C address                                     
#define tip_pin 4                         // The pin that the rain guage is attached to.
HTU21D htu;                               // Create an instance of the object to be used
                                          // in cactus.io function names.

float temperature;                        // temperature reading from HTU21D module
float humidity;                           // humidity reading from HTU21D module
float heat_index;                         // calculated heat index
float rain_guage_counter = 0;             // Need to begin sketch = 0. It will increment if rain
                                          // guage bucket tips and be will be reset = 0 after
                                          // each I2C query
float air_pressure;                       // barometric pressure reading from air pressure module
float value_to_send;                      // variable assigned in loop to data sent via I2C
int button_state = 0;                     // current state of the button
int debounce_time = 250;                  // change debounce time if necessary

void setup() {
  pinMode(tip_pin, INPUT);                // set tip pin as input
  Wire.begin(slave_address);              // initialise Leostick as i2c slave
  Wire.onReceive(receiveData);            // callbacks for i2c communication
  Wire.onRequest(sendData);               // callbacks for i2c communication         
  BaroSensor.begin();                     // initialise the pressure sensor module
  Serial.begin(9600);                     // only required if using the serial monitor
}

void loop() {
  // check_rain_guage
  button_state = digitalRead(tip_pin);    // check_rain_guage
  if (button_state == LOW) {
    delay(debounce_time);
    rain_guage_counter++;
  }
  htu.readSensor();                       
  temperature = htu.getTemperature_C();
  humidity = htu.getHumidity();
  heat_index = htu.getHeatIndex_C();
  //BaroSensor.begin();                   // initialise the pressure sensor module
  air_pressure = BaroSensor.getPressure();// check MS5637 pressure module

 
  Serial.print(rain_guage_counter);       // Use the following Serial.print lines for debugging
  Serial.print("  ");                     // They can be deleted or commented out later.
  Serial.print(temperature);
  Serial.print("  ");
  Serial.print(humidity);
  Serial.print("  ");
  Serial.print(heat_index);
  Serial.print("  ");
  Serial.println(air_pressure);
}

void receiveData(int byte_count)          // function to handle data recieved from
{                                         // I2C master
  while(Wire.available()) {
    int number_command = Wire.read();

    switch (number_command)
    {
      case 1:
        value_to_send = rain_guage_counter;
        delay(50);
        rain_guage_counter = 0;           // reset counter to 0 to begin the next
        break;                            // 15 minute logging period
      case 2:
        value_to_send = temperature;
        delay(50);
        break;
      case 3:
        value_to_send = humidity;
        delay(50);
        break;
      case 4:
        value_to_send = heat_index;
        delay(50);
        break;
      case 5:
        value_to_send = air_pressure;
        delay(50);
        break;
     }
  }
}
 
void sendData()                           // function for sending data via I2C
{                                         // convert the float values in a vector
  char vector_to_send[4];                 // of 4 bytes to send via I2C bus
                                     
  memcpy(vector_to_send,(char*)&(value_to_send),4);
  Wire.write(vector_to_send,4);
}


Code: [Select]
root@bpro10 # get_w_data
0,25.92,45.00,26.33,1006.43
root@bpro10 # get_w_data
0,25.91,45.24,26.33,1006.45
root@bpro10 # get_w_data
0,25.90,45.18,26.34,1006.45
root@bpro10 # get_w_data
0,25.91,45.39,26.34,1006.43
root@bpro10 # get_w_data
0,25.89,45.25,26.32,1006.47
root@bpro10 # get_w_data
0,25.88,45.20,26.31,1006.43

Jimmus

Well, the only place where those variables are changed is in get_data().  So either get_data() is not being called, or htu.readSensor() is not upadting the variables correctly.

Either scenario could be happening, and how you fix it will depend heavily on which it is.  If get_data() isn't being called, suspect that the hardware isn't triggering an interrupt.  If get_data() IS being called, suspect the wire communications in htu.readSensors.  The cactus library has absolutely no error handling, so you'll have to figure out how to detect communication failure and how to handle that.

But first, figure out if get_data() is being called.  It should be pretty easy to put some kind of debug signal into the code to indicate whether or when get_data() gets called.  Toggle an LED or do a serial write or something inside the get_data() function.

hanjione

Thanks to all for their input! It has given me some food for thought. Note that my situation of all variables ( from two sensors) sometimes not being updated occurs only when polling data on the Leo. from my SBC via I2C. The circuit is switched on/off and powered with the SBC. There is no USB connection. When the sketch is run independent of the SBC and connected to a PC via USB there is no problem of stale variables when shorting the pin to create an interrupt.  

I  will put together and run a script at intervals that will poll data via i2c first without interrupt which will give me the raw data in memory and then immediately following I will send an interrupt and again poll the raw data. Output will be time stamped and logged. This is the best I can do and maybe it will provide some insight.

Secondly there may be an issue with my air pressure sensor. I am using an Adafruit Humidity/Temperature sensor breakout board  with pull up resistors. My pressure sensor is a Freetronics Barometric Pressure sensor on a breakout board with pull up resistors. It appears that that it might be necessary to disable the pull ups on the baro sensor if there is another sensor with pull ups on the same bus. I have an extra baro sensor and will have a squiz at this after a seeing what I find with a prolonged period of logging as above.

hanjione

#11
Dec 10, 2018, 01:11 am Last Edit: Dec 10, 2018, 01:18 am by hanjione
Perhaps the subject should be changed to "I2C problem"

After logging for 10.5 hours it appears that variables not being updated is not the problem. Variables are updated on the interrupt. After a period of time something happens and my SBC can no longer poll raw data via I2C from the Leo. Consequently the script on the SBC that processes the data will use the last set of raw data that had been downloaded. It seems to me that this is an I2C problem that cumulatively gets worse and eventually no raw data can be downloaded via I2C from the Leo. I'm thinking trying a log again but with a one minute delay separating the interrupt and the I2C polling request from the SBC.

I polled raw data before and after the interrupt. Raw data before the interrupt (clean) matched the raw data polled after the previous interrupt (intrpt). The sketch is working as intended.
Code: [Select]
09/12/2018,23:30,0,18.80,62.14,24.71,1005.89, clean
09/12/2018,23:30,0,18.80,62.87,24.63,1006.02, intrpt

09/12/2018,23:45,0,18.77,62.87,24.63,1006.02, clean
09/12/2018,23:45,0,18.77,63.67,24.51,1006.34, intrpt

10/12/2018,00:00,0,18.78,63.67,24.51,1006.34, clean
10/12/2018,00:00,0,18.71,64.25,24.46,1006.28, intrpt

10/12/2018,00:15,0,18.71,64.25,24.46,1006.28, clean
10/12/2018,00:15,0,18.49,65.70,24.34,1006.25, intrpt

2.5 hours after starting the log no data is downloaded after the interrupt, however the variables have been updated after the interrupt since the following clean poll downloads raw data although the rain gauge bucket tips variable is missing.
Code: [Select]
10/12/2018,01:45,0,16.90,73.80,23.65,1006.27, clean
10/12/2018,01:45,0,16.46,75.19,23.63,1006.21, intrpt

10/12/2018,02:00,0,16.46,75.19,23.63,1006.21, clean
10/12/2018,02:00,, intrpt

10/12/2018,02:15,16.24,76.52,23.43,1006.10, clean
10/12/2018,02:15,0,15.91,77.19,23.52,1006.39, intrpt

All good again and until 4 hours later when the clean raw data is missing the bucket tips variable and the clean raw data does not match the previous data taken after the interrupt. This looks like get_data had been called again somewhere between 06:00 and 06:15.
Code: [Select]
10/12/2018,06:00,0,16.37,73.58,24.15,1007.58, clean
10/12/2018,06:00,0,16.32,73.54,24.21,1007.68, intrpt

10/12/2018,06:15,16.27,73.28,24.33,1007.66, clean
10/12/2018,06:15,0,16.22,73.68,24.27,1007.63, intrpt

10/12/2018,06:30,0,16.22,73.68,24.27,1007.63, clean
10/12/2018,06:30,0,16.33,73.61,24.18,1007.89, intrpt

Finally there is total failure at which time the script that processes the data on the SBC would start logging variables from the last successful poll evry 15 minutes until the Leo is rebooted.
Code: [Select]
10/12/2018,08:15,, clean
10/12/2018,08:15,, intrpt

10/12/2018,08:30,, clean
10/12/2018,08:30,, intrpt

10/12/2018,08:45,, clean
10/12/2018,08:45,, intrpt

10/12/2018,09:00,, clean
10/12/2018,09:00,, intrpt

10/12/2018,09:16,21.72,50.83,24.91,nan, clean
10/12/2018,09:16,, intrpt

10/12/2018,09:31,, clean
10/12/2018,09:31,, intrpt

10/12/2018,09:46,, clean
10/12/2018,09:46,, intrpt

Note that the one minute increment in the last 2 entries above were probably due to a .05 and a .10 second delay I had put in the logging script.

sterretje

Did your old sensors also use I2C for communicatiom?
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

hanjione

The current set up out in the shed uses I2C between the Leo and SBC but not within circuit to Leo. The prototype I'm working on now
is I2C from sensors in circuit to Leo as well as Leo to SBC. This increasingly looks like an I2C conflict of some sorts. I've done up a new logging script on the SBC that does the interrupt first, delays 5 seconds, and then polls data. Perhaps separating the two I2C functions with a delay will solve the problem ..... dunno.

sterretje

I'm surprised that you actually get data on the SBC. Once you do a BaroSensor.begin(), the I2C in the controller switches to master and will not listen to the slave address so 'commands' from the SBC will be ignored. Now you have two masters on the bus. When they both talk at the same time, there is a bus conflict; I2C supports that (one master will win and the other one will give up) and it's called 'lost arbitration' (or something like that). Multimaster is (partially?) supported in the Wire library, it can at least report back that it did loose the arbitration.

One thing that you need to do is after you have received data from the sensor is to switch the I2C to slave again using Wire.begin(slave_address) so it can listen to the SBC. If it will be successful is another question.

You will probably be far better of with a different means of communication with the SBC. Serial communication would be simple. Alternatively, there is a software I2C implementation that might work; that way you can have two separate I2C buses, one for communication the SBC and one for the communication with the sensor. No idea how well the software I2C implementation works.
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

Go Up