How the heck do global variables and timing work?

Some background on what I'm trying to accomplish. I'm building a sketch that displays the weather forecast using ambient design. Information is pulled from a PHP script that builds a page that displays a unique character indicating the weather forecast (S=Same C=Cold W=Warm). The LEDs display the forecast temperature based on that.

Using the TwitterClient ethernet example, I've been able to retrieve the data from my site and turn on the correct LED. In the process of building this sketch I've also learned the difference between delay() and millis(), which is where I'm stumbling now. In the example sketch below, I'd like the LEDs (for now) to blink if the forecast matches, which does not occur, and the LED simply turns on.

Ideally the LED should blink in the duration between server calls replacing digitalWrite(whitePin, HIGH) in the sketch. I haven't included a blink example (having used the Blink Without Delay as a guide) in the sketch below, as I'm unable to make this work properly. My understanding is that the sketch is cycling through connectToServer() but the timing won't properly work with another timing routine inside that. It has been suggested that global variables may solve this issue.

I'll be honest, I'm new at this and I'm not a programmer. While the code is most likely bloated and perhaps inefficient, it does mostly work. I look forward to any advice you may impart.

Using Uno with ethernet shield.

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

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
char serverName[] = "lessismorecast.com";
EthernetClient client;

const int whitePin = 9;
const int redPin = 6;
const int bluePin = 5;

const unsigned long requestInterval = 14400000;  // delay between requests
unsigned long lastAttemptTime = 0;  // last time connected to the server
String currentLine = "";  // string to hold the text from server
String forecast = "";  // string to hold the forecast
boolean readingforecast = false;  // if you're currently reading the forecast
boolean requested;  // whether you've made a request since connecting

void setup() {
  pinMode(whitePin, OUTPUT);
  pinMode(redPin, OUTPUT);
  pinMode(bluePin, OUTPUT);  

  // currentTime = millis();
  // loopTime = currentTime;  

  currentLine.reserve(256);  // reserve space for the strings
  forecast.reserve(150);  // reserve space for forecast strings

  // Start up the ethernet shield
  Serial.begin(9600);
  if (Ethernet.begin(mac) == 0) {  // start ethernet using mac & IP address
    Serial.println("Failed to configure Ethernet using DHCP");  
    while(true);  // no point in carrying on, so stay in endless loop
  }

  connectToServer();
  
}

void loop() {

  if (client.connected()) {
    if (client.available()) {  // read incoming bytes
      char inChar = client.read();
      currentLine += inChar;  // add incoming byte to end of line
      if (inChar == '\n') {  // if you get a newline, clear the line
        currentLine = "";
      } 
      
      if ( currentLine.endsWith("<text>")) {  // if the current line ends with <text>, it will be followed by the forecast
        readingforecast = true;  // Clear the forecast string
        forecast = "";
      }

      if (readingforecast) {  // Add bytes to the forecast String
        if (inChar == 'S') {
          digitalWrite(whitePin, HIGH);  // white
          Serial.println("tomorrow is the same");
        }
      }

      if (readingforecast) {
        if (inChar == 'W') {
          digitalWrite(redPin, HIGH);  // red
          Serial.println("tomorrow is warmer");
        }
      }

      if (readingforecast) {
        if (inChar == 'C') {
          digitalWrite(bluePin, HIGH);  // blue
          Serial.println("tomorrow is colder");
        }
      }

      if (readingforecast) {
        if (inChar != '<') {
          forecast += inChar;
        } 
        else {
          readingforecast = false;  // End of forecast if "<" character is reached
          Serial.println(forecast);   
          Serial.println("disconnecting...");
          client.stop();  // close the connection to the server
        }
      }
    }   
  }
  
  else if (millis() - lastAttemptTime > requestInterval) {
    connectToServer();  // if you're not connected, and two minutes have passed since your last connection, then attempt to connect again
  }

}

void connectToServer() {
  digitalWrite(whitePin, LOW);  // Reset LED to off
  digitalWrite(redPin, LOW);  // Reset LED to off
  digitalWrite(bluePin, LOW);  // Reset LED to off
  
  // attempt to connect, and wait a millisecond:
  delay(1000);  // give the Ethernet shield a second to initialize
  Serial.println("connecting...");
  if (client.connect(serverName, 80)) {  // Connect to server
    Serial.println("connected...");
    // make HTTP GET request to twitter:
    client.println("GET /beacon.php HTTP/1.0");  // Make a HTTP request
    client.println("HOST: lessismorecast.com");
    client.println("User-Agent: Arduino 1.0");
    client.println();
  }

  lastAttemptTime = millis();  // note the time of this connect attempt
}

n the process of building this sketch I've also learned the difference between delay() and millis(), which is where I'm stumbling now.

This suggests to me that you haven't learned the difference between delay() (sit on your ass doing nothing for some time) and millis() (how long has the Arduino been running).

I'd like the LEDs (for now) to blink if the forecast matches, which does not occur, and the LED simply turns on.

There is no blinkTheLedOnPin() function. You need to understand what blinking and LED means. The pin that the LED is attached to needs to be turned on, then after some time, it needs to be turned off. Then, later, it needs to be turned on again. Then, off again then on again.

It seems to me like you are trying to mix the blink the led code with the get the forecast code. That won't work. The Arduino needs to perform an action - get the forecast - then perform another action - based on what the forecast is, and how long the appropriate pin has been in its current state, possibly change the state of that pin.

Create a function that returns a char ('S', 'W', 'C', or 'N' (no connection/no information). That function contains all the code to connect to the server and get the forecast. It does NOTHING with the LED pins.

Create another function that takes a char ('S', 'W', 'C' or 'N') and uses that to determine which pin to toggle. That function then determines whether it is time to toggle the appropriate pin.

Then., loop() is simple:

void loop()
{
    char status = getForecast();
    diddleWithPin(status);
}

You can debug each function independently, by Serial.print()ing status without calling diddleWithPin() and by hardcoding status, instead of calling getForecast().

PaulS:

I'd like the LEDs (for now) to blink if the forecast matches, which does not occur, and the LED simply turns on.

There is no blinkTheLedOnPin() function. You need to understand what blinking and LED means. The pin that the LED is attached to needs to be turned on, then after some time, it needs to be turned off. Then, later, it needs to be turned on again. Then, off again then on again.

It seems to me like you are trying to mix the blink the led code with the get the forecast code. That won't work. The Arduino needs to perform an action - get the forecast - then perform another action - based on what the forecast is, and how long the appropriate pin has been in its current state, possibly change the state of that pin.

Create a function that returns a char ('S', 'W', 'C', or 'N' (no connection/no information). That function contains all the code to connect to the server and get the forecast. It does NOTHING with the LED pins.

Create another function that takes a char ('S', 'W', 'C' or 'N') and uses that to determine which pin to toggle. That function then determines whether it is time to toggle the appropriate pin.

Then., loop() is simple:

void loop()

{
    char status = getForecast();
    diddleWithPin(status);
}




You can debug each function independently, by Serial.print()ing status without calling diddleWithPin() and by hardcoding status, instead of calling getForecast().

Thanks, Paul! You're correct, I don't have a blinkLED() function in the current sketch. And you're right, I had been mixing up the blink and forecast code together previously. The advice you've given about the separate functions is helpful and I'll fix the sketch based on that. Being new to programming, I perceived that nesting of functions was an option, but what you say does make sense.

So if I understand you correctly, much of what I have in the existing loop() can actually be kept in discrete functions instead?

So if I understand you correctly, much of what I have in the existing loop() can actually be kept in discrete functions instead?

Yes. Just make sure that you separate the code into the correct functions.

Huzzah, it works! Thanks, Paul. My solution is listed below.

If I could ask another question of you: the output for forecast adds a greater-than sign, such as forecast == ">S"
I'm unsure where it is picking that up from. Any suggestions?

Again, thanks for your assistance on helping me create this sketch. It all works correctly. Now I can go through it again and refine what's there and trim the redundant information.

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

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
char serverName[] = "lessismorecast.com";
EthernetClient client;

const int whitePin = 9;
const int redPin = 6;
const int bluePin = 5;

int ledState = LOW;                              // ledState used to set the LED
long previousMillis = 0;                         // will store last time LED was updated
long interval = 1000;                            // interval at which to blink (milliseconds)

const unsigned long requestInterval = 14400000;  // delay between requests
unsigned long lastAttemptTime = 0;               // last time connected to the server
String currentLine = "";                         // string to hold the text from server
String forecast = "";                            // string to hold the forecast
boolean readingforecast = false;                 // if you're currently reading the forecast
boolean requested;                               // whether you've made a request since connecting

void setup() {

  pinMode(whitePin, OUTPUT);
  pinMode(redPin, OUTPUT);
  pinMode(bluePin, OUTPUT);  

  currentLine.reserve(256);                      // reserve space for the strings
  forecast.reserve(150);                         // reserve space for forecast strings

  Serial.begin(9600);                            // Start up the ethernet shield
  if (Ethernet.begin(mac) == 0) {                // start ethernet using mac & IP address
    Serial.println("Failed to configure Ethernet using DHCP");  
    while(true);                                 // no point in carrying on, so stay in endless loop
  }

  // Initialize functions:
  getForecast();
  displayForecast();
  connectToServer();
}

void loop() {

  getForecast();
  displayForecast();

}

void displayForecast() {
  
  if (forecast == ">S") {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;            // save the last time you blinked the LED
      if (ledState == LOW)                       // if the LED is off turn it on and vice-versa
        ledState = HIGH;
      else
        ledState = LOW;
      digitalWrite(whitePin, ledState);          // set the LED with the ledState of the variable
    }
  }
  
  if (forecast == ">W") {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;            // save the last time you blinked the LED
      if (ledState == LOW)                       // if the LED is off turn it on and vice-versa
        ledState = HIGH;
      else
        ledState = LOW;
      digitalWrite(redPin, ledState);            // set the LED with the ledState of the variable
    }
  }
  
  if (forecast == ">C") {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;            // save the last time you blinked the LED
      if (ledState == LOW)                       // if the LED is off turn it on and vice-versa
        ledState = HIGH;
      else
        ledState = LOW;
      digitalWrite(bluePin, ledState);           // set the LED with the ledState of the variable
    }
  }

}

void getForecast() {

  if (client.connected()) {
    if (client.available()) {                    // read incoming bytes
      char inChar = client.read();
      currentLine += inChar;                     // add incoming byte to end of line
      if (inChar == '\n') {                      // if you get a newline, clear the line
        currentLine = "";
      } 
      
      if ( currentLine.endsWith("<text>")) {     // if the current line ends with <text>, it will be followed by the forecast
        readingforecast = true;                  // Clear the forecast string
        forecast = "";
      }

      if (readingforecast) {
        if (inChar != '<') {
          forecast += inChar;
        } 
        else {
          readingforecast = false;               // End of forecast if "<" character is reached
          Serial.println(forecast);   
          Serial.println("disconnecting...");
          client.stop();                         // close the connection to the server
        }
      }
    }   
  }
  
  else if (millis() - lastAttemptTime > requestInterval) {
    connectToServer();                           // connect after six hours
  }

}

void connectToServer() {
  
  // attempt to connect, and wait a millisecond:
  delay(1000);                                   // give the Ethernet shield a second to initialize
  Serial.println("connecting...");
  if (client.connect(serverName, 80)) {          // Connect to server
    Serial.println("connected...");
    // make HTTP GET request to twitter:
    client.println("GET /test.html HTTP/1.0");   // Make a HTTP request
    client.println("HOST: lessismorecast.com");
    client.println("User-Agent: Arduino 1.0");
    client.println();
  }

  lastAttemptTime = millis();                    // note the time of this connect attempt
}

I would guess that it's arriving here...

     if (readingforecast) {
        if (inChar != '<') {
          forecast += inChar;
        }

Try placing a Serial.print(c); right after forecast += inChar;