If this is your first time using the Feather HUZZAH ESP8266, you’ll want to begin with our guide for setting that up. There’s some software to install and a few persnickety items to be selected just right in the Arduino IDE:

To confirm that you have the driver installed and IDE properly configured, load the basic “blink” example sketch, edit the LED pin number to pin 0, then try uploading to the board. If it won’t cooperate, work carefully through each of the steps in the guide linked above.

Do not continue until you have the “blink” sketch successfully working on the Feather HUZZAH ESP8266 board.

 Once you’re ready to proceed, copy and paste the following code into a new sketch, and edit the wireless network name and password (around line 34) to match your setup.

Further down, just below the code, we’ll explain how to set this up for your location…

/* -------------------------------------------------------------------------
FAIR WEATHER FRIEND -- a migraine headache forecaster using the Adafruit
HUZZAH ESP8266 WiFi microcontroller (can also work on other ESP8266 boards)
with forecasts provided by AccuWeather.com.  LED on pin 15 shows status:

  Steady on      = One-time initialization
  Fast flicker   = Connecting to network, polling data
  Slow blink     = Symptoms predicted in forecast (24-48 hrs)
  Blip ea. 4 sec = Symptoms not in forecast (sleeping)

Adafruit invests time and resources providing this open source code,
please support open-source hardware by purchasing products from Adafruit!

Configure the code below with your WiFi credentials.  Then visit
www.accuweather.com and look up the migraine (or other!) forecast for your
location.  Copy the URL into the appropriate spot in the code below.
They have other forecasts that are less grim -- hiking, golf weather, etc.
When changing the forecast type, you'll need to dig through the page's HTML
source to find a string that uniquely identifies the condition sought, while
avoiding false positives.  This code just uses string matches and is not
sophisticated in that regard.  See additional notes later in the code.


#include <ESP8266WiFi.h>

#define LED_PIN       15        // LED+ is connected here
#define POLL_INTERVAL (15 * 60) // Time between server queries (seconds)
#define FAIL_INTERVAL 30        // If error, time before reconnect (seconds)
#define READ_TIMEOUT  10000L    // Client read timeout, milliseconds

char ssid[] = "NETWORK_NAME",
     pass[] = "NETWORK_PASSWORD",
     host[] = "www.accuweather.com",
     page[] = "/en/us/new-york-ny/10013/migraine-weather/3709_pc";

// This structure is used during string-matching operations.  Only the
// 'string' element is initialized here; other elements are initialized
// or modified as needed in multiFind().  This code is NOT AVR-friendly;
// PROGMEM strings are not used, it's assumed this will be running on
// an ESP8266 (or ported to other non-AVR board that just normally puts
// const strings in program memory instead of RAM).
struct stringMatch {
  const char * const string;
  uint8_t            stringLength;
  uint8_t            matchedLength;
} matchList0[] = {
  { "<h3>Today</h3>"    },
  { "<h3>Tomorrow</h3>" },
  { NULL                }  // END OF LIST, don't remove this
}; // Can create add'l string match lists here if needed

WiFiClient client;

void setup(void) {
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH); // Steady on = startup

// STRING-MATCH FUNCTION -----------------------------------------------------

// multiFind() scans a connected Client object for one or more strings
// (NULL-terminated stringMatch array 'list'), returns index of the first
// string matched (0 to n-1) or -1 if timeout or no match.
static int8_t multiFind(struct stringMatch *list) {
  uint32_t t;
  char     c;
  uint8_t  i;

  // Reset all stringMatch items prior to search...
  for(i=0; list[i].string; i++) {
    list[i].stringLength  = strlen(list[i].string);
    list[i].matchedLength = 0;

  for(t=millis();;) {
    if(client.available()) {          // Data pending from Client?
      c = client.read();              // Read it
      if(c == 0) break;               // End of data reached, no match
      for(i=0; list[i].string; i++) { // Compare against each stringMatch item...
        if(c == list[i].string[list[i].matchedLength]) { // Matched another byte?
          if(++list[i].matchedLength ==                  // Matched whole string?
               list[i].stringLength) return i;           // WINNER, return index
        } else { // Character mismatch, reset counter to start
          list[i].matchedLength = 0;
      t = millis(); // Reset timeout
    } else if((millis() - t) > READ_TIMEOUT) {
  return -1; // No string match, or timeout

void loop() {

  uint32_t t, hi, lo, pauseTime = FAIL_INTERVAL;

  // Fast blink during WiFi connection...
  analogWriteFreq(4);        // 4 Hz
  analogWrite(LED_PIN, 100); // ~10% duty cycle

  Serial.print("WiFi connecting..");
  WiFi.begin(ssid, pass);
  while(WiFi.status() != WL_CONNECTED) {

  // Slightly slower (but still quick) blink while searching
  analogWriteFreq(1);        // 1 Hz
  analogWrite(LED_PIN, 100); // ~10% dury cycle

  Serial.print("Contacting server...");
  if(client.connect(host, 80)) {
    Serial.print(F("OK\r\nRequesting data..."));
    client.print("GET ");
    client.print(" HTTP/1.1\r\nHost: ");
    client.print("\r\nConnection: Close\r\n\r\n");

    // multiFind() searches the incoming stream for a list of possible
    // string matches, returning the index of the found item (or -1 if
    // no match).  Stream position will be immediately after the found
    // item (allowing further searches to be performed from that point
    // forward), or end of stream in -1 case.
    // client.find() is the normal Arduino Stream search function, which
    // looks for a single item.  In this code, we're using multiFind()
    // to skip past some of AccuWeather's false positives, to pick a
    // starting point for a simple string search that more reliably
    // indicates migraine weather in the forecast...
    if((multiFind(matchList0) >= 0) &&
       client.find("Migraine Headache <span>Weather")) {
      // Found it -- migraine weather in next 24-28 hrs.
      hi = lo = 500; // 1 Hz, 50% duty cycle
    } else { // No match
      Serial.println(F("not found"));
      hi =   10; // Tiny blip 
      lo = 3990; // at about 1/4 Hz
    // This is just one example...more complex code might need multiple
    // find() and/or multiSearch() calls with different lists as a sort
    // of decision tree.

    Serial.println("Closing server connection.");
    pauseTime = POLL_INTERVAL;
  } else {
  // WiFi is turned off between server queries, to save a little power if
  // you decide to make this battery-operated.
  Serial.println("Stopping WiFi.");
  analogWrite(LED_PIN, 0);

  // Delay until next server query time. The values of 'hi' and 'lo'
  // determine the LED blink speed. This code doesn't use any low-power
  // sleep techniques, as the ESP8266 doesn't appear to support PWM while
  // sleeping...it has to be blinked in software.
  Serial.print("Pausing for ");
  Serial.println(" seconds.");
  t = millis();
  while((millis() - t) < (pauseTime * 1000)) {
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(LED_PIN, LOW);

Setting Up for Your Location

Just below the WiFi network name and password are these two lines:

host[]  = "www.accuweather.com",
page[]  = "/en/us/new-york-ny/10013/migraine-weather/3709_pc";

The first line stays as-is. The second, we need to visit the AccuWeather web site and type our location into the search field…

After bringing up the local forecast, click the “+” button in the forecast dock to show different available forecasts, then click “MIGRAINE” to add this to the dock options. Then click “MIGRAINE” in the dock to display the current forecast.

On the next page we’ll show how to set up the software to scan for different things, but for now let’s look up migraines.

With the migraine page now loaded, copy the page URL (not the entire site URL, just the page, from the first slash (/) forward), like this:

Paste this over the page[] string in the code…keep the quotes and semicolon…

page[]  = "/en/us/elephant-butte-nm/87935/migraine-weather/344484";

Make sure all your Tools→Board menu options are set up for the Feather HUZZAH ESP8266, plug in USB and click the “upload” button.

If using a DC power supply, do not have this and USB connected at the same time. Unplug one before connecting the other.

“Reading” the Indicator LED

When first powered up, a steady LED indicates the system is initializing. This should only take a brief moment. If not…

  • If the LED doesn’t turn on at all: possibly an electrical short, or the LED is on the wrong pin.

Check your wiring if there’s a problem!

Steady Blink

A slow, steady blink (1/2 second on, 1/2 second off) indicates that a match was found…possible headache- or migraine-aggravating conditions in the next 24 to 48 hours. In my case, this means being mindful of symptoms and taking medication at the earliest sign of trouble and/or before going to bed. Or go and check the AccuWeather web site to see the specifics.

Off with Periodic “Blip”

If there’s no steady blink, this means that migraine conditions are not currently forecast in the next 48 hours. A tiny “blip” of the LED (every four seconds) lets you know that the program is still alive and is just resting until the next search.

Fast Blink

A very fast blink or flutter of the LED lets you know it’s connecting to the WiFi network and searching the AccuWeather forecast. It will be extra fast during the WiFi connect phase, then a little slower while processing data from the server…it’s a complex web page and may take several seconds to complete.

If this mode exits quickly (within a couple seconds), it may be having trouble accessing the wireless network. Check the network name and password in the code. Or it might be a malformed URL. Did you copy the page name correctly? WITH the leading slash, but NO http or domain name included. Connect a USB cable and check the Serial Monitor for status messages.

The connection and scan occur every 15 minutes; it really doesn’t need to be very frequent at all. This is set by the 'POLL_INTERVAL' variable in the code…it’s the number of seconds between scans. (15 * 60) gives us 15 minutes.

If the wireless network is unreachable, the software will try connecting again in 30 seconds.

Okay! Now, what if we want a fun forecast? This gets more involved…

This guide was first published on Mar 20, 2016. It was last updated on Sep 19, 2014.

This page (Code) was last updated on Sep 19, 2014.

Text editor powered by tinymce.