[Solved] Printing to LCD from incoming XBee Data

Is there an easy way for these values to go to "0" or "error" if the Tx goes offline?

The design of your program is somewhat based on the original conditions where the transmission was faster than the display and there was run on and jumbled data.

Now with the tx every 30 seconds, and the display taking 24 seconds the can be some clean up. Setting newData = false and newData = true can be done in one location for each. I have added another boolean variable to control the display, so that you still only parse once and are ready for the next packet.

Try this. It untested, but I think the code will display the 6 data screens and then a "Waiting new data" screen until there is a new packet received and parsed.

#include <SoftwareSerial.h>
SoftwareSerial XBee(2, 3); // RX, TX

#include <Wire.h>
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 7, d5 = 6, d6 = 5, d7 = 4;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#include <string.h>
#include <stdio.h>

const byte numChars = 64;  //This means 64 characters are expected.
char receivedChars[numChars]; // array that is received
char tempChars [numChars]; // temporary array for use when parsing

//variables to hold the parsed data
int TempCbmp = 0;
int TempFbmp = 0;
int TempCdht = 0;
int TempFdht = 0;
int HICdht = 0;
int HIFdht = 0;
float H1dht = 00.00;
float DPdht = 00.00;
long P1bmp = 000000;
float P2bmp = 000.00;
float P3bmp = 00.00;

boolean newData = false;
boolean displayNewData = false;//add new control variable

//non blocking display control variables
unsigned long lastDisplaySwitch;
unsigned long interval = 4000; //4 seconds for each lcd screen
byte count = 0;// for display screen

//============

void setup()
{
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.clear ();
  lcd.setCursor(0, 0);
  lcd.print("Waiting New Data"); //should be 16 chars and fill 1st row
        
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  Serial.begin(9600);
}

void loop() {

  recvWithEndMarker();

  if (newData == true) {
    //this temporary copy is necessary to protect the original data
    //because strtok() used in parseData() replaces the commas with \0
    memset(tempChars, '\0', 64); //clear tempChars
    strcpy(tempChars, receivedChars);
    memset(receivedChars, '\0', 64);//clear receivedChars
    parseData();
  }

  if(displayNewData == true)
    showDisplay();

}

//========

void recvWithEndMarker() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (XBee.available() > 0 && newData == false) { // If Xbee is receiving data AND newData is false, THEN "rc" is the XBee data
    rc = XBee.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;  //if rc is NOT the endMarker, then index "rc" to the "receivedChars" array.
        ndx++;                    // go to the next character and index it as well
        if (ndx >= numChars) {    // what does this mean? why would ndx ever be greater than 64?
          ndx = numChars - 1;     // what does this do?
        }
      }
      else {
        receivedChars[ndx] = '\0';  //terminate the string when the endMarker is received
        recvInProgress = false;     // and mark as NOT recvInProgress
        ndx = 0;                    // move index back to 0 position?
        newData = true;             // why is new data here marked as true? shouldn't it be above the "ndx=0"?
      }
    }
    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

//=======

void parseData() { // split the data into it's parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ","); // get the first part - the string
  TempCbmp = atoi(strtokIndx); // copy it to TempCbmp

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempFbmp = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempCdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempFdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  HICdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  HIFdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ",");
  H1dht = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  DPdht = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  P1bmp = atol(strtokIndx);     // convert this part to a long

  strtokIndx = strtok(NULL, ",");
  P2bmp = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  P3bmp = atof(strtokIndx);     // convert this part to a float

  newData = false;
  displayNewData = true;

}

//======

void showDisplay()
{
  if (millis() - lastDisplaySwitch > interval)
  {
    lastDisplaySwitch += interval;
    count++;

    switch (count) {
      case 1:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("TempC(bmp): ");
        lcd.print(TempCbmp);
        lcd.setCursor(0, 1);
        lcd.print("TempF(bmp): ");
        lcd.print(TempFbmp);  // Round 1 of data display complete
        break;
      case 2:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("TempC(dht): ");
        lcd.print(TempCdht);
        lcd.setCursor(0, 1);
        lcd.print("TempF(dht): ");
        lcd.print(TempFdht);  // Round 2 of data display complete
        break;
      case 3:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("HIC: ");
        lcd.print(HICdht);
        lcd.setCursor(0, 1);
        lcd.print("HIF: ");
        lcd.print(HIFdht);  // Round 3 of data display complete
        break;
      case 4:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("H: ");
        lcd.print(H1dht);
        lcd.setCursor(0, 1);
        lcd.print("DP: ");
        lcd.print(DPdht);  // Round 4 of data display complete
        break;
      case 5:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("P(Pa): ");
        lcd.print(P1bmp);
        lcd.setCursor(0, 1);
        lcd.print("P(mm): ");
        lcd.print(P2bmp);
        break;
      case 6:
        lcd.setCursor(0, 1);
        lcd.print("P(in): ");
        lcd.print(P3bmp);
        break;
      case 7:
        displayNewData = false;
        count = 0; //reset display counter
        lcd.setCursor(0, 0);
        lcd.print("Waiting New Data"); //should be 16 chars and fill 1st row
        lcd.setCursor(0, 1);
        lcd.print("                "); //16 spaces to clear second row
        break;
      default:
        // if nothing else matches, do the default
        // default is optional
        break;
    }
  }
}

Thanks cattledog.

From testing, it looks like it works but is inconsistent in running through all the cases. The LCD display rarely starts with Case 1 or 2. The LCD display usually starts with Case 3, or Case 4 but once it has run through all the cases, it turns to "Waiting New Data" and occasionally cycle through the Pressure values but eventually just displays the "Waiting New Data. text"

Not to sure why this is happening but I will try to better understand your code.

Thanks for your code and help.

The LCD display rarely starts with Case 1 or 2. The LCD display usually starts with Case 3, or Case 4

Try changing this line. It think the count increments several times before the last value catches up.

//lastDisplaySwitch += interval;
lastDisplaySwitch = millis();

Thanks so much cattledog. Its working great now. That seemed to do it. :slight_smile:

I couldn't have gotten there without everyone's help. Thanks to all of you for your help.

Next step is to estimate the power usage and determine battery and PV size for the Tx.

Thanks again!

Thanks to everyone for the help here. Cattledog, thanks for all your help with that last bit of code.

I'm introducing sleeping functions to the Tx code to reduce the needed size of the battery. As I look into these calculations, I'm realizing that data may be sent every 15 min. In this case I would like the data that was received in the last transmition to still be displayed on the LCE screen, but I would also like to see how old the data is. How would I update the Rx code to display the length of time that's passed since the last transmission was received. Tor example the Rx would display the following fields:

TempC(bmp): X
TempF(bmp): X
TempC(dht): X
TempF(dht): X
HIC: X
HIF: X
H1: XX.XX
DP: XX.XX
P(Pa): XXXXXX
P(mm): XXX.XX
P(in): XX.XX
Last Xmit: 5min 37sec

This would continue to cycle through the different values from the last transmission but the "Last Xmit" field would update to 6 min or 9 min depending on the length of time that passed. How could I do this?

#include <SoftwareSerial.h>
SoftwareSerial XBee(2, 3); // RX, TX

#include <Wire.h>
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 7, d5 = 6, d6 = 5, d7 = 4;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#include <string.h>
#include <stdio.h>

const byte numChars = 64;  //This means 64 characters are expected.
char receivedChars[numChars]; // array that is received
char tempChars [numChars]; // temporary array for use when parsing

//variables to hold the parsed data
int TempCbmp = 0;
int TempFbmp = 0;
int TempCdht = 0;
int TempFdht = 0;
int HICdht = 0;
int HIFdht = 0;
float H1dht = 00.00;
float DPdht = 00.00;
long P1bmp = 000000;
float P2bmp = 000.00;
float P3bmp = 00.00;

boolean newData = false;
boolean displayNewData = false;//add new control variable

//non blocking display control variables
unsigned long lastDisplaySwitch;
unsigned long interval = 4000; //4 seconds for each lcd screen
byte count = 0;// for display screen

//============

void setup()
{
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.clear ();
  lcd.setCursor(0, 0);
  lcd.print("Waiting New Data"); //should be 16 chars and fill 1st row
       
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  Serial.begin(9600);
}

void loop() {

  recvWithEndMarker();

  if (newData == true) {
    //this temporary copy is necessary to protect the original data
    //because strtok() used in parseData() replaces the commas with \0
    memset(tempChars, '\0', 64); //clear tempChars
    strcpy(tempChars, receivedChars);
    memset(receivedChars, '\0', 64);//clear receivedChars
    parseData();
  }

  if(displayNewData == true)
    showDisplay();

}

//========

void recvWithEndMarker() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (XBee.available() > 0 && newData == false) { // If Xbee is receiving data AND newData is false, THEN "rc" is the XBee data
    rc = XBee.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;  //if rc is NOT the endMarker, then index "rc" to the "receivedChars" array.
        ndx++;                    // go to the next character and index it as well
        if (ndx >= numChars) {    // what does this mean? why would ndx ever be greater than 64?
          ndx = numChars - 1;     // what does this do?
        }
      }
      else {
        receivedChars[ndx] = '\0';  //terminate the string when the endMarker is received
        recvInProgress = false;     // and mark as NOT recvInProgress
        ndx = 0;                    // move index back to 0 position?
        newData = true;             // why is new data here marked as true? shouldn't it be above the "ndx=0"?
      }
    }
    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

//=======

void parseData() { // split the data into it's parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ","); // get the first part - the string
  TempCbmp = atoi(strtokIndx); // copy it to TempCbmp

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempFbmp = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempCdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempFdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  HICdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  HIFdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ",");
  H1dht = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  DPdht = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  P1bmp = atol(strtokIndx);     // convert this part to a long

  strtokIndx = strtok(NULL, ",");
  P2bmp = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  P3bmp = atof(strtokIndx);     // convert this part to a float

  newData = false;
  displayNewData = true;

}

//======

void showDisplay()
{
  if (millis() - lastDisplaySwitch > interval)
  {
    lastDisplaySwitch = millis();
    count++;

    switch (count) {
      case 1:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("TempC(bmp): ");
        lcd.print(TempCbmp);
        lcd.setCursor(0, 1);
        lcd.print("TempF(bmp): ");
        lcd.print(TempFbmp);  // Round 1 of data display complete
        break;
      case 2:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("TempC(dht): ");
        lcd.print(TempCdht);
        lcd.setCursor(0, 1);
        lcd.print("TempF(dht): ");
        lcd.print(TempFdht);  // Round 2 of data display complete
        break;
      case 3:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("HIC: ");
        lcd.print(HICdht);
        lcd.setCursor(0, 1);
        lcd.print("HIF: ");
        lcd.print(HIFdht);  // Round 3 of data display complete
        break;
      case 4:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("H: ");
        lcd.print(H1dht);
        lcd.setCursor(0, 1);
        lcd.print("DP: ");
        lcd.print(DPdht);  // Round 4 of data display complete
        break;
      case 5:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("P(Pa): ");
        lcd.print(P1bmp);
        lcd.setCursor(0, 1);
        lcd.print("P(mm): ");
        lcd.print(P2bmp);
        break;
      case 6:
        lcd.setCursor(0, 1);
        lcd.print("P(in): ");
        lcd.print(P3bmp);
        break;
      case 7:
        displayNewData = false;
        count = 0; //reset display counter
        lcd.setCursor(0, 0);
        lcd.print("Waiting New Data"); //should be 16 chars and fill 1st row
        lcd.setCursor(0, 1);
        lcd.print("                "); //16 spaces to clear second row
        break;
      default:
        // if nothing else matches, do the default
        // default is optional
        break;
    }
  }
}

Thanks

This would continue to cycle through the different values from the last transmission but the "Last Xmit" field would update to 6 min or 9 min depending on the length of time that passed. How could I do this?

Here's one possible approach which makes minimal changes to the current program. This is untested but give it a try. I'm short of time right now, and if this approach doesn't work properly I'll take another look based on your feedback.

1). Remove the condition on show display

//if(displayNewData == true)
    showDisplay();
  1. Create a new global variable unsigned long lastReceived and modify the parseData() function to end with
newData = false;
  //displayNewData = true;
lastReceived = millis();
count = 0;
  1. Modify Case7 of the showDisplay() function
case 7:
        //displayNewData = false;
        count = 0; //reset display counter
        lcd.setCursor(0, 0);
        //lcd.print("Waiting New Data"); //should be 16 chars and fill 1st row
        lcd.print("Last Xmit Time ");
        lcd.setCursor(0, 1);
        //lcd.print("                "); //16 spaces to clear second row
        unsigned long deltaMillis = millis() - lastReceived;
        //convert deltaMillis to minutes and seconds division by 1000 then use /60 and modulo 60
        //lcd.print minutes and seconds value
        break;

Thanks cattledog. I updated my code to the following:

#include <SoftwareSerial.h>
SoftwareSerial XBee(2, 3); // RX, TX

#include <Wire.h>
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 7, d5 = 6, d6 = 5, d7 = 4;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#include <string.h>
#include <stdio.h>

const byte numChars = 64;  //This means 64 characters are expected.
char receivedChars[numChars]; // array that is received
char tempChars [numChars]; // temporary array for use when parsing

//variables to hold the parsed data
int TempCbmp = 0;
int TempFbmp = 0;
int TempCdht = 0;
int TempFdht = 0;
int HICdht = 0;
int HIFdht = 0;
float H1dht = 00.00;
float DPdht = 00.00;
long P1bmp = 000000;
float P2bmp = 000.00;
float P3bmp = 00.00;

boolean newData = false;
boolean displayNewData = false;   //add new control variable

//non blocking display control variables
unsigned long lastDisplaySwitch;
unsigned long interval = 4000;    //4 seconds for each lcd screen
byte count = 0;                   // for display screen

unsigned long lastReceived;

//============

void setup()
{
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.clear ();
  lcd.setCursor(0, 0);
  lcd.print("Waiting New Data"); //should be 16 chars and fill 1st row
       
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  Serial.begin(9600);

}

void loop() {

  recvWithEndMarker();

  if (newData == true) {
    //this temporary copy is necessary to protect the original data
    //because strtok() used in parseData() replaces the commas with \0
    memset(tempChars, '\0', 64);      //clear tempChars
    strcpy(tempChars, receivedChars);
    memset(receivedChars, '\0', 64);  //clear receivedChars
    parseData();
  }
  //if(displayNewData == true)
    showDisplay();
}

//========

void recvWithEndMarker() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (XBee.available() > 0 && newData == false) { // If Xbee is receiving data AND newData is false, THEN "rc" is the XBee data
    rc = XBee.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;  //if rc is NOT the endMarker, then index "rc" to the "receivedChars" array.
        ndx++;                    // go to the next character and index it as well
        if (ndx >= numChars) {    // what does this mean? why would ndx ever be greater than 64?
          ndx = numChars - 1;     // what does this do?
        }
      }
      else {
        receivedChars[ndx] = '\0';  //terminate the string when the endMarker is received
        recvInProgress = false;     // and mark as NOT recvInProgress
        ndx = 0;                    // move index back to 0 position?
        newData = true;             // why is new data here marked as true? shouldn't it be above the "ndx=0"?
      }
    }
    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

//=======

void parseData() { // split the data into it's parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ","); // get the first part - the string
  TempCbmp = atoi(strtokIndx); // copy it to TempCbmp

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempFbmp = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempCdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempFdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  HICdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  HIFdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ",");
  H1dht = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  DPdht = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  P1bmp = atol(strtokIndx);     // convert this part to a long

  strtokIndx = strtok(NULL, ",");
  P2bmp = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  P3bmp = atof(strtokIndx);     // convert this part to a float

  newData = false;
  // displayNewData = true;
  lastReceived = millis();
  count = 0;

}

//======

void showDisplay()
{
  if (millis() - lastDisplaySwitch > interval)
  {
    lastDisplaySwitch = millis();
    count++;

    switch (count) {
      case 1:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("TempC(bmp): ");
        lcd.print(TempCbmp);
        lcd.setCursor(0, 1);
        lcd.print("TempF(bmp): ");
        lcd.print(TempFbmp);  // Round 1 of data display complete
        break;
      case 2:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("TempC(dht): ");
        lcd.print(TempCdht);
        lcd.setCursor(0, 1);
        lcd.print("TempF(dht): ");
        lcd.print(TempFdht);  // Round 2 of data display complete
        break;
      case 3:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("HIC: ");
        lcd.print(HICdht);
        lcd.setCursor(0, 1);
        lcd.print("HIF: ");
        lcd.print(HIFdht);  // Round 3 of data display complete
        break;
      case 4:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("H: ");
        lcd.print(H1dht);
        lcd.setCursor(0, 1);
        lcd.print("DP: ");
        lcd.print(DPdht);  // Round 4 of data display complete
        break;
      case 5:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("P(Pa): ");
        lcd.print(P1bmp);
        lcd.setCursor(0, 1);
        lcd.print("P(mm): ");
        lcd.print(P2bmp);
        break;
      case 6:
        lcd.setCursor(0, 1);
        lcd.print("P(in): ");
        lcd.print(P3bmp);
        break;
      case 7:
        count = 0; //reset display counter
        lcd.setCursor(0, 0);
        lcd.print("Last Xmit Time ");
        lcd.setCursor(0, 1);
        unsigned long deltaMillis = millis() - lastReceived;
        //convert deltaMillis to minutes and seconds division by 1000 then use /60 and modulo 60
        //lcd.print minutes and seconds value
        break;
      default:
        // if nothing else matches, do the default
        // default is optional
        break;
    }
  }
}

I got the following error messages.

dgweather_RxV10.ino:218: error: crosses initialization of 'long unsigned int deltaMillis'
         unsigned long deltaMillis = millis() - lastReceived;
                       ^
exit status 1
crosses initialization of 'long unsigned int deltaMillis'

Any thoughts on what the issue is here?

Thanks again for your help. Much appreciated!

Any thoughts on what the issue is here?

This was new to me, so I used Mr. Google with "crosses initialization error C++" and was led to all sorts of interesting discussions of variable declarations and variable scope in switch case statements.

There are two ways to fix this.

You can declare deltaMillis as a global variable, and it looks quite nice sitting with the declaration of lastReceived. I actually prefer this approach as most of the variables and code in the sketch is not rigorously encapsulated and there are plenty of other global variables.

unsigned long lastReceived;
unsigned long deltaMillis;

Then in the case 7, don't redeclare it, but just use

//unsigned long deltaMillis = millis() - lastReceived;
deltaMillis = millis() - lastReceived;

Alternatively, you can localize the variable and resolve the switch case scope problem by enclosing case 7: code in brackets

case7 :
{
//all case 7 code
}

Thanks again for your help cattledog.

I'm obviously pretty new at this. I'm struggling understanding the millis() function and attempted to use the %modulo function but it doesn't seem to be working. The lcd displays "0 min 0 sec" every time now.

Operating code:

#include <SoftwareSerial.h>
SoftwareSerial XBee(2, 3); // RX, TX

#include <Wire.h>
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 7, d5 = 6, d6 = 5, d7 = 4;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#include <string.h>
#include <stdio.h>

const byte numChars = 64;  //This means 64 characters are expected.
char receivedChars[numChars]; // array that is received
char tempChars [numChars]; // temporary array for use when parsing

//variables to hold the parsed data
int TempCbmp = 0;
int TempFbmp = 0;
int TempCdht = 0;
int TempFdht = 0;
int HICdht = 0;
int HIFdht = 0;
float H1dht = 00.00;
float DPdht = 00.00;
long P1bmp = 000000;
float P2bmp = 000.00;
float P3bmp = 00.00;

boolean newData = false;
boolean displayNewData = false;   //add new control variable

//non blocking display control variables
unsigned long lastDisplaySwitch;
unsigned long interval = 4000;    //4 seconds for each lcd screen
byte count = 0;                   // for display screen

unsigned long lastReceived;
unsigned long deltaMillis;

//============

void setup()
{
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.clear ();
  lcd.setCursor(0, 0);
  lcd.print("Waiting New Data"); //should be 16 chars and fill 1st row
       
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  Serial.begin(9600);
}

void loop() {
  recvWithEndMarker();
  if (newData == true) {
    //this temporary copy is necessary to protect the original data
    //because strtok() used in parseData() replaces the commas with \0
    memset(tempChars, '\0', 64);      //clear tempChars
    strcpy(tempChars, receivedChars);
    memset(receivedChars, '\0', 64);  //clear receivedChars
    parseData();
  }
  //if(displayNewData == true)
    showDisplay();
}

//========

void recvWithEndMarker() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (XBee.available() > 0 && newData == false) { // If Xbee is receiving data AND newData is false, THEN "rc" is the XBee data
    rc = XBee.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;  //if rc is NOT the endMarker, then index "rc" to the "receivedChars" array.
        ndx++;                    // go to the next character and index it as well
        if (ndx >= numChars) {    // what does this mean? why would ndx ever be greater than 64?
          ndx = numChars - 1;     // what does this do?
        }
      }
      else {
        receivedChars[ndx] = '\0';  //terminate the string when the endMarker is received
        recvInProgress = false;     // and mark as NOT recvInProgress
        ndx = 0;                    // move index back to 0 position?
        newData = true;             // why is new data here marked as true? shouldn't it be above the "ndx=0"?
      }
    }
    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

//=======

void parseData() { // split the data into it's parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ","); // get the first part - the string
  TempCbmp = atoi(strtokIndx); // copy it to TempCbmp

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempFbmp = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempCdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  TempFdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  HICdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  HIFdht = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ",");
  H1dht = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  DPdht = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  P1bmp = atol(strtokIndx);     // convert this part to a long

  strtokIndx = strtok(NULL, ",");
  P2bmp = atof(strtokIndx);     // convert this part to a float

  strtokIndx = strtok(NULL, ",");
  P3bmp = atof(strtokIndx);     // convert this part to a float

  newData = false;
  // displayNewData = true;
  lastReceived = millis();
  count = 0;

}

//======

void showDisplay()
{
  if (millis() - lastDisplaySwitch > interval)
  {
    lastDisplaySwitch = millis();
    count++;

    switch (count) {
      case 1:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("TempC(bmp): ");
        lcd.print(TempCbmp);
        lcd.setCursor(0, 1);
        lcd.print("TempF(bmp): ");
        lcd.print(TempFbmp);  // Round 1 of data display complete
        break;
      case 2:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("TempC(dht): ");
        lcd.print(TempCdht);
        lcd.setCursor(0, 1);
        lcd.print("TempF(dht): ");
        lcd.print(TempFdht);  // Round 2 of data display complete
        break;
      case 3:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("HIC: ");
        lcd.print(HICdht);
        lcd.setCursor(0, 1);
        lcd.print("HIF: ");
        lcd.print(HIFdht);  // Round 3 of data display complete
        break;
      case 4:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("H: ");
        lcd.print(H1dht);
        lcd.setCursor(0, 1);
        lcd.print("DP: ");
        lcd.print(DPdht);  // Round 4 of data display complete
        break;
      case 5:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("P(Pa): ");
        lcd.print(P1bmp);
        lcd.setCursor(0, 1);
        lcd.print("P(mm): ");
        lcd.print(P2bmp);
        break;
      case 6:
        lcd.setCursor(0, 1);
        lcd.print("P(in): ");
        lcd.print(P3bmp);
        break;
      case 7:
        lcd.clear();
        count = 0; //reset display counter
        lcd.setCursor(0, 0);
        lcd.print("Last Xmit Time:");
        lcd.setCursor(0, 1);
        deltaMillis = millis()-lastReceived;
        //convert deltaMillis to minutes and seconds division by 1000 then use /60 and modulo 60
        //lcd.print minutes and seconds value
        lcd.print(deltaMillis/1000/60);                      // prints minutes in lcd
        lcd.print(" min ");
        lcd.print ((deltaMillis/1000/60)%60);                // prints the remaining seconds
        lcd.print(" sec");
        break;
        default:
        // if nothing else matches, do the default
        // default is optional
        break;
    }
  }
}

Where am I going wrong?
Thanks again.

The seconds math is wrong

//lcd.print ((deltaMillis/1000/60)%60);  // prints the remaining seconds
 lcd.print ((deltaMillis/1000)%60);      
lcd.print(" sec");

Think this through. deltaMillis/1000 is seconds. You then are working with the number of elapsed seconds.

Integer math truncation with /60 will give minutes. Modulo(remainder) math with %60 will give number of seconds beyond whole minutes.

But you should be seeing minutes values with this

lcd.print(deltaMillis/1000/60);

If fixing the seconds %60 doesn't get you what you want, you will have to do some debugging. I don't know if you can use any Serial prints for debug or if you will have to use the lcd, but you should get some checking on the deltaMillis value to see if it and lastReceived are behaving as expected.

Thank you!

I'm a bit embarrassed I didn't get that the first go around so thanks for working through that. I can say now that I am better understanding millis though and the code is working great.

Thank you!

As this project evolves, the last update I'd like to implement is add code to transmit and display battery voltage at the end of the weather data array. This would allow for monitoring of the battery. The Arduino Fio has a built in voltage meter which seems to be pretty well known. I am trying to include this code into the transmitter but running into some bumps. Current code is below:

#include <LowPower.h>
#define xbeePowerPin 9 // Arduino pin used to sleep the XBee

#include "DHT.h"
#define DHTPIN 7     // what digital pin we're connected to

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

DHT dht(DHTPIN, DHTTYPE);

#include <Wire.h>
#include <Adafruit_BMP085.h>
#include "LowPower.h"

unsigned long XBEETurnOn;
unsigned long XBEETurnOff;
int resultVcc;
float resultVccFloat;

// See https://code.google.com/archive/p/tinkerit/wikis/SecretVoltmeter.wiki
// This will measure the voltage of the battery connected to the Arduino 
float readVcc(){
  signed long resultVcc;
  float resultVccFloat; // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(10);             // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);   // convert
  while (bit_is_set(ADCSRA,ADSC));      // Measuring
  resultVcc = ADCL;
  resultVcc |= ADCH<<8;
  resultVcc = 1125300L / resultVcc; // Back-calculate AVcc in mV
  resultVccFloat = (float) resultVcc /1000; //Convert to float in Volts
  return resultVccFloat;
}

Adafruit_BMP085 bmp;
int tempC;  // Variable for holding temp in C | oringinally float, changed to int
int tempF;  // Variable for holding temp in F | oringinally float, changed to int
float pressure; //Variable for holding pressure reading in Pa | oringinally float, changed to int
float pressuremmHg; // Variable for holding pressure reading in mmHg | oringinally float, changed to int
float pressureinHg; // Variable for holding pressure reading in inHg| oringinally float, changed to int
int dewpoint; // Variable for holding pressure reading in *C| oringinally float, changed to int

// We'll use SoftwareSerial to communicate with the XBee:
#include <SoftwareSerial.h>
// XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX)
SoftwareSerial XBee(2, 3); // RX, TX

  char dataPacket [70]; //an array with 12 integer elements, but 70 total characters. How is this 70 characters?
  int i; //used as an index into the array
  char P1[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char P2[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char P3[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char H1[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char Vcc[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats

void setup() {
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  Serial.begin(9600);
  dht.begin();
  bmp.begin();
    }
    
void loop() {
    // Fio Enter power down state for number of cycles indicated below with ADC and BOD module disabled
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec 
    //Need to add more for 1 min sleep once code is correct and working
    
//DHT Values Below:
    // Reading temperature or humidity takes about 250 milliseconds! | oringinally float, changed to int
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.readHumidity(); 
    // Read temperature as Celsius (the default) | oringinally float, changed to int
    int t = dht.readTemperature();
    // Read temperature as Fahrenheit (isFahrenheit = true) | oringinally float, changed to int
    int f = dht.readTemperature(true);
    // Compute heat index in Fahrenheit (the default) | oringinally float, changed to int
    int hif = dht.computeHeatIndex(f, h);
    // Compute heat index in Celsius (isFahreheit = false)| oringinally float, changed to int
    int hic = dht.computeHeatIndex(t, h, false);
    int DP = (t - ((100 - h) / 5)); // Calculates the Dew Point in *C if Humidity >50% (DHT) dd.dd
    
//DHT Values Below:
    tempC = bmp.readTemperature();        //  Read Temperature (bmp)
    tempF = tempC * 1.8 + 32.;            // Convert degrees C to F (bmp)
    pressure = bmp.readPressure();        // Read Pressure (bmp)
    pressuremmHg = (pressure * .0075);    //(bmp) 
    pressureinHg = (pressure / 3386.39);  //(bmp)

    dtostrf(pressure,9,2,P1);     // due to issues showing pressue in the "sprintf" function because it's a float value. 9 digits, 0 significant digits
    dtostrf(pressuremmHg,6,2,P2); // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(pressureinHg,5,2,P3); // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(h,5,2,H1);            // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(resultVccFloat,4,3,Vcc);            
  
    {
      pinMode(xbeePowerPin, OUTPUT);
      XBEETurnOn = millis();
      digitalWrite(xbeePowerPin, LOW);
      delay (1000);
      // sprintf(dataPacket, "X%d" ,gx) - this is used to compile multiple datastrings togeter
      // d=integer, f=float ,s=sting
      sprintf(dataPacket,"<%d,%d,%d,%d,%d,%d,%s,%d,%s,%s,%s,%s>",tempC,tempF,t,f,hic,hif,H1,DP,P1,P2,P3,Vcc);
      Serial.println(dataPacket);
      XBee.print(dataPacket);
      // all together the output to the serial monitor is going to be the following
      //tt(2),ff(2),II(2),ii(2),hh(2),dd(2),HH.HH(5),DD.DD(5),PPPPPP.PP(6),mmm.mm(6),NN.NN(5),V.VVV(4) 59 characters total
      pinMode(xbeePowerPin, INPUT_PULLUP);
      digitalWrite(xbeePowerPin, HIGH);
      XBEETurnOff = millis();
      // Serial.println (XBEETurnOff-XBEETurnOn);
      Serial.print (resultVcc);
      Serial.println(" =Vcc");
      Serial.print (resultVccFloat);
      Serial.println (" =resultVccFloat");
      Serial.print( readVcc(), DEC );
      Serial.println (" =readVcc");
      delay(1000);
    }
}

It seems to be uploading fine to the Fio but voltage isn't being converted correctly. It is displayed 0.000 in the array with the weather data. The Vcc and the resultVccFloat calculation didn't work but readVcc is calculated just. Below is an output of the Serial read for one loop cycle:

<18,64,18,65,17,65,82.00,14,101198.00,758.98,29.88,0.000>
0 =Vcc
0.00 =resultVccFloat
5.1380000114 =readVcc

I tried to follow the same approach as discussed at the beginning of his thread as well as the link above but I'm missing something. Can anyone help me with the below questions:

  1. Why isn't the voltage read in the loop section?
  2. Is the voltage reading being calculated or read during every loop cycle?
  3. Why is the output of the voltage in volts and not millivolts?
  4. I'd like to get the "5.138" volt value into the array. Does someone know how to do this?

Thanks in advance for the help.

I'd like to implement is add code to transmit and display battery voltage at the end of the weather data array. This would allow for monitoring of the battery

I thought the FIO was a 3.3v system. Vcc should be 3.3 after the regulator. I'm confused by the 5 volt value you are seeing. What voltage is the battery. How is it connected to the system. I would think that Vcc after the regulator would not really be monitoring the battery voltage.

  1. Why isn't the voltage read in the loop section?
  2. Is the voltage reading being calculated or read during every loop cycle?

The only place you call the function readVcc() is here

Serial.print( readVcc(), DEC );
Serial.println (" =readVcc");

The voltage reading is performed by the function only when you call it.

  1. Why is the output of the voltage in volts and not millivolts?

You have changed the function from the posted reference. The original function returned a long mv value.
You are returning a float which you have explicitly converted to volts

resultVccFloat = (float) resultVcc /1000; //Convert to float in Volts
 return resultVccFloat;
  1. I'd like to get the "5.138" volt value into the array. Does someone know how to do this?

There are other problems with the code which need to be sorted out. You have both global and local variables with the same name and you are running into issues of scope(What is valid where?) My advice is to get rid of the globals, use the return value as a float, and if you want to see intermediate values for debug, print out from within the function.

//eliminate these global declarations
//int resultVcc;
//float resultVccFloat;

replace with a new global

float measuredVcc
// See https://code.google.com/archive/p/tinkerit/wikis/SecretVoltmeter.wiki
// This will measure the voltage of the battery connected to the Arduino 
float readVcc(){
  //signed long resultVcc;
  long resultVcc;
  float resultVccFloat;
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(10);             // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);   // convert
  while (bit_is_set(ADCSRA,ADSC));      // Measuring
  resultVcc = ADCL;
  resultVcc |= ADCH<<8;
  resultVcc = 1125300L / resultVcc; // Back-calculate AVcc in mV
  //Serial.println(resultVcc); //mv
  resultVccFloat = (float) resultVcc /1000; //Convert to float in Volts
  return resultVccFloat;
}

deal with this voltage function in similar fashion to what you do with the DHT functions.

measuredVcc = readVcc();
dtostrf(measuredVcc,4,3,Vcc);

Thanks again for you help cattledog! You always seem to have great answers and suggestions!

Regarding your question:

I thought the FIO was a 3.3v system. Vcc should be 3.3 after the regulator. I'm confused by the 5 volt value you are seeing. What voltage is the battery. How is it connected to the system. I would think that Vcc after the regulator would not really be monitoring the battery voltage.

The plan is to buy a 3.3 volt battery. Either one of the following:

I guess those are 3.7 volt batteries so sorry if I misspoke in previous posts. I probably hadn't looked to deeply into the batteries at that point. I think the above batteries should suite my needs once getting the fio to sleep for 1 min, wake to send the data, then put the Fio back to sleep.

I think the reason it's returning a 5V reading is because while working on the code, I have it plugged into the USB through FTDI pins. It seems like the normal voltage from USB is 5V. I hope when I no longer use the FTDI, and put this device out in the field, it should read and return the the voltage of the power source, the battery. Is that a correct assumption?

Thank you for the code suggestions. It's working much better, however the values in the array are not the same as the values that are written from the "mmSerial.print( readVcc(), DEC ); " function. Why is that? In the Serial Monitor, I get the following results as an example. As you can see, sometimes "readVcc" and "measuredVcc" match but not always.

<18,64,19,66,19,66,84.00,15,101412.00,760.59,29.95,5.161>
5.1380000114 =readVcc
<18,64,19,66,19,66,83.60,15,101425.00,760.69,29.95,5.161>
5.1380000114 =readVcc
<18,64,19,66,19,66,83.40,15,101421.00,760.66,29.95,5.138>
5.1380000114 =readVcc
<18,64,19,66,19,66,83.20,15,101419.00,760.64,29.95,5.161>
5.1380000114 =readVcc
<18,64,19,66,19,66,83.10,15,101416.00,760.62,29.95,5.161>
5.1609997749 =readVcc
<18,64,19,66,19,66,83.30,15,101424.00,760.68,29.95,5.138>
5.1380000114 =readVcc
<18,64,19,66,19,66,83.30,15,101420.00,760.65,29.95,5.138>
5.1380000114 =readVcc
<18,64,19,66,19,66,83.40,15,101421.00,760.66,29.95,5.161>
5.1380000114 =readVcc
<18,64,19,66,19,66,83.20,15,101415.00,760.61,29.95,5.138>
5.1609997749 =readVcc

New code from cattledog's suggestions are below:

#include <LowPower.h>
#define xbeePowerPin 9 // Arduino pin used to sleep the XBee

#include "DHT.h"
#define DHTPIN 7     // what digital pin we're connected to

#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321

DHT dht(DHTPIN, DHTTYPE);

#include <Wire.h>
#include <Adafruit_BMP085.h>
#include "LowPower.h"

unsigned long XBEETurnOn;
unsigned long XBEETurnOff;
float measuredVcc;

float readVcc(){
  signed long resultVcc;
  float resultVccFloat; // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(10);             // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);   // convert
  while (bit_is_set(ADCSRA,ADSC));      // Measuring
  resultVcc = ADCL;
  resultVcc |= ADCH<<8;
  resultVcc = 1125300L / resultVcc; // Back-calculate AVcc in mV
  //Serial.println(resultVcc); //mv
  resultVccFloat = (float) resultVcc /1000; //Convert to float in Volts
  return resultVccFloat;
}

Adafruit_BMP085 bmp;
int tempC;  // Variable for holding temp in C | oringinally float, changed to int
int tempF;  // Variable for holding temp in F | oringinally float, changed to int
float pressure; //Variable for holding pressure reading in Pa | oringinally float, changed to int
float pressuremmHg; // Variable for holding pressure reading in mmHg | oringinally float, changed to int
float pressureinHg; // Variable for holding pressure reading in inHg| oringinally float, changed to int
int dewpoint; // Variable for holding pressure reading in *C| oringinally float, changed to int

#include <SoftwareSerial.h>
SoftwareSerial XBee(2, 3); // RX, TX

  char dataPacket [70]; //an array with 12 integer elements, but 70 total characters. How is this 70 characters?
  int i; //used as an index into the array
  char P1[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char P2[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char P3[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char H1[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char Vcc[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats

void setup() {
  XBee.begin(9600);
  Serial.begin(9600);
  dht.begin();
  bmp.begin();
    }
    
void loop() {
    // Fio Enter power down state for number of cycles indicated below with ADC and BOD module disabled
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec 
    //Need to add more for 1 min sleep once code is correct and working

    measuredVcc = readVcc();
    
//DHT Values Below:
    // Reading temperature or humidity takes about 250 milliseconds! | originally float, changed to int
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.readHumidity(); 
    // Read temperature as Celsius (the default) | originally float, changed to int
    int t = dht.readTemperature();
    // Read temperature as Fahrenheit (isFahrenheit = true) | originally float, changed to int
    int f = dht.readTemperature(true);
    // Compute heat index in Fahrenheit (the default) | originally float, changed to int
    int hif = dht.computeHeatIndex(f, h);
    // Compute heat index in Celsius (isFahreheit = false)| originally float, changed to int
    int hic = dht.computeHeatIndex(t, h, false);
    int DP = (t - ((100 - h) / 5)); // Calculates the Dew Point in *C if Humidity >50% (DHT) dd.dd
    
//DHT Values Below:
    tempC = bmp.readTemperature();        //  Read Temperature (bmp)
    tempF = tempC * 1.8 + 32.;            // Convert degrees C to F (bmp)
    pressure = bmp.readPressure();        // Read Pressure (bmp)
    pressuremmHg = (pressure * .0075);    //(bmp) 
    pressureinHg = (pressure / 3386.39);  //(bmp)

    dtostrf(pressure,9,2,P1);     // due to issues showing pressue in the "sprintf" function because it's a float value. 9 digits, 0 significant digits
    dtostrf(pressuremmHg,6,2,P2); // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(pressureinHg,5,2,P3); // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(h,5,2,H1);            // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(measuredVcc,4,3,Vcc);            
  
    {
      pinMode(xbeePowerPin, OUTPUT);
      XBEETurnOn = millis();
      digitalWrite(xbeePowerPin, LOW);
      delay (1000);
      // sprintf(dataPacket, "X%d" ,gx) - this is used to compile multiple datastrings togeter
      // d=integer, f=float ,s=sting
      sprintf(dataPacket,"<%d,%d,%d,%d,%d,%d,%s,%d,%s,%s,%s,%s>",tempC,tempF,t,f,hic,hif,H1,DP,P1,P2,P3,Vcc);
      Serial.println(dataPacket);
      XBee.print(dataPacket);
      // all together the output to the serial monitor is going to be the following
      //tt(2),ff(2),II(2),ii(2),hh(2),dd(2),HH.HH(5),DD.DD(5),PPPPPP.PP(6),mmm.mm(6),NN.NN(5),V.VVV(4) 59 characters total
      pinMode(xbeePowerPin, INPUT_PULLUP);
      digitalWrite(xbeePowerPin, HIGH);
      XBEETurnOff = millis();
      // Serial.println (XBEETurnOff-XBEETurnOn);
      Serial.print( readVcc(), DEC );
      Serial.println (" =readVcc");
      delay(1000);
    }
}

Thanks so much for the help! I really appreciate it!

however the values in the array are not the same as the values that are written from the "mmSerial.print( readVcc(), DEC )

They are taken from two different calls to the function. The values may not be the same. The variation appears to be less than 1% of the reading.

I hope when I no longer use the FTDI, and put this device out in the field, it should read and return the the voltage of the power source, the battery. Is that a correct assumption?

I'm not familiar with the relationship of Vcc to the battery voltage and the voltage regulator. Certainly if the battery starts at 3.7 and the system voltage drops below 3.3v you will know that the battery voltage is low.

I have very little knowledge of these battery voltage issues. Sparkfun might be a good source for information about how to measure the battery voltage applied to the FIO.

What are your plans for field installation and recharge of the battery?

Thanks for all your help cattledog. I really appreciate it.

I'm planning to use a solar panel to charge the battery. From what I understand the Fio should be able to use the Solar to charge the battery. I'm pretty sure it was designed for applications like this. So my next step are:

  1. Look into matching Solar to the Battery and Fio. (I've started this forum regarding Battery and Solar needs Off Grid Weather Station: Usage Determination to size Battery/Solar Combo - Project Guidance - Arduino Forum
  2. Put the weather station Tx on a vector board.
  3. 3D print or find a small weather station box and place it somewhere in the back yard.
  4. Create a little housing for the Rx so it looks a little cleaner in home.

Its been a fun project with lots of learning. I'm really thankful for all the feedback and suggestions all have provided.

Last code question. I was trying to determine the amount of time the Fio is on during the loop cycle. I did this for the Xbee to help determine energy requirements and it worked great. When tying to do this for the Fio though I get weird results, even though I feel like my process was the same for the Fio as it was for the Xbee. I feel like there is something I'm missed when using the millis() function.

Defined Global Variables:

unsigned long XBEETurnOn;
unsigned long XBEETurnOff;
unsigned long FioOn;
unsigned long FioOff;
float measuredVcc;

Code in the Loop Section:

void loop() {
    // Fio Enter power down state for number of cycles indicated below with ADC and BOD module disabled
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec 
    //Need to add more for 1 min sleep once code is correct and working
    
    FioOn = millis();
    measuredVcc = readVcc();
    
//DHT Values Below:
    // Reading temperature or humidity takes about 250 milliseconds! | oringinally float, changed to int
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.readHumidity(); 
    // Read temperature as Celsius (the default) | oringinally float, changed to int
    int t = dht.readTemperature();
    // Read temperature as Fahrenheit (isFahrenheit = true) | oringinally float, changed to int
    int f = dht.readTemperature(true);
    // Compute heat index in Fahrenheit (the default) | oringinally float, changed to int
    int hif = dht.computeHeatIndex(f, h);
    // Compute heat index in Celsius (isFahreheit = false)| oringinally float, changed to int
    int hic = dht.computeHeatIndex(t, h, false);
    int DP = (t - ((100 - h) / 5)); // Calculates the Dew Point in *C if Humidity >50% (DHT) dd.dd
    
//DHT Values Below:
    tempC = bmp.readTemperature();        //  Read Temperature (bmp)
    tempF = tempC * 1.8 + 32.;            // Convert degrees C to F (bmp)
    pressure = bmp.readPressure();        // Read Pressure (bmp)
    pressuremmHg = (pressure * .0075);    //(bmp) 
    pressureinHg = (pressure / 3386.39);  //(bmp)

    dtostrf(pressure,9,2,P1);     // due to issues showing pressue in the "sprintf" function because it's a float value. 9 digits, 0 significant digits
    dtostrf(pressuremmHg,6,2,P2); // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(pressureinHg,5,2,P3); // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(h,5,2,H1);            // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(measuredVcc,4,3,Vcc);            
  
    {
      XBEETurnOn = millis();
      pinMode(xbeePowerPin, OUTPUT);
      digitalWrite(xbeePowerPin, LOW);  //signal Xbee on
      delay (1000);
      // sprintf(dataPacket, "X%d" ,gx) - this is used to compile multiple datastrings togeter
      // d=integer, f=float ,s=sting
      sprintf(dataPacket,"<%d,%d,%d,%d,%d,%d,%s,%d,%s,%s,%s,%s>",tempC,tempF,t,f,hic,hif,H1,DP,P1,P2,P3,Vcc);
      Serial.println(dataPacket);
      XBee.print(dataPacket);
      // all together the output to the serial monitor is going to be the following
      //tt(2),ff(2),II(2),ii(2),hh(2),dd(2),HH.HH(5),DD.DD(5),PPPPPP.PP(6),mmm.mm(6),NN.NN(5),V.VVV(4) 59 characters total
      pinMode(xbeePowerPin, INPUT_PULLUP);
      digitalWrite(xbeePowerPin, HIGH);
      XBEETurnOff = millis();
      Serial.print ("XbeeTimeOn: ");
      Serial.println (XBEETurnOff-XBEETurnOn);
      Serial.print( readVcc(), DEC );
      Serial.println (" =readVcc");
      delay(1000);
    }
    
    FioOff = millis();
    Serial.print ("FioTimeOn: ");
    Serial.println (FioOff - FioOn);
    Serial.println (FioOn);
    Serial.println (FioOff);
}

I'm getting the following output in the Serial Monitor:

<17,62,17,63,16,62,79.40,12,101343.00,760.07,29.93,5.115>
XbeeTimeOn: 1063
5.1149997711 =readVcc
F⸮Z⸮U⸮⸮⸮⸮Kj⸮$⸮⸮⸮j⸮⸮j⸮$⸮⸮⸮j⸮⸮<17,62,17,63,16,62,79.50,12,101341.00,760.06,29.93,5.115>
XbeeTimeOn: 1063
5.1149997711 =readVcc
F

I feel like this is really odd because it was the same code for Fio as was the Xbee. Xbee works great but Fio does not. Do the brackets impact this? Any suggestions are much appreciated!

It's often said that Arduino's are fast, and serial output is slow. You are running into that situation.

Serial printing from the Arduino to the monitor is buffered, and loop() is taking the the code back to the power down sleep before the buffer is emptied

Because the added print statements were at the very end of loop() and the sleep was at the very start, you are encountering the situation where the Arduino is going back to sleep before the outgoing Serial buffer is cleared and strange things are happening.

The answer is to use the Serial.flush() function, which waits for the transmission of outgoing serial data to complete. It pauses the program while the transmit buffer is flushed. Then when loop() puts the Arduino back to sleep, the printing has been completed.

FioOff = millis();
  Serial.print("FioTimeOn: ");
  Serial.println(FioOff - FioOn);
  Serial.println(FioOn);
  Serial.println(FioOff);
  Serial.flush();

Do the brackets impact this?

You do have some unnecessary brackets, but they are not the cause of what you are seeing.

Brilliant! Thank you! Works great and I have a way better understanding how long the Fio is on. 2.402 seconds to be exact! :slight_smile: So great we can actually measure that. Helps a lot to determine the full energy needs for the operating time. Thanks again!

I'll try to do some more learning about brackets to stay more organized in the future. Its been one of the things which haven't clicked yet for me. Thanks again.

Posting final code below for those interested.

#include <LowPower.h>
#define xbeePowerPin 9 // Arduino pin used to sleep the XBee

#include "DHT.h"
#define DHTPIN 7     // what digital pin we're connected to

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor

// Initialize DHT sensor.
// Note that older versions of this library took an optional third parameter to
// tweak the timings for faster processors.  This parameter is no longer needed
// as the current DHT reading algorithm adjusts itself to work on faster procs.
DHT dht(DHTPIN, DHTTYPE);

#include <Wire.h>
#include <Adafruit_BMP085.h>
#include "LowPower.h"

unsigned long XBEETurnOn;
unsigned long XBEETurnOff;
unsigned long FioOn;
unsigned long FioOff;
float measuredVcc;

// See https://code.google.com/archive/p/tinkerit/wikis/SecretVoltmeter.wiki
// This will measure the voltage of the battery connected to the Arduino 
float readVcc(){
  signed long resultVcc;
  float resultVccFloat; // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(10);             // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);   // convert
  while (bit_is_set(ADCSRA,ADSC));      // Measuring
  resultVcc = ADCL;
  resultVcc |= ADCH<<8;
  resultVcc = 1125300L / resultVcc; // Back-calculate AVcc in mV
  //Serial.println(resultVcc); //mv
  resultVccFloat = (float) resultVcc /1000; //Convert to float in Volts
  return resultVccFloat;
}

/***************************************************
  This is an example for the BMP085 Barometric Pressure & Temp Sensor

  Designed specifically to work with the Adafruit BMP085 Breakout
  ----> https://www.adafruit.com/products/391

  These displays use I2C to communicate, 2 pins are required to
  interface
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ****************************************************/

// Connect VCC of the BMP085 sensor to 3.3V (NOT 5.0V!)
// Connect GND to Ground
// Connect SCL to i2c clock - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 5
// Connect SDA to i2c data - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 4
// EOC is not used, it signifies an end of conversion
// XCLR is a reset pin, also not used here

Adafruit_BMP085 bmp;
int tempC;  // Variable for holding temp in C | oringinally float, changed to int
int tempF;  // Variable for holding temp in F | oringinally float, changed to int
float pressure; //Variable for holding pressure reading in Pa | oringinally float, changed to int
float pressuremmHg; // Variable for holding pressure reading in mmHg | oringinally float, changed to int
float pressureinHg; // Variable for holding pressure reading in inHg| oringinally float, changed to int
int dewpoint; // Variable for holding pressure reading in *C| oringinally float, changed to int

// We'll use SoftwareSerial to communicate with the XBee:
#include <SoftwareSerial.h>
// XBee's DOUT (TX) is connected to pin 2 (Arduino's Software RX)
// XBee's DIN (RX) is connected to pin 3 (Arduino's Software TX)
SoftwareSerial XBee(2, 3); // RX, TX

  char dataPacket [70]; //an array with 12 integer elements, but 70 total characters. How is this 70 characters?
  int i; //used as an index into the array
  char P1[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char P2[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char P3[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char H1[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats
  char Vcc[15]; // these are values created through the "dtostrf" function because the "sprintf" doesn't allow floats

void setup() {
  // Set up both ports at 9600 baud. This value is most important
  // for the XBee. Make sure the baud rate matches the config
  // setting of your XBee.
  XBee.begin(9600);
  Serial.begin(9600);
  dht.begin();
  bmp.begin();
    }
    
void loop() {
    // Fio Enter power down state for number of cycles indicated below with ADC and BOD module disabled
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); //8 sec
    LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); //4 sec
    
    FioOn = millis();
    measuredVcc = readVcc();
    
//DHT Values Below:
    // Reading temperature or humidity takes about 250 milliseconds! | oringinally float, changed to int
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.readHumidity(); 
    // Read temperature as Celsius (the default) | oringinally float, changed to int
    int t = dht.readTemperature();
    // Read temperature as Fahrenheit (isFahrenheit = true) | oringinally float, changed to int
    int f = dht.readTemperature(true);
    // Compute heat index in Fahrenheit (the default) | oringinally float, changed to int
    int hif = dht.computeHeatIndex(f, h);
    // Compute heat index in Celsius (isFahreheit = false)| oringinally float, changed to int
    int hic = dht.computeHeatIndex(t, h, false);
    int DP = (t - ((100 - h) / 5)); // Calculates the Dew Point in *C if Humidity >50% (DHT) dd.dd
    
//DHT Values Below:
    tempC = bmp.readTemperature();        //  Read Temperature (bmp)
    tempF = tempC * 1.8 + 32.;            // Convert degrees C to F (bmp)
    pressure = bmp.readPressure();        // Read Pressure (bmp)
    pressuremmHg = (pressure * .0075);    //(bmp) 
    pressureinHg = (pressure / 3386.39);  //(bmp)

    dtostrf(pressure,9,2,P1);     // due to issues showing pressue in the "sprintf" function because it's a float value. 9 digits, 0 significant digits
    dtostrf(pressuremmHg,6,2,P2); // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(pressureinHg,5,2,P3); // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(h,5,2,H1);            // due to issues showing pressue in the "sprintf" function because it's a float value.
    dtostrf(measuredVcc,4,3,Vcc);            
  
    {
      XBEETurnOn = millis();
      pinMode(xbeePowerPin, OUTPUT);
      digitalWrite(xbeePowerPin, LOW);  //signal Xbee on
      delay (1000);
      // sprintf(dataPacket, "X%d" ,gx) - this is used to compile multiple datastrings togeter
      // d=integer, f=float ,s=sting
      sprintf(dataPacket,"<%d,%d,%d,%d,%d,%d,%s,%d,%s,%s,%s,%s>",tempC,tempF,t,f,hic,hif,H1,DP,P1,P2,P3,Vcc);
      Serial.println(dataPacket);
      XBee.print(dataPacket);
      // all together the output to the serial monitor is going to be the following
      //tt(2),ff(2),II(2),ii(2),hh(2),dd(2),HH.HH(5),DD.DD(5),PPPPPP.PP(6),mmm.mm(6),NN.NN(5),V.VVV(4) 59 characters total
      pinMode(xbeePowerPin, INPUT_PULLUP);
      digitalWrite(xbeePowerPin, HIGH);
      XBEETurnOff = millis();
      Serial.print ("XbeeTimeOn: ");
      Serial.println (XBEETurnOff-XBEETurnOn);
      Serial.print( readVcc(), DEC );
      Serial.println (" =readVcc");
      delay(1000);
    }
    
    FioOff = millis();
    Serial.print ("FioTimeOn: ");
    Serial.println (FioOff - FioOn);
    Serial.flush();
}

hey! Please, can you share the latest running code?