Thimble Learning Platform
Back to full view

Weather Station 2

Introduction

This is the second in themed kits.

Objectives

  • Learn about weather.
  • Have fun!
  • I've Never Used an Arduino

    Then this is a great place to start! If you're already familiar with setting up the Arduino and the IDE, you can skip to the next section.

    Terms 

    So when we talk about Arduino, the Arduino IDE, and coding, there are a lot of words being thrown around. This section will clear all of those up and get you ready to start building!

    Arduino 

    The Arduino is a electronics prototyping platform. That means it's a flexible platform for building, testing, and, of course, prototyping electronics. That could be simple LEDs that light up at night, or advanced robots. When we talk about the Arduino we are talking about the physical board itself. The one that comes in this kit is red, but they come in many different shapes and colors. The Arduino brand and original board were created by arduino.cc and you can read more about the details of its history here.

    Arduino IDE 

    The Arduino IDE, or Integrated Development Environment, is not hardware, but a piece of software. If you wanted to write an essay, you might use Microsoft Word or Google Docs. If you wanted to edit pictures, you'd probably use Photoshop or Paint.net. If you want to program code for the Arduino, you'd use the Arduino IDE. Its a computer program (tool) that let's you develop and test out code. Once written, the code can be upload to the physical Arduino where it will stay until you upload new code.

    Download

    Since it is a program, you'll need to install it.

    For Windows computers use this link Windows Installer

    For Mac computers use this link Mac Installer

    After following each link, press the Just Download button to begin downloading the Arduino IDE. The Mac version has a few more steps and we have a video just for that.

    Mac Installation
    For Mac users

    Installation

    Open the exe you downloaded. You'll see a screen asking you to agree to their license. You can press I Agree.

    LicenseLicense

    Next up are the installation options. The Install USB Drivers lets the Arduino board communicate with the IDE. We definitely want this. The Associate .ino files makes any Arduino file on your computer open up to the Arduino IDE. Very helpful, so we'll keep that too. Press Next >.

    OptionsOptions

    Now you are being asked where you'd like the Arduino IDE program installed. I don't mind it in the standard Program Files so I just press Install. You can change where it will be installed, just remember what you chose. I recommend not changing it.

    FolderFolder

    Now the IDE is installing. Let it complete then move on to the next section.

    InstallingInstalling

    Arduino IDE 101

    Open up the Arduino IDE for the first time. So, let's figure out what's what.

    Arduino IDEArduino IDE

    There is a mostly empty space in the middle that has this text:

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void setup() {
      // put your setup code here, to run once:
    
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    
    }
    

    This is a simple Arduino Sketch. A Sketch is code you write that will later be uploaded to the Arduino board.

    There are few steps to go from code written in a sketch to code that runs on the Arduino board. Those are Verify > Compile > Upload.

    All of these you can get to by going under Sketch on the top bar of the program. Or but using the Checkmark and Arrow icons right below that. The Checkmark verifies your code will compile, and the arrow button uploads the code to the Arduino.

    Sketch
    Icons

    Verify - looks at your code and makes sure it can actually run. It checks that there are no errors or things out of place. If your code will work, it goes to the Compile stage.

    Compile - The code you've written is great for human. It is readable and hopefully understandable. But this isn't great for a machine ie the Arduino. The Compile stage takes your written code and translates into something that the Arduino can understand and run, called machine code. It saves that compiled file and tried to Upload it to the board.

    Upload - This takes the compiled files and tries to send it to a plugged in Arduino. The IDE doesn't know where you've plugged in your Arduino or what kind it is. You have to tell it. I'll show you how to set that up in the next section.

    Arduino Board Setup

    For this section make sure your Arduino Board that came with your kit is plugged in. Use the included micro USB cable to plug your Arduino into any free USB port on you computer.

    Navigate to the Tools section at the top of the Arduino IDE.

    ToolsTools

    You'll be greeted with a scary drop down. To setup your Arduino to accept uploads, do the following: Go to Board: and make sure Arduino/Genuino Uno is selected.

    Next is the Port. For Mac users refer to the video for these instructions. With your Arduino plugged in, you'll see some options. Mine says COM 15. This is the channel that the IDE will use to communicate with your Arduino. If you have a lot of COM # devices and don't know which is your Arduino, unplug your Arduino and take note of that numbers are there. Then plug it back in. Whichever is the new one in the list is your Arduino. Select that port.

    With the board and port selected and ready to go, let's upload our first sketch.

    Sketches

    We are going to upload a simple sketch to make an LED on the board blink. Head to File > Examples > Basics > Blink . You should see this sketch.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    /*
      Blink
    
      Turns an LED on for one second, then off for one second, repeatedly.
    
      Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
      it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
      the correct LED pin independent of which board is used.
      If you want to know what pin the on-board LED is connected to on your Arduino
      model, check the Technical Specs of your board at:
      https://www.arduino.cc/en/Main/Products
    
      modified 8 May 2014
      by Scott Fitzgerald
      modified 2 Sep 2016
      by Arturo Guadalupi
      modified 8 Sep 2016
      by Colby Newman
    
      This example code is in the public domain.
    
      http://www.arduino.cc/en/Tutorial/Blink
    */
    
    // the setup function runs once when you press reset or power the board
    void setup() {
      // initialize digital pin LED_BUILTIN as an output.
      pinMode(LED_BUILTIN, OUTPUT);
    }
    
    // the loop function runs over and over again forever
    void loop() {
      digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
      delay(1000);                       // wait for a second
      digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
      delay(1000);                       // wait for a second
    }
    

    Don't worry about the coding for now. Let's upload this sketch. You can press the Arrow icon, go to Sketch then Upload, or you can use the shortcut Ctrl+U in order to upload the code. Your sketch will auto-verify, compile, then upload to your Arduino board.

    You will see the message Done uploading and your board should now have a blinking light.

    Uploading Sketches and Code From Thimble 

    We provide example code for all our projects. To use those you'll need to copy them from our website. Then, go into the Arduino IDE, File > New. It's best to then delete all the code already there in the sketch before you paste our code in, as that will avoid any errors from copying over the old code.

    Serial Plotter

    You'll be asked to use the Serial Plotter on some projects. This reads any information coming from the Arduino and displays it on a graph. It can only be accessed after you've uploaded your code. It is in the Tools menu.

    Troubleshooting 

    The most common issues are solved by double checking your Board and Port settings.

    Libraries

    What are Libraries? 

    Libraries are a collection of code that has already been written. We use them when we want to add functionality without have to write everything from scratch.

    Some the projects in this set need libraries so this section will show you how to install those.

    Libraries to be downloaded 

    Our libraries come in the form of zip files that need to be downloaded but NOT unzipped.

    WiFi ESP - Commands for the ESP WiFi Module.

    Chainable LED - Needed to light up the RGB leds.

    Linked Lists - Used to organize data in some advanced code.

    Adding Them to the Arduino IDE 

    Open up the Arduino IDE and go to Sketch > Include Library > Add .ZIP Library.... After that navigate to the zip files and add them one by one. And that's it!

    Add a LibraryAdd a Library

    Serial Communication

    What is Serial Communication? 

    Serial communication is a method of using signals to send data. It's commonly used when passing data between integrated circuits and microcontrollers. Information is passed bit by bit, literally. There are many different implementations of serial communication. Let's go over some of the most common ones.

    UART 

    UART, or Universal Asynchronous Receiver-Transmitter, is a very common and cheap way to provide serial communication. UART always has at least two signal wires: a receiving line (RX) and a transmitting line (TX). You will also likely see a power and ground line to match the signaling voltages between the two devices. UART communication is between only two devices. The devices must agree beforehand on a couple of settings in order to communicate. A mismatch in settings will cause a lot of weird symbols to be detected, and thus cause a miscommunication. These settings are usually programmed into the devices ahead of time, but some devices are also configurable. So what are these settings?

    Standard UART HookupStandard UART Hookup

    Baud Rate

    Baud rate is how fast the communication is taking place. A Baud is actually a unit of measurement name after Emile Baudot, who invented a system of transmitting information using the telegraph. Baudot code is similar to Morse code and you can find out more information here.

    A baud is defined as the number of symbols that you can send in a second. In the case of serial communication for a microcontroller, it's how many 1s or 0s you can send in a second. It's more costly in terms of system resources to send at higher baud rates. So, that could mean more expensive hardware or more power consumption. Both devices must be on the same baud rate in order to communicate. If there is a mismatch information will be lost or decoded into the wrong symbols.

    Baud Rates VisualizedBaud Rates Visualized

    Start and Stop Bits

    These are symbols found at the beginning and end of a message that tell you if it's the beginning or the end. A UART receiver will be listening for the start symbols and only begin recording a message after a start bit. The stop bits tell the receiver when the message is over.

    Both devices in a UART pair must agree on the amount of bits or symbols to use for start and stop. You would use longer start and stop bits when the medium you are using for communication is noisy and you want to be extra sure your message is getting across. That could either mean the information is very important or the channel you are sending on might have a lot of interference that disrupts communication.

    Parity Bit

    A parity bit is a single bit or symbol used for "cheap" error checking. The transmitter of a message will add up the bits in the message and that number will either be odd or even, making the parity bit 1 or 0, respectively. The parity bit tells the receiver if the message sum should be odd or even, providing a simple check for if the message was received correctly. This method of error checking is very simple and can fail if more than one bit in the message is incorrect, but it costs very little in system resources, so it's common to see it implemented. The devices must agree on if parity bits are to be used.

    There are two variants of parity bits: even and odd. If the UART is using even parity, the parity bit will be a 0 when the count of bits whose value is 1 is even, and a 1 when the count of bits whose value is 0 is odd. In odd parity, the opposite is true. The parity bit will be a 0 when the count of bits whose value is 1 is odd, and 1 when it is even.

    Data Bits

    These bits are the binary expression of the data you are sending. So, if a sensor is reporting back a value of 26, it would be converted to binary and be transmitted as 11010 . Devices must agree on how many data bits are to be sent.

    Sending the Data

    With all these settings agreed upon, the UART channel can be used between two devices. When data is ready to be sent, it is packaged into a frame. A frame consists of the start bit, then data bits, followed by a parity bit, and, lastly, the stop bits. This frame is sent through one of the device's TX lines (at the speed set by the baud rate) to the other device's RX line, where it is received and processed by the device.

    SPI 

    SPI or Serial Peripheral Interface is another way of sending data between devices. It differs from UART in that SPI is Synchronous. This means that the devices sync up their clocks and have no need to use start and stop bits or agree on a baud rate. This setup needs more connections, using at least 4 wires to communicate.

    Clock Line

    One device in the SPI setup will generate the clock signal that all other devices will use. This device is known as the master. There is no need for baud rates since all data transfer will be based off this clock signal, which gets it's own wire.

    MOSI Line

    The next line is used for Master-Out-Slave-In (MOSI) communication. This is a data line for the master device to send to slave devices only. If a slave device needs to send data back it will have to do it on another line.

    MISO Line

    MISO stands for Master-In-Slave-Out and is a data line used only for slave devices to send back information.

    Slave Select Line

    The last line is called the Slave Select (SS) line. It is also sometimes called the Chip Select or CS line. It is used to indicate to slave devices that information is about to be sent. SPI can have multiple slave devices, so a separate SS line would be used for each device that the master wants to communicate with. If the SS line is pulled low, the slave device knows to listen, while if it is high, the slave device ignores the message until it is selected again.

    Advantages

    Due to the design (multiple lines for communication) SPI can be much faster than UART and can communicate with more than two devices.

    Disadvantages

    Communication needs at least 4 lines and that number increases with more devices being used. This comes with the obvious area trade-off. SPI requires additional data lines for routing, additional pins on the master device for each slave device SS line, and, ultimately, increased physical area to allow for these lines.

    I2C 

    I2C or Inter-integrated Circuit protocol is a communication protocol that borrows the best bits from UART and SPI. It only uses two wires like UART, but it can support more than 2 devices like SPI. I2C substitutes the Slave Select hardware method for setting up device communication with a software addressing structure. So, instead of a physical wire for chip select/slave select, it is done via software addresses.

    Lines

    I2C has a clock line and a data line. Master to slave and slave to master communication happens over the same data line.

    Protocol

    The master will first send the address of which slave it wants to communicate with over the data line. The slaves all listen on the data line, and if their address isn't called, they stop listening. The master then sends the data it wants to send and the only device still listening for data is the one the master addressed.

    Advantages

    I2C only uses 2 lines and can communicate with up to 112 devices. It is faster than UART.

    Disadvantages

    I2C isn't as fast as SPI. Its top throughput is limited when compared to SPI because of the overhead of the addressing scheme and because all of the devices have to share the same data line.

    What Should I Have Received?

    List of Parts 

    This is a list of all the parts you should have received in your Weather Station 2 kit. Your WiFi module might look different and you wrapper colors may vary.

    Parts

    All Parts x Qty
    WiFi Module x 1
    Chainable RGB LED x 2
    Button Module x 2
    Pack of Wrappers x 1
    Cables x 5

    Additional parts

    You should also have received a sheet of six stickers and a postcard with instructions.

    First Time WiFi Module Setup

    Start 

    You must follow this guide the first time you want to use the WiFi module. The module comes with a different Baud rate than what our libraries use. If you'd like to know more about Baud rates and why we have to change it, check out the page after this one.

    My module is different from the pictures 

    In the middle of the production of this kit, our manufacturer changed what the WiFi module looks like. There is one that has a silver rectangle and one button. The other version has a black square and two buttons. The hardware is identical. They have the same hardware specifications and function the same.

    Both WiFi modules side by sideBoth WiFi modules side by side

    Modules 

    Gather the following parts to complete this project.

    Parts

    All Parts x Qty
    WiFi x 1
    Cable x 1

    WiFi Module 

    Take a cable and unwrap it. Plug one side into the WiFi socket and the other into any Digital socket.

    Parts needed
    Take a cable
    Unwrap it
    Plug one side into the WiFi socket
    The other into any Digital socket

    Upload

    Upload the following code. The example below uses the D8 digital pin.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    #include <SoftwareSerial.h>
    #define TIMEOUT 10000
    
    //Creates serial communication object
    SoftwareSerial espSerial(8, 9); // RX, TX
    
    String response = "";
    
    int baudCount = 0;
    
    bool timedout = false;
    bool set = false;
    bool confirmed = false;
    
    unsigned long timestamp = 0;
    
    void setup() {
      // Open serial communications
      Serial.begin(9600);
      espSerial.begin(115200);
      Serial.println("Testing WiFi Module...");
      espSerial.write("AT\r\n");
    }
    
    
    
    void loop() { // run over and over
      while (response == "" && !timedout) {
        if (espSerial.available()) {
          delay(200);
          espSerial.write("AT\r\n");
          //Display in the Serial Monitor
          response = espSerial.readString();
        }
        if(millis() > (TIMEOUT + timestamp)){
          timedout = true;
          timestamp = millis();
        }
      }
      if(timedout && !set && !confirmed){
        Serial.println("Timeout communicating with WiFi Module at 115200 Baud");
      }
    
      if (response.endsWith("OK\r\n") && !set && !timedout) {
        Serial.println("Found WiFi Module. Setting Baudrate to 9600...");
        espSerial.write("AT+UART_DEF=9600,8,1,0,0\r\n");
        set = true;
        response = "";
        espSerial.end();
        espSerial.begin(9600);
        delay(200);
        espSerial.write("AT\r\n");
      }
      if (!set && timedout) {
        Serial.println("Setting Baudrate to 9600...");
        espSerial.write("AT+UART_DEF=9600,8,1,0,0\r\n");
        set = true;
        response = "";
        espSerial.end();
        espSerial.begin(9600);
        delay(200);
        espSerial.write("AT\r\n");
        timestamp = millis();
        timedout = false;
      }
    
      if (response.endsWith("OK\r\n") && set && !confirmed) {
        Serial.println("WiFi Module configured to 9600 baud");
        delay(200);
        Serial.println("Ready for Thimble use");
        confirmed = true;
      }
    }
    

    Did it work? 

    Open the Serial Monitor after uploading to see if it worked. You want to see this output text...

    Download file Copy to clipboard
    1
    2
    3
    4
    Testing WiFi Module...
    Found WiFi Module. Setting Baudrate to 9600...
    WiFi Module configured to 9600 baud
    Ready for Thimble use
    

    I only see Testing WiFi Module

    Press the reset button on your Arduino. If still only see Testing ESP... run the code below.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #include <SoftwareSerial.h>
    
    //Creates serial communication object
    SoftwareSerial espSerial(8, 9); // RX, TX
    
    String response = "";
    
    bool confirmed = false;
    
    void setup() {
      // Open serial communications
      Serial.begin(9600);
      espSerial.begin(9600);
      Serial.println("Testing WiFi Module...");
      espSerial.write("at\r\n");
    }
    
    
    void loop() { // run over and over
      while (response == "") {
        if (espSerial.available()) {
          delay(200);
          espSerial.write("at\r\n");
          //Display in the Serial Monitor
          response = espSerial.readString();
        }
      }
    
      if (response.endsWith("OK\r\n") && !confirmed) {
        Serial.println("Your WiFi Module is working!");
        Serial.println("You are done with this step");
        confirmed = true;
      }
    }
    

    Did it work?

    Open the Serial Monitor after uploading to see if it worked. If you see this...

    Download file Copy to clipboard
    1
    2
    3
    Testing ESP...
    Your WiFi Module is working!
    You are done with this step
    

    ...then you are ready to go. If not, then double check you are using D8 as your socket and run the first code again.

    Weather Station 2

    Start 

    Starting here.

    Modules 

    Gather the following parts to complete this project.

    Parts

    All Parts x Qty
    WiFi x 1
    Cable x 1
    Finished WS1 project x 1

    WiFi Module 

    Remove the LCD Display module and button module. Take a cable and unwrap it. Plug one side into the WiFi socket and the other into any Digital socket.

    Parts Needed
    Remove button from base board
    Remove button module
    Remove LCD from base board
    Remove LCD module
    Take a cable
    Unwrap it
    Plug one side into the WiFi socket
    The other into any Digital socket

    Upload

    Upload the following code. The example below uses the D8 digital pin.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    #include "WiFiEsp.h"
    #include <Wire.h>
    #include "Seeed_BME280.h"
    #include <math.h>
    #include "Arduino.h"
    #include "SI114X.h"
    #include "SoftwareSerial.h"
    
    //Create Serial communication Object
    SoftwareSerial Serial1(8, 9); // RX, TX
    
    //Create sensor objects
    SI114X sunlightSensor;
    BME280 bme280;
    
    //title to use for your webpage
    String title = "Your Weather Station";
    
    //Weather variables
    float temperature;
    float humidity;
    float pressure;
    float uv;
    
    //Wifi settings
    char ssid[] = "Weather Node";    // your network SSID (name)
    char pass[] = "password";        // your network password
    int status = WL_IDLE_STATUS;     // the Wifi radio's status
    int reqCount = 0;                // number of requests received
    
    //Wifi server object
    WiFiEspServer server(80);
    
    // use a ring buffer to increase speed and reduce memory allocation
    RingBuffer buf(8);
    
    void setup()
    {
      //Start Serial Communication with computer and WiFi module
      Serial.begin(9600);   // initialize serial for debugging
      Serial1.begin(9600);    // initialize serial for ESP module
      WiFi.init(&Serial1);    // initialize ESP module
    
      //Start sunlight sensor commmunications
      while (!sunlightSensor.Begin()) {
        Serial.println("Sunlight Error");
        delay(1000);
      }
      //Start BME280 communications
      if (!bme280.init()) {
        Serial.println("BME Error");
      }
    
      //Print out debugging messages
      Serial.print("Attempting to start AP ");
      Serial.println(ssid);
    
      // start access point
      status = WiFi.beginAP(ssid);
    
      //Print out more debugging info
      Serial.println("Access point started");
      printWifiStatus();
    
      // start the web server on port 80
      server.begin();
      Serial.println("Server started");
    }
    
    
    void loop()
    {
      //Get temperature
      temperature = bme280.getTemperature();
      //convert to F from C
      temperature = temperature * 1.8 + 32;
    
      //Get humidity
      humidity = bme280.getHumidity();
    
      //Get pressure
      pressure = bme280.getPressure();
    
      //Get uv value and convert to UV Index values
      uv = (float)sunlightSensor.ReadUV() / 100;
    
      //Wait for a device to connect to the WiFi
      WiFiEspClient client = server.available();  // listen for incoming clients
    
        // if you get a client,
      if (client) {
        // print a message out the serial port
        Serial.println("New client");
    // initialize the circular buffer
        buf.init();
          // loop while the client's connected
        while (client.connected()) {
          // if there's bytes to read from the client,
          if (client.available()) {
            // read a byte, then
            char c = client.read();
            // push it to the ring buffer
            buf.push(c);
    
            // you got two newline characters in a row
            // that's the end of the HTTP request, so send a response
            if (buf.endsWith("\r\n\r\n")) {
              sendHttpResponse(client);
              break;
            }
          }
        }
    
        // give the web browser time to receive the data
        delay(10);
    
        // close the connection
        client.stop();
        Serial.println("Client disconnected");
      }
    }
    
    void sendHttpResponse(WiFiEspClient client)
    {
      client.print(
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/html\r\n"
        // the connection will be closed after completion of the response
        "Connection: close\r\n"
         // refresh the page automatically every 20 sec
        "Refresh: 20\r\n"
        "\r\n");
    
      client.print(
        "<!DOCTYPE HTML>\r\n"
        "<html>\r\n"
        "<head>\r\n"
        "<style>\r\n"
        "</style>\r\n"
        "</head>\r\n"
        "<body>\r\n"
        "<h1>" + title + "</h1>\r\n"
        "<br>\r\n");
    
      // Print # of requests received
      client.print("Requests received: ");
      client.print(++reqCount);
    
      client.print("<p id=\"date\"></p>"
        "<script>"
        "document.getElementById(\"date\").innerHTML = 'Date: ' + Date();"
        "</script>"
        "<p><br>\r\n");
    
      // Print temperature
      client.print("Temperature: ");
      client.print(temperature);
      client.print("F"
        "<br>\r\n"
        "Humidity: ");
    
      client.print(humidity);
      client.print("%"
        "<br>\r\n"
        "Air Pressure: ");
      client.print(pressure);
    
      client.print("Pa"
        "<br>\r\n"
        "UV Index: ");
      client.print(uv);
      client.print("<br>\r\n"
        "</body>\r\n"
        "</html>\r\n");
    }
    
    void printWifiStatus()
    {
      // print your WiFi IP address
      IPAddress ip = WiFi.localIP();
      Serial.print("IP Address: ");
      Serial.println(ip);
    
      // print where to go in the browser
      Serial.println();
      Serial.print("To see this page in action, connect to ");
      Serial.print(ssid);
      Serial.print(" and open a browser to http://");
      Serial.println(ip);
      Serial.println();
    }
    

    Observe

    To make sure we have everything set up correctly, open up the Serial Monitor. The WiFi module needs to initialize. After that, an IP will show up and we'll need that for the next step. In my case the IP address is 192.168.4.1 . Go over to your WiFi settings on any device with a web browser, and connect to "Weather Node". The password is "password". Once connected, head over to a web browser and type in that IP address. You'll be taken to a page that displays the current weather data from your weather station.

    Modify

    You can change the ssid[] to change the name of the WiFi network and the pass[] to any password you'd like. You can change the title variable to what you'd like your weather station to be called on the webpage.

    Experiment

    Check out the HTML and CSS page to learn more about customizing your Weather Station webpage.

    Connecting to WiFi 

    If you'd like to upload your weather data to the internet and be able to monitor it remotely then start here to connect your WiFi module to a WiFi network.

    Upload

    Upload the following code.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    #include "WiFiEsp.h"
    #include "SoftwareSerial.h"
    
    //Creates serial communication object
    SoftwareSerial Serial1(8, 9); // RX, TX
    
    void setup() {
      // initialize serial for debugging
      Serial.begin(9600);
      // initialize serial for ESP module
      Serial1.begin(9600);
      // initialize ESP module
      WiFi.init(&Serial1);
    
      // Print WiFi MAC address
      printMacAddress();
    }
    
    void loop()
    {
      // scan for existing networks
      Serial.println();
      Serial.println("Scanning available networks...");
      //function to scan for wifi networks
      listNetworks();
      //Wait 10 seconds before scanning agian
      delay(10000);
    }
    
    
    void printMacAddress()
    {
      // get your MAC address
      byte mac[6];
      //This is a unique hardware address for the wifi module
      WiFi.macAddress(mac);
    
      // print MAC address
      char buf[20];
      //replaces %02X with a MAC address byte
      sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
      Serial.print("MAC address: ");
      Serial.println(buf);
    }
    
    void listNetworks()
    {
      // scan for nearby networks
      int numSsid = WiFi.scanNetworks();
      //This runs when there isn't a wifi connection
      if (numSsid == -1) {
        Serial.println("Couldn't get a WiFi connection");
        //The Arduino will stay in this loop forever if it can't get a connection
        while (true);
      }
    
      // print the list of networks seen
      Serial.print("Number of available networks:");
      Serial.println(numSsid);
    
      // print the network number and name for each network found
      for (int thisNet = 0; thisNet < numSsid; thisNet++) {
        //Print the network number
        Serial.print(thisNet);
        Serial.print(") ");
        //Print network name
        Serial.print(WiFi.SSID(thisNet));
        //Print out the signal strength
        Serial.print("\tSignal: ");
        Serial.print(WiFi.RSSI(thisNet));
        Serial.print(" dBm");
        //Print password type
        Serial.print("\tEncryption: ");
        printEncryptionType(WiFi.encryptionType(thisNet));
      }
    }
    
    void printEncryptionType(int thisType) {
      // read the encryption type and print out the name
      switch (thisType) {
        case ENC_TYPE_WEP:
          Serial.print("WEP");
          break;
        case ENC_TYPE_WPA_PSK:
          Serial.print("WPA_PSK");
          break;
        case ENC_TYPE_WPA2_PSK:
          Serial.print("WPA2_PSK");
          break;
        case ENC_TYPE_WPA_WPA2_PSK:
          Serial.print("WPA_WPA2_PSK");
          break;
        case ENC_TYPE_NONE:
          Serial.print("None");
          break;
      }
      Serial.println();
    }
    

    Observe

    Open up the Serial Monitor and wait for the WiFi module to scan. It will print out a list of all the possible WiFi networks it can connect to. You should see the name of your normal WiFi network.

    Modify

    Before uploading this next bit of code, swap out ssid with your WiFi network's name and pass with its password.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    /*
     WiFiEsp example: UdpNTPClient
     Get the time from a Network Time Protocol (NTP) time server.
     Demonstrates use of UDP to send and receive data packets
     For more on NTP time servers and the messages needed to communicate with them,
     see http://en.wikipedia.org/wiki/Network_Time_Protocol
    */
    
    #include "WiFiEsp.h"
    #include "WiFiEspUdp.h"
    #include "SoftwareSerial.h"
    
    //Create channel for communication to wifi module
    SoftwareSerial Serial1(8, 9); // RX, TX
    
    //Wifi settings
    char ssid[] = "YOURWIFINETOWRK";       // your network SSID (name)
    char pass[] = "YOURPASSWORD";      // your network password
    int status = WL_IDLE_STATUS;     // the Wifi radio's status
    
    //Location of the time server
    char timeServer[] = "time.nist.gov";  // NTP server
    unsigned int localPort = 2390;        // local port to listen for UDP packets
    
    //Communication settings
    const int NTP_PACKET_SIZE = 48;  // NTP timestamp is in the first 48 bytes of the message
    const int UDP_TIMEOUT = 2000;    // timeout in miliseconds to wait for an UDP packet to arrive
    byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
    
    // A UDP instance to let us send and receive packets over UDP
    WiFiEspUDP Udp;
    
    void setup()
    {
      // initialize serial for debugging
      Serial.begin(9600);
      // initialize serial for ESP module
      Serial1.begin(9600);
      // initialize ESP module
      WiFi.init(&Serial1);
    
      // attempt to connect to WiFi network
      while ( status != WL_CONNECTED) {
        Serial.print("Attempting to connect to WPA SSID: ");
        Serial.println(ssid);
        // Connect to WPA/WPA2 network
        status = WiFi.begin(ssid, pass);
      }
    
      // you're connected now, so print out the data
      Serial.println("You're connected to the network");
    
      //Open the communication port
      Udp.begin(localPort);
    }
    
    void loop()
    {
      // send an NTP packet to a time server
      sendNTPpacket(timeServer);
    
      // wait for a reply for UDP_TIMEOUT miliseconds
      unsigned long startMs = millis();
      while (!Udp.available() && (millis() - startMs) < UDP_TIMEOUT) {
    
      }
      //Print out the information received
      Serial.println(Udp.parsePacket());
      //If packet is sent from the time server
      if (Udp.parsePacket()) {
        Serial.println("packet received");
        // We've received a packet, read the data from it into the buffer
        Udp.read(packetBuffer, NTP_PACKET_SIZE);
    
        // the timestamp starts at byte 40 of the received packet and is four bytes,
        // or two words, long. First, esxtract the two words:
    
        unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
        unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
        // combine the four bytes (two words) into a long integer
        // this is NTP time (seconds since Jan 1 1900):
        unsigned long secsSince1900 = highWord << 16 | lowWord;
        Serial.print("Seconds since Jan 1 1900 = ");
        Serial.println(secsSince1900);
    
        // now convert NTP time into everyday time:
        Serial.print("Unix time = ");
        // Unix time starts on Jan 1 1970, which is 70 years after NTP time starts.
        // Seventy years is 2208988800 seconds after we add in the extra days for the leap years.
        const unsigned long seventyYears = 2208988800UL;
        // subtract seventy years:
        unsigned long epoch = secsSince1900 - seventyYears;
        // print Unix time:
        Serial.println(epoch);
    
    
        // print the hour, minute and second:
        Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
        Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
        Serial.print(':');
        if (((epoch % 3600) / 60) < 10) {
          // In the first 10 minutes of each hour, we'll want a leading '0'
          Serial.print('0');
        }
        Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
        Serial.print(':');
        if ((epoch % 60) < 10) {
          // In the first 10 seconds of each minute, we'll want a leading '0'
          Serial.print('0');
        }
        Serial.println(epoch % 60); // print the second
      }
      // wait ten seconds before asking for the time again
      delay(10000);
    }
    
    // send an NTP request to the time server at the given address
    void sendNTPpacket(char *ntpSrv)
    {
      // set all bytes in the buffer to 0
      memset(packetBuffer, 0, NTP_PACKET_SIZE);
      // Initialize values needed to form NTP request
      // (see URL above for details on the packets)
    
      packetBuffer[0] = 0b11100011;   // LI, Version, Mode
      packetBuffer[1] = 0;     // Stratum, or type of clock
      packetBuffer[2] = 6;     // Polling Interval
      packetBuffer[3] = 0xEC;  // Peer Clock Precision
      // 8 bytes of zero for Root Delay & Root Dispersion
      packetBuffer[12]  = 49;
      packetBuffer[13]  = 0x4E;
      packetBuffer[14]  = 49;
      packetBuffer[15]  = 52;
    
      // all NTP fields have been given values, now
      // you can send a packet requesting a timestamp:
      Udp.beginPacket(ntpSrv, 123); //NTP requests are to port 123
    
      Udp.write(packetBuffer, NTP_PACKET_SIZE);
    
      Udp.endPacket();
    }
    

    Observe Again

    Open up the Serial Monitor and check it out. You should connect to your WiFi network then you'll see reports with the time in the UNIX standard. If it doesn't connect make sure your ssid and pass are correct.

    Wunderground 

    Wunderground is a weather reporting service that supports the use of personal weather stations or PWS. We'll be using that PWS service as the endpoint for our weather data. We'll need to register an account, create our PWS, then push data to it.

    Register

    Head over to this link to sign up for a Wunderground account. Once joined, head over to this link to create your personal weather station name and details. You'll need to type in your location so the service know where you are reporting the weather. Hit Verify Location on the bottom to go to the next page. There are two required fields on this page. Neighborhood is the name of your weather station so name it something descriptive to your area. The other field is Station Hardware. Since we build our weather stations, scroll down that list and select other. Once finished with the settings, press Submit to get your ID and KEY that we need for the next section. The information on the next page is what we'll put into the Arduino sketch so keep it open. If you ever need to find your ID and KEY again just navigate here.

    Upload

    Copy the code below but before uploading your code, replace the WID and WKEY with your ID and KEY from Wunderground. Also change ssid[] and pass[] to your WiFi network name and password so we can send data. After doing both of those changes, upload the code.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    #include "WiFiEsp.h"
    #include <Wire.h>
    #include "Seeed_BME280.h"
    #include <math.h>
    #include "Arduino.h"
    #include "SI114X.h"
    #include "SoftwareSerial.h"
    
    //Defines for Wunderground variables
    #define WID "WUNDERGROUND_ID"
    #define WKEY "WUNDERGOUND_KEY"
    #define getPayload "/weatherstation/updateweatherstation.php?ID="  WID  "&PASSWORD="  WKEY  "&dateutc=now&tempf=%s&humidity=%s&baromin=%s&UV=%s&action=updateraw"
    
    
    //Create serial communication object
    SoftwareSerial Serial1(8, 9); // RX, TX
    
    //Wifi settings
    char ssid[] = "YOURWIFINETOWRK";       // your network SSID (name)
    char pass[] = "YOURPASSWORD";      // your network password
    int status = WL_IDLE_STATUS;     // the Wifi radio's status
    
    //Server URL for later
    char server[] = "weatherstation.wunderground.com";
    
    //Storage for getPayload
    char buf[200];
    
    
    // Initialize the Ethernet client object
    WiFiEspClient client;
    
    // Construct sensor objects
    SI114X sunlightSensor;
    BME280 bme280;
    
    // Variables for weather measurements
    float temperature;
    float humidity;
    float pressure;
    float uv;
    
    // Letter version of the variables from above
    char cTemp[8];
    char cHum[8];
    char cPress[8];
    char cUv[8];
    
    // Variable to hold time
    unsigned long previousMillis = 0;
    
    // Milliseconds to wait between updates.
    const long interval = 2 * 60 * 1000L;
    
    void setup() {
      // Initialize serial for debugging
      Serial.begin(9600);
      // Initialize serial for ESP module
      Serial1.begin(9600);
      // Initialize ESP module
      WiFi.init(&Serial1);
    
      // Initialize sunlight sensor
      while (!sunlightSensor.Begin()) {
        Serial.println("Sunlight Error");
        delay(1000);
      }
    
      // Initialize BME280 sensor
      if (!bme280.init()) {
        Serial.println("BME Error");
      }
    
      // Attempt to connect to WiFi network
      while (status != WL_CONNECTED) {
        Serial.print("Attempting to connect to WPA SSID: ");
        Serial.println(ssid);
        // Connect to WPA/WPA2 network
        status = WiFi.begin(ssid, pass);
      }
    
      // You're connected now, so print out the data
      Serial.println("You're connected to the network");
    
      // Print out all info about wifi connection
      printWifiStatus();
    
      // Run updateWeather function
      updateWeather();
      // Run sendData function
      sendData();
    }
    
    void loop() {
      // Store the current time
      unsigned long currentMillis = millis();
    
      // Check to see if enough time has passed
      if (currentMillis - previousMillis >= interval) {
        // Save the last time data was sent
        previousMillis = currentMillis;
    
        // Run updateWeather function
        updateWeather();
        // Run sendData function
        sendData();
      }
    
      // While there is a connection to the server
      while (client.available()) {
        // Store the response
        char c = client.read();
        // And print it to the serial console
        Serial.write(c);
      }
    
      // If the server's disconnected, stop the client:
      if (!client.connected()) {
        // Prints debugging messages
        //Serial.println();
        //Serial.println("disconnecting from server.");
        // Disconnect the server
        client.flush();
        client.stop();
      }
    }
    
    void updateWeather() {
      // Read temp and convert to F
      temperature = bme280.getTemperature();
      temperature = temperature * 1.8 + 32;
    
      // Read humidity
      humidity = bme280.getHumidity();
    
      // Read Pressure and convert to inches of water
      pressure = bme280.getPressure() * 0.00040146;
    
      // Read UV and convert to UV Index range
      uv = (float)sunlightSensor.ReadUV() / 100;
    }
    
    void sendData() {
      // Convert float numbers to char arrays
      dtostrf(temperature, 4, 2, cTemp);
      dtostrf(humidity, 4, 2, cHum);
      dtostrf(pressure, 4, 2, cPress);
      dtostrf(uv, 4, 2, cUv);
    
      // Try and send the data
      if (client.connect(server, 80)) {
        // Make a HTTP request:
        sprintf(buf, getPayload, cTemp, cHum, cPress, cUv);
        client.println(String("GET ") + buf + String(" HTTP/1.1"));
        //Serial.println(String("GET ") + buf + String(" HTTP/1.1"));
        client.println("Host: weatherstation.wunderground.com");
        client.println("Connection: close");
        client.println();
      }
    
    }
    void printWifiStatus()
    {
      // Print the SSID of the network you're attached to
      Serial.print("SSID: ");
      Serial.println(WiFi.SSID());
    
      // Print your WiFi shield's IP address
      IPAddress ip = WiFi.localIP();
      Serial.print("IP Address: ");
      Serial.println(ip);
    
      // Print the received signal strength
      long rssi = WiFi.RSSI();
      Serial.print("Signal strength (RSSI):");
      Serial.print(rssi);
      Serial.println(" dBm");
    }
    

    Observe

    Open up the Serial Monitor and you'll see a couple of steps. The first is the initialization of the WiFi module. Then the connection to your local WiFi network. Once, you're online the weather station will attempt to send weather data to Wunderground. Assuming your ID and Key are correct you'll see a success message and within a few minutes you can visit your PWS page to see your weather data.

    Get Weather Data

    Start 

    This project will allow you to request and display weather data from anywhere you want.

    Modules 

    Gather the following parts to complete this project.

    Parts

    All Parts x Qty
    LCD Display x 1
    WiFi x 1
    Button x 1
    Cable x 3

    WiFi Module 

    Take a cable and unwrap it. Plug one side into the WiFi socket and the other into any Digital socket.

    Parts needed
    Take a cable
    Unwrap it
    Plug one side into the WiFi socket
    The other into any Digital socket

    Upload

    Upload the following code. The example below uses the D8 digital pin.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    #include "WiFiEsp.h"
    #include "SoftwareSerial.h"
    
    //Creates serial communication object
    SoftwareSerial Serial1(8, 9); // RX, TX
    
    void setup() {
      // initialize serial for debugging
      Serial.begin(9600);
      // initialize serial for ESP module
      Serial1.begin(9600);
      // initialize ESP module
      WiFi.init(&Serial1);
    
      // Print WiFi MAC address
      printMacAddress();
    }
    
    void loop()
    {
      // scan for existing networks
      Serial.println();
      Serial.println("Scanning available networks...");
      //function to scan for wifi networks
      listNetworks();
      //Wait 10 seconds before scanning agian
      delay(10000);
    }
    
    
    void printMacAddress()
    {
      // get your MAC address
      byte mac[6];
      //This is a unique hardware address for the wifi module
      WiFi.macAddress(mac);
    
      // print MAC address
      char buf[20];
      //replaces %02X with a MAC address byte
      sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
      Serial.print("MAC address: ");
      Serial.println(buf);
    }
    
    void listNetworks()
    {
      // scan for nearby networks
      int numSsid = WiFi.scanNetworks();
      //This runs when there isn't a wifi connection
      if (numSsid == -1) {
        Serial.println("Couldn't get a WiFi connection");
        //The Arduino will stay in this loop forever if it can't get a connection
        while (true);
      }
    
      // print the list of networks seen
      Serial.print("Number of available networks:");
      Serial.println(numSsid);
    
      // print the network number and name for each network found
      for (int thisNet = 0; thisNet < numSsid; thisNet++) {
        //Print the network number
        Serial.print(thisNet);
        Serial.print(") ");
        //Print network name
        Serial.print(WiFi.SSID(thisNet));
        //Print out the signal strength
        Serial.print("\tSignal: ");
        Serial.print(WiFi.RSSI(thisNet));
        Serial.print(" dBm");
        //Print password type
        Serial.print("\tEncryption: ");
        printEncryptionType(WiFi.encryptionType(thisNet));
      }
    }
    
    void printEncryptionType(int thisType) {
      // read the encryption type and print out the name
      switch (thisType) {
        case ENC_TYPE_WEP:
          Serial.print("WEP");
          break;
        case ENC_TYPE_WPA_PSK:
          Serial.print("WPA_PSK");
          break;
        case ENC_TYPE_WPA2_PSK:
          Serial.print("WPA2_PSK");
          break;
        case ENC_TYPE_WPA_WPA2_PSK:
          Serial.print("WPA_WPA2_PSK");
          break;
        case ENC_TYPE_NONE:
          Serial.print("None");
          break;
      }
      Serial.println();
    }
    

    Observe

    Open up the Serial Monitor and wait for the WiFi module to scan. It will print out a list of all the possible WiFi networks it can connect to. You should see the name of your normal WiFi network.

    Modify

    Before uploading this next bit of code, swap out ssid with your WiFi network's name and pass with its password.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    /*
     WiFiEsp example: UdpNTPClient
     Get the time from a Network Time Protocol (NTP) time server.
     Demonstrates use of UDP to send and receive data packets
     For more on NTP time servers and the messages needed to communicate with them,
     see http://en.wikipedia.org/wiki/Network_Time_Protocol
    */
    
    #include "WiFiEsp.h"
    #include "WiFiEspUdp.h"
    #include "SoftwareSerial.h"
    
    //Create channel for communication to wifi module
    SoftwareSerial Serial1(8, 9); // RX, TX
    
    //Wifi settings
    char ssid[] = "YOURWIFINETOWRK";       // your network SSID (name)
    char pass[] = "YOURPASSWORD";      // your network password
    int status = WL_IDLE_STATUS;     // the Wifi radio's status
    
    //Location of the time server
    char timeServer[] = "time.nist.gov";  // NTP server
    unsigned int localPort = 2390;        // local port to listen for UDP packets
    
    //Communication settings
    const int NTP_PACKET_SIZE = 48;  // NTP timestamp is in the first 48 bytes of the message
    const int UDP_TIMEOUT = 2000;    // timeout in miliseconds to wait for an UDP packet to arrive
    byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
    
    // A UDP instance to let us send and receive packets over UDP
    WiFiEspUDP Udp;
    
    void setup()
    {
      // initialize serial for debugging
      Serial.begin(9600);
      // initialize serial for ESP module
      Serial1.begin(9600);
      // initialize ESP module
      WiFi.init(&Serial1);
    
      // attempt to connect to WiFi network
      while ( status != WL_CONNECTED) {
        Serial.print("Attempting to connect to WPA SSID: ");
        Serial.println(ssid);
        // Connect to WPA/WPA2 network
        status = WiFi.begin(ssid, pass);
      }
    
      // you're connected now, so print out the data
      Serial.println("You're connected to the network");
    
      //Open the communication port
      Udp.begin(localPort);
    }
    
    void loop()
    {
      // send an NTP packet to a time server
      sendNTPpacket(timeServer);
    
      // wait for a reply for UDP_TIMEOUT miliseconds
      unsigned long startMs = millis();
      while (!Udp.available() && (millis() - startMs) < UDP_TIMEOUT) {
    
      }
      //Print out the information received
      Serial.println(Udp.parsePacket());
      //If packet is sent from the time server
      if (Udp.parsePacket()) {
        Serial.println("packet received");
        // We've received a packet, read the data from it into the buffer
        Udp.read(packetBuffer, NTP_PACKET_SIZE);
    
        // the timestamp starts at byte 40 of the received packet and is four bytes,
        // or two words, long. First, esxtract the two words:
    
        unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
        unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
        // combine the four bytes (two words) into a long integer
        // this is NTP time (seconds since Jan 1 1900):
        unsigned long secsSince1900 = highWord << 16 | lowWord;
        Serial.print("Seconds since Jan 1 1900 = ");
        Serial.println(secsSince1900);
    
        // now convert NTP time into everyday time:
        Serial.print("Unix time = ");
        // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
        const unsigned long seventyYears = 2208988800UL;
        // subtract seventy years:
        unsigned long epoch = secsSince1900 - seventyYears;
        // print Unix time:
        Serial.println(epoch);
    
    
        // print the hour, minute and second:
        Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
        Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
        Serial.print(':');
        if (((epoch % 3600) / 60) < 10) {
          // In the first 10 minutes of each hour, we'll want a leading '0'
          Serial.print('0');
        }
        Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
        Serial.print(':');
        if ((epoch % 60) < 10) {
          // In the first 10 seconds of each minute, we'll want a leading '0'
          Serial.print('0');
        }
        Serial.println(epoch % 60); // print the second
      }
      // wait ten seconds before asking for the time again
      delay(10000);
    }
    
    // send an NTP request to the time server at the given address
    void sendNTPpacket(char *ntpSrv)
    {
      // set all bytes in the buffer to 0
      memset(packetBuffer, 0, NTP_PACKET_SIZE);
      // Initialize values needed to form NTP request
      // (see URL above for details on the packets)
    
      packetBuffer[0] = 0b11100011;   // LI, Version, Mode
      packetBuffer[1] = 0;     // Stratum, or type of clock
      packetBuffer[2] = 6;     // Polling Interval
      packetBuffer[3] = 0xEC;  // Peer Clock Precision
      // 8 bytes of zero for Root Delay & Root Dispersion
      packetBuffer[12]  = 49;
      packetBuffer[13]  = 0x4E;
      packetBuffer[14]  = 49;
      packetBuffer[15]  = 52;
    
      // all NTP fields have been given values, now
      // you can send a packet requesting a timestamp:
      Udp.beginPacket(ntpSrv, 123); //NTP requests are to port 123
    
      Udp.write(packetBuffer, NTP_PACKET_SIZE);
    
      Udp.endPacket();
    }
    

    Observe Again

    Open up the Serial Monitor and check it out. You should connect to your WiFi network then you'll see reports with the time in the UNIX standard. If it doesn't connect make sure your ssid and pass are correct. If you see the time being reported then you're ready for the next step.

    Getting the Weather Data 

    Take a cable and unwrap it. Plug one side into the LCD socket and the other into any I2C socket. Take another cable and unwrap it. Plug one side into the button socket and the other into any Digital socket.

    Take a cable
    Unwrap it
    Plug one side into the LCD socket
    The other into any I2C socket
    Take another cable
    Unwrap it
    Plug one side into the button socket
    The other into any Digital socket

    Upload

    Upload the following code. Replace ssid with your network name from above as well as pass. Replace CITY and STATE with your location. This example uses D4 for the button.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    #include "WiFiEsp.h"
    #include "SoftwareSerial.h"
    #include "rgb_lcd.h"
    #include "OneButton.h"
    
    //Create objects
    rgb_lcd lcd;
    SoftwareSerial Serial1(8, 9); // RX, TX
    WiFiEspClient client;
    OneButton button(4, false);
    
    //Wifi settings
    char ssid[] = "YOURNETWORK";       // your network SSID (name)
    char pass[] = "YOURPASSWORD";      // your network password
    int status = WL_IDLE_STATUS;     // the Wifi radio's status
    
    // Weather Options
    // State can be country
    // Any spaces must be replcaed with '%20'
    // Example: "Buenos Aires" -> "Buenos%20Aries"
    #define CITY "Buffalo"
    #define STATE "NY"
    
    // Weather Variables
    int temp;
    int humidity;
    float pressure;
    String outside;
    
    // Server and URL
    char server[] = "query.yahooapis.com";
    #define url "/v1/public/yql?q=select%20item.condition.temp%2C%20atmosphere.humidity%2Catmosphere.pressure%2Citem.condition.text%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22" CITY "%2C%20" STATE "%22)&format=json"
    
    //Misc Variables
    int lcdPos = 0;
    int infoCount = 1;
    bool firstUpdate = true;
    
    //LCD color in RGB
    const int colorR = 50;
    const int colorG = 50;
    const int colorB = 25;
    
    //How long to wait before updating weather
    const long interval = 300000;
    
    //Time storable variables
    unsigned long currentMillis = 0;
    unsigned long previousMillis = 0;
    
    void setup() {
    
      //Intialize LCD
      lcd.begin(16, 2);
      lcd.setRGB(colorR, colorG, colorB);
      lcd.setCursor(0, 0);
      lcd.print("Setting WiFi");
    
      //Intialize button
      button.attachClick(buttonClick);
    
      // initialize serial for debugging
      Serial.begin(9600);
      // initialize serial for ESP module
      Serial1.begin(9600);
      // initialize ESP module
      WiFi.init(&Serial1);
    
      // attempt to connect to WiFi network
      while ( status != WL_CONNECTED) {
        Serial.print("Attempting to connect to WPA SSID: ");
        Serial.println(ssid);
        // Connect to WPA/WPA2 network
        status = WiFi.begin(ssid, pass);
      }
    
      // you're connected now, so print out the data
      Serial.println("You're connected to the network");
    }
    
    void loop() {
      //Update time and check button
      currentMillis = millis();
      button.tick();
    
      //Run is first time or enough time has passed
      if (currentMillis - previousMillis >= interval || firstUpdate) {
        Serial.println("Updating");
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Updating Weather...");
        //Try and send the data
        if (client.connect(server, 80)) {
          // Make a HTTP request:
          client.println(String("GET ") + url + String(" HTTP/1.1"));
          client.println("Host: query.yahooapis.com");
          client.println("Connection: close");
          client.println();
        }
    
        //Wait in this loop until you get a response
        while ( !client.available() ) {
          delay(1);
        }
    
        //Ask for the four variables
        switch (infoCount) {
          case 1:
            //Ask for Temperature
            if ( client.find("temp\":") ) {
              //Store the value
              temp = client.parseInt();
              //Display it to serial monitor
              Serial.print("Temperature: ");
              Serial.print(temp);
              Serial.println(" F");
              //Stop the connection
              client.flush();
              client.stop();
            }
            //Set to next variable
            infoCount++;
            //exits the switch case
            break;
    
          case 2:
          //Ask for Humidity
            if ( client.find("humidity\":") ) {
              //Store the value
              humidity = client.parseInt();
              //Display it to serial monitor
              Serial.print("Humidity: ");
              Serial.print(humidity);
              Serial.println(" %");
              //Stop the connection
              client.flush();
              client.stop();
            }
            //Set to next variable
            infoCount++;
            //exits the switch case
            break;
    
          case 3:
          //Ask for Pressure
            if ( client.find("pressure\":") ) {
              //Store the variable
              pressure = client.parseFloat();
              //Display it to serial monitor
              Serial.print("Pressure: ");
              Serial.print(pressure);
              Serial.println(" kPa");
              //Stop the connection
              client.flush();
              client.stop();
            }
            //Set to next variable
            infoCount++;
            //Set to next variable
            break;
    
          case 4:
          //Ask for a text description of the weather
            if ( client.find("text\":\"") ) {
              //Store the response
              outside = client.readStringUntil('"');
              //Display it to serial monitor
              Serial.print("Outside is ");
              Serial.print(outside);
              //Update time
              previousMillis = currentMillis;
              //Fake press button to show values
              buttonClick();
              firstUpdate = false;
              //Stop the connection
              client.flush();
              client.stop();
            }
            //Set count back to first one for next update
            infoCount = 1;
            break;
        }
        // if the server's disconnected, stop the client:
        if (!client.connected()) {
          Serial.println();
          Serial.println("disconnecting from server.");
          client.flush();
          client.stop();
        }
      }
    }
    
    //What to do when the button is pressed
    void buttonClick() {
      //We only have 3 states for the buttons
      if (lcdPos < 2) {
        lcdPos++;
      } else {
        //If the lcdPos is about to go out of bounds
        //Reset it
        lcdPos = 0;
      }
      //Clear all characters from the display
      lcd.clear();
      //Display info based on the lcdPos variable
      switch (lcdPos) {
        case 0:
        //Set cursor to the top left
          lcd.setCursor(0, 0);
          //Print a generic statement
          lcd.print("Showing Weather");
          //Set cursor to bottom left
          lcd.setCursor(0, 1);
          //Display CITY variable
          lcd.print(CITY);
          //exit switch case
          break;
    
        case 1:
        //Set cursor to the top left
          lcd.setCursor(0, 0);
          lcd.print("Humidity = ");
          //Set cursor after the previous statement
          lcd.setCursor(11, 0);
          //Print humidity variable
          lcd.print(humidity);
          //Set cursor to bottom left
          lcd.setCursor(0, 1);
          lcd.print("Tem = ");
          //Set cursor to bottom left
          lcd.setCursor(6, 1);
          //Print out the temperature variable
          lcd.print(temp);
          //Move cursor to after the variable
          lcd.setCursor(8, 1);
          //Print the unit
          lcd.print("F");
          //exit the switch case
          break;
    
        case 2:
        //Set cursor to the top left
          lcd.setCursor(0, 0);
          lcd.print("Press = ");
          //Set cursor after the previous statement
          lcd.setCursor(8, 0);
          //Print out the pressure variable
          lcd.print(pressure);
          //Move cursor after variable
          lcd.setCursor(13, 0);
          //Print the unit
          lcd.print("kPa");
          //Set cursor to bottom left
          lcd.setCursor(0, 1);
          //Print out the description
          lcd.print(outside);
          //exit the switch case
          break;
      }
    }
    

    Observe

    Open up the Serial Monitor to see every step the project is taking before the information gets to the LCD Display. Press the button to cycle through the conditions.

    Booting up
    Getting weather data
    Displaying conditions
    Displaying conditions
    Displaying location

    Modify

    Change the interval to change the update rate. It is expressed in milliseconds. 1 minute is 60000 milliseconds.

    Chainable RGB LED

    Start 

    The Chainable RGB LED module has two universal sockets. One for data in and one for data out. You are able to control many of these modules with just one connection to the Arduino.

    Both sides
    IN side
    OUT side

    Modules 

    Gather the following parts to complete this project.

    Parts

    All Parts x Qty
    RGB LED x 2
    Potentiometer x 1
    Cable x 3

    Chainable RGB LED setup 

    Take a cable and unwrap it. Plug one side into the IN socket of a chainable RGB LED and the other into the D8 socket.

    Parts needed
    Take a cable
    Unwrap it
    Plug one side into the IN socket of a chainable RGB LED
    The other into the D8 socket

    Upload

    Upload the following code.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include <ChainableLED.h>
    
    //Amount of chained LEDs
    #define NUM_LEDS  2
    
    //Create led object
    ChainableLED leds(8, 9, NUM_LEDS);
    
    void setup()
    {
      //initialize LEDs
      leds.init();
    }
    
    //Variable to keep track of which led to update
    byte pos = 0;
    
    void loop()
    {
      //loop through each led
      for (byte i=0; i<NUM_LEDS; i++)
      {
        //Toggles between the two leds
        if (i==pos)
          //set led to red
          leds.setColorRGB(i, 255, 0, 0);  
        else
          //set led to blue
          leds.setColorRGB(i, 0, 0, 255);
      }
      //Wait a small time so the leds don't flicker too fast
      delay(250);
    }
    

    Observe

    The RGB LED on the module will be RED.

    Red RGB LEDRed RGB LED

    Modify

    Take a cable and unwrap it. Plug one side into the OUT socket of the first RGB led and the other side into the IN socket of the second RGB led. Now one led will be red while the other will be blue.

    Take another cable
    Unwrap it
    Plug one side into the OUT socket of the first RGB LED
    The other side into the IN socket of the second RGB LED
    The first is red and the second is blue

    Experiment

    The color is set via the leds.setColorRGB() function. It takes four arguments. What led you'd like to change in a chain, the red value, green value, and blue value. The color (255,0,0) is all red while (0,0,255) is all blue. Each color channel goes from 0 to 255. Using these 3 color channels you can create over 16 million different colors! Check out the color picker here to find your favorite and display it on the leds.

    Color Spaces 

    Red, Green, and Blue values (RGB) are one way to express colors or one color space. There are a couple other ones you might run into. If you've ever changed a color printer cartridge, you won't find RGB you'll find Cyan, Magenta, Yellow and Black which create the CMYK color space. Black is abbreviated with a 'K' because old school printing plates would have to be lined up using a 'key' that would be in black. Hue, saturation, and brightness are another color space. The color is set via the hue value, which usually range from 0 to 360. That range lines up with the degrees found in a circle. The primary colors red, yellow, and blue are space evenly apart at 120 degree intervals. Saturation is the amount of color expressed as a percent. 0 degrees hue would be red and at 100% saturation you have a very red .... red while at 0% saturation you'd have no red. The only colors that exist at 0% saturation are what would be found on a gray scale. The last number is brightness also expressed as a percent. 0% brightness displays black while 100% displays white. So for the red color found on the Arduino you want an HSB value around (0,90,40).

    RGB Color SpaceRGB Color Space
    HSB Color SpaceHSB Color Space

    HSB LEDs

    We can set the chainable leds using the HSB color space. It's easier to change the color using code when you just need to change one value, the hue, to get a drastic color change. To show a rainbow effect we'd just need one variable and cycle it from 0 to 360. To do this in RGB values we'd need 3 variables and cycles those from 0 to 255 in every permutation. The leds.setColorHSB() function takes 4 arguments. The first is the position of the led in the chain, and the other three are the hue, saturation, and brightness; all ranging from 0 to 1. Check out the code below to see the HSB function in action.

    Upload

    Upload the following code.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <ChainableLED.h>
    
    //Amount of chained LEDs
    #define NUM_LEDS  2
    
    //Create led object
    ChainableLED leds(8, 9, NUM_LEDS);
    
    void setup()
    {
      //initialize LEDs
      leds.init();
    }
    
    //Color variables
    float hue = 0.0;
    float amount = 0.025;
    
    //Time to wait or switch color
    int delayTime = 50;
    
    //Variable to store color changing direction
    boolean up = true;
    
    
    void loop()
    {
      //loop through each led
      for (byte i=0; i<NUM_LEDS; i++)
      //Change color based on HSL value
        leds.setColorHSB(i, hue, 1.0, 0.5);
        //Wait for a time
      delay(delayTime);
    
    //If up is true hue increases
      if (up)
        hue+= amount;
    //If up is false hue decreases
      else
        hue-= amount;
    
    //If hue is the highest it can be, up is set false
      if (hue>=1.0 && up)
        up = false;
    //If hue is the lowest it can be, up is set true
      else if (hue<=0.0 && !up)
        up = true;
    }
    

    Observe

    The LEDs loop through the colors of the rainbow.

    Color changing LEDs
    Color changing LEDs

    Modify

    Changing the amount variable will change how many colors are shown. Changing delayTime will make the color changes faster or slower.

    Adding Some Controls 

    Take a cable and unwrap it. Plug one side into the rotary potentiometer socket and the other into the A0 socket.

    Take a cable
    Unwrap it
    Plug one side into the rotary potentiometer socket
    The other into the A0 socket

    Upload

    Upload the following code.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    #include <ChainableLED.h>
    
    //Number of chainable LEDs
    #define NUM_LEDS  2
    
    //Socket with the dial plugged in it
    #define dialSocket A0
    
    //color variables
    float hue = 0.0;
    float saturation = 0.9;
    float brightness = 0.5;
    
    //Variables to store dial values
    int currentValue = 0;
    int previousValue = 0;
    
    //Variable to count waiting periods
    int count = 0;
    
    //Creates LED object
    ChainableLED leds(8, 9, NUM_LEDS);
    
    void setup()
    {
      //Initialize LEDs
      leds.init();
    
      //Start Serial Communication
      Serial.begin(9600);
    }
    
    void loop()
    {
      //Store the dial value
      currentValue = analogRead(dialSocket);
    
      //If the value has changed
      if (currentValue != previousValue) {
        //Reset count variable
        count = 0;
        //If it hasn't changed...
      } else {
        //Increase count
        count++;
        //Wait a little before checking again
        delay(80);
      }
    
    //If the count variable is bigger than 2
      if (count > 2) {
        //get dial value and convert to a hue
        hue = mapfloat(currentValue, 0, 1024, 0, 1);
        //Print that out to the Serial Monitor
        Serial.println(hue);
        //Set
        leds.setColorHSB(0, hue, saturation, brightness);
        leds.setColorHSB(1, hue, saturation, brightness);
      }
    
      previousValue = currentValue;
    }
    
    float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
    {
      return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }
    

    Observe

    Turn the dial than wait a moment. The color will change based on the dial's position. You could map the color value instantly but that introduces some flickering.

    Color changing based on dial reading
    Color changing based on dial reading
    Color changing based on dial reading

    Modify

    Try changing the saturation and brightness variables to be able to scroll through more colors.

    Super Simon

    Start 

    Using the additional buttons in Weather Station 2 we'll be creating a closer representation of the classic game Simon.

    Modules 

    Gather the following parts to complete this project.

    Parts

    All Parts x Qty
    Button x 3
    Touch x 1
    RGB LED x 1
    Cable x 5

    Buttons Buttons Everywhere 

    We'll be using a total of four buttons. Three are the standard push buttons while one is the touch button. Our first step is placing the buttons in an up, down, left, and right pattern. And then making sure they are connected the right way.

    Take a cable and unwrap it. Plug one side into a button socket and the other into any Digital socket but it is recommended to leave the D8 socket free for the RGB LED. Repeat for the remaining 3 modules. Use a piece of tape and arrange the buttons and touch module in a + pattern.

    All the parts you'll need
    Take a cable...
    ... and unwrap it
    Plug one side into a button socket
    ... and the other into any Digital socket
    All four in the '+' pattern

    Upload

    Copy the following code. Change out the place holder variable assignments with the sockets you chose to use.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    //Library we use to help with button presses
    #include <OneButton.h>
    
    // Button/Socket variables to be changed
    #define upSocket 4
    #define downSocket 5
    #define leftSocket 6
    #define rightSocket 7
    
    //Creating the button objects
    OneButton upButton(upSocket, false);
    OneButton downButton(downSocket, false);
    OneButton leftButton(leftSocket, false);
    OneButton rightButton(rightSocket, false);
    
    void setup() {
      // Start serial for debugging
      Serial.begin(9600);
    
      // Create events for buttons
      upButton.attachClick(upClick);
      downButton.attachClick(downClick);
      leftButton.attachClick(leftClick);
      rightButton.attachClick(rightClick);
    
    }
    
    void loop() {
      // Update button states
      upButton.tick();
      downButton.tick();
      leftButton.tick();
      rightButton.tick();
    }
    
    // Button Press functions
    //Run when the up button is pressed
    void upClick() {
      //Print out a statement to the Serial Monitor
      Serial.println("Up Button Pressed");
    }
    
    //Run when the down button is pressed
    void downClick() {
        //Print out a statement to the Serial Monitor
      Serial.println("Down Button Pressed");
    }
    
    //Run when the left button is pressed
    void leftClick() {
        //Print out a statement to the Serial Monitor
      Serial.println("Left Button Pressed");
    }
    
    //Run when the right button is pressed
    void rightClick() {
        //Print out a statement to the Serial Monitor
      Serial.println("Right Button Pressed");
    }
    
    

    Observe

    Open up the Serial Monitor. Press the button in the down position. You should see a message saying you've pressed the down button. If nothing or the wrong button message appears double check the sockets you put in the socket variables and re-upload. Check each button and its message.

    Pressing down
    Correct serial message
    Pressing right
    Correct serial message
    Pressing up
    Correct serial message
    Pressing left
    Correct serial message

    Adding Some Color 

    Take a cable and unwrap it. Plug one side into the IN socket of a chainable RGB LED and the other into the D8 socket.

    Take a cable
    Unwrap it
    Plug one side into the IN socket of a chainable RGB LED
    The other into the D8 socket

    Upload

    Copy the following code. Replace the button variables with yours from above. Then upload the code.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    //Library we use to help with button presses and RGB LED
    #include <OneButton.h>
    #include <ChainableLED.h>
    
    //Number of RGB LED modules
    #define NUM_LEDS  1
    
    // Button/Socket variables to be changed
    #define upSocket 4
    #define downSocket 5
    #define leftSocket 6
    #define rightSocket 7
    
    //RGB Led object
    ChainableLED led(8, 9, NUM_LEDS);
    
    //Creating the button objects
    OneButton upButton(upSocket, false);
    OneButton downButton(downSocket, false);
    OneButton leftButton(leftSocket, false);
    OneButton rightButton(rightSocket, false);
    
    void setup() {
      // Start serial for debugging
      Serial.begin(9600);
    
      //Intialize the led and set to white
      led.init();
      led.setColorRGB(0, 255, 255, 255);
    
    
      // Create events for buttons
      upButton.attachClick(upClick);
      downButton.attachClick(downClick);
      leftButton.attachClick(leftClick);
      rightButton.attachClick(rightClick);
    
    }
    
    void loop() {
      // Update button states
      upButton.tick();
      downButton.tick();
      leftButton.tick();
      rightButton.tick();
    }
    
    //Button Press functions
    //Run when the up button is pressed
    void upClick() {
      //Print out a statement to the Serial Monitor
      Serial.println("Up Button Pressed");
      //Sets LED to red
      led.setColorRGB(0, 255, 0, 0);
    }
    
    //Run when the down button is pressed
    void downClick() {
      //Print out a statement to the Serial Monitor
      Serial.println("Down Button Pressed");
        //Sets LED to yellow
      led.setColorRGB(0, 255, 255, 0);
    }
    
    //Run when the left button is pressed
    void leftClick() {
      //Print out a statement to the Serial Monitor
      Serial.println("Left Button Pressed");
        //Sets LED to green
      led.setColorRGB(0, 0, 255, 0);
    }
    
    //Run when the right button is pressed
    void rightClick() {
      //Print out a statement to the Serial Monitor
      Serial.println("Right Button Pressed");
        //Sets LED to blue
      led.setColorRGB(0, 0, 0, 255);
    }
    
    

    Observe

    Now when you press each button it triggers the button event and will make the RGB LED light up a unique color.

    Button Color
    Up Red
    Down Yellow
    Left Green
    Right Blue
    White default color
    Down = Yellow(ish)
    Right = Blue
    Up = Red
    Left = Green

    Modify

    You can change the colors defined in the set led.setColorRGB() function for each button and thus putting your own spin on a time tested classic.

    Super Simon 

    With all the buttons in the correct sockets we can upload the full Super Simon code.

    Upload

    Copy the following code and again put in your button sockets. Then upload the code.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    //Library we use to help with button presses and RGB LED
    #include <OneButton.h>
    #include <ChainableLED.h>
    #include <LinkedList.h>
    
    //Set the button positions to numbers
    #define up 0
    #define down 1
    #define left 2
    #define right 3
    
    //Create the list that stores the pattern
    LinkedList<int> pattern;
    
    //Game states
    enum possibleStates {
      ADD,
      LISTEN,
      GAMEOVER
    };
    
    //Variable to hold the state
    int currentState = 0;
    
    //Number of RGB LED modules
    #define NUM_LEDS  1
    
    // Button/Socket variables to be changed
    #define upSocket 4
    #define downSocket 5
    #define leftSocket 6
    #define rightSocket 7
    
    //RGB Led object
    ChainableLED led(8, 9, NUM_LEDS);
    
    //Creating the button objects
    OneButton upButton(upSocket, false);
    OneButton downButton(downSocket, false);
    OneButton leftButton(leftSocket, false);
    OneButton rightButton(rightSocket, false);
    
    //Variable to hold loop information
    int patternCount = 0;
    
    void setup() {
      // Start serial for debugging
      Serial.begin(9600);
    
      //Intialize the led and set to white
      led.init();
      //Set color to white
      led.setColorRGB(0, 255, 255, 255);
    
    
      // Create events for buttons
      upButton.attachClick(upClick);
      downButton.attachClick(downClick);
      leftButton.attachClick(leftClick);
      rightButton.attachClick(rightClick);
    
      //Make the pattern actually random
      randomSeed(analogRead(0));
    
    }
    
    void loop() {
      // Update button states
      upButton.tick();
      downButton.tick();
      leftButton.tick();
      rightButton.tick();
    
      //This is used for debugging
      // Serial.println(currentState);
    
      //If the game is over then...
      if (currentState == GAMEOVER) {
        //Set led to white
        led.setColorRGB(0, 255, 255, 255);
        //Print out the score which is also the size the pattern was before
        Serial.print("Your score is ");
        Serial.println(pattern.size() - 1);
        //Stall the microcontroller
        while (true) {
          // Wait forever!
        }
      }
    
      //This state waits for button presses
      if (currentState == LISTEN) {
        //Print out debugging statements
        //Serial.println("Listening...");
        //If you've reached the end of the pattern
        if (patternCount == pattern.size()) {
          //Set pattern counter to 0
          patternCount = 0;
          //Set state to add to pattern
          currentState = ADD;
        }
      }
    
      //Add a direction to the pattern list
      if (currentState == ADD) {
        //Print out debugging message
        //Serial.println("Adding to pattern");
        //Wait a little
        delay(100);
    
        //Add a direction to the list
        pattern.add(random(0, 4));
    
        //Show Pattern
        displayPattern(500);
    
        //Set state to listen
        currentState = LISTEN;
      }
    
    }
    
    //Button Press functions
    //Runs when up button is pressed
    void upClick() {
      //Print debug message
      //Serial.println("Up Button Pressed");
      //If the up button was the one that was correct
      if (patternCorrect(up)) {
        //Increase your place in the pattern
        patternCount++;
        //Set color to Red
        led.setColorRGB(0, 255, 0, 0);
        //Hold for half a second
        delay(500);
        //Turn led off
        led.setColorRGB(0, 0, 0, 0);
        //If it wasn't the right button
      } else {
        //Set game to game over state
        currentState = GAMEOVER;
      }
    }
    
    //Runs when the down button is pressed
    void downClick() {
      //Print out a debugging message
      //Serial.println("Down Button Pressed");
      //If the down button was the correct one in the pattern
      if (patternCorrect(down)) {
        //Increase place in pattern list
        patternCount++;
        //Set led to Yellow
        led.setColorRGB(0, 255, 255, 0);
        //Hold for half a second
        delay(500);
        //Turn off LED
        led.setColorRGB(0, 0, 0, 0);
        //If down button was wrong....
      } else {
        //Set game to game over state
        currentState = GAMEOVER;
      }
    }
    
    //runs if the left button was pressed
    void leftClick() {
      //Print out a debugging message
      //Serial.println("Left Button Pressed");
      //If the left button was the correct one in the pattern
      if (patternCorrect(left)) {
        //Increase place in pattern
        patternCount++;
        //set led to Green
        led.setColorRGB(0, 0, 255, 0);
        //Hold for half a second
        delay(500);
        //Turn led off
        led.setColorRGB(0, 0, 0, 0);
        //If the left button was wrong
      } else {
        //Set game to game over state
        currentState = GAMEOVER;
      }
    }
    
    //Runs when the right button is pressed
    void rightClick() {
      //Print out a debugging message
      //Serial.println("Right Button Pressed");
      //If the right button was the correct one in the pattern
      if (patternCorrect(right)) {
        //Increase place in the pattern
        patternCount++;
        //Set LED to blue
        led.setColorRGB(0, 0, 0, 255);
        //Hold for half a second
        delay(500);
        //Turn LED off
        led.setColorRGB(0, 0, 0, 0);
        //If right button was not the right one
      } else {
        //Set game to game over state
        currentState = GAMEOVER;
      }
    }
    
    //Shows the current pattern in memory
    void displayPattern(int wait) {
      //Loop through every direction in the pattern
      for (int i = 0; i < pattern.size(); i++) {
        //Switch cases to pick which color to display
        switch (pattern.get(i)) {
          //Case for up
          case 0:
          //Set LED to Red
            led.setColorRGB(0, 255, 0, 0);
            //Break out of loop
            break;
          //Case for down
          case 1:
          //Set LED to Yellow
            led.setColorRGB(0, 255, 255, 0);
            //Break out of loop
            break;
          //Case for Left
          case 2:
          //Set LED to Green
            led.setColorRGB(0, 0, 255, 0);
            //Break out of loop
            break;
          //Case for right
          case 3:
          //Set LED to Blue
            led.setColorRGB(0, 0, 0, 255);
            //Break out of loop
            break;
        }
        //Wait for a while
        delay(wait * 2);
        //Turn LED off
        led.setColorRGB(0, 0, 0, 0);
        //Wait for half as much
        delay(wait);
      }
    }
    
    //Return true if the correct direction was pressed
    bool patternCorrect(int dir) {
      //Check if the button pressed is correct
      if (pattern.get(patternCount) == dir) {
        //If so return true
        return true;
        //If not
      } else {
        //Return false
        return false;
      }
    }
    
    

    Observe

    The RGB LED will flash with which color to press. An incorrect press will send the game into its GAMEOVER state, which prints out your score to the serial monitor and sets the LED to white. The game only restarts after the reset button is pressed. Every correct pattern entry extends the pattern one more.

    Modify

    Here is a template to download and cutout to make your Super Simon more like the original.

    PDF Template

    SVG Template

    AI Template

    Cutout template
    Assembled
    GAME OVER

    Experiment

    Try adding the speaker so that each color also has a unique noise.

    Further reading HTML and CSS

    Motivation 

    In the Weather Station part 2 project, you used HTML and CSS to describe to your web browser how to display the data. This section will provide a simple tutorial for revisiting that project and customizing the information and the way it is displayed.

    HTML

    HTML stands for Hypertext Markup Language and is one of the cornerstone technologies for the World Wide Web. HTML is used to structure documents (mostly web pages). It was invented in 1980 by Tim Berners-Lee. The language is composed of elements, which are represented by tags. For example, if you wanted to include an image on your webpage, you would use the image element, which is represented as the <img /> tag.

    In the early days, HTML was used both to structure a web page and to describe how it is presented. However, needing to contstantly modify large amounts of HTML in order to make small changes to the presentational layout proved to be unsustainable for rapidly-changing web pages. Based on the recommendations of the World Wide Web Consortium (W3C), CSS has been adopted to describe the presentation of web pages, allowing HTML to only provide the structure and content of the document.

    CSS

    CSS stands for Cascading Stylesheets and is a language used to describe the presentation of web pages as well as other documents (XML, SVG, etc). This separation allows things like fonts, color, and size to be determined by the style sheet, rather than having these design and presentation elements defined in the structure of the page itself. Like with HTML, specifications are maintained by the W3C.

    Weather Station Example 

    In the Weather Station part 2 project, the following HTML document was created:

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!DOCTYPE HTML>
    <html>
    <head>
    <style>
    </style>
    </head>
    <body>
    <h1>Your Weather Station</h1>
    <br>
    Requests received: 3<p id="date"></p><script>document.getElementById("date").innerHTML = 'Date: ' + Date();</script><p><br>
    Temperature: 78.75F<br>
    Humidity: 30.00%<br>
    Air Pressure: 100090.00Pa<br>
    UV Index: 0.02<br>
    </body>
    </html>
    
    

    This is a simple HTML document that follows a pretty standard structure, with the <head> , <style> , and <body> tags. The actual content of the document is also very simple, containing a <h1> heading of "Your Weather Station", the number of requests received (dynamically populated by the web server), the current date (populated by some javascript), and the weather data populated by our weather station.

    By adding CSS within the <style> and </style> tags, we can change the way this webpage appears. As an example,

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    body {
        color: red;
    }
    h1 {
        color: blue;
    }
    

    Placing the above CSS inside the <style> tag will change the <h1> ("Your Weather Station") text to blue, and the rest of the text to the color red.

    You can test these changes on your web browser and then, once you get the code as you like it, modify the code and upload it to your Uno board so the weather station displays the page in your browser the way you want it to look.

    Further learning 

    Mozilla Developer Network's documanation pages provide excellent tutorials and reference documentation on HTML, CSS, Javascript, and even server-side website programming.

    WS3Schools, a website that derives its name from W3C, but that has no official affiliation, provides great, free, and in-depth tutorials on HTML, CSS, Javascript, and other popular languages and frameworks used for building webpages.