17
Wi-Fi and the Cloud

  • Parts You'll Need for This Chapter
  • Adafruit Feather M0 Wi-Fi w/ATWINC1500 (soldered w/ PCB antenna)
  • USB cable (Type A to Micro-B)
  • Half-size or full-size breadboard
  • Assorted jumper wires
  • 220Ω resistors (×4)
  • 4.7kΩ resistors (×2)
  • 5 mm common-anode RGB LED
  • Piezo buzzer
  • 5V 1A USB port wall power supply (optional)
  • 4-digit 7-segment display with I2C backpack (1.2 inch or 0.56 inch, any color)
  • Wi-Fi network credentials (and optionally, router administrator access)

This is it, the final frontier (and chapter). Short of launching your Arduino into space, connecting it to the internet is probably the closest that you will get to making the entire world your playground. internet connectivity, in general, is an extremely complex topic; you could easily write entire volumes of books about the best way to interface the Arduino with the Internet of Things, or IoT, as it is now often called. Because it is infeasible to cover the multitude of ways you can interface your Arduino with the web, this chapter focuses on imparting some knowledge with regard to how network connectivity works with your Arduino (or any IoT device) and how you can use a Wi-Fi–enabled Arduino to both serve up web pages and interact with data from the cloud. Specifically, you will learn about traversing your network topology, how a web page is served, and how to interface with a third-party web-based application programming interface, or API.

The Web, the Arduino, and You

Explaining all the workings of the web is a bit ambitious for one chapter in a book, so for this chapter, you can essentially think of your Arduino's relation to the internet using the diagram shown in Figure 17-1.

First, you will work only in the realm of your local network. When working within your local network, you can talk to your Arduino via a web browser only if they are both connected to the same router (either wired or by Wi-Fi). Then, you will explore ways in which you can traverse your router to access functionality from your Arduino anywhere in the world (or at least anywhere you can get an internet connection).

Diagram of a simplified view of the web and your local network via a web browser.

Figure 17-1: A simplified view of the web and your local network

Networking Lingo

Before you get your feet wet with networking your Arduino, let's get some lingo straight. Following are words, concepts, and abbreviations that you will need to understand as you work through this chapter.

The Internet vs. the World Wide Web vs. the Cloud

What we often refer to as the web, the internet, and the cloud are actually all slightly different things, so to start off, it's worth understanding the minor differences between them, as these terms are frequently used interchangeably.

The internet is the actual physical network of interconnected devices around the world that all speak to each other. Originally conceptualized in the 1960s, the ARPANET was a project of ARPA, the Advanced Research Projects Agency in the United States. Universities around the world as well as scientific and research agencies built upon the ARPANET, slowly developing it into the internet that we recognize today. While the internet defined the physical layer and the protocols used to transmit data from point to point, it didn't encapsulate the idea of “websites” until the 1990s. Researchers at CERN, the European Organization for Nuclear Research, introduced the concept of hyperlinked pages, creating the World Wide Web that you surf today. The web is just one of many applications that run on top of the internet.

The cloud is a relatively new term that generally refers to the movement of networked services from local networks (intranets) up to the public interent. In the case of a business, this could mean moving away from on-site network file storage to using a service like Google Drive or Dropbox.

It also applies to application software. For example, whereas most software that you interact with used to exist on your local computer (Microsoft Word is one example), high-speed internet connectivity has made it possible to move many applications to the cloud. Google Docs allows you edit a Word document that is actually stored on a Google server (in the cloud) instead of on your desktop. The main advantage of cloud services is that they reduce dependence on local computing resources, and instead rely on remote server farms that can achieve incredible levels of data throughput by combining a lot of computing power under one roof.

IP Address

An Internet Protocol (IP) address is a unique address that identifies each device that connects to the internet. In the case of your home network, there are actually two kinds of IP addresses that you need to worry about: the local IP address and the global IP address. If your home or office has a router (like the one in Figure 17-1), everything within your local network has a local IP address that is visible only to other devices within your network. Your router/modem has one public-facing global IP address that is visible to the rest of the internet. If you want to move data between somewhere else on the internet and a device behind a router, you need to use network address translation (NAT).

Network Address Translation

There are not enough IP addresses to have one for every device in the world. Furthermore, users often do not want their computers and other networked devices to be visible to the rest of the world. For this reason, routers are used to create isolated networks of computers with local IP addresses. However, when you do want one of these machines to be accessible from the rest of the internet, you need to use NAT through the router. This allows a remote device to send a request to your router asking to talk to a device in your local network. When you connect your Arduino to the larger web later in this chapter, you will use a form of NAT.

MAC Address

MAC addresses, unlike IP addresses, are globally unique. (Well, they're supposed to be, but in practice, they often are not.) MAC addresses are assigned to every physical network interface and do not change. For instance, when you buy a computer, the Wi-Fi module inside has a unique MAC address, and the Ethernet adapter has a unique MAC address. This makes MAC addresses useful for identifying physical systems on a network. Device manufacturers must work with IEEE to obtain a reserved block of MAC addresses to be assigned to the devices that they build.

HTML

Hypertext Markup Language, or HTML, is the language of the web. To display a web page from your Arduino, you will write some simple HTML that creates buttons and sliders for sending data.

HTTP and HTTPS

Hypertext Transfer Protocol, or HTTP, defines the protocol for communicating across the World Wide Web, and is most commonly used in browsers. HTTP defines a set of header information that must be sent as part of a message across the web. This header defines how a web page will display in addition to whether the request was successfully received and acknowledged. HTTPS is HTTP over Secure Sockets Layer (or SSL); most of the web has moved to using this more secure standard that encrypts all data sent between clients and servers.

GET/POST

GET and POST define two ways for transferring information to a remote web server. If you've ever seen a URL that looks like jeremyblum.com/?s=arduino, you've seen a GET request. GET defines a series of variables following a question mark in the URL. In this case, the variable s is being set to Arduino. When the page receives this URL, it identifies this variable, performs the search, and returns the results page.

A POST is very similar, but the information is not transmitted in a visible medium through the URL. Instead, the same variables are transmitted transparently in the background. This is generally used to hide sensitive information or to ensure that a page cannot be linked to if it contains unique information.

DHCP

Dynamic Host Configuration Protocol, or DHCP, makes connecting devices to your local network a breeze. Odds are that whenever you've connected to a Wi-Fi (or wired) network, you haven't had to manually set an IP address at which the router can connect to you. So, how does the router know to route packets to you? When you connect to the network, a DHCP request is initiated with the router that allows the router to dynamically assign you an available IP address. This makes network setup much easier because you don't have to know about your network configuration to connect to it. However, it can make talking to your Arduino a bit tougher because you need to find out which IP address it was assigned.

DNS

DNS stands for Domain Name System. Every website that you access on the internet has a unique IP address that is the location of the server on the web. When you type in google.com, a DNS server looks at a table that informs it of the IP address associated with that “friendly” URL. It then reports that IP address back to your computer's browser, which can, in turn, talk to the Google server. DNS allows you to type in friendly names instead of remembering the IP addresses of all your favorite websites. DNS is to websites as your phone's contact list is to phone numbers.

Clients and Servers

In this chapter, you learn how to make a Wi-Fi–enabled Arduino act as either a client or a server. All devices connected to the internet are either clients or servers, though some actually fill both roles. A server does as the name implies: When information is requested from it, it serves this information up to the requesting computer over the network. This information can come in many forms: as a web page, database information, an email, or a plethora of other things. A client is the device that requests data and obtains a response. When you browse the internet from your computer, your computer's web browser is acting as a client.

Your Wi-Fi–Enabled Arduino

For all the examples in this chapter, you will use an Adafruit Feather M0 Wi-Fi with ATWINC1500 (hereafter simply referred to as the Feather board, Feather, or Arduino). Because of the complexity involved in Wi-Fi connectivity and networking software, it is only feasible for this book to pick this one platform and focus on its use. However, many other Arduinos with Wi-Fi connectivity are available to buy. Because they all use slightly different Wi-Fi chipsets, they will not all work in an identical manner. This chapter will therefore try to explain general concepts in such a way that you can easily extrapolate them to similar hardware if you are not using this exact board.

This Feather board uses an Atmel Cortex M0+ microcontroller, in place of the AVR microcontrollers that you've used in all the previous chapters. The Cortex microarchitecture delivers considerably more horsepower than the AVR chips, and is generally more complex to work with. Thankfully, the Arduino IDE and compiler effectively mask that complexity by abstracting the hardware peripherals for you. You'll program it no differently from any other Arduino that you've worked with.

Controlling Your Arduino from the Web

First, you will configure your Arduino to act as a web server. Using some HTML forms, and the provided Wi-Fi libraries, you will have your Arduino connect to a Wi-Fi network and serve a web page that you can access to control some of its I/O pins. You will expose buttons to the web interface for toggling the colors in an RGB LED and controlling a speaker's frequency. The program that you write for this purpose is extensible, allowing you to add control of additional devices as you become more comfortable working with the Arduino.

Setting Up the I/O Control Hardware

If your Feather board came with its pins unsoldered, then solder them on and install it into a breadboard. You will set up some test hardware that is connected to your Arduino server so that you can control it from the web. For this example, you are connecting an RGB common-anode LED and a piezo buzzer or ordinary speaker. Wire it up as shown in Figure 17-2. You need to connect your RGB LED to pins 5, 10, and 11. The piezo buzzer or speaker should connect to pin A5 (analog inputs can also be used as digital outputs). Don't forget to use current limiting resistors for both your piezo buzzer and your LED - 150Ω or 220Ω will work fine.

Image of the Arduino Wi-Fi “server” wired to RGB LED Pins 5, 10, and 11, and piezo buzzer connected to Pin A5.

Figure 17-2: Arduino Wi-Fi “server” wired to RGB LED and piezo buzzer

Created with Fritzing

Recall that this Feather board operates at 3.3V logic levels. By connecting the LED's common anode pin to the “USB” pin on the Feather, you are connecting to the 5V supply provided by the USB interface. This enables you to continue using the same current limiting resistor values that you have previously calculated as sufficient for an LED running off a 5V supply. If you were to run the LED off the Feather's “3V” pin, you'd need to reduce the resistor values to achieve the same brightness levels.

It's worth familiarizing yourself with the hardware design of this board — there are details on the Adafruit website, at blum.fyi/feather-wifi-pinout. Specifically, note that Digital pin 9 is also pin A7 in the Arduino software and is connected to a resistor divider for monitoring battery voltage. Hence, it may “float” at a voltage, and is therefore not ideal for driving the LED. pins 2, 4, 7, and 8, along with the hardware SPI pins, are used to communicate with the onboard Wi-Fi chipset.

Preparing the Arduino IDE for Use with the Feather Board

In the last chapter, you learned how to add support for third-party boards to the Arduino IDE. Recall that this involved two steps: first, adding the board URL to the IDE preferences, and second, searching for and adding the specific board support package from the Boards Manager window. See Figure 16-2 and Figure 16-3 if you need a refresher.

You've already added the Adafruit boards URL in Chapter 16, “Bluetooth Connectivity,” so you do not need to do that again. If you skipped Chapter 16, go back to the section, “Adding Support for Third-Party Boards to the Arduino IDE,” and follow the instructions to add a new boards URL. Then, go to Tools ➢ Board ➢ Boards Manager as you did before. This time, instead of searching for Adafruit AVR boards (which included the 32U4 that you used in the last chapter), you need to search for SAMD. Install both of the Arduino and Adafruit SAMD support packages, as highlighted in Figure 17-3. Then, restart the IDE. You should now be able to select Adafruit Feather M0 from the list of boards.

Screenshot of the Boards Manager page to enable Arduino and Adafruit SAMD board support installation.

Figure 17-3: Arduino and Adafruit SAMD board support installation

This board may also require drivers to be installed on Windows. You should already be good to go if you installed the Adafruit drivers in the last chapter. If you didn't, just download and install them from blum.fyi/adafruit-windows-drivers.

Finally, you need to install the Arduino library for interacting with the WINC1500 that is integrated onto your Feather board. Go to SketchInclude LibraryManage Libraries and search for WINC1500. Install the WiFi101library as shown in Figure 17-4.

Ensuring the Wi-Fi Library Is Matched to the Wi-Fi Module's Firmware

The WINC1500 library that is mounted onto your Feather board is its own little computer that manages all the heavy lifting related to Wi-Fi connectivity. It contains its own microcontroller running its own firmware to manage this task. The library that you just installed allows the main microcontroller (the M0+ that you will be programming with the IDE) to talk to the microcontroller inside the WINC1500. In order for that communication to work properly, the library running on the M0+ must be speaking the same language as the WINC1500. It's possible, therefore, for the firmware running on the WINC1500 to be incompatible with the version of the library that you've just installed. If you purchased this board a long time ago, but just installed the WiFi101 library, then the library may be expecting to talk to a WINC1500 with newer firmware.

Screenshot of the Library Manager page to enable WiFi101 library installation.

Figure 17-4: WiFi101 library installation

Checking the WINC1500's Firmware Version

Before you proceed, it's worthwhile to run a simple test script that will connect to the WINC1500 and attempt to query its firmware version to see if it matches what the library is expecting. If the WINC1500 fails to reply, or reports an outdated firmware version, then you need to update its firmware.

Load the firmware-checking example sketch by going to File ➢ Examples ➢ WiFi101 ➢ CheckWifi101FirmwareVersion. You need to add one line to the setup function in this example sketch to set the proper pins for the Wi-Fi module on the Feather board. Above the serial.begin() line, add the following:

WiFi.setpins(8,7,4,2);

This tells the library that the Chip Select, Interrupt, Reset, and Enable lines to the Wi-Fi module are connected to digital pins 8, 7, 4, and 2 on the M0+ microcontroller. Once you've added that line, upload the code to your Feather, and launch the serial monitor. If it tells you that the Library version matches the loaded firmware version, then you're all set! If you get a version mismatch like the one shown in Figure 17-5, then you need to update the firmware on the Wi-Fi module.

Updating the WINC1500's Firmware

If you received a firmware mismatch error, then you need to update the firmware that is running on the Feather's Wi-Fi module before you try to utilize it. Adafruit provides excellent step-by-step instructions on how to perform the upgrade, which you can find at blum.fyi/feather-wifi-update. Follow these instructions, then load the firmware-checking sketch again (with the added lines to set the right pins). This time, it should report a match with the required firmware version.

Writing an Arduino Server Sketch

You'll approach the challenge of building your Arduino web server code in four steps. First, you'll get your Feather to connect to your Wi-Fi network and obtain an IP address. Second, you'll develop the simplest web server possible, so that you can see what HTTP requests look like, how to parse them, and how to respond to them. Third, you'll design a simple webpage that you want your Arduino to display. Finally, you'll integrate the web page and some hardware control code into your web server sketch to make a fully functional project.

Screenshot depicting the mismatch of added pins and the loaded firmware version.

Figure 17-5: Added pins and firmware mismatch

Connecting to the Network and Retrieving an IP Address via DHCP

Thanks to the wonders of DHCP, securely connecting to a Wi-Fi network with the Arduino Wi-Fi library is a snap. Before you look at the code, I'll explain what is going to happen. At the top of your program, you should include the serial Peripheral Interface (SPI) and Wi-Fi libraries for interfacing with the onboard Wi-Fi module. You'll create a global variable for tracking the Wi-Fi connection status, and constants for holding the Wi-Fi network name (also known as the network's SSID) and password. This assumes that you will be connecting to a Wi-Fi network with modern WPA or WPA2 security (nearly any home network you may encounter).

Within the setup(), you will set the pins for the Wi-Fi chipset and start the Wi-Fi connection with the specified network credentials. As you've done in previous chapters, you'll use while(!serial); to halt program execution until the USB serial monitor is open. Once you open the serial monitor, the Feather will connect to the Wi-Fi network, and report the IP address that it was assigned via DHCP. Listing 17-1 shows this program.

Load Listing 17-1 onto your Feather, being sure to enter your network credentials at the top of the sketch. Note that the sketch is also set up to control the onboard red LED. When the connection to the Wi-Fi network succeeds, the red LED will illuminate. Open the serial monitor. You should see the connection occur, and an IP address should be printed as shown in Figure 17-6. My Wi-Fi network is called “Exploring Arduino” with a password of “voltaire” (an 18th-century philosopher who advocated for free speech—an important tenet of the modern World Wide Web!).

Once your Arduino is connected, you can ping it to confirm that it is responding to local network requests. On a computer that is on the same network as your Arduino, open your command prompt or terminal application and type ping XXX.XXX.XXX.XXX (replacing the X's with your Arduino's reported IP address) to confirm that the Arduino replies to the ping request. This command is the same on all modern operating systems. Figure 17-6 shows the Arduino replying to the ping with a latency of 2 to 3 milliseconds.

Screenshot displaying how the Arduino is connected to Wi-Fi and responding to ping requests.

Figure 17-6: Arduino connected to Wi-Fi and responding to ping requests

Writing the Code for a Bare-Minimum Web Server

Now that your Arduino is connecting to Wi-Fi, you can implement a very simple HTTP server that can listen for incoming requests and reply with an acknowledgement.

At a bare minimum, a server just needs to listen for incoming requests, and send back a reply once the full request has been received. To start, the server doesn't even have to understand these requests, or parse them. It just needs to know that they've been received and send back an empty page.

Inside the main loop(), the Arduino waits for a client to connect to its server. Once a client is connected (a browser visiting the web page causes this connection), the Arduino web server reads incoming data until the HTTP request has been fully received (indicated by the receipt of an empty line). To better understand what the request sent by your browser looks like, you'll print out this request to the serial monitor as it comes in. Once the HTTP request is received in full, the Arduino replies with a “200 response” to the browser to indicate that the request was successful.

In addition to the response code, the server also needs to confirm to the browser that it is speaking the same HTTP “language” and in what format the returned data will be provided. The complete response header looks like this:

HTTP/1.1 200 OK
Content-Type: text/html

This header must be followed by a blank line, and then the content of an HTML page. For this bare-minimum test, you can just return the bare header, and the browser will show a blank page. Listing 17-2 shows this bare-minimum server code.

As with Listing 17-1, you need to fill in your Wi-Fi credentials. The main loop works in the way it was described earlier. Each time your browser sends a request to the Arduino, it is parsed in the while (client.connected()) loop. You read the incoming data one line at a time. Note the use of a do…while() loop in place of the more standard while() loops that you've used up to this point. The only difference between these two loops is that do…while() loops always do one iteration through the loop body before checking the conditions for loop continuation. This is useful in this scenario because you are checking if the current line of received data has been fully received (indicated by the presence of a carriage return and newline character at the end). If you checked the given condition at the beginning of the loop, the loop would never execute because an empty string doesn't end with those characters. You first need to ensure that at least one incoming character makes it into the string being checked.

As each full line is received, it is printed out to the serial monitor. Once an empty line is received (incoming_line == " "), you know the complete request has come in and you can reply to it with the 200 response code header described earlier in this section.

Load Listing 17-2 onto your Feather and open the serial monitor. Then, open a web browser and navigate to the URL provided by your Arduino's serial monitor. Note that the computer must be on the same network (either wired or connected via Wi-Fi). It should load a blank white page (because you only sent back a 200 response code with no data). You should see the requests come into your Arduino's serial monitor. You may receive more than one request per page load, as your browser will likely try to request the favicon for the webpage (the tiny icon that is used when you save a bookmark) from the standard location of /favicon.ico. Figure 17-7 shows an example of incoming data to your Arduino server.

When you're just loading the “root” page, the GET request shows a path of /, which is highlighted in Figure 17-7. Once you add a form to this page, clicking elements on the page will pass GET arguments that will transform that line in the HTTP data to look like this: GET /?L=10 HTTP/1.1. By parsing that line, you'll be able to tell what was clicked, and take an action. For example, L=10 will tell your Arduino to toggle the LED on pin 10. Next, you'll construct the HTML form that will enable this functionality.

Screenshot displaying the output of Arduino running a server and receiving requests.

Figure 17-7: Arduino running a server and receiving requests

Open up your favorite text editor (I recommend Sublime Text—it is available for all OS platforms, and will highlight and color-code your HTML) and create a new file with a .html extension. It doesn't matter what you name the file; test.html will work fine. This will be a very bare-bones website, so don't worry about making this a fully “compliant” HTML website; it will be missing some tags that are normally used, such as <body> and <head>. These missing tags will not affect how the page is rendered in the browser. In your new HTML file, enter the markup from Listing 17-3.

This HTML page includes four form elements (the HTML between each <form …> and </form> tag). <form> specifies the beginning of a form, and </form> specifies the end. Within each form are <input /> tags that specify what data will be passed to the server when the form is submitted. In the case of the LED toggle buttons, a variable called L will be passed to the server via a GET method with a value equivalent to the I/O pin number that you will be toggling. Once you copy this HTML code snippet into your Arduino sketch, you can replace those hard-coded pins with pin constants. The action element set to '' (an empty string) in the form tag indicates that the same page should be reloaded when the variable is passed to the server. The hidden input specifies that this value will just be passed when the Submit button is pressed.

For the frequency slider, you are using an HTML5 input element called range. This will make a range slider. You can move the slider (in increments of 100) to select a frequency that will be transmitted as the value of a variable called S. In older browsers, this slider may render as an input box rather than a slider, if they don't support the range element. To see what the page will look like, open it up with your favorite browser (I recommend Google Chrome). In Chrome, you need to press Ctrl+O (Windows) or Cmd+O (OS X) to display an Open dialog box. The rendered HTML file should look similar to Figure 17-8.

If you press any of the buttons, you should see a GET statement appended to the address in your browser's URL bar. In Figure 17-8, the GET statement in the URL bar shows that I just pressed the Toggle Blue button because the L variable is set to the blue LED pin, 11.

Screenshot of an Open dialog box displaying a web page content test in Chrome.

Figure 17-8: Web page content test in Chrome

Given all the requirements listed in the previous sections, you can now construct a server program for the Arduino. The sketch in Listing 17-4 works very well for accomplishing the tasks of controlling an RGB LED and speaker. If you want to add extra functionality with more GET variables, it should be fairly straightforward to do so. The areas where you can insert this extra functionality are called out in the code comments.

This code executes all the functionality that was described in the previous sections. Be sure to change the Wi-Fi credentials address listed in this code to match your network. For simplicity, the Arduino responds to every incoming request with the page that you designed in Listing 17-3. Note that the hard-coded pin numbers have been replaced by the pin variables concatenated into the relevant strings. As each line from the client is read in, if (incoming_line.startsWith("GET /?")) grabs the ones that may contain commands for the Arduino and strips out the relevant command elements. After the full request has been read in and the reply is sent, the command string is checked for its contents by stripping out the command character (L for the LEDs or S for the speaker) and performing an action based on the command's value. Load Listing 17-4 on to your Arduino and launch the serial monitor.

Controlling Your Arduino from Inside and Outside Your Local Network

Now that the server code is running, and your Arduino is connected to the network with a valid IP address, you can access it with a browser and control it. First, you will do so over your local network, and then you'll learn how you can take advantage of port forwarding in your router to access it from outside of your local network.

Controlling Your Arduino over the Local Network

To confirm that the web interface is working properly, ensure that your computer is attached to the same network as your Arduino (via Wi-Fi or Ethernet). Open your favorite browser, and enter the IP address from the previous section into the URL bar. This should open an interface that looks just like the HTML page you created earlier. Try pressing the buttons to toggle the various LED colors on and off. Move the slider and hit the frequency adjustment button to set the frequency of the speaker. You should see and hear the Arduino responding. The serial monitor will show the incoming requests as they are received. Notice the GET commands being passed to the Arduino server through the browser's URL bar (see Figure 17-9).

Screenshot displaying an Arduino control web page and serial debugging output on pressing the buttons to toggle the various LED shades on and off.

Figure 17-9: Arduino control web page and serial debugging

After you're satisfied with controlling the lights and sounds over the local network, you can follow the steps in the next section to enable control from anywhere in the world.

Using Port Forwarding to Control Your Arduino from Anywhere

The steps in the previous section enabled you to control your Arduino from anywhere within your local network. This is because the IP address that you are connecting to is a local address that sits behind your router. If you want to control your Arduino from computers outside of your local network, you need to take advantage of advanced technologies that will allow you to tunnel to your device through your router from the outside world. To do this, you need to implement three steps:

  1. Reserve the local DHCP address used by your Arduino.
  2. Forward an external port on your router to an internal port pointing at your Arduino.
  3. Connect your router to a dynamic DNS updating service.

Logging In to Your Router  First, log in to your router's administration panel. The admin panel URL is the gateway IP address for your network. In almost all home network configurations, this consists of the first three decimal-separated values of your Arduino's local IP address, followed by a 1. If, for example, your Arduino's IP address were 192.168.0.141, then your gateway address would probably (but not necessarily) be 192.168.0.1. Try typing that address into your browser to see whether you get a login screen. Enter the login credentials for your router admin page; these are not the same as your wireless login credentials. (If you never changed them from the default values, you may be able to find them in your router's setup manual or on a sticker attached to your router.)

If that IP address does not work, you need to determine it manually. On Windows, you can open a command prompt and type ipconfig. You want to use the Default Gateway address for your active network connection. If you are on a Mac, access System Preferences, go to Network, click the Advanced button, go to the TCP/IP tab, and use the Router Address. If you are in Linux, open a terminal, type route -n, and use the last Gateway Address listing that is nonzero.

Reserving Your Arduino's DHCP Address  Once you're in your router's admin console, look for an option to reserve DHCP addresses. By reserving a DHCP address, you are ensuring that every time a device with a particular MAC address connects to the router, it will be assigned the same local IP address. Reserved IP addresses are never given to a client with a MAC address other than the specified address, even if that reserved client is not presently connected to the router. By reserving your Arduino's DHCP IP address, you ensure that you'll always be able to forward web traffic to it in the next step.

Once you find the option, reserve whatever IP address your Arduino is currently using by assigning it to the MAC address that is printed on the sticker attached to the top of the Wi-Fi module on your Feather (see Figure 17-10). Be sure to apply the setting, which may require restarting your router. You can confirm that this works by restarting both your router and the Arduino and seeing if your Arduino gets the same IP address when it reconnects.

Forwarding Port 80 to Your Arduino  Now that you have an unchanging local IP address for your Arduino, you need to pipe incoming web traffic to that internal IP address. Port forwarding is the act of listening for traffic on a certain externally facing port of a router and always forwarding that traffic to a specific internal IP address. Port 80 is the default port for HTTP communication, so that is what you will use. Locate the right option in your router administration panel and forward external port 80 to internal port 80 on the IP address that you just assigned to your Arduino. If the router specifies a range for the ports, just make the range 80–80. Now, all traffic to your router on port 80 will go to your Arduino.

Using a Dynamic DNS Updating Service  The last step is to figure out how to access your router from elsewhere in the world. If you are working on a commercial network (or you pay a lot for your home's internet connection), you may have a static global IP address. This is rare for residential internet connections, but still possible; check with your internet service provider (ISP). If that is the case, just type what is my ip into Google, and it will tell you what your global IP address is. If you know you have a static IP address, you can access that IP address from anywhere in the world, and traffic on it should forward to your Arduino. If you want, you can even buy a domain name and set up your domain name's DNS servers to point to that IP address.

Image displaying the MAC address printed on the Feather's Wi-Fi module.

Figure 17-10: The MAC address is printed on the Feather's Wi-Fi module

However, the odds are good that you have a dynamic global IP address. Your ISP probably changes your IP address once every few days or weeks. So, even if you figure out what your global IP address is today, and access your Arduino via this IP address, it may stop working tomorrow. There is a clever way around this, which is to use dynamic IP services. These services run a small program on your router that periodically checks your global IP address and reports it back to a remote web server. This remote web server then updates a subdomain that you own (such as myarduino.ddns.net) to always point to your global IP address, even when it changes.

Many modern routers have built-in support for certain Dynamic DNS services — you should pick one that your router supports. Some are free, while others charge a nominal yearly fee. You can follow the setup instructions in your router's admin panel to create an account with one of these services and connect it to your router. After doing this, you can access your Arduino remotely, even with a dynamically changing global IP address. In case your router does not support any dynamic DNS services, remember that some also offer clients that will run on computers within your network rather than on the router directly.

Once you have determined your public IP address (or obtained a dynamically updating URL), you can enter it into your browser, and you should be able to connect to your Arduino. Give the address to a friend so they can test it remotely!

Interfacing with Web APIs

In the preceding section, you learned how to turn your Arduino into a web server that exposed a web interface for controlling its I/O pins over the local network or the internet. However, an equally common reason for connecting your Arduino to the web is to interface with application programming interfaces (or APIs). APIs are interfaces exposed by service providers to allow computing systems to programmatically access and/or supply data to or from their services. Here are some examples of APIs from companies and organizations you may be familiar with:

  • The Google Maps API allows application developers to embed Google Maps data into their apps.
  • The GitHub API allows programmatic access to software projects stored on GitHub. (I use this feature to automatically publish packaged code downloads on exploringarduino.com when I push software updates to the Exploring Arduino GitHub repository.)
  • The Phillips Hue API allows you to write software that controls web-connected Philips lightbulbs in your home.
  • The NASA API provides a programmatic way to search and download space-related imagery.
  • The Facebook API is used by web developers to enable you to log into their websites using your Facebook credentials.

Using a Weather API

For this final project, you'll use an open weather API provided by OpenWeatherMap.org to create a live temperature display for your location. The OpenWeatherMap project is one of many websites that provide a freely accessible weather API with live data for any given location. Its free API offers real-time data and is rate-limited to a maximum of 60 API requests per minute—more than sufficient for this project, which will only update once per minute.

APIs are constantly changing, and it is possible that the exact procedure for communicating with the OpenWeatherMap API will be different by the time you pick up this book. The remainder of this chapter should teach you a general approach for interacting with an API, and should be applicable to any API that enables data access with an API key and returns data in a structured format. Good APIs are “versioned” and will continue to work the same way for a long period of time if you continue to specify the particular version.

Creating an Account with the API Service Provider

To start, you'll probably need an account with the API provider. In the case of the OpenWeatherMap API, just navigate to openweathermap.org and click the Sign Up link. If you've found a different API provider that you plan to use for this project, sign up on their website. Once you have signed up and your account has been activated, log into it.

After you sign in, you should see an API Keys section in your account page. Click it to go to a page that lists your API key. An API key is automatically created for you when you set up the account, so there is no need to create another one. Figure 17-11 shows the API Keys page. Keep this page open; you'll need to copy the listed API key into your program. This API key is unique to you and will authenticate you to the OpenWeatherMap servers.

Screenshot displaying the OpenWeatherMap.org API key management page.

Figure 17-11: OpenWeatherMap.org API key management page

Understanding How APIs Are Structured

APIs are designed with the express purpose of enabling two services, potentially owned or developed by different companies, to communicate with each other and exchange data in an efficient manner. To do this, a few requirements must be met:

  1. The API must be able to authenticate the service that is requesting or uploading data.
  2. The API should return data in a consistent and easily machine-readable format that uses as little storage and bandwidth as possible to convey the necessary information.
  3. The API should not be changed in non-backwards-compatible ways, to ensure that services that rely on it can continue to function.
  4. The authenticated API users should only be able to access information to which they have permissions.

To accomplish these design goals, APIs generally employ two important technologies: serializable data formats and key-based or token-based authentication systems. You've already created an account and received your API key. Just like a key for your house, you shouldn't share it with strangers! Keep it private, and use it only for your own projects.

JSON-Formatted Data and Your Arduino

One of the most popular serializable data formats is JSON (pronounced Jay-Sahn), which stands for JavaScript Object Notation. As the name implies, JSON was derived from the JavaScript programming language, but it is now used universally in most programming languages. It is a particularly popular way for formatting data returned by APIs because it maintains human readability while still being easily machine-parseable.

So, what is “serializable”? This just means that a complex, multi-layer data structure encoded in JSON can be easily converted back and forth between a string-type representation that can be easily transmitted from servers to clients with no special protocol requirements. Consider this simple object, which contains information about this book, shown in JSON format:

json_object = { "title" : "Exploring Arduino", 
                "author_first" : "Jeremy",
                "author_last" : "Blum",
                "edition_list" : [1,2],
                "num_chapters" : 17}

This json_object contains five key-index values. Three of them are strings, one of them is a list of numbers, and one is a single number. Individual items in this object can be accessed like this: book_title = json_object["title"]. JSON objects can include nested objects inside of them, allowing for a vast amount of organized data to be easily stored.

To send this data over an HTTP connection, you “serialize” it into a string representation that looks like this: '{"title":"Exploring Arduino","author_first":"Jeremy","author_last":"Blum","edition_list":[1,2],"num_chapters":17}'. On the receiving end, it can be unpacked and the relevant data can be easily extracted into the required variables. You will use a JSON Arduino library to unpack data returned from the weather API.

Fetching and Parsing Weather Data

To request weather data from the API, you first need to issue a request to the desired endpoint on the API server, while providing the right arguments in the URL. API servers offer a variety of endpoints that each serve up different information. For instance, a /user endpoint may return information about the requested user account, and a /data endpoint may return whatever kind of data the service provides.

Like the requests you learned about earlier, this one will be a GET request. For this project, you'll be getting the current weather for a city of your choice. Each endpoint will be appended to the end of the base URL, and then parameters will be passed to the API in the form of GET arguments in the URL. Per the API documentation provided at openweathermap.org/current, the endpoint for getting the current weather is api.openweathermap.org/data/2.5/weather?q={cityname}, where {city_name} will be replaced by your city of choice. Some URL GET parameters are endpoint-specific, while others apply to all API requests. For example, the optional units parameter can be included in any API request to set whether the returned data is in degrees Celsius or degrees Fahrenheit. If no unit parameter is specified, the temperature is returned in Kelvin.

All queries must also include an appid parameter, set to the API key that you obtained earlier. This allows the API provider to track usage of the API so that they can correctly charge their paying customers, and instruct their servers to ignore unauthorized data requests. Putting that all together, you end up with a complete GET request URL that breaks down as shown in Figure 17-12.

Before you program your Arduino to send a GET request to that URL, it's worth trying it in your browser to see what the response JSON object will look like. Copy the API request shown in Figure 17-12 into your browser's URL bar, being sure to insert your personal API key that you received for OpenWeatherMap.org. Optionally, replace San Francisco with the city of your choice. You should get a reply that looks like Figure 17-13.

Illustration of an API URL request analysis to track their servers to ignore unauthorized data requests.

Figure 17-12: API request analysis

Screenshot displaying the API response in a browser's URL bar.

Figure 17-13: API response in a browser

This confirms that your API key is functional, and your query is valid. But, it's pretty hard to read in this form. Do a web search for JSON Pretty Printer and copy the contents of your API reply into it. It should return a prettier version of the API response that looks like this:

{
 "coord": {
  "lon": -122.42,
  "lat": 37.78
 },
 "weather": [
  {
   "id": 721,
   "main": "Haze",
   "description": "haze",
   "icon": "50n"
  }
 ],
 "base": "stations",
 "main": {
  "temp": 13.1,
  "pressure": 1011,
  "humidity": 81,
  "temp_min": 11.11,
  "temp_max": 15
 },
 "visibility": 4828,
 "wind": {
  "speed": 2.32,
  "deg": 249.424
 },
 "clouds": {
  "all": 90
 },
 "dt": 1558316288,
 "sys": {
  "type": 1,
  "id": 4322,
  "message": 0.0097,
  "country": "US",
  "sunrise": 1558270612,
  "sunset": 1558322141
 },
 "id": 5391959,
 "name": "San Francisco",
 "cod": 200
}

Looking at the response in this format, it's clear that the current temperature will be in the ["main"]["temp"] variable. There's also a lot of other useful weather information that you can use to expand on this project! Now that you know how to issue an API GET request and how to find the relevant data in the output, it's time to make the Arduino Feather do the heavy lifting.

Getting the Local Temperature from the Web on Your Arduino

Programming your Arduino to issue the GET request that you just issued from your browser is very similar to the process you used to launch the server on your Arduino earlier. You'll continue to use the WiFi101 library, but you will now initialize the Arduino as a client, instead of as the server. You'll format your URL request, send it to the API, and wait to receive a reply back, which you will parse into a serialized JSON string. Once you have that string, you can use the Arduino JSON library to turn the JSON string into an object from which you can extract the relevant data (current temperature).

First, install the Arduino JSON library. Open the Library Manager panel in the Arduino IDE, set the Type to Arduino, and search for Arduino JSON. Install the official Arduino_JSON library as shown in Figure 17-14.

As you build your Arduino sketch, start with the same Wi-Fi connection logic that you implemented earlier in this chapter. Import the Arduino JSON library with #include <Arduino_JSON.h>. Add some new constants for holding the data that you'll use for constructing your API request:

const char SERVER[] = "api.openweathermap.org";
const char HOST_STRING[] = "HOST: api.openweathermap.org";
const String API_KEY = "PUT YOUR API KEY HERE";
const String CITY = "San Francisco"; // Replace with your City
const String UNITS = "F"; // Set to F or C
Screenshot of the Library Manager for installing the Arduino JSON library.

Figure 17-14: Installing the Arduino JSON library

After connecting to Wi-Fi in the setup() function, you can initialize a client connection to the API server as follows:

 String api_units = "metric";
 if (UNITS == "F")
 {
  api_units = "imperial";
 }
 String request = "GET /data/2.5/weather?units=" + 
                  api_units + 
                  "&q=" + 
                  CITY + 
                  "&appid=" + 
                  API_KEY + 
                  " HTTP/1.1";

 // Connect to Server and issue a Request
 if (client.connect(SERVER, 80))
 {
  client.println(request);
  client.println(HOST_STRING);
  client.println("Connection: close");
  client.println();
 }

This code constructs the request URL from the variables you've assigned, and connects to the server on port 80 (the standard port for HTTP connections). Finally, you wait for a reply and parse out the JSON string:

 // Wait for available reply
 while (!client.available());
  
 // Throw data out until we get to the JSON object that starts with '{'
 // Print the header info so issues can be debugged
 while(true)
 {
  char h = client.read();
  if (h == '{') break;
  Serial.print(h);
 }

 // Once we hit the JSON data, read it into a String
 String json = "{";
 do
 {
  char c = client.read();
  json += c;
 } while (client.connected());
 client.stop();
 JSONVar api_object = JSON.parse(json);
 Serial.println("Raw JSON:");
 Serial.println(api_object);
 double temp = (double) api_object["main"]["temp"];
 Serial.print("Temperature = ");
 Serial.print(temp);
 Serial.println(UNITS);

Recall that a while loop with no contents will effectively halt the program until its condition is true. This means that the program will wait until data is returned to the client. Once it is, you can throw out the header HTTP data by reading until the first open bracket of the JSON reply. Although this code isn't explicitly parsing out potential HTTP error codes, it does print out the header information to the serial console so that you can debug issues with your program. For example, if you use an invalid API key, the remote server will reject your request, resulting in a 401 error as shown in the serial monitor in Figure 17-15.

Screenshot displaying an error resulting from use of an invalid API key.

Figure 17-15: Error resulting from use of an invalid API key

Similar to the do…while() loop that you used earlier in this chapter, incoming data is appended into a string until it is complete. Finally, JSON.parse() parses the JSON-serialized string into a data structure whose elements can be accessed. double temp = (double) api_object["main"]["temp"]; creates a double-precision floating point variable called temp that is set equal to the value of the temperature that was returned from the API. Finally, this is printed to the serial monitor.

Putting all that together and adding some serial debugging strings, you end up with the simple sketch in Listing 17-5. This one talks to an API and extracts your local temperature data!

Load this sketch onto your Feather (you don't need anything to be attached to it other than the USB cable to your computer). Don't forget to fill in your Wi-Fi credentials and your API key where indicated. Optionally, you can change the units from imperial to metric. After the sketch is loaded on, open your serial monitor; you should see a result like Figure 17-16.

Screenshot displaying a successful Web API call from the Exploring Arduino.

Figure 17-16: Successful API call from the Arduino

Completing the Live Temperature Display

Now that your Arduino is successfully communicating with a web API, pulling down data, and parsing JSON, you can add a little bit of hardware flair. Showing the temperature on the serial monitor isn't particularly useful, because your computer is already connected to the web! Connect an LED seven-segment readout to your Arduino Feather so that you can display your city's current temperature on it (without needing to be tethered to your computer). Adafruit sells a super-sized 1.2-inch, four-digit, seven-segment readout with an I2C “backpack” that will let you connect it to your Feather with only a few wires. The details for this part are linked from the website for this chapter—it is also available in several colors.

With a simple diffusing piece of thin plastic or paper, you can make an aesthetically pleasing, Wi-Fi–connected temperature readout that will make it easy to decide if you need to grab your jacket on the way out of your home for the day! Figure 17-17 shows an example of the finished project.

Wiring up the LED Readout Display

Before you make the requisite sketch updates to control the display, get it wired up to your Arduino Feather. The suggested large Adafruit four-digit, seven-segment display includes an I2C driver chip. You need to connect the pins to your Feather as follows:

  • The pin labeled 'IO' connects the 3.3V. This is the logic voltage to be used for the I2C communications. The voltage provided to this pin sets the HIGH logic level to be used for communications by determining what voltage the onboard pull-up resistors are connected to. Since the Feather is a 3.3V device, and because additional pull-up resistors are necessary to achieve reliable performance (see the "Tuning I2C Buses over Wires" sidebar), this pin must be connected to the Feather's 3.3V supply.
    Image of a completed live temperature display,  using a simple diffusing piece of thin plastic or paper.

    Figure 17-17: Completed live temperature display

  • The + pin connects to 5V (the pin labelled USB). This is the positive voltage supply for the LEDs. They require 5V (as opposed to 3.3V) because each segment on the display comprises two LEDs in series. Their combined forward voltage exceeds 3.3V, thus requiring a high voltage supply.
  • The - pin connects to Ground.
  • The D pin connects to the Feather's I2C Data Pin (SDA).
  • The C pin connects to the Feather's I2C Clock Pin (SCL).
Image of a feather circuit wired to an LED display with the stronger pull-up resistors added.

Figure 17-19: Feather wired to an LED display

Created with Fritzing

With your Feather wired as described here, and the stronger pull-up resistors added as described in the previous sidebar, your setup should look like Figure 17-19.

Driving the Display with Temperature Data

Starting with the sketch you've already written to grab temperature data from the web, you now need to add libraries to support the LED display, and to parse the data that will generate the digits for the display.

Open the Arduino IDE's Library Manager and install the two required libraries by searching for both Adafruit GFX and Adafruit LED Backpack. Install the libraries as shown in Figure 17-20 and Figure 17-21.

Screenshot of the Library Manager to install the Adafruit GFX Library.

Figure 17-20: Install the Adafruit GFX Library

Include the libraries at the top of your sketch using the following code:

#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>

You also need to create an object for writing data to the display:

Adafruit_7segment seven_seg_display = Adafruit_7segment();
Screenshot of the Library Manager screen to install the Adafruit LED Backpack.

Figure 17-21: Install the Adafruit LED Backpack Library

The temperature that you received from the web needs to be formatted before you can display it on the readout. Use the first three digits of the four-digit display to show the temperature value, rounded to the nearest whole number. Use the last digit to show a “C” or an “F” for the temperature unit. Allowing for negative numbers (the negative sign occupies the first digit if present), you'll be able to show whole numbers from –99 to 999 (let's hope it doesn't actually get that hot or cold). So, the first thing you should do is round the data you received to a whole number, and constrain it to that range:

int temp_round = constrain(round(temp), -99, 999);

The provided functions for printing to the display right-justify the number. However, you want to reserve the rightmost digit for printing the unit. An efficient way to deal with this is to just multiply the temperature by 10. This effectively appends a 0 to the end, which you can then overwrite with the unit before committing the string to the LED driver chip:

  seven_seg_display.print(temp_round*10); 

Next, print the degree indicator (there is a small dot on the LED display that is perfectly positioned for this):

 seven_seg_display.writeDigitRaw(2, 0x10);

This snippet is writing a raw hex value to the second digit on the display. Digit 0 is the first number, Digit 1 is the second number, Digit 2 represents the five dots on the LED readout, Digit 3 is the third number, and Digit 4 is the fourth number. By writing 0x10 to the second digit, you ensure that the degree dot is turned on.

Finally, write an "F" or a "C" to the final position on the display. You can also flip the units each time so that on each update, the temperature is shown in a different format:

 if (UNITS == "F")
 {
  seven_seg_display.writeDigitRaw(4,0x71); // Print a "F"
  UNITS = "C"; // Show the opposite unit on the next update
 }
 else
 {
  seven_seg_display.writeDigitRaw(4,0x39); // Print a "C"
  UNITS = "F"; // Show the opposite unit on the next update
 }

The hex representations of the "F" and the "C" are present in the number table in this library's source code. You can see the exact location at blum.fyi/led-backpack-number-codes.

Add a one-minute delay to the end of the loop so that you do not constantly poll the API server. If you don't do this, you will eventually exceed your free allocation of API calls, and your IP address will be blocked by the server! Putting everything together, you end up with the sketch in Listing 17-6.

Replace the network credentials and API key with your own. Also be sure to set your city accordingly. In the listing, I've commented out the serial wait loop so that it will not wait for a serial interface before it starts working. This will allow you to just connect to power (potentially using a USB power brick) and deploy your live temperature display anywhere in your home where you get a Wi-Fi signal.

Once you've got this project working, how else can you envision using the data from this API? Perhaps consider connecting a speaker to alert you when thunderstorms are approaching, or connect a light bulb that will turn on at the official sunset time and off at the official sunrise time.

Summary

In this chapter, you learned the following:

  • The internet has a lot of acronyms. You learned the meanings of IP, DHCP, DNS, MAC, and more.
  • Internet-connected devices can act as a clients and/or server.
  • HTML can be used to render a form for controlling your Arduino over the web.
  • You Arduino can act as a simple web server.
  • APIs can be leveraged to communicate with third-party data services.
  • Your Arduino can query an API and receive data back in JSON format.
  • Your Arduino can de-serialized JSON strings and extract relevant data from them.
  • An I2C bus's pull-ups must be properly tuned to ensure that it performs reliably.
  • Using third-party libraries, an Arduino can control an I2C-based, four-digit, seven-segment LED display.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset