Xbee remote sensor to XBee/SD card

Dear Programmer,

This is in response to the suggestion from my “Starting Over” forum subject. Been trying to send this for answers after learning how to navigate the forum:
Dear Forum,
You may be glad to know that my “AVRdude error” disappeared, once I got it into my thick head that you don’t compile and upload with the Xbee attached and my thanks to Sparkfun’s hammering that warning in its tutorials. To my great satisfaction and your relief, I wrote a movement sensor program that sends the data perfectly from the Xbee shield/Uno to the Xbee/explorer/computer/X-CTU terminal. I use “do” loops to print “1” or “0” every six seconds to mySerial, depending on the PIR state being high or low, respectively. Now, I can risk self-inflicted humiliation with two questions:
First and less important is that I’m not sure how the program works, because the “do” list for “mySerial.print” is followed by “Serial.read()” and “while Serial.available()==0). Alternatively, the “do” list for “Serial.print) is followed by “myserial.read()” and “while myserial.available()==0). Are these opposite commands placed to set up the next “do” loop? I’d have the same question if I just used “while” instead of a “do” loop. But it works.
Second question: My plan is to send the PIR sensor data from a “remote” Xbee to a datalogger SD card on a “base”,instead of the explorer/computer. I’ve configured the Xbees with puTTY on the explorer and I would like a simple arrangement for the “remote” like a lily pad Xbee reading the sensor. I don’t know if the lily pad is possible, since the datalogger would be stacked on the Xbee shield/UNO “base” that contains the program. Also, my working Ladyada datalogger wants “dataFile.print” and the base would look for “mySerial.print”. Is there a problem, or does it need some conversion from mySerial to dataFile?
Once again, thanks for your help.
Here’s my stab at a sketch for “mySerial.print” to “dataFile.print”. See the “//” questions.

/*
 * Xbee wireless PIR sensor to datalogger with Arduino 1.0.1
 */
 #include <SD.h>
 #include <SoftwareSerial.h>
 SoftwareSerial mySerial = SoftwareSerial(0,1);
 const int chipSelect = 10;
unsigned long time; 
const int ledPin = 8;                // choose the pin for the LED
const int inputPin = 7;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
 
void setup() 
{
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input
 
  Serial.begin(9600);
  Serial.print("Initializing SD Card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  } 
 Serial.println("card initialized");
 Serial.print("Goodnight Moon");
 mySerial.begin(9600);
 mySerial.println("Hello World");
 
}
void loop()
{                                                          
  delay(6000);
  float time = millis();
  
  pirState = digitalRead(inputPin);  // read input value
  
    if (pirState == HIGH) {         // check if the input is HIGH
    digitalWrite(ledPin, HIGH);    //LED on
    delay (500);
    digitalWrite(ledPin, LOW);     //LED OFF
      
   File dataFile = SD.open("datalog.txt", FILE_WRITE);
    do {
     Serial.print(" ");     // this is just to check on the serial monitor
      Serial.print(time/60000); 
      Serial.print(",  "); 
      Serial.println("1");
      mySerial.read();
    }
    while (mySerial.available()==0);
    
    do {    
     
     mySerial.print (" ");
     mySerial.print(time/60000);
     mySerial.print(", ");
     mySerial.println("1");
     Serial.read();
    }
     while (Serial.available()==0); 
    
    do {
    dataFile.print("");
    dataFile.print(time/6000);
    dataFile.print (", ");
    dataFile.println('1');
    mySerial.read();            //would this work?
    }
    while (mySerial.available()==0); 
    
    }                             //Closes "if" statement
    
 else {
    do {                         //omitted Serial.pring list
      mySerial.print(" ");
      mySerial.print(time/60000);
      mySerial.print(" ");
      mySerial.println("0");
      Serial.read();
    }     
      
      while (Serial.available()==0);
   
    do {
      dataFile.print(" ");
      dataFile.print(time/60000);
      dataFile.print(" ");
      dataFile.println("0");
      mySerial.read();          //again, will this work?
 }
     while mySerial.available()==0);   //would this work?
        
    }                          //Closes "else" statwement           
     if (pirState == HIGH) { 
      // We only want to print on the output change, not state
      pirState = LOW;
     } 
  
                                //Closes loop
}

I get an error “dataFile not declared in this scope” but this is not my question–see // above

Cheers and thanks,
Oldguy

------------------------end

That’s it.

I noticed something strange…

You shouldn’t open the file every time you use it.

You should open the file on setup and write to it in the loop() function. Once the size of the file gets big or some other condition, close it, and create or open a new one. This is nt the problem you’re having, but it may lead to problems once you test it.

Try declaring the File variable as global…

/*
 * Xbee wireless PIR sensor to datalogger with Arduino 1.0.1
 */
 #include <SD.h>
 #include <SoftwareSerial.h>
 SoftwareSerial mySerial = SoftwareSerial(0,1);
 const int chipSelect = 10;
unsigned long time; 
const int ledPin = 8;                // choose the pin for the LED
const int inputPin = 7;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
 
File dataFile;<--------------------------------CHANGED

void setup() 
{
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input
 
  Serial.begin(9600);
  Serial.print("Initializing SD Card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  } 
 Serial.println("card initialized");
 Serial.print("Goodnight Moon");
 mySerial.begin(9600);
 mySerial.println("Hello World");
 
}
void loop()
{                                                          
  delay(6000);
  float time = millis();
  
  pirState = digitalRead(inputPin);  // read input value
  
    if (pirState == HIGH) {         // check if the input is HIGH
    digitalWrite(ledPin, HIGH);    //LED on
    delay (500);
    digitalWrite(ledPin, LOW);     //LED OFF
      
   dataFile = SD.open("datalog.txt", FILE_WRITE); <------------- CHANGED
    do {
     Serial.print(" ");     // this is just to check on the serial monitor
      Serial.print(time/60000); 
      Serial.print(",  "); 
      Serial.println("1");
      mySerial.read();
    }
    while (mySerial.available()==0);
    
    do {    
     
     mySerial.print (" ");
     mySerial.print(time/60000);
     mySerial.print(", ");
     mySerial.println("1");
     Serial.read();
    }
     while (Serial.available()==0); 
    
    do {
    dataFile.print("");
    dataFile.print(time/6000);
    dataFile.print (", ");
    dataFile.println('1');
    mySerial.read();            //would this work?
    }
    while (mySerial.available()==0); 
    
    }                             //Closes "if" statement
    
 else {
    do {                         //omitted Serial.pring list
      mySerial.print(" ");
      mySerial.print(time/60000);
      mySerial.print(" ");
      mySerial.println("0");
      Serial.read();
    }     
      
      while (Serial.available()==0);
   
    do {
      dataFile.print(" ");
      dataFile.print(time/60000);
      dataFile.print(" ");
      dataFile.println("0");
      mySerial.read();          //again, will this work?
 }
     while mySerial.available()==0);   //would this work?
        
    }                          //Closes "else" statwement           
     if (pirState == HIGH) { 
      // We only want to print on the output change, not state
      pirState = LOW;
     } 
  
                                //Closes loop
}

Dear Bubulindo,
Good point and thanks for catching it. The code below was changed, opening the file in setup(). I assume it’s now global and that something is not needed in loop() to point to it. :slight_smile:

/*
 * Xbee wireless PIR sensor to datalogger with Arduino 1.0.1
 */
 #include <SD.h>
 #include <SoftwareSerial.h>
 SoftwareSerial mySerial = SoftwareSerial(0,1);
 const int chipSelect = 10;
unsigned long time;        //Note--time will be cchanged to RTC
const int ledPin = 8;                // choose the pin for the LED
const int inputPin = 7;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
 
void setup() 
{
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input
 
  Serial.begin(9600);
  Serial.print("Initializing SD Card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  } 
 Serial.println("card initialized");
 Serial.print("Goodnight Moon");
 mySerial.begin(9600);
 mySerial.println("Hello World");
  File dataFile = SD.open("datalog.txt", FILE_WRITE);//This 
   //was be moved to setup to prevent opening every cycle
 
}
void loop()
{
  delay(6000);
  float time = millis();
  
  pirState = digitalRead(inputPin);  // read input value
  
    if (pirState == HIGH) {         // check if the input is HIGH
    digitalWrite(ledPin, HIGH);    //LED on
    delay (500);
    digitalWrite(ledPin, LOW);     //LED OFF
      
  
    do {
     Serial.print(" ");     // this is just to check on the serial monitor
      Serial.print(time/60000); 
      Serial.print(",  "); 
      Serial.println("1");
      mySerial.read();
    }
    while (mySerial.available()==0);
    
    do {    
     
     mySerial.print (" ");
     mySerial.print(time/60000);
     mySerial.print(", ");
     mySerial.println("1");
     Serial.read();
    }
     while (Serial.available()==0); 
    
    do {
    dataFile.print("");
    dataFile.print(time/6000);
    dataFile.print (", ");
    dataFile.println('1');
    mySerial.read();            //would this work?
    }
    while (mySerial.available()==0); 
    
    }                             //Closes "if" statement
    
 else {
    do {                         //omitted Serial.pring list
      mySerial.print(" ");
      mySerial.print(time/60000);
      mySerial.print(" ");
      mySerial.println("0");
      Serial.read();
    }     
      
      while (Serial.available()==0);
   
    do {
      dataFile.print(" ");
      dataFile.print(time/60000);
      dataFile.print(" ");
      dataFile.println("0");
      mySerial.read();          //again, will this work?
 }
     while mySerial.available()==0);   //would this work?
        
    }                          //Closes "else" statwement           
     if (pirState == HIGH) { 
      // We only want to print on the output change, not state
      pirState = LOW;
     } 
  
                                //Closes loop
}

I assume it's now global and that something is not needed in loop() to point to it.

No, it is not. You need to learn about scope as it applies to variables. The dataFile variable is local to setup() and will not be visible to loop(). Of course, the compiler would have told you that.

  float time = millis();

What? Can't you be bothered to read the documentation? The millis() function does not return a float.

    do {
     Serial.print(" ");     // this is just to check on the serial monitor
      Serial.print(time/60000); 
      Serial.print(",  "); 
      Serial.println("1");
      mySerial.read();
    }
    while (mySerial.available()==0);

Why is this in a do/while loop? Why are you reading from the serial port when you KNOW there is nothing to read? That is what the Serial.available() function is telling you.

    do {    
     
     mySerial.print (" ");
     mySerial.print(time/60000);
     mySerial.print(", ");
     mySerial.println("1");
     Serial.read();
    }
     while (Serial.available()==0);

More incorrect code.

    do {
    dataFile.print("");
    dataFile.print(time/6000);
    dataFile.print (", ");
    dataFile.println('1');
    mySerial.read();            //would this work?
    }
    while (mySerial.available()==0);

And more.

    do {                         //omitted Serial.pring list
      mySerial.print(" ");
      mySerial.print(time/60000);
      mySerial.print(" ");
      mySerial.println("0");
      Serial.read();
    }     
      
      while (Serial.available()==0);
   
    do {
      dataFile.print(" ");
      dataFile.print(time/60000);
      dataFile.print(" ");
      dataFile.println("0");
      mySerial.read();          //again, will this work?
 }
     while mySerial.available()==0);   //would this work?

And even more.

  do {
     Serial.print(" ");     // this is just to check on the serial monitor
      Serial.print(time/60000); 
      Serial.print(",  "); 
      Serial.println("1");
      mySerial.read();
    }
    while (mySerial.available()==0);

The test is done at the end. Too late. Rework to do the test at the start. eg.

while  (mySerial.available() > 0) 
   {
    mySerial.read();
    Serial.print(" ");     // this is just to check on the serial monitor
    Serial.print(time/60000); 
    Serial.print(",  "); 
    Serial.println("1");
    }

Plus it eludes me why you read serial and discard it.

Or do you want to read when nothing is available? Why?

Hello PaulS and Nick,

Ouch: I needed that; a lesson to remember! You’re quite right; it’s silly to use “float” for something that returns integers. Yet, on re-visiting the documentation on millis(), it’s plain that they wouldn’t think the reader so dumb that they would have to point this out, so they didn’t. Thanks for the reminder. I learned about “scope” via chimpanzee innumerable trial and errors, rather than Google>scope>Arduino; so easy! Having written lots of code in BASIC eons ago, I wasn’t prepared for global variables. C++ is so---- mysterious.
Why would I use a “do-while” loop? That’s what I was asking you in excessive prose. Of course, “while” alone seemed preferable, as I mentioned, but I didn’t understand why I had to see if serial was available in order to print mySerial. Thanks to your aphorisms, now I see my mistake in the Adafruit tutorial on Xbees (below). In my carelessness beyond measure, I didn’t see that asking if mySerial available is to return mySerial.read. Thanks for keeping my nose to the grindstone.

Rather than float questions to exercise your patience again, I’ll hit the books hard before asking. Also, the question about “remotes” and “bases” was not for “programming questions” and I now see some websites that sketchily deal with this issue, written by very young doers, not teachers.

Cheers, and thanks for your help to so many throughout the incessant barrage of questions I see in programming.

Oldguy :slight_smile:

void loop() 
                    // run over and over again {    
if (mySerial.available()) {       
Serial.print((char)mySerial.read());   } 
  
if (Serial.available()) { mySerial.print((char)Serial.read());   }   delay(100); }

Very good. Now practice some formatting. You’ll thank me later.

void loop () 
  {
  if (mySerial.available ()) 
    {       
    Serial.print ((char) mySerial.read ());   
    } // end if

  if (Serial.available ()) 
    { 
    mySerial.print((char) Serial.read ());   
    }  // end if
 } // end of loop

I don't see what the delay achieves unless you want the program to be unresponsive.

Forum FYI Subject: Float millis() I was asked by a forum advisor 1) why would anyone ever use “float” as a data type? and 2) “What? Can't you be bothered to read the documentation? The millis() function does not return a float”. On re-visiting the site, one does get the impression that millis() returns an number that wouldn’t be used as “float”. Apparently, that’s mistaken. I don’t remember why I used “float” as a data type for millis(), but I wouldn’t have done it on my own. It was part of a sketch I copied from somewhere and I missed it. Millis was already declared as “unsigned long” so I deleted “float time = millis();. The problem was: Serial.print (time/60000); displayed nothing but “0,” on the Serial monitor. Weeks after trying to find out why, I tried float time = millis() again within void loop(), as before, declaring “unsigned long time = milis(); as the global variable . The Serial.print command now gave 0.05, 0.10 minutes and so on. I wasted a lot of time, believing the programming expert and afraid to ask any more questions from this forum. :0

Well, it does return an unsigned long.

You have hit a totally different problem. For example:

int foo = 10 / 20;

In this case foo will be zero (same as in your example of time/60000, unless time is >= 60000).

This example sketch does exactly what I would expect:

void setup ()
  {
  Serial.begin (115200);  
  }  // end of setup
  
void loop ()
  {
  unsigned long time = millis ();
  
  Serial.println (time / 60000);
  
  delay (1000);
  }  // end of loop

It displays zeroes for a minute then 1 for the next minute and so on. As instructed.

0
0
0
0
0
0
0
0   <--- 60 lots of zero
1
1
1
1
1
1
1

The problem was: Serial.print (time/60000); displayed nothing but “0,” on the Serial monitor

Did you let it run for a minute? That might have given a clue.

The “experts” here aren’t trying to make you look foolish or demonstrate their superior knowledge. However what said was right, you will lose precision if you try to store millis() result into a float.

In your case, for printing purposes, you could promote the time to a float like this:

void setup ()
  {
  Serial.begin (115200);  
  }  // end of setup
  
void loop ()
  {
  unsigned long time = millis ();
  
  Serial.println ((float) time / 60000);
  
  delay (1000);
  }  // end of loop

Now it prints:

0.00
0.02
0.03
0.05
0.07
0.08
0.10
0.12
0.13
0.15
0.17
0.18
0.20
0.22
0.23
0.25
0.27
0.28
0.30
0.32
0.33
0.35
0.37
0.38
0.40
0.42
0.43
0.45
0.47
0.48
0.50
0.52
0.53

On re-visiting the site, one does get the impression that millis() returns an number that wouldn’t be used as “float”.

It isn’t a float. However you can turn it into a float if your application requires it.

A float has 24 bits of precision (mantissa). An unsigned long has 32. After a while your float will discard precision. This may or may not matter to you.

… afraid to ask any more questions from this forum

You will learn by asking questions. We will try to help you. Had you posted your problem code before “Weeks after trying to find out why” you might have got a prompt answer.

Quoting your reply: “The “experts” here aren’t trying to make you look foolish or demonstrate their superior knowledge. However what said was right, you will lose precision if you try to store millis() result into a float.”

No, what he said was: “What? Can’t you be bothered to read the documentation? The millis() function does not return a float.” And he was “right”??

Do you see anything at all about “losing precision” in this nasty reply or any thing helpful? I greatly appreciate your comments about float and precision, but I don’t see how I’m losing precision when I now have been getting the same results you just showed me.
You’ve been very helpful and you certainly don’t need to backpedal for this guy, putting the onus on me for something so blatantly peevish.
Arduino’s blurb on “millis()” would be improved greatly by adding what you wrote for me here.

With respect,

Oldguy

I don’t see how I’m losing precision when I now have been getting the same results you just showed me.

Yes, well that won’t show up until you have used up the 24 bit of precision in the mantissa. If you stick with unsigned longs it will count up for 49.7 days and then wrap around. If you use 24 bits of mantissa in a float it will start losing precision after about 4.6 hours. The slight loss you may or may not care about (effectively the exponent will be increased to compensate).

Just as an analogy, say you could only report someone’s height in single digits (plus a multiplier). So they might be 6 feet tall, for example. Or 5 feet. But not 5.5 feet because that is two digits in the mantissa. This may or may not trouble you.

bubulindo: I noticed something strange...

You shouldn't open the file every time you use it.

That depends on your use. Closing a file updates the directory which can be real handy in cases where the program may be stopped at any time, like power outage. If you only write once a minute or more then open and close file is the smart thing to do unless you like to fix drive data.

Oldguy; you may think/feel how you will about some remarks made but that person just wants you to think carefully and pay attention to what you read, which from your posts.. you don't. If he could succeed in changing the way you approach coding then that would be by far the best help you've gotten. If you think he's rough then you haven't gotten ankle deep in the machine yet. He's a nice guy.

Yet, on re-visiting the documentation on millis(), it’s plain that they wouldn’t think the reader so dumb that they would have to point this out, so they didn’t.

Or maybe they have this idea of presenting the material as some kind of reference. It's up to the user to see the details and stick to them. It's up to students to do the homework until they understand the whole of the material. You're programming machines, not writing essays for people.