Receive Serial Data via Xbee and Print to ThingSpeak

Hello,

I would appreciate some help in writing the correct program for my Arduino Mega.

My hardware setup:

Sending Unit: Arduino Uno + Sparkfun Weather Shield and sensors + Xbee Pro S2C

Receiving Unit: Arduino Mega + W5100 Ethernet Shield + Xbee Pro S2

Sending Unit code come from Sparkfun weather shield website and is attached.

The attached program will print to my serial monitor via USB.

It also prints to my serial monitor via XBee when there is no code loaded onto the receiving unit (Mega).

Note that I wired my receiving unit so that the Xbee is connect to the TX1/RX1 pins on the Mega and was successful in getting it to print to the serial monitor using the following code:

if (Serial.available() > 0) {
    int inByte = Serial1.read();
    Serial.write(inByte);
  }
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);

I have also been successful in using the WriteVoltage example from the ThingSpeak library. So I'm connecting to the internet and able to write to ThingSpeak.

I have scoured these forums and other sources on the internet to find the correct code for the receiving unit (Mega). I finally decided to try what I can using some of the examples. I would sincerely appreciate any guidance. I'm still relative newbie to coding and trying to get an electrical engineering degree at age 39 (they say it's never too late!).

The receiving unit code is:

#include<SPI.h>
#include <Ethernet.h>
#include<Wire.h>
#include<ThingSpeak.h>

/*#define winddir 1
#define windspeedmph 2
#define humidity 3
#define tempf 4
#define rainin 5
#define dailyrainin 6
#define pressure 7*/

int DEBUG = 0;

byte mac[] = {0x90, 0xA2, 0XDA, 0x0F, 0x72, 0xFF };//for shield in the system

IPAddress ip(192, 168, 1, 13); //LOCAL IP address not used by other devices on the network

EthernetClient client;

unsigned long myChannelNumber = 225397; //ThingSpeak Channel number
const char * myWriteAPIKey = "KE8HNNPHCH8FEDAH";//ThingSpeak Write API Key

void setup() {
  Serial.begin(9600);  // connect to the serial port
  Serial1.begin(9600);
  // Turn the internet on
  Serial.print("\nInitializing...");
  if (Ethernet.begin(mac) ){
    Serial.println("Initialization complete");
    } 
  else {
    Serial.println("Something went wrong during ethernet startup!");
    }
   #ifdef ARDUINO_AVR_YUN
    Bridge.begin();
  #else   
    #if defined(ARDUINO_ARCH_ESP8266) || defined(USE_WIFI101_SHIELD) || defined(ARDUINO_SAMD_MKR1000)
      WiFi.begin(ssid, pass);
    #else
      Ethernet.begin(mac);
    #endif
  #endif

  ThingSpeak.begin(client);
  
}

void loop() {
if (Serial.available() > 0) {
    int inByte = Serial1.read();
    Serial.write(inByte);
  }
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);

  Serial.print(char(Serial1.read()));
  }
  int winddir = Serial.parseInt();
  float windspeedmph = Serial.parseFloat();
  float humidity = Serial.parseFloat();
  float tempf = Serial.parseFloat();
  float rainin = Serial.parseFloat();
  float dailyrainin = Serial.parseFloat();
  float pressure = Serial.parseFloat();

if (Serial.read() == '\n') {

  ThingSpeak.writeField(myChannelNumber, 1, winddir, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 2, windspeedmph, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 3, humidity, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 4, tempf, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 5, rainin, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 6, dailyrainin, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 7, pressure, myWriteAPIKey);
  delay(20000);
}
    
}

When I open the serial monitor I get the "Initializing...Initialization Complete" and nothing else.

Note, I don't have much error recognition built in and am open to suggestions.

Credit will be giving within the program if so desired.

Please let me know if any additional information is needed.

Many, many thanks!

_4482_Weather_Full_Test.ino (14.6 KB)

#include<SPI.h>
#include <Ethernet.h>
#include<Wire.h>
#include<ThingSpeak.h>

Haveyougotsomethingagainstspaces?

   #ifdef ARDUINO_AVR_YUN
    Bridge.begin();
  #else  
    #if defined(ARDUINO_ARCH_ESP8266) || defined(USE_WIFI101_SHIELD) || defined(ARDUINO_SAMD_MKR1000)
      WiFi.begin(ssid, pass);
    #else
      Ethernet.begin(mac);
    #endif

Don't start with trying to support every board and every means of connectivity. Ditch the pre-processor directives and the code for devices you don't have.

  if (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);

  Serial.print(char(Serial1.read()));
  }

If there is one byte to read, read them both. Why?

Why are you using byte in the name of a variable whose type is not byte? That makes as much sense as defining an int called myFloat.

#include<SPI.h>

#include <Ethernet.h>

#include<Wire.h>

#include<ThingSpeak.h>

Haveyougotsomethingagainstspaces?

No sir, nothing against spaces. Should it be as it is above or is there something else I need to add or subtract?

Ethernet.begin(mac);
    
Don't start with trying to support every board and every means of connectivity. Ditch the pre-processor directives and the code for devices you don't have.

[b]Like this?[/b]

[code]  if (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);

  Serial.print(char(Serial1.read()));
  }

If there is one byte to read, read them both. Why?

This is where I am obviously out of my league. I got part of that code from the Arduino reference page when attempting to configure the Mega to display the incoming data from the Xbee on TX1/RX1 (pins 18,19)in the serial monitor. **
I added this myself in an attempt to get the data to print to the serial monitor:
__
**__ <strong>**Serial.print(char(Serial1.read()));**</strong> __**__
**My understanding was that the Ethernet shield and Xbee shield both use pins 0,1 thus I thought that configuring it as such would help avoid errors. When I used that code by itself, the serial data displayed in my serial monitor correctly. **
**How can I get the incoming serial data to display? Is the code I wrote to parse the value from the serial data acceptable or do I need to modify it somehow? **
Why are you using byte in the name of a variable whose type is not byte? That makes as much sense as defining an int called myFloat.
Again, this was from the Arduino reference page and it worked for the sole task of printing the data from the XBee to the serial monitor
Thank you for your time and suggestions.
__
[/quote]**__

Should it be as it is above or is there something else I need to add or subtract?

There should be a space after the word include, on all the lines.

This is where I am obviously out of my league.

You are checking to see if there is data on the first port, and, if there is, reading from the first port and writing to the second. Then, without seeing if there is data on the second port, you read from the second port and write to the first port.

Really, not a good idea.

My understanding was that the Ethernet shield and Xbee shield both use pins 0,1

The ethernet shield is an SPI device. It is not a Serial device. So, it doesn't use any serial pins.

The XBee uses whatever pins YOU connected it to. You do not have to connect it to pins 0 and 1, and you should not.

If you don't have a YUN, get rid of the code that is just for the YUN. If you don't have a Wifi shield, get rid of the code that is for the WiFi shield.

How can I get the incoming serial data to display?

Incoming from what? On which port? Display where? Detail matter. A great deal.

Again, this was from the Arduino reference page

Please post a link, so we can try to get that crap fixed.

There should be a space after the word include, on all the lines.

Done, thank you.

You are checking to see if there is data on the first port, and, if there is, reading from the first port and writing to the second. Then, without seeing if there is data on the second port, you read from the second port and write to the first port.

Really, not a good idea.

Please post a link, so we can try to get that crap fixed.

The ethernet shield is an SPI device. It is not a Serial device. So, it doesn't use any serial pins.

The XBee uses whatever pins YOU connected it to. You do not have to connect it to pins 0 and 1, and you should not.

Understood.

If you don't have a YUN, get rid of the code that is just for the YUN. If you don't have a Wifi shield, get rid of the code that is for the WiFi shield.

Got it and done, thanks.

Incoming from what? On which port? Display where? Detail matter. A great deal.

At present, I have a weather station with Arduino Uno + Sparkfun weather shield and sensors + XBee Pro S2C that transmits data to Mega + Ethernet shield + XBee Pro S2C.
**I would it to come out of the ethernet port (port 80?) and to the Arduino serial monitor. **
I would like to print the serial data coming into the Mega from the Xbee on TX1/RX1 to the serial monitor and also parse the serial data (ex. int and float values for windspeedmph and tempf) in order to display on my ThingSpeak channel.

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data.

AFAIK they work with an XBEE.

...R

I would it to come out of the ethernet port (port 80?) and to the Arduino serial monitor.

The data would come out of the ethernet port because you made a GET request. Look at the ethernet client example.

Thanks to both of you. I will have a look at the linked thread and the suggested example.

First and foremost, thanks again for the advice. I hate to admit that I am still stuck on this.

After reading through the examples posted by Robin2 I think that Example 5 could work for this project. However I do have some questions regarding that example and the data I am attempting to parse and post to the internet.

Robin2 example page: Serial Input Basics - updated - Introductory Tutorials - Arduino Forum

I've attached a screen shot of the incoming serial data string.

Do I change the number of characters in the numChars variable to the maximum number of characters in my serial monitor output? The example has 32 I should end up with a max of 108.

const byte numChars = 32; //Should I place 108 instead of 32?
char receivedChars[numChars];
char tempChars[numChars];

I'm assuming that I can change the start and end markers in recvWithStartEndMarkers function:

from this:
char startMarker = '<';
char endMarker = '>';

to this:

char startMarker = '$';
char endMarker = '#';

Is the above correct?

I would like to get the value from each variable an post to ThingSpeak.

For example:

I would like to get the int value from winddir and use it as shown below.

ThingSpeak.writeField(myChannelNumber, 1, winddir, myWriteAPIKey);

I've used the above with a ThingSpeak example for posting a single voltage value and can verify that it confirm I am connecting to the internet with my original code via Ethernet Shield and posting the values to my channel.

I am using an Arduino Mega with Ethernet Shield and receiving the weather data sting via XBee connected to TX1/RX1.

I appreciate any feedback that is offered. I'd also like to mention that while I'm still stuck on this part of the project, the previous advice helped me understand the value of creating and using functions in one's programs. Many thanks for that!

Image from Reply #8 so we don't have to download it. See this Image Guide

...R

Please post your sample data as text. I cannot read that image.

I think you have the right idea with changing the size of the array and the start- and end-markers. Always make the array a few characters bigger than what you need to be absolutely certain that nothing is written outside the bounds of the array as that would corrupt other data.

What happened when you tried those changes?

The Arduino is great for learning-by-doing and often the fastest way to get an answer is just to try something.

...R

Below is one line of data from the image I attached. I tried to embed it in the post but was having trouble. Thanks for the link to the image guide.

$,winddir=-1, windspeedmph=0.0, humidity=46.4, tempf=77.6,rainin=0.00,dailyrainin=0.00,pressure=100537.00,#

I tried making the changes I mentioned to start and end markers and character count and got the following output to the serial monitor:

Message winddir = -1

Integer 0

Float 0.0

I will work on it some more. Thanks again.

zanderbz:
I tried making the changes

If you need more help be sure to post the latest version of your program so we are all looking at the exact same code.

...R

So I've made an attempt to modify the code to suit my needs. It is posted below:

// Example 5 - Receive with start- and end-markers combined with parsing

const byte numChars = 110;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

      // variables to hold the parsed data
char messageFromPC[numChars] = {0};
int winddir = 0;
float windspeedmph = 0.0;
float humidity = 0.0;
float tempf = 0.0;
float rainin = 0.0;
float dailyrainin = 0.0;
float pressure = 0.0;

boolean newData = false;

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

void setup() {
    Serial.begin(9600);
    Serial1.begin(9600);
    //Serial.println("This demo expects 3 pieces of data - text, an integer and a floating point value");
    //Serial.println("Enter data in this style <HelloWorld, 12, 24.7>  ");
    //Serial.println();
}

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

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}

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

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '

One thing that is clear very quickly is that the code I've written simply passes the values I've assigned for each variable which is either 0 or 0.0.

So obviously I need not declare a value. But what should I do in place of declaring a value?

I feel like there is something I need to do with the parseData() function but I don't quite understand what the function is doing in the first place. I would be thankful for a brief explanation.

void parseData() {      // split the data into its parts

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

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    winddir = atoi(strtokIndx);     // convert this part to an integer

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

Thanks again.;
char endMarker = '#';
char rc;

while (Serial1.available() > 0 && newData == false) {
    rc = Serial1.read();

    if (recvInProgress == true) {
        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            recvInProgress = false;
            ndx = 0;
            newData = true;
        }
    }

    else if (rc == startMarker) {
        recvInProgress = true;
    }
}

}

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

void parseData() { // split the data into its parts

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

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

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

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

}

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

void showParsedData() {
Serial.print(" Message ");
Serial.println(messageFromPC);
Serial.print(" Wind Direction = ");
Serial.println(winddir);
Serial.print(" Wind Speed = ");
Serial.println(windspeedmph);
Serial.print(" Temperature F = ");
Serial.println(humidity);
Serial.print(" Humidity = ");
Serial.println(tempf);
Serial.print(" Rain Inches = ");
Serial.println(rainin);
Serial.print(" Daily Rain Inches = ");
Serial.println(dailyrainin);
Serial.print(" Pressure = ");
Serial.println(pressure);
Serial.println();
delay(5000);
}


One thing that is clear very quickly is that the code I've written simply passes the values I've assigned for each variable which is either 0 or 0.0.

![](upload://sIGXZusyaG0VemW4igklX3BQXa7.png)

So obviously I need not declare a value. But what should I do in place of declaring a value? 

I feel like there is something I need to do with the parseData() function but I don't quite understand what the function is doing in the first place. I would be thankful for a brief explanation.

§DISCOURSE_HOISTED_CODE_1§


Thanks again.

So I went back and tweaked my original code and am happy to say that I'm printing my data to the serial monitor and posting to ThingSpeak.

//PVAMU ELEG 4482 Weather Station, Alexander Bowen 

#include <SPI.h>
#include <Ethernet.h>
#include <Wire.h>
#include <ThingSpeak.h>

/*#define winddir 1
#define windspeedmph 2
#define humidity 3
#define tempf 4
#define rainin 5
#define dailyrainin 6
#define pressure 7*/

int DEBUG = 0;

byte mac[] = {0x90, 0xA2, 0XDA, 0x0F, 0x72, 0xFF };//for shield in the system

IPAddress ip(192, 168, 1, 14); //LOCAL IP address not used by other devices on the network

EthernetClient client;

unsigned long myChannelNumber = 225397; //ThingSpeak Channel number
const char * myWriteAPIKey = "KE8HNNPHCH8FEDAH";//ThingSpeak Write API Key

void setup() {
  Serial.begin(9600);  // connect to the serial port
  Serial1.begin(9600);
  // Turn the internet on
  Serial.print("\nInitializing...");
  if (Ethernet.begin(mac) ){
    Serial.println("Initialization complete");
    } 
  else {
    Serial.println("Something went wrong during ethernet startup!");
    }
  Serial.println();  
  ThingSpeak.begin(client);

}

void loop() {
 /* if (Serial.available() > 0) {
    int inByte = Serial1.read();
    Serial.write(inByte);
  }
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);  
  }*/

   
   
  if (Serial1.read() == '

However, I have one remaining issue that needs addressing. I need to get the program to recognize the $ as the start marker, the # as the end marker and perhaps the commas as separators. My data is parsing out of order about every 3rd time.

Please note I mean to disrespect to Robin2 who has been trying to help me by using the serial data examples. I've been working on understanding both and this one just (almost) worked.

Thanks! ;D){
  int winddir = Serial1.parseInt();
  float windspeedmph = Serial1.parseFloat();
  float humidity = Serial1.parseFloat();
  float tempf = Serial1.parseFloat();
  float rainin = Serial1.parseFloat();
  float dailyrainin = Serial1.parseFloat();
  float pressure = Serial1.parseFloat();
  Serial.print(" Wind Direction = ");
  Serial.println(winddir);
  Serial.print(" Wind Speed = ");
  Serial.println(windspeedmph);
  Serial.print(" Humidity = ");
  Serial.println(humidity);
  Serial.print(" Temperature F = ");
  Serial.println(tempf);
  Serial.print(" Rain Inches = ");
  Serial.println(rainin);
  Serial.print(" Daily Rain Inches = ");
  Serial.println(dailyrainin);
  Serial.print(" Pressure = ");
  Serial.println(pressure);
  Serial.println();

ThingSpeak.setField(1, winddir);
  ThingSpeak.setField(2, windspeedmph);
  ThingSpeak.setField(3, humidity);
  ThingSpeak.setField(4, tempf);
  ThingSpeak.setField(5, rainin);
  ThingSpeak.setField(6, dailyrainin);
  ThingSpeak.setField(7, pressure);
  ThingSpeak.writeFields(myChannelNumber,myWriteAPIKey);
  /ThingSpeak.writeField(myChannelNumber, 2, windspeedmph, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 3, humidity, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 4, tempf, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 5, rainin, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 6, dailyrainin, myWriteAPIKey);
  ThingSpeak.writeField(myChannelNumber, 7, pressure, myWriteAPIKey);
/
  delay(20000);
  }
   
}


However, I have one remaining issue that needs addressing. I need to get the program to recognize the $ as the start marker, the # as the end marker and perhaps the commas as separators. My data is parsing out of order about every 3rd time.

Please note I mean to disrespect to Robin2 who has been trying to help me by using the serial data examples. I've been working on understanding both and this one just (almost) worked.

Thanks! ;D
  if (Serial1.read() == '

You shouldn't be reading from the serial port unless you KNOW that there is data available() to read.

Nowhere in Robin2's post is there anything about using parseInt() or parseFloat().){


You shouldn't be reading from the serial port unless you KNOW that there is data available() to read.

Nowhere in Robin2's post is there anything about using parseInt() or parseFloat().