Quantcast
Channel: SparkFun Tutorials
Viewing all 1123 articles
Browse latest View live

LED Robot Pop Up Card

$
0
0

LED Robot Pop Up Card a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t386

Introduction

Craft a paper circuit pop up card with a cycling RGB LED, battery, and copper tape. If you’ve built any of our paper circuit pop up cards before, feel free to jump right in to following the template. If this is your first paper circuit, we’ll walk you through the process.

alt text

Suggested Reading

If you are brand new to working with electronics, here’s some helpful reading to check out:

Materials and Tools

Here is a list of all the materials and tools you’d need to follow along:

Notes:

Additional Materials:

  • Cardstock (2-3 pieces)
  • ¾" Square of Vellum, wax paper, rice or parchment paper- creates a nice diffused effect for the LED to shine through the robot’s heart
  • Clear Tape
  • Gluestick/Glue
  • Scissors/Hobby Knife
  • Needle Nose Pliers
  • Decorating Supplies - stickers, markers, or paints to embellish your designs

Step 1: Print Template

Right-click the images below, and choose “Save Link As” to download the templates to your computer. Each file has a circuit template page and a pop up template page.

Robot Template - 2 pages

alt text

If you have a Silhouette Electronic Cutter, click here to download a Silhouette Studio file for the pop up layer

Print your templates out on cardstock. If needed, adjust your printer’s margins, or choose ‘Fit to Page’ in the print settings. The card template is slightly smaller than the paper, so make sure to cut along the black border for the final card size.

Set the pop up page aside for now. We’ll build our circuit first and then assemble the pop up once the electronics are all installed.

Step 2: Create Copper Traces

Time to create a path for our electricity with copper tape. Each template has icons to help guide you in constructing the circuit.

alt text

Line A

Take a look at the template and find the circle marked A. Peel away a few inches of the paper backing from the copper tape and stick down along the grey line.

alt text

Cut when you reach the scissors icon.

Line B

Next we’ll place tape along Line B which includes a corner. To keep a solid connection of copper around corners, we’ll be using a folding technique to press the tape into shape.

Start by sticking the copper tape down until you reach the corner, then fold the tape backward on itself. Use a fingernail or pen to give it a good crease at the edge.

alt text

Then carefully move the tape down around the corner - you should see the fold forming - and press down flat against the paper. The neatness of the fold doesn’t matter that much, it will be covered by your pop up in the end. Finally, cut the tape when you reach the scissors icon.

alt text

alt text

alt text

Line C

The last copper tape line will also form a battery holder. We’ll start by folding ½" of copper tape onto itself, sticking the adhesive sides together to form a flap.

alt text

This allows the top of the copper to fold down over the coin cell battery - the positive side of the battery is the top and negative side is the bottom, which allows us to create a ‘battery sandwich’ with copper tape touching each side.

alt text

See the diagrams below to explore how this method works. We won’t be installing the battery until the end of our project, so set that aside for now. Fold the card in half along the dotted center line before moving onto the next step.

alt text

alt text

Step 3: Prepare and Place LED

Before prepping the LED, fold the card in half along the dotted line to save the hassle of trying to make a neat fold once there are components sticking up from the paper.

Now that our copper is in place, time to add the LED. The template has an LED symbol which shows shaped wires - we use this method to help us remember which side is positive and negative on the LED.

Read more about LED polarity in our Light-emitting Diodes (LEDs) Tutorial .

alt text

Here are directions for bending a 3mm LED to prepare it for our circuit.

Note: the cycling RGB LEDs have a clear bulb - we used a yellow LED for these photos.

Using pliers (or your finger), bend the longer leg of the LED flat and then form into a zig zag shape. Be careful not to break the wire by bending back and forth over the same joint too many times.

alt text

Next, bend the other leg flat and curl into a spiral. Use the end of the pliers to lightly grab the end of the wire and curl around the tool.

alt text

Once all shaping is complete, place the LED on a table or flat surface to make sure it sits flat and upright. If not, make any adjustments now.

Tape Down LEDs

Line up the positive lead with the copper tape marked + and the negative with -. Use clear tape over the leads to hold down to the copper.

alt text

Step 4: Attach Button

alt text

Next, we’ll place the LilyPad button over the oval icon on the template facing up. It doesn’t matter which side touches postive and negative. Make sure the conductive pads on the bottom of the button touch the copper tape, then tape down the ends with clear tape. Be careful not to tape directly over the push part of the button, or it may interfere with the ability to press it. You can also use a LilyPad switch instead of a button - the installation is the same.

alt text

Step 5: Insert Battery

Once all the components are installed, it’s time to test our circuit by adding a battery. Carefully slip the battery underneath the copper tape flap we made earlier, and center it inside the circle icon. Make sure the positive side of the battery (top, marked with the battery model and +) is facing up. Press the copper over the battery, and tape with clear tape.

alt text

Now, press the button, and the LED should light up!

alt text

alt text

Troubleshooting

  • Check the tape connections - use your nails or a pencil to make sure the tape is firmly adhering the components to the copper tape.
  • Check the battery - make sure it is sandwiched firmly between the top and bottom copper tape lines and that the top copper is not accidentally touching the bottom of the battery.
  • Check the wires of the LED - double check that they weren’t accidentally broken while bending them into shapes with pliers.

Here is what the finished circuit should look like:

Step 6: Prepare Pop Up

Use a hobby knife to cut along the lines of the pop up template. Tape or glue a piece of vellum or a semi-transparent paper to the back of robot over the heart to diffuse the light from the RGB LED.

alt text

To pop the robot up from the page, first turn paper over to the front side (the heart should be on the right side of the robot). Carefully fold the robot’s feet toward you.

alt text

Rotate the card so the robot’s head is facing you and crease the top fold toward you and second fold away from you.

alt text

alt text

To finish the pop up, fold the card in half - making sure not to accidentally fold the legs. The robot should fold up neatly in the card.

alt text

alt text

To attach to your circuit, place the robot pop up page over the copper tape circuit and adjust as necessary so the LED shines through the robot’s heart. Trim edges if needed and use glue or tape to fix the corners down. Be careful not to glue under the pop up pieces or they may stick down when closed.

Step 7: Decorate and Customize

After assembling, use a marker or stickers to indicate where the button should be pressed on the top layer. Add extra decorations and embellishments to make your project unique!

alt text

alt text

Resources and Going Further

Things to try:

  • Try these techniques with other pop up designs or create your own - can you figure out how to adapt the copper tape path to fit your card choice?
  • Get crafty with stickers, paint, or markers after you’ve built your card to customize it even more.

Further Reading:


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado


Photon Development Guide

$
0
0

Photon Development Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t400

Introduction

Particle’s Photon Development Board is an awesomely powerful platform for projects that require WiFi and Internet-connectivity. Whether you’re creating the next, great, IoT project, or just want an easy to use, over-the-air-programmable ARM Cortext M3 development board, the Photon is an excellent foundation.

Photon Kit

KIT-13345
$29

As with any microcontroller platform, there is no shortage of routes you can take to develop firmware for the Photon. There is a web-based IDE, which make it easy to share and import import code and program your Photon remotely. There’s a pre-configured local IDE, which shares many of the online IDE’s advantages, but allows you to keep code stored on your hard drive. Or there are the more “hardcore” ARM development environments, which, while more complicated, can provide complete control over the contents of your Photon’s program memory.

Covered In This Tutorial

The purpose of this tutorial is provide a quick overview of the options you have when your developing firmware for the Photon. The online Build IDE is easy, but it’s not for everyone – that shouldn’t stop anyone from getting a chance to use this powerful, cost-effective WiFi development platform.

This tutorial is split into a few sections. Navigate using the menu on the right, or click below to skip straight to the section you’re most interested in:

  • Particle Build– A beginner friendly, browser-based, online IDE hosted on Particle.io.
  • Particle Dev– An offline editor that allows you to locally store your source code, but still requires Internet connectivity for compiling and flashing code to your Photon.
  • ARM GCC and DFU Bootloading– The heart of the Photon is an STM32 ARM microcontroller, so if you already have an ARM IDE set up, the Photon’s open-source firmware will make it easy to port to the Photon. Plus, because the Photon has a built-in USB bootloader, loading the code can take place entirely offline too!

Particle Build (Online)

The Particle Build IDE is an always-online, browser-based portal where your Photon code can be edited, saved, and shared. This cloud-based IDE handles code compilation and flashes the built binaries to your Photon over-the-air. You don’t even need your Photon next to you to update its program!

To load the Build IDE head over to build.particle.io.

Open the Build IDE!

If it’s your first time working with Build you may have to create an account, otherwise log in to one you’ve already created.

Tour of the Build IDE

The majority of the Build IDE’s window is taken up by your code view – as it should be. To navigate around the Build IDE there are buttons on the left side of the window. Hover over any button to get a brief overview of what it does – you’ll quickly become familiar with all of these icons.

Screenshot of the Build IDE

  • Flash IconFlash: Remotely upload your application code to the selected device.
  • Verify IconVerify: Compile your code and check for errors. Any compilation errors will be listed under the code.
  • Save IconSave: Save early. Save often! This’ll save your app, which you’ll be able to find under the Code view.
  • Code IconCode: This button will list of all of your saved apps. From there you can click one to load it.
  • Libraries IconLibraries: Opens a list of libraries – those you’ve contributed and those added by the community.
  • Documentation IconDocs: Your go-to link for everything from the IDEs, to firmware API, to hardware datasheets.
  • Devices IconDevices: Click here to select which Photon (or Core, or Electron) you want to flash code to.
  • Settings IconSettings This is where you can log out, change your password, or find your access token.

Selecting Your Device, Programming Blink

Before you can upload any code, you have to tell the IDE which of your Photon’s you’d like to flash it to. Click on the “Devices” tab to see your list of Photons, Cores, and P1 modules.

Build IDE device selection

Mark the Photon you want to program with a yellow star, by clicking to the left of the name. You can only select one device at a time.

After selecting your device, navigate over to the Code tab, and either create a new App or select one of the examples. For your first try, the Blink and LED example is always a good tool to test with.

Finally, click the Flash button in the top-left corner and watch the bottom status-bar area of the IDE. The text down there will keep you up-to-date on how the flash process is progressing. Your Photon’s RGB LED will also display it’s status – it should go from breathing cyan (connected to cloud), to blinking magenta (receiving flash), to blinking green (update done, connecting to WiFi), back to breathing cyan.

Including Libraries

Particle’s community-contributed libraries allow you to easily plug proven code to your application with the click of a button. Adding a library to an app can be somewhat confusing the first time through, so here’s a quick rundown.

Create an App– Before you can add a library to an application, you have to have one created for it. Switch to the Code tab, and click Create New App. Name it whatever you please, you can always change it. Press enter to create the app.

Creating an app

Find your Library– Navigate over to the Libraries tab. Then, using the search box, find the library you’re looking for. For example, to add our LSM9DS1 library to your app, search for “SparkFunLSM9DS1”.

Searching for a library

Select the Library– Once you’ve found the library you’re looking for, click on it to load up an overview of it. Here you’ll be able to view all of the library’s contents, and even get some information about what the library does.

Build library view

Include in App– Finally, click the Include in App button, then click the app it’s destined for. To verify, click Add to this App. Magically, an #include statement will appear at the top of your app.

library included

Note that simply copy/pasting the #include statement won’t actually add the library to your app, you need to go through the “Include in App” process.


For more information on using the Particle Build IDE, check out Particle’s Build documentation. There you can learn more about contributing libraries, checking your memory usage, and using keyboard shortcuts.

Particle Dev (Half-Online, Half Offline)

If you’re uneasy about leaving your hard work in the mysterious “cloud,” but still want all of the benefits offered by Particle Build, Particle Dev is a great middle-of-the-road option.

Like Build, Particle Dev takes care of your toolchain setup (compiler, linker, etc.) and allows you to program remotely, plus you get the added benefit of keeping all of your source code locally saved to your machine.

For the most part Particle Dev is still an online IDE. Your code is stored locally, but the “cloud” is still required to compile. You’ll need to be connected to the Internet to get the full use out of it. (There may be hope though: in Particle’s words “This is not an offline development tool, yet.”)

Download Particle Dev

Particle Dev is currently available for Mac and Windows. Head over to particle.io/dev to download it.

Download Particle Dev

Follow along with the install wizard to install it on your Windows machine. Mac users can unzip the “Particle Dev” application, and stick it in your applications folder.

Getting Started with Particle Dev

If you’ve tested the waters with Particle Build, you should already be somewhat familiar with Particle Dev’s menu icons. Hover over any icon to get a succinct description of what it does.

Particle Dev Overview

The first time you load up Particle Dev, you’ll have to log in to your Particle account. That will give you access to flash your Photons and Cores. Go “Particle” >“Log in to Particle Cloud…” or click “Click to log in to Particle Cloud…” to set it up.

Open a Project Folder

Now it’s time to write some code! Projects in Particle Dev are folder-based. If you’d like to follow along with an example click the button below to download a simple “Blink” app – unzip it anywhere you’d like.

Download the Blink Example Folder

Begin by opening a project using the File>Open Folder… menu option (on the Mac version select “Open Project Folder”). Navigate into a folder, and click “Select folder” (or “Open” on Mac).

Open project folder

The contents of your project folder will be displayed to the left of the text editor window. Click on any file to view its contents.

Blink.ino open

You can edit this code just as if you were in the Build IDE. Click the checkmark icon (“Compile and show errors if any”) to try compiling your code. If it builds, you’ll see a “Success!” message down at the bottom of the window.

Select a Device and Flash

Once you’ve gotten your application code to the point where you’re ready to upload it save.

Watch out! Particle Dev doesn't automatically save before compiling and uploading your code. Make sure you save before clicking "Flash" to make sure your Photon gets the latest version of your code.

Then select a device by going to Particle>Select device. If your Photon is connected to Particle Cloud, there should be a comfortingly pulsing teal dot next to it.

Selecting a device

With your device selected, and code in a compilable state, click the Flash icon (“Compile and upload code using cloud”).

Upload code to device

The status of your code upload will be displayed in the bottom of the IDE. As code is being flashed, your Photon should briefly blink magenta before transitioning into its new application.

Using Particle Libraries

It’ll take a few extra clicks and drags to add a library to your Particle Dev project folder – it’s not quite as easy as Particle Build.

To begin, you’ll need to download the library. Most Particle libraries should already be hosted on GitHub, so find the library you want and view it there. You can find SparkFun’s list of Particle libraries here.

On the GitHub repository page, click “Download ZIP” to get your own copy of the library.

Download ZIP

Unzip the library. Then you’ll need to do some surgery on the contents. Grab the library’s source files – they should be in the “firmware” directory and end with either a .cpp or .h.

Copying library source

Don’t grab the “examples” folder! If you want to use one of the examples as your main application source file, drag it into your project folder as well.

Copying library source into project folder

Then open your main source file back up in Particle Dev, and add the necessary #include statement.

Library included with app

Click the checkmark icon to compile and make sure everything’s still happy, and keep on coding!


There’s a whole lot more to the Particle Dev IDE. It’s fully customizable – make sure you check out the “Settings” tab. It has an integrated Serial Monitor and Spark Variable viewer. Make sure you check out Particle’s Dev documentation to learn more about the IDE.

ARM GCC and the DFU Bootloader (Offline)

At its heart, the Photon is just an STM32F205 ARM processor with a Broadcom WiFi chip built in. Developing firmware for it doesn’t have to be different from any other ARM processor. Plus, because the Photon is completely open source, you have access to all of the firmware to help get you started.

Get The Tools

This is a much more advanced approach than the previous two environments. You’ll need to set up a toolchain on your computer to be able to build firmware and flash your Photon.

  • GNU Tools for ARM– Primarily, this includes ARM GCC – an open-source tool for compiling C and C++ files for ARM processors.
    • Make sure your ARM-gcc is up to date. Type the arm-none-eabi-gcc --version command to get a read on your gcc’s version – make sure it’s at least 4.9.3.
  • Make– To work the makefile magic, you need the “make” tool installed. Mac and Linux users should already have this wonderful tool available, Windows users may need to install it though.
  • dfu-util– The Photon has a USB DFU (device firmware upgrade) bootloader, which allows you to flash code locally. For your computer to communicate with the bootloader, you’ll need this utility.
  • Windows users may also need to install MinGW and MSYS to get an assortment of programming tools and a simple shell interface.

You may also need to do some re-ordering of your machine’s PATH environment variable. This post on Particle’s forum was a big help in getting my Windows machine operational, it may help you too.

Download the Photon Firmware

Particle is awesomely open-source, everything from the Photon’s hardware layout to the firmware is available to download, view, and modify. You can find it all on their GitHub page.

The Photon’s latest firmware release is hosted in the latest branch of the firmware repo. You can download the firmware by either clicking “Download Zip” on that GitHub page, or you can use git via the command line. (Visit git-scm.com to download the git tool.)

git clone https://github.com/spark/firmware.git
cd firmware
git checkout latest

Update the Photon Firmware

Before you can begin flashing an application of your own design, you’ll need to update the Photon’s firmware. It’s easily done, but requires some patience.

Before you type any commands, put your Photon in DFU mode: hold down both the SETUP and RESET buttons. Then release RESET, but hold SETUP until you see the RGB blink yellow. That means the Photon is in DFU mode.

Attention Windows Users: Windows users may have to install the Photon's USB driver for DFU mode. Download Zadig and follow along with this tutorial.

To verify that the Photon is in DFU mode and dfu-util is installed properly, try the dfu-util -l command. You should see a response like this:

Found DFU: [2b04:d006] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/03*016Ka,01*016Kg,01*064Kg,07*128Kg"Found DFU: [2b04:d006] devnum=0, cfg=1, intf=0, alt=1, name="@DCT Flash /0x00000000/01*016Kg"

The important parts there are the hex values inside the braces – the USB VID and PID, which should be 2b04 and d006 for the Photon.

Two commands are required to build and flash the firmware: switch to the modules directory then call make with a few parameters to build and upload the firmware:

make PLATFORM=photon clean all program-dfu

Then sit tight as make and then dfu-util work their magic. Be warned this may take a long while.

Navigating the Photon Firmware

The Photon’s firmware code base is massive, but it’s well sorted. Unless you want to mess with really low-level stuff, you can ignore most directories. The user directory is where the Photon’s application code is stored.

Application directory

The “src” directory contains the default application to be built. You can either put your application code in here (overwriting “application.cpp”) or create a directory of your own in “applications”.

There are some example applications in the “applications” folder, including tinker– the default application every Photon ships with.

As an example of our own, lets create a “blink” directory in “user/applications”. Then create a file called “application.cpp”.

To code up a simple blink app, copy and paste this into “application.cpp”:

#include "application.h"

int ledPin = D7;

void setup()
{
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  digitalWrite(ledPin, HIGH);
  delay(250);
  digitalWrite(ledPin, LOW);
  delay(250);
}

Don’t forget to #include "application.h", other than that your code will look a lot like any other Arduino/Photon sketch. Easy enough. Now the hard(er) part – compiling.

Compiling with Make Magic

To build your firmware you’ll need to call make, while configuring a few variables at the same time. You need to tell make what platform you’re compiling for (we’re assuming its a Photon, but it could also be a Core or P1 module). And you also need to tell it which app to make.

First, change directories to the main folder. Then type this:

make PLATFORM=photon APP=blink

The APP=blink bit will tell make to look for application code in the “user/applications/blink” folder. There are more make options available as well, check out Particle’s build documentation.

Your first build may take some time, but, now that most everything is built, successive make’s should go much faster. A successful build should yield a response like this:

text    data     bss     dec     hex filename
1956      44     236    2236     8bc ../../../build/target/user-part/platform-6-m-lto/applications/blink.elf

The final output of our build will be: firmware/build/target/user-part/platform-6-m-lto/applications/blink.bin. That’s what all this work was for. That’s what we’ll be flashing with dfu-util.

Compile Locally, Flash Locally (Over USB)

Now that you have a .bin file – cryptic instruction code that only the Photon will understand – it’s time to send it over to the development board. This is where you’ll need dfu-util.

Make sure you put your Photon in DFU mode again! Then use this dfu-util command to upload your BIN file to the Photon’s application memory:

dfu-util -d 2b04:d006 -a 0 -i 0 -s 0x80A0000:leave -D ../build/target/user-part/platform-6-m/blink.bin

Alternatively, you can make your life a bit easier by using the make command to invoke dfu-util:

make PLATFORM=photon APP=blink program-dfu

Compile Locally, Flash Remotely (With Particle CLI)

Claiming control over your toolchain and source files doesn’t mean you can’t enjoy the luxury of over-the-air programmability! Particle’s Command Line Interface (Particle CLI) gives you command line access to all of their cloud utilities.

Particle CLI, which uses node.js, is a multitool for the Photon. You can use it to configure WiFi, list connected devices, compile code remotely, and – pertinent to this section of the tutorial – flash code over-the-air.

After installing Particle CLI, you can use the particle flash command to upload code you’ve compiled to your Photon remotely. All you need is the name of your Photon and the compiled BIN file.

For example, to upload the blink.bin file we created earlier, send this command (assuming you’re still in the “main” directory):

particle flash MY_PHOTON_NAME ../build/target/user-part/platform-6-m/blink.bin

Replace MY_PHOTON_NAME with that of your Photon, and it should quickly transition from breathing cyan (connected to cloud), to blinking magenta (flashing new application), to running your new application.

Adding Libraries

Adding a library to an app built with local gcc is a lot like adding a library using Particle Dev.

Download your library of interest (usually it’ll be on GitHub). Then move the library’s source files (excluding the “examples” folder) into your application folder.

For example, if you want to use the Weather Shield library, copy the .cpp and .h files, and stick them into your application folder.

Adding a library to an app

Finally, #include the library in your main application file and compile away!


Additional documentation on developing on Particle’s firmware with GCC can be found in the README in Particle’s firmware repository.

Resources & Going Further

For exhaustive documentation on the Photon, head over to particle.io. Here are a few links there that may be helpful:

Now that you have a handle on the development environment, what are you going to do with the Photon? Need some inspiration? Check out some of these related tutorials:

Weather Station Wirelessly Connected to Wunderground

Build your own open source, official Wunderground weather station that updates every 10 seconds over Wifi via an Electric Imp.

Pushing Data to Data.SparkFun.com

A grab bag of examples to show off the variety of routes your data can take on its way to a Data.SparkFun.com stream.

Photon IMU Shield Hookup Guide

Learn how to use the SparkFun Photon IMU Shield for your Photon device which houses an on-board LSM9DS1 system-in-a-chip that houses a 3-axis accelerometer, 3-axis gyroscope, and 3-axis magnetometer.

Photon Battery Shield Hookup Guide

The Photon Battery Shield has everything your Photon needs to run off, charge, and monitor a LiPo battery. Read through this hookup guide to get started using it.

Photon Wearable Shield Hookup Guide

Learn how to use the Photon Wearable Shield for your next projects!

Photon OLED Shield Hookup Guide

The Photon OLED Shield has everything you need to add a small yet crisp OLED screen to your Photon projects. This hookup guide will show you how to get started.

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

SparkFun Blocks for Intel® Edison - 9 Degrees of Freedom Block

$
0
0

SparkFun Blocks for Intel® Edison - 9 Degrees of Freedom Block a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t281

Introduction

The 9 Degrees of Freedom Block for the Intel® Edison uses the LSM9DS0 9DOF IMU for full-range motion sensing. Use this Block to determine orientation, acceleration, and compass headings. The LSM9DS0 combines a 3-axis accelerometer, a 3-axis gyroscope, and a 3-axis magnetometer. The IMU is connected to the Edison through the I2C bus.

9 Degree of Freedom Block

9 Degree of Freedom Block

Suggested Reading

If you are unfamiliar with Blocks, take a look at the General Guide to Sparkfun Blocks for Intel Edison.

Other tutorials that may help you on your Edison adventure include:

Board Overview

board overview

The 9DOF Block has a lot of jumpers on it, but you can use it without understanding or changing any of them. Here’s a description of each one:

A (INT2) - Accelerometer/magnetometer interrupt 2. This pin can be configured to change on a number of different conditions. See datasheet pp 58 and 65-67 for more details on configuring the device. Closing this jumper with a solder blob connects the INT2 pin on the LSM9DS0 to GPIO 49 on the Edison.

B (INT1) - Accelerometer/magnetometer interrupt 1. This pin can be configured to change on a number of different conditions. See datasheet pp 58 and 63-65 for more details on configuring the device. Closing this jumper with a solder blob connects the INT2 pin on the LSM9DS0 to GPIO 48 on the Edison.

C (DRDYG) - Data Ready, gyroscope. Closing this jumper connects the pin to GPIO 47. See datasheet page 43 for information on configuring this pin.

D (INTG) - Gyroscope interrupt. This pin can be configured to change on a number of different conditions. Closing this jumper will connect the pin to GPIO 46. See datasheet pages 43 and 47-50 for information on configuring this pin.

E (DEN) - Data enable, gyroscope. Enable or !pause data collection. This pin can safely be ignored. Closing this jumper allows processor control of data collection via GPIO 165.

F (CLOCK/DATA) - I/O interface selection jumpers. Default setting is to I2C1 but cutting the small trace visible between the two upper pads of each jumper and closing the bottom two pads with a solder blob allow the user to route control to SPIDEV2. SPI is currently an unsupported feature and will likely be removed from a future revision.

G (CSG) - SPI chip select, gyroscope. Closing this jumper connects the signal to GPIO 111 on the Edison, which is FS0 on SPIDEV2. The CS pin can be either handled manually or by the driver. SPI is currently an unsupported feature and will likely be removed from a future revision.

H (CSXM) - SPI chip select, accelerometer/magnetometer. Closing this jumper connects the signal to GPIO 110 on the Edison, which is FS1 on SPIDEV2. The CS pin can be either handled manually or by the driver. SPI is currently an unsupported feature and will likely be removed from a future revision.

I (SDOG) - SPI serial data out (MISO), gyroscope. SPI is currently an unsupported feature and will likely be removed from a future revision.

J (SDOXM) - Serial data out (MISO), accelerometer/magnetometer. SPI is currently an unsupported feature and will likely be removed from a future revision.

K (I2C PUs) - Pull-up resistor removal for I2C SDA and SCL lines. Most likely, you won’t want to remove these resistors from the system; however, if you have a lot of devices on the I2C bus, you may need to remove some of the pull-ups from the lines to reduce the pull-up strength.

L (CS PUs) - Pull-up resistor removal for SPI chip select lines. Normally pull-up resistors should be left in place. SPI is currently an unsupported feature and will likely be removed from a future revision.

M (SDOG PU) - Closed by default, this pin sets the I2C address used by the gyroscope. When closed, the gyroscope’s address is 0x6b. When open, jumper SDOG PD (labeled ‘O’ above) must be closed.

N (SDOXM PU) - Closed by default, this pin sets the I2C address used by the magnetometer/accelerometer. When closed, their collective address is 0x1d. When open, jumper SDOXM PD (labeled ‘P’ above) must be closed.

O (SDOG PD) - Open by default, this pin sets the I2C address used by the gyroscope. When closed, the gyroscope’s address is 0x6a.

P (SDOXM PD) - Open by default, this pin sets the I2C address used by the magnetometer/accelerometer. When closed, their collective address is 0x1e.

Connecting the 9 DOF Block

To use the 9 DOF Block simply attach an Intel Edison to the back of the board or add it to your current stack. Blocks can be stacked without hardware but it leaves the expansion connectors unprotected from mechanical stress.

Installed Block

9 DOF Block Installed

We have a nice Hardware Pack available that gives enough hardware to secure three blocks and an Edison.

alt text

Intel Edison Hardware Pack

NOTE: The 9 DOF Block does not have console access or a voltage regulator. It is recommended to use a console communication block in conjunction with this block like ones found in the General Guide to Sparkfun Blocks for Intel Edison.

C++ Code Examples

We’re assuming that you’re using the Eclipse IDE as detailed in our Beyond Arduino tutorial. If you aren’t, you’ll need to read over that tutorial to get up to speed.

Getting Started

Follow the instructions in the programming tutorial to create a new project named “SparkFun_9DOF_Edison_Block_Example”. Once you’ve created the project, open the project files on disk (hint: you can find the path to the project by choosing “Properites” from the project menu), and copy the three source files found in the Edison 9DOF Block CPP library GitHub repository into the “src” directory.

Download a zip file of the repository

Code

Everything you need to know is in the comments.

language:c
#include "mraa.hpp"
#include <iostream>
#include <unistd.h>
#include "SFE_LSM9DS0.h"
using namespace std;

int main()
{
  LSM9DS0 *imu;
  imu = new LSM9DS0(0x6B, 0x1D);
  // The begin() function sets up some basic parameters and turns the device
  //  on; you may not need to do more than call it. It also returns the "whoami"
  //  registers from the chip. If all is good, the return value here should be
  //  0x49d4. Here are the initial settings from this function:
  //  Gyro scale:        245 deg/sec max
  //  Xl scale:          4g max
  //  Mag scale:         2 Gauss max
  //  Gyro sample rate:  95Hz
  //  Xl sample rate:    100Hz
  //  Mag sample rate:   100Hz
  // These can be changed either by calling appropriate functions or by
  //  pasing parameters to the begin() function. There are named constants in
  //  the .h file for all scales and data rates; I won't reproduce them here.
  //  Here's the list of fuctions to set the rates/scale:
  //  setMagScale(mag_scale mScl)      setMagODR(mag_odr mRate)
  //  setGyroScale(gyro_scale gScl)    setGyroODR(gyro_odr gRate)
  //  setAccelScale(accel_scale aScl)  setGyroODR(accel_odr aRate)
  // If you want to make these changes at the point of calling begin, here's
  //  the prototype for that function showing the order to pass things:
  //  begin(gyro_scale gScl, accel_scale aScl, mag_scale mScl,
  //                gyro_odr gODR, accel_odr aODR, mag_odr mODR)
  uint16_t imuResult = imu->begin();
  cout<<hex<<"Chip ID: 0x"<<imuResult<<dec<<" (should be 0x49d4)"<<endl;

  bool newAccelData = false;
  bool newMagData = false;
  bool newGyroData = false;
  bool overflow = false;

  // Loop and report data
  while (1)
  {
    // First, let's make sure we're collecting up-to-date information. The
    //  sensors are sampling at 100Hz (for the accelerometer, magnetometer, and
    //  temp) and 95Hz (for the gyro), and we could easily do a bunch of
    //  crap within that ~10ms sampling period.
    while ((newGyroData & newAccelData & newMagData) != true)
    {
      if (newAccelData != true)
      {
        newAccelData = imu->newXData();
      }
      if (newGyroData != true)
      {
        newGyroData = imu->newGData();
      }
      if (newMagData != true)
      {
        newMagData = imu->newMData(); // Temp data is collected at the same
                                      //  rate as magnetometer data.
      }
    }

    newAccelData = false;
    newMagData = false;
    newGyroData = false;

    // Of course, we may care if an overflow occurred; we can check that
    //  easily enough from an internal register on the part. There are functions
    //  to check for overflow per device.
    overflow = imu->xDataOverflow() |
               imu->gDataOverflow() |
               imu->mDataOverflow();

    if (overflow)
    {
      cout<<"WARNING: DATA OVERFLOW!!!"<<endl;
    }

    // Calling these functions causes the data to be read from the IMU into
    //  10 16-bit signed integer public variables, as seen below. There is no
    //  automated check on whether the data is new; you need to do that
    //  manually as above. Also, there's no check on overflow, so you may miss
    //  a sample and not know it.
    imu->readAccel();
    imu->readMag();
    imu->readGyro();
    imu->readTemp();

    // Print the unscaled 16-bit signed values.
    cout<<"-------------------------------------"<<endl;
    cout<<"Gyro x: "<<imu->gx<<endl;
    cout<<"Gyro y: "<<imu->gy<<endl;
    cout<<"Gyro z: "<<imu->gz<<endl;
    cout<<"Accel x: "<<imu->ax<<endl;
    cout<<"Accel y: "<<imu->ay<<endl;
    cout<<"Accel z: "<<imu->az<<endl;
    cout<<"Mag x: "<<imu->mx<<endl;
    cout<<"Mag y: "<<imu->my<<endl;
    cout<<"Mag z: "<<imu->mz<<endl;
    cout<<"Temp: "<<imu->temperature<<endl;
    cout<<"-------------------------------------"<<endl;

    // Print the "real" values in more human comprehensible units.
    cout<<"-------------------------------------"<<endl;
    cout<<"Gyro x: "<<imu->calcGyro(imu->gx)<<" deg/s"<<endl;
    cout<<"Gyro y: "<<imu->calcGyro(imu->gy)<<" deg/s"<<endl;
    cout<<"Gyro z: "<<imu->calcGyro(imu->gz)<<" deg/s"<<endl;
    cout<<"Accel x: "<<imu->calcAccel(imu->ax)<<" g"<<endl;
    cout<<"Accel y: "<<imu->calcAccel(imu->ay)<<" g"<<endl;
    cout<<"Accel z: "<<imu->calcAccel(imu->az)<<" g"<<endl;
    cout<<"Mag x: "<<imu->calcMag(imu->mx)<<" Gauss"<<endl;
    cout<<"Mag y: "<<imu->calcMag(imu->my)<<" Gauss"<<endl;
    cout<<"Mag z: "<<imu->calcMag(imu->mz)<<" Gauss"<<endl;
    // Temp conversion is left as an example to the reader, as it requires a
    //  good deal of device- and system-specific calibration. The on-board
    //  temp sensor is probably best not used if local temp data is required!
    cout<<"-------------------------------------"<<endl;
    sleep(1);
  }

  return MRAA_SUCCESS;
}

Resources and Going Further

Now that you have had a brief overview of the 9 DOF Block, take a look at some of these other tutorials. These tutorials cover programming, Block stacking, and interfacing with the Intel Edison ecosystems.

Edison General Topics:

Block Specific Topics:

Check out these other Edison related tutorials from SparkFun:

SparkFun Blocks for Intel® Edison - UART Block

A quick overview of the features of the UART Block.

SparkFun Blocks for Intel® Edison - Base Block

A quick overview of the features of the Base Block.

SparkFun Blocks for Intel® Edison - GPIO Block

A quick overview of the features of the GPIO Block.

Loading Debian (Ubilinux) on the Edison

How to load a Debian distribution (specifically Ubilinux) onto the Edison.

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

LED Butterfly Pop Up Card

$
0
0

LED Butterfly Pop Up Card a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t407

Introduction

Craft an illuminated butterfly pop up card with copper tape, two LEDs, and a battery.

alt text

Suggested Reading

If you are brand new to working with electronics, here’s some helpful reading to check out:

Materials and Tools

Here is a list of all the materials and tools you’d need to follow along:

Notes:

Additional Materials:

  • Cardstock (2-3 pieces)
  • Vellum, wax paper, rice or parchment paper- creates a nice diffused effect for LEDs in the butterfly’s wings
  • Clear Tape
  • Gluestick/Glue
  • Scissors/Hobby Knife
  • Needle Nose Pliers
  • Decorating Supplies - stickers, markers, or paints to embellish your designs

Step 1: Print Template

Right-click the images below, and choose “Save Link As” to download the templates to your computer. Each file has a circuit template page and a pop up template page.

Print your templates out on cardstock. If needed, adjust your printer’s margins, or choose ‘Fit to Page’ in the print settings. The card template is slightly smaller than the paper, so make sure to cut along the black border for the final card size.

Set the pop up page aside for now. We’ll build our circuit first and then assemble the pop up once the electronics are all installed.

Butterfly Template - 2 pages

alt text

If you have a Silhouette Electronic Cutter, click here to download a Silhouette Studio file for the pop up layer

Step 2: Create Copper Traces

Time to create a path for our electricity with copper tape.

alt text

Line A

Take a look at the template and find the circle marked A. Peel away a few inches of the paper backing from the copper tape and stick down along the grey line. Cut when you reach the scissors icon.

alt text

Line B

Next we’ll place tape along Line B which includes a corner. To keep a solid connection of copper around corners, we’ll be using a folding technique to press the tape into shape.

Start by sticking the copper tape down until you reach the corner, then fold the tape backward on itself. Use a fingernail or pen to give it a good crease at the edge.

alt text

Then carefully move the tape down around the corner - you should see the fold forming - and press down flat against the paper. The neatness of the fold doesn’t matter that much, it will be covered by your pop up in the end. Finally, cut the tape when you reach the scissors icon.

alt text

alt text

alt text

Line C

The last copper tape line will also form a battery holder. We’ll start by folding ½" of copper tape onto itself, sticking the adhesive sides together to form a flap.

alt text

This allows the top of the copper to fold down over the coin cell battery - the positive side of the battery is the top and negative side is the bottom, which allows us to create a ‘battery sandwich’ with copper tape touching each side.

alt text

See the diagrams below to explore how this method works. We won’t be installing the battery until the end of our project, so set that aside for now. Continue following Line C (around two corners) until the scissors icon.

alt text

alt text

Step 3: Prepare and Place LEDs

Before prepping the LED, fold the card in half along the dotted line to save the hassle of trying to make a neat fold once there are components sticking up from the paper.

Now that our copper is in place, time to add the LEDs. The template has two LED symbols which show shaped wires - we use this method to help us remember which side is positive and negative on the LED.

Read more about LED polarity in our Light-emitting Diodes (LEDs) Tutorial .

alt text

Here are directions for bending each 3mm LED (as shown in the image above) to prepare it for our circuit.

Using pliers (or your finger), bend the longer leg of the LED flat.

alt text

Then form the wire into an L, it helps to use the copper tape lines as a reference for where the bend should be.

alt text

Next, bend the end of the wire into a zig zag shape. Be careful not to break the wire by bending back and forth over the same joint too many times.

alt text

Now for the other side of the LED. Bend the other leg flat and curl into a spiral. Use the end of the pliers to lightly grab the end of the wire and curl around the tool.

alt text

Once all shaping is complete, place the LEDs on a table or flat surface to make sure they sit flat and upright. If not, make any adjustments now.

Tape Down LEDs

Line up the positive leads with the copper tape marked + and the negatives with -. Use clear tape over the leads to hold down to the copper.

alt text

Step 4: Attach Button

alt text

Next, we’ll place the LilyPad button over the oval icon on the template facing up. It doesn’t matter which side touches postive and negative. Make sure the conductive pads on the bottom of the button touch the copper tape, then tape down the ends with clear tape. Be careful not to tape directly over the push part of the button, or it may interfere with the ability to press it. You can also use a LilyPad switch instead of a button - the installation is the same.

alt text

Step 5: Insert Battery

Once all the components are installed, it’s time to test our circuit by adding a battery. Carefully slip the battery underneath the copper tape flap we made earlier, and center it inside the circle icon. Make sure the positive side of the battery (top, marked with the battery model and +) is facing up. Press the copper over the battery, and tape with clear tape.

alt text

Now, press the button, and the LED should light up!

alt text

alt text

Troubleshooting

  • Check the tape connections - use your nails or a pencil to make sure the tape is firmly adhering the components to the copper tape.
  • Check the battery - make sure it is sandwiched firmly between the top and bottom copper tape lines and that the top copper is not accidentally touching the bottom of the battery.
  • Check the wires of the LED - double check that they weren’t accidentally broken while bending them into shapes with pliers.

Here is what the finished circuit should look like:

Step 6: Prepare Pop Up

Use a hobby knife to cut along the lines of the pop up template.

Tape or glue pieces of vellum or a semi-transparent paper to the back of the wings to help diffuse the light from the LEDs.

alt text

Fold the page in half and crease.

alt text

Then flip the pop up over to the front side and carefully pull the wings toward you to make them pop out from the page.

alt text

Step 7: Assemble and Admire

Place the butterfly pop up over the copper tape circuit and use glue or tape to attach the corners (make sure not to accidentally glue the wings down). Gently fold down the pop up to close the card.

Finally, use a marker or stickers to indicate where the button should be pressed. Add extra decorations and embellishments to make your project unique!

alt text

Resources and Going Further

Things to try:

  • Try these techniques with other pop up designs or create your own - can you figure out how to adapt the copper tape path to fit your card choice?
  • Get crafty with stickers, paint, or markers after you’ve built your card to customize it even more.

Further Reading:


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

LSM9DS1 Breakout Hookup Guide

$
0
0

LSM9DS1 Breakout Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t373

Introduction

The LSM9DS1 is a versatile, motion-sensing system-in-a-chip. It houses a 3-axis accelerometer, 3-axis gyroscope, and 3-axis magnetometer – nine degrees of freedom (9DOF) in a single IC! Each sensor in the LSM9DS1 supports a wide range of…ranges: the accelerometer’s scale can be set to ± 2, 4, 8, or 16 g, the gyroscope supports ± 245, 500, and 2000 °/s, and the magnetometer has full-scale ranges of ± 2, 4, 12, or 16 gauss. The IMU-in-a-chip is so cool we put it on a quarter-sized breakout board.

LSM9DS1 with quarter for scale

The LSM9DS1 is equipped with a digital interface, but even that is flexible: it supports both I2C and SPI, so you’ll be hard-pressed to find a microcontroller it doesn’t work with.

Covered In This Tutorial

This tutorial is devoted to all things LSM9DS1. We’ll introduce you to the chip itself, then the breakout board. Then we’ll switch over to example code, and show you how to interface with the board using an Arduino and our LSM9DS1 Arduino library.

The tutorial is split into the following pages:

Required Materials

This tutorial explains how to use the LSM9DS1 Breakout Board with an Arduino. To follow along, you’ll need the following materials:

Example hookup

The LSM9DS1 is a 3.3V device! Supplying voltages greater than ~3.6V can permanently damage the IC. As long as your Arduino has a 3.3V supply output, and you're OK with using I2C, you shouldn't need any extra level shifting. But if you want to use SPI, you may need a level shifter.

A logic level shifter is required for any 5V-operating Arduino (UNO, RedBoard, Leonardo, etc). If you use a 3.3V-based ‘duino – like the Arduino Pro 3.3V or 3.3V Pro Mini– there is no need for level shifting.

Suggested Reading

If you’re not familiar with some of the concepts below, we recommend checking out that tutorial before continuing on.

LSM9DS1 Overview

The LSM9DS1 is one of only a handful of IC’s that can measure three key properties of movement – angular velocity, acceleration, and heading – in a single IC.

The gyroscope can measure angular velocity– that is “how fast, and along which axis, am I rotating?” Angular velocities are measured in degrees per second– usually abbreviated to DPS or °/s. The LSM9DS1 can measure up to ± 2000 DPS, though that scale can also be set to either 245 or 500 DPS to get a finer resolution.

An accelerometer measures acceleration, which indicates how fast velocity is changing – “how fast am I speeding up or slowing down?” Acceleration is usually either measured in m/s2 (meters per second per second) or g’s (gravities [about 9.8 m/s2]). If an object is sitting motionless it feels about 1 g of acceleration towards the ground (assuming that ground is on earth, and the object is near sea-level). The LSM9DS1 measures its acceleration in g’s, and its scale can be set to either ± 2, 4, 8, or 16 g.

Finally, there’s the magnetometer, which measures the power and direction of magnetic fields. Though they’re not easily visible, magnetic fields exist all around us – whether you’re holding a tiny ferromagnet or feeling an attraction to Earth’s magnetic field. The LSM9DS1 measures magnetic fields in units of gauss (Gs), and can set its measurement scale to either ± 4, 8, 12, or 16 Gs.

By measuring these three properties, you can gain a great deal of knowledge about an object’s movement and orientation. 9DOF’s have tons and tons of applications. Measuring the force and direction of Earth’s magnetic field with a magnetometer, you can approximate your heading. An accelerometer in your phone can measure the direction of the force of gravity, and estimate orientation (portrait, landscape, flat, etc.). Quadcopters with built-in gyroscopes can look out for sudden rolls or pitches, and correct their momentum before things get out of hand.

Axis labels for the LSM9DS1 IC

Axis orientations of the LSM9DS1 IC. Note the IC’s polarity-marking dot (for some reason they rotated the magnetometer in the datasheet).

The LSM9DS1 measures each of these movement properties in three dimensions. That means it produces nine pieces of data: acceleration in x/y/z, angular rotation in x/y/z, and magnetic force in x/y/z. The LSM9DS1 Breakout has labels indicating the accelerometer and gyroscope axis orientations, which share a right-hand rule relationship with each other. Note that, according to the datasheet, the x and y axes of the magnetic sensor are flipped (the image above is copied from the datasheet).

The LSM9DS1 is, in a sense, two IC’s smashed into one package – like if you combined an LSM6DS3 accel/gryo with an LSM303DLMTR accel/mag. One half of the device takes care of all-things gyroscope and accelerometer, and the other half manages solely the magnetometer. In fact, a few of the control pins are dedicated to a single sensor – there are two chip select pins (CS_AG for the accel/gyro and CS_M for the mag) and two serial data out pins (SDO_AG and SDO_M).

Choose Your Own Adventure: SPI or I2C

In addition to being able to measure a wide variety of movement vectors, the LSM9DS1 is also multi-featured on the communication interface end. It supports both SPI and I2C, so you should have no difficulty finding a microcontroller that can talk to it.

SPI is generally the easier of the two to implement, but it also requires more wires – four versus I2C’s two.

Because the LSM9DS1 supports both methods of communication, some pins have to pull double-duty. The Serial Data Out (SDO) pin for example, does just that for SPI mode, but if you’re using the device over I2C it becomes an address selector. The chip select (CS_M and CS_AG) pins activate SPI mode when low, but if they’re pulled high the device assumes I2C communication. In the section below we discuss each of the LSM9DS1’s pins, watch out for those pins that support both interfaces.

For much more detailed information about the IC, we encourage you to check out the datasheet!

Breakout Board Overview

Now that you know everything you need to about the LSM9DS1 IC, let’s talk a bit about the breakout board it’s resting on. On this page we’ll discuss the pins that are broken out, and some of the other features on the board.

The Pinout

In total, the LSM9DS1 Breakout breaks out 13 pins.

Top image

The bare-minimum connections required are broken out on the left side of the board. These are the power and I2C pins (the communication interface the board defaults to):

Pin LabelPin FunctionNotes
GNDGround0V voltage supply
VDDPower SupplySupply voltage to the chip. Should be regulated between 2.4V and 3.6V.
SDASPI: MOSI
I2C: Serial Data
SPI: Device data in (MOSI)
I2C: Serial data (bi-directional)
SCLSerial ClockI2C and SPI serial clock.

The remaining pins are broken out on the other side. These pins break out SPI functionality and interrupt outputs:

Pin LabelPin FunctionNotes
DENGyroscope Data EnableMostly unknown. The LSM9DS1 datasheet doesn't have much to say about this pin.
INT2Accel/Gyro Interrupt 2INT1 and INT2 are programmable interrupts for the accelerometer and gyroscope. They can be set to alert on over/under thresholds, data ready, or FIFO overruns.
INT1Accel/Gyro Interrupt 1
INTMMagnetometer InterruptA programmable interrupt for the magnetometer. Can be set to alert on over-under thresholds.
RDYMagnetometer Data ReadyAn interrupt indicating new magnetometer data is available. Non-programmable.
CS MMagnetometer Chip SelectThis pin selects between I2C and SPI on the magnetometer. Keep it HIGH for I2C, or use it as an (active-low) chip select for SPI.
HIGH (1): SPI idle mode / I2C enabled
LOW (0): SPI enabled / I2C disabled.
CS AGAccel/Gyro Chip SelectThis pin selects between I2C and SPI on the accel/gyro. Keep it HIGH for I2C, or use it as an (active-low) chip select for SPI.
HIGH (1): SPI idle mode / I2C enabled
LOW (0): SPI enabled / I2C disabled.
SDO MSPI: Magnetometer MISO
I2C: Magnetometer Address Select
In SPI mode, this is the magnetometer data output (SDO_M).
In I2C mode, this selects the LSb of the I2C address (SA0_M)
SDO AGSPI: Accel/Gyro MISO
I2C: Accel/Gryo Address Select
In SPI mode, this is the accel/gryo data output (SDO_AG).
In I2C mode, this selects the LSb of the I2C address (SA0_AG)

Power Supply

The VDD and GND pins are where you’ll supply a voltage and 0V reference to the IC. The breakout board does not regulate this voltage, so make sure it falls within the allowed supply voltage range of the LSM9DS1: 2.4V to 3.6V. Below is the electrical characteristics table from the datasheet.

Electrical characteristics

The communication pins are not 5V tolerant, so they’ll need to be regulated to within a few mV of VDD.

Another very cool thing about this sensor is how low-power it is. In normal operation – with every sensor turned on – it’ll pull around 4.5mA.

Communication

CS_AG, CS_M, SDO_AG, SDO_M, SCL, and SDA are all used for the I2C and SPI interfaces. The function of these pins depends upon which of the two interfaces you’re using.

If you’re using using I2C here’s how you might configure these pins:

  • Pull CS_AG and CS_M HIGH. This will set both the accel/gyro and magnetometer to I2C mode.
  • Set SDO_AG and SDO_M either HIGH or LOW. These pins set the I2C address of the gyro and accel/mag sensors.
  • Connect SCL to your microcontroller’s SCL pin.
  • Connect SDA to your microcontroller’s SDA pin.
  • The board has a built-in 10kΩ pull-up resistor on both SDA and SCL lines. If that value is too high, you can add a second resistor in parallel to divide the pull-up resistance down (another 10kΩ in parallel, for example, would create an equivalent 5kΩ resistance).

Or, if you’re using SPI:

  • Connect CS_AG and CS_M to two individually controllable pins on your microcontroller. These chip-selects are active-low – when the pin goes LOW, SPI communication with either the accel/gyro (CS_AG) or magnetometer (CS_M) is enabled.
  • SDO_AG and SDO_M are the serial data out pins. In many cases you’ll want to connect them together, and wire them to your microcontroller’s MISO (master-in, slave-out) pin.
  • Connect SCL to your microcontroller’s SCLK (serial clock) pin.
  • Connect SDA to your microcontroller’s MOSI (master-out, slave-in) pin.

Interrupts

There are a variety of interrupts on the LSM9DS1. While connecting up to these is not as critical as the communication or power supply pins, using them will help you get the most out of the chip.

The accelerometer- and gyroscope-specific interrupts are INT1 and INT2. These can both be programmed to interrupt as either active-high or active-low, triggering on events like data ready or when an acceleration or angular rotation exceeds a set threshold.

DRDY and INTM are devoted magnetometer interrupts. DRDY will go low when new magnetometer readings are available. INTM is a little more customizable – it can be used to trigger whenever magnetic field readings exceed a threshold on a set axis.

The Jumpers

Flipping the LSM9DS1 breakout over reveals a trio of two-way, surface mount jumpers. Each of these jumpers comes closed. Their purpose is to automatically put the LSM9DS1 into I2C mode.

Jumpers on breakout

The three two-way jumpers on the back of the board. Follow the labels to see which pin they pull up.

Each of these jumpers pulls a pair of pins up to VDD, through a 10kΩ resistor. The middle pad of the jumper connects to the resistor, and the edge pads connect to a pin (follow the labels to find out which one). You can see how those jumpers match up on the schematic:

Jumpers on schematic

The top jumper connects CS_AG and CS_M to a pull-up – this’ll set the LSM9DS1 to I2C mode. The middle jumper pulls up SDO_AG and SDO_M, which sets the I2C address of the chip. Finally, the far-left jumper adds pull-up resistors to the I2C communication pins – SDA and SCL.

The intention of these jumpers is to make it as easy-as-possible to use the board; using as few wires as possible. If you’re using the breakout with I2C, you can ignore the four SDO and CS pins.

To disable any of these jumpers, whip out your handy hobby knife, and carefully cut the small traces between middle pad and edge pads. Even if you’re using SPI, though, the jumpers shouldn’t hinder your ability to communicate with the chip.

Hardware Assembly

On this page we’ll discuss assembly hints. There’s really not much to assembling the breakout board – the real key is solderingsomething into the breakout holes.

Solder Something

To get a solid electrical and physical connection to the LSM9DS1 Breakout, you’ll need to solder either connectors or wires to the break-out pins. What, exactly, you solder into the board depends on how you’re going to use it.

If you’re going to use the breakout board in a breadboard or similar 0.1"-spaced perfboard, we recommend soldering straight male headers into the pins (there are also long headers if you need ‘em).

I2C and power pins soldered

If you’re only going to use the I2C interface – and ignore the interrupts – you can get away with soldering just the four-pin header.

If you’re going to mount the breakout into a tight place, you may want to opt for soldering wires (stranded or solid-core) into the pins.

Mounting the Breakout

Because the LSM9DS1 senses motion, it’s important (for most applications, at least) to keep it pinned in place. So the boards have four mounting holes toward the corners. The drill holes are 0.13" in diameter, so they should accommodate any 4/40 screw.

LSM9DS1 Breakout dimensions

Consult the EAGLE PCB design files to find out more about the Breakout’s dimensions.

Hardware Hookup

The LSM9DS1 will work over either I2C or SPI. Here are some example wiring diagrams, demonstrating how to wire up each interface.

I2C Hardware Hookup

Out-of-the-box, the board is configured for an I2C interface, as such we recommend using this hookup if you’re otherwise agnostic. All you need is four wires!

i2c fritzing hookup

Connecting the LSM9DS1 to a RedBoard via I2C.

This hookup relies on all of the jumpers on the back of the board being set (as they should be, unless they’ve been sliced). If the jumpers have been cut, connect all four CS and SDO pins to 3.3V.

No level shifters even though the Arduino’s I/O pins are 5V? Well, I2C is a funny interface: pins aren’t directly pulled high by a GPIO, instead an open-drain MOSFET relies on pull-up resistors to create a logic high. Because the breakout board pull-up resistors are stronger (less resistance) than the Arduino’s internal pull-ups, the voltage on the logic pins will be much closer to 3.3V (though a little higher) than 5V – within a tolerable range. Just make sure you power the breakout off of the Arduino’s 3.3V power rail!

SPI Hardware Hookup

For the SPI hookup, we’ll use the Arduino’s hardware SPI pins – 11 (MISO), 12 (MOSI), and 13 (SCLK) – in addition to two digital pins of your choice (for the chip selects). If you’re using a 3.3V Arduino, like the 3.3V/8MHz Pro Mini, you can hook the microcontroller pins directly up to the LSM9DS1.

SPI hookup

If you’re using a 5V Arduino, you’ll also need to grab a level shifter to keep the SPI signals within the tolerable voltage range. You could use the SparkFun Bi-Directional Logic Level Converter for example:

Logic level shifter

(Unless you enjoy wire-tangles, the I2C or 3.3V SPI hookups are recommended.)

Installing the Arduino Library

We’ve written a full-featured Arduino library to help make interfacing with the LSM9DS1’s gyro, accelerometer, and magnetometer as easy-as-possible. Visit the GitHub repository to download the most recent version of the library, or click the link below:

Download the SparkFun LSM9DS1 Arduino Library

For help installing the library, check out our How To Install An Arduino Library tutorial. You’ll need to move the SparkFun_LSM9DS1_Arduino_Library folder into a libraries folder within your Arduino sketchbook.

The LSM9DS1_Basic_I2C Example

To verify that your hookup works, load up the LSM9DS1_Basic_I2C example by going to File>Examples>LSM9DS1 Breakout>LSM9DS1_Basic_I2C. (If you’re using the SPI hookup, load the LSM9DS1_Basic_SPI example instead.)

The default values set by this sketch should work for a fresh, out-of-the-box LSM9DS1 Breakout – it assumes all of the jumpers on the backside are closed. Upload the sketch, then open up your serial monitor, setting the baud rate to 115200. You should see something like this:

Example serial monitor output

The current reading from each axis on each sensor is printed out, then those values are used to estimate the sensor’s orientation. Pitch is the angle rotated around the y-axis, roll is the board’s rotation around the x-axis, and heading (i.e. yaw) is the sensor’s rotation around the z-axis. Try rotating the board (without pulling out any wires!) to see how the values change.

Using the Arduino Library

To help demonstrate the library’s functionality, a handful of examples are included which range from basic to more advanced. To begin to get a feel for the library’s API, try loading up some of the other examples. The comments at the top of the sketch will instruct you on any extra hookup that may be required to use interrupts or other features.

On this page we’ll quickly run down some of the more basic, fundamental concepts implemented by the library.

Setup Stuff

To enable the library, you’ll need to include it, and you also need to include the SPI and Wire libraries:

language:c
#include <SPI.h> // SPI library included for SparkFunLSM9DS1
#include <Wire.h> // I2C library included for SparkFunLSM9DS1
#include <SparkFunLSM9DS1.h> // SparkFun LSM9DS1 library

Make sure the SPI and Wire includes are above the “SparkFunLSM9DS1” (even though you’ll only be using one of the two interfaces).

Constructor

The constructor creates an instance of the LSM9DS1 class. Once you’ve created the instance, that’s what you’ll use to control the breakout from there on. This single line of code is usually placed in the global area of your sketch.

The constructor should be left without any parameters:

language:c
// Use the LSM9DS1 class to create an object. [imu] can be
// named anything, we'll refer to that throught the sketch.
LSM9DS1 imu;

Setting Up the Interface

The LSM9DS1 has tons of settings to be configured. Some are minute, others are more critical. The three most critical settings we’ll need to configure are the communication interface and the device addresses. To configure these values, we’ll make calls to the IMU’s settings struct.

Here’s an example that sets the IMU up for I2C mode, with the default (high) I2C addresses:

language:c
// SDO_XM and SDO_G are both pulled high, so our addresses are:
#define LSM9DS1_M   0x1E // Would be 0x1C if SDO_M is LOW
#define LSM9DS1_AG  0x6B // Would be 0x6A if SDO_AG is LOW
...
imu.settings.device.commInterface = IMU_MODE_I2C; // Set mode to I2C
imu.settings.device.mAddress = LSM9DS1_M; // Set mag address to 0x1E
imu.settings.device.agAddress = LSM9DS1_AG; // Set ag address to 0x6B

Alternatively, if you’re using SPI mode, the imu.settings.device.mAddress and imu.settings.device.agAddress values become the chip select pins. For example, if you’re using SPI mode with CS_AG connected to D10 and CS_M connected to D9, your setting configuration would look like this:

language:c
imu.settings.device.commInterface = IMU_MODE_SPI; // Set mode to SPI
imu.settings.device.mAddress = 9; // Mag CS pin connected to D9
imu.settings.device.agAddress = 10; // AG CS pin connected to D10

Configuring any value from the imu.settings.device can’t take place in the global are of a sketch. If you get a compilation error, like 'imu' does not name a type, you may have those in the wrong place – put them in setup().

begin()-ing and Setting Sensor Ranges

Once you’ve created the LSM9DS1 object, and configured its interface, call the begin() member function to initialize the IMU.

The begin() function will take the settings you’ve adjusted in the previous step, and attempt to communicate with and initialize the sensors. You can check the return value of begin() to verify whether or not the set up was successful – it will return 0 if something goes wrong.

language:c
if (!imu.begin())
{
    Serial.println("Failed to communicate with LSM9DS1.");
    Serial.println("Looping to infinity.");
    while (1)
      ;
}

Once begin() has returned a success, you can start reading some sensor values!

Reading and Interpreting the Sensors

What good is the sensor if you can’t get any data from it!? Here are the functions you’ll need to get acceleration, rotation speed, and magnetic field strength data from the library.

readAccel(), readGyro(), and readMag()

These three functions – readAccel(), readGyro(), and readMag()– poll the LSM9DS1 to get the most up-to-date readings from each of the three sensors.

The read functions don’t take any parameters, and they don’t return anything, so how do you get that data? After the function runs its course, it’ll update a set of three class variables, which will have the sensor data you desire. readAccel() will update ax, ay, and az, readGyro() will update gx, gy, and gz, and readMag() will update mx, my, and mz. Here’s an example:

language:c
imu.readAccel(); // Update the accelerometer data
Serial.print(imu.ax); // Print x-axis data
Serial.print(", ");
Serial.print(imu.ay); // print y-axis data
Serial.print(", ");
Serial.println(imu.az); // print z-axis data

An example of reading and printing all three axes of accelerometer data.

Those values are all signed 16-bit integers, meaning they’ll range from -32,768 to 32,767. That value doesn’t mean much unless you know the scale of your sensor, which is where the next functions come into play.

calcAccel(), calcGyro(), and calcMag()

The library keeps track of each sensor’s scale, and it implements these helper functions to make translating between the raw ADC readings of the sensor to actual units easy.

calcAccel(), calcGyro(), and calcMag() all take a single parameter – a signed 16-bit integer – and convert to their respective units. They all return a float value, which you can do with as you please.

Here’s an example of printing calculated gyroscope values:

language:c
imu.readGyro(); // Update gyroscope data
Serial.print(imu.calcGyro(imu.gx)); // Print x-axis rotation in DPS
Serial.print(", ");
Serial.print(imu.calcGyro(imu.gy)); // Print y-axis rotation in DPS
Serial.print(", ");
Serial.println(imu.calcGyro(imu.gz)); // Print z-axis rotation in DPS

Setting Sensor Ranges and Sample Rates

Some of the more commonly altered attributes in the IMU are the sensor ranges and output data rates. These values can be configured, once again, by setting a value in the settings struct.

For example, to set the IMU’s accelerometer range to ±16g, gyroscope to ±2000 °/s, and magnetometer to ±8 Gs, do something like this:

language:c
imu.settings.accel.scale = 16; // Set accel range to +/-16g
imu.settings.gyro.scale = 2000; // Set gyro range to +/-2000dps
imu.settings.mag.scale = 8; // Set mag range to +/-8Gs
imu.begin(); // Call begin to update the sensor's new settings

The output data rates are a bit more abstract. These values can range from 1-6, where 1 is the slowest update rate and 6 is the fastest.

Resources & Going Further

Hopefully that info dump was enough to get you rolling with the LSM9DS1. If you need any more information, here are some more resources:

Going Further

Now that you’ve got the LSM9DS1 up-and-running, what project are you going to incorporate motion-sensing into? Need a little inspiration? Check out some of these tutorials!

  • Dungeons and Dragons Dice Gauntlet– This project uses an accelerometer to sense a “rolling the dice” motion. You could swap in the LSM9DS1 to add more functionality – like compass-based damage multipliers!
  • Are You Okay? Widget– Use an Electric Imp and accelerometer to create an “Are You OK” widget. A cozy piece of technology your friend or loved one can nudge to let you know they’re OK from half-a-world away.
  • Leap Motion Teardown– An IMU sensor is cool, but image-based motion sensing is the future. Check out this teardown of the miniature-Kinect-like Leap Motion!
  • Pushing Data to Data.SparkFun.com– Need an online place to store your IMU data? Check out data.sparkfun.com! This tutorial demonstrates how to use a handful of Arduino shields to post your data online.

Leap Motion Teardown

Let's see what's inside the amazing new Leap Motion input device!

Dungeons and Dragons Dice Gauntlet

A playful, geeky tutorial for a leather bracer that uses a LilyPad Arduino, LilyPad accelerometer, and seven segment display to roll virtual 4, 6, 8, 10, 12, 20, and 100 side dice for gaming.

Are You Okay? Widget

Use an Electric Imp and accelerometer to create an "Are You OK" widget. A cozy piece of technology your friend or loved one can nudge to let you know they're OK from half-a-world away.

Pushing Data to Data.SparkFun.com

A grab bag of examples to show off the variety of routes your data can take on its way to a Data.SparkFun.com stream.

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

MPU-9150 Hookup Guide

$
0
0

MPU-9150 Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t397

Introduction

The MPU-9150 is a nine degrees of freedom (9DOF) inertial measurement unit (IMU) in a sigle package. It houses a 3-axis accelerometer, 3-axis gyroscope, 3-axis magnetometer and a Digital Motion Processor™ (DMP™) hardware accelerator engine. The range of each sensor is configurable: the accelerometer's scale can be set to ±2g, ±4g, ±6g, ±8g, or ±16g, the gyroscope supports ±250, ±500, and ±2000 °/s, and the magnetometer has full-scale range of ±1200µT (±12 gauss).

MPU-9150

The MPU-9150 supports I2C. There is a single reference to in the datasheet for SPI, but all other evidence points to the contrary. We have only testing using I2C, and, for the purposes of this tutorial, we will only be covering how to use this device in I2C mode.

Covered In This Tutorial

This tutorial is devoted to all things MPU-9150. First we'll introduce you to the breakout board. Then we'll switch over to example code and show you how to interface with the board using an Arduino and our [SFE_MPU-9150 Arduino library](https://cdn.sparkfun.com/assets/learn_tutorials/3/9/7/MPU-9150_libraries.zip).

The tutorial is split into the following sections:

Required Materials

This tutorial explains how to use the MPU-9150 Breakout Board with an Arduino. To follow along, you’ll need the following materials:

The MPU-9150 is a 3.3V device! Supplying voltages greater than 6V can permanently damage the IC. InvenSense recommends running from 2.375V to 3.465V. As long as your Arduino has a 3.3V supply output, and you're OK with using I2C, you shouldn't need any extra level shifting.

Suggested Reading

If you’re not familiar with some of the concepts below, we recommend checking out that tutorial before continuing on.

Hardware Overview

The Pinout

In total, the MPU-9150 Breakout breaks out 11 pins.

MPU-9150 product photo

The bare-minimum connections required are broken out on the left side of the board. These are the power and I2C pins (the communication interface the board defaults to):

Pin LabelPin FunctionNotes
GNDGround0V voltage supply
VCCPower SupplySupply voltage to the chip. Should be regulated between 2.375V and 3.465V.
SDAI2C: Serial DataI2C: Serial data (bi-directional)
SCLSerial ClockI2C serial clock (up to 400kHz)

The remaining pins break out additional functionality and interrupt outputs:

Pin LabelPin FunctionNotes
ESDAuxiliary I2C master serial data9150 can act as a master or pass-through to additional I2C devices.
ESCAuxiliary I2C master serial clock9150 can act as a master or pass-through to additional I2C devices.
COUTClock outputOutputs a 50% duty cycle square wave (see register map).
CINClock inputOptional external reference clock input, grounded through jumper.
I using an external clock source, cut 'CLK' jumper.
AD0I2C Slave Address LSBIf low I2C address is 0x68, else 0x69. Connected to jumper. (0x68 default)
FSYNCFrame synchronizationExternal frame sync input that latches to capture external bus interrupts
If using this pin cut 'SYNC' jumper.
INTInterrupt pinConfigurable digital output to signal the host processor of an event.

Power Supply

The VCC and GND pins are where you'll supply a voltage and 0V reference to the IC. The breakout board does not regulate this voltage, so make sure it falls within the allowed supply voltage range of the MPU-9150: 2.375V to 3.465V. Logic voltage levels can be as low as 1.8V ±5% up to VCC.

The communication pins are not 5V tolerant, so they’ll need to be regulated to within a few mV of VDD.

Communication

SDA, SCL, ESD, and ESC are used for the I2C interfaces. The auxiliary clock and data pins will require external pull-up resistors. These ave to be tuned on a case-by-case basis depending on bus capacitance to get proper rise times.

SDA and SCL have integrated 10KΩ pull-ups. If you plan on using more than one I2C device on the bus, you might want to remove these pull-ups. If the I2C lines are pulled-up too strongly by multiple sets of pull-ups, the bus will likely be out of spec and not function correctly.

The following image shows a stock PCBA on the left, and one with the pull-up resistors removed on the right. To remove these, I recommend reflowing a fair amount of solder onto the resistors. It doens't take very long to fully cover all 4 joints with molten solder and the two part will slide right off. So easily in my case that I wasn't able to capture a picture of the excess solder before the parts had come off. Wick off the excess solder, wipe clean with some DI water, and you are done.

Pull-ups removed

Interrupts

There are a variety of interrupts on the MPU-9150. While connecting up to these is not as critical as the communication or power supply pins, using them will help you get the most out of the chip. The INT pin is a digital output to the host controller. The FSYNC pin can be configured as an interrupt input to optionally be passed through out the INT pin.

These can be programmed to interrupt as either active-high or active-low. More details on these configurations can be found in the product register map.

The Jumpers

The most commonly used jumper will be to the address pin (AD0). It defaults to being pulled to ground selecting address 0x68, but with a soldering iron can be changed to be pulled up, switching the I2C address to 0x69.

The AD0 pin broken out is connected directly to ground by default. Alternatively it's connected straight to VCC. Make sure to remove the solder from both sides of the 3-way jumper before connecting it externally.

Address jumper

The address selection jumper on the front of the board. Allows you to select the LSB of the I2C address

The intention of these jumpers is to make it as easy-as-possible to use the board; using as few wires as possible. The CLK jumper is used to ground the external clock input as the datasheet recommends when not using an external clock. Make sure to cut this jumper if you attach an external clock to the CIN connection. The SYNC jumper ties FSYNC to ground as the manufacturer instructs one to do when not using it. Make sure to cut this jumper if use the FSYNC through hole connection.

Back of MPU-9150 Breakout

To disable any of these jumpers, whip out your handy hobby knife, and carefully cut the small traces between middle pad and edge pads.

Funny Business

Be very careful trusting the datasheet. It's full of inconsistencies or lacking clarification. For example pin 22. There is a single reference to it functioning as a clock output (next image). There are several places stating that it's not to be used, such as the table shown two images below.

Clock output, is it real?

Only reference to CLKOUT in datasheet


Clock output, is it real?

One of several examples of pin 22 being recommended to not be connected or a clock

The register map reads "This bit also enables the clock output.", but that's the only reference. It's not even specified clearly to which bit they are referring. We were able to test this, and sure enough it outputs a clock. Maybe this clock is is okay to use. Maybe it was designed for factory testing only.

Clock output

Enabling this clock output was persistent even after a power cycle. Be careful when changing under documented bits in the registers!

SPI is another example of being poorly documented. The following image implies that the address selection line is also SDO. The digital IO power supply pin is also the chip select line. Most serial peripherals we are familiar with don't source power out their chip select pins.

Clock output

Only reference to SPI in the datasheet or the register map

Finally one last quote to show how amazing the datasheet is: "The internal registers and memory of the MPU-9150 can be accessed using either I2C at 400 kHz." Either fast mode I2C, or what? As often as we tell you to RTFM, sometimes the manual can be misleading.

Hardware Assembly

The basic use case for the MPU-9150 requires 4 connections to the µController or µProcessor; power, ground, I2C clock and data. The following images shows how we used a SparkFun FTDI Basic Breakout, and an 3.3V Arduino Pro Mini to power and interface to an MPU-9150. The demo required the use of an interrupt (right-most yellow jumper) connected to D2 (INT0).

MPU-9150 connected to a 3.3V Pro Mini

An MPU-9150 wired up to and Arduino Pro Mini for the MPU6050_DMP6 demo

Make connections to the breakout anyway that makes you happy. The board in the above photo has a right angle header soldered on because that made us happy the day we built it up. We could have used a straight header, or wire, etc. Please note that different mounting orientations will alter the orientation of the axes. Make sure your code matches the physical orientation for your projects.

For this demo, we made the following connections:

Arduino Pro MiniMPU‑9150 BreakoutNotes
VCCVCC+3.3V
GNDGND+0V
SDASDASerial data @ +3.3V CMOS logic
SCLSCLSerial clock @ +3.3V CMOS logic
D2INTINT0 on Arduino | Interrupt output "totem pole or open-drain" on MPU-9150

The whole system in our testing was powered via USB through the FTDI basic.

USB to FTDI to Pro Mini to MPU-9150 Breakout Fritzing diagram

Electrical connections for demo

Installing the Arduino Library

Download and Install the Library

Visit the GitHub repository to download the most recent version of the libraries, or click the link below:

Download the MPU-9150 Arduino Libraries

For help installing the library, check out our Installing an Arduino Library tutorial. You'll need to move/copy the both the I2Cdev and MPU6050 directories inside the firmware directory into the libraries directory within your Arduino sketchbook. If you don't have a directory called libraries, create one and drop both directories in there.

The example Arduino code allows you to do things like print raw accelerometer, gyro, and magnetometer data. The original library came from i2cdevlib.com and was based on the very similar MPU-6050, which only used an accelerometer and gyro. The MPU-6050 device library was modified to include raw magnetometer data for the MPU-9150.

Running the MPU6050_DMP6 Example

Now, you can now run the example sketches. Open File ⇒ Examples ⇒ MPU6050 ⇒ Examples ⇒ MPU6050_DMP6. By default, this sketch is configured to do a fun teapot demo that uses processing. That's a little more involved than the scope of this hookup guide. We will output the acceleration components with gravity removed.

Uncomment line 102 //#define OUTPUT_READABLE_REALACCEL. Comment out line 112 #define OUTPUT_TEAPOT. Compile and upload the sketch. When it finishes uploading, open the serial monitor, and set the baud rate to 115200 baud. You should see this:

Initializing I2C devices...
Testing device connections...
MPU6050 connection successful

Send any character to begin DMP programming and demo:

At the top of the serial monitor type any ‘normal’ character (such as alphanumeric) and press Send. Your system should respond with:

Initializing DMP...
Enabling DMP...
Enabling interrupt detection (Arduino external interrupt 0)...
DMP ready! Waiting for first interrupt...

If you have the interrupt line connected properly, you should see lines similar to this:

areal   -8680   1460    -1448
areal   -9721   1460    -1463
...

Resources & Going Further

Hopefully that info dump was enough to get you rolling with the MPU-9150. If you need any more information, here are some more resources:

Going Further

Now that you’ve got the MPU-9150 up-and-running, what project are you going to incorporate motion-sensing into? Need a little inspiration? Check out some of these tutorials!

Dungeons and Dragons Dice Gauntlet

A playful, geeky tutorial for a leather bracer that uses a LilyPad Arduino, LilyPad accelerometer, and seven segment display to roll virtual 4, 6, 8, 10, 12, 20, and 100 side dice for gaming.

Tilt-a-Whirl Hookup Guide

A simple hook up guide to get started with the Tilt-a-Whirl sensor.

APDS-9960 RGB and Gesture Sensor Hookup Guide

Getting started guide for the Avago APDS-9960 color, proximity, and gesture sensor.
New!

LSM6DS3 Breakout Hookup Guide

A hookup guide for the LSM6DS3, which features a 3-axis accelerometer, 3-axis gyroscope, and FIFO buffer.

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

Photon RedBoard Hookup Guide

$
0
0

Photon RedBoard Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t421

Introduction

The SparkFun Photon RedBoard is an over-the-air-programmable WiFi development board that is compatible with the Particle cloud. At the heart of the Photon RedBoard is Particle’s P1 module, which packs an ARM Cortex M3 processor and a Broadcom WiFi controller into a single chip. The I/O, USB, and power connectors are all broken out to a familiar Arduino shape.

Photon RedBoard

SparkFun Photon RedBoard

Aside from the form-factor, the Photon RedBoard is nearly identical to a Particle Photon: same microprocessor, an RGB LED to indicate connectivity or other states, and RESET and MODE buttons to help configure the chip.

Covered In This Tutorial

The purpose of this tutorial is to familiarize you with all aspects of the Photon RedBoard – both hardware and software – and to help get you started down the path of Photon development. The tutorial is broken down into a handful of sections, including:

  • Board Overview– This section glosses over some of the Photon’s hardware components and features.
  • Getting Started– A step-by-step guide to connecting your Photon to WiFi and Particle’s cloud.
  • Arduino Shield (In)Compatibility– Despite the familiar size, the Photon RedBoard isn’t quite Arduino-compatible. If you plan on using it with shields, read through this section first.

Use the navigation bar on the right to find your way through.

Suggested Reading

This tutorial builds on a few electronics concepts you should be familiar with. If any of the topics below sound foreign to you, consider checking out those tutorials before continuing on.

  • Logic Levels– The Photon RedBoard operates at 3.3V. This tutorial will explain the difference between 3.3V and 5V devices.
  • Analog to Digital Conversion– The Photon RedBoard features a number of 12-bit ADC inputs. ADC’s help us convert the analog world into the digital values a microcontroller can understand.
  • Light-Emitting Didoes (LEDs)– LEDs are key on the Photon RedBoard. They help to relay connectivity or diagnostic status.

Board Overview

The Photon RedBoard is a mash-up of the Particle Photon and the SparkFun RedBoard. There are features from each of those boards in the layout:

Photon RedBoard top features and components

For a quick summary of the board’s components:

  • External Supply: A barrel jack power supply can be used to power the Photon with a voltage between 4.5-15V.
  • LEDs
    • Power: A red LED indicates that the board is receiving power.
    • RGB: This LED displays the state of the Photon RedBoard’s P1 module – whether it’s connected, connecting, or in DFU mode.
    • D7: This LED is attached to the Photon’s D7 pin. It’s a handy debugging tool.
  • Headers
    • Power: The power header delivers GND, 3.3V, USB voltage (~4.8V), and the external supply voltage (VIN).
    • Analog: Six of the Photon’s analog-to-digital (ADC) pins are broken out here.
    • 8-pin Digital: The Photon’s hardware UART (RX & TX) and digital pins D2-D7.
    • 10-pin Digital: A mish-mash of unique pins – like the digital-to-analog output (DAC) – plus SPI and I2C interfaces.
    • Spare: Bonus GPIO! These “spare” pins aren’t enabled in the Particle firmware yet, but it’s coming.
  • Buttons
    • Reset: The reset button can be used to re-start the Photon module.
    • Mode: To switch between running, safe, listening, and DFU modes.
  • USB Connector: USB can be used to power the Photon RedBoard, but it can also be used as a serial data interface.
  • JTAG Header: The 20-pin JTAG header matches the pinout for an ST-Link/v2 JTAG in-circuit debugger/programmer.

Photon I/O Pins

The Photon pinout doesn’t exactly match an Arduino – there just aren’t enough pins. But the pins that are broken out have all sorts of extra functionality. Here’s a summary of each of the pins and their capabilities:

Pin LabelName(s) – Function(s)
(Alternate names bolded)
Analog Output (PWM)?5V Tolerant Digital Input?
A0
A0
A1
A1
A2
A2SS (SPI1)
A3
A3SCK (SPI1)DAC2
A4
A4MISO (SPI1)
A5
A5MOSI (SPI1)
RX
RX (UART1)
TX
TX (UART1)
D2
MOSI (SPI3)CAN RXI2S SD
D3
MISO (SPI3)JTAG TRST
D4
SCK (SPI3)I2S SCKJTAG TDO
D5
SS (SPI3)I2S WSJTAG TDI
D6
JTAG TCK
D7
JTAG TMS
DAC
A6DAC1
WKP
A7
SS/A2
A2SS (SPI1)
MOSI/A5
A5MOSI (SPI1)
MISO/A4
A4MISO (SPI1)
SCK/A3
A3SCK (SPI1)DAC2
SDA/D0
SDA (I2C1)
SCL/D1
SCL (I2C1)CAN TX
Analog InSPI1SPI3DACI2CCANJTAGI2S

Differences Between Photon RedBoard and Arduino Pinout

The Photon RedBoard maps most digital, analog, and serial interfaces to its Arduino equal, but there are a few significant differences.

There are no explicitly digital Photon pins beyond D7, so you won’t find D8, D9, D10, D11, D12, or D13. The Photon RedBoard breaks out DAC, WKP, SS/A2, MOSI/A5, MISO/A4, and SCK/A3 in their place.

Double vision: A2, A3, A4, and A5 are broken out twice on the Photon RedBoard! They can be found, obviously, on the analog input header. But those pins also serve as the Photon's hardware SPI port, so they're also broken out on the 10-pin digital header (and labeled with their SPI purpose). More on that in the Arduino Shield (In)Compatibility section.

The hardware UART pins – labeled RX and TX – are broken out where you would expect to see D0 and D1. These pins can be used with the Photon’s Serial1 class, or as digital inputs or outputs. For example…

language:c
pinMode(RX, OUTPUT); // Set RX pin as a digital output
digitalWrite(RX, HIGH); // Write RX pin HIGH

…will set the RX pin high.

The DAC and WKP pins serve as the Photon’s main digital-to-analog output and wake-from-sleep input, respectively. But they can also be used as analog inputs or digital inputs/outputs. To use the DAC pin as a analog or digital I/O, reference it as A6; the WKP pin can be referenced using A7.

Pin LabelAlternate Analog Pin
WKPA6
DACA7

The I2C pins at the top of the 10-pin header – SDA/D0 and SCL/D1– are where you’d expect to see them on any Arduino board (unless it’s a really old Arduino). These pins can also be digitally referenced with D0 and D1 – don’t confuse them with the RX and TX pins, though!

Also note that there is no AREF pin on the Photon RedBoard – that pin is left as not-connected (and not labeled).

Power Supply Header

The 8-pin power header breaks out three possible voltage supplies, plus ground and RESET– the Photon’s active-low reset signal.

Power supply header

Here’s a quick reference for the three voltage supplies on the header:

Pin LabelNominal VoltageVoltage sourceCurrent Output Limit
3.3V3.3V3.3V regulator output800mA
V-USB4.8V (5V minus a diode drop)USB500mA (fused)
VIN4.5-15VBarrel JackDepends on external supply
3.3V! The Photon operates at 3.3V -- it is not capable of running at 5V! Most digital pins are 5V tolerant, when configured as inputs, but the analog pins are not!

RGB LED

The RGB LED on the Photon RedBoard identifies the connectivity status – or other state information – of the P1 module. The color-to-mode mapping of the Photon RedBoard is described in detail in Particle’s Device Mode documentation. As a quick summary:

LED ColorLED activityDevice Mode
CyanBreathingConnected to WiFi and Particle Cloud
CyanBlinkingConnected to WiFi, Connecting to Particle Cloud
GreenBlinkingConnecting to WiFi
BlueBlinkingListening mode (waiting for WiFi info)
PinkBlinkingReceiving new application over-the-air
PinkBreathingConnected in safe mode
WhiteBreathingApplication running, WiFi off
Orange-YellowBlinkingDFU mode

MODE and RESET Buttons

The RESET button – as with RESET buttons you may already be familiar with – can be used to restart the Photon RedBoard’s application. After pressing and releasing the button, the Photon will begin to boot up from scratch, which means it’ll have to re-connect to WiFi.

Mode and reset buttons

The MODE button can be used to place the Photon in any number of diagnostic modes, described in the Photon device mode documentation. It can be used to boot into safe mode (connected to WiFi/Cloud, but no application running), DFU mode, or to factory reset the device.

To place the Photon in any of those modes, begin by holding both RESET and MODE down. Then release RESET, while still holding MODE. Release the MODE button to leave the Photon RedBoard in the desired state.

The MODE button can also be used to place the Photon in listening mode (to re-configure WiFi, check the firmware version, or module identity), while an application is running. To accomplish that, hold the MODE button down for about 4-5 seconds, until the RGB LED begins blinking blue.

Spare I/O Pins

Compared to the Photon’s P0 module, the P1 on the Photon RedBoard delivers six extra GPIO pins. These are labeled as “spare” GPIO, and broken out near the analog header. “S1” corresponds to “SPARE1”, “S2” is “SPARE2”, and so on.

This header is not equipped with a connector – soldering to the pins is left to the user.

At this time the spare GPIO are not implemented in Photon firmware, but they should be usable in future releases.

Getting Started

The Photon can be powered over either USB (using a Micro-B Cable) or with a 4.5-15V barrel jack power supply (like either our 5V and 9V wall warts). To begin using your Photon RedBoard, plug it in!

USB plugged into Photon RedBoard

The red “POWER” LED should illuminate to indicate the Photon RedBoard is on. Once that’s verified, it’s time to set up WiFi.

Configuring WiFi, Connecting to Your Particle Account

To use the Particle cloud – and their online IDE – you’ll need a Particle account. Head over to build.particle.io to sign up, if you haven’t already.

Create a Particle Account!

When you power on a Photon RedBoard for the first time, it should boot up into listening mode– indicated by a blinking, blue LED. It’ll remain in listening mode until configured with your WiFi network and password.

There are a handful of ways to configure a Photon’s WiFi credentials, including with the Particle smartphone app (iOS8+ or Android), or through a serial terminal. Unless you’re very comfortable with serial terminals– or just don’t have a smartphone nearby – we recommend using the app.

Both methods are described below, click one of the headings to expand your section of interest:

Step 1: Download the Particle App

The Partice app is available for both iOS (8.0 and up) and Android phones: iPhone | Android.

Step 2: Follow the in-app directions to connect your Photon

1. After opening the app, press "Get Started", to arrive at the device list screen. Scroll to the bottom and select Setup a Photon...

Select "Setup a Photon..." to begin setting up your Photon RedBoard.

2. Verify that your Photon RedBoard's RGB LED is blinking blue, then select "READY" on the next screen.

3. Navigate to your phone's WiFi settings and find a network named something like Photon-9XYZ (with a random set of four characters as the suffix). Select it.

Your Phone should find a network name prefaced with "Photon-", connect to it.

4. Switch back to the Particle app. After a few seconds, the app should present you with a list of WiFi networks. Select the network you want to connect your Photon RedBoard to. On the next screen type in the network's password.

5. The penultimate screen will show the Photon RedBoard's progress as it connects to your WiFi network and the Particle cloud.

6. Finally, create a name for your Photon. You can go with Particle's (wonderfully random) suggestion, or one of your own.

If, for some reason or another, you can't use the Particle app to commision your Photon RedBoard, one alternative is using a Serial Terminal.

Windows users: To use the Photon in serial port mode, Windows users will need to install a driver. Follow Particle's "Installing the Particle Driver" guide for help installing the driver.

An alternative to this method – one that doesn't require serial terminal-ing – is Particle CLI. After installing particle CLI you can use the particle setup command to run through device setup. More information on that here.

1. Open a serial terminal program (check out our Serial Terminal Basics tutorial for suggestions) to your Photon's serial port. On Windows, the port should look something like COM#. On Mac, the port will be something like /dev/tty.usbmodem####.

2. Type i to get your Photon's Device ID. Copy it down – at least temporarily.

3. Type w to enter WiFi configuration. Then follow along with the prompts to enter you WiFi network's name and password.

Type 'i' to get your Photon's Device ID, then 'w' to configure its WiFi.

4. Go to the Particle Build IDE. Create an account if you haven't already, or log in.

5. Hop over to the "Devices" tab. Select Add New Device, and paste in your Photon RedBoard's Device ID.

Paste your Photon's Device ID into the Build IDE.

6. Name your new Photon RedBoard

7. Your Photon RedBoard will initially appear under the "Other" section, but after reloading the IDE, it should find its home under the "P1s" section.

Tinkering

The Photon RedBoard ships with the Tinker application installed. If you have a smartphone, this is a great tool for verifying your RedBoard’s functionality, and to get a feel for the basics of electronics.

Load up the Particle app– now that your RedBoard is commissioned and connected to your account, it should show up in your device list. And it should have a green dot next to it, indicating it’s running the Tinker application. Click the device to enter Tinker.

Tinker - select a device

Select a Photon running the Tinker app to tinker with it.

The RedBoard has an LED attached to D7 – perfect for tinkering with. Press “D7”, then select digitalWrite to set it as a digital output. It’ll initially set itself LOW. To set D7 high, click the pin again. The blue D7 LED should turn on almost instantly!

Set D7 HIgh

Set D7 as a digitalWrite() pin, then set it HIGH to turn the LED on.

Play around with the other pins. Try analog reading! See what voltage those pins are floating at. Or attach some external LEDs and try analog writing!

Arduino Shield (In)Compatibility

One of the Photon RedBoard’s most unique feature’s is its familiar Arduino-shaped form-factor, which makes it – at least mechanically – compatible with most Arduino shields. Unfortunately, there’s a big difference between mechanically compatible and electrically.

Weather shield on Photon RedBoard

The SparkFun Weather Shield half works with the Photon RedBoard – the digital sensors function over I2C, GPS communicates over the UART, but trouble arises when you try to attach wind sensors to the analog pins.

Some Arduino shields will work with the Photon RedBoard, but many others will not. Here’s what to watch out for when you consider loading the RedBoard with a shield:

3.3V Logic

The most important distinction between an Arduino Uno and the Photon RedBoard is the operating voltage. The Photon RedBoard’s P1 module operates at 3.3V. That means the I/O pins will also operate between 0-3.3V. If a pin is configured for digital output, it will produce 3.3V for a logic HIGH and 0V for a logic LOW.

Digital input range: In digital input mode, all pins except A3 and DAC are 5V-tolerant – so they can handle logic HIGH's up to 5V. (Internal pull-up resistors must be disabled if you plan on connecting 5V sources to input pins.)

Analog input range: The range of the analog input pins – if they're used as analog inputs instead of digital – is limited to 0-3.3V. The analog pins are not 5V-tolerant in ADC mode!

The power header does supply all of the voltage’s you’d expect. The V-USB pin should supply about 5V (it’ll be closer to 4.8V). VIN will either supply about 4.5V when powered by USB, or – if a barrel jack supply is connected – the direct voltage supplied from the external jack.

D8-D13 and Duplicated A2-A5

A second key difference between an Arduino Uno and the Photon RedBoard is the RedBoard’s lack of explicit GPIO from D8-D13. Where you’d usually expect to see those labels, you’ll instead find a combination of unique GPIO and duplicated analog pins – providing a hardware SPI port.

10-pin digital i/o header

Watch out when using the 10-pin digital I/O header!

Duplicated A2-A5 pins: Note that the A2-A5 (SS, MOSI, MISO, and SCK) are broken out to two places on the Photon RedBoard. You can't use those pins for analog input and SPI communication simultaneously.

Also note that, instead of D8 and D9, the DAC and WKP pins are broken out. While they have unique functions themselves, these pins can be used as digital inputs or outputs, or even analog inputs! You’ll just need to remember to invoke them by their equivalent analog pin names: A6 (DAC) and A7 (WKP).

Mismatched Analog Output Pins

In total, the Photon RedBoard offers more analog (PWM) outputs than an Arduino Uno, but those analog outputs aren’t all in the same spot. The table below provides a quick reference to matched, unmatched, and extra PWM:

Arduino PWM PinsPhoton RedBoard PWM Pins
RX (D0 position)
TX (D1 position)
D2
D3D3
D5
D6
D9WKP (D9 position)
D10
D11MOSI/A5 (D11 position)
MISO/A4 (D12 position)
SDA/D0 (SDA position)
SCL/D1 (SCL position)
Photon OnlyArduino OnlyPWM Match

Pins 5, 6, and 10 – usually equipped with PWM on an Arduino – can not be used for analogWrite()‘ing. But there are a number of bonus PWM pins, in the D0, D1, D2, D12, SDA, and SCL positions!

D0, D1, RX, and TX

Most of the digital pins from 0 to 7 are right where you’d expect them – all of them except D0 and D1. In place of D0 and D1 are the Photon’s hardware UART pins: RX and TX.

In code, these serial pins can be controlled with the Serial1 object (don’t forget the ‘1’ – Serial without the ‘1’ is the USB port serial).

The RX and TX pins can also be used as digital inputs or outputs. Simply use their labeled names to control or read their state. For example:

language:c
pinMode(RX, OUTPUT); // Set RX as an output
digitalWrite(RX, HIGH); // Write RX high
pinMode(TX, INPUT); // Set TX as an input
int txState = digitalRead(TX); // Read TX's logic level
Serial.print("TX's status is: " + String(txState)); // Print TX's logic level over USB

D0 and D1 do still exist in Photon RedBoard world! They’re the alternative names for the I2C pins, at the top of the 10-pin digital header. The can be controlled using either D0 and D1 or SDA and SCL.

I2C Pins – Not on A4/A5

If you’re a veteran Arduino developer, you may remember a time – before the Arduino Leonardo – when there weren’t explicitly broken out I2C pins (SDA and SCL). Instead, those pins were broken out to the A4 and A5 pins. That’s not the case on the Photon RedBoard! The I2C pins are only broken out to the SDA and SCL pins!

Older shields that haven’t been updated to the “R3” layout may still have the I2C pins connected to A4 and A5. If that’s the case, you may need to do a bit of re-wiring before it can be functional with the Photon RedBoard.

No ICSP Header (for SPI)

The Arduino R3 footprint also introduced a greater need for the ICSP header, which often carries SPI signals. Unfortunately, with a big-ol' module hogging the edge of the board, there wasn’t a home for the ICSP header.

If you’re using a shield that requires SPI, but expects those SPI signals to be delivered by the ICSP header (instead of pins 10-13), you may have to do some re-routing on the shield.

Resouces & Going Further

The Photon RedBoard is open-source, so there are loads of resources available should you need them:

As you continue developing firmware for the Photon RedBoard, we encourage you to check out all of the development environment options available. Check out our Photon Development Guide for more information!

New!

Photon Development Guide

August 19, 2015

A guide to the online and offline Particle IDE's to help aid you in your Photon development.

And there are loads of other SparkFun tutorials that might help inspire your next project, including:

Galileo Unread Email Counter

How to create a simple unread-email checker with the Intel/Arduino Galileo.

Are You Okay? Widget

Use an Electric Imp and accelerometer to create an "Are You OK" widget. A cozy piece of technology your friend or loved one can nudge to let you know they're OK from half-a-world away.

Pushing Data to Data.SparkFun.com

A grab bag of examples to show off the variety of routes your data can take on its way to a Data.SparkFun.com stream.

Photon Weather Shield Hookup Guide

Create Internet-connected weather projects with the SparkFun Weather Shield for the Photon.

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

8-Pin SOIC to DIP Adapter Hookup Guide

$
0
0

8-Pin SOIC to DIP Adapter Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t401

Introduction

The SparkFun 8-Pin SOIC to DIP Adapter is a small PCB that lets you adapt SOIC packages into a DIP footprint. These are useful for modding and upgrading devices that use 8-pin DIP ICs, when the upgraded IC is only available in a SOIC footprint. You can also use it for prototyping, to make SOIC packages compatible with solderless breadboards.

SOIC Adapter Array

The updated version of this adapter comes as a small array of PCBs – if you’re adapting one chip, you’re likely to adapt more than one. The PCBs easily snap apart, resulting in four individual boards.

The SOIC land pattern on the board has also been improved from its predecessor. The pads are very long, in order to accomodate both narrow and wide packages. The SOIC lands are staked down with plated through-holes, to prevent lifting during rework. The SOIC is also contained entirely within the DIP outline, for situations with no extra clearance around the IC.

Old Vs. New

Suggested Reading

Check out any of the links below, if you are unfamiliar with a given topic.

Assembly

Assembling the adapter is fairly straightforward, but there are a couple of tricks that make it easier.

Materials

To build up an adapter, you’ll need the following pieces.

Tools

The following tools are also recommended.

Building An Adapter

Snap Boards Apart

The array of boards was scored when it was manufactured. If you bend along the scores, the boards snap apart easily.

Snap Apart

Prepare To Solder

It’s easiest to solder the IC in place before you mount the headers, so you don’t have to work around the protruding pins.

If this is your first time soldering surface mount ICs, patience and a steady hand are the key to good solder joints.

  • Neatness Counts – you want to put on enough solder to join the legs to the pads, but not so much that adjacent legs are accidentally bridged.
  • Work Quickly – if you leave the hot soldering iron on the board too long, you risk burning the traces and pads off the board. You want the iron at a temperature where the solder flows almost immediately when you apply it to the iron. Somewhat counter-intuitively, a hotter iron can be less damaging than a cooler one – having the iron at a hotter temperature allows you to work more quickly, reducing the potential for damage.

Solder IC In Place

First, heat one corner land (aka pad) on the PCB.

Heating first pad

Once it’s hot, flow a little solder onto it.

One Solder Blob

Pin 1 of the IC is usually marked with a small dimple in the IC body, or that end of the chip is marked with a notch. Line these marks up with the corresponding marks in the silkscreen. The silkscreen actually has three marks, a notch at one end of the IC, a dot within the IC outline, and a dot just outside the IC (which remains visible once the IC is soldered down).

SOIC Pin1 Marks

Grab the IC with the tweezers, and orient it over the footprint. Reheat the solder blob from the previous step, so the IC lead adheres to it. Press the IC down flat before the solder cools.

One Leg Soldered

At this point, it doesn’t have to be a perfect solder fillet, but the IC should be sitting flat on the board, and all of the leads should align with the PCB pads. If the alignment isn’t good, or if pin one is the wrong way around, reheat the solder, and adjust the placement. This becomes a lot harder to fix after more pads get soldered down.

With the chip properly aligned, you can work your way around the chip, soldering each lead. It works best if you use the tip of the soldering iron to heat the IC lead and the PCB land at the same time, then flow a tiny bit of solder to join them.

All Leads Soldered

When you get back to the first lead, you can reheat the joint, flow a little more solder, and make sure the leave a neat fillet.

Solder In Header Pins

With the IC in place, now you can solder on the header pins. It’s easier if you have a jig that can hold the pins while you solder. It turns out that a solderless breadboard has a bunch of holes with the proper alignment!

Start by breaking off the 4-pin segments off the header. Insert them into the breadboard, two rows apart.

Pin in Breadboard

Set the PCB over the headers. Take care to keep the pins aligned straight up and down.

PCB On  Pins

Work you way around the PCB, soldering each pin from the top of the board. When it’s complete, it should look like the board below.

Fully Assembled

Deflux

Once you’ve finished soldering, look at the solder joints. If they appear to have a yellow or brown coating on or around them, the board has flux residue (under a magnifying glass, it might look like burnt sugar). Flux is acidic, and can cause problems with long-term reliability, so it’s best to clean it off.

You’ll have to check the documentation for your solder for the proper cleaning methodology. Some flux is water soluble, while some requires a solvent like isopropyl alcohol or acetone.

Verify

Before we jump into applications of the SOIC-to-DIP adapter board, let’s take a moment to doublecheck our work.

A quick visual inspection can help spot solder bridges, or solder joints that didn’t flow properly. It’s also a good time for one last check, to make certain that pin one of the SOIC is properly oriented.

Whoops!

For a little extra confidence, you can also use a multimeter in continuity mode, to verify that the legs of the SOIC are connected to the pins.

Using The Adapter

Adapter Orientation

The pins of the DIP footprint are rotated 90° in relation to the pins of the SOIC. We covered the pin-1 markings for the SOIC in the assembly section.

Pin 1 of the DIP footprint is marked two ways.

First, the solder pad for pin 1 is square, while the others are round.

DIP Pin 1, top

Second, pins 1 and 8 are marked in the legend silkscreened on the bottom of the board.

DIP Pins, bottom

Case Studies

On a Breadboard

This adapter is useful when you want to build a breadboard prototype using a chip that’s only available in SOIC. It allows the chip to properly fit the rows of the breadboard.

Breadboard Example

Dual-opamp Oscillator On Breadboard

Upgrading Old Devices

Another common use of a SIOC-to-DIP adapter is upgrading or modifying existing equipment.

At one time, this was a way to update the BIOS on a PC motherboard, though more recently DIP-8 EPROMS have been less prevalent, and surface-mount memories are more commonplace, which are usually reprogrammable without being removed from the motherboard.

Chip substitution is also a common practice among hi-fi and pro audio enthusiasts, sometimes called chip rolling or POOGE-ing (Progressive Optimization Of Generic Equipment). Many of these devices are filled with single or dual operational amplifiers in DIP-8 packages. It can be an easy, inexpensive upgrade for an older device to put in newer opamps, which feature lower distortion, less intrinsic noise, and lower DC-offset. Sometimes these newer amplifiers are only available in surface-mount packages, such as the Texas Instruments OPA1641/1642.

Modded PCB

When doing modifications of this sort, there are a couple of things to be aware of.

  • First, note whether the original equipment has the IC in a socket. If so, it should be easy to remove the old chip, and install the adapter.
  • If there is no socket, and the old IC is soldered directly to the board, take care in removing the old IC, to prevent damaging the PCB. It might be easier to cut the legs off the old IC, and desolder them one-by-one.
  • If there wasn’t a socket, you might consider installing one as part of the upgrade.
  • Finally, note the orientation of the IC before you begin, so you can be certain to get the replacement in properly.

Resources and Going Further

Resources

This board is a redesign of the now retired 8-Pin SOIC-DIP adapter.

If you’re debugging SOIC-8 based designs, you might find the SOIC-8 test clip to be useful.

We also offer a number of other surface mount to through-hole conversion boards:

Going Further


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado


MPU-9150 Hookup Guide

$
0
0

MPU-9150 Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t397

Introduction

The MPU-9150 is a nine degrees of freedom (9DOF) inertial measurement unit (IMU) in a sigle package. It houses a 3-axis accelerometer, 3-axis gyroscope, 3-axis magnetometer and a Digital Motion Processor™ (DMP™) hardware accelerator engine. The range of each sensor is configurable: the accelerometer's scale can be set to ±2g, ±4g, ±6g, ±8g, or ±16g, the gyroscope supports ±250, ±500, and ±2000 °/s, and the magnetometer has full-scale range of ±1200µT (±12 gauss).

MPU-9150

The MPU-9150 supports I2C. There is a single reference to in the datasheet for SPI, but all other evidence points to the contrary. We have only testing using I2C, and, for the purposes of this tutorial, we will only be covering how to use this device in I2C mode.

Covered In This Tutorial

This tutorial is devoted to all things MPU-9150. First we'll introduce you to the breakout board. Then we'll switch over to example code and show you how to interface with the board using an Arduino and our [SFE_MPU-9150 Arduino library](https://cdn.sparkfun.com/assets/learn_tutorials/3/9/7/MPU-9150_libraries.zip).

The tutorial is split into the following sections:

Required Materials

This tutorial explains how to use the MPU-9150 Breakout Board with an Arduino. To follow along, you’ll need the following materials:

The MPU-9150 is a 3.3V device! Supplying voltages greater than 6V can permanently damage the IC. InvenSense recommends running from 2.375V to 3.465V. As long as your Arduino has a 3.3V supply output, and you're OK with using I2C, you shouldn't need any extra level shifting.

Suggested Reading

If you’re not familiar with some of the concepts below, we recommend checking out that tutorial before continuing on.

Hardware Overview

The Pinout

In total, the MPU-9150 Breakout breaks out 11 pins.

MPU-9150 product photo

The bare-minimum connections required are broken out on the left side of the board. These are the power and I2C pins (the communication interface the board defaults to):

Pin LabelPin FunctionNotes
GNDGround0V voltage supply
VCCPower SupplySupply voltage to the chip. Should be regulated between 2.375V and 3.465V.
SDAI2C: Serial DataI2C: Serial data (bi-directional)
SCLSerial ClockI2C serial clock (up to 400kHz)

The remaining pins break out additional functionality and interrupt outputs:

Pin LabelPin FunctionNotes
ESDAuxiliary I2C master serial data9150 can act as a master or pass-through to additional I2C devices.
ESCAuxiliary I2C master serial clock9150 can act as a master or pass-through to additional I2C devices.
COUTClock outputOutputs a 50% duty cycle square wave (see register map).
CINClock inputOptional external reference clock input, grounded through jumper.
I using an external clock source, cut 'CLK' jumper.
AD0I2C Slave Address LSBIf low I2C address is 0x68, else 0x69. Connected to jumper. (0x68 default)
FSYNCFrame synchronizationExternal frame sync input that latches to capture external bus interrupts
If using this pin cut 'SYNC' jumper.
INTInterrupt pinConfigurable digital output to signal the host processor of an event.

Power Supply

The VCC and GND pins are where you'll supply a voltage and 0V reference to the IC. The breakout board does not regulate this voltage, so make sure it falls within the allowed supply voltage range of the MPU-9150: 2.375V to 3.465V. Logic voltage levels can be as low as 1.8V ±5% up to VCC.

The communication pins are not 5V tolerant, so they’ll need to be regulated to within a few mV of VDD.

Communication

SDA, SCL, ESD, and ESC are used for the I2C interfaces. The auxiliary clock and data pins will require external pull-up resistors. These ave to be tuned on a case-by-case basis depending on bus capacitance to get proper rise times.

SDA and SCL have integrated 10KΩ pull-ups. If you plan on using more than one I2C device on the bus, you might want to remove these pull-ups. If the I2C lines are pulled-up too strongly by multiple sets of pull-ups, the bus will likely be out of spec and not function correctly.

The following image shows a stock PCBA on the left, and one with the pull-up resistors removed on the right. To remove these, I recommend reflowing a fair amount of solder onto the resistors. It doens't take very long to fully cover all 4 joints with molten solder and the two part will slide right off. So easily in my case that I wasn't able to capture a picture of the excess solder before the parts had come off. Wick off the excess solder, wipe clean with some DI water, and you are done.

Pull-ups removed

Interrupts

There are a variety of interrupts on the MPU-9150. While connecting up to these is not as critical as the communication or power supply pins, using them will help you get the most out of the chip. The INT pin is a digital output to the host controller. The FSYNC pin can be configured as an interrupt input to optionally be passed through out the INT pin.

These can be programmed to interrupt as either active-high or active-low. More details on these configurations can be found in the product register map.

The Jumpers

The most commonly used jumper will be to the address pin (AD0). It defaults to being pulled to ground selecting address 0x68, but with a soldering iron can be changed to be pulled up, switching the I2C address to 0x69.

The AD0 pin broken out is connected directly to ground by default. Alternatively it's connected straight to VCC. Make sure to remove the solder from both sides of the 3-way jumper before connecting it externally.

Address jumper

The address selection jumper on the front of the board. Allows you to select the LSB of the I2C address

The intention of these jumpers is to make it as easy-as-possible to use the board; using as few wires as possible. The CLK jumper is used to ground the external clock input as the datasheet recommends when not using an external clock. Make sure to cut this jumper if you attach an external clock to the CIN connection. The SYNC jumper ties FSYNC to ground as the manufacturer instructs one to do when not using it. Make sure to cut this jumper if use the FSYNC through hole connection.

Back of MPU-9150 Breakout

To disable any of these jumpers, whip out your handy hobby knife, and carefully cut the small traces between middle pad and edge pads.

Funny Business

Be very careful trusting the datasheet. It's full of inconsistencies or lacking clarification. For example pin 22. There is a single reference to it functioning as a clock output (next image). There are several places stating that it's not to be used, such as the table shown two images below.

Clock output, is it real?

Only reference to CLKOUT in datasheet


Clock output, is it real?

One of several examples of pin 22 being recommended to not be connected or a clock

The register map reads "This bit also enables the clock output.", but that's the only reference. It's not even specified clearly to which bit they are referring. We were able to test this, and sure enough it outputs a clock. Maybe this clock is is okay to use. Maybe it was designed for factory testing only.

Clock output

Enabling this clock output was persistent even after a power cycle. Be careful when changing under documented bits in the registers!

SPI is another example of being poorly documented. The following image implies that the address selection line is also SDO. The digital IO power supply pin is also the chip select line. Most serial peripherals we are familiar with don't source power out their chip select pins.

Clock output

Only reference to SPI in the datasheet or the register map

Finally one last quote to show how amazing the datasheet is: "The internal registers and memory of the MPU-9150 can be accessed using either I2C at 400 kHz." Either fast mode I2C, or what? As often as we tell you to RTFM, sometimes the manual can be misleading.

Hardware Assembly

The basic use case for the MPU-9150 requires 4 connections to the µController or µProcessor; power, ground, I2C clock and data. The following images shows how we used a SparkFun FTDI Basic Breakout, and an 3.3V Arduino Pro Mini to power and interface to an MPU-9150. The demo required the use of an interrupt (right-most yellow jumper) connected to D2 (INT0).

MPU-9150 connected to a 3.3V Pro Mini

An MPU-9150 wired up to and Arduino Pro Mini for the MPU6050_DMP6 demo

Make connections to the breakout anyway that makes you happy. The board in the above photo has a right angle header soldered on because that made us happy the day we built it up. We could have used a straight header, or wire, etc. Please note that different mounting orientations will alter the orientation of the axes. Make sure your code matches the physical orientation for your projects.

For this demo, we made the following connections:

Arduino Pro MiniMPU‑9150 BreakoutNotes
VCCVCC+3.3V
GNDGND+0V
SDASDASerial data @ +3.3V CMOS logic
SCLSCLSerial clock @ +3.3V CMOS logic
D2INTINT0 on Arduino | Interrupt output "totem pole or open-drain" on MPU-9150

The whole system in our testing was powered via USB through the FTDI basic.

USB to FTDI to Pro Mini to MPU-9150 Breakout Fritzing diagram

Electrical connections for demo

Installing the Arduino Library

Download and Install the Library

Visit the GitHub repository to download the most recent version of the libraries, or click the link below:

Download the MPU-9150 Arduino Libraries

For help installing the library, check out our Installing an Arduino Library tutorial. You'll need to move/copy the both the I2Cdev and MPU6050 directories inside the firmware directory into the libraries directory within your Arduino sketchbook. If you don't have a directory called libraries, create one and drop both directories in there.

The example Arduino code allows you to do things like print raw accelerometer, gyro, and magnetometer data. The original library came from i2cdevlib.com and was based on the very similar MPU-6050, which only used an accelerometer and gyro. The MPU-6050 device library was modified to include raw magnetometer data for the MPU-9150.

Running the MPU6050_DMP6 Example

Now, you can now run the example sketches. Open File ⇒ Examples ⇒ MPU6050 ⇒ Examples ⇒ MPU6050_DMP6. By default, this sketch is configured to do a fun teapot demo that uses processing. That's a little more involved than the scope of this hookup guide. We will output the acceleration components with gravity removed.

Uncomment line 102 //#define OUTPUT_READABLE_REALACCEL. Comment out line 112 #define OUTPUT_TEAPOT. Compile and upload the sketch. When it finishes uploading, open the serial monitor, and set the baud rate to 115200 baud. You should see this:

Initializing I2C devices...
Testing device connections...
MPU6050 connection successful

Send any character to begin DMP programming and demo:

At the top of the serial monitor type any ‘normal’ character (such as alphanumeric) and press Send. Your system should respond with:

Initializing DMP...
Enabling DMP...
Enabling interrupt detection (Arduino external interrupt 0)...
DMP ready! Waiting for first interrupt...

If you have the interrupt line connected properly, you should see lines similar to this:

areal   -8680   1460    -1448
areal   -9721   1460    -1463
...

Resources & Going Further

Hopefully that info dump was enough to get you rolling with the MPU-9150. If you need any more information, here are some more resources:

Going Further

Now that you’ve got the MPU-9150 up-and-running, what project are you going to incorporate motion-sensing into? Need a little inspiration? Check out some of these tutorials!

ITG-3200 Hookup Guide

Learn how to interact with the ITG-3200 Triple Axis Gyroscope.

Are You Okay? Widget

Use an Electric Imp and accelerometer to create an "Are You OK" widget. A cozy piece of technology your friend or loved one can nudge to let you know they're OK from half-a-world away.

MMA8452Q Accelerometer Breakout Hookup Guide

How to get started using the MMA8452Q 3-axis accelerometer -- a solid, digital, easy-to-use acceleration sensor.

Photon IMU Shield Hookup Guide

Learn how to use the SparkFun Photon IMU Shield for your Photon device which houses an on-board LSM9DS1 system-in-a-chip that houses a 3-axis accelerometer, 3-axis gyroscope, and 3-axis magnetometer.

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

SparkFun Inventor's Kit for Photon Experiment Guide

$
0
0

SparkFun Inventor's Kit for Photon Experiment Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t332

Introduction

The SparkFun Inventor’s Kit for Photon, also known as the SIK for Photon, is the latest and greatest in Internet of Things kits. Whether you’re a software developer just getting in to hardware, an electronics engineer learning software, or somewhere in between, this kit will help you get your projects hooked up to the Internet in no time.

For an overview of the Photon RedBoard and a preview of the kinds of experiments you’ll get to build with this kit, check out the video below.

Set Aside Some Time - Each experiment in this kit has two parts, with the second half usually containing an Internet-connected component. Please allow yourself ample time to complete each experiment. You may not get through all the experiments in one sitting. Please understand that the second half of each experiment is bonus material and relies on outside services, websites and technologies, to which some of you may not have access.

Included Materials

Here is a complete list of all the parts included in the SIK for Photon.

Photon Inventor's Kit Parts

The SparkFun Inventor’s Kit for Photon Includes the following:

If, at any time, you are unsure which part a particular experiment is asking for, reference this section.

Suggested Reading

The following links are here to help guide you in your journey through the SIK for the Photon. Referencing these documents throughout this guide will help you get the most out of this kit.

  • The Photon RedBoard Hookup Guide - This guide goes over the features of the Photon RedBoard in great detail, from the functions of each pin to a compare and contrast between the Photon RedBoard, the Photon, and the classic Arduino Uno.
  • Photon Development Guide - Learn how to develop with your Photon or Photon RedBoard using the three different methods described in this tutorial.
  • Getting Started with Particle - The Particle website has tons of great documentation to get you started in the world of IoT development.

Each experiment will also have a Suggested Reading section to aid you in understanding the components and concepts used in that particular experiment.

Using the Kit

Before embarking upon the experiments, there are a few items to cover first. If you have completed one of our other Inventor’s Kits before, you should already be familiar with most of the concepts in this section. If this is your first Inventor’s Kit, please read carefully to ensure the best possible SIK experience.

Photon RedBoard

The SparkFun Photon RedBoard is quite literally the brains of the SIK for Photon. Sporting an ARM Cortex M3 processor and a Broadcom WiFi controller, it is a powerful system rolled into one of the most common form-factors now found in embedded electronics. To learn more about the Photon RedBoard and all its functionality, visit the Photon Redboard Hookup Guide.

New!

Photon RedBoard Hookup Guide

August 27, 2015

Your guide to commissioning, tinkering, and programming the SparkFun Photon RedBoard.

Base Plate

Building circuits can be a monumental task when you’ve never done so before. To make circuit development easier, we have included a baseplate onto which you can attach your breadboard and your Photon RedBoard.

To attach the breadboard, peel off the adhesive backing, and place the breadboard on the baseplate, making sure that the SparkFun logo and text on your breadboard all face the same direction.

To attach the Photon RedBoard, use the included screws and screwdriver to attach the board to the baseplate. Again, be sure that the text on the pins matches the directions of the breadboard text and the SparkFun logo. The USB connector should be pointing up when looking directly at the baseplate.

base plate

Breadboard

Solderless breadboards are the go-to prototyping tool for those getting started with electronics. If you have never used a breadboard before, we recommend reading through our How to Use a Breadboard tutorial before starting with the experiments.

How to Use a Breadboard

May 14, 2013

Welcome to the wonderful world of breadboards. Here we will learn what a breadboard is and how to use one to build your very first circuit.

Jumper Wires

This kit includes twenty 6" long jumper wires terminated as male to male. Multiple jumpers can be connected next to one another on a 0.1" header or breadboard.

jumpers

Each group of jumpers are connected to each other and can either be pulled apart in any quantity or kept whole based on you needs.

peeling

Screwdriver

Last, we’ve included a pocket screwdriver set to aid you in any mechanical portions of this guide. Unscrew the cap on the tail end of the screwdriver to reveal the various tips that can be inserted into the head of the screwdriver.

alt text

You will need to swap out tips for various tasks throughout this guide.

alt text

Using the Particle IDE

If you’ve worked with Arduino or with our SparkFun Inventor’s Kit for Arduino, then you are familiar with the Arduino IDE, short for Integrated Development Environment. Particle has created their own cloud-based IDE, and they have adopted the Arduino language and syntax allowing you to move from an Arduino to the Photon and Photon RedBoard with ease.

Particle has written a great getting started guide for using their Web IDE, called Particle Build. You can read through their documentation by following the link below.

Getting Started with Particle Build

We have also written a Photon Development guide to help aid you in your experience. There are numerous ways to develop with the Photon and Photon RedBoard, and this guide covers the three most common methods: Particle Build, Particle Dev, and ARM GCC.

New!

Photon Development Guide

August 20, 2015

A guide to the online and offline Particle IDE's to help aid you in your Photon development.

For the purposes of this guide, we recommend sticking to the online Particle Build IDE. However, once you feel comfortable using the Photon RedBoard, you are free to explore the other methods for development.

Getting Started with the Photon RedBoard

The Photon RedBoard can be powered over either USB (using the included Micro-B Cable) or with a 4.5-15V barrel jack power supply (like either our 5V and 9V wall warts). To begin using your Photon RedBoard, plug it in!

USB plugged into Photon RedBoard

The red “POWER” LED should illuminate to indicate the Photon RedBoard is on. Once that’s verified, it’s time to set up WiFi.

Configuring WiFi, Connecting to Your Particle Account

To use the Particle cloud – and their online IDE – you’ll need a Particle account. Head over to build.particle.io to sign up, if you haven’t already.

Create a Particle Account!

When you power on a Photon RedBoard for the first time, it should boot up into listening mode– indicated by a blinking, blue LED. It’ll remain in listening mode until configured with your WiFi network and password.

There are a handful of ways to configure a Photon’s WiFi credentials, including with the Particle smartphone app (iOS8+ or Android), or through a serial terminal. Unless you’re very comfortable with serial terminals– or just don’t have a smartphone nearby – we recommend using the app.

Windows users: To use the Photon in serial port mode, Windows users will need to install a driver. Follow Particle's "Installing the Particle Driver" guide for help installing the driver.

Both setup methods are described below, click one of the buttons to expand your section of interest:

Step 1: Download the Particle App

The Partice app is available for both iOS (8.0 and up) and Android phones: iPhone | Android.

Step 2: Follow the in-app directions to connect your Photon

1. After opening the app, press "Get Started", to arrive at the device list screen. Scroll to the bottom and select Setup a Photon...

Select "Setup a Photon..." to begin setting up your Photon RedBoard.

2. Verify that your Photon RedBoard's RGB LED is blinking blue, then select "READY" on the next screen.

3. Navigate to your phone's WiFi settings and find a network named something like Photon-9XYZ (with a random set of four characters as the suffix). Select it.

Your Phone should find a network name prefaced with "Photon-", connect to it.

4. Switch back to the Particle app. After a few seconds, the app should present you with a list of WiFi networks. Select the network you want to connect your Photon RedBoard to. On the next screen type in the network's password.

5. The penultimate screen will show the Photon RedBoard's progress as it connects to your WiFi network and the Particle cloud.

6. Finally, create a name for your Photon. You can go with Particle's (wonderfully random) suggestion, or one of your own.

If, for some reason or another, you can't use the Particle app to commision your Photon RedBoard, one alternative is using a Serial Terminal.

An alternative to this method – one that doesn't require serial terminal-ing – is Particle CLI. After installing particle CLI you can use the particle setup command to run through device setup. More information on that here.

1. Open a serial terminal program (check out our Serial Terminal Basics tutorial for suggestions) to your Photon's serial port. On Windows, the port should look something like COM#. On Mac, the port will be something like /dev/tty.usbmodem####.

2. Type i to get your Photon's Device ID. Copy it down – at least temporarily.

3. Type w to enter WiFi configuration. Then follow along with the prompts to enter you WiFi network's name and password.

Type 'i' to get your Photon's Device ID, then 'w' to configure its WiFi.

4. Go to the Particle Build IDE. Create an account if you haven't already, or log in.

5. Hop over to the "Devices" tab. Select Add New Device, and paste in your Photon RedBoard's Device ID.

Paste your Photon's Device ID into the Build IDE.

6. Name your new Photon RedBoard

7. Your Photon RedBoard will initially appear under the "Other" section, but after reloading the IDE, it should find its home under the "P1s" section.

Some of the experiments will have two parts. The first part, will focus on teaching you about electronic concepts and technologies and how to build a circuit around those. The second part is bonus material showing you how to connect your circuit to the Internet or creating a more complex circuit. You will be able to complete part one of each experiment with the included parts. However, the second half of each experiment relies on outside services, websites and technologies, some of which you may not have access to or may not want to engage in. Should part two of a particular experiment not interest you, you may move on to the next experiment.

Introduction

LEDs (light-emitting diodes) are small, powerful lights that are used in many different applications. To start off the SIK for Photon, we will work on blinking an LED using a digital output. Chances are you have blinked an LED before, or perhaps this is your first time – but have you turned on an LED with the Internet? Blinking an LED is the most basic “Hello World” test for hardware, but now we’re going to be saying “Hello Internet.” This experiment will also walk you through uploading your first sketch with the Particle IDE.

Parts Needed

You will need the following parts:

  • 1x LED (Choose any color in the bag full of LEDs)
  • 1x 330Ω Resistor
  • 2x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
LED - Assorted (20 pack)

COM-12062
$2.95
5
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95
Resistor 330 Ohm 1/6 Watt PTH - 20 pack

COM-11507
$0.95
1

Suggested Reading

Before continuing on with this experiment, we recommend you be familiar with the concepts in the following tutorial:

  • What is a Circuit?– This tutorial will explain what a circuit is, as well as discuss voltage in further detail.
  • Voltage, Current, Resistance, and Ohm’s Law– Learn the basics of electronics with these fundamental concepts.
  • LEDs (Light-emitting Diodes)– LEDs are found everywhere. Learn more about LEDs and why they are used in some many products all over the world.
  • Resistors– Why use resistors? Learn more about resistors and why they are important in circuits like this one.
  • Polarity– Polarity is a very important characteristic to pay attention to when building circuits.

Hardware Hookup

Ready to party? Components like resistors need to have their legs bent into 90° angles in order to correctly fit the breadboard sockets. You can also cut the legs shorter to make them easier to work with on the breadboard.

Bent resistor

Pay close attention to the LED. The negative side of the LED is the short leg, marked with a flat edge.

LED drawing

Each experiment will have a Fritzing hook-up diagram. Connect the components to the breadboard and Photon RedBoard by following the Fritzing diagram below:

Pay special attention to the component’s markings indicating how to place it on the breadboard. Polarized components can only be connected to a circuit in one direction. Orientation matters for the following component: LED

Photon Particle Blink LED

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

All jumper wires work the same. They are used to connect two points together. All the experiments will show the wires with different colored insulations for clarity, but using different combinations of colors is completely acceptable.

Be sure to the polarity on the LED is correct. The longer lead should be connected to D0.

Photon Code

Now to the exciting part! You’ll need to go to the Particle Build IDE (preferably on another tab or window) to get started. If you are not familiar with the Particle Build IDE, please refer to the Particle Build IDE documentation or the Particle Build IDE overview in the beginning of this guide.

Using an online IDE might be little different at first for some users, but it ends up being fantastic. You can program your Photon from anywhere in the world as long as you have an Internet connection.

Double check your Photon RedBoard’s RGB LED is breathing cyan. If it is, then your Photon RedBoard is connected to the Internet and ready to go. If your Photon RedBoard is not breathing cyan, please refer to the Connecting your Photon to WiFi documentation. If you go to the navigation bar, click on Devices to see if your Photon RedBoard is connected.

Screen shot of devices for Photon RedBoard.

There will be a breathing cyan dot next to your Photon RedBoard in the Devices section.

For each experiment, we recommend creating a new app. Go to the Code section in the navigation bar on the left. The hit the CREATE NEW APP button. After typing in the name, you can hit enter.

Name app

When you create a new app, particle build creates an .ino file and renames the tab in the Particle Build editor.

Let’s add the code! Copy and paste the code below into the in Particle Build editor. You can simply hit the COPY CODE button below and paste (Right-click then click Paste or Command + V) into the Particle Build Editor.

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 1 - Part 1: Hello World Blink an LED
    This sketch was written by SparkFun Electronics
    August 31, 2015
    https://github.com/sparkfun

    This is a simple example sketch that turns on an LED
    for one second, off for one second, and repeats forever.

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/

int led = D0; // LED is connected to D0

// This routine runs only once upon reset
void setup()
{
  pinMode(led, OUTPUT); // Initialize D0 pin as output
}

// This routine loops forever
void loop()
{
  digitalWrite(led, HIGH);  // Turn ON the LED
  delay(1000);              // Wait for 1000mS = 1 second
  digitalWrite(led, LOW);   // Turn OFF the LED
  delay(1000);              // Wait for 1 second
}

Once you’ve pasted the code into the window, let’s get in the habit of checking our code. Hit Verify (circle icon with a check mark in the top left of the Particle Build’s IDE navigation bar) to check if there is any errors in the code. Next, hit Save (folder icon) to save our code, so we can come back to the code later if we want to.

Drum roll please! Hit Flash (lightning bolt icon) to load the code onto the Photon RedBoard.

What You Should See

Once you click Flash, you should see your Photon RedBoard flash magenta, followed by green, and then back to breathing Cyan. Now, your LED should be blinking. You’ve just wirelessly programmed your Photon RedBoard to blink an LED, from the cloud!

Photon RedBoard LED hookup

Great job on successfully uploading your first sketch. If you want to learn how blink an LED with the Tinker mobile app, you can head to part 2 of this experiment!

Code to Note

Variables

A variable can be a number, a character, and even a string of characters. We’ll often use variables to store numbers that change, such as measurements from the outside world, or to make a sketch easier to understand (sometimes a descriptive name makes more sense than looking at a number).

Variables can be different “data types”, which is the kind of number we’re using (can it be negative? Have a decimal point?) We’ll introduce more data types later, but for the moment we’ll stick with good old “integers” (called “int” in your sketch). Integers are whole numbers (0, 3, 5643).

You must “declare” variables before you use them, so that the computer knows about them. Here we’ll declare a integer variable, and at the same time, initialize it to specific values. We’re doing this so that further down, we can refer to the pin by name rather than number.

Note that variable names are case-sensitive! If you get an “(variable) was not declared in this scope” error, double-check that you typed the name correctly.

Here we’re creating a variable called “led” of type “int” and initializing it to have the value “0”:

int led = D0;

The Photon RedBoard has digital input/output pins. These pins can be configured as either inputs or outputs. This is declared with a built-in function called pinMode(). The pinMode() function takes two values, which you type in the parenthesis after the function name. The first value is a pin number (or variable representing a pin number), and the second value declares how the pin behaves, usually as some sort of INPUT or OUTPUT.

Here we’ll set up pin 0 (the one connected to a LED) to be an output. We’re doing this because we need to send voltage “out” of the Photon RedBoard to light up the LED.

pinMode(led, OUTPUT);

When you’re using a pin as an OUTPUT, you can command it to be HIGH (output 3.3 volts in this case), or LOW (output 0 volts).

digitalWrite(led, HIGH);

For more info on the types of PinModes available, visit the Particle Documentation.

Comments

There are different ways to make comments in your code. Comments are a great way to quickly describe what is happening in your code.

// This is a comment - anything on a line after "//" is ignored // by the computer.

/* This is also a comment - this one can be multi-line, but it must start and end with these characters */

Troubleshooting

  • Code refuses to flash– Try putting your Photon RedBoard into Safe Mode. Then, hit Flash. Sometimes to get your Photon RedBoard breathing cyan again, unplugging from USB and replugging back can get your board connecting to WiFi again. Please keep in mind if you just starting working with the Photon RedBoard it might take a couple minutes for the firmware to upload when first connected to the Internet.

  • LED isn’t blinking– Try checking your connections again. Make sure the positive side of the LED is connected to D0. It is really easy to put the jumper wire in the wrong hole on a breadboard.

  • Photon RedBoard RGB isn’t doing anything– Don’t worry! We have an awesome tech support staff to help you out if there is something wrong. Please contact our tech support team.

Part 2: Blink an LED with Tinker

Tinker is a mobile app for iPhone or Android smartphones that makes it easy to control your digital and analog pins on your Photon or Photon RedBoard.

First you will need to download the app on your smartphone.

  • iPhone users

  • Android users

  • Windows users– Unfortunately, this extra feature is only for iOS and Android devices. Don’t worry, this is the only bonus material that uses with the Tinker app.

  • Non-smartphone users– Most of us would love to go back the happy memories of not owning a smartphone and not getting 50+ alerts each day. We applaud you! We also might secretly envy you. Unfortunately, this bonus material isn’t going to be your cup of tea. Don’t worry, this is the only bonus material that uses with the Tinker app.

When the app is done installing on your phone, you will need to sign in with your login information (the same as the Particle website). After logging in, you will see a list with all your devices.

Photon Device ID

Clicking on the more icon (three dots) next to the online dot, gives you more options from which to choose. For example, you can rename your board here. We suggest naming your Photon RedBoard with unique names. When you start having a lot of Photons and demos going at once, it is hard to remember which is which.

When clicking on the more icon, do not choose Re-Flash Tinker. At the moment, it will re-flash your board with firmware that isn't the latest and greatest with the Photon RedBoard!

When you click over the name of the Photon RedBoard you want to work with, you will see a new screen with a list of the different pins you can control.

List of Photon RedBoard pins

First, click the circle that is label with D0. Anytime you want to work with a pin, you will need to select it first. You can select multiple pins at a time.

alt text

You’ll notice that you have a couple different options. For this example, we are going to control the LED ON and OFF, by clicking on digitalWrite.

alt text

New Photon Code (There is none!)

What is great about this mobile app, you do not have to do any coding! Once you are signed into Tinker, you can start playing with the pins.

What You Should See

To turn the LED ON/HIGH, click the circle labeled D0 again. It should show the LED turn on.

alt text

To turn the LED OFF/LOW, click the circle labeled D0 again. It should show the LED turn off.

alt text

This is a super fun and easy way to control an LED or any other hardware. To reset all the pins and start fresh, click on the more icon (three dots) on the top right. You will see Reset all pin functions option.

Dig into the app a little bit more! What happens when you click D0 and choose analogWrite? What happens when you select D7 instead? HINT: Take at look at your board!

Troubleshooting

  • The app froze– If your app ever stops on you, close out the app and reopen. Report bugs to the wonderful Particle team on their forums.

  • I have a Windows phone– Unfortunately, this extra feature is only for iOS and Android devices. Don’t worry, this is the only bonus material that uses with the Tinker app.

  • There is a yellow circle and it says it is non-tinker– All Photon RedBoards were shipped out with the latest firmware that works with Tinker. If you get the yellow circle, it means the firmware on your RedBoard was re-flashed with old firmware. You will need to flash with the latest firmware. Please follow the directions here for now. We are working with Particle to make sure the Tinker app is pushing the latest firmware when re-flashing.

Experiment 2: With the Touch of a Button

Introduction

Now that you have conquered blinking an LED and know what an output is, it is time to learn inputs!

In this circuit, we’ll be introducing one of the most common and simple inputs – a push button – by using a digital input. Just like the LED, the push button is a basic component that is used in almost everything. The way a push button works with your Photon is that when the button is pushed, the voltage goes LOW. You Photon reads this and reacts accordingly. RedBoard Photon has internal pull-ups resistor, which keeps the voltage HIGH when you’re not pressing the button.

Parts Needed

You will need the following parts:

  • 1x LED
  • 1x Push Button
  • 1x 330Ω Resistor
  • 4x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
LED - Assorted (20 pack)

COM-12062
$2.95
5
Tactile Button Assortment

COM-10302
$4.95
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95
Resistor 330 Ohm 1/6 Watt PTH - 20 pack

COM-11507
$0.95
1

Suggested Reading

  • Switch Basics– The push button is a momentary switch. Momentary switches are switches which only remain in their on state as long as they’re being actuated (pressed, held, magnetized, etc.). Learn more about the different types of switches.
  • Pull-up Resistors - Pull-up resistors are very common when using microcontrollers (MCUs) or any digital logic device. This tutorial will explain when and where to use pull-up resistors, then we will do a simple calculation to show why pull-ups are important.

How to use logic like a Vulcan:

One of the things that makes the Photon RedBoard so useful is that it can make complex decisions based on the input it’s getting. For example, you could make a thermostat that turns on a heater if it gets too cold, or a fan if it gets too hot, and it could even water your plants if they get too dry. In order to make such decisions, the particle environment provides a set of logic operations that let you build complex “if” statements. They include:

==EQUIVALENCEA == B is true if A and B are the SAME.
!=DIFFERENCEA != B is true if A and B are NOT THE SAME.
&&ANDA && B is true if BOTH A and B are TRUE.
||ORA || B is true if A or B or BOTH are TRUE.
!NOT!A is TRUE if A is FALSE.!A is FALSE if A is TRUE.

You can combine these functions to build complex if() statements. For example:

language:c

if ((mode == heat) && ((temperature < threshold) || (override == true)))
{
digitalWrite(HEATER, HIGH);
}

…will turn on a heater if you’re in heating mode AND the temperature is low, OR if you turn on a manual override. Using these logic operators, you can program your Photon RedBoard to make intelligent decisions and take control of the world around it!

Hardware Hookup

Your kit comes with a bunch of different color push button. All push buttons behave the same, so go ahead and use your favorite color! Add the push button to the same LED circuit from the first experiment. Follow the Fritzing diagram below.

Pay special attention to the component’s markings indicating how to place it on the breadboard. Polarized components can only be connected to a circuit in one direction. Orientation matters for the following component: LED

Photon RedBoard Button and LED hook-up

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

Photon Code

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 2 - Part 1: With a Touch of a Button
    This sketch was written by SparkFun Electronics
    August 31, 2015
    https://github.com/sparkfun

    This is a simple example sketch that turns on an LED
    when pushing down on the push button

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/

int led = D0; // LED is connected to D0
int pushButton = D2; // Push button is connected to D2

// This routine runs only once upon reset
void setup()
{
  pinMode(led, OUTPUT); // Initialize D0 pin as output
  pinMode(pushButton, INPUT_PULLUP);
  // Initialize D2 pin as input with an internal pull-up resistor
}

// This routine loops forever
void loop()
{
  int pushButtonState;

  pushButtonState = digitalRead(pushButton);

  if(pushButtonState == LOW){ // If we push down on the push button
    digitalWrite(led, HIGH);  // Turn ON the LED
  }
  else
  {
    digitalWrite(led, LOW);   // Turn OFF the LED
  }

}

What You Should See

When you hold down the push button, those warm fuzzy feelings from the first experiment should happen again, and the LED should shine brightly. The LED will be off when the button is released.

Photon RedBoard Button Plus LED hookup

Press down on the push button to see the LED light up

Code to Note

pinMode(pushButton, INPUT_PULLUP);

The digital pins can be used as inputs as well as outputs. Before you do either, you need to tell the Photon which direction you’re going. Normally, with push buttons we would use a pull-up resistor. However, you can program the Photon RedBoard to use its internal pull-up and pull-down resistors.

pushButtonState = digitalRead(pushButton);

To read a digital input, you use the digitalRead() function. It will return HIGH if there’s 5V present at the pin, or LOW if there’s 0V present at the pin.

if(pushButtonState == LOW)

Because we’ve connected the button to GND, it will read LOW when it’s being pressed. Here we’re using the “equivalence” operator (“==”) to see if the button is being pressed.

Troubleshooting

  • If nothing is happening when holding down the push button, don’t panic! Double check your jumper wire and LED connections. It is easy to miss a jumper wire or two.

  • Code refuses to flash – Try putting your Photon RedBoard into safe mode. Then, hit Flash. Sometimes to get your Photon RedBoard breathing cyan again, unplugging from USB and replugging back can get your board connecting to WiFi again. Please keep in mind if you just starting working with the Photon RedBoard it might take a couple minutes for the firmware to upload when first connected to the Internet.

Part 2: Control the Internet with IFTTT and Push Button

Have you ever wanted to control something on the Internet with a touch of a button? How about having a button that orders new laundry detergent when pressed? Okay, that has already been done with Amazon Dash Button. However, you can make one too! For the second part of this experiment, we are going to use IFTTT to send an email when the push button is pressed.

A fun feature of the Photon RedBoard is that it works with Particle’s IFTTT channel. IFTTT is short for “if this then that.” It is a free site that makes connecting different popular apps or products really easy and fast!

Let’s get started!

New Photon Code

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 2 - Part 2: Control the Internet with IFTTT and Push Button
    This sketch was written by SparkFun Electronics
    August 31, 2015
    https://github.com/sparkfun

    This is a simple example sketch that sends an email
    with IFTTT when the push button is pressed

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/

int led = D0; // LED is connected to D0
int pushButton = D2; // Push button is connected to D2

// This routine runs only once upon reset
void setup()
{
  pinMode(led, OUTPUT); // Initialize D0 pin as output
  pinMode(pushButton, INPUT_PULLUP);
  // Initialize D2 pin as input with an internal pull-up resistor
}

// This routine loops forever
void loop()
{
  int pushButtonState;

  pushButtonState = digitalRead(pushButton);

  if(pushButtonState == LOW){ // If we push down on the push button
    digitalWrite(led, HIGH);  // Turn ON the LED
    Spark.publish("pushButtonState","Pressed",60,PRIVATE);
    // Add a delay to prevent getting tons of emails from IFTTT
    delay(5000);
  }
  else
  {
    digitalWrite(led, LOW);   // Turn OFF the LED
  }

}

Code to Note

Spark.publish("pushButtonState", "Pressed",60,PRIVATE);

What is great about IFTTT is that you do not need a lot of extra code. This is the only piece of code we added. Visit the Spark.publish() page to learn more.

Setup IFTTT

Now that the code is loaded on your Photon RedBoard, we can jump into setting up IFTTT.

Sign up, or log into IFTTT, and activate your account from your email address. For this experiment we are going to create an IF recipe. The IF recipe is connecting two different apps and products in an if this then that statement. Click on My Recipe on the top navigation bar on the IFTTT site.

Follow the 7 steps below to create an IF recipe.

1: This

Click on this

alt text

Find and select the Particle Channel. You will need to connect to the Particle channel.

alt text

2: Choose a Trigger

Selected New event published

alt text

3: Complete Trigger Fields

This is where you will enter the published event name. Type in pushButtonState in the If (Event Name) field. Go ahead and select your personal Photon RedBoard from the drop down menu.

alt text

4: That

Click on the that button and find the Email Channel.

alt text

Remember, your Photon RedBoard has the unique name you gave it and will show up differently then the above image.

alt text

5: Choose an Action

Depending on what Channel you are using, there might be one or more different actions you can choose from. For this experiment, we will send an email.

alt text

6: Complete Action Fields

You can customize what you want to see in the subject and body of your email.

alt text

7: Create and connect

Name your recipe and hit Create Recipe.

alt text

You’ve just created your first IFTTT recipe!

What You Should See

When you push down on the button, an email will send! You might have allow the gearworks of the internet to churn for a minute or two, but you should be able to see a new email. If you don’t feel like waiting, go ahead and click on the refresh-looking icon, named “Check Recipe now”.

You might have noticed there’s a vast amount of options for creating recipes. The possibilities are endless! Using the same code, here are a few more examples on what you can do:

  • Post on social media and websites like Facebook, Twitter, Reddit, GitHub, Pinterest, Tumblr, and more!
  • There are tons of products you might already use that have their own Channels. Including appliances and other home products
  • Send a text message
  • Shop at different sites

Troubleshooting

  • Not seeing the email? A lot of people have multiple email addresses. Double check the same email you used to sign up for IFTTT. There is a Check Recipe now button for testing your recipes. This can be found under the My Recipes page.
  • Still not working? Try redoing the IFTTT recipe again or check for typos.

Experiment 3: Houseplant Monitor

Introduction

This experiment uses a soil moisture sensor to allow you to monitor your houseplants. The first half of the experiment will introduce the concept of Analog Inputs, which allow us to read values that vary between two known thresholds rather than just being a HIGH or LOW digital value. You will use a built in Analog-to-Digital Converter on the Photon RedBoard to read the analog value coming from the soil moisture sensor. In the second half, we will expose that analog variable to the Particle Cloud so that other online applications can request the current moisture content and send you notifications when your plant needs watering.

Parts Needed

You will need the following parts:

  • 1x Soil Moisture Sensor
  • 3x Jumper Wire
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
SparkFun Soil Moisture Sensor

SEN-13322
$4.95
1
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95
Screw Terminals 3.5mm Pitch (3-Pin)

PRT-08235
$0.95

Tools Needed

You will need the screwdriver included in the Photon SIK. Find the second smallest flathead head tip, labeled CR-V 2.0, and insert it into the tip of the screwdriver.

Suggested Reading

Before continuing on with this experiment, we recommend you be familiar with the concepts in the following tutorials:

Hardware Hookup

Hook up your circuit as pictured below:

Experiment 3 Diagram

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

The easiest way to connect the soil moisture sensor to the RedBoard is to insert one end of each jumper wire into the 3-pin screw terminal attached to the soil sensor, and then screw each pin down until the jumper wire is secured and won’t pull out of the screw terminal.

jumpers screw terminal

Photon Code

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 3 - Part 1: LED Houseplant Monitor
    This sketch was written by SparkFun Electronics
    Joel Bartlett <joel@sparkfun.com>
    August 31, 2015
    https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments

    This application monitors the moisture level of your houseplant
    and turns the RGB LED red when the plant needs watered.
    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/
int val = 0;//variable to store soil value
int soil = A2;//Declare a variable for the soil moisture sensor
int soilPower = D6;//Variable for Soil moisture Power
//Rather than powering the sensor through the V-USB or 3.3V pins,
//we'll use a digital pin to power the sensor. This will
//prevent oxidation of the sensor as it sits in the corrosive soil.

void setup()
{
Serial.begin(9600);   // open serial over USB

pinMode(soilPower, OUTPUT);//Set D7 as an OUTPUT
digitalWrite(soilPower, LOW);//Set to LOW so no power is flowing through the sensor
}

void loop()
{
Serial.print("Soil Moisture = ");
//get soil moisture value from the function below and print it
Serial.println(readSoil());

delay(1000);//take a reading every second
//This time is used so you can test the sensor and see it change in real-time.
//For in-plant applications, you will want to take readings much less frequently.

    //If your soil is too dry, turn on Red LED to notify you
    //This value will vary depending on your soil and plant
    if(readSoil() < 200)
    {
      // take control of the RGB LED
      RGB.control(true);
      RGB.color(255, 0, 0);//set RGB LED to Red
    }
    else
    {
      // resume normal operation
      RGB.control(false);
    }
}
//This is a function used to get the soil moisture content
int readSoil()
{
    digitalWrite(soilPower, HIGH);//turn D6 "On"
    delay(10);//wait 10 milliseconds
    val = analogRead(soil);
    digitalWrite(soilPower, LOW);//turn D6 "Off"
    return val;
}

What You Should See

Once the code is uploaded to your Photon RedBoard, open your favorite Serial Terminal program. Connect to the Photon RedBoard. You should see soil moisture data begin to stream in the window.

Soil Moisture Readings

Pressing your finger across the two prongs at varying pressures results in different moisture readings.

When the sensor detects very little moisture, the RGB LED on the Photon RedBoard will turn Red, notifying you that your plant needs watered. When the moisture level is satisfactory, the LED will breathe cyan, as usual.

alt text

An exposed sensor should read close to zero, producing Red on the RGB LED.

Code to Note

Serial

Among other things, this example introduces serial communication with functions like Serial.begin() and Serial.print(). To initialize a serial interface, call Serial.begin([baud]) where [baud] sets the baud rate of the interface. In this example, we set the baud rate to 9600bps – a reliable (if slow) standard rate – in the setup() function:

language:c
void setup()
{
    ...
    Serial.begin(9600); // Start the serial interface at 9600 bps
    ...
}

To send data out of a serial interface, use either Serial.print(), Serial.println(), or Serial.write(). This example only uses the first two.

language:c
Serial.print("Soil Moisture = ");
//get soil moisture value from the function below and print it
Serial.println(readSoil());

For more information on Serial functions, check out Particle’s reference documentation.

Functions

int readSoil() is a user-made function. As with any other object-oriented language, you can declare your own functions that can be passed and can return different types of variables.

This function has no parameters passed to it, but it does return the soil moisture value as an integer (INT). You can create your own functions to accomplish tasks that you do not want to type out over and over again. Instead, you can call that function anywhere you would have written all that other code.

Troubleshooting

  • Configuring the soil sensor can take a little trial and error. Different soils and moisture levels will result in different data. To get good values on which to base your plant’s condition, it best to take a reading when it is is as dry as possible without jeopardizing the plant’s well being. Take another reading after you’ve recently watered the plant to get your upper threshold. You can then adjust the code accordingly.

Part 2: Spark Variables

Being notified visually that your plant needs watered is useful, but what about when you leave for a week? How will you know if your plant is happy and thriving while you’re gone? One way to give you a view into your plants status is to use the spark.variable function, which is a built-in feature of the Particle firmware. This second example will use this feature to allow you to check the status of your plant anywhere that you have an Internet connection.

New Photon Code

Copy, paste and upload this new sketch. You’ll notice not much has changed. The Spark.variable("soil", &soil, INT); line is the only new addition.

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 3 - Part 2: Internet Houseplant Monitor
    This sketch was written by SparkFun Electronics
    Joel Bartlett <joel@sparkfun.com>
    August 31, 2015
    https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments

    This application monitors the moisture level of your houseplant
    and exposes that data to be monitored via the Internet.
    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/
int val = 0;//variable to store soil value
int soil = A2;//Declare a variable for the soil moisture sensor
int soilPower = D6;//Variable for Soil moisture Power
//Rather than powering the sensor through the V-USB or 3.3V pins,
//we'll use a digital pin to power the sensor. This will
//prevent oxidation of the sensor as it sits in the corrosive soil.

void setup()
{
Serial.begin(9600);   // open serial over USB

pinMode(soilPower, OUTPUT);//Set D7 as an OUTPUT
digitalWrite(soilPower, LOW);//Set to LOW so no power is flowing through the sensor

//This line creates a variable that is exposed through the cloud.
//You can request its value using a variety of methods
Spark.variable("soil", &val, INT);
}

void loop()
{
Serial.print("Soil Moisture = ");
//get soil moisture value from the function below and print it
Serial.println(readSoil());

delay(1000);//take a reading every second
//This time is used so you can test the sensor and see it change in real-time.
//For in-plant applications, you will want to take readings much less frequently.

    //If your soil is too dry, turn on Red LED to notify you
    //This value will vary depending on your soil and plant
    if(readSoil() < 200)
    {
      // take control of the RGB LED
      RGB.control(true);
      RGB.color(255, 0, 0);//set RGB LED to Red
    }
    else
    {
      // resume normal operation
      RGB.control(false);
    }
}
//This is a function used to get the soil moisture content
int readSoil()
{
    digitalWrite(soilPower, HIGH);//turn D6 "On"
    delay(10);//wait 10 milliseconds
    val = analogRead(soil);
    digitalWrite(soilPower, LOW);//turn D6 "Off"
    return val;
}

What You Should See

If you haven’t already, place your sensor in the plant you would like to monitor.

alt text

You can open the serial terminal to see the soil moisture value, as in the previous example. However, you can now also request that same value through the web. In order to do so, you’ll need your Photon’s device ID as well as your account’s access token. The device ID can be found in Particle Build by clicking the ‘>’ next to your device name.

Particle Device ID

Find your Device ID under the “Devices” tab, by clicking the carrot next to your Photon.

Your access token can be found under the “Settings” tab.

Access Token

Find your access token under the “Settings” tab.

Armed with those long strings of hex characters, open a new browser tab and navigate to:

https://api.particle.io/v1/devices/DEVICE_ID/soil?access_token=ACCESS_TOKEN

Make sure to sub in the proper values for DEVICE_ID and ACCESS_TOKEN.

TIP: You can also use the Name of your device instead of the device ID.

If everything was entered correctly, you should see something like this where ‘result’ is the current value:

alt text

The Spark variable responds with a JSON string including a “result” key and value.

Now, you can create a bookmark using that URL. Every time you refresh that page, you’ll get the current status of your plant! You can expand upon this in many ways. You can use examples from other experiments to get email notifications when your plant needs water, or you could even build an webpage that pulls that value in and displays it in a more visually appealing manner.

Code to Note

The Spark.variable("soil", &val, INT); line is the only new addition to this code, however, it is a very important addition, allowing for other applications to request the soil moisture value. The first parameter is the name of the exposed variable. This will be the name your request in the URL. You can declare up to 10 cloud variables, and each variable name is limited to a max of 12 characters. The second parameter requires a basic understanding of pointers. The ampersand (&) symbol means the address of the variable it precedes, so in this case it’s requesting the value that resides at the memory address allocated to the val variable, which contains the current soil moisture value. The last parameter is the type of variable that will be exposed, in this case an integer. For more info on cloud variables, visit the particle website.

Troubleshooting

  • Having issues seeing the online data? Make sure you have grabbed the correct Device ID for the board you are working with. If you have numerous Particle devices associated with your account, it’s easy to get the device ID from device mixed up with that of another. If you see a ‘Permission Denied’ error like the one below, you either have the wrong device ID, or there is a typo in the ID you’re attempting to use.

alt text

  • Similarly, you may get an access token error. If so, visit the Settings section of Particle Build, and reset your access token or make sure there is no typos.

alt text

alt text

  • If you get a time out error, make sure your device is properly powered and connected to the web.

alt text

Experiment 4: Color Selector

Introduction

In this experiment you’ll learn about analog input and output, the difference between analog and digital, and how to incorporate analog inputs and outputs into your project. We will also touch on some more advanced concepts, like using internal pull-up resistors and integrating with Twitter via IFTTT (If This Than That).

Parts Needed

You will need the following parts:

  • 1x Breadboard
  • 1x Photon RedBoard
  • 1x Potentiometer
  • 3x Simon Buttons (Red, Green, and Blue)
  • 11x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
Trimpot 10K with Knob

COM-09806
$0.95
4
Tactile Button Assortment

COM-10302
$4.95
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95

Suggested Reading

There are a variety of core concepts in electronics that we will be touching on in this circuit, but not discussing in depth. However, we do have some great tutorials that go into more detail about what’s going on behind the scenes.

  • Analog vs. Digital - understanding the difference between analog and digital devices is going to be very helpful for this section.
  • Pulse-Width Modulation - pulse-width modulation (or PWM) is the way digital microcontrollers simulate analog output.
  • Analog to Digital Conversion - knowing how your microcontroller translates between digital and analog signals will help you understand many of the basics covered here.

Hardware Hookup

Connect the components on the breadboard and wire them to the Photon RedBoard as shown below. The red button should go to pin D2, the green button to D3, and the blue button to pin D4. The potentiometer should go to pin A0. Don’t forget to run power (3.3V) and ground (GND) from your Photon to your breadboard.

alt text

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

Digital vs. Analog (A Very Short Summary)

If you haven’t guessed by now, the letter in front of each pin number refers to either D (Digital) or A (Analog) - but how do we know which one to use for each component? Basically, we want to think about the number of states or possible values a part can have: digital parts have only 2 states - ON or OFF (TRUE or FALSE, 1 or 0, etc.), while analog parts can have a range of states or values (think about the brightness of a light, or the volume control on your computer).

So, in general, a smooth range of values means analog, 2 (or more) discrete values means digital. That’s why our buttons, which can only be pressed or not pressed at any given time, are connected to digital pins. Our potentiometer (which is really just a fancy word for dial or knob) can be at a wide range of values, so it gets connected to an analog pin.

Photon RedBoard Code Part 1

If you’re use to Arduino, this part’s a little different. Instead of downloading and installing an IDE, you’ll need to open up an Internet browser and open this link (preferably on another tab or window):

www.particle.io/build

It is a bit of an adjustment at first, but it ends up being fantastic since you can program your Photon RedBoard from anywhere in the world as long as you have an Internet connection.

Now, copy and paste this code into the IDE:

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 4 - Part 1
    This sketch was written by SparkFun Electronics
    Ben Leduc-Mills
    August 31, 2015
    https://github.com/sparkfun

    This is an example sketch using buttons and a potentiometer to change the color and brightness of the Photon RedBoard onboard LED.

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/
int redButton = D2; // declare variable for red button
int greenButton = D3; // declare variable for green button
int blueButton = D4; // declare variable for blue button

int potentiometer = A0; // declare variable for potentiometer
int colorMode; // declare variable to keep track of color
int potValue = 0; // // declare variable for the value of the potentiometer

void setup() {

    RGB.control(true); // command to control the RGB led on the Photon
    // buttons need an internal pullup resistor - see below for notes
    pinMode(redButton, INPUT_PULLUP);
    pinMode(greenButton, INPUT_PULLUP);
    pinMode(blueButton, INPUT_PULLUP);
    pinMode(potentiometer, INPUT); // potentiometers are an analog input
    colorMode = 0;

}

void loop() {

    // change colorMode variable depending on which button was pressed
    if(digitalRead(redButton) == LOW) { // double equals checks for equality
        colorMode = 1; // single equals is for assigning a new value to the variable
    }

    if(digitalRead(greenButton) == LOW) {
        colorMode = 2;
    }

    if(digitalRead(blueButton) == LOW) {
        colorMode = 3;
    }

    // read from the potentiometer, divide by 16 to get a number we can use for a color value
    potValue = analogRead(potentiometer)/16;
    changeColor(colorMode, potValue); // call changeColor function

}

// changeColor takes a color mode and a potentiometer value and changes the color and brightness of the Photon RGB LED
void changeColor(int _colorMode, int _potValue) {

    if(_colorMode == 1) {
        RGB.color(_potValue, 0, 0);
    }

    if(_colorMode == 2) {
        RGB.color(0, _potValue, 0);
    }

    if(_colorMode == 3) {
        RGB.color(0, 0, _potValue);
    }

    else if(_colorMode == 0) {
        RGB.color(0,0,0);
    }
}

What You Should See

After the you click ‘flash’ and the upload process is complete, you should be able to control the color and brightness of the Photon RedBoard’s onboard LED. The buttons will turn the LED red, green, or blue, and turning the potentiometer will affect the brightness of the LED.

Switching colors:

alt text

Fading:

alt text

Pretty neat, huh? Now, let’s take this circuit and make it a part of the Internet of Things!

Troubleshooting

  • If your buttons aren’t working, make sure they are pushed down firmly into the breadboard and that you declared them as INPUT_PULLUP in your code.
  • If the LED is still breathing Cyan, double check that you put in the RGB.control(true); line in your setup() function.

Part 2: Turn on an LED with IFTTT (If This Then That)

IFTTT is a website that uses conditional statements and does very useful things with well known applications, such as Gmail, Craigslist, Twitter, or Facebook. Such as, Tweet a color to your LED– which is what we’re about to do!

Code to Note

RGB.control(true);

You may have noticed by now that there is no LED on your breadboard. Instead, we’re going to take control of the RGB LED on the Photon RedBoard that’s usually reserved for showing the status of the board. We do this by using the built-in RGB library and setting control to us, the user.


pinMode(redButton, INPUT_PULLUP);

Push buttons like the ones we’re using operate by closing or opening a circuit when you push down the button. The Photon can detect this change and report it to us so we know when someone pushes our buttons. Often, buttons are hooked up to the breadboard with a ‘pull-up’ resistor (usually 10KΩ) which in essence pulls the voltage reading from the button to HIGH. This means that when we push the button the value goes LOW, which seems to make sense to us logically. Luckily for us, the Photon has internal pull-up resistors that we can turn on through the code - by changing the pinMode type from the usual INPUT to INPUT_PULLUP.


Photon Code Part 2

Since we’ll be needing IFTTT to communicate with our Photon RedBoard, we’ll need to modify our code. Particle.io has created many useful IoT functions for the Photon RedBoard, and we’ll be using one of them – more specifically Particle.function(). In our case, we’re going to find a way to ‘tweet’ an RGB color value, and have the onboard LED of the Photon RedBoard turn that color.

Go ahead and paste this code into the Particle Build IDE:

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 4 - Part 2
    This sketch was written by SparkFun Electronics
    Ben Leduc-Mills
    August 31, 2015
    https://github.com/sparkfun

    This is an example sketch showing how to change the color of the Photon Redboard onboard LED using Twitter and IFTTT (If this then that).

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/
//declare the name of our function (and its parameters) at the top of our program
int rgbColor(String val);

//variables for our colors (red, green, blue)
int r,g,b;


void setup() {
    //take control of the Photon RGB LED
    RGB.control(true);
    //register our function in the Particle cloud
    Particle.function("rgbColor", rgbColor);
}

void loop() {
    //don't need to do anything in the loop
}

//our actual function call
//looking for a string of three numbers that represents an RGB color
//e.g. 200,12,42
int rgbColor(String val) {

    //check if incoming string is empty
    if(val.length() > 0) {

        //if not, use indexOf to find the first comma delimiter
        //this string class has no split command
        //more about indexOf: https://docs.particle.io/reference/firmware/photon/#indexof-
        int i = val.indexOf(",");

        //use substring to get the value from the beginning of the string until the first comma
        //then use toInt to convert from a string to an integer
        //which gets us our first number, the r value
        r = val.substring(0,i).toInt();

        //more string manipulation to get our g and b values
        int j = val.indexOf(",", i+1);

        g = val.substring(i+1, j).toInt();

        b = val.substring(j+1, val.length()).toInt();

        //put it all together and make the LED light up
        RGB.color(r, g, b);

        //if we're successful return 1
        return 1;
    }
    //something went wrong
    else return -1;
}

Setup IFTTT

Now that we’ve prepped our code to talk with the IFTTT service, we have to actually sign up for it and create our Internet of Things ‘recipe’. If you’re completing the exercises in order, you will have signed up for IFTTT in the last exercise, if not, go ahead and sign up now.

  • Sign up, or log into IFTTT, and activate your account from your email address.
  • Create your first recipe:
    • Click on the blue lettered: “This”
    • Type Twitter into the search bar and click on it
    • Connect your Twitter account to IFTTT, hit continue
    • On the “Choose a Trigger” page, select “New tweet by @yourtwitter with hashtag”
    • For the hashtag, type in #rgb

You should see something like:

alt text

  • Click on “Create trigger”
  • Click on “That”
  • Search for “Particle”, click on it
  • Choose “Call a function”, and select the function we put in our photon code: “rgbColor on (your Photon RedBoard’s name)”.
  • In the “with input(Function Input)” field, choose TextNoHashtag

You should see:

alt text

  • Finally, click on “Create Action” and finally “Create Recipe”

Great! You’ve just created an IFTTT recipe that calls our rgbColor() function whenever we send a tweet with a specific hashtag.

What You Should See

Send a tweet with the hashtag #rgb followed by numbers for red, green, and blue values. You might have to allow the gears of the Internet to churn for a minute or two, but you should eventually see your LED turn on. If you don’t feel like waiting, go ahead and click on the refresh-looking icon in the IFTTT dashboard for your recipe, named “Check Recipe now”.

Your tweet should look something like:

alt text

Code to Note

Particle.function("rgbColor", rgbColor);

Particle.function is a function specifically made for communication with IFTTT. It works as Particle.function(“cloudNickname”, firmwareFunctionName). The cloud name can be maximum 12 characters long. There’s a great write up for this provided by Particle.io, click HERE.

Manipulating groups of words and symbols, or ‘strings’ is a key component in many programs - so much so that Particle has a String ‘class’ - which allows us to use several different pre-built methods for dealing with strings. In fact, use four of them just in this exercise: length(), indexOf(), substring(), and toInt(). More info on these and other useful methods for strings can be found in the Particle docs here.

Troubleshooting

  • If the LED is still breathing Cyan, double check that you put in the RGB.control(true); line in your setup() function.
  • If the function name doesn’t show up when trying to complete the ‘call a function’ step (6 of 7), make sure your board is plugged in, and that you’ve saved your code in the cloud with the Particle.function("turnOnLED", LEDstate); line in your code.
  • If the function still doesn’t show up, you may have to go into IFTTT, delete your Particle channel (and all your recipes), then reconnect the channel and rebuild your recipe from scratch.

Going Further

We’ve just scraped the beginning of analog input (your potentiometer) and analog output (the brightness and colors of the RGB LED). Try changing some of the code to get the red, green, and blue LED’s to mix. Try altering your IFTTT recipe to change the LED color based on a text from your phone, or the temperature in Tahiti. Let us know what you came up with!

Experiment 5: Music Time

Introduction

In this circuit, we’ll again bridge the gap between the digital world and the analog world. We’ll be using a piezo speaker that makes a small “click” when you apply voltage to it (try it!). By itself that isn’t terribly exciting, but if you turn the voltage on and off hundreds of times a second, the piezo speaker will produce a tone. And if you string a bunch of tones together, you’ve got music! This circuit and sketch will play a classic tune. We’ll never let you down!

Parts Needed

You will need the following parts:

  • Photon RedBoard, Breadboard, and Base Plate
  • 1x Piezo Speaker
  • 2x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
Piezo Speaker - PC Mount 12mm 2.048kHz

COM-07950
$1.95
5
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95

Suggested Reading

  • tone()– Read up about tone() to get started on making your own songs!

Let’s Talk More about Polarity

We talked about polarity shortly in the past experiments. In the realm of electronics, polarity indicates whether a circuit component is symmetric or not. A non-polarized component – a part without polarity – can be connected in any direction and still function the way it’s supposed to function. A symmetric component rarely has more than two terminals, and every terminal on the component is equivalent. You can connect a non-polarized component in any direction, and it’ll function just the same.

A polarized component – a part with polarity – can only be connected to a circuit in one direction. A polarized component might have two, twenty, or even two-hundred pins, and each one has a unique function and/or position. If a polarized component was connected to a circuit incorrectly, at best it won’t work as intended. At worst, an incorrectly connected polarized component will smoke, spark, and be one very dead part.

To learn more about polarity, check out our What is Polarity? tutorial!

Hardware Hookup

If the piezo speaker doesn’t easily fit into the holes on the breadboard, try rotating it slightly.

Pay special attention to the component’s markings indicating how to place it on the breadboard. Polarized components can only be connected to a circuit in one direction. Orientation matters for the following component: Piezo Speaker

RedBoard piezo speaker hookup

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

Photon Code

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 5 - Part 1: Music Time
    This sketch was written by SparkFun Electronics
    August 31, 2015
    https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments

    This application plays Rick Astley - Never Gonna Give You Up song
    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License (http://opensource.org/licenses/MIT)
*/

const int speakerPin = D2;

// We'll set up an array with the notes we want to play
// change these values to make different songs!

// Length must equal the total number of notes and spaces

const int songLength = 18;

// Notes is an array of text characters corresponding to the notes
// in your song. A space represents a rest (no tone)

char notes[] = "cdfda ag cdfdg gf "; // a space represents a rest

// Beats is an array of values for each note and rest.
// A "1" represents a quarter-note, 2 a half-note, etc.
// Don't forget that the rests (spaces) need a length as well.

int beats[] = {1,1,1,1,1,1,4,4,2,1,1,1,1,1,1,4,4,2};

// The tempo is how fast to play the song.
// To make the song play faster, decrease this value.

int tempo = 150;


void setup()
{
  pinMode(speakerPin, OUTPUT);
}


void loop()
{
  int i, duration;

  for (i = 0; i < songLength; i++) // step through the song arrays
  {
    duration = beats[i] * tempo;  // length of note/rest in ms

    if (notes[i] == ' ')          // is this a rest?
    {
      delay(duration);            // then pause for a moment
    }
    else                          // otherwise, play the note
    {
      tone(speakerPin, frequency(notes[i]), duration);
      delay(duration);            // wait for tone to finish
    }
    delay(tempo/10);              // brief pause between notes
  }

  // We only want to play the song once, so we'll pause forever:
  while(true){}
  // If you'd like your song to play over and over,
  // remove the above statement
}


int frequency(char note)
{
  // This function takes a note character (a-g), and returns the
  // corresponding frequency in Hz for the tone() function.

  int i;
  const int numNotes = 8;  // number of notes we're storing

  // The following arrays hold the note characters and their
  // corresponding frequencies. The last "C" note is uppercase
  // to separate it from the first lowercase "c". If you want to
  // add more notes, you'll need to use unique characters.

  // For the "char" (character) type, we put single characters
  // in single quotes.

  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  int frequencies[] = {262, 294, 330, 349, 392, 440, 494, 523};

  // Now we'll search through the letters in the array, and if
  // we find it, we'll return the frequency for that note.

  for (i = 0; i < numNotes; i++)  // Step through the notes
  {
    if (names[i] == note)         // Is this the one?
    {
      return(frequencies[i]);     // Yes! Return the frequency
    }
  }
  return(0);  // We looked through everything and didn't find it,
              // but we still need to return a value, so return 0.
}

What You Should See

You should see - well, nothing! But you should be able to hear a song. If it isn’t working, make sure you have assembled the circuit correctly and verified and uploaded the code to your board or see the troubleshooting section.

Photon RedBoard piezo speaker

See if you can recreate your favorite songs!

Code to Note

Up until now we’ve been working solely with numerical data, but the Photon RedBoard can also work with text. Characters (single, printable, letters, numbers and other symbols) have their own type, called “char”. When you have an array of characters, it can be defined between double-quotes (also called a “string”), OR as a list of single-quoted characters.

tone(pin, frequency, duration);

One of Photon RedBoard’s many useful built-in commands is the tone() function. This function drives an output pin at a certain frequency, making it perfect for driving piezo speakers. If you give it a duration (in milliseconds), it will play the tone then stop. If you don’t give it a duration, it will keep playing the tone forever (but you can stop it with another function, noTone() ).

Troubleshooting

  • No Sound - Given the size and shape of the piezo buzzer it is easy to miss the right holes on the breadboard. Try double checking its placement.

  • Can’t Think While the Melody is Playing - Just pull up the piezo buzzer whilst you think, upload your program then plug it back in.

  • Feeling Let Down and Deserted - The code is written so you can easily add your own songs.

Experiment 6: Environment Monitor

Introduction

This experiment will hook the Photon up to a temperature/humidity sensor and a photocell to observe lighting conditions. We’ll initially use serial communication to check their readings. Once you’ve gotten a handle on interacting with those sensors, we’ll gather their data and regularly post it to data.sparkfun.com, SparkFun’s free data storage service.

In addition to covering the basics of serial communication, this experiment will also introduce libraries. Libraries are a powerful tool in the Particle IDE. They’re pre-written files of code designed to accomplish certain tasks – like reading a sensor – in a very concise manner. They’ll make your life a lot easier.

Enivorment Monitor Photo

Once the experiment is completed, you’ll have a fully functional environmental data logging station, which can be observed from anywhere in the world!

Parts Needed

  • 1x RHT03 Humidity and Temperature Sensor
  • 1x Mini Photocell
  • 1x 330Ω Resistor
  • 7x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
Humidity and Temperature Sensor - RHT03

SEN-10167
$9.95
13
Mini Photocell

SEN-09088
$1.5
4
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95
Resistor 330 Ohm 1/6 Watt PTH - 20 pack

COM-11507
$0.95
1

Suggested Reading

  • Serial Communication– Serial interfaces allow devices to exchange complex strings of data using just two wires. In this experiment, we’ll use a serial interface between the Photon RedBoard and our computer to check the latest readings from the light and temperature/humidity sensors.
  • Serial Terminal Basics– To interact with a serial interface on your computer, you’ll need a serial terminal program. You may already have a serial terminal installed on your computer. If not, check out this tutorial for our recommendations, and a guide to getting started.
  • Including Particle Libraries– Libraries are one of the most powerful tools availabe in the Particle Build IDE. Read through this tutorial to find out how to add a library to your application code.

Hardware Hookup

Hook up your circuit as shown below:

Environment monitor fritzing diagram

The yellow wire running from the photocell to the Photon’s A0 pin carries an analog value relative to the light being sensed. The blue wire, running between the RHT03 sensor and Photon pin D3, is a very precisely-timed digital data interface. The SparkFunRHT03 library simplifies this interface to just a few lines of code.

Both the photocell and the RTH03 need a power supply to be operational. In this circuit, we’re powering both off the Photon’s 3.3V/GND supply.

Photon Code

Copy and paste the code below into a new application – ours is called EnvironmentLoggerSerial:


/////////////////////
// Pin Definitions //
/////////////////////
const int RHT03_DATA_PIN = D3; // RHT03 data pin
const int LIGHT_PIN = A0; // Photocell analog output
const int LED_PIN = D7; // LED to show when the sensor's are being read

///////////////////////////
// RHT03 Object Creation //
///////////////////////////
RHT03 rht; // This creates a RTH03 object, which we'll use to interact with the sensor

unsigned int minimumLight = 65536;
unsigned int maximumLight = 0;
float minimumTempC = 5505;
float maximumTempC = 0;
float minimumTempF = 9941;
float maximumTempF = 0;
float minimumHumidity = 100;
float maximumHumidity = 0;

#define PRINT_RATE 1500 // Time in ms to delay between prints.

void setup()
{
    // Serial.begin() is used to open up a serial interface between the Photon
    // and your computer.
    // The '9600' parameter configures the speed of the interface. This value is
    // called the "baud rate", which is equivalent to bits per second (bps).
    Serial.begin(9600); // Start the serial interface at 9600 bps

    // Using the 'rht' object created in the global section, we'll begin by calling
    // its member function `begin`.
    // The parameter in this function is the DIGITAL PIN we're using to communicate
    // with the sensor.
    rht.begin(RHT03_DATA_PIN);  // Initialize the RHT03 sensor

    // Don't forget to set the pin modes of our analog sensor (INPUT) and the LED (OUTPUT):
    pinMode(LIGHT_PIN, INPUT); // Set the photocell pin as an INPUT.
    pinMode(LED_PIN, OUTPUT); // Set the LED pin as an OUTPUT
    digitalWrite(LED_PIN, LOW); // Initially set the LED pin low -- turn the LED off.
}

void loop()
{
    digitalWrite(LED_PIN, HIGH); // Turn the LED on -- it'll blink whenever the sensor is being read.

    // Use the RHT03 member function `update()` to read new humidity and temperature values from the sensor.
    // There's a chance the reading might fail, so `update()` returns a success indicator. It'll return 1
    // if the update is successful, or a negative number if it fails.
    int update = rht.update();

    if (update == 1) // If the update succeeded, print out the new readings:
    {
        // Use analogRead to get the latest light sensor reading:
        unsigned int light = analogRead(LIGHT_PIN);
        // Do some math to calculate the minimum and maximum light sensor readings we've seen:
        if (light > maximumLight) maximumLight = light;
        if (light < minimumLight) minimumLight = light;

        // The `humidity()` RHT03 member function returns the last successfully read relative
        // humidity value from the RHT03.
        // It'll return a float value -- a percentage of RH between 0-100.
        // ONLY CALL THIS FUNCTION AFTER SUCCESSFULLY RUNNING rht.update()!.
        float humidity = rht.humidity();
        // Do some math to calculate the max/min humidity
        if (humidity > maximumHumidity) maximumHumidity = humidity;
        if (humidity < minimumHumidity) minimumHumidity = humidity;

        // The `tempF()` RHT03 member function returns the last succesfully read
        // farenheit temperature value from the RHT03.
        // It returns a float variable equal to the temperature in Farenheit.
        // ONLY CALL THIS FUNCTION AFTER SUCCESSFULLY RUNNING rht.update()!.
        float tempF = rht.tempF();
        // Do some math to calculate the max/min tempF
        if (tempF > maximumTempF) maximumTempF = tempF;
        if (tempF < minimumTempF) minimumTempF = tempF;

        // `tempC()` works just like `tempF()`, but it returns the temperature value in
        // Celsius.
        // ONLY CALL THIS FUNCTION AFTER SUCCESSFULLY RUNNING rht.update()!.
        float tempC = rht.tempC();
        // Do some math to calculate the max/min tempC
        if (tempC > maximumTempC) maximumTempC = tempC;
        if (tempC < minimumTempC) minimumTempC = tempC;


        // `Serial.print()` is used to send data from the Photon to our computer over the serial interface.
        // The parameter passed to `print()` can be anything from a String, to a constant array of charaters,
        // to a float value, integer, or nearly any other type of variable.
        Serial.print("Light: "); // Print "Light: "
        Serial.print(light); // Print the light reading
        Serial.print(" ("); // Print " ("
        Serial.print(minimumLight); // Print the minimum light reading
        Serial.print('/'); // Print a '/' -- single quotes mean we're only sending one character
        Serial.print(maximumLight); // Print the maximum light reading.
        Serial.println(") (min/max)"); // Finish the line by printing ") (min/max)"
        // The full line will look something like: "Light: 545 (8/791) (min/max)"

        // Print the temperature in °C:
        // Example printout: "Temp: 24.9 °C (23.5/24.5) (min/max)"
        Serial.print("Temp: ");
        Serial.print(tempC, 1); // Printing a float, we can set the precision (number of decimal points) with the second parameter
        Serial.print("");
        // `write()` can be used to write a SINGLE BYTE value over the serial line:
        Serial.write(248); // 248 is the ASCII value for the ° symbol. We're fancy.
        Serial.print("C (");
        Serial.print(minimumTempC, 1);
        Serial.print('/'); // Print a '/'
        Serial.print(maximumTempC, 1);
        Serial.println(") (min/max)");

        // Print the temperature in °F:
        // Example printout: "Temp: 76.1 °F (74.3/76.1) (min/max)"
        Serial.print("Temp: "); // Print "Temp: "
        Serial.print(tempF, 1); // Print the tempF variable -- 1 decimal point
        Serial.print(' ');      // Print a space
        Serial.write(248);      // Print ASCII value 248 (the ° symbol)
        Serial.print("F (");    // Print "F ("
        Serial.print(minimumTempF, 1); // Print minimum temperature -- 1 decimal point
        Serial.print('/');      // Print a '/'
        Serial.print(maximumTempF, 1); // Print maximum temp -- 1 decimal point
        Serial.println(") (min/max)"); // Finsh the line by printing ") (min/max)"

        // Print the relative humidity:
        // Example printout: Humidity: 29.7 % (29.10/41.80) (min/max)
        Serial.print("Humidity: ");
        Serial.print(humidity, 1);
        Serial.print(" %");
        Serial.print(" (");
        Serial.print(minimumHumidity, 1);
        Serial.print("/");
        Serial.print(maximumHumidity, 1);
        Serial.println(") (min/max)");

        Serial.println(); // Print a blank line:
    }
    else // If the update failed, give the sensor time to reset:
    {
        Serial.println("Error reading from the RHT03."); // Print an error message
        Serial.println(); // Print a blank line

        delay(RHT_READ_INTERVAL_MS); // The RHT03 needs about 1s between read attempts
    }
    digitalWrite(LED_PIN, LOW); // Turn the LED off

    delay(PRINT_RATE); // delay for 1s, printing too much will make the output very hard to read.
}

But wait! Don’t try to upload it yet. In fact, if you try to compile, you should get an error, because we need to add the SparkFunRHT03 library.

Click the Libraries icon library icon on the left. Then click into the search bar and find SparkFunRHT03. Click it to get a description of the library, and options for using it.

Searching for and including a library

Next, click the INCLUDE IN APP button, and select your new application, and verify by selecting ADD TO THIS APP.

Adding a library to an app

Two lines of code should be appended to your application:

language:c
// This #include statement was automatically added by the Particle IDE.
#include "SparkFunRHT03/SparkFunRHT03.h"

That’s the library! Now you can verify and flash.

Including libraries: Unfortunately, there aren't any shortcuts to including libraries in your applications. Simply copying and pasting the #include statement won't actually include the library files in your application. Every time you want to include a library, you'll have to go through the process described above.

What You Should See

After uploading the code, open up a serial terminal to your Photon RedBoard’s port. If you’re on a Windows machine, it should look something like “COMN”. On Mac or Linux, the port should be something like “/dev/tty.usbmodenNNNN”.

Make sure the baud rate is set to 9600. Once the Photon connects (starts pulsing cyan), you should begin to see the light, temperature, and humidity values stream by.

Example serial monitor output

Cover up the light sensor, or shine a flashlight on it. Covering the sensor should make the readings go down – can you get it to 0?

Breathe on the temperature/humidity sensor. Does the temperature value go up? Can you influence the humidity value as well (don’t try too hard – no pouring water on the sensor!)?

Code to Note

Including the SparkFunRHT03 library gives us access to the RHT03 class. To begin using that class, we need to begin by creating an RHT03 object in the global section:

language:c
RHT03 rht; // This creates a RTH03 object, which we'll use to interact with the sensor

rht is the object we’ll use from here on to interact with the sensor. Once that’s set up, we can call rht.begin(<pin>) in the setup() function to initialize the sensor. <pin> should be the Photon digital pin connected to the sensor.

language:c
const int RHT03_DATA_PIN = D3; // RHT03 data pin
...
void setup()
{
    ...
    rht.begin(RHT03_DATA_PIN); // Initialize the RHT03 sensor
    ...
}

Finally, we can read the the temperature and humidity values from the sensor. This is a two-step process: (1) read the sensor to update all values, then (2) using get functions to use the value.

Begin by calling rht.update(). If update() succeeds (indicated by returning a 1), you can call rht.tempF(), rht.tempC(), and rht.humidity() to get the values of interest:

language:c
int update = rht.update(); // Read from the RHT
if (update == 1) // If the update was successful:
{
    float humidity = rht.humidity(); // Read humidity into a variable
    float tempC = rht.tempC(); // Read celsius temperature into a variable
    float tempF = rht.tempF(); // Read farenheit temperature into a variable
    ...
}

Troubleshooting

If your terminal program won’t let you open the serial port – citing an error like: “COM port in use”. Or, if the COM port has disappeared from a selectable list, try following these steps:

  1. Close the serial terminal program.
  2. Reset your Photon RedBoard
  3. Wait for the RedBoard to connect to WiFi
  4. Open the terminal program back up and try connecting.

If all you see is a stream of “Error reading from the RHT03”, make sure everything is correctly wired up. If you’re still not having any luck with the sensor, consider getting in touch with our tech support team. (It’s normal for the “Error…” output to show up occasionally – the RHT03’s one-wire interface isn’t very robust.)

Part 2: Posting Environment Data to Data.SparkFun.com

As you may have already experienced, using the serial port on a Photon can be very cumbersome. Let’s come up with some way to show off the sensor readings without having to fuss with terminal programs. SparkFun’s Data.SparkFun.com service is a great, FREE, utility for posting, storing, and reading sets of data. It’s powered by Phant, an open-source, Node.js-based tool that can be run on any server or computer.

To get the most out of this example, you should create a data.sparkfun.com data stream of your own. Go to data.sparkfun.com/streams/make, then fill out the form to look something like this:

Example Phant stream creation

Phant Field Names: The most critical box in the Phant stream creation is the "Fields" list. For this example, make sure those five strings are typed exactly as:

humidity, light, station, tempc, and tempf.

After creating the stream, you'll be given a set of keys – long strings of random characters. Don't lose the keys! Either copy them down, or use the "Email me my keys" feature.

New Photon Code

Create a new application, or paste this code over the previous example.


/////////////////////
// Pin Definitions //
/////////////////////
const int RHT03_DATA_PIN = D3; // RHT03 data pin
const int LIGHT_PIN = A0; // Photocell analog output
const int LED_PIN = D7; // LED to show when the sensor's are being read

///////////////////////////
// RHT03 Object Creation //
///////////////////////////
RHT03 rht; // This creates a RTH03 object, which we'll use to interact with the sensor

///////////////////////////////////////////////
// Phant (data.sparkfun.com) Keys and Server //
///////////////////////////////////////////////
// These keys are given to you when you create a new stream:
const char server[] = "data.sparkfun.com"; // Phant destination server
const char publicKey[] = "LQLwdljGJ2TqXgxvAlGv"; // Phant public key
const char privateKey[] = "A1x5rDzw47HWPqyog0lo"; // Phant private key
Phant phant(server, publicKey, privateKey); // Create a Phant object

///////////////////////
// Post Rate Control //
///////////////////////
// data.sparkfun.com limits how often you can post to the service. You are allowed up
// to 100 posts every 15 minutes.
unsigned long lastPost = 0; // lastPost keeps track of the last UNIX time we posted
const unsigned int POST_RATE_S = 60; // This sets the post rate to 60 seconds. Avoid setting it below 10s.

//////////////////////////
// Station Name Globals //
//////////////////////////
String stationName = ""; // String object to keep track of our Photon's name

void setup()
{
    Serial.begin(9600); // Start the serial interface at 9600 bps

    rht.begin(RHT03_DATA_PIN); // Initialize the RHT03 sensor

    pinMode(LIGHT_PIN, INPUT); // Set the photocell pin as an INPUT.
    pinMode(LED_PIN, OUTPUT); // Set the LED pin as an OUTPUT
    digitalWrite(LED_PIN, LOW); // Initially set the LED pin low -- turn the LED off.

    // getDeviceName() -- defined at the bottom of this code -- polls Particle's
    // server to get the name of the Photon running this application.
    getDeviceName(); // Update the stationName String
}

void loop()
{
    // This conditional should only run when the last successful post to Phant
    // was POST_RATE_S (60 seconds) or longer ago.
    // Time.now() returns the current UNIX timestamp (number of seconds since January 1, 1970).
    // It should increase by 1 every second. On a successful POST, we set lastPost equal to Time.now().
    if (lastPost + POST_RATE_S <= Time.now())
    {
        digitalWrite(LED_PIN, HIGH); // Turn the LED on to indicate we're posting
        int update = rht.update(); // Get new values from the RHT03.

        if (update == 1) // If the RHT03 update was successful
        {
            int postResult = 0; // This variable will keep track of whether or not

            //while (postResult <= 0)
            // Phant posts aren't always successful. Our postToPhant() function,
            // defined below, will return 1 if it was successful. Or a negative
            // number if it failed.
            while (postToPhant() <= 0)
            {
                Serial.println("Phant post failed. Trying again."); // Debug statement
                delay(1000); // Delay 1s so we don't flood the server
            }
            // After a successful Phant POST:
            Serial.println("Phant post success!"); // Debug print
            // Set lastPost to current time, so we don't post for another POST_RATE_S seconds:
            lastPost = Time.now();
        }
        else // If the RHT03 update failed:
        {
            delay(RHT_READ_INTERVAL_MS); // Delay to give the sensor time to reset
        }
        digitalWrite(LED_PIN, LOW); // Turn the LED off to indicate we're done posting (/trying to post)
    }
}

// postToPhant() gathers all of our sensor data, bundles it into a Phant post,
// and sends it out to data.sparkfun.com.
// It'll return either 1 on success, or a negative number if the post fails
int postToPhant(void)
{
    Serial.println("Posting to Phant!"); // Debug statement

    // Use phant.add(, ) to add data to each field.
    // Phant requires you to update each and every field before posting,
    // make sure all fields defined in the stream are added here.
    phant.add("humidity", rht.humidity(), 1); // These first three phant adds set a field value to float variable
    phant.add("tempc", rht.tempC(), 1); // The third parameter -- valid for float variables -- sets the number
    phant.add("tempf", rht.tempF(), 1); // of decimal points after the number.
    phant.add("light", analogRead(LIGHT_PIN)); // We can also add an integer
    phant.add("station", stationName); // phant.add(, ) is perfectly valid too!

    // phant.particlePost() performs all of the Phant server connection and HTTP POSTING for you.
    // It'll either return a 1 on success or negative number on fail.
    // It uses the field/value combinations added previously to send Phant its data.
    // MAKE SURE YOU COMMIT A phant.add() FOR EVERY FIELD IN YOUR STREAM BEFORE POSTING!
    return phant.particlePost();
}

///////////////////////////////
// Get Device Name Functions //
///////////////////////////////
// These sets of functions poll Particle's server for the name of our Photon.
// This method is described in Particle's documentation here:
// https://docs.particle.io/reference/firmware/photon/#get-device-name
// Function handlers are used here -- when the Spark.subscribe function
// returns, it will call the nameHandler([topic], [data]) function with our
// Photon's name.
bool validName = false; // Boolean to track if we have a valid name or not

// nameHandler() is a function handler. It's passed to Spark.subscribe() and
// called when that function returns.
// The [data] variable should have the name of our Photon when it's done.
void nameHandler(const char *topic, const char *data)
{
    stationName = String(data);  // Store the name in the stationName global variable
    validName = true; // Set validName to true, so getDeviceName can stop blocking.
}

// getDeviceName manages the spark subscribing and publishing required to get our
// Photon's name. It'll block for up to 30 seconds. On success it'll return a
// positive number. On fail, it'll return 0.
int getDeviceName(void)
{
    Spark.subscribe("spark/", nameHandler);
    Spark.publish("spark/device/name");

    int timeout = 30;
    while ((!validName) && (timeout > 0))
    {
        Serial.println("Waiting for name..." + String(timeout--));
        delay(1000); // Spark.process() is called during delay()
    }

    Serial.println("Station name = " + stationName);

    return timeout;
}

But wait! There’s more! Time to include another library. This time search for the SparkFunPhant library, and include it in your app. If you created a new application, you’ll also need to re-include the SparkFunRHT03 library.

include the SparkFunPhant library

If you created a Phant stream of your own, paste your public and private keys into the publicKey[] and privateKey[] variable values. If you haven’t created your own stream yet, feel free to take ours for a test drive. You can find the stream here. Just please, please don’t abuse it. Don’t set your post rate to faster than 60 seconds, and, once you’ve verified it works, try creating a stream of your own.

What You Should See

After uploading the code, keep an eye on your Photon’s blue, D7 LED. It should illuminate when the device is attempting to send data to data.sparkfun.com, and it’ll turn off when it’s done. To check if your data has been posted to Phant, load up the stream in your web browser (click here if you’re using our test stream). You should see a new row of data at the top, stamped with your Photon’s name, data, and a recent timestamp.

Example screenshot of data.sparkfun.com

Every minute you should see a new row of data appear. Let it run all night! See how cold your room gets. Or find out if anyone’s turning the lights on while you’re sleeping.

Now that your data is on data.sparkfun.com, you can have all sorts of fun with it. For instance, you can load your public key into analog.io– our friends who’ve created a beautiful front-end graphing interface for Phant. Or you can try to create graphs of your own – check out this tutorial using Google Charts.

Code to Note

This application introduces the Phant library, a useful tool to make posting to Phant as painless as can be. To set the Phant library up in your sketch, begin by declaring your keys and the Phant server (data.sparkfun.com):

language:c
const char server[] = "data.sparkfun.com"; // Phant destination server
const char publicKey[] = "LQLwdljGJ2TqXgxvAlGv"; // Phant public key
const char privateKey[] = "A1x5rDzw47HWPqyog0lo"; // Phant private key
Phant phant(server, publicKey, privateKey); // Create a Phant object

The last line there creates a Phant object – phant (lowercase ‘p’) – which we’ll use throughout the rest of the application to send data to Phant. This constructor call gives the Phant object almost everything it needs to know about your Phant stream: the server and the keys.

Posting data to Phant is a two step process: adding field/data combinations, and posting them to the server. To pair your fields with data, call the phant.add(<field>, <value>) function where <field> is a String matching exactly one of the fields in your stream. The <value> part of the Phant post can be almost any data type: in our example we’re adding float’s, ints, and a String.

The entire Phant post takes place in our int postToPhant(void) function:

language:c
int postToPhant(void)
{
    phant.add("humidity", rht.humidity(), 1); // These first three phant.add's pair a field with a float
    phant.add("tempc", rht.tempC(), 1); // The third param - optional for floats - sets the precision
    phant.add("tempf", rht.tempF(), 1); // (the number of decimal points after the number).
    phant.add("light", analogRead(LIGHT_PIN)); // We can also add an integer
    phant.add("station", stationName); // Or add a String is perfectly valid too!

    return phant.particlePost(); // Post our values to the Phant server
}

After you’ve commited an add for every field in your Phant stream, call phant.particlePost() to send them out to the server. This function takes care of the TCP connection, and sends out a valid HTTP POST to the Phant server. When it’s done, it’ll either return a 1 on success, or yield a negative number if it fails.

Troubleshooting

If you’re not seeing any data from your Photon being posted to the stream, it may be failing on the phant.particlePost() function. There are a few reasons the Phant POST might fail:

  • No Internet connection – make sure your Photon is pulsing cyan.
  • Invalid POST – make sure you have a phant.add for every field in your stream. Also make sure there aren’t any typos in the field names.
  • Stream overload – Phant streams on data.sparkfun.com are limited to 300 posts every 15 minutes. Our shared stream is subject to that same limitation. Consider making a stream of your own. It’s free!

We still use serial in this example, but really only for debugging. If you’re still not having any luck, open up your serial port and see what it has to say.

Experiment 7: Automatic Fish Feeder

Introduction

In this experiment, you’ll learn how to control the physical world with digital world using a servo motor. Having the ability to control devices such as motors, relays, actuators, and other moving objects opens up a world of opportunities. For this particular example, you will learn how to control a servo motor with the Photon RedBoard. Once you learn the basics of controlling a servo motor, you’ll automate the task of feeding fish or other small pets by creating an internet-connected auto-feeder.

Parts Needed

You will need the following parts:

  • 1x Servo Motor with bag of Motor Mounts and Screws
  • 1x Button
  • 5x Jumper Wire
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
Servo - Generic (Sub-Micro Size)

ROB-09065
$8.95
6
Tactile Button Assortment

COM-10302
$4.95
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95

Along with the parts mentioned above, you will also need a bottle cap or something similar (not included with the Inventor’s Kit) to build your fish feeder. The cap from a water or soda bottle will work best.

motor mounts

The various motor mounts included with your servo motor.

Tools Needed

You will need the screwdriver included in the Photon SIK. If screwing the bottle cap to a servo motor mount proves to be too difficult, you can substitute hot glue or other adhesives to attach the cap the a motor mount.

Suggested Reading

Before continuing on with this experiment, we recommend you be familiar with the concepts in the following tutorials:

  • Pulse-width Modulation (PWM) - Pulse-width modulation is the driving force behind how servo motors work and maintain their precision.
  • Servo Motor Background - This portion of our Servo Trigger Hookup Guide has a lot of good background information on the theory behind servo motor operation.
  • Particle Servo Library - Particle built the servo library into their default functions, so you don’t have to download any extra libraries to use servo motors! Learn about all the functions you can use with Servo.
  • Particle Time Library - We will also be using the built-in Particle Time Library to tell our automatic feeder to dispense food at a specific time.
  • Particle Cloud Functions - Some of these will be used in the second half of this experiment to expose variables and functions for us to manipulate them over the web.

Hardware Hookup

Hook up your circuit as pictured below:

Circuit #7

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

You can plug the jumper wire ends directly into the servo motor header.

The button will only be used for a brief portion of this experiment. After it has served its use, you can leave out the breadboard entirely. Should you wish to leave your fish feeder intact after you’ve completed all the other experiments, you can hook up the servo to just the Photon RedBoard to make the project more compact.

Photon Code Part 1a

For this experiment, we will need a few different pieces of code. The very first sketch you’ll need to upload will put the servo motor in a known state. When first unpacked, it’s difficult to know what position the servo motor is in, so this sketch moves the motor all the way to the end of its 180° range of motion. Copy and paste the following code into the Particle IDE, and upload it to the Photon RedBoard.

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 7 - Part 1a: Servo Motor
    This sketch was written by SparkFun Electronics
    Joel Bartlett <joel@sparkfun.com>
    August 31, 2015
    https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments

    This application controls a servo with the press of a button.
    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/
Servo myservo;// create servo object using the built-in Particle Servo Library

int button = D1;    //declare variable for button
int servoPin = D0;  //declare variable for servo
int pos = 0;        //variable to keep track of the servo's position
bool flag = 1;      //variable to keep track of the button presses

// This routine runs only once upon reset
void setup()
{
  Serial.begin(9600);//Start the Serial port @ 9600 baud

  pinMode(button, INPUT_PULLUP);   // sets button pin as input with internal pullup resistor


  myservo.attach(servoPin);  //Initialize the servo attached to pin D0
  myservo.write(180);        //set servo to furthest position
  delay(500);                //delay to give the servo time to move to its position
  myservo.detach();          //detach the servo to prevent it from jittering

}

// This routine loops forever
void loop()
{
    if(digitalRead(button) == LOW) //if a button press has been detected...
    {
      //This is known a s state machine.
      //It will move the servo to the opposite end from where it's set currently
      if(flag == 1)
        pos = 0;
      if(flag == 0)
        pos = 180;

      myservo.attach(servoPin);
      myservo.write(pos);
      delay(500);           //debounce and give servo time to move
      myservo.detach();

      flag = !flag;         //set flag to the opposite of what it's currently set to
      Serial.println(pos);  //prints to the serial port to keep track of the position
    }

}

What You Should See Part 1a

When you first power your Photon RedBoard, give it a few seconds to connect to the web, then you should see the motor move to the 180° position. Once there, it will stay until the button is pressed. Press the button to move it to 0°. Press the button again and it will move back to the first position. The purpose of this code it to give you an idea for the full range of motion for the servo motor and to help you plan out your fish feeder.

circuit 7

Assemble the Fish Feeder

With the above sketch still loaded on your RedBoard, it’s time to build the actual feeding mechanism. Take your bottle cap, and screw (or glue) it onto a motor mount of your choosing. We opted for the single arm in this setup.

bottle cap motor mount

Next, power your RedBoard with the Micro USB cable, if it isn’t already, and open up your favorite Serial Terminal program at 9600 baud. You should now see the position of the servo printed to the screen when the button is pressed.

0
180
0
180
0
180

Hold you servo so that the moving portion is facing you and is on the right-hand side of the motor. Now it’s time to attach the mount to the servo so that the cap dumps your food into the tank. This may take a few tries to get it exactly right.

Move the servo in the 180° position, using the button. Attach the mount by pressing it onto the servo gear until it is snug. We found that positioning the cap at a 45° angle works well as it holds the food and provides a good angle for dumping the food. If you would like to secure your mount with the provided screw, now would be the best time. You may need to screw then unscrew the cap (making holes in it), attach the mount to the servo, screw the mount, then screw the cap back on to the mount.

hold food

With the feeding mechanism attached, press the button again to move into the dumping position (0°). This is your chance to make any adjustments necessary to ensure all of the food gets dumped out.

dump food

If you attached your cap to the motor mount opposite of the image above, you may have to reverse these steps, starting at position 0° and then dumping at 180°.

Photon Code Part 1b

Now, the code will trigger the servo motor at a specific time instead of on a button press. You will need to change the Timezone to your local timezone. You will also need to select an hour and minute at which you’d like your feeder to activate. Copy, paste, and upload the code below.

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 7 - Part 1b: Servo Motor with Time
    This sketch was written by SparkFun Electronics
    Joel Bartlett <joel@sparkfun.com>
    August 31, 2015
    https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments

    This application controls a servo at a specific time.
    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/
Servo myservo;// create servo object using the built-in Particle Servo Library

int servoPin = D0;  //declare variable for servo

void setup()
{

  myservo.attach(servoPin);  //Initialize the servo attached to pin D0
  myservo.write(180);        //set servo to 180. This position will hold the food
  delay(500);                //delay to give the servo time to move to its position
  myservo.detach();          //detach the servo to prevent it from jittering

  Time.zone(-6);//Set timezone to Mountain Daylight Time (MDT) Spring/Summer
  //Time.zone(-7);//Set timezone to Mountain Standard Time (MST) Fall/Winter
  //Find out your time zone here: http://www.timeanddate.com/time/map/

  Spark.syncTime();//sync with the Particle Cloud's time

}

void loop()
{
  //This if statement checks to see if it is the correct hour and minute of the day to dispense food.
  //The Photon uses 24 hour time so there's no confusion between 1am and 1pm, etc.
  if(Time.hour() == 15 && Time.minute() == 0)  //feed at 3:00pm
  {
    myservo.attach(servoPin);
    myservo.write(0);//set to a zero position. Dumps food
    delay(2000);
    myservo.write(180);//set to a zero position
    delay(500);
    myservo.detach();//detach to keep the servo from jittering

    delay(60000);//wait the rest of the minute out
  }

}

What You Should See Part 1b

Depending on what time you chose in your code, you should see your servo motor turn to the food dumping position at that time and then return to its original position. Before implementing, you should test your code out by entering a time in the not-so-distant future, so you can see the servo move and verify it is working. Once, verified, program the actual feeding time in and integrate it into your fish tank or other habitat.

This code will only get you through one day of feeding. If you will be gone for say three days and want to feed on the second day, you can use the Time.month() and Time.day() functions to check for a specific time on a specific day of the month to feed. Learn more about all of the time functions at Particle.io.

Code to Note

To prepare the Photon RedBoard to control a servo, you must first create a Servo “object” for each servo (here we’ve named it “myservo”), and then “attach” it to a digital pin (here we’re using digital pin D0). This is creating an instance of the Servo class, which is part of the built-in Servo Library.

Servo myservo;

To use the servo after it’s been declared, you must attach it. This tells the Photon to which pin this servo is connected. The detach function relinquishes that pin and stops all signals from being sent to the servo.

myservo.attach(D0);
myservo.detach();

The servo in this kit doesn’t spin all the way around, but they can be commanded to move to a specific position. We use the servo library’s write() command to move a servo to a specified number of degrees(0 to 180). Remember that the servo requires time to move, so give it a short delay() if necessary.

servo.write(180);
delay(500);

The Time library is also built in to the Photon firmware. However, you may have noticed that it does not need to be initialized before it can be used. You can call any of the Time functions at any point in your code. For example, we call Time.zone(-6) without any reference to Time before that.

Troubleshooting

  • Servo not moving at all - Make sure you have the correct pins in the correct place. Most all servo are color-coded with Red being Power, Black being GND, and White or Yellow being the Signal.

  • Servo twitching and jittering a lot - If you do not detach your servo after every call, it may twitch and jitter a lot. If this is a problem, add myservo.detach() after every myservo.write() function. Don’t forget to re-attach it before writing it again.

  • These servos can only withstand a small amount of abuse. If held in a position it doesn’t like for very long, the servo can be permanently damaged. If all the connections are correct, you’re detaching after every servo write, and the servo is still misbehaving, you may have damaged your servo. If your servo arrived in this state, contact our customer service team, and they’ll help you get a replacement.

Part 2: Feed Your Pets from the Internet

Thus far, we’ve only used the web to sync the time on our Photon Redboard. Accurate time is great, but let’s see if we can’t use the Internet more to our advantage. In part 2 of this experiment, you’ll learn how to trigger your auto fish feeder from anywhere in the world as long as it and you have an Internet connection.

New Photon Code

Your circuit will be the same as it was in Part 1, but the code will be slightly different. We’ve added a new line, Spark.function("feed",triggerFeed); and the corresponding function triggerFeed(), explained in the comments below. Copy, paste, and upload the code.

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 7 - Part 2: Automatic Fish Feeder
    This sketch was written by SparkFun Electronics
    Joel Bartlett <joel@sparkfun.com>
    August 31, 2015
    https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments

    This application controls a servo at a specific time and over the web.
    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/
Serv
Servo myservo;// create servo object using the built-in Particle Servo Library

int servoPin = D0;  //declare variable for servo

int myMinute = 0;  //00
int myHour = 9; //9 am
int myDay = 14;
int myMonth = 8;//August


void setup()
{

  myservo.attach(servoPin);  //Initialize the servo attached to pin D0
  myservo.write(180);        //set servo to 180. This position will hold the food
  delay(500);                //delay to give the servo time to move to its position
  myservo.detach();          //detach the servo to prevent it from jittering

  Time.zone(-6);//Set timezone to Mountain Daylight Time (MDT) Spring/Summer
  //Time.zone(-7);//Set timezone to Mountain Standard Time (MST) Fall/Winter
  //Find out your time zone here: http://www.timeanddate.com/time/map/

  Spark.syncTime();//sync with the Particle Cloud's time

  // We are also going to declare a Spark.function so that we can trigger our feeder from the cloud.
  Spark.function("feed",triggerFeed);
  // When we ask the cloud for the function "feed", it will employ the function triggerFeed() from this app.

}

void loop()
{
  //The Photon uses 24 hour time so there's no confusion between 1am and 1pm, etc.
  //This time we are checking for a specific date and time.
  if(Time.month() == myMonth && Time.day() == myDay) //check the date, if it's not the right day, don't bother with the time.
  {
      if(Time.hour() == myHour && Time.minute() == myMinute) //check the time
      {
        myservo.attach(servoPin);
        myservo.write(0);//set to a zero position. Dumps food
        delay(2000);
        myservo.write(180);//set to a zero position
        delay(500);
        myservo.detach();//detach to keep the servo from jittering

        delay(60000);//wait the rest of the minute out
      }
  }

}


int triggerFeed(String command)
{
/* Spark.functions always take a string as an argument and return an integer.
Since we can pass a string, it means that we can give the program commands on how the function should be used.

In this case, telling the function "feed" will trigger the servo.
Then, the function returns a value to us to let us know what happened.
In this case, it will return 1 if the function was called and -1 if we
received a totally bogus command that didn't do anything.
This does not check for any mechanical failures however. Only code.
*/

    if (command=="feed")
    {
        myservo.attach(servoPin);
        myservo.write(0);//set to a zero position. Dumps food
        delay(2000);
        myservo.write(180);//holding position
        delay(500);
        myservo.detach();
        return 1;
    }
    else
        return -1;
}

What You Should See

When you register a function or variable in the cloud, you’re making a space for it on the internet. There’s a specific address that identifies you and your device. You can send requests, like GET and POST requests, to this URL just like you would with any webpage in a browser.

The code below will give you a webpage from which you can trigger the auto-feeder. Copy and paste the code below into your favorite text editor that can save files as .html.

language:html<!-- Replace your-device-ID-goes-here with your actual device ID
and replace your-access-token-goes-here with your actual access token--><!DOCTYPE><html><body><center><br>
    Feed your pets anywhere you have an Internet connection!<br><br><form name= "input" action="https://api.particle.io/v1/devices/your-device-ID-goes-here/led?access_token=your-access-token-goes-here" method="post"><input type= "submit" name="%" value= "feed" style="height:50px; width:150px"></form></center></body></html>

Edit the code in your text file so that “your-device-ID-goes-here” is your actual device ID, and “your-access-token-goes-here” is your actual access token. These things are accessible through your IDE at build.particle.io. Your device ID can be found in your Devices drawer (the crosshairs) when you click on the device you want to use, and your access token can be found in Settings (the cogwheel).

Open that .html file in a browser. You’ll see a very simple HTML form that allows you to call the triggerFeed functions we exposed earlier.

Feed Me

You’ll get some info back after you submit the page that gives the status of your device and lets you know that it was indeed able to post to the URL. If you want to go back, just click “back” on your browser.

Similarly, you can also call this function by opening a Command Line Terminal and typing

particle call device_name feed feed

Remember to replace device_name with either your device ID or the casual nickname you made for your device when you set it up.

Now, if you need to trigger your autofeeder ahead of schedule for any reason, you can do so with the click of a button on your smartphone or computer.

Code to Note

The Spark.function line is allowing us to “expose” that function to the rest of the Internet. This allows us to trigger the feeder from other mediums such as a webpage or the command line. You can learn more about the Particle Cloud Functions here.

Troubleshooting

  • If you cannot get your servo to move via the web or command line interface, you may be calling the wrong device ID. If you have more than one Photon, Photon RedBoard or Core, then it is possible that the device IDs may have been mixed up at some point.
  • Another problem could be your access token. If you get an error saying the access token is expired, visit the Particle IDE at build.particle.io and refresh your access token.

Going Further

Need to feed your fish for multiple days? Add a second servo, or grab a continuous rotation servo to create a feeding mechanism that only dispenses a small portion of its food reserves at a time.

You could then try to create a webpage app that allows you to change the feeding time on the fly without having to reprogram your Photon RedBoard.

Experiment 8: Activity Tracker

Introduction

This experiment will get you (or at least your Photon) moving, because we’ll be wiring up a motion-sensing, orientation-finding, 3-axis accelerometer. This experiment serves as an introduction to accelerometers and I2C communication. You’ll also learn about system modes, which allow you to turn WiFi on or off and save loads of battery juice.

Activity Tracker Photo

Parts Needed

  • 1x MMA8452Q Accelerometer
  • 1x Green Button
  • 6x Jumper wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
SparkFun Triple Axis Accelerometer Breakout - MMA8452Q

SEN-12756
$9.95
2
Tactile Button Assortment

COM-10302
$4.95
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95

Suggested Reading

Two big new electronics concepts are introduced in this experiment: Accelerometers and I2C Communication. If you’re interested in learning a lot more about them, check out our tutorials. For now, here’s a quick introduction:

Accelerometers

Accelerometers are movement sensors. They feel acceleration – the rate of change of velocity (how fast something is speeding up or slowing down). They’ve become incredibly common throughout consumer electronics, with a huge range of applications: smartphone’s use them to sense orientation, activity trackers often track steps using an accelerometer, and hard drives use them to sense free-fall (giving them enough time to move and protect delicate parts).

Even if a device isn’t visibly moving, an accelerometer can still give you a lot of information about its orientation. Accelerometers sense the acceleration of gravity, which is a constant, pulling force toward earth. In fact, one of the most common units of acceleration is the g– “gravity” – which is equal to about 9.8 m/s2. An accelerometer sitting flat and motionless, will sense 1g of acceleration towards the ground (usually on the z-axis), and 0g of acceleration in the other two dimensions (x and y).

MMA8452Q axis orientations

There are a huge variety of accelerometers out there. They can monitor anywhere from 1-to-3 axes of acceleration, support various communication interfaces, and host a range of other unique features. In this experiment, we’re using the MMA8452Q– a 3-axis accelerometer with a digital interface. It can be set to sense a maximum of either ±2, 4, or 6 g. It also supports a neat feature called tap, or “pulse” detection.

I2C

The accelerometer we’re using in this experiment communicates over an interface called I2C, short for Inter ICCommunication. I2C is an extremely popular embedded computing communication interface. It’s relatively slow (the Photon and accelerometer communicate at about 400kHz), but only requires two wires for communication. These wires are usually called serial data (SDA) and serial clock (SCL). As you’ll see in the next section, hooking up I2C circuits is as easy as connecting SDA-to-SDA and SCL-to-SCL (don’t forget to power the device too!).

Devices on an I2C bus are called “slaves”, while the lone, controlling component (our Photon RedBoard in this case), is called the “master”. What makes I2C even more powerful is it allows multiple slave devices on a single, two-wire bus. Want to add a pressure sensor to your circuit? Just connect the SDA’s and SCL’s. Each slave device on an I2C bus has a unique address, so they can ignore messages for other devices, and only take action on bytes meant for them.

Hardware Hookup

Here’s the hookup for both parts of this experiment:

Activity Track fritzing hookup

As we mentioned in the I2C section above, an I2C bus is made up of two signals: SDA and SCL. An I2C interface also requires pullup-resistors on those signals, but the breakout board already takes care of that for you.

The button signal is routed to the RedBoard’s D3 pin. We’ll internally pull that pin up, so when the button is inactive it’ll read HIGH. When the button is pressed, D3 will go LOW. We won’t use the button in the first part, but it’ll come in handy later on.

Photon Code

In this experiment, we’ll once again be using the Libraries feature of the Build IDE. To communicate with the accelerometer, we’ll be using the SparkFunMMA8452Q library.

This time, however, we’ll be using another feature of the Libraries tab: examples. Most libraries include at least one example, to help demonstrate their features and usage. To use the SparkFunMMA8452Q library’s example, click USE THIS EXAMPLE (make sure the MMA8452Q-Serial_Example tab is active up top).

Using a library example

The Build IDE will create a new app in your code tab called MMA8452Q-Serial_Example, and it will already have the library included for you.

All you have to do is flash the code!

What You Should See

This part of the experiment is designed to get you familiar with what, exactly, an accelerometer senses. We’ll be reading the acceleration from each of the three axes, then printing those values out to the serial monitor.

Once the Photon begins running the application, open up a serial terminal to view the data. Acceleration sensed on all three axes is displayed at a rate of about 1Hz. There are also some fancy ASCII bar graphs, to visually represent the acceleration values.

Accelerometer activity printed to serial monitor

Sitting flat, you should see about 1 g of acceleration on the z-axis, and nearly 0 g on the x- and y-axes. Carefully – without disturbing any of the wires – tilt your RedBoard and Breadboard around, and monitor the three accelerometer readings. If you flip the board 90°, the z-axis should become 0 g and either x or y will go to 1. Can you tilt the board and get each axis nearly equal?

Code to Note

This example introduces the SparkFunMMA8452Q library. To begin using the library, create an MMA8452Q class object. This’ll often go in the global section of the code:

language:c
// Create an MMA8452Q object, used throughout the rest of the sketch.
MMA8452Q accel; // Default constructor, SA0 pin is HIGH

We’ll use accel from here on to access the accelerometer functions and variables. To initialize the sensor, stick accel.begin() in your setup(). You can give the begin() function two parameters, if you want to configure the sensing range of the accelerometer or the output data rate (ODR).

language:c
// Initialize the accelerometer with begin():
// begin can take two parameters: full-scale range, and output data rate (ODR).
// Full-scale range can be: SCALE_2G, SCALE_4G, or SCALE_8G (2, 4, or 8g)
// ODR can be: ODR_800, ODR_400, ODR_200, ODR_100, ODR_50, ODR_12, ODR_6 or ODR_1
accel.begin(SCALE_2G, ODR_1); // Set up accel with +/-2g range, and slowest (1Hz) ODR

The scale can be set to either ±2, 4, or 8 g, while the output data rate can be set to either 1, 6, 12, 50, 100, 200, 400, or 800 Hz. In our example, the scale is set to its minimum – meaning it’ll have the highest resolution, but be limited to sensing a maximum of ±2 g.

To check if new data is available from the sensor, use the accel.available() function, which will return 1 if there’s data to be read or 0 otherwise.

Once new data is available, call accel.read() to read all acceleration data from the sensor. Then you’ll be able to access any of six class variables: x, y, z– the “raw” 12-bit values from the accelerometer – or cx, cy, and cz, the calculated accelerations in g.

The whole process goes something like this:

language:c
if (accel.available())
{
    accel.read();

    Serial.println("X: " + String(accel.x) + " | " + String(accel.cx, 2) + " g");
    Serial.println("Y: " + String(accel.y) + " | " + String(accel.cy, 2) + " g");
    Serial.println("Z: " + String(accel.z) + " | " + String(accel.cz, 2) + " g");
}

Troubleshooting

Motion and breadboard/jumper circuits aren’t usually a great combination. If your circuit works initially, but mysteriously stops working, a jumper may have been (even briefly) disconnected. Double-check all of your wiring, and restart if anything stops working. Move things around carefully! The base plate is extremely useful for this experiment.

Part 2: Tracking Steps, Publishing Your Acitvity

Accelerometers are commonly used as the motion-sensing foundation of pedometers – step counters. And while the baseplate probably isn’t the most comfortable thing to strap to your belt and walk around with, creating an activity monitor of our own can be a fun exercise. Plus it provides an algorithm you can endlessly try to tweak and perfect.

New Photon Code

Create a new application, and post this code in:

// Pin definitions:
const int BUTTON_PIN = 3; // The Publish button is connected to D3
const int LED_PIN = 7; // The on-board LED is used to indicate status

MMA8452Q accel; // Create an accelerometer object to be used throught the sketch

// stepCount keeps track of the number of steps since the last publish:
unsigned int stepCount = 0;

// To save battery power, we'll put the Photon in SEMI_AUTOMATIC mode.
// So our Photon will not attempt to connect to the Cloud automatically.
// It'll be up to us to issue Particle.connect() commands, when we want
// to connect.
// More on modes here: https://docs.particle.io/reference/firmware/photon/#system-modes
SYSTEM_MODE(SEMI_AUTOMATIC); // Set system mode to SEMI_AUTOMATIC

// We'll use a few booleans to keep track of _if_ we need to publish,
// and if our publish was successful.
bool publishFlag = false;
bool publishSuccess = false;

void setup()
{
    // Begin by turning WiFi off - we're running off batteries, so
    // need to save as much power as possible.
    WiFi.off(); // Turn WiFi off

    // Configure our button and LED pins
    pinMode(LED_PIN, OUTPUT); // LED pin is set as an OUTPUT
    digitalWrite(LED_PIN, HIGH); // Write LOW to set LED off to begin
    pinMode(BUTTON_PIN, INPUT_PULLUP); // Button is configured as input w/ pull-up active

    // Initialize our accelerometer. Set the scale to high-resolution (2g)
    // Set the output data rate to 50Hz.
    accel.begin(SCALE_2G, ODR_50);

    // Next, we'll configure our accelerometer's tap detection interface. This is a
    // very tweak-able function. We can configure the Threshold, time limit, and latency:

    // Pulse threshold: the threshold which is used by the system to detect
    // the start and end of a pulse.
    // Threshold can range from 1-127, with steps of 0.063g/bit.
    //byte threshold = 3; // 3 * 0.063g = 0.189g // This might work better in some cases
    byte threshold = 1; // 2 * 0.063g = 0.063g

    // Pulse time limit: Maximum time interval that can elapse between the start of
    // the acceleration exceeding the threshold, and the end, when acceleration goes
    // below the threshold to be considered a valid pulse.
    byte pulseTimeLimit = 255; // 0.625 * 255 = 159ms (max)

    // Pulse latency: the time interval that starts after first pulse detection, during
    // which all other pulses are ignored. (Debounces the pulses).
    // @50Hz: Each bit adds 10ms (max: 2.56s)
    byte pulseLatency = 64; // 1.25 * 64 = 640ms

    // Use the setupTap function to configure tap detection in our accelerometer:
    accel.setupTap(threshold, threshold, threshold, pulseTimeLimit, pulseLatency);
}

void loop()
{
    // Use the readTap() function to check if a tap was detected:
    byte tap = accel.readTap();
    // readTap will return 1 if there was a tap
    if (tap != 0) // If there was a tap
    {
        stepCount++; // Increment stepCount
        toggleLED(); // Toggle the LED state
    }

    // Next, check if the button was pressed (it's active-low)
    if (digitalRead(BUTTON_PIN) == LOW)
    {   // If the button was pressed
        if (publishFlag == false) // and if we aren't publishing:
        {
            // We're in SEMI_AUTOMATIC system mode, so it's up to us
            // to connecto to the Particle cloud.
            // We've also turned WiFi off, so we'll have to connect to that too.
            WiFi.on();  // Turn WiFi on
            WiFi.connect(); // Connect to our WiFi network
            Particle.connect(); // Connect to the Particle cloud

            digitalWrite(LED_PIN, LOW); // Turn the LED off
            publishFlag = true; // Indicate that we are publishing
        }
    }

    // It takes a few seconds to connect to the Particle cloud.
    // We'll use Particle.connected() to check if we've connected
    // If we're connected, and need to publish:
    if (publishFlag && Particle.connected())
    {
        // Each step usually creates about 2 taps (arm or leg going forward then backward)
        stepCount /= 2; // Dividing by 2 usually makes for a much more accurate step count:

        // Call Particle.publish to push our step count to the web:
        publishSuccess = Particle.publish("Steps", String(stepCount / 2));

        // If the publish was successful
        if (publishSuccess)
        {
            publishFlag = false; // clear the publishFlag
            stepCount = 0; // and reset the step count
        }
        // Allow the Photon some time to Publish before disconnecting
        delay(5000); // Delay 5 seconds
    }

    // If we've successfully published, and are still connected to the Partice cloud
    if (publishSuccess && Particle.connected())
    {
        WiFi.off(); // Turn off WiFi
        publishSuccess = false; // Clear our publishSuccess flag
    }
}

// toggleLED() simply toggles the LED each time it's called. From on to off, or off to on.
void toggleLED()
{
    static bool ledState = true;
    if (ledState)
    {
        digitalWrite(LED_PIN, HIGH);
        ledState = false;
    }
    else
    {
        digitalWrite(LED_PIN, LOW);
        ledState = true;
    }
}

As with the previous part’s code, you’ll need to include the SparkFunMMA8452Q library in this application.

What You Should See

After flashing code to your Photon, remove the USB cable, and plug in a 9V battery via the included 9V-to-Barrel Jack adapter.

Before walking away, open up the Particle Dashboard, and click over to the “Logs” tab. It should be empty for now.

When your Photon boots up, it’ll immediately begin running the application code. You’ll also notice the RGB LED is blinking white-ish, which indicates the device is not connected to the Particle cloud.

Strap your circuit to your belt, or just hold it in your hand, and take about 50 steps. When you want to publish your step count, hit the green button. Your Photon will stop tracking steps while it connects to WiFi and the Particle Cloud, then posts the step count. When the activity publishes, you should see a new row-entry on the dashboard.

Particle Dashboard

After successfully publishing, the Photon RedBoard shuts of WiFi and goes back into step counting mode.

There are a variety of ways to check for published events from your Photon. In raw URL form, you can subscribe to events by directing your browser to a URL like:

https://api.particle.io/v1/devices/DEVICE_ID/events?access_token=ACCESS_TOKEN

Replacing DEVICE_ID with your Photon RedBoard’s device ID (found under the “Devices” tab), and swapping in your access token (in the “Settings” tab) for ACCESS_TOKEN. You’ll initially get a mostly blank page, but as your Photon connects and publishes, new events will pop up:

HTTP event subscription

Those events include your Photon RedBoard connecting, disconnecting, and publishing an event called “Steps”. If you listen for those events, and do some JSON-parsing, you can build a simple HTML/Javascript page to list your step counts.

As a simple example, copy and paste this code into a text editor. Grab your device ID and access token and plug them into the deviceID and accessToken variables (between the quotes).

language:html<!DOCTYPE HTML><html><body><p><p><h3><span id="subscription"></span></h3></p><p>Status: <span id="connectStatus">Nothing yet</span></p><p>Step Counts: <span id="stepList"></span></p><script type="text/javascript">
    function connect()
    {
        var deviceID = ""; // Photon RedBoard device ID
        var accessToken = ""; // Particle account access token

        // Subscribe to any events published by our Photon Device ID:
        var eventSource = new EventSource(
        "https://api.particle.io/v1/devices/" + deviceID + "/events/?access_token=" + accessToken);

        // When the connection opens, it'll publish an "open" event:
        eventSource.addEventListener('open', function(event)
        {
            var subSpan = document.getElementById("subscription");
            subSpan.innerHTML = "Connected. Listening for steps!";
        }, false);

        // If our eventSource fails to connect, it'll give an error:
        eventSource.addEventListener('error', function(event)
        {
            var subSpan = document.getElementById("subscription");
            subSpan.innerHTML = "Not connected :(";
        }, false);

        // Online/offline messages are published as "spark/status" events:
        eventSource.addEventListener('spark/status', function(event)
        {
            var jData = JSON.parse(event.data); // Parse the JSON data
            // Find the "data" key. It'll either be "offline" or "online":
            var statusData = jData.data;
            // Find the "publihsed_at" key, it'll be a timestamp:
            var statusTime = jData.published_at;

            // Put the data we've parsed into the "connectStatus" span:
            var statusSpan = document.getElementById("connectStatus");
            statusSpan.innerHTML = statusData + " (" + statusTime + ")";
        }, false);

        eventSource.addEventListener('Steps', function(event)
        {
            var jData = JSON.parse(event.data); // Parse the JSON data

            var stepCount = jData.data; // Find the "data" key, it'll be the step count
            var stepTime = jData.published_at; // Also get the timestamp from "published_at" key

            // Put the data we've parsed into the "stepList" span:
            var stepsSpan = document.getElementById("stepList");
            stepsSpan.innerHTML = stepsSpan.innerHTML + stepCount + " (" + stepTime + ")" + "<br>";

        }, false);
    }
    window.onload = connect; // Run the connect() function when the page loads
    </script></body></html>

Then open your HTML file with your favorite web browser. And publish some step counts. Eventually your HTML page will begin to fill out:

An HTML page to listen for published steps

System Modes, and Controlling WiFi

Photon System Modes are used to control the Photon RedBoard’s connection to WiFi and the Particle cloud. In this experiment we turn off WiFi whenever it’s not necessary – you don’t need an Internet connection to track steps!

The Photon's WiFi component is a relatively huge power-consumer (pulling, on average, about 50-60mA). That may not be a big deal when it's powered over USB, but when we're operating on battery power, every miliwatt (mW) saved means less money going toward replacing batteries. Turning off WiFi can more than double the amount of time our project can run off a battery.

Control of the Photon’s WiFi connection begins before setup(), by calling the SYSTEM_MODE macro:

language:c
// SEMI_AUTOMATIC mode starts the application up without WiFi, not connected to the
// Particle cloud. Call Particle.connect() manually to connect to WiFi, after that
// Particle.process()'s will be handled for us.
SYSTEM_MODE(SEMI_AUTOMATIC); // Semi-automatic WiFi/cloud mode

SYSTEM_MODE can either be AUTOMATIC, SEMI_AUTOMATIC or MANUAL. AUTOMATIC is the default state, which you should be extremely familiar with by now. The Photon boots up and immediately tries to connect to WiFi and the Cloud before running the sketch.

In SEMI_AUTOMATIC mode, our Photon jumps straight into our application code. It won’t try to connect to the cloud until we call Particle.connect(). While the device is connected, its connection with the cloud is automatically administered, and Particle.disconnect() can be called to disconnect from the Particle cloud.

In addition to the SYSTEM_MODE control, the application also manages our WiFi connection with WiFi.on(), WiFi.connect(), and WiFi.off(). For example, after the button has been pressed, we tell the Photon RedBoard to connect to WiFi and the Particle cloud like this:

language:c
WiFi.on(); // Turn the WiFi module on
WiFi.connect(); // Connect to the pre-set WiFi SSID
Particle.connect(); // Connect to the Particle Cloud

And once we’ve successfully published, WiFi.off() is called to shut the WiFi system down.

It takes some extra planning to use anything but AUTOMATIC mode, but it can result in a big payoff.

Sensing Steps

Before we can publish any step count, we have to sense them. To be honest, pedometer algorithm’s are tough. The MMA8452Q has a neat feature called pulse detection, that we can pigeonhole into our step-counting application. We can set the accelerometer to continuously monitor all three axes for short pulses of motion on any of the three axes, which will be assumed as a step.

To setup pulse or “tap” detection on the MMA8452Q, use the setupTap() function – giving it threshold and timing parameters. We’ll set the threshold to be very low – so just about any movement will create a tap – and set the time window between pulses to about 500ms.

language:c
// Threshold can range from 1-127, with steps of 0.063g/bit.
byte threshold = 1; // 2 * 0.063g = 0.063g (minimum threshold

// Pulse time limit: Maximum time interval that can elapse between the start of
// the acceleration exceeding the threshold, and the end, when acceleration goes
// below the threshold to be considered a valid pulse.
byte pulseTimeLimit = 255; // 0.625 * 255 = 159ms (max)

// Pulse latency: the time interval that starts after first pulse detection, during
// which all other pulses are ignored. (Debounces the pulses).
// @50Hz: Each bit adds 10ms (max: 2.56s)
byte pulseLatency = 64; // 1.25 * 64 = 640ms
accel.setupTap(threshold, threshold, threshold, pulseTimeLimit, pulseLatency);

How fast do you walk? Everyone has a different gait, so you may have to tweak these values to get a more accurate step count.

Code to Note

Particle Publish will be our tool for sharing our step activity with the world. Publish can be used to post named event data to the Particle Cloud, where it can be grabbed by another application and displayed or used otherwise.

In this example, we’re publishing our step data under the “Steps” event name, and including the step count under that data. Just a few lines of code are required to publish, and verify a successful publish:

language:c
// Call Particle.publish to push our step count to the web:
publishSuccess = Particle.publish("Steps", String(stepCount / 2));

// If the publish was successful
if (publishSuccess)
{
    publishFlag = false; // clear the publishFlag
    stepCount = 0; // and reset the step count
}

The Particle Dashboard is an easy tool for viewing your device’s published data. In leiu of anything more complex, we can use that interface to easily monitor the number of steps our RedBoard has taken.

Beyond the Dashboard, once you’ve published your data to the Cloud, there should be no shortage of actions you can take with it. You can set up a webhook, to monitor the event and post its data. Or even configure a second Photon to Subscribe to the event, to track multiple Photon activity monitors.

Troubleshooting

Saving power is great, but it can lead to some headaches if you need to upload new code to the Photon. If your Photon’s WiFi is off, or if it’s not connected to the cloud, you won’t be able to flash new code to it. Thankfully, there’s Safe Mode. By booting your Photon into safe mode, it skips running your application and instead connects to the Particle Cloud and waits for a new program.

To boot into safe mode, hold down both the RESET and MODE buttons. Then release RESET to turn the Photon on. When the RGB LED begins blinking pink-ish, release MODE. Your Photon will run through its WiFi/Cloud connection process, then breathe pink. Once it’s breathing pink, you’ll be able to successfully flash new code to it.

Going Further

There are plenty of projects that can combine motion sensing and the Internet. How about putting this same circuit on top of your dryer– have it alert you when the shaking stops, so you can go grab your clothes before they wrinkle (escaping any potential scolding from your significant other)!

Experiment 9: Home Security

Introduction

Home security and automation are hot topics for IoT (Internet of Things). In this experiment, you will learn how to detect if someone is entering a room. There are a lot of different ways you can detect motion in your home. For this kit, we included both the PIR motion sensor and the Magnetic Door Switch Set. PIR motion sensor is great for when you need to detect motion farther away within a room. The Magnetic Door Switch Set is a small reed switch assembly specifically designed to alert you when doors, drawers, or any other aperture opens.

We are going to show you two different circuits. One for the PIR motion sensor and the other for the Magnetic Door Switch. The code and hook-up for both circuits are fairly similar, so we decided to put both of them in one big experiment about home security.

If you want to have an alarm, add piezo speaker to the experiments below. For the sake of your ears, we are using the onboard LED (D7) to show when motion is detected instead of the piezo speaker. You will thank us later.

Alright, time to learn how to catch anyone who steals cookies out of your cookie jar!

PIR Motion Sensor

PIR (passive infrared) sensor is a simple to use motion sensor. We are going to power it up and wait 1-2 seconds for the sensor to get a snapshot of the still room. If anything moves after that period, the ‘alarm’ pin will go low.

Parts Needed

You will need the following parts:

  • Photon RedBoard, Breadboard, and Base Plate
  • 1x PIR Sensor
  • 1x JST Right Angle Connector - Through-Hole 3-Pin
  • 3x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
PIR Motion Sensor (JST)

SEN-13285
$9.95
2
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95
JST Right Angle Connector - Through-Hole 3-Pin

PRT-09750
$0.95

Suggested Reading

  • Pull-up Resistors - Pull-up resistors are very common when using microcontrollers (MCUs) or any digital logic device. This tutorial will explain when and where to use pull-up resistors, then we will do a simple calculation to show why pull-ups are important.

PIR Motion Sensor Hardware Hookup

To make it easier to breadboard the PIR motion sensor, you will want to add the JST Right Angle Connector - Through-Hole 3-Pin.

Connecting the JST connector to the PIR Sensor cable

Make sure to push the PIR motion sensor’s JST female connector into the JST right angle connector as much as you can. After you do that, now you are ready to breadboard.

PIR RedBoard Home Security

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

Photon Code

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 9 - Part 1: Home Security with PIR
    This sketch was written by SparkFun Electronics
    August 31, 2015
    https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments

    This application uses a PIR motion sensor to detect motion
    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License (http://opensource.org/licenses/MIT)
*/

int pirPin = D0; // PIR is connected to D3
int led = D7;    // Onboard LED is D7

void setup()
{
  pinMode(pirPin, INPUT_PULLUP); // Initialize D3 pin as input with an internal pull-up resistor
  pinMode(led, OUTPUT);          // Initialize D7 pin as output
}


void loop()
{
  int pirValState;

  pirValState = digitalRead(pirPin);

  if(pirValState == LOW){      // Was motion detected
    digitalWrite(led, HIGH);   // Turn ON the LED
    delay(2000); // Wait 1-2 seconds for the sensor to get a snapshot of the still room
  }
  else
  {
    digitalWrite(led, LOW);  // Turn the LED off
  }

}

What You Should See

When you wave your hand over the PIR motion sensor, the onboard LED (D7) will light up!

Get creative and add a piezo speaker! You can be far away and hear an alarm go off if someone sneaks in! Maybe useful in a zombie apocalypse to give you enough time to escape. Might attract attention of more zombies too. We haven’t decided yet.

Piezo speaker and PIR motion sensor hookup Photon RedBoard

Code to Note

if(pirValState == LOW){ // Was motion detected

When motion is detected, the ‘alarm’ pin will go low.

Troubleshooting

  • If nothing happens, double check your connections. It is easy to swap the jumper wires for the PIR motion sensor. The white wire on the PIR motion sensor is connected to the GND. Lots of time when we see a black wire, we think it automatically is GND. It is always important to check the datasheets and hook-up guides. Wire colors varies from different manufacturers and don’t always follow the same standards.

Magnetic Door Switch Set

These types of switches are primarily used in home security systems. One half of the assembly set on a window or door frame and the other attached to the window or door itself. When the switch set is separated from each other the contact is broken and triggers an alarm. You can use this set in non-security applications too. For example to trigger a music box to play a song when you open the box.

When each piece comes within 20mm of each other they complete the circuit with the internal reed switches.

Parts Needed

You will need the following parts:

  • Photon RedBoard, Breadboard, and Base Plate
  • 1x Magnetic Door Switch Set
  • 2x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95
Magnetic Door Switch Set

COM-13247
$2.95

Suggested Reading

  • Pull-up Resistors - Pull-up resistors are very common when using microcontrollers (MCUs) or any digital logic device. This tutorial will explain when and where to use pull-up resistors, then we will do a simple calculation to show why pull-ups are important.
  • Switch Basics - Reed switches open or close when exposed to the presence of a magnetic field. Learn more about different types of switches with this tutorial.

Magnetic Door Switch Set Hookup

You have two parts of the Magnetic Door Switch Set. Set off to the side, the part that does not have the wires. Connect the part that has the wires coming out to the breadboard. It doesn’t matter which wire goes to GND pin or a digital pin. They are interchangeable.

Home Secruity Reed Switch Photon RedBoard

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

Photon Code

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 9 - Part 1: Home Security with Magnetic Door Switch Set
    This sketch was written by SparkFun Electronics
    August 31, 2015
    https://github.com/sparkfun/Inventors_Kit_For_Photon_Experiments

    This application uses a Magnetic Door Switch Set to detect motion
    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License (http://opensource.org/licenses/MIT)
*/

int magnetPin = D3; // magnet part is connected to D3
int led = D7;     // Onboard LED is D7

void setup()
{
  pinMode(magnetPin, INPUT_PULLUP); // Initialize D3 pin as input with an internal pull-up resistor
  pinMode(led, OUTPUT);           // Initialize D7 pin as output
}


void loop()
{
  int magnetValState;

  magnetValState = digitalRead(magnetPin);

  if(magnetValState == HIGH){      // Was motion detected
    digitalWrite(led, HIGH);     // Turn ON the LED
  }
  else
  {
    digitalWrite(led, LOW);  // Turn the LED off
  }

}

What You Should See

Move the two parts next to each other. The onboard LED (D7) will be off. If you move the parts away from each other the onboard LED will come on.

You can add a piezo speaker as an alarm or play fun songs when something opens.

Reed Switch with Photon RedBoard hookup

Code to Note

if(magnetValState == HIGH){ // Was motion detected

This code is almost identical to the PIR motion sensor. Instead of LOW, we switched it HIGH, so the onboard LED (D7) will only come on when both parts are away from each other.

Troubleshooting

  • If your code refuses to flash, try putting your Photon RedBoard into safe mode. Then, hit Flash.

Experiment 10: Pong!

Introduction

In this experiment, we’ll use the OLED Breakout, potentiometer, and a photo resistor to make a game of Pong, where the paddles are controlled by the analog values of the two sensors.

alt text

Parts Needed

You will need the following parts (besides your Photon RedBoard and Breadboard, of course):

  • 1x OLED Breakout Board
  • 1x Potentiometer
  • 1x Photoresistor
  • 1x 330 Ohm Resistor
  • 15x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
Mini Photocell

SEN-09088
$1.5
4
SparkFun Micro OLED Breakout

LCD-13003
$14.95
13
Trimpot 10K with Knob

COM-09806
$0.95
4
Resistor 330 Ohm 1/6th Watt PTH

COM-08377
$0.25

Suggested Reading

Hardware Hookup

We’ll be connecting three devices to our Photon RedBoard - the OLED breakout, a trim potentiometer, and a photocell. These last two are analog sensors that are going to act as the paddle controllers for each player, while the OLED screen displays the ball, the paddles, and the score.

alt text

Having a hard time seeing the circuit? Click on the Fritzing diagram to see a bigger image.

The photocell is hooked up to pin A0 and GND though a 330 (NOT 10K) Ohm resistor from one pin, while the other pin is connected to +3.3V. The potentiometer’s middle pin goes to pin A1, while the side pins are connected to GND and 3.3V.

Here’s a handy table for the OLED Breakout connections:

Photon RedBoard PinOLED Breakout Pin
A2CS
A3SCK
A5SDI
D5D/C
D6RST
3V33.3V
GNDGND

The table and Fritzing image are much more helpful, but if you wanted to see inside the box, here’s a shot of the breadboarded circuit:

alt text

Photon Code

Pong is a great programming exercise for getting used to a new language or environment. There are tons of great pong tutorials out there, so we’re not going to focus too much on the general code beyond the parts that interact with the hardware.

The first thing we need to do after creating a new app is to add the SparkFun OLED library to our sketch. This let’s us draw and write all manner of things to the OLED screen quite easily. First, click on the bookmark icon in the sidebar (it’s the fourth one up from the bottom). Under ‘Community Libraries’, there should be a search box - type in OLED, and you should see the ‘SparkFunMicroOLED’ Library pop up, like so:

alt text

Click on the library name, and then on the big blue button that says “Include in App”. That should cause a line like #include "SparkFunMicroOLED/SparkFunMicroOLED.h" to appear near the top of your sketch. Voila!

Now that you’ve imported the library, you can copy and paste the rest of the code beneath:

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 10 - Part 1
    This sketch was written by SparkFun Electronics
    Ben Leduc-Mills
    August 31, 2015
    https://github.com/sparkfun

    This is an example sketch for an analog sensor Pong game.

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/
//////////////////////////
// MicroOLED Definition //
//////////////////////////

#define PIN_RESET D6  // Connect RST to pin 6
#define PIN_DC    D5  // Connect DC to pin 5 (required for SPI)
#define PIN_CS    A2 // Connect CS to pin A2 (required for SPI)
//MicroOLED oled(MODE_SPI, PIN_RESET, PIN_DC, PIN_CS);
MicroOLED oled(MODE_SPI, PIN_RESET, PIN_DC, PIN_CS);

//#define SINGLE_PLAYER

const int player1Pin = A1;
#ifndef SINGLE_PLAYER
const int player2Pin = A0;
#endif

/*** Sensor Calibration ****/

int sensor1Calibration = LCDHEIGHT; //photocell w/330
int sensor2Calibration = LCDHEIGHT; //potentiometer

//potentiometer
int sensor1Min = 0;
int sensor1Max = 4096;
//photocell
int sensor2Min = 100;
int sensor2Max = 1000;


/*** Game Settings ***/
const int renderDelay = 16;
const int startDelay = 2000;
const int gameOverDelay = 3000;
const int scoreToWin = 10;

int player1Score = 0;
int player2Score = 0;

const float paddleWidth = LCDWIDTH / 16.0;
const float paddleHeight = LCDHEIGHT / 3.0;
const float halfPaddleWidth = paddleWidth / 2.0;
const float halfPaddleHeight = paddleHeight / 2.0;

float player1PosX = 1.0 + halfPaddleWidth;
float player1PosY = 0.0;
float player2PosX = LCDWIDTH - 1.0 - halfPaddleWidth;
float player2PosY = 0.0;

// This is only used in SINGLE_PLAYER mode:
#ifdef SINGLE_PLAYER
float enemyVelY = 0.5;
#endif


/*** Ball Physics ***/
const float ballRadius = 2.0;
const float ballSpeedX = 1.0;
float ballPosX = LCDWIDTH / 2.0;
float ballPosY = LCDHEIGHT / 2.0;
float ballVelX = -1.0 * ballSpeedX;
float ballVelY = 0;

void setup()
{
// in our setup, we call a few custom functions to get everything ready
  initializeGraphics();
  initializeInput();
  displayGameStart();
  Serial.begin(9600);
}


void loop()
{
  updateGame(); //custom function to advance the game logic
  renderGame(); //custom function to dislay the current game state on the OLED screen
  // print out sensor values via serial so we can calibrate
  Serial.print(analogRead(A0));
  Serial.print("<-A0 | A1-> ");
  Serial.println(analogRead(A1));

  //winning conditions
  if (player1Score >= scoreToWin)
  {
    gameOver(true);
  }
  else if (player2Score >= scoreToWin)
  {
    gameOver(false);
  }
}

//start the OLED and set the font type we want
void initializeGraphics()
{
  oled.begin();
  oled.setFontType(1);
}

//get either 1 or 2 pins ready for analog input
void initializeInput()
{
  pinMode(player1Pin, INPUT);
  #ifndef SINGLE_PLAYER
  pinMode(player2Pin, INPUT);
  #endif
}

//display the start screen
void displayGameStart()
{
  oled.clear(PAGE);
  renderString(20, 10, "Get");
  renderString(10, 30, "Ready!");
  oled.display();
  delay(startDelay);
}

//update the positions of the player paddles and the ball
void updateGame()
{
  updatePlayer1();
  updatePlayer2();
  updateBall();
}

//reset the score, ball position, and paddle positions for a new game
void resetGame()
{
  player2Score = 0;
  player1Score = 0;
  player2PosY = 0.0;
  ballPosX = LCDWIDTH / 2.0;
  ballPosY = LCDHEIGHT / 2.0;
  ballVelX = -1.0 * ballSpeedX;
  ballVelY = 0.0;
}


float clampPaddlePosY(float paddlePosY)
{
  float newPaddlePosY = paddlePosY;

  if (paddlePosY - halfPaddleHeight < 0)
  {
    newPaddlePosY = halfPaddleHeight;
  }
  else if (paddlePosY + halfPaddleHeight > LCDHEIGHT)
  {
    newPaddlePosY = LCDHEIGHT - halfPaddleHeight;
  }

  return newPaddlePosY;
}

void updatePlayer1()
{
  int potVal = analogRead(player1Pin);
  player1PosY = map(potVal, sensor1Min, sensor1Max, 0, sensor1Calibration);
}

void updatePlayer2()
{
  // If it's a single player game, update the AI's position
#ifdef SINGLE_PLAYER
  // Follow the ball at a set speed
  if (player2PosY < ballPosY)
  {
    player2PosY += enemyVelY;
  }
  else if (player2PosY > ballPosY)
  {
    player2PosY -= enemyVelY;
  }

  player2PosY = clampPaddlePosY(player2PosY);
#else  // Else if this is multiplayer, get player 2's position
  int lightVal = analogRead(player2Pin);
  player2PosY = map(lightVal, sensor2Min, sensor2Max, 0, sensor2Calibration);
  //Serial.println(player2PosY);
#endif
}

void updateBall()
{
  ballPosY += ballVelY;
  ballPosX += ballVelX;

  // Top and bottom wall collisions
  if (ballPosY < ballRadius)
  {
    ballPosY = ballRadius;
    ballVelY *= -1.0;
  }
  else if (ballPosY > LCDHEIGHT - ballRadius)
  {
    ballPosY = LCDHEIGHT - ballRadius;
    ballVelY *= -1.0;
  }

  // Left and right wall collisions
  if (ballPosX < ballRadius)
  {
    ballPosX = ballRadius;
    ballVelX = ballSpeedX;
    player2Score++;
  }
  else if (ballPosX > LCDWIDTH - ballRadius)
  {
    ballPosX = LCDWIDTH - ballRadius;
    ballVelX *= -1.0 * ballSpeedX;
    player1Score++;
  }

  // Paddle collisions
  if (ballPosX < player1PosX + ballRadius + halfPaddleWidth)
  {
    if (ballPosY > player1PosY - halfPaddleHeight - ballRadius &&
        ballPosY < player1PosY + halfPaddleHeight + ballRadius)
    {
      ballVelX = ballSpeedX;
      ballVelY = 2.0 * (ballPosY - player1PosY) / halfPaddleHeight;
    }
  }
  else if (ballPosX > player2PosX - ballRadius - halfPaddleWidth)
  {
    if (ballPosY > player2PosY - halfPaddleHeight - ballRadius &&
        ballPosY < player2PosY + halfPaddleHeight + ballRadius)
    {
      ballVelX = -1.0 * ballSpeedX;
      ballVelY = 2.0 * (ballPosY - player2PosY) / halfPaddleHeight;
    }
  }
}


void renderGame()
{
  oled.clear(PAGE);

  renderScores(player1Score, player2Score);
  renderPaddle(player1PosX, player1PosY);
  renderPaddle(player2PosX, player2PosY);
  renderBall(ballPosX, ballPosY);

  oled.display();
  delay(renderDelay);
}

void renderString(int x, int y, String string)
{
  oled.setCursor(x, y);
  oled.print(string);
}

void renderPaddle(int x, int y)
{
  oled.rect(
    x - halfPaddleWidth,
    y - halfPaddleHeight,
    paddleWidth,
    paddleHeight);
}

void renderBall(int x, int y)
{
  oled.circle(x, y, 2);
}

void renderScores(int firstScore, int secondScore)
{
  renderString(10, 0, String(firstScore));
  renderString(LCDWIDTH - 14, 0, String(secondScore));
}

void gameOver(bool didWin)
{
  if (didWin)
  {
#ifdef SINGLE_PLAYER
    renderString(20, 10, "You");
    renderString(20, 30, "Win!");
#else
    renderString(0, 10, "Playr 1");
    renderString(15, 30, "Wins");
#endif
  }
  else
  {
#ifdef SINGLE_PLAYER
    renderString(20, 10, "You");
    renderString(15, 30, "Lose!");
#else
    renderString(0, 10, "Playr 2");
    renderString(15, 30, "Wins");
#endif
  }

  oled.display();
  delay(gameOverDelay);

  // Get ready to start the game again.
  resetGame();
  displayGameStart();
}

What You Should See

After successfully flashing the code onto your Photon RedBoard, you should see the words ‘Get Ready!’ on the OLED screen, followed soon after by the familiar Pong game screen. If you’ve calibrated your sensors well, you should be able to move each paddle up and down to defend your side. After a player wins, the game should start all over again.

If you turn the potentiometer or place your hand over the photocell, the paddles should move up and down, allowing you to play a game of Pong:

alt text

Code to Note

Whew! That’s a lot of code to digest all at once. Let’s look at a few of the key sections that make this work.

Defining our pin assignments and declaring our OLED object happens right at the top:

language:c
#define PIN_RESET D6  // Connect RST to pin 6
#define PIN_DC    D5  // Connect DC to pin 5 (required for SPI)
#define PIN_CS    A2 // Connect CS to pin A2 (required for SPI)
MicroOLED oled(MODE_SPI, PIN_RESET, PIN_DC, PIN_CS);

//#define SINGLE_PLAYER

const int player1Pin = A1; //connected to potentiometer
#ifndef SINGLE_PLAYER
const int player2Pin = A0; //connected to photocell
#endif

This is also where we decide a few important things: which communication mode we’re using - SPI in our case means we put MODE_SPI as that first variable in the MicroOLED object. Below that, we’re also deciding if we want a 1 player or 2 player game. To make the game one player, simply uncomment the //#define SINGLE_PLAYER line.

The next section is crucial - it’s where we ‘calibrate’ our sensors, by telling the program what we expect the minimum and maximum values for each sensor to be. Changes are you’ll have to run the program first and look at the serial monitor while testing your sensors to get a good ‘sense’ of the value ranges.

language:c
/*** Sensor Calibration ****/

int sensor1Calibration = LCDHEIGHT; //photocell w/330
int sensor2Calibration = LCDHEIGHT; //potentiometer

//potentiometer
int sensor1Min = 0;
int sensor1Max = 4096;
//photocell
int sensor2Min = 100;
int sensor2Max = 1000;

For example, if you’re in a very brightly lit room, your photocell will get a wider range of values than if you’re in a dimly lit place – but we still want the paddle to move the entire range of the screen, which is why we save the LCDHEIGHT value to a separate variable to use later on.

After we set a number of global variables that control things like how fast the ball moves and what number each game goes until (the winning number), we’re on to our setup() and loop() functions.

language:c
void setup()
{
  // in our setup, we call a few custom functions to get everything ready
  initializeGraphics();
  initializeInput();
  displayGameStart();
  Serial.begin(9600);
}

void loop()
{
  updateGame(); //custom function to advance the game logic
  renderGame(); //custom function to dislay the current game state on the OLED screen
  // print out sensor values via serial so we can calibrate
  Serial.print(analogRead(A0));
  Serial.print("<-A0 | A1-> ");
  Serial.println(analogRead(A1));

  //winning conditions
  if (player1Score >= scoreToWin)
  {
    gameOver(true);
  }
  else if (player2Score >= scoreToWin)
  {
    gameOver(false);
  }
}

You’ll notice that we make use of a lot of custom functions in order to keep our main setup and loop short and readable. In the setup(), we call a few functions that help start up the OLED screen, initialize our input pins, display the start screen, and open up serial communication. The loop() is simply updating the game state, displaying that state, checking to see if anyone won, and ending the game if so. Dig into all of these functions to get a better sense of how the game works!

Troubleshooting

The main challenge (besides being good at Pong) is making sure your sensor inputs are calibrated to the point where each paddle covers the entire height of the OLED screen. Checking the serial monitor while testing your sensors is the best way to get an idea of what the range for each sensor is.

Part 2: Scoreboard!

What does every serious Pong competition need? A scoreboard, that’s what! In this half of the exercise, we’re going to use Particle.variable() with the Particle Cloud API to create a web page that stores our Pong scores locally (i.e., without a database) in HTML 5 localData, displays them in a table, and updates when a new score in ready.

alt text

Photon Code

Here’s the new code that goes on the Photon RedBoard - in reality, there are just a few changes. We’ll go over them in the next section, but this is the complete sketch on the Photon side.

language:c
/*  SparkFun Inventor's Kit for Photon
    Experiment 10 - Part 2
    This sketch was written by SparkFun Electronics
    Ben Leduc-Mills
    August 31, 2015
    https://github.com/sparkfun

    This is an example sketch for an analog sensor Pong game, with
    an html/javascript scoreboard.

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon RedBoard
    Released under the MIT License(http://opensource.org/licenses/MIT)
*/

//////////////////////////
// MicroOLED Definition //
//////////////////////////

#define PIN_RESET D6  // Connect RST to pin 6
#define PIN_DC    D5  // Connect DC to pin 5 (required for SPI)
#define PIN_CS    A2 // Connect CS to pin A2 (required for SPI)
//MicroOLED oled(MODE_SPI, PIN_RESET, PIN_DC, PIN_CS);
MicroOLED oled(MODE_SPI, PIN_RESET, PIN_DC, PIN_CS);



//#define SINGLE_PLAYER

const int player1Pin = A1;
#ifndef SINGLE_PLAYER
const int player2Pin = A0;
#endif

/*** Sensor Calibration ****/

int sensor1Calibration = LCDHEIGHT; //photocell w/330
int sensor2Calibration = LCDHEIGHT; //potentiometer

//potentiometer
int sensor1Min = 0;
int sensor1Max = 4096;
//photocell
int sensor2Min = 100;
int sensor2Max = 1000;


/*** Game Settings ***/
const int renderDelay = 16;
const int startDelay = 2000;
const int gameOverDelay = 3000;
const int scoreToWin = 10;

int player1Score = 0;
int player2Score = 0;

const float paddleWidth = LCDWIDTH / 16.0;
const float paddleHeight = LCDHEIGHT / 3.0;
const float halfPaddleWidth = paddleWidth / 2.0;
const float halfPaddleHeight = paddleHeight / 2.0;

float player1PosX = 1.0 + halfPaddleWidth;
float player1PosY = 0.0;
float player2PosX = LCDWIDTH - 1.0 - halfPaddleWidth;
float player2PosY = 0.0;

// This is only used in SINGLE_PLAYER mode:
#ifdef SINGLE_PLAYER
float enemyVelY = 0.5;
#endif


/*** Ball Physics ***/
const float ballRadius = 2.0;
const float ballSpeedX = 1.0;
float ballPosX = LCDWIDTH / 2.0;
float ballPosY = LCDHEIGHT / 2.0;
float ballVelX = -1.0 * ballSpeedX;
float ballVelY = 0;


//score keeper
char postScore[64];

//game ID
int gameID = 0;


void setup()
{
  // in our setup, we call a few custom functions to get everything ready
  //here's our call to Particle.variable - we're just sending a string called 'data' out to the cloud
  Particle.variable("data", &postScore, STRING);
  initializeGraphics();
  initializeInput();
  displayGameStart();
  Serial.begin(9600);
}


void loop()
{
  updateGame(); //custom function to advance the game logic
  renderGame(); //custom function to dislay the current game state on the OLED screen

  //winning conditions
  if (player1Score >= scoreToWin)
  {
    gameOver(true);
  }
  else if (player2Score >= scoreToWin)
  {
    gameOver(false);
  }
}

//start the OLED and set the font type we want
void initializeGraphics()
{
  oled.begin();
  oled.setFontType(1);
}

//get either 1 or 2 pins ready for analog input
void initializeInput()
{
  pinMode(player1Pin, INPUT);
  #ifndef SINGLE_PLAYER
  pinMode(player2Pin, INPUT);
  #endif
}

//display the start screen
void displayGameStart()
{
  oled.clear(PAGE);
  renderString(20, 10, "Get");
  renderString(10, 30, "Ready!");
  oled.display();
  delay(startDelay);
}

//update the positions of the player paddles and the ball
void updateGame()
{
  updatePlayer1();
  updatePlayer2();
  updateBall();
}

//reset the score, ball position, and paddle positions for a new game
void resetGame()
{
  player2Score = 0;
  player1Score = 0;
  player2PosY = 0.0;
  ballPosX = LCDWIDTH / 2.0;
  ballPosY = LCDHEIGHT / 2.0;
  ballVelX = -1.0 * ballSpeedX;
  ballVelY = 0.0;
}


float clampPaddlePosY(float paddlePosY)
{
  float newPaddlePosY = paddlePosY;

  if (paddlePosY - halfPaddleHeight < 0)
  {
    newPaddlePosY = halfPaddleHeight;
  }
  else if (paddlePosY + halfPaddleHeight > LCDHEIGHT)
  {
    newPaddlePosY = LCDHEIGHT - halfPaddleHeight;
  }

  return newPaddlePosY;
}

void updatePlayer1()
{
  int potVal = analogRead(player1Pin);
  player1PosY = map(potVal, sensor1Min, sensor1Max, 0, sensor1Calibration);
}

void updatePlayer2()
{
  // If it's a single player game, update the AI's position
#ifdef SINGLE_PLAYER
  // Follow the ball at a set speed
  if (player2PosY < ballPosY)
  {
    player2PosY += enemyVelY;
  }
  else if (player2PosY > ballPosY)
  {
    player2PosY -= enemyVelY;
  }

  player2PosY = clampPaddlePosY(player2PosY);
#else  // Else if this is multiplayer, get player 2's position
  int lightVal = analogRead(player2Pin);
  player2PosY = map(lightVal, sensor2Min, sensor2Max, 0, sensor2Calibration);
  //Serial.println(player2PosY);
#endif
}

void updateBall()
{
  ballPosY += ballVelY;
  ballPosX += ballVelX;

  // Top and bottom wall collisions
  if (ballPosY < ballRadius)
  {
    ballPosY = ballRadius;
    ballVelY *= -1.0;
  }
  else if (ballPosY > LCDHEIGHT - ballRadius)
  {
    ballPosY = LCDHEIGHT - ballRadius;
    ballVelY *= -1.0;
  }

  // Left and right wall collisions
  if (ballPosX < ballRadius)
  {
    ballPosX = ballRadius;
    ballVelX = ballSpeedX;
    player2Score++;
  }
  else if (ballPosX > LCDWIDTH - ballRadius)
  {
    ballPosX = LCDWIDTH - ballRadius;
    ballVelX *= -1.0 * ballSpeedX;
    player1Score++;
  }

  // Paddle collisions
  if (ballPosX < player1PosX + ballRadius + halfPaddleWidth)
  {
    if (ballPosY > player1PosY - halfPaddleHeight - ballRadius &&
        ballPosY < player1PosY + halfPaddleHeight + ballRadius)
    {
      ballVelX = ballSpeedX;
      ballVelY = 2.0 * (ballPosY - player1PosY) / halfPaddleHeight;
    }
  }
  else if (ballPosX > player2PosX - ballRadius - halfPaddleWidth)
  {
    if (ballPosY > player2PosY - halfPaddleHeight - ballRadius &&
        ballPosY < player2PosY + halfPaddleHeight + ballRadius)
    {
      ballVelX = -1.0 * ballSpeedX;
      ballVelY = 2.0 * (ballPosY - player2PosY) / halfPaddleHeight;
    }
  }
}


void renderGame()
{
  oled.clear(PAGE);

  renderScores(player1Score, player2Score);
  renderPaddle(player1PosX, player1PosY);
  renderPaddle(player2PosX, player2PosY);
  renderBall(ballPosX, ballPosY);

  oled.display();
  delay(renderDelay);
}

void renderString(int x, int y, String string)
{
  oled.setCursor(x, y);
  oled.print(string);
}

void renderPaddle(int x, int y)
{
  oled.rect(
    x - halfPaddleWidth,
    y - halfPaddleHeight,
    paddleWidth,
    paddleHeight);
}

void renderBall(int x, int y)
{
  oled.circle(x, y, 2);
}

void renderScores(int firstScore, int secondScore)
{
  renderString(10, 0, String(firstScore));
  renderString(LCDWIDTH - 14, 0, String(secondScore));
}

// OK - here's the new bit
void gameOver(bool didWin)
{
  //at the end of a game, increment the gameID by 1
  gameID += 1;
  //now, send out the player 1 score, player 2 score, and the game ID
  //we do this by using 'sprintf', which takes a bunch of random data types (in our case, integers),
  //and puts them all into a nicely formatted string - which is exactly what our Particle.variable is supposed to be
  sprintf(postScore, "{\"player1Score\":%d, \"player2Score\":%d, \"gameID\":%d}", player1Score, player2Score, gameID);
  //give us a sec, and start a new game
  delay(1000);

  if (didWin)
  {
#ifdef SINGLE_PLAYER
    renderString(20, 10, "You");
    renderString(20, 30, "Win!");
#else
    renderString(0, 10, "Playr 1");
    renderString(15, 30, "Wins");
#endif
  }
  else
  {
#ifdef SINGLE_PLAYER
    renderString(20, 10, "You");
    renderString(15, 30, "Lose!");
#else
    renderString(0, 10, "Playr 2");
    renderString(15, 30, "Wins");
#endif
  }

  oled.display();
  delay(gameOverDelay);

  // Get ready to start the game again.
  resetGame();
  displayGameStart();
}

HTML Code

This is where the real action is happening for this exercise - on the web. We’ll be using a combination of good ol' HTML, the HTML 5 localStorage ability, and some basic javaScript to create a page that reads the values of our Particle.variable, stores them (without using a database), displays them in a table, and checks every 15 seconds for a new score to come in.

Create a new html file in your favorite text editor, save it as ‘experiment10.html’ and paste in the code below (doesn’t matter where you save it, just remember where it is):

language:javascript<!DOCTYPE html><html><head><meta charset=utf-8 /><META HTTP-EQUIV="refresh" CONTENT="15"><!-- refresh our page every 15 seconds --><!--
SparkFun Inventor's Kit for Photon
  Experiment 10 - Part 2
  This sketch was written by SparkFun Electronics
  Ben Leduc-Mills
  August 31, 2015
  https://github.com/sparkfun

  This is an example sketch for an analog sensor Pong game, with
  an html/javascript scoreboard.

  Development environment specifics:
  Particle Build environment (https://www.particle.io/build)
  Particle Photon RedBoard
  Released under the MIT License(http://opensource.org/licenses/MIT)
--><title>SparkFun Photon SIK Experiment 10 - Part 2</title></head><body><div style="height:300px;overflow:auto;"><!-- make a div set to overflow so our table scrolls --><table id="scorekeeper"> <!-- table id - important soon --></table></div><FORM> <!-- form button to clear data if we want --><INPUT TYPE="button" onClick="handlers.clearAppData()" VALUE="Clear Scores"></FORM><!-- javascript starts here --><script type="text/javascript">

            //object to contain our data
            var app = {
                scores: [{
                }]
            };

            //to keep track of game number - don't want to record the same game more than once
            var gameNum =0;

            //set of functions to handle getting and setting the local storage data from our api call
            //thanks to stackoverflow user bundleofjoy(http://stackoverflow.com/users/1217785/bundleofjoy) for the inspiration
            var handlers = {

                //make an api call and save new data from our Particle sketch into our local storage object
                saveData: function () {
                    var xmlhttp = new XMLHttpRequest();

                    // IMPORTANT: replace this with your device name, Particle.variable name, and your access token!
                    var url = "https://api.particle.io/v1/devices/{your device name}/{your Particle.variable name}?access_token={your access_token}";
                    xmlhttp.open("GET", url, true);
                    xmlhttp.send();


                    xmlhttp.onreadystatechange = function(){
                        if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                            var data = JSON.parse(xmlhttp.responseText);
                            var result = decodeURI(data.result);

                            //parse our json object
                            var p = JSON.parse(result);

                            //check if the latest game number from Particle is greater than what we last stored locally
                            //if so, add a new score to our scores array
                            if(p.gameID > gameNum){
                                console.log("saved new");
                                app.scores.push({player1Score: p.player1Score, player2Score: p.player2Score, gameID: p.gameID});
                                localStorage.setItem("app", JSON.stringify(app));
                                gameNum = p.gameID;

                            }

                        }
                    }
                },

                //get the data currently stored in our HTML 5 localStorage object
                getData: function () {
                    // Retrieves the object app from localStorage
                    var retrievedObject = localStorage.getItem("app");
                    var savedData = JSON.parse(retrievedObject);

                    //if there's something in saved data, let's grab it!
                    if(savedData !== null){

                        //and put the data into something we can see on the page (our table)
                        handlers.displayTable();

                        //let's also update our local gameID variable with the latest gameID
                        for(scores in savedData) {
                        var current = savedData.scores.length-1;
                        gameNum = savedData.scores[current].gameID;
                    }
                  }

                  return savedData;
                },

                //deletes all our local data
                clearAppData: function (event) {
                    localStorage.clear();
                    return false;
                },

                //displays our data in an html table
                displayTable: function() {
                    var retrievedObject = localStorage.getItem("app");
                    var savedData = JSON.parse(retrievedObject);
                    var out = "<th>Game Number</th><th>Player 1 Score</th><th>Player 2 Score</th>";

                    //iterate through all the scores in our saved data and put them into one long string
                    for(var i = 1; i < savedData.scores.length-1; i++) {
                        out += "<tr>" + "<td>";
                        out += savedData.scores[i].gameID+ "</td>";
                        out += "<td>" + savedData.scores[i].player1Score+ "</td>";
                        out += "<td>" + savedData.scores[i].player2Score + "</td>";
                        out +=  "</tr>"
                    }

                    console.log(out);
                    //put that long string into our table using out table's id property
                    document.getElementById('scorekeeper').innerHTML = out;
                }

            };

        //when the window loads (like on refresh), this gets called
        window.onload = function () {
            //get data from localStorage
            var saved = handlers.getData();
            //check if its null / undefined
            if ([null, undefined].indexOf(saved) === -1) {

                //it's not null/undefined - data exists!
                console.log("Data exists. Here's the data : ", saved);
                //set your "app" to data from localStorage
                app = saved;

                handlers.saveData();

            } else {
                //localStorage is empty
                console.log("Data does not exist, save to localStorage");
                //so, save incoming data in localStorage
                handlers.saveData();
            }
        };</script></body></html>

What You Should See

Plug in your Photon RedBoard, and make sure it’s running Pong and posting a JSON object to the URL you specified. Now, if you open the HTML file in your browser of choice (the URL might be something like ‘file://localhost/Users/User1/Desktop/Experiment10.html’ on a mac), you should see something like this:

alt text

If the page is blank, give it a minute or two - the updates are sometimes a little out of sync with what the Pong game is currently doing (and remember, it only posts new data after a game has been won or lost).

The page will refresh every 15 seconds, check for a new score, add it if there is one, and show it to you. You can push the ‘clear scores’ button to empty the scores table, though to reset the gameID you’ll have to restart your Photon.

Sweet! Now, as long as your Photon is online you can point your friends to a live scoreboard of your match scores.

Code to Note: Photon Part 2

Some important bits have changed, so pay attention.

//score keeper
char postScore[64];

//game ID
int gameID = 0;

We add to variable at the top of our code to contain postScore[] a character array that we will register as our Particle.variable(), and gameID, which we will use as a unique identifier for each game played.

In setup(), we register our postScore[] variable so we can access from the cloud:

Particle.variable("data", &postScore, STRING);

That first argument (“data” in our case) is what we will use in the Particle api to retrieve our info - but it could be called anything.

Now for the fun part - formatting our data to a string to be accessed by the Particle cloud API.

// OK - here's the new bit
void gameOver(bool didWin)
{
  //at the end of a game, increment the gameID by 1
  gameID += 1;
  //now, send out the player 1 score, player 2 score, and the game ID
  //we do this by using 'sprintf', which takes a bunch of random data types (in our case, integers),
  //and puts them all into a nicely formatted string - which is exactly what our Particle.variable is supposed to be
  sprintf(postScore, "{\"player1Score\":%d, \"player2Score\":%d, \"gameID\":%d}", player1Score, player2Score, gameID);

Read the comments for a better idea of what’s going on, or if you’re feeling very adventurous, check out the c++ reference page for sprintf.

Code to Note: HTML

Ok, so it looks like a mess of gibberish right now - that’s fine. The most important thing is that you find the URL variable and change it to match your credentials. It looks like this (should be around line 39):

var url = "https://api.particle.io/v1/devices/{your device name}/{your Particle.variable name}?access_token={your access_token}";

The parts in curly brackets {} are the parts you need to replace. If even one thing is off, none of this will work. Computers are pretty finicky that way, trust me. A great way to check is to paste your URL in a browser (make sure your Photon code is uploaded and your Photon RedBoard is plugged in first) - you should see that URL return a JSON object (it’s a nice way of organizing data for the web). It might look something like this (I took bits out for privacy):

{"cmd": "VarReturn","name": "data","result": "{\"player1Score\":10, \"player2Score\":2, \"gameID\":1}","coreInfo": {"last_app": "","last_heard": "2015-08-26T21:35:28.021Z","connected": true
}

If you see something like, you’re all set - if not, check your credentials and try again.

Troubleshooting

Granted, this is a rather complicated experiment (I’m not even sure that anyone had done this with a Photon yet when we wrote this). Being comfortable with HTML and javaScript is obviously a big help here. Here are a few links that might help you out (they helped me out, anyway) while looking through the code and comments:

Going Further

There is SO much potential to go further with this project. Display the data with the last game played at the top of the table. Add some CSS and make it look pretty. Keep track of player 1 wins vs. player 2 wins over time. Calculate the average margin of victory. Visualize the scores in a graph over time. So. Many. Possibilities. Please share with us if you make some improvements!

Experiment 11: OLED Apps - Weather & Clock

Introduction

This experiment will re-visit a hardware component you should already be familiar with: buttons. But the method we use to interact with the buttons in this experiment will be wholly different. We’re going to use hardware interrupts to be instantly notified if a button is pressed.

Combining the buttons with an OLED – user input with user interface – allows us to create an endless variety of interactive applications. In this experiment – using hardware interrupts as a foundation – we’ll create a simple clock, complete with stopwatch and timer. Then – once you’re comfortable with external interrupts – we’ll connect the same circuit to the Internet to create a fully functional weather forecasting application. You’ll never have to listen to that weather reporter again!

OLED weather app photo

Parts Needed

  • 1x MicroOLED Breakout
  • 3x Buttons: green, yellow & blue
  • 15x Jumper Wires
Using a Photon by Particle instead or you don't have the kit? No worries! You can still have fun and follow along with this experiment. We suggest using the parts below:
SparkFun Micro OLED Breakout

LCD-13003
$14.95
13
Tactile Button Assortment

COM-10302
$4.95
Jumper Wires - Connected 6" (M/M, 20 pack)

PRT-12795
$1.95

Suggested Reading

External interrupts are used in these experiments to read the state of our buttons. An interrupt is a program flow-control trick microprocessors use to execute a routine outside the loop(), whenever a pin’s digital state changes.

Implementing an interrupt requires three parameters:

  • Pin– Most of the Photon RedBoard’s pins can be used to generate an interrupt. All of them except D0 and A5.
  • Event– When a digital pin changes state it either rises (goes from HIGH to LOW) or falls (goes from LOW to HIGH). An interrupt can be set to trigger on either of those events or both.
  • Interrupt routine– An interrupt service routine (ISR) is a function defined in your code – with no parameters, and no return value – that immediately runs whenever an interrupt event triggers.

Interrupts can trigger at almost any moment in the run cycle of an application, whether it’s between a couple lines of code in your loop(), or during a function call. Even a delay() function can be paused by an interrupt. Once an interrupt has fired, and its ISR has run its course, your application code will continue running from where it was interrupted.

Hardware Hookup

Hook up your circuit as shown below (if you still have the OLED hooked up from the last experiment, don’t change a thing there, just add the buttons):

OLED Apps circuit diagram

Most of that wire-jungle is devoted to the Micro OLED’s SPI interface. It’s critical that the three hardware SPI pins – SCK/A3, MISO/A4, and MOSI/A5 – not be relocated.

Our three button signals are all routed to Photon pins capable of handling external interrupts. We’ll configure these pins with internal pull-up resistors, so when the button is pressed down, the pin will read as LOW.

Photon Code

Create a new application – we call ours DigitalWatch– and paste the code below into it:

language:c
#include "math.h"

/////////////////
// Button Pins //
/////////////////
#define MODE_GREEN_PIN   D2
#define STOP_BLUE_PIN    D3
#define START_YELLOW_PIN D4

//////////////////////////
// MicroOLED Definition //
//////////////////////////
#define PIN_OLED_RST D6  // Connect RST to pin 6
#define PIN_OLED_DC  D5  // Connect DC to pin 5 (required for SPI)
#define PIN_OLED_CS  A2  // Connect CS to pin A2 (required for SPI)
MicroOLED oled(MODE_SPI, PIN_OLED_RST, PIN_OLED_DC, PIN_OLED_CS);

// Set your Time zone below, adjust from UTC.
// E.g. -6.0 is mountain time in the United Sates,
// -4.0 would be eastern, -7.0 pacific
#define TIME_ZONE -6.0 // Mountain time (Niwot, CO; SparkFun HQ!)

////////////////////////////
// CLock Mode Definitions //
////////////////////////////
#define NUM_CLOCK_STATES 3 //total number of clock modes
// The clockStates enum defines our three clock modes, and their order
enum clockStates {
    MODE_CLOCK,     // Simple 24-hour clock
    MODE_STOPWATCH, // Stopwatch
    MODE_TIMER      // Egg timer
};
int clockState = MODE_CLOCK; // clockState keeps track of our current mode

/////////////////////////
// StopWatch Variables //
/////////////////////////
unsigned long stopWatchStart = 0; // Keeps track of the stopwatch start time
unsigned long stopWatchStop = 0; // Keeps track of the stopwatch stop time
bool stopWatchRunning = false; // Boolean so we know if the stopwatch is running or not
unsigned long runTime; // A varaible we'll use to calculate how long the stopwatch has been running

/////////////////////
// Timer Variables //
/////////////////////
bool settingTimer = true; // Boolean to track whether the timer is being set or not
unsigned long timerTime;  // Variable to keep track of when the timer ends

///////////////////////////////
// Button Debounce Variables //
///////////////////////////////
// debounceTime defines the number of milliseconds that must pass between
// button presses.
#define debounceTime 100 // 100ms debounce time
unsigned int lastPressTime = 0; // keeps track of the last time a button was pressed

// ISR: startWatch() -- called whenever the yellow button is pressed.
// This function starts the stopwatch, if it's in that mode.
// If it's in timer mode, pressing the yellow button will allow you to set the time.
void startWatch()
{
    if (softwareDebounce()) // If the button hasn't been pressed in debounceTime ms
    {
        if (clockState == MODE_STOPWATCH) // If we're in stopwatch mode
        {
            if (!stopWatchRunning) // And if the stopwatch is stopped
            {
                // Set the stopWatchRunning flag to true. The loop() will see that
                // next time through, and begin running the watch.
                stopWatchRunning = true;
                // Depending on whether the stopwatch has been paused or reset, we need to
                // modify the stopWatchStart value.
                if (stopWatchStop == stopWatchStart) // If the stopwatch has been reset
                    stopWatchStart = millis(); // stopwatch start time is just millis()
                else // If it was paused, but then re-started
                    stopWatchStart += millis() - stopWatchStop; // artificially increase start time by the amount paused
            }
            else
            {
                // Going Further: Add splits, pressing run while running starts a new lap
            }
        }
        else if (clockState == MODE_TIMER) // If we're in timer mode
        {
            // Both blue and yellow buttons need to be pressed to activate
            // setting timer mode
            noInterrupts(); // Turn off interrupts briefly
            if (digitalRead(STOP_BLUE_PIN) == LOW) // Check if the other button is pressed
                settingTimer = true; // Switch to setting the timer mode
            interrupts(); // re-enable interrupts
        }
    }
}

// ISR: stopWatch() -- called when the blue button is pressed.
// This function stops the stop watch, if it's running.
// If the stopwatch is not running, it resets the stopwatch.
void stopWatch()
{
    if (softwareDebounce()) // If the button hasn't been pressed in debounceTime ms
    {
        if (clockState == MODE_STOPWATCH) // And we're in stopwatch mode
        {
            if (stopWatchRunning) // If the stopwatch is runinng, we need to stop it
            {
                stopWatchStop = millis(); // Store the stop time
                stopWatchRunning = false; // Set the stopWatchRunning flag to false
                // Next time loop() looks at the at boolean, it'll stop the watch.
            }
            else // If already stopped, reset the stopwatch
            {
                stopWatchStart = millis(); // Set the stopwatch start time to now
                stopWatchStop = stopWatchStart; // reset the stop time.
            }
        }
        else if (clockState == MODE_TIMER) // Otherwise, if we're in timer mode
        {
            // Both blue and yellow buttons need to be pressed to activate
            // setting timer mode
            noInterrupts(); // Turn off interrupts briefly
            if (digitalRead(START_YELLOW_PIN) == LOW) // Check if the other button is pressed
                settingTimer = true; // Switch to setting the timer mode
            interrupts(); // Re-enable interrupts
        }
    }
}

// ISR: changeMode is attached to the MODE_PIN interrupt. It is configured to be called
// any time the pin falls (when the button is pressed).
void changeMode()
{
    // softwareDebounce() helps "debounce" our button pin. When a button is pressed,
    // signal noise, and mechanical imperfections can make it rise and fall many times
    // before holding low.
    // softwareDebounce() filters out the high-frequency button-press noise, so the ISR
    // only runs once when the button is pressed.
    if (softwareDebounce())
    {
        // Increment clockState by one, then use the mod operation (%)
        // to roll back to 0 if we increment past a defined state.
        // E.g., NUM_CLOCK_STATES is 3, so clockState will go 0, 1, 2, 0, 1, 2, ...
        clockState = (clockState + 1) % NUM_CLOCK_STATES;
    }
}

void setup()
{
    pinMode(MODE_GREEN_PIN, INPUT_PULLUP); // Setup the MODE_PIN as an input, with pull-up.
    // Since it has a pull-up, the MODE_PIN will be LOW when the button is pressed.
    // Attach an interrupt to MODE_PIN, `changeMode` will be the name of the ISR,
    // setting the event to RISING means the ISR will be triggered when the button is released
    attachInterrupt(MODE_GREEN_PIN, changeMode, RISING);

    pinMode(START_YELLOW_PIN, INPUT_PULLUP); // Set the yellow button pin as an input, with pull-up
    attachInterrupt(START_YELLOW_PIN, startWatch, FALLING); // Call startWatch ISR whenever the pin goes low

    pinMode(STOP_BLUE_PIN, INPUT_PULLUP); // Set the blue button as an input, with pull-up
    attachInterrupt(STOP_BLUE_PIN, stopWatch, FALLING); // Call stopWatch ISR whenever the button is pressed

    // Call the interrupts() function to enable interrupts.
    // The opposite of interrupts() is noInterrupts(), which disables external interrupts.
    interrupts();

    oled.begin(); // Initialize the OLED

    Time.zone(TIME_ZONE); // Set up the timezone
}

// loop() looks at the clockState variable to decied which digital watch
// function to draw on the screen.
void loop()
{
    unsigned int h, m, s, ms; // Misc. variables used throughout the switch

    switch (clockState) // Check the value of clockState
    {
    case MODE_CLOCK: // If we're in clock mode:
        // Call the displayClock function (defined later), with the current
        // hour, minute, and second from the Time class.
        displayClock(Time.hour(), Time.minute(), Time.second());
        break;
    case MODE_STOPWATCH: // If we're in stopwatch mode:
        if (stopWatchRunning) // And if the stopwatch is running
        {
            // Calculate the most up-to-date run time:
            runTime = millis() - stopWatchStart;
        }
        else // If the stopwatch is stopped
        {
            // Calculate the total run time:
            runTime = stopWatchStop - stopWatchStart;
        }
        // runTime is the number of MILLISECONDS that the stopwatch has run
        // Let's calculate that out to  hours, minutes, seconds, and miliseconds
        h = runTime / (60 * 1000 * 60); // 3600000 milliseconds in an hour
        runTime -= h * (60 * 1000 * 60); // subtract the number of hour-milliseconds out
        m = runTime / (60 * 1000); // 60000 milliseconds in a minute
        runTime -= (60 * 1000) * m; // subtract the number of minute-milliseconds out
        s = runTime / 1000; // 1000 milliseconds in second
        runTime -= (1000 * s); // subtract the number of second-milliseconds
        ms = runTime; // We're left with the number of milliseconds
        // Call the displayStopwatch() function (defined later) to
        // draw a stopwatch:
        displayStopWatch(h, m, s, ms);
        break;
    case MODE_TIMER: // Finally, if we're in timer mode
        if(settingTimer) // and if we're setting the timer
        {
            setTimer(); // Call setTimer() (defined below)
        }
        else // If the timer's already set, draw the time left
        {
            if (timerTime >= Time.now()) // If we haven't hit the alarm time yet
            {
                s = timerTime - Time.now(); // calculate the amount of seconds left in our timer
                h = (s / 60) / 60; // Use that value to find the number of hours left, if any
                m = (s / 60) % 60; // Then find the number of minutes left
                s = s % 60; // Then find the number of seconds left
                // Call the displayTimer() function, defined later to draw the timer.
                displayTimer(h, m, s);
            }
            else // If our timer has run out
            {
                displayAlert(); // Display an alarm!
            }
        }
        break;
    }
}

// displayClock draws the current hour and minute in a HH:MM format.
// Our large font doesn't leave room for seconds, so we'll use seconds to
// blink the colon.
void displayClock(unsigned int hours, unsigned int minutes, unsigned int seconds)
{
    oled.clear(PAGE); // Clear the display
    oled.setFontType(3); // Switch to the large-number font
    oled.setCursor(0, 0); // Set the cursor to top-left

    // printWithLeadZero adds as many '0''s as required to fill out a number's
    // digit count. E.g., if hours = 7, this will print "07"
    // The second parameter defines the number of digits a number should fill.
    printWithLeadZero(hours, 2); // Print two-characters for hours

    // Next the colon (:), or not.
    if ((seconds % 2) == 0) // If seconds is even
        oled.print(":");    // Print a colon
    else                    // Otherwise
        oled.print("");    // Print a spaces

    // Another printWithLeadZero, this time for minutes.
    printWithLeadZero(minutes, 2);

    oled.display(); // Update the display
}

// displayStopWatch draws the number of hours, minutes, seconds, and milliseconds
// passed since the stopwatch started.
void displayStopWatch(unsigned int hour, unsigned int min, unsigned int sec, unsigned int ms)
{
    oled.clear(PAGE); // Clear the display
    oled.setFontType(2); // Set the font to big, seven-segment-style numbers
    oled.setCursor(0, 0); // Set the cursor to top-left
    printWithLeadZero(hour, 2); // Print the hour, with an extra '0' if necessary
    oled.print('.'); // Print a '.' (':' isn't specified in this font)
    printWithLeadZero(min, 2); // Print the minute value

    // Now move the cursor down a little more than the next line.
    oled.setCursor(0, oled.getFontHeight() + 3);
    oled.print('.'); // Print another '.'
    printWithLeadZero(sec, 2); // Print seconds, with lead zero if necessary

    oled.setFontType(1); // Change the font to medium-size
    printWithLeadZero(ms, 3); // Print the number of milliseconds (this time with 3 digits)

    oled.display(); // Update the display
}

void setTimer()
{
    unsigned int hour = 0;  // variable to keep track of timer hours
    unsigned int minute = 0; // variable to keep track of timer minutes

    // We're not going to use interrupts this time. Just going to
    // poll for button presses
    // To disable interrupts, call the noInterrupts() function.
    noInterrupts(); // Disable interrupts

    // Wait for both blue and yellow buttons to be released
    while (!digitalRead(STOP_BLUE_PIN) || !digitalRead(START_YELLOW_PIN))
        Particle.process(); // Maintain the cloud connection

    // Loop forever, until the green button is pressed.
    while(digitalRead(MODE_GREEN_PIN))
    {
        // If the yellow button is pressed (allowing for the software debounce)
        if (!digitalRead(START_YELLOW_PIN) && softwareDebounce())
            hour = (hour + 1) % 99; // Increment the hour value, don't let it go above 99
        // If the blue button is pressed (plus debounce time)
        if (!digitalRead(STOP_BLUE_PIN) && softwareDebounce())
            minute = (minute + 1) % 60; // Increment the minute value (don't exceed 60)

        // Draw the time we're setting:
        oled.clear(PAGE); // Clear display
        oled.setFontType(1); // Medium size font
        oled.setCursor(0, 0); // top-left cursor
        oled.print("TimerSt"); // Print a different heading, to show it's the setting timer mode
        oled.line(0, oled.getFontHeight(), oled.getLCDWidth(), oled.getFontHeight());

        oled.setFontType(2); // Medium-size 7-segment display font
        oled.setCursor(0, (oled.getLCDHeight() / 2));
        printWithLeadZero(hour, 2); // Draw the hours, with leading 0
        oled.print(".");
        printWithLeadZero(minute, 2); // Draw the minutes, with a leading 0
        oled.display(); // Update the display

        Particle.process(); // Maintain cloud connection
    }
    // Wait for the green button to be released (go HIGH)
    while (digitalRead(MODE_GREEN_PIN) == LOW)
        Particle.process(); // Maintain the cloud connection

    // Calculate the timerTime, in seconds, based on current time (Time.now())
    timerTime = Time.now() + (hour * 60 * 60) + (minute * 60);

    // We may have entered this mode accidentally. If the timer hasn't been set
    // Don't start it.
    if (timerTime != Time.now())
        settingTimer = false; // remain in setting timer mode

    interrupts(); // re-enable interrupts
}

// If hour is 0, then it'll draw HH:MM.
// If hour is greater than 0, it'll draw MM:SS
void displayTimer(unsigned int hour, unsigned int min, unsigned int sec)
{
    unsigned int first, second; // variables to track first and second digit
    // Figure out if we need to draw HH:MM or MM:SS
    if (hour != 0)
    {
        first = hour;
        second = min;
    }
    else
    {
        first = min;
        second = sec;
    }

    oled.clear(PAGE); // Clear display

    oled.setFontType(1); // Medium font
    oled.setCursor(0, 0); // Top-left cursor
    oled.print(" Timer "); // Draw "timer" heading
    oled.line(0, oled.getFontHeight(), oled.getLCDWidth(), oled.getFontHeight()); // Then a line

    oled.setFontType(2); // Medium, 7-segment number font
    oled.setCursor(0, (oled.getLCDHeight() / 2)); // Set cursor below about the middle-left
    printWithLeadZero(first, 2); // Print the first number
    // If we're display HH:MM, we'll blink the decimal point every other second:
    if (((Time.second() % 2) == 0) || hour == 0)
        oled.print(".");
    else // If we're displaying MM:SS, always display the .
        oled.print("");
    printWithLeadZero(second, 2); // Print the second number
    oled.display(); // Update the display
}

// Display an alarm, if the timer has ended.
void displayAlert()
{
    oled.clear(PAGE);  // Clear display
    oled.setFontType(1); // Medium size font (7 columns, 3 rows)
    oled.setCursor(0, 0); // Top-left cursor
    oled.print("!!!!!!!");
    oled.print("!ALARM!");
    oled.print("!!!!!!!");
    oled.display(); // update display
}

// Prints as many lead zero's as necessary given a number 'n', and a
// number of 'digits' it should fill out.
// E.g. printWithLeadZero(42, 3) will print "042"
// printWithLeadZero(7, 2); will print "07"
void printWithLeadZero(unsigned int n, int digits)
{
    for (int i=1; i<digits; i++) // Cycle through digit-1 times
    {
        if (n < (pow(10, i))) // If a number is less than 10^i
            oled.print("0"); // Print a leading zero
    }
    oled.print(n); // print the rest of the number
}

// softwareDebounce keeps our button from "bouncing" around. When a button is
// pressed, the signal tends to fluctuate rapidly between high and low.
// This function filters out high-frequency button presses, limiting
// them to debounceTime ms.
bool softwareDebounce()
{
    // If it's been at least debounceTime ms since the last press
    if (millis() > (debounceTime + lastPressTime))
    {
        lastPressTime = millis(); // update lastButtonPush
        return true;
    }
    // Otherwise return false.
    return false;
}

Before uploading the application, change the TIME_ZONE variable towards the top of the application to your location’s time zone, adjusted from UTC.

Include the SparkFunMicroOLED Library

To use the Micro OLED display, we’ll take advantage of the SparkFunMicroOLED library. Click over to the “Libraries”, search for SparkFunMicroOLED, and click INCLUDE IN APP.

Including the SparkFunMicroOLED library

Then click on your new DigitalWatch application, and click ADD TO THIS APP. An #include "SparkFunMicroOLED/SparkFunMicroOLED.h" line should be added to the top of your application.

What You Should See

The application begins in our digital clock state. Not much to see here, aside from the colon blinking every second. The current time is fetched from the Time.hour(), Time.minute() and Time.second() functions, so it should be well-synced with the Internet clock.

Press the green button to switch to the next clock display: a stopwatch. In this mode, the yellow button starts the counter, and the blue button stops and resets it.

Clicking mode again will set the clock into timer mode. Increment the number of hours by clicking the yellow button, and increment minutes by clicking blue. When your timer is all set, click the green button to start it. Now what this clock is really missing is a buzzer. Try adding one from experiment 5.

To set the timer, press both the yellow and blue buttons simultaneously to enter time-setting mode again.

Code to Note

Using External Interrupts

There are three external interrupts used in this application – one for each button. Implementing an interrupt is a two-step process: attaching the interrupt to a pin, and creating an ISR. To attach the interrupt, call the attachInterrupt() function, which takes three parameters:

  • Pin number: The pin number being monitored for state change.
  • ISR name: The specific name of the interrupt function to be called when an interrupt ocurrs.
  • Event: The pin-change event, which can be RISING, FALLING, or CHANGE. If set to change, the ISR will trigger whenever the pin state rises or falls.

For example, to attach an interrupt on the green, mode-changing pin, we call this attachInterrupt() function:

language:c
setup()
{
    ...
    pinMode(MODE_GREEN_PIN, INPUT_PULLUP); // Setup the MODE_PIN as an input, with pull-up.
    // Since it has a pull-up, the MODE_PIN will be LOW when the button is pressed.
    // Attach an interrupt to MODE_PIN, `changeMode` will be the name of the ISR,
    // setting the event to RISING means the ISR will be triggered when the button is released
    attachInterrupt(MODE_GREEN_PIN, changeMode, RISING);

    pinMode(START_YELLOW_PIN, INPUT_PULLUP); // Set the yellow button pin as an input, with pull-up
    attachInterrupt(START_YELLOW_PIN, startWatch, FALLING); // Call startWatch ISR whenever the pin goes low
    ...
}

Then, outside the setup() and loop() functions, we create an ISR with the same name as that in the ISR, changeMode.

language:c
// changeMode is attached to the MODE_PIN interrupt. It is configured to be called
// any time the pin falls (when the button is pressed).
void changeMode()
{
    // softwareDebounce() helps "debounce" our button pin. When a button is pressed,
    // signal noise, and mechanical imperfections can make it rise and fall many times
    // before holding low.
    // softwareDebounce() filters out the high-frequency button-press noise, so the ISR
    // only runs once when the button is pressed.
    if (softwareDebounce())
    {
        // Increment clockState by one, then use the mod operation (%)
        // to roll back to 0 if we increment past a defined state.
        // E.g., NUM_CLOCK_STATES is 3, so clockState will go 0, 1, 2, 0, 1, 2, ...
        clockState = (clockState + 1) % NUM_CLOCK_STATES;
    }
}

Note that our ISR doesn’t have any parameters, and it doesn’t return anything. Instead we usually use global variables to have our interrupts control the application.

Troubleshooting

If the buttons aren’t behaving as you’d like them to – whether they’re too sensitive, or not sensitive enough – you can try modifying the debounceTime variable near the top of the code. This sets the amount of milliseconds that must pass between button triggers.

Most of the time-keeping mechanisms in our clock are cloud-based. That means they’re very accurate, but won’t work if the Photon RedBoard isn’t connected to the Internet. If your Photon RedBoard’s LED isn’t pulsing cyan, that may be the reason your clock isn’t working (the dangers of Internet-ing everything!).

Part 2: Weather Forecasting Application

Now that you know a thing-or-two about hardware interrupts – and you’ve got a working OLED and button circuit – time to take it to the Internet! We’ve got components wired up to combine a user-interface (UI) with user-input, let’s use them to create a Weather Forecast App.

This experiment uses the OpenWeatherMapAPI, culling the amazing weather database for weather forecasts for anywhere in the world. With a simple HTTP GET request, we can find out the temperature, humidity, pressure, precipitation, wind, and other weather data, then display that data on the Micro OLED. And that weather data can be a current forecast, or future (hourly or daily) guesstimate.

Pre-Experiment Setup

To customize your experiment, there are a few pieces of data you’ll need to gather and stuff in your code.

Find Your City’s Geographic Coordinates

To predict the weather in your city, you’ll need to know its geographic coordinates. Don’t know your city’s latitude and longitude offhand? Use Google Maps. Here’s how:

  1. Go to Google Maps
  2. Search, scroll, or navigate to your location
  3. Right click, select “What’s here?”
  4. Check out the info card under the search box

Grabbing the lat and lon from google maps

If you’re curious what the weather at SparkFun HQ is, you can use lat, long = 40.090554, -105.184861.

Get an OpenWeatherMap API Key

To gather data from OpenWeatherMap, you’ll need an API key – a unique hashed string that identifies you. The key is free, but it limits you to 1,200 API calls per minute. Don’t spam their server!

Getting an OpenWeatherMap API key is simple:

  1. Register on the OpenWeatherMap sign up page
    • You’ll need to create a username/password, and supply your email address
  2. Find the API key under the “Setup” tab in your account

Getting an OpenWeatherMap API Key

Currently the experiment does work without an API key, but it’s best to go about this on the up-and-up.

New Photon Code

Here’s the main application code. Create a new application – we’ve titled ours OLED-WeatherApp– and paste this in.

language:c
////////////////////////////
// Button Pin Definitions //
////////////////////////////
#define GREEN_BUTTON  D2 // Green button switches through display pages
#define BLUE_BUTTON   D3 // Blue button updates future forecast
#define YELLOW_BUTTON D4 // Yellow butotn updates current forecast

//////////////////////////
// MicroOLED Definition //
//////////////////////////
#define PIN_OLED_RST D6 // Connect RST to pin 7 (req. for SPI and I2C)
#define PIN_OLED_DC  D5 // Connect DC to pin 3 (required for SPI)
#define PIN_OLED_CS  A2 // Connect CS to pin A2 (required for SPI)
MicroOLED oled(MODE_SPI, PIN_OLED_RST, PIN_OLED_DC, PIN_OLED_CS);

//////////////////////////////
// OpenWeatherMap Variables //
//////////////////////////////
// OpenWeathermap API key. Get one (for free) by signing up here:
//                   http://home.openweathermap.org/users/sign_up
const String OPENWEATHER_API_KEY = "";
// Forecast location declaration (currently set for Niwot, CO - SparkFun HQ!):
const float LATITUDE = 40.090554; // Weather forecast location's latitude
const float LONGITUDE = -105.184861; // Weather forecast location's longitude
// Create an OpenWeather object, giving it our API key as the parameter:
OpenWeather weather(OPENWEATHER_API_KEY);

///////////////////////////
// Future Forecast Order //
///////////////////////////
// These variables control how many future forecasts our app will do, and their order.
const int FUTURE_FORECAST_NUM = 6; // Number of future forecasts
// forecastOrder defines the order of forecasts. Each value is the number of
// hours in the future to forecast. It should be multiples of 3. Once you get
// to 24, it should be multiples of 24.
const int forecastOrder[FUTURE_FORECAST_NUM] = {3, 6, 9, 24, 48, 72};
// forecastOrderIndex keeps track of our position in the forecastOrder array
volatile int forecastOrderIndex = FUTURE_FORECAST_NUM - 1;

///////////////////////////////
// Display Mode Page Control //
///////////////////////////////
// This enum defines all of our display modes and their order.
enum t_displayModes {
    DISPLAY_BIG_3,                    // avg temperature, humidity, and pressure
    DISPLAY_TEMPERATURES,             // min, max, and avg temperatures
    DISPLAY_WIND,                     // any wind conditions, speed, and direction.
    DISPLAY_PRECIPITATION,            // precipitation (if any)
    DISPLAY_WEATHER_CONDITION_NAME,   // descripton of the weather
    DISPLAY_WEATHER_CONDITION_SYMBOL, // a graphic for the weather
    DISPLAY_UPDATE_TIME               // the forecast's time
};
const int NUM_DISPLAY_MODES = 7; // Number of values defined in enum above
volatile int displayMode = NUM_DISPLAY_MODES - 1; // Keeps track of current display page
#define DISPLAY_UPDATE_RATE 10 // Cycle display every 10 seconds
int lastDisplayUpdate = 0; // Stores time of last display update

/////////////////////
// Interrupt Flags //
/////////////////////
// These boolean flags help us keep track of what needs to happen after
// we execute an ISR.
volatile bool doUpdateWeather = true; // If true, loop should update the current weather
volatile bool doFutureUpdate = false; // If true, loop should update future weather
volatile bool doUpdateDisplay = false; // If true, loop should update the display

///////////////////////////////
// Software Debounce Control //
///////////////////////////////
unsigned long lastButtonPush = 0; // Stores the time of the last button press
const int BUTTON_DEBOUNCE_TIME = 200; // Minimum time (in ms) between button presses

// ISR: updateWeather - sets a flag to have loop() get the current weather
// forecast. Also prints a message to the OLED to indicate as much.
void updateWeather()
{
    // softwareDebounce() (defined at the bottom of the application code)
    // makes sure button presses can only occur every BUTTON_DEBOUNCE_TIME ms.
    if (softwareDebounce())
    {
        oled.clear(PAGE); // Clear the display
        oled.setFontType(0); // Smallest font
        oled.setCursor(0, 0); // Cursor to top-left
        // multiLinePrint takes a long string, and tries to break it into multiple
        // lines - split by spaces in the string.
        multiLinePrint("Updating Current Weather!");
        oled.display(); // Update the display

        // Now set doUpdateWather to true, so our loop() knows to get the
        // current weather update next time it runs through.
        doUpdateWeather = true;
        // Play with displayMode's value so the app doesn't skip to the
        // next page.
        displayMode = (displayMode + FUTURE_FORECAST_NUM - 1) % FUTURE_FORECAST_NUM;
    }
}

// ISR: updateFuture - sets a flag to have loop() get a future forecast
// Also prints a message to the OLED to indicate as much.
void updateFuture()
{
    if (softwareDebounce()) // If it's been BUTTON_DEBOUNCE_TIME since the last press
    {

        forecastOrderIndex = (forecastOrderIndex + 1) % FUTURE_FORECAST_NUM;

        oled.clear(PAGE); // Clear the display
        oled.setFontType(0); // Set font to smallest type
        oled.setCursor(0, 0); // Set cursor to top-left
        multiLinePrint("Updating Future Forecast!"); // Print the message
        oled.println(); // Print a blank line
        // Now let's print the future time in hours or days of the forecast:
        if (forecastOrder[forecastOrderIndex] < 24) // Less than 24 hours?
            oled.println(String(forecastOrder[forecastOrderIndex]) + " hrs"); // Print # of hours
        else // Otherwise
            oled.println(String(forecastOrder[forecastOrderIndex] / 24) + " day(s)"); // Print number of days
        oled.display(); // Update the display

        // Now set doFutureUpdate to true, so our loop() knows to get the
        // future weather update next time it runs through.
        doFutureUpdate = true;
        // Play with displayMode's value so the app doesn't skip to the
        // next page.
        displayMode = (displayMode + FUTURE_FORECAST_NUM - 1) % FUTURE_FORECAST_NUM;
    }
}

// ISR: forwardDisplayMode - cycles through pages in the display.
void forwardDisplayMode()
{
    if (softwareDebounce()) // If it's been BUTTON_DEBOUNCE_TIME since the last press
    {
        // set doDisplayUpdate flag, so loop() knows to update the display
        // next time through.
        doUpdateDisplay = true;
    }
}

void setup()
{
    // Set up our button pin modes, and tie them to interrupts.
    // For all of these buttons, set the interrupt event to RISING.
    // Since they're active-low, that'll mean they trigger when
    // the button is released.
    // YELLOW_BUTTON: updates the current weather forecast
    pinMode(YELLOW_BUTTON, INPUT_PULLUP);
    attachInterrupt(YELLOW_BUTTON, updateWeather, RISING);
    // BLUE_BUTTON: updates a future weather forecast
    pinMode(BLUE_BUTTON, INPUT_PULLUP);
    attachInterrupt(BLUE_BUTTON, updateFuture, RISING);
    // GREEN_BUTTON: cycles our display to the next page
    pinMode(GREEN_BUTTON, INPUT_PULLUP);
    attachInterrupt(GREEN_BUTTON, forwardDisplayMode, RISING);

    // OpenWeatherMap configuration: set the units to either IMPERIAL or METRIC
    // Which will set temperature values to either farenheit or celsius
    weather.setUnits(IMPERIAL); // Set temperature units to farenheit

    // Display set up:
    oled.begin(); // Initialize the display.
    oled.clear(PAGE); // Clear the display
    oled.setFontType(0); // Set font to smallest type
    oled.setCursor(0, oled.getLCDHeight()/2); // Set font cursor to middle-left
    oled.println("Connecting"); // Display the start-up message
    oled.display(); // Update the display
}

// loop() is mostly just a series of flag checks. If a flag was set by an interrupt,
// loop() has to take an action, make sure to clear the flag afterwards.
void loop()
{
    // If the yellow button is pressed and released, doUpdateWeather should be
    // true. If it is, we'll try to update the weather.
    if (doUpdateWeather)
    {
        // Use the OpenWeather class's current(<lat>, <lon>) function to
        // update the current weather forecast.
        // If current() succeeds, it'll return 1, and update a number of
        // get function return values we can then use.
        if (weather.current(LATITUDE, LONGITUDE))
        {
            // Clear the doUpdateWeather flag, so this doesn't run again
            // until the button is pressed.
            doUpdateWeather = false;
            // Set the doUpdateDisplay flag, so loop() updates the display
            // when it gets to that point.
            doUpdateDisplay = true;
        }
        else
        {   // If the weather update fails
            delay(1000); // Delay 1 second
            // Dont' clear the doUpdateWeather flag, so we'll try again
        }
    }

    // If the blue button is pressed and released, doFutureUpdate should be
    // true. If it is, we'll try to update the future forecast.
    if (doFutureUpdate)
    {
        // Future forecasts can either be daily or hourly. If our position in
        // the forecastOrder array points to a number less than 24 hours do an hourly forecast
        if (forecastOrder[forecastOrderIndex] < 24)
        {
            // To do an hourly forecast use the hourly(<lat>, <lon>, <hours/3>) function.
            // We can only forecast in 3-hour intervals. So, for example, to forecast out
            // 9 hours, call hourly(LATITUDE, LONGITUDE, 3); where 3 = 9/3
            if (weather.hourly(LATITUDE, LONGITUDE, (forecastOrder[forecastOrderIndex] / 3) + 1))
            {
                // If the hourly forecast succeeds
                doFutureUpdate = false; // Clear the doFutureUpdate flag
                doUpdateDisplay = true; // Set the doUpdateDisplay flag
            }
            else
            {
                // If the hourly forecast failed
                delay(1000); // Delay 1 second, then try again next time through the loop()
            }
        }
        else
        {
            // Daily forecasts can be scraped using the daily(<lat>, <lon>, <days - 1>) funciton.
            // If you want today's daily forecast (different from current) call daily(LATITUDE, LONGITUDE, 1);
            // For tomorrow's forecast, call daily(LATITUDE, LONGITUDE, 2), etc.
            // Since forecastOrder points to a number of hours, divide them by 24 to get the days
            if (weather.daily(LATITUDE, LONGITUDE, (forecastOrder[forecastOrderIndex] / 24) + 1))
            {
                // If the daily forecast succeeds:
                doFutureUpdate = false; // Clear the doFutureUpdate flag
                doUpdateDisplay = true; // Set the doUpdateDisplay flag
            }
            else
            {
                // If the daily update fails
                delay(1000); // delay for 1 second, then try again.
            }
        }
    }

    // There are two cases where we need to update the app's display:
    //  1. If the doUpdateDisplay was set by an interrupt or other function.
    //  2. Every DISPLAY_UPDATE_RATE seconds, the display will cycle itself
    if ((Time.now() >= (lastDisplayUpdate + DISPLAY_UPDATE_RATE)) || doUpdateDisplay)
    {
        // Increment displayMode, next time through a new page will be displayed:
        displayMode = (displayMode + 1) % NUM_DISPLAY_MODES;

        updateWeatherDisplay(); // This function draws the active weather data page

        // Update lastDisplayTime, so we don't come back for DISPLAY_UPDATE_RATE seconds
        lastDisplayUpdate = Time.now();
        doUpdateDisplay = false; // Clear the doUpdateDisplay flag, in case that's what triggerd
    }

    displayProgressBar(); // Draws a progress bar at the bottom of the screen
}

// updateWeatherDisplay uses the displayMode variable to draw one of our weather
// apps pages of data.
void updateWeatherDisplay()
{

    oled.clear(PAGE); // clear the display, our display functions won't

    switch (displayMode)
    {
    case DISPLAY_BIG_3: // Display temperature, humidity, and pressure
        displayBig3();
        break;
    case DISPLAY_TEMPERATURES: // Displays average, min, and max temps
        displayTemperatures();
        break;
    case DISPLAY_WIND: // Displays any wind characteristics
        displayWind();
        break;
    case DISPLAY_PRECIPITATION: // Displays any rain/snow
        displayPrecipitation();
        break;
    case DISPLAY_WEATHER_CONDITION_NAME: // Displays weather conditions
        displayWeatherConditionName();
        break;
    case DISPLAY_WEATHER_CONDITION_SYMBOL: // Displays weather picture
        displayWeatherConditionSymbol();
        break;
    case DISPLAY_UPDATE_TIME: // Displays the forecast update time
        displayUpdateTime();
        break;
    }

    oled.display(); // Actually draw the display (our display functions won't)
}

void displayBig3()
{
    oled.setCursor(0, 0); // Set the cursor to top-left
    oled.setFontType(1); // Set font size to "medium"
    // To get the latest temperature, humidity, and pressure values
    // from our OpenWeather class. Use the temperature(), humidity(),
    // and pressure() functions.
    oled.println(String(weather.temperature(), 1) + " F"); // Print temperature
    oled.println(String(weather.humidity()) + "% RH"); // Print humidity
    oled.println(String((unsigned int) weather.pressure()) + "hPa"); // Print pressure
}

void displayTemperatures()
{
    // printHeading is defined toward the bottom of the application code.
    // It prints a title with a line under it. It returns the vertical cursor position
    int cursorY = printHeading("   Temps  "); // Print Temps, with a line under it

    oled.setFontType(0); // set font size to smallest
    oled.setCursor(0, cursorY); // Set cursor for the rest of our text

    // The average, maximum, and minimum forecast temperatures can be retreived
    // using the temperature(), maxTemperature(), and minTemperature() functions.
    // All three return float values.
    oled.print("Avg: " + String(weather.temperature(), 1)); // Print average temperature

    cursorY += oled.getFontHeight() + 1;
    oled.setCursor(0, cursorY); // Move the cursor a little more than one character-height down
    oled.print("Max: " + String(weather.maxTemperature(), 1)); // Print maximum temperature

    cursorY += oled.getFontHeight() + 1;
    oled.setCursor(0, cursorY); // Move the cursor a little more than one character-height down
    oled.print("Min: " + String(weather.minTemperature(), 1)); // Print minimum temperature
}

void displayWind()
{
    int cursorY = printHeading("   Wind   "); // Print the "Wind" heading

    oled.setFontType(0); // Smallest font
    oled.setCursor(0, cursorY); // Use return from printHeading to set our cursor Y position

    // The wind forecast includes wind speed (in meters per second), direction, and a
    // descriptive name (like "light breeze"). To get those values call either:
    // - windSpeed() -- a float variable, e.g. 3.14
    // - windDirection() -- a String variable, e.g. "W", or "SE"
    // - windName() -- a String variable, e.g. "light breeze"

    // Print wind speed and compass direction
    oled.print(String(weather.windSpeed(), 2) + ' ' + weather.windDirection());

    cursorY += oled.getFontHeight() + 3;
    oled.setCursor(0, cursorY); // Move cursor down font height + 3.
    multiLinePrint(weather.windName()); // Print the wind's description
}

void displayPrecipitation()
{
    int cursorY = printHeading("  Precip  "); // Print "precip" heading

    oled.setFontType(1); // Medium-size font
    oled.setCursor(0, cursorY); // Set cursor based on return from printHeading

    // To get the precipitation forecast, call precipitationType(), which may return
    // "Rain", "Snow", or NULL if there is not precipitation in the forecast
    // If there is precipitation, you can find the amount by calling
    // precipitationValue(), which returns a the amount of precipitation in mm (?)
    if (weather.precipitationType() == NULL)
    {
        // If there is no precipitation forecasted, print "None"
        oled.print(" None  ");
    }
    else
    {
        // If precip is forecasted, use multiLinePrint to print the type
        // of precip (rain, snow, etc.)
        multiLinePrint(weather.precipitationType());
        // Then print the amount forecasted:
        oled.print(String(weather.precipitationValue(), 2) + "mm");
    }
}

void displayWeatherConditionName()
{
    int cursorY = printHeading("Conditions"); // Print "conditions" heading.

    oled.setFontType(0); // Smallest font
    oled.setCursor(0, cursorY); // Cursor just below heading line

    // Weather conditions, like "light rain", "haze", "broken clouds", etc.
    // can be retrieved using the conditionName() function, which returns a String
    multiLinePrint(weather.conditionName()); // Print condition name broken up by space
}

// Draw bitmap based on weather condition code.
// More info here: http://openweathermap.org/weather-conditions
void displayWeatherConditionSymbol()
{
    // There are tons of weather condition codes defined by OpenWeatherMap. Listed here:
    // http://openweathermap.org/weather-conditions
    // E.g. 211=thunderstorm, 781=tornado, 800=clear sky
    // Groupings of weather ID's all map to a certain picture.
    // use the conditionID() function to get the weather code, an integer
    int weatherCode = weather.conditionID();

    // Then map that integer to a weather symbol to be drawn
    if ((weatherCode >= 200) && (weatherCode <= 232)) // Thunderstorm
        oled.drawBitmap(bmp_thunderstorm);
    else if ((weatherCode >= 300) && (weatherCode <= 321)) // Drizzle
        oled.drawBitmap(bmp_shower_rain);
    else if ((weatherCode >= 500) && (weatherCode <= 531)) // Rain
        oled.drawBitmap(bmp_shower_rain);
    else if ((weatherCode >= 600) && (weatherCode <= 622)) // Snow
        oled.drawBitmap(bmp_snow);
    else if ((weatherCode >= 701) && (weatherCode <= 781)) // Atmosphere
        oled.drawBitmap(bmp_haze);
    else if (weatherCode == 800) // Clear sky
        oled.drawBitmap(bmp_clear_sky);
    else if (weatherCode == 801) // Few clouds
        oled.drawBitmap(bmp_few_clouds);
    else if (weatherCode == 802) // Scattered clouds
        oled.drawBitmap(bmp_scattered_clouds);
    else if ((weatherCode == 803) || (weatherCode == 804)) // Broken or overcast
        oled.drawBitmap(bmp_broken_clouds);
    else
    {
        //! TODO: could be tornado, tropical storm, hurricane, cold, hot, windy
        // hail, calm, breezy, gales, storms.
        // Use the LCDAssistant program to make more bitmaps.
    }
}

void displayUpdateTime()
{
    int cursorY = printHeading(" Time/Day "); // Print time/day heading

    oled.setFontType(0); // Smallest font
    oled.setCursor(0, cursorY); // Cursor just below the heading line

    // The day of a weather forecast can be retreived using the getDate()
    // function, which is a string.
    // It'll be in the formware of YYYY-MM-DD, e.g. 2015-09-04
    // 10 characters! perfect for the 0 font.
    oled.print(weather.getDate());  // Print the date

    cursorY += oled.getFontHeight() + 3;
    oled.setCursor(0, cursorY); // Move text cursor down a little extra

    // The time of a weather forecast is available with the getTime() funciton.
    // This function also returns a String, in the format of HH:MM:SS,
    // e.g. 18:42:00
    oled.println(weather.getTime()); // Print the forecast time
}

// This is a handy function that takes a long-ish string, finds the spaces
// and prints each word line-by-line.
// It's not smart enough to print two small words on the same line, but it could be!
void multiLinePrint(String s)
{
    String split = s; // We'll continut to split the split String until it's just one word

    // Loop until the our split String is small enough to fit on a single line.
    // Divide the LCD's pixel width (64) by the width of a character+1 to get
    // characters per line.
    while (split.length() >= (oled.getLCDWidth() / (oled.getFontWidth() + 1)))
    {
        // Use the indexOf String function to find a space (' ').
        int space = split.indexOf(' ');
        if (space > 0) // If there is a space, indexOf will return it's position.
        {
            // print from the beginning of our split string to the character
            // before the space.
            oled.println(split.substring(0, space));
        }
        else // Otherwise, if there is no space, it'll return a negative number
        {    // If there are no spaces left
            break;  // break out of the while loop
        }
        // Shorten up the split string, get rid of everything we've printed,
        // plus the space. Then loop back through.
        split = split.substring(space + 1);
    }
    // Print the last bit of the split string.
    oled.println(split);
}

// Print heading takes a string, prints it out at the top of the display,
// then prints a line under that string.
// It'll return the y-cursor position 3 pixels under the line.
int printHeading(String heading)
{
    oled.setFontType(0); // Smallest font type
    oled.setCursor(0, 0); // Top-left cursor position
    oled.print(heading); // Print the heading text

    int vPos = oled.getFontHeight() + 3; // Calculate the y-value of our line
    oled.line(0, vPos, oled.getLCDWidth(), vPos); // Draw the line horizontally

    return vPos + 3; // Return the y-cursor position
}

// This function draws a line at the very bottom of the screen showing how long
// it'll be before the screen updates.
void displayProgressBar()
{
    // Use lastDisplayUpdate's time, the current time (Time.now()) and the
    // total time per page (DISPLAY_UPDATE_RATE) to calculate what portion
    // of the display bar needs to be drawn.
    float percentage = (float)(Time.now() + 1 - lastDisplayUpdate) / (float)DISPLAY_UPDATE_RATE;
    // Mutliple that value by the total lcd width to get the pixel length of our line
    int progressWidth = percentage * oled.getLCDWidth();
    // the y-position of our line should be at the very bottom of the screen:
    int progressY = oled.getLCDHeight() - 1;

    // First, draw a blank line to clear any old lines out:
    oled.line(0, progressY, oled.getLCDWidth(), progressY, BLACK, NORM);
    // Next, draw our line:
    oled.line(0, progressY, progressWidth, progressY);
    oled.display(); // Update the display
}

// softwareDebounce keeps our button from "bouncing" around. When a button is
// pressed, the signal tends to fluctuate rapidly between high and low.
// This function filters out high-frequency button presses, limiting
// them to BUTTON_DEBOUNCE_TIME ms.
bool softwareDebounce()
{
    // If it's been at least BUTTON_DEBOUNCE_TIME ms since the last press
    if (millis() > (lastButtonPush + BUTTON_DEBOUNCE_TIME))
    {
        lastButtonPush = millis(); // update lastButtonPush
        return true;
    }
    // Otherwise return false.
    return false;
}

Include the SparkFunMicroOLED Library

You’re probably used to this now. Follow the same set of steps from the first part of this experiment to include the SparkFunMicroOLED library into your application. It should add a…

#include "SparkFunMicroOLED/SparkFunMicroOLED.h"`

…line to the top of your application.

Adding Additional Files

Let’s throw another wrench into this experiment. Instead of including another library to handle the OpenWeatherMap API – but to still keep our main main code as clean as possible – let’s add additional files to our application.

To add additional files to an application, click the “+” icon in the very upper-right-hand corner of the code window.

Adding files to an application

After clicking the plus, two new tabs will appear at the top of the code window – one .cpp and one .h file. Name these files “OpenWeatherMap”.

New files created

Then, paste this code into OpenWeatherMap.h

language:c
/*  OpenWeatherMap.h
    Jim Lindblom <jim@sparkfun.com>
    August 31, 2015

    This is a simple library for interacting with the
    OpenWeatherMap API:
    http://openweathermap.org/api

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon

    Distributed as-is; no warranty is given.
*/

#include "application.h"

enum t_forecast_type {
  CURRENT_FORECAST,
  HOURLY_FORECAST,
  DAILY_FORECAST
};

struct t_forecast_symbol {
  int number;
  String name;
};

struct t_forecast_precip {
  String value;
  String type;
};

struct t_forecast_wind {
  String dirDeg;
  String dirCode;
  String dirName;
  String speedValue;
  String speedName;
};

struct t_forecast_temperature {
  String value;
  String minT;
  String maxT;
  String nightT;
  String eveT;
  String mornT;
  String unit;
};

struct t_forecast_pressure {
  String unit;
  String value;
};

struct t_forecast_humidity {
  String value;
  String unit;
};

struct t_forecast_clouds {
  String value;
  String all;
  String unit;
};

struct t_current_visibility {
  String value;
};

struct t_forecast {
  t_forecast_type type;
  String day;
  t_forecast_symbol symbol;
  t_forecast_precip precip;
  t_forecast_wind wind;
  t_forecast_temperature temperature;
  t_forecast_pressure pressure;
  t_forecast_humidity humidity;
  t_forecast_clouds clouds;
  t_current_visibility vis;
};

enum t_forecast_units
{
  IMPERIAL,
  METRIC
};

// Bitmaps downloaded/converted here: http://luc.devroye.org/fonts-71141.html
// http://luc.devroye.org/KickstandApps-WeatherIcons-2013.png

const uint8_t bmp_few_clouds [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF0, 0xFF, 0xFF, 0xFC, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0,
0xF8, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x07, 0x1F, 0x3E, 0x7E, 0x7C, 0x3C, 0x18, 0x00, 0x80, 0xC0, 0xE0, 0xF0,
0xF0, 0xF9, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0xF0, 0xF0, 0xE1, 0xE3, 0xC7, 0x87,
0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0,
0xE0, 0xF0, 0xF8, 0xF8, 0x7C, 0x3C, 0x3E, 0x1E, 0x1F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x1F, 0x1E, 0x3E, 0x3C, 0x7C, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x01, 0x03, 0x07, 0x3F, 0xFF,
0xFF, 0xFC, 0xC0, 0x00, 0x03, 0x0F, 0x0F, 0x0F, 0x07, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0x3E, 0x3E, 0x1E, 0x1F, 0x0F, 0x0F,
0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xE3, 0xF7, 0xFF, 0xFF, 0xFF, 0xF8, 0xF0, 0xF0, 0xFE, 0xFF,
0xFF, 0x9F, 0x01, 0x00, 0xE0, 0xF8, 0xF8, 0xF8, 0xF0, 0xE0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7E, 0xFF, 0xFF, 0xFF, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07,
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x3F, 0x7C, 0x7C, 0x78, 0xF8, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x78, 0x7C, 0x7E,
0x3F, 0x1F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const uint8_t bmp_clear_sky [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE, 0xFE, 0xFC, 0xF0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0x0E, 0x3E, 0x7C, 0xFC, 0x7C, 0x38, 0x00, 0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF1, 0xF1, 0x79, 0x78,
0x78, 0x78, 0x78, 0x78, 0xF0, 0xF0, 0xF0, 0xE3, 0xC3, 0xC7, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xC0, 0xC0, 0xC0, 0xE0,
0xE0, 0x00, 0x00, 0x00, 0xC0, 0xFC, 0xFE, 0xFF, 0x1F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x3F, 0xFF, 0xFE, 0xF8, 0x80, 0x00, 0x07,
0x1F, 0x1F, 0x0F, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x07,
0x07, 0x00, 0x00, 0x00, 0x03, 0x1F, 0x7F, 0xFF, 0xFC, 0xF0, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0xF0, 0xFE, 0xFF, 0x7F, 0x1F, 0x01, 0x00, 0xE0,
0xF8, 0xF8, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0,
0x70, 0x7C, 0x3E, 0x3F, 0x3E, 0x1C, 0x00, 0x00, 0x01, 0x03, 0x07, 0x07, 0x8F, 0x8F, 0x8F, 0x0F,
0x1E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0xC7, 0xC3, 0xE1, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7F, 0x7F, 0x3F, 0x0F,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const uint8_t bmp_scattered_clouds [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8,
0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xC0, 0xC0, 0xC0, 0xE0, 0xF8,
0xFC, 0xFE, 0xFF, 0x7F, 0x1F, 0x0F, 0x0F, 0x07, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03, 0x07, 0x07, 0x0F, 0x1F, 0x3F, 0xFF, 0xFE, 0xFC,
0xF8, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xC0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, 0x3F, 0x1F, 0x1F, 0x0F, 0x0F, 0x07, 0x07, 0x07,
0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xE0, 0xF0, 0xF0, 0xF1, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x07, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xE0,
0x00, 0x07, 0x1F, 0x3F, 0xFF, 0xFF, 0xFE, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x80, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xC0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x1F, 0x1F,
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00
};

const uint8_t bmp_broken_clouds [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0, 0xE0,
0xC0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF0, 0xF0, 0x78, 0x7C, 0x7E, 0x1F,
0x0F, 0x07, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03,
0x03, 0x0F, 0x1F, 0xFF, 0xFC, 0xF8, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0,
0xE0, 0xF0, 0xF0, 0x78, 0x7E, 0x7F, 0x7F, 0x7F, 0x7B, 0x79, 0x70, 0xF0, 0xF0, 0xF0, 0xE0, 0xC0,
0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x07, 0x0F, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x1F, 0xFE, 0xFC, 0xF8, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0x78, 0x78, 0x3C, 0x3E, 0x3F, 0x0F, 0x07, 0x03,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x81, 0x87,
0xCF, 0xFF, 0xFE, 0xFC, 0xF0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF8, 0x7F, 0x3F, 0x1F, 0x03, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03,
0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x0F, 0xFF, 0xFF, 0xFD, 0xE1, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x0F, 0x1F, 0x3F, 0x3E, 0x78, 0x78, 0x70, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x70, 0x78, 0x7C, 0x3F, 0x1F, 0x0F, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const uint8_t bmp_shower_rain [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0x78, 0x7C, 0x3C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x3C, 0x3C, 0x7C, 0xF8, 0xF0, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF0, 0xF8, 0xF8, 0x7C, 0x3E,
0x1E, 0x1E, 0x1F, 0x1F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC1, 0xE7, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0,
0xE0, 0xE0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01,
0x01, 0x01, 0x03, 0x0F, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x3F, 0x7C, 0x78,
0xF8, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFC,
0xFC, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0,
0xF0, 0xF8, 0x7C, 0x3F, 0x3F, 0x1F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x87,
0x87, 0x07, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x1F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF,
0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x1F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const uint8_t bmp_thunderstorm [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0x78, 0x7C, 0x3C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x3C, 0x3C, 0x7C, 0xF8, 0xF0, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF0, 0xF8, 0xF8, 0x7C, 0x3E,
0x1E, 0x1E, 0x1F, 0x1F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC1, 0xE7, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0,
0xE0, 0xE0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01,
0x01, 0x01, 0x03, 0x0F, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x3F, 0x7C, 0x78,
0xF8, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFC, 0xFC, 0x0C, 0x00, 0x00,
0x00, 0x80, 0xE0, 0xF8, 0x7C, 0x1C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF8, 0x7C, 0x3F, 0x3F, 0x1F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x9F, 0xFF, 0xF8, 0xF8, 0xC0, 0xE0,
0xF2, 0x7B, 0x3F, 0x1F, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const uint8_t bmp_snow [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0x78, 0x3C, 0x3C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
0x0E, 0x1E, 0x1E, 0x1E, 0x1E, 0x3E, 0x3C, 0x7C, 0xF8, 0xF0, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF0, 0xF8, 0xF8, 0x7C, 0x3E,
0x1E, 0x1E, 0x1F, 0x0F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE1, 0xE7, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0,
0xE0, 0xE0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01,
0x01, 0x01, 0x03, 0x0F, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x3F, 0x7C, 0x78,
0xF8, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x37, 0xBE, 0xFC,
0x7E, 0x7F, 0xD9, 0x98, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF8, 0x7C, 0x3F, 0x3F, 0x1F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x80, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x03, 0x01,
0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x80, 0x98, 0xF8, 0xE0, 0xE0, 0xF8, 0x98, 0x80, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 0x66, 0x76, 0x3F, 0x1F, 0x7E, 0xFF, 0x0D, 0x0C, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x1D, 0x0F, 0x07, 0x07, 0x0F, 0x1D, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const uint8_t bmp_haze [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF0, 0xFE,
0xFF, 0xFC, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x1F,
0x7F, 0xFE, 0xFE, 0xFC, 0x78, 0x30, 0x00, 0x00, 0x00, 0x80, 0x80, 0xC0, 0xC0, 0xC3, 0xC3, 0xC3,
0xC3, 0xC3, 0xC3, 0xC0, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x10, 0x38, 0x78, 0xFC, 0xFE, 0xFF,
0x3F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
0x00, 0x01, 0xC0, 0xE0, 0xF8, 0xFC, 0xFE, 0x7F, 0x3F, 0x1F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x1F, 0x1F, 0x7F, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0x81, 0x00,
0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x07, 0x07, 0x0F, 0x0F, 0x1F, 0x1F, 0x1F, 0x07, 0x00, 0x00,
0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x67, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0x47, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};


class OpenWeather
{
public:
  OpenWeather();
  OpenWeather(String apiKey);

  int current(float latitude, float longitude);
  int current(String cityName);
  int current(uint32_t cityID);

  int hourly(float lat, float lon, unsigned int tripleHours);
  int hourly(String cityName, unsigned int tripleHours);
  int hourly(uint32_t cityID, unsigned int tripleHours);

  int daily(float lat, float lon, unsigned int days);

  void setUnits(t_forecast_units units);

  float temperature();
  float maxTemperature();
  float minTemperature();
  unsigned int humidity();
  float pressure();

  String getDate();
  String getTime();

  float windSpeed();
  String windDirection();
  String windName();

  float precipitationValue();
  String precipitationType();

  int conditionID();
  String conditionName();

  t_forecast forecast;

private:
  String _apiKey;
  t_forecast_units _units;

  int currentForecast(String location);
  int hourlyForecast(String location, unsigned int count);
  int dailyForecast(String location, unsigned int count);

  String parseXML(String * search, String tag, String attribute);
  int tagIndex(String * xml, String tag, bool start);

  void printDebug();
};

And paste this code into OpenWeatherMap.cpp

language:c
/*  OpenWeatherMap.cpp
    Jim Lindblom <jim@sparkfun.com>
    August 31, 2015

    This is a simple library for interacting with the
    OpenWeatherMap API:
    http://openweathermap.org/api

    Development environment specifics:
    Particle Build environment (https://www.particle.io/build)
    Particle Photon

    Distributed as-is; no warranty is given.
*/

#include "OpenWeatherMap.h"

String weatherServer = "api.openweathermap.org";

OpenWeather::OpenWeather()
{
  _apiKey = NULL;
  _units = IMPERIAL;
}

OpenWeather::OpenWeather(String apiKey)
{
  _apiKey = apiKey;
}

void OpenWeather::setUnits(t_forecast_units units)
{
  _units = units;
}

int OpenWeather::current(float lat, float lon)
{
  return currentForecast("lat=" + String(lat) + "&lon=" + String(lon));
}

int OpenWeather::current(String cityName)
{
  return currentForecast("q=" + cityName);
}

int OpenWeather::current(uint32_t cityID)
{
  return currentForecast("id=" + String(cityID));
}

int OpenWeather::hourly(float lat, float lon, unsigned int tripleHours)
{
  return hourlyForecast("lat=" + String(lat) + "&lon=" + String(lon), tripleHours);
}

int OpenWeather::hourly(String cityName, unsigned int tripleHours)
{
  return hourlyForecast("q=" + cityName, tripleHours);
}

int OpenWeather::hourly(uint32_t cityID, unsigned int tripleHours)
{
  return hourlyForecast("id=" + String(cityID), tripleHours);
}

int OpenWeather::daily(float lat, float lon, unsigned int days)
{
  if (days > 10)
    return -1;
  return dailyForecast("lat=" + String(lat) + "&lon=" + String(lon), days);
}

float OpenWeather::temperature()
{
  return forecast.temperature.value.toFloat();
}

float OpenWeather::maxTemperature()
{
  return forecast.temperature.maxT.toFloat();
}

float OpenWeather::minTemperature()
{
  return forecast.temperature.minT.toFloat();
}

unsigned int OpenWeather::humidity()
{
  return forecast.humidity.value.toInt();
}

float OpenWeather::pressure()
{
  return forecast.pressure.value.toFloat();
}

String OpenWeather::getDate()
{
  int timeStart = forecast.day.indexOf('T');

  return forecast.day.substring(0, timeStart);
}

String OpenWeather::getTime()
{
  if (forecast.type == DAILY_FORECAST)
    return NULL;

  int timeStart = forecast.day.indexOf('T');
  return forecast.day.substring(timeStart + 1);
}

float OpenWeather::windSpeed()
{
  return forecast.wind.speedValue.toFloat();
}

String OpenWeather::windDirection()
{
  return forecast.wind.dirCode;
}

String OpenWeather::windName()
{
  return forecast.wind.speedName;
}

String OpenWeather::precipitationType()
{
  if ((forecast.precip.type == "no") || (forecast.precip.type == NULL))
  {
    return NULL;
  }
  return forecast.precip.type;
}

float OpenWeather::precipitationValue()
{
  return forecast.precip.value.toFloat();
}

int OpenWeather::conditionID()
{
  return forecast.symbol.number;
}

String OpenWeather::conditionName()
{
  return forecast.symbol.name;
}

int OpenWeather::currentForecast(String location)
{
  /////////////////////////////////////////
  // Connect to client and send HTTP GET //
  /////////////////////////////////////////
  TCPClient client;
  if (client.connect(weatherServer, 80))
  {
      client.print("GET /data/2.5/weather?");
      client.print(location);
      client.print("&mode=xml");
      if (_apiKey != NULL)
        client.print("&APPID=" + _apiKey);
      if (_units == IMPERIAL)
        client.print("&units=imperial"); // Change imperial to metric for celsius (delete this line for kelvin)
      else if (_units == METRIC)
        client.print("&units=metric");
      client.println(" HTTP/1.0");
      client.println("Host: " + weatherServer);
      client.println("Content-length: 0");
      client.println();
  }
  else
  {
      return 0;
  }

  ///////////////////////////////////////
  // Wait for Response, Store in Array //
  ///////////////////////////////////////
  String rsp;
  int timeout = 1000;
  while ((!client.available()) && (timeout-- > 0))
    delay(1);
  if (timeout <= 0)
    return 0;
  while (client.available())
  {
    char c = client.read();
    rsp += c;
  }

  ////////////////
  // Disconnect //
  ////////////////
  client.stop();
  Serial.println("Response: ");
  Serial.println(rsp);

  //////////////////////////////
  // Sort Data into Variables //
  //////////////////////////////
  forecast.temperature.value = "" + parseXML(&rsp, "temperature", "value");
  forecast.temperature.minT = "" + parseXML(&rsp, "temperature", "min");
  forecast.temperature.maxT = "" + parseXML(&rsp, "temperature", "max");
  forecast.temperature.unit = "" + parseXML(&rsp, "temperature", "unit");
  forecast.humidity.value = "" + parseXML(&rsp, "humidity", "value");
  forecast.humidity.unit = "" + parseXML(&rsp, "humidity", "unit");
  forecast.pressure.value = "" + parseXML(&rsp, "pressure", "value");
  forecast.pressure.unit = "" + parseXML(&rsp, "pressure", "unit");
  forecast.wind.speedValue = "" + parseXML(&rsp, "speed", "value");
  forecast.wind.speedName = "" + parseXML(&rsp, "speed", "name");
  forecast.wind.dirDeg = "" + parseXML(&rsp, "direction", "value");
  forecast.wind.dirCode = "" + parseXML(&rsp, "direction", "code");
  forecast.wind.dirName = "" + parseXML(&rsp, "direction", "name");
  forecast.clouds.all = "" + parseXML(&rsp, "clouds", "value");
  forecast.clouds.value = "" + parseXML(&rsp, "clouds", "name");
  forecast.vis.value = "" + parseXML(&rsp, "visibility", "value");
  forecast.precip.value = "" + parseXML(&rsp, "precipitation", "value");
  forecast.precip.type = "" + parseXML(&rsp, "precipitation", "mode");
  forecast.symbol.number = parseXML(&rsp, "weather", "number").toInt();
  forecast.symbol.name = "" + parseXML(&rsp, "weather", "value");
  forecast.day = "" + parseXML(&rsp, "lastupdate", "value");
  forecast.type = CURRENT_FORECAST;

  //printDebug();

  return 1;
}

int OpenWeather::hourlyForecast(String location, unsigned int count)
{
  /////////////////////////////////////////
  // Connect to client and send HTTP GET //
  /////////////////////////////////////////
  TCPClient client;
  if (client.connect(weatherServer, 80))
  {
      client.print("GET /data/2.5/forecast?");
      client.print(location);
      client.print("&cnt=" + String(count));
      client.print("&mode=xml");
      if (_apiKey != NULL)
        client.print("&APPID=" + _apiKey);
      if (_units == IMPERIAL)
        client.print("&units=imperial"); // Change imperial to metric for celsius (delete this line for kelvin)
      else if (_units == METRIC)
        client.print("&units=metric");
      client.println(" HTTP/1.0");
      client.println("Host: " + weatherServer);
      client.println("Content-length: 0");
      client.println();
  }
  else
  {
      return 0;
  }

  ///////////////////////////////////////
  // Wait for Response, Store in Array //
  ///////////////////////////////////////
  String rsp;
  unsigned int forecastCount = 0;
  int timeout = 1000;
  while ((!client.available()) && (timeout-- > 0))
    delay(1);
  if (timeout <= 0)
    return 0;
  while (client.available())
  {
    char c = client.read();
    rsp += c;
    if (rsp.indexOf("</time>") > 0)
    {
      forecastCount++;
      if (forecastCount < count)
      {
        rsp = NULL; // Clear out response
      }
      else
      {
        break;
      }
    }
  }

  ////////////////
  // Disconnect //
  ////////////////
  client.stop();
  Serial.println("Response: ");
  Serial.println(rsp);

  forecast.day = "" + parseXML(&rsp, "time", "from");
  forecast.symbol.number = parseXML(&rsp, "symbol", "number").toInt();
  forecast.symbol.name = "" + parseXML(&rsp, "symbol", "name");
  forecast.precip.value = "" + parseXML(&rsp, "precipitation", "value");
  forecast.precip.type = "" + parseXML(&rsp, "precipitation", "type");
  forecast.wind.dirDeg = "" + parseXML(&rsp, "windDirection", "deg");
  forecast.wind.dirCode = "" + parseXML(&rsp, "windDirection", "code");
  forecast.wind.dirName = "" + parseXML(&rsp, "windDirection", "name");
  forecast.wind.speedValue = "" + parseXML(&rsp, "windSpeed", "mps");
  forecast.wind.speedName = "" + parseXML(&rsp, "windSpeed", "name");
  forecast.temperature.value = "" + parseXML(&rsp, "temperature", "value");
  forecast.temperature.minT = "" + parseXML(&rsp, "temperature", "min");
  forecast.temperature.maxT = "" + parseXML(&rsp, "temperature", "max");
  forecast.temperature.nightT = "" + parseXML(&rsp, "temperature", "night");
  forecast.temperature.eveT = "" + parseXML(&rsp, "temperature", "eve");
  forecast.temperature.mornT = "" + parseXML(&rsp, "temperature", "morn");
  forecast.pressure.unit = "" + parseXML(&rsp, "pressure", "unit");
  forecast.pressure.value = "" + parseXML(&rsp, "pressure", "value");
  forecast.humidity.value = "" + parseXML(&rsp, "humidity", "value");
  forecast.humidity.unit = "" + parseXML(&rsp, "humidity", "unit");
  forecast.clouds.value = "" + parseXML(&rsp, "clouds", "value");
  forecast.clouds.all = "" + parseXML(&rsp, "clouds", "all");
  forecast.clouds.unit = "" + parseXML(&rsp, "clouds", "unit");
  forecast.type = HOURLY_FORECAST;

  //printDebug();

  return 1;
}

int OpenWeather::dailyForecast(String location, unsigned int count)
{
  /////////////////////////////////////////
  // Connect to client and send HTTP GET //
  /////////////////////////////////////////
  TCPClient client;
  if (client.connect(weatherServer, 80))
  {
      client.print("GET /data/2.5/forecast/daily?");
      client.print(location);
      client.print("&cnt=" + String(count));
      client.print("&mode=xml");
      if (_apiKey != NULL)
        client.print("&APPID=" + _apiKey);
      if (_units == IMPERIAL)
        client.print("&units=imperial"); // Change imperial to metric for celsius (delete this line for kelvin)
      else if (_units == METRIC)
        client.print("&units=metric");
      client.println(" HTTP/1.0");
      client.println("Host: " + weatherServer);
      client.println("Content-length: 0");
      client.println();
  }
  else
  {
      return 0;
  }

  ///////////////////////////////////////
  // Wait for Response, Store in Array //
  ///////////////////////////////////////
  String rsp;
  unsigned int forecastCount = 0;
  int timeout = 1000;
  while ((!client.available()) && (timeout-- > 0))
    delay(1);
  if (timeout <= 0)
    return 0;
  while (client.available())
  {
    char c = client.read();
    rsp += c;
    if (rsp.indexOf("</time>") > 0)
    {
      forecastCount++;
      if (forecastCount < count)
      {
        rsp = NULL; // Clear out response
      }
      else
      {
        break;
      }
    }
  }

  ////////////////
  // Disconnect //
  ////////////////
  client.stop();
  Serial.println("Response: ");
  Serial.println(rsp);

  forecast.day = "" + parseXML(&rsp, "time", "day");
  forecast.symbol.number = parseXML(&rsp, "symbol", "number").toInt();
  forecast.symbol.name = "" + parseXML(&rsp, "symbol", "name");
  forecast.precip.value = "" + parseXML(&rsp, "precipitation", "value");
  forecast.precip.type = "" + parseXML(&rsp, "precipitation", "type");
  forecast.wind.dirDeg = "" + parseXML(&rsp, "windDirection", "deg");
  forecast.wind.dirCode = "" + parseXML(&rsp, "windDirection", "code");
  forecast.wind.dirName = "" + parseXML(&rsp, "windDirection", "name");
  forecast.wind.speedValue = "" + parseXML(&rsp, "windSpeed", "mps");
  forecast.wind.speedName = "" + parseXML(&rsp, "windSpeed", "name");
  forecast.temperature.value = "" + parseXML(&rsp, "temperature", "day");
  forecast.temperature.minT = "" + parseXML(&rsp, "temperature", "min");
  forecast.temperature.maxT = "" + parseXML(&rsp, "temperature", "max");
  forecast.temperature.nightT = "" + parseXML(&rsp, "temperature", "night");
  forecast.temperature.eveT = "" + parseXML(&rsp, "temperature", "eve");
  forecast.temperature.mornT = "" + parseXML(&rsp, "temperature", "morn");
  forecast.pressure.unit = "" + parseXML(&rsp, "pressure", "unit");
  forecast.pressure.value = "" + parseXML(&rsp, "pressure", "value");
  forecast.humidity.value = "" + parseXML(&rsp, "humidity", "value");
  forecast.humidity.unit = "" + parseXML(&rsp, "humidity", "unit");
  forecast.clouds.value = "" + parseXML(&rsp, "clouds", "value");
  forecast.clouds.all = "" + parseXML(&rsp, "clouds", "all");
  forecast.clouds.unit = "" + parseXML(&rsp, "clouds", "unit");
  forecast.type = DAILY_FORECAST;

  //printDebug();

  return 1;
}

String OpenWeather::parseXML(String * search, String tag, String attribute)
{
    int tagStart = tagIndex(search, tag, 1);
    int tagEnd = tagIndex(search, tag, 0);
    if (tagStart >= 0)
    {
      int attributeStart = search->indexOf(attribute, tagStart);
      if ((attributeStart >= 0) && (attributeStart < tagEnd)) // Make sure we don't get value of another key
      {
        attributeStart = search->indexOf("\"", attributeStart);
        if (attributeStart >= 0)
        {
            int attributeEnd = search->indexOf("\"", attributeStart + 1);
            if (attributeEnd >= 0)
            {
                return search->substring(attributeStart + 1, attributeEnd);
            }
        }
      }
    }

    return NULL;
}

int OpenWeather::tagIndex(String * xml, String tag, bool start)
{
  String fullTag = "<";
  if (start)
  {
    fullTag += tag;
    fullTag += ' '; // Look for a space after the tag name
  }
  else
  {
    fullTag += '/';
    fullTag += tag;
    fullTag += '>';
  }

  return xml->indexOf(fullTag);
}

void OpenWeather::printDebug()
{
  Serial.println("=====================================================");
  Serial.print("day: "); Serial.println(forecast.day);
  Serial.print("symbol.number: "); Serial.println(forecast.symbol.number);
  Serial.print("symbol.name: "); Serial.println(forecast.symbol.name);
  Serial.print("precip.value: "); Serial.println(forecast.precip.value);
  Serial.print("precip.type: "); Serial.println(forecast.precip.type);
  Serial.print("wind.dirDeg: "); Serial.println(forecast.wind.dirDeg);
  Serial.print("wind.dirCode: "); Serial.println(forecast.wind.dirCode);
  Serial.print("wind.dirName: "); Serial.println(forecast.wind.dirName);
  Serial.print("wind.speedValue: "); Serial.println(forecast.wind.speedValue);
  Serial.print("wind.speedName: "); Serial.println(forecast.wind.speedName);
  Serial.print("temperature.value: "); Serial.println(forecast.temperature.value);
  Serial.print("temperature.minT: "); Serial.println(forecast.temperature.minT);
  Serial.print("temperature.maxT: "); Serial.println(forecast.temperature.maxT);
  Serial.print("temperature.unit: "); Serial.println(forecast.temperature.unit);
  Serial.print("pressure.unit: "); Serial.println(forecast.pressure.unit);
  Serial.print("pressure.value: "); Serial.println(forecast.pressure.value);
  Serial.print("humidity.value: "); Serial.println(forecast.humidity.value);
  Serial.print("humidity.unit: "); Serial.println(forecast.humidity.unit);
  Serial.print("clouds.value: "); Serial.println(forecast.clouds.value);
  Serial.print("clouds.all: "); Serial.println(forecast.clouds.all);
  Serial.print("vis.value: "); Serial.println(forecast.vis.value);
  Serial.println("=====================================================");
}

If you click back to the main application code, and scroll to the top, you’ll notice it automatically added:

language:c
// This #include statement was automatically added by the Particle IDE.
#include "OpenWeatherMap.h"

Such convenience!

Adding additional, external source files is a great way to functionally separate parts of your code, without having to go through the library-including process.

Add your OpenWeather API Key and Latitude/Longitude

Last thing before weather forecasting! There are three variables in the main source file to change. First, copy your OpenWeatherMap API key, and paste it into the OPENWEATHER_API_KEY String near the top of the code – between the quotes.

language:c
// Example: Defining an OpenWeatherMap API key as a String
const String OPENWEATHER_API_KEY = "42tHisISaNeXampLEApIKey123456890";

Then, get your latitude and longitude, and paste it into the LATIITUDE and LONGITUDE variables right below the API key.

language:c
// Example: Defining the lat and long as float variables:
const float LATITUDE = 40.090554;
const float LONGITUDE = -105.184861;

Edits done! Flash away!

What You Should See

When the code begins to run, your Photon RedBoard will connect to the OpenWeatherMap server, and gather all of the data it can regarding the current weather forecast. There’s a lot of data to display! It’s not all going to fit on the MicroOLED. Initially the “big 3” pieces of weather data will be displayed: temperature, relative humidity, and pressure.

The app is configured to cycle the display every 10 seconds – the progress of that 10-second cycle is displayed by the line at the bottom of the display. If you’ve read everything you can on the current screen, press the green button to cycle to the next one.

To refresh the current weather forecast, click the yellow button.

Our application can also predict the weather! To see into the future, click the blue button. The first blue button click will get the forecast for 3 hours from now. The same display cycle will occur, this time with data for 3 hours from now. Successive blue button clicks will forecast the weather 6, then 9 hours, then 1, 2, and 3 days.

Don't spam the buttons! Each time you click the blue or yellow buttons, the Photon will request a weather forecast from the OpenWeatherMap server. There are limits to how often your free API key can make requests of the OpenWeatherMap server. Don't exceed it!

Code to Note

We’ve written a simplified class to interact with the OpenWeatherMap API and server called OpenWeather. That’s what’s included in the two OpenWeatherMap extra files. To use the class, begin by creating an OpenWeather object, like this:

language:c
// Create an OpenWeather object, giving it our API key as the parameter:
OpenWeather weather(OPENWEATHER_API_KEY);

The OpenWeatherMap API allows you to get the current weather status, or a future forecast– hours or days from now. To gather the current forecast for a set location, there are a few options:

language:c
// To get the weather for a latitude/longitude location:
weather.current(float latitude, float longitude); // Get the weather at a specific latitude and longitude.

// To get a weather update for a city name, call current(String cityName).
// cityName can be the lone name of a city, e.g. "Denver"
// or it can be a "city,countrycode", like "Denver,US"
weather.current(String cityName); // Get the weather for a specific city name or city/country code.

// Or, to be really specific about it, pass the current function your city ID.
// WHich can be grabbed from here: http://bulk.openweathermap.org/sample/
weather.current(uint32_t cityID);

Once you’ve made a call to the weather.current() function – and it succeeds, returning a 1 – you can use a number of get functions to read the temperature, humidity, precipitation forecast and more. Some examples include:

language:c
float temperature = weather.temperature(); // Get the average temperature
unsigned int humidity = weather.humidity(); // Get the relative humidity %
float pressure = weather.pressure(); // Get the pressure, in hPa

float windSpd = weather.windSpeed(); //get the wind speed in mps
String windDir = weather.windDirection(); // Get wind compass direction, like "S", "NW", etc.
String windDescription = weather.windName(); // Get the wind description, like "Breezy"

String precipType = precipitationType(); // Get the type of precipitation "NONE", "Rain", "snow"
float precipAmount = weather.precipitationValue(); // Get amount of precip in mm

There are a few more, like weather.conditionName(), which are all demonstrated in the experiment code. Check through the comments to see how they’re used.

Alternatively, if you want to forecast future weather, call either hourly() or daily(), which each have an extra parameter defining the time of the forecast. For example:

language:c
// Get the hourly forecast in three-hour intervals:
weather.hourly(LATITITUDE, LONGITUDE, 2); // Get forecast 6 hours from now

// Get a daily forecast:
weather.daily(LATITUDE, LONGITUDE, 2); // Get tomorrow's weather forecast

Then use the same variable-getting functions to grab temperature, humidity, pressure, and the rest of the forecast.

Troubleshooting

To help diagnose any issues, Serial.println()’s are scattered through the program. If you’re having any trouble, try opening a serial port to see what your Photon says.

If you’ve made it past the first part of the experiment, you should have already verified the OLED and button circuits are working. Is weather data not being displayed? Maybe your Photon RedBoard isn’t able to communicate with the OpenWeatherMap server. Try checking the status of the OpenWeatherMap server is it down for just you?.

Appendix: Troubleshooting

Flashing in Safe Mode

Are you having trouble flashing new code to your Photon? Try booting it up into safe mode, and flashing again.

To put your Photon RedBoard into safe mode:

  1. Hold down RESET and MODE.
  2. Release RESET, but continue holding MODE down.
  3. When the RGB LED blinks pink, release the MODE button.

The Photon RedBoard will dance through its WiFi and Particle Cloud connecting process. Once connected, it will begin to breathe pink, indicating it’s in safe mode.

In safe mode, the Photon won’t run your application. All it does is maintain its connection to the Particle Cloud. Find out more about safe mode in Particle’s Device Mode documentation.

If safe mode works, but uploading in run mode didn’t, double check your application code to make sure there aren’t any infinite loops. The Photon RedBoard maintains its communication with the Particle cloud during delay() calls, or at the end of a loop(). If you have any while(1)-type loops in your code, sprinkle a Particle.process() in to avoid any future flashing troubles.

Modifying the WiFi Configuration

If you’ve taken your Photon RedBoard somewhere new, and need to connect it to a different WiFi network, you can use the MODE button to enter new WiFi settings.

Restting the WiFi credentials will not erase your application!

To erase stored WiFi credentials while the Photon is running, hold down the MODE button until the RGB LED begins to blink blue. At this point, the Photon will be back in listening mode. You can use the Particle app, serial terminal, or Particle CLI to update your WiFi credentials.

If, for any reason, the above method isn’t working for you, try following these steps to get into listening mode:

  1. Hold down RESET and MODE.
  2. Release RESET, but continue holding MODE down.
  3. When the RGB LED blinks white, release the MODE button.
    • It’ll take about 10 seconds before it blinks white. The RGB will blink pink, yellow, and green before then.

Serial Monitor Locked Out

If your terminal program won’t let you open the serial port – citing an error like: COM port in use. Or if the COM port has disappeared from a selectable list, try following these steps:

  1. Close the serial terminal program.
  2. Reset your Photon RedBoard
  3. Wait for the Photon RedBoard to connect to WiFi
  4. Open the terminal program back up and try connecting.

This usually occurs when the Photon RedBoard resets while the terminal program is still open and connected. If you’re uploading a new program to the Photon RedBoard, or just hitting the RESET button, manually disconnect your serial port before doing so.

Serial Monitor Pausing

In some cases, particularly on Windows machines, your Photon or Photon RedBoard may cause your serial terminal to quit unexpectedly. The board will begin sending data to the serial port once it has an Internet connection, and the serial termninal may not be able to handle the incoming data.

To pause the sending of data until a serial connection has been made AND until the user sends any key press to the Photon RedBoard, add this code to your setup():

language:c
// Make sure your Serial Terminal app is closed before powering your device
// Now open your Serial Terminal, and hit any key to continue!
Serial.println("Press any key to begin");
//This line pauses the Serial port until a key is pressed
while(!Serial.available()) Particle.process();

It is worth noting that this line used to be: while(!Serial.available()) SPARK_WLAN_Loop();. Should you ever come across an example using that line, you will need to update it to the Spark.proccess() line above.

Firmware Updates

Periodically, Particle will release firmware updates for the Photon RedBoard’s P1 module. These usually include bug fixes, or other changes that’ll make your life easier.

You can use the Build IDE to view and control what firmware you’re writing for. Navigate over to the device tab, and click the carrot next to your Photon. A dropdown will indicate the firmware you’re building for. The latest is always recommended.

Selecting the firmware

If you want to get a jump on running the latest firmware, the easiest method for updating to it is to go through Particle’s command line interface – Particle CLI.

With Particle CLI installed, place your Photon into DFU mode, by performing the MODE/RESET device mode switch and releasing MODE when the RGB blinks yellowish-orange. Then issue this command on the command line:

particle update

That’s it! Your computer will download the latest firmware, then flash it over USB to your Photon RedBoard.

Resources and Going Further

Congratulations on making it through Inventor’s Kit for Photon Experiment Guide! Hopefully you’ve got a solid grasp on the fundamentals of electronics as well as how to get them interacting with the Internet world at large. All of the experiments and circuits in this guide are available in our Inventor’s Kit for Photon GitHub Repository.

Inventor's Kit for Photon GitHub Repository

If you’ve found any bugs in the code, or want to add some suggestions, consider filing an issue there.

As you venture out into creating Photon projects of your own, remember that you’re not alone! There are loads of resources available, should you need them:

Going Further

We encourage you to check out all of the development environment options available for the Photon RedBoard. Check out our Photon Development Guide for more information on using the Particle Build IDE, or a custom ARM-GCC environment:

New!

Photon Development Guide

August 20, 2015

A guide to the online and offline Particle IDE's to help aid you in your Photon development.

And there are loads of other SparkFun tutorials that might help inspire your next project, including:

Galileo Unread Email Counter

How to create a simple unread-email checker with the Intel/Arduino Galileo.

Are You Okay? Widget

Use an Electric Imp and accelerometer to create an "Are You OK" widget. A cozy piece of technology your friend or loved one can nudge to let you know they're OK from half-a-world away.

Pushing Data to Data.SparkFun.com

A grab bag of examples to show off the variety of routes your data can take on its way to a Data.SparkFun.com stream.

Photon Weather Shield Hookup Guide

Create Internet-connected weather projects with the SparkFun Weather Shield for the Photon.

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

How to Use a Multimeter

$
0
0

How to Use a Multimeter a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t428

Introduction

So… how do I use a multimeter? This tutorial will show you how to use a digital multimeter (DMM), an indispensable tool that you can use to diagnose circuits, learn about other people’s electronic designs, and even test a battery. Hence the ‘multi’-‘meter’ (multiple measurement) name. We will be using the SparkFun VC830L throughout the tutorial but these methods should apply to most multimeters.

using a multimeter

Using a Multimeter to test the voltage on a LiPo Battery.

The most basic things we measure are voltage and current. A multimeter is also great for some basic sanity checks and troubleshooting. Is your circuit not working? Does the switch work? Put a meter on it! The multimeter is your first defense when troubleshooting a system. In this tutorial we will cover measuring voltage, current, resistance and continuity.

Suggested Reading

You may want to know about these concepts to help you with this tutorial:

Parts of a Multimeter

->multimeter parts<-

A multimeter is has three parts:

  • Display
  • Selection Knob
  • Ports

The display usually has four digits and the ability to display a negative sign. A few multimeters have illuminated displays for better viewing in low light situations.

The selection knob allows the user to set the multimeter to read different things such as milliamps (mA) of current, voltage (V) and resistance (&ohm;).

Two probes are plugged into two of the ports on the front of the unit. COM stands for common and is almost always connected to Ground or ‘-’ of a circuit. The COM probe is conventionally black but there is no difference between the red probe and black probe other than color. 10A is the special port used when measuring large currents (greater than 200mA). mAVΩ is the port that the red probe is conventionally plugged in to. This port allows the measurement of current (up to 200mA), voltage (V), and resistance (&ohm;). The probes have a banana type connector on the end that plugs into the multimeter. Any probe with a banana plug will work with this meter. This allows for different types of probes to be used.

Probe Types

There are many different types of probes available for multimeters. Here are a few of our favorites:

  • Banana to Alligator Clips : These are great cables for connecting to large wires or pins on a breadboard. Good for performing longer term tests where you don’t have to have to hold the probes in place while you manipulate a circuit.
  • Banana to IC Hook : IC hooks work well on smaller ICs and legs of ICs.
  • Banana to Tweezers : Tweezers are handy if you are needing to test SMD components.
  • Banana to Test Probes : If you ever break a probe, they are cheap to replace!

Measuring Voltage

To start, let’s measure voltage on a AA battery: Plug the black probe into COM and the red probe into mAVΩ. Set the multimeter to “2V” in the DC (direct current) range. Almost all portable electronics use direct current), not alternating current. Connect the black probe to the battery’s ground or ‘-’ and the red probe to power or ‘+’. Squeeze the probes with a little pressure against the positive and negative terminals of the AA battery. If you’ve got a fresh battery, you should see around 1.5V on the display (this battery is brand new, so its voltage is slightly higher than 1.5V).

alt text

If you’re measuring DC voltage (such as a battery or a sensor hooked up to an Arduino) you want to set the knob where the V has a straight line. AC voltage (like what comes out of the wall) can be dangerous, so we rarely need to use the AC voltage setting (the V with a wavy line next to it). If you’re messing with AC, we recommend you get a non-contact tester rather than use a digital multimeter.

Volts DC

Use the V with a straight line to measure DC Voltage

Volts AC

Use the V with a wavy line to measure AC Voltage

What happens if you switch the red and black probes? The reading on the multimeter is simply negative. Nothing bad happens! The multimeter measures voltage in relation to the common probe. How much voltage is there on the ‘+’ of the battery compared to common or the negative pin? 1.5V. If we switch the probes, we define ‘+’ as the common or zero point. How much voltage is there on the ‘-’ of the battery compared to our new zero? -1.5V!

alt text

Now let’s construct a simple circuit to demonstrate how to measure voltage in a real world scenario. The circuit is simply a 1k&ohm; and a Blue super bright LED powered with a SparkFun Breadboard Power Supply Stick. To begin, let’s make sure the circuit you are working on is powered up correctly. If your project should be at 5V but is less than 4.5V or greater than 5.5V, this would quickly give you an indication that something is wrong and you may need to check your power connections or the wiring of your circuit.

alt text

Measuring the voltage coming off of a Power Supply Stick.

Set the knob to “20V” in the DC range (the DC Voltage range has a V with a straight line next to it). Multimeters are generally not autoranging. You have to set the multimeter to a range that it can measure. For example, 2V measures voltages up to 2 volts, and 20V measures voltages up to 20 volts. So if you’ve measuring a 12V battery, use the 20V setting. 5V system? Use the 20V setting. If you set it incorrectly, you will probably see the meter screen change and then read ‘1’.

alt text

With some force (imagine poking a fork into a piece of cooked meat), push the probes onto two exposed pieces of metal. One probe should contact a GND connection. One probe to the VCC or 5V connection.

We can test different parts of the circuit as well. This practice is called nodal analysis, and it is a basic building block in circuit analysis. By measuring the voltage across the circuit we can see how much voltage each component requires. Let’s measure the whole circuit first. Measuring from where the voltage is going in to the resistor and then where ground is on the LED, we should see the full voltage of the circuit, expected to be around 5V.

alt text

We can then see how much voltage the LED is using. This is what is referred to as the voltage drop across the LED. If that doesn’t make sense now, fear not. It will as you explore the world of electronics more. The important thing to take away is that different parts of a circuit can be measured to analyze the circuit as a whole.

alt text

This LED is using 2.66V of the available 5V supply to illuminate. This is lower than the forward voltage stated in the datasheet on account of the circuit only having small amount of current running though it, but more on that in a bit.

Overload

What happens if you select a voltage setting that is too low for the voltage you’re trying to measure? Nothing bad. The meter will simply display a 1. This is the meter trying to tell you that it is overloaded or out-of-range. Whatever you’re trying to read is too much for that particular setting. Try changing the multimeter knob to a the next highest setting.

alt text

Reading the 5V across this circuit is too much for the 2V setting on the multimeter.

Selection Knob

alt text

Why does the meter knob read 20V and not 10V? If you’re looking to measure a voltage less than 20V, you turn to the 20V setting. This will allow you to read from 2.00 to 19.99.

The first digit on many multimeters is only able to display a ‘1’ so the ranges are limited to 19.99 instead of 99.99. Hence the 20V max range instead of 99V max range.

Warning! In general, stick to DC circuits (the settings on the multimeter with straight lines, not curvy lines). Most multimeters can measure AC (alternating current) systems, but AC circuits can be dangerous. A wall outlet with AC or 'main voltage' is the stuff that can zap you pretty good. VERY carefully respect AC. If you need to check to see if an outlet is 'on' then use a AC tester. Really the only times we've needed to measure AC are when we've got an outlet that is acting funny (is it really at 110V?), or if we're trying to control a heater (such as a hot plate). Go slow and double check everything before you test an AC circuit.

Measuring Resistance

Normal resistors have color codes on them. If you don’t know what they mean, that’s ok! There are plenty of online calculators that are easy to use. However, if you ever find yourself without internet access, a multimeter is very handy at measuring resistance.

Pick out a random resistor and set the multimeter to the 20kΩ setting. Then hold the probes against the resistor legs with the same amount of pressure you when pressing a key on a keyboard.

alt text

The meter will read one of three things, 0.00, 1, or the actual resistor value.

  • In this case, the meter reads 0.97, meaning this resistor has a value of 0.97k&ohm;, or about 1k&ohm; or 1000 &ohm; (remember you are in the 20kΩ or 20,000 Ohm mode so you need to move the decimal three places to the right or 9,900 Ohms).

  • If the multimeter reads 1 or displays OL, it’s overloaded. You will need to try a higher mode such as 200kΩ mode or 2MΩ (megaohm) mode. There is no harm if this happen, it simply means the range knob needs to be adjusted.

  • If the multimeter reads 0.00 or nearly zero, then you need to lower the mode to 2kΩ or 200Ω.

Remember that many resistors have a 5% tolerance. This means that the color codes may indicate 10,000 Ohms (10kΩ), but because of discrepancies in the manufacturing process a 10kΩ resistor could be as low as 9.5kΩ or as high as 10.5kΩ. Don’t worry, it’ll work just fine as a pull-up or general resistor.

Let’s drop the meter down to the next lowest setting, 2K&ohm;. What happens?

alt text

Not a whole lot changed. Because this resistor (a 1K&ohm;) is less than 2K&ohm;, it still shows up on the display. However, you’ll notice that there is one more digit after the decimal point giving us a slightly higher resolution in our reading. What about the next lowest setting?

alt text

Now, since 1k&ohm; is greater than 200&ohm;, we’ve maxed out the meter, and it is telling you that it is overloaded and that you need to try a higher value setting.

As a rule of thumb, it’s rare to see a resistor less than 1 Ohm. Remember that measuring resistance is not perfect. Temperature can affect the reading a lot. Also, measuring resistance of a device while it is physically installed in a circuit can be very tricky. The surrounding components on a circuit board can greatly affect the reading.

Measuring Current

Reading current is one of the trickiest and most insightful readings in the world of embedded electronics. It’s tricky because you have to measure current in series. Where voltage is measure by poking at VCC and GND (in parallel), to measure current you have to physically interrupt the flow of current and put the meter in-line. To demonstrate this, we’ll use the same circuit we used in the measuring voltage section.

The first thing we’ll need is an extra piece of wire. As mentioned, we’ll need to physically interrupt the circuit to measure the current. Said another way, pull out the VCC wire going to the resistor, add a wire where that wire was connected, and then probe from the power pin on the power supply to the resistor. This effectively “breaks” power to the circuit. We then insert the multimeter in-line so that it can measure the current as it “flows” through to the multimeter into the bread board.

For these pictures, we cheated and used alligator clips. When measuring current, it’s often good to watch what your system does over time, for a few seconds or minutes. While you might want to stand there and hold the probes to the system, sometimes it’s easier to free up your hands. These alligator clip probes can come in handy. Note that almost all multimeters have the same sized jacks (they’re called “banana plugs”) so if you’re in a pinch, you can use your friend’s probes.

alt text

With the multimeter connected, we can now set the dial to the proper setting and measure some current. Measuring current works the same as voltage and resistance – you have to get the correct range. Set the multimeter to 200mA, and work from there. The current consumption for many breadboard projects is usually under 200mA. Make sure the red probe is plugged into the 200mA fused port. On our favorite multimeter, the 200mA hole is the same port/hole as voltage and resistance reading (the port is labeled mAVΩ). This means you can keep the red probe in the same port to measure current, voltage, or resistance. However, if you suspect that your circuit will be using close to or more than 200mA, switch your probe to the 10A side, just to be safe. Overloading the current can result in a blown fuse rather than just an overload display. More on that in a bit.

alt text

This circuit was only pulling 1.8mA at the time of measurement, not a lot of current. The average reading was closer to 2.1mA.

Realize that the multimeter is acting as a piece of wire – you’ve now completed the circuit, and the circuit will power on. This is important because as time goes on the LED, microcontroller, sensor, or whatever device being measured may change its power consumption (such as turning on an LED can resulting in a 20mA increase for a second, then decrease for a second when it tunrs off). On the multimeter display you should see the instantaneous current reading. All multimeters take readings over time and then give you the average, so expect the reading to fluctuate. In general, cheaper meters will average more harshly and respond more slowly, so take each reading with a grain of salt. In your head, take an average range such as 7 to 8mA under normal 5V conditions (not 7.48mA).

Similar to the other measurements, when measuring current, the color of the probes does not matter. What happens if we switch probes? Nothing bad happens! It simply causes the current reading to become negative:

alt text

Current is still flowing through the system, you’ve just changed your perspective and now the meter reads negative.

Remember! When you're done using the meter, always return the meter to read voltage (return the probes to the voltage port, set the meter to read the DC voltage range if necessary). It's common to grab a meter and begin to quickly measure the voltage between two pins. If you have left your meter in 'current' mode, you won't see the voltage on the display. Instead you'll see '0.000' indicating that there is no current between VCC and GND. Within that split second you will have connected VCC to GND through your meter and the 200mA fuse will blow = not good. So before you put the meter down for the night, remember to leave your meter in a friendly state.

Measuring current can be tricky the first couple of times. Don’t worry if you blow the fuse - we’ve done it dozens of times! We’ll show you how to replace the fuse in a later section.

Continuity

Continuity testing is the act of testing the resistance between two points. If there is very low resistance (less than a few &ohm;s), the two points are connected electrically, and a tone is emitted. If there is more than a few &ohm;s of resistance, than the circuit is open, and no tone is emitted. This test helps insure that connections are made correctly between two points. This test also helps us detect if two points are connected that should not be.

Continuity is quite possibly the single most important function for embedded hardware gurus. This feature allows us to test for conductivity of materials and to trace where electrical connections have been made or not made.

Set the multimeter to ‘Continuity’ mode. It may vary among DMMs, but look for a diode symbol with propagation waves around it (like sound coming from a speaker).

alt text

Multimeter is set to continuity mode.

Now touch the probes together. The multimeter should emit a tone (Note: Not all multimeters have a continuity setting, but most should). This shows that a very small amount of current is allowed to flow without resistance (or at least a very very small resistance) between probes.

Warning! In general, turn OFF the system before checking for continuity.

On a breadboard that is not powered, use the probes to poke at two separate ground pins. You should hear a tone indicating that they are connected. Poke the probes from the VCC pin on a microcontroller to VCC on your power supply. It should emit a tone indicating that power is free to flow from the VCC pin to the micro. If it does not emit a tone, then you can begin to follow the route that copper trace takes and tell if there are breaks in the line, wire, breadboard, or PCB.

Continuity is a great way to test if two SMD pins are touching. If your eyes can’t see it, the multimeter is usually a great second testing resource.

When a system is not working, continuity is one more thing to help troubleshoot the system. Here are the steps to take:

  1. If the system is on, carefully check VCC and GND with the voltage setting to make sure the voltage is the correct level. If the 5V system is running at 4.2V check your regulator carefully, it could be very hot indicating the system is pulling too much current.
  2. Power the system down and check continuity between VCC and GND. If there is continuity (if you hear a beep), then you’ve got a short somewhere.
  3. Power the system down. With continuity, check that VCC and GND are correctly wired to the pins on the microcontroller and other devices. The system may be powering up, but the individual ICs may be wired wrong.
  4. Assuming you can get the microcontroller running, set the multimeter aside, and move on to serial debugging or use a logic analyzer to inspect the digital signals.

Continuity and large capacitors: During normal troubleshooting. you will be probing for continuity between ground and the VCC rail. This is a good sanity check before powering up a prototype to make sure there is not a short on the power system. But don’t be surprised if you hear a short ‘beep!’ when probing. This is because there is often significant amounts of capacitance on the power system. The multimeter is looking for very low resistance to see if two points are connected. Capacitors will act like a short for a split second until they fill up with energy, and then act like an open connection. Therefore, you will hear a short beep and then nothing. That’s ok, it’s just the caps charging up.

Changing the Fuse

One of the most common mistakes with a new multimeter is to measure current on a bread board by probing from VCC to GND (bad!). This will immediately short power to ground through the multimeter causing the bread board power supply to brown out. As the current rushes through the multimeter, the internal fuse will heat up and then burn out as 200mA flows through it. It will happen in a split second and without any real audible or physical indication that something is wrong.

Wow, that was neat. Now what? Well first, remember that measuring current is done in series (interrupt the VCC line to the breadboard or microcontroller to measure current). If you try to measure the current with a blown fuse, you’ll probably notice that the meter reads ‘0.00’ and that the system doesn’t turn on like it should when you attach the multimeter. This is because the internal fuse is broken and acts as a broken wire or open. Don’t worry, this happens all the time, and it costs about $1 to fix.

To change the fuse, find your handy dandy mini screw driver, and start taking out screws. The SparkFun DMM is pretty easy to pull apart. Start by removing the battery plate and the battery.

alt text

Next, remove the two screws hiding behind the battery plate.

alt text

Lift the face of the multimeter slightly.

alt text

Now notice the hooks on the bottom edge of the face. You will need to slide the face sideways with a little force to disengage these hooks.

alt text

Once the face is unhooked, it should come out easily. Now you can see inside the multimeter!

alt text

Gently lift up on the fuse, and it will pop out.

alt text

Make sure to replace the correct fuse with the correct type. In other words, replace the 200mA fuse with a 200mA fuse.

Warning!DO NOT put a 10A fuse where a 200mA fuse should go. The placement of the fuses may not match the placement of the probe ports. Read the metal cap on either end of the fuse to double check which is which.

The components and PCB traces inside the multimeter are designed to take different amounts of current. You will damage and possibly ruin your multimeter if you accidentally push 5A through the 200mA port.

There are times where you need to measure high current devices like a motor or heating element. Do you see the two places to put the red probe on the front of the multimeter? 10A on the left and mAVΩ on the right? If you try to measure more than 200mA on the mAVΩ port you run the risk of blowing the fuse. But if you use the 10A port to measure current, you run a much lower risk of blowing the fuse. The trade-off is sensitivity. As we talked about above, by using the 10A port and knob setting, you will only be able to read down to 0.01A or 10mA. Most of my systems use more than 10mA so the 10A setting and port works well enough. If you’re trying to measure very low power (micro or nano amps) the 200mA port with the 2mA, 200uA, or 20uA could be what you need.

alt text

Remember: If your system has the potential to use more than 100mA you should start with the red probe plugged into the 10A port and 10A knob setting.

With sub $50 digital multimeters, the measurements you are likely to take are just trouble shooting readings, not scientific experimental results. If you really need to see how the IC uses current or voltage over time, use an Agilent or other high quality bench unit. These units have higher precision and offer a wide range of fancy functions (some include Tetris!). Bunnie Huang, hardware designer behind Chumby, uses high-precision current readings to trouble shoot boards during the final testing procedures of a Chumby. By looking at the current consumption of different boards that have failed (for example a given failed board uses 210mA over the normal), he could identify what was wrong with the board (when the RAM fails, it generally uses 210mA over normal). By pinpointing what may be potentially wrong, the rework and repair of boards is made much easier.

What Makes a Good Multimeter?

Everyone has his or her preference, but in general multimeters that have continuity are preferred. Every other feature is just icing on the cake.

Agilent Meter

There are fancy multimeters that are autoranging, meaning they automatically change their internal range to attempt to find the correct voltage, resistance, or current of the thing you’re poking at. Auto-ranging can be very helpful if you know how to use it. Generally speaking, autoranging multimeters are higher quality and generally have more features. So if someone gives you a multimeter with auto-range, put it to use! Just know how to get it into manual mode. A circuit’s voltage or current can fluctuate quite quickly. With some of the systems, the current or voltage is so sporadic that the auto-range can’t keep up sensibly.

A back-lit LCD is fancy, but when was the last time you measured your circuit in the dark? We generally steer clear of scary forests and situations that require us to test stuff in the middle of the night, but some people may want or need a dark-friendly multimeter.

A good click on the range selector is actually a major plus in our book. A soft knob is usually indicative of a shoddy meter.

Decent probes are a plus. Over time the leads will tend to break down at the flex point. We’ve seen wires come completely out of probes - and it’s always at the moment you need the probes to work! If you do break a probe, they are reasonably cheap to replace.

Auto-off is a great feature that is rarely seen on cheaper multimeters. This is a feature that can benefit beginners and advanced users alike, as it’s easy to forget to turn the meter off at 2AM. The SparkFun digital multimeter doesn’t have this feature, but luckily the meter is very low-power. We’ve left the multimeter for two days straight before the 9V battery began to get low. That said, don’t forget to turn your meter off!

You’re now ready to use your digital multimeter to start measuring the world around you. Feel free to start using it to answer many questions. I believe my LED is getting 20mA, is it really? How much voltage does a lemon have? Is a glass of water conductive? Can I use aluminum foil to replace these wires? A digital multimeter will answer these and many more questions about electronics.

Resources and Going Further

Now that you know the basics of how to use a digital multimeter, check out these tutorials to use your new skill:


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

BadgerHack

$
0
0

BadgerHack a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t426

Introduction

The Badger

What you hold in your hands is a powerful piece of technology. OK, it’s no tricorder, but it can do some pretty cool stuff. You can program the BadgerStick (the thin board) to control a myriad of electronics, like buttons, lights, and LCDs. The LED board can be used to display patterns or show text.

In the first section of this guide, we will show you how to solder headers on to your BadgerStick and LED board to make a complete badge.

Completed badge

On top of a sweet badge, you also get a development platform that you can use in lots of other projects. Once you have put your badge together, worn it with pride, and shown if off to everyone, you can remix it and make your own project!

The rest of the guide will focus on hacking your badge: how you can create your own graphics or add other electronics to make it do cool stuff.

Playing games on my BadgerStick

Suggested Reading

If you are new to soldering or electronics, we highly recommend you check out the following:

When you are ready to start hacking your badge, we definitely recommend reading:

Make Your Badge

Now that you have the Badger kit, let’s make a badge! When we ask you to solder a pin, you will want to keep a few tips in mind (click for larger image):

SparkFun Soldering Tips

If you need a refresher on how to solder, we recommend the How to Solder guide.

1. Solder the 8-pin male header to the LED board

Insert the 8-pin male header into the LED board with the pin ends facing out. Note that the pins are coming out of the top of the board (the side with the LEDs).

Put the male header into the LED board

Flip the board over and solder all the pins.

Solder the male header to the LED board

2. Solder the 8-pin female header to pins 2-9 on the BadgerStick

Insert the 8-pin female header into the holes labeled 2, 3, 4, 5, 6, 7, 8, 9 on the BadgerStick. Make sure that the pins are coming out of the top of the board (the side with all the electronics).

Put the female header into the BadgerStick

Flip the board over, and ensure that only the holes in the white box labeled “LED Array” are used. Solder all 8 pins.

Solder the female header to the BadgerStick

3. Solder the 3-pin female header to the game port on the BadgerStick

Insert the 3-pin female header into the holes labeled TX, GND, RX on the bottom of the board. The header should be coming out of the top of the board.

Put the 3-pin female header into the game port

Flip the board over, and solder all 3 pins.

Solder the 3-pin header to the BadgerStick

4. Solder the the red battery wire to the + battery pin (VBAT)

Poke the red battery wire through the backside of the BadgerStick on the pin labeled VBAT (“+” on the backside).

Red wire through the BadgerStick

Solder the red wire to the hole.

Solder the red wire

5. Solder the black battery wire to the - battery pin (GND)

Poke the black battery wire through the backside of the BadgerStick on the pin labeled GND (“-” on the backside).

Black wire through the BadgerStick

Solder the black wire to the hole.

Solder the black wire

Flip the BadgerStick over, and verify that the red wire is going to the hole labeled ‘+’ on the underside and that the black wire is going to the hole labeled ‘-’.

Battery wires going into the BadgerStick

6. Add batteries

Using a Phillips screwdriver, remove the screw from the battery pack.

Use a screwdriver to open the battery pack

Open the battery pack cover, and take note of the battery markings in the pack.

Markings in battery pack

Put the batteries in the pack as noted by the markings. The bumped end of the battery is + and the flat end is -.

Batteries in battery pack

Put the battery pack cover back on, and secure it with the screw.

Closed battery pack

7. Connect the LED board to the BadgerStick

Connect the LED board and BadgerStick, by sliding the headers together. Ensure the LEDs and electronics on the BadgerStick are facing the same direction.

Connected LED board to BadgerStick

Flip the boards over, and double-check your solder connections:

  • 8-pin male header soldered to the pins in the white box on the LED board
  • 8-pin female header soldered to the pins in the white box on the BadgerStick labeled “LED Array”
  • 3-pin female header soldered to the pins in the white box on the BadgerStick labeled “Game Port”
  • Red battery wire soldered to + Battery pin
  • Black battery wire soldered to the - Battery pin

Back of completed badge electronics

Before you stick your electronics to the badge, turn on the battery pack to make sure everything is working. Troubleshooting your board will be a lot easier if it’s not adhered to the badge.

8. Affix the components to the badge

Add one piece of double-sided foam tape to the LED board and another piece to the BadgerStick.

Add tape to LED board and BadgerStick

Add another two pieces of foam tape on top of the existing foam tape.

Add another layer of tape

Stick the LED board and BadgerStick to the front of the plastic badge.

Put electronics on badge

Put one more piece of foam tape on the battery pack on the side with the screw.

Tape on the battery pack

Stick the battery pack to the back of the plastic badge. To prevent the wires from hanging out, wrap them around the side of the badge as in the picture.

Battery pack on badge

9. Turn it on

Turn your badge over, and find the little switch on the battery pack. Flip it to “ON.”

Fire it up!

Flip your badge back over. Wait about 3 seconds, and you should see the LED matrix activate!

Light it up!

10. Badger it up!

You are now the proud owner of a SparkFun Badger badge! Attach a lanyard…

Attach a lanyard

…and wear it with pride.

Wearing the Badger

Hack Your Badge

What do you do with your badge after the event? Well, you can hack it!

To program the BadgerStick, you will need the Arduino Integrated Development Environment (IDE). Find the section below about installing and configuring Arduino for your particular operating system (Windows, OS X, or Linux), and then move on to “Install Libraries.”

Install and Configure Arduino (Windows)

Download and Install Arduino

Download the latest Arduino IDE from arduino.cc or by clicking the button for Arduino 1.6.1.

Download Arduino 1.6.1

Double-click the downloaded executable, and follow the instructions to install Arduino.

Install COM Port Drivers

Navigate to FTDI’s Virtual COM Port drivers page, and click to download the “setup executable.”

Double-click on the executable to run it, and follow the prompts to install the Virtual COM Port drivers.

Extracting VCP drivers

Update boards.txt

We need to modify a file in the Arduino installation in order to work with the BadgerStick. Download the hardware files for BadgerStick support:

Download BadgerStick Support Files

Unzip and navigate to the folder BadgerStick_Support_Files/hardware/arduino/avr. Copy the boards.txt file.

Copy boards.txt

Paste them in <your Arduino installation folder>/hardware/arduino/avr, agreeing to overwrite/replace any existing files.

Paste boards.txt

That’s it! Move on to the “Install Libraries” section.

Install and Configure Arduino (OS X)

Install Java

If you do not have Java already installed, you will need it for Arduino. Go to Java for OS X 2014-001, and click the Download button.

Use Finder to navigate to your Downloads folder, and double-click the downloaded file to mount the image.

Download Java for OS X

Double-click the JavaForOSX.pkg file to run the installer.

Install Java for OS X

Follow the instructions to install Java 6.

Download and Install Arduino

Download the latest Arduino IDE from arduino.cc or by clicking the button for Arduino 1.6.1 (for Java 6).

Download Arduino 1.6.1

Locate the downloaded file in Finder and double-click it to extract the .zip file.

Unzip the Arduino application

Drag the newly unzipped Arduino application to the Applications folder.

Copy Arduino to the Applications folder

Install COM Port Drivers

Navigate to FTDI’s Virtual COM Port drivers page and click to download the latest version of the VCP drivers for Mac OS X x64 (64-bit).

Locate the downloaded driver file in Finder, and double-click it to mount the drivers as a disk image.

Mount COM drivers image

If you are on OS X 10.3, double-click the FTDIUSBSerialDriver_10_3. If you have a newer version of OS X, double-click the other installer. Follow the on-screen instructions to install the Virtual COM Port drivers.

Install COM drivers

If you run into an error like “can’t be opened because it is from an unidentified developer,” you will need to change your application security settings. See this post on how to do that.

Update boards.txt

We need to modify a file in the Arduino installation in order to work with the BadgerStick. Download the hardware files for BadgerStick support:

Download BadgerStick Support Files

Double-click the downloaded zip file to unzip it. Navigate to <your Downloads folder>/hardware/arduino/avr, and copy boards.txt.

Copy boards.txt

Right-click (or ctrl+click) on the Arduino application (in Applications), and select “Show Package Contents.” This will allow you to browse the files within the Arduino application.

Contents of the Arduino package

Navigate to Contents/Resources/Java/hardware/arduino/avr, and paste boards.txt. Agree to replace (overwrite) the existing boards.txt.

Paste boards.txt in the Arduino package

That’s it! Move on to the “Install Libraries” section.

Install and Configure Arduino (Linux)

Download and Install Arduino

Note that these steps were performed on Ubuntu 14.04. They might differ depending on your particular flavor of Linux.

Download the latest Arduino IDE from arduino.cc or by clicking the button for Arduino 1.6.0 (64-bit).

Download Arduino 1.6.1

Navigate to your Downloads directory, right click on the downloaded tar file, and select “Extract Here.”

Untar the Arduino download

If you try and run the Arduino application, you might get an error like “java: not found.” Additionally, you may not have avr-gcc installed. So, open a command window, and enter the following:

sudo apt-get install openjdk-6-jre avr-libc gcc-avr

Enter ‘y’ when asked to install the additional packages.

Update boards.txt

We need to modify a file in the Arduino installation in order to work with the BadgerStick. Download the hardware files for BadgerStick support:

Download BadgerStick Support Files

Unzip and navigate to the folder BadgerStick_Support_Files/hardware/arduino/avr. Copy the boards.txt file.

Copy boards.txt

Paste them in <your Arduino installation folder>/hardware/arduino/avr agreeing to overwrite/replace any existing files.

Paste boards.txt

Enable Double-Click to Run Application

To run the Arduino program, you can simply navigate to the downloaded (and extracted) directory, and run the arduino script:

cd ~/Downloads/arduino-1.6.0/
./arduino

To make the script run when you double-click it in a Files window, open up Files, go to Edit → Preferences.

Select preferences in Files

Go to the “Behavior” tab, and select “Ask each time.”

Select "Ask each time"

Close the Preferences window, and double-click the arduino script. You will be asked what to do. Select “Run” to start the Arduino application.

Choose to run the script

That’s it! Move on to the “Install Libraries” section.

Install Libraries

Now that the Arduino IDE is installed and configured, we need 2 important libraries for controlling the LED array. The following steps use screenshots from Windows, but the steps are the same for all operating systems.

Install the Chaplex Library

Charlieplexing is a slick way to control many LEDs with only a few input/ouptut (IO) pins. We rely on the Arduino Chaplex library to do much of this for us. Download the library to get started.

Download Chaplex Library

Run the Arduino IDE, and select Sketch → Import Library… → Add Library…

Add library in Arduino

Find the Chaplex.zip file you just downloaded, select it, and click open.

Install LED Array Library

We built a library on top of the Chaplex library that will let you draw shapes and text to the LED array with ease. Download that library here:

Download SparkFun_LED_8x7 Library

In the Arduino IDE, select Sketch → Import Library… → Add Library… Find the SparkFun_LED_8x7.zip file you just downloaded, select it, and click open.

Your First Badger Hack

Plug your BadgerStick (with the LED array attached) into any available USB port on your computer. Open the Arduino IDE, and you will be presented with a template sketch.

Arduino template sketch

Go to Tools → Board, and select the BadgerStick (3.3V, 4 MHz).

Select BadgerStick Board

Go to Tools → Port, and select the COM port that is associated with your BadgerStick (e.g. COM25 in the example screenshot).

Select COM Port

Now, we get to write some code! In the Arduino IDE, copy in the following code:

language:c
#include <SparkFun_LED_8x7.h>
#include <Chaplex.h>

// Global variables
static byte led_pins[] = {2, 3, 4, 5, 6, 7, 8, 9}; // Pins for LEDs

void setup() {

  // Initialize LED array
  Plex.init(led_pins);

  // Clear display
  Plex.clear();
  Plex.display();
}

void loop() {

  // Scroll text 1 time
  Plex.scrollText("Hello world", 1);

  // Wait 7 seconds to let the text finish scrolling
  delay(7000);

  // Stop scrolling the text
  Plex.stopScrolling();
  delay(2000);
}

Notice that we had to include both the SparkFun_LED_8x7 library and Chaplex library in the code. We initialized the LED display (lovingly called “Plex”) and scroll the phrase “Hello world” across the screen once, wait 2 seconds, and repeat.

Press the “Upload” button in the top left of the Arduino IDE to compile and send the program to the BadgerStick. If you are asked to save the file, you can choose to save your current project, or just press “Cancel” to continue uploading (without saving).

Arduino upload button

Wait while the IDE compiles and uploads the code. Once it finishes, you should see some pretty text scrolling across your LED array!

Hello world

Let’s make some shapes! The SparkFun_LED_8x7 library is also capable of creating basic shapes. Delete the code you have in the current Arduino window, and copy in the following:

language:c
#include <SparkFun_LED_8x7.h>
#include <Chaplex.h>

// Global variables
byte led_pins[] = {2, 3, 4, 5, 6, 7, 8, 9}; // Pins for LEDs
byte i;

void setup() {

  // Initialize and clear display
  Plex.init(led_pins);
  Plex.clear();
  Plex.display();
}

void loop() {

  // Clear the display buffer
  Plex.clear();

  // Draw a line (x0, y0, y1, y1)
  Plex.line(1, 4, 1, 6);

  // Draw a rectangle (x, y, width, height)
  Plex.rect(0, 0, 3, 3);

  // Draw a filled circle (x, y, radius)
  Plex.circleFill(5, 4, 2);

  // Display our shapes and wait a second before repeating
  Plex.display();
  delay(1000);
}

Upload the new code, and your LED board should show some fun shapes.

Make a Game

OK, so the display might be small (don’t get your hopes up of running Doom on an 8x7 monochrome display). However, we can use it to display information and even play some basic games.

Remember the game Breakout? I don’t, really, but the concept was cool. Let’s make a Breakout clone on our BadgerStick!

Hardware

Required Components

We will need a few other components to make a simple controller for the BadgerStick.

More Soldering

To begin, snap off 15 pins from the break-away headers, and solder them to the through-holes on the side opposite the LED array of the BadgerStick.

Solder pins to the BadgerStick

Solder the Thumb Joystick to the Thumb Joystick Breakout board.

Solder joystick to breakout board

Snap off 5 pins from the break-away headers, and solder them to the through-holes on the Joystick Breakout Board.

Solder pins to joystick breakout board

Connections

Place the BadgerStick in the breadboard with pin 10 in position i13 and pin 5V in position i27.

Connect the rest of the components as follows:

ComponentBreadboard
Thumb Joystick Breakout*i7 (VCC)i6 (VERT)i5 (HOR)i3 (GND)
Pushbuttonc20c22f20f22
Pushbuttonc28c30f28f30
Jumper Wire( - )g30
Jumper Wire( - )g25
Jumper Wire( - )g22
Jumper Wire( - )j3
Jumper Wirej7g23
Jumper Wirej6g18
Jumper Wirej5g17
Jumper Wireg21g28

* Pins not listed are not used.


Gaming badger

IMPORTANT: You can leave the battery pack soldered into the BadgerStick if you desire. If you remove the battery pack, you will need to supply power through another means, such as a USB port or a USB extension cable.

You should now have a makeshift game controller with a tiny LED screen!

Gaming badger with batteries

The Code

In a new Arduino sketch window, copy in the following code:

language:c
/****************************************************************
BadgerHack_Breakout.ino

This code is beerware; if you see me (or any other SparkFun
employee) at the local, and you've found our code helpful, please
buy us a round!

Distributed as-is; no warranty is given.
****************************************************************/

#include <SparkFun_LED_8x7.h>
#include <Chaplex.h>

// Constants
#define DEBUG                1
#define FPS                  60
#define SENSITIVITY          100
#define MAX_X_SPAN           127
#define PADDLE_SIZE          2
#define INITIAL_BALL_SPEED   0.1
#define BALL_SPEED_INC       0.004
#define PAUSE_BEFORE_SHOOT   1000    // ms
#define ROW_SIZE             7
#define COL_SIZE             8
#define FIELD_SIZE           ROW_SIZE * COL_SIZE

// Pin definitions
#define RNG_SEED_PIN  2
#define X_PIN         0
#define Y_PIN         1
#define BUTTON_1_PIN  3
#define BUTTON_2_PIN  4

// Global variables
byte led_pins[] = {2, 3, 4, 5, 6, 7, 8, 9}; // Pins for LEDs
uint16_t horz_zero;
uint16_t vert_zero;
uint8_t paddle_size;

// Setup
void setup() {

#if DEBUG
  Serial.begin(9600);
  Serial.println(F("Breakout demo for BadgerHack"));
#endif

  // Initialize and clear display
  Plex.init(led_pins);
  Plex.clear();
  Plex.display();

  // Seed our random number generator
  randomSeed(analogRead(2));

  // Calibrate our joystick by finding the center
  horz_zero = analogRead(X_PIN);
  vert_zero = analogRead(Y_PIN);
}

// Loop - play the game forever
void loop() {

  // Play the game inifinity times
  playGame();
}

/****************************************************************
 * Functions
 ***************************************************************/

// Play the game
void playGame() {

  // Calculate the new x max based on paddle size
  paddle_size = PADDLE_SIZE;
  uint16_t field_max = MAX_X_SPAN -
                        (paddle_size - 1) * (MAX_X_SPAN / 7);

  // Create new game variables
  boolean playing;
  boolean win;
  unsigned long frame_start;
  uint8_t millis_per_frame = 1000/FPS;
  unsigned long game_start;
  boolean ball_moving;
  int16_t paddle_move;
  int16_t paddle_field = field_max / 2;
  uint8_t paddle_x;
  float ball_x = 3;
  float ball_y = 5;
  float inc_x;
  float inc_y;
  float ball_speed;
  uint8_t ball_round_x;
  uint8_t ball_round_y;
  uint16_t ball_theta;
  boolean can_deflect = true;
  int i;
  uint8_t x;
  uint8_t y;
  byte the_wall[] = { 1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,
                      0,0,0,0,0,0,0,
                      0,0,0,0,0,0,0,
                      0,0,0,0,0,0,0,
                      0,0,0,0,0,0,0 };
#if DEBUG
  Serial.println("New game!");
  Serial.print("Field span: ");
  Serial.println(field_max);
#endif

  // Note when we start the game (so we can shoot the ball)
  game_start = millis();
  ball_moving = false;

  // Assign an initial direction and speed to the ball
  ball_theta = initBallTheta();
  ball_speed = INITIAL_BALL_SPEED;

  // Play the game until we win or lose
  playing = true;
  while ( playing ) {

    // For each frame, aim for refresh rate
    frame_start = millis();

    // Check for a win condition
    win = true;
    for ( i = 0; i < FIELD_SIZE; i++ ) {
      if ( the_wall[i] > 0 ) {
        win = false;
        break;
      }
    }
    if ( win ) {
      Plex.clear();
      Plex.scrollText("You win!", 1);
      delay(5000);
      Plex.stopScrolling();
      playing = false;
    }

    // Read the value of the joystick and map to a movement
    paddle_move = analogRead(X_PIN) - horz_zero;
    paddle_move = paddle_move / SENSITIVITY;
#if 0
    Serial.print("Moving: ");
    Serial.println(paddle_move);
#endif

    // Move the paddle and calculate its real x position
    paddle_field = paddle_field + paddle_move;
    if ( paddle_field <= 0 ) {
      paddle_field = 0;
    }
    if ( paddle_field >= field_max ) {
      paddle_field = field_max;
    }
    paddle_x = map(paddle_field, 0, field_max,
                   0, 6 - (paddle_size - 1));

    // If the ball has been shot, move it
    if ( ball_moving ) {

      // Calculate the ball's new position
      ball_x += ball_speed * cos(ball_theta * (M_PI / 180));
      ball_y += ball_speed * sin(ball_theta * (M_PI / 180));

      // Check the ball against the paddle
      if ( (ball_y > 6) &&
            (ball_x >= paddle_x) &&
            (ball_x <= (paddle_x + (paddle_size - 1))) &&
            can_deflect ) {
        ball_y = 6 - abs(6 - ball_y);
        ball_theta = 360 - ball_theta;
        can_deflect = false;
      }

      // Allow ball to be deflected once it leaves paddle range
      if ( ball_y <= 6 ) {
        can_deflect = true;
      }

      // Check if the ball moved past the paddle (lose)
      if ( ball_y > 7 ) {
#if DEBUG
        Serial.print("LOSE! x=");
        Serial.print(ball_x);
        Serial.print(" y=");
        Serial.print(ball_y);
        Serial.print(" Paddle:");
        Serial.print(paddle_x);
        Serial.print("-");
        Serial.println(paddle_x + paddle_size - 1);
#endif
        playing = false;
      }

      // Check the ball against the walls (and bounce!)
      if ( ball_y < 0 ) {
        ball_y = abs(ball_y);
        ball_theta = 360 - ball_theta;
      }
      if ( ball_x < 0 ) {
        ball_x = abs(ball_x);
        ball_theta = (540 - ball_theta) % 360;
      }
      if ( ball_x > 6 ) {
        ball_x = 6 - abs(6 - ball_x);
        ball_theta = (540 - ball_theta) % 360;
      }

      // Bounce if we hit a block above the ball
      i = (floor(ball_y) * ROW_SIZE) + roundFloat(ball_x);
      if ( the_wall[i] > 0 ) {
        the_wall[i]--;
        ball_y = (i / ROW_SIZE) + abs((i / ROW_SIZE) - ball_y);
        ball_theta = 360 - ball_theta;
        ball_speed += BALL_SPEED_INC;
      }

      // Bounce if we hit a block below the ball
      i = (ceil(ball_y) * ROW_SIZE) + roundFloat(ball_x);
      if ( the_wall[i] > 0 ) {
        the_wall[i]--;
        ball_y = (i / ROW_SIZE) - abs((i / ROW_SIZE) - ball_y);
        ball_theta = 360 - ball_theta;
        ball_speed += BALL_SPEED_INC;
      }

      // Bounce if we hit a block to the left the ball
      i = (roundFloat(ball_y) * ROW_SIZE) + floor(ball_x);
      if ( the_wall[i] > 0 ) {
        the_wall[i]--;
        ball_y = (i / ROW_SIZE) + abs((i / ROW_SIZE) - ball_y);
        ball_theta = (540 - ball_theta) % 360;
        ball_speed += BALL_SPEED_INC;
      }

      // Bounce if we hit a block to the right the ball
      i = (roundFloat(ball_y) * ROW_SIZE) + ceil(ball_x);
      if ( the_wall[i] > 0 ) {
        the_wall[i]--;
        ball_y = (i / ROW_SIZE) - abs((i / ROW_SIZE) - ball_y);
        ball_theta = (540 - ball_theta) % 360;
        ball_speed += BALL_SPEED_INC;
      }

    } else {

      // See if we need to start moving the ball
      if ( millis() >= game_start + PAUSE_BEFORE_SHOOT ) {
        ball_moving = true;
      }
    }

    // Round the ball's position to the nearest pixel
    ball_round_x = roundFloat(ball_x);
    ball_round_y = roundFloat(ball_y);

    // Draw tbe wall, the paddle, and the ball
    Plex.clear();


    for ( y = 0; y < COL_SIZE; y++ ) {
      for ( x = 0; x < ROW_SIZE; x++ ) {
        if ( the_wall[(x * ROW_SIZE) + (ROW_SIZE - 1 - y)] > 0 ) {
          Plex.pixel(x, y);
        }
      }
    }
    for ( i = 0; i < paddle_size; i++ ) {
      Plex.pixel(7, map(paddle_x + i, 0, 6, 6, 0));
    }
    Plex.pixel(ball_round_y, map(ball_round_x, 0, 6, 6, 0));
    Plex.display();

    // Wait until we reach our target end of frame
    while ( millis() < frame_start + millis_per_frame ) {
      delay(1);
    }
 #if 0
   Serial.print("FPS: ");
   Serial.println( 1000 / (millis() - frame_start) );
 #endif

  }
}

// Create a randomized ball launch angle
unsigned int initBallTheta() {

  unsigned int theta;

  // Choose an angle  in the range of 210-239 deg or 301-330 deg
  theta = random(0, 60);
#if DEBUG
  Serial.print("RNG:");
  Serial.print(theta);
#endif
  if ( theta < 30 ) {
    theta = 210 + theta;
  } else {
    theta = 271 + theta;
  }

#if DEBUG
  Serial.print(" Theta:");
  Serial.println(theta);
#endif

  return theta;
}

// Rounds a floating value to an integer
int roundFloat(float x) {
  if ( x >= 0 ) {
    return (int) (x + 0.5);
  }
  return (int) (x - 0.5);
}

Play

Upload the program to the BadgerStick, and prepare to play!

Use the joystick to move the paddle back and forth to bounce the ball. You win when you “break” all the lights on the top part of the screen. You lose if you let the ball go past your paddle.

Let's play Breakout!

You might notice that we do not use the 2 buttons in the game. Breakout only requires a joystick. However, you now have a basic platform that is perfect for creating games!

What other games would you want to make? Here are some ideas:

Resources and Going Further

You’ve built the badge, programmed it with some new graphics, and created a basic game. Now what?

If this is your first time using Arduino, you have just opened up a new, wide world of embedded systems. Check out Arduino’s site for more information on the various platforms and software. SparkFun also carries a large number of Arduino-compatible boards.

If you want to use your BadgerStick for other cool projects, we won’t stop you. In fact, we might encourage it:

Hack on, fellow creators.

If you make something cool with your badge, share it with #BadgerHack, or create a tutorial on hackster.io.

Resources

You made it all the way to the end! Congratulations. Have a badger.

Badger badger badger badger


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

Light Up Pennant with E-Textiles

$
0
0

Light Up Pennant with E-Textiles a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t427

Introduction

Here’s a fun way to decorate a game room, classroom, or office - a twinkling pennant. Rather than walk you step-by-step through one project, we’ll be showing you some general tips for creating your own unique pennant with a few different types of LilyPad components.

alt text

Suggested Reading

This is a beginner to intermediate project, depending on the type of hardware you choose. You should be comfortable sewing a LilyPad LED using conductive thread.

Here are a few tutorials we suggest reading before you begin:

Materials and Tools

This project is pretty open ended - like a choose your own adventure! If you’d like to go with a non-programming project, we recommend using a LilyTwinkle or LilyTiny (you can always reprogram them later). You can also create this project using a LilyPad Arduino Simple Board and LEDs (or any additional LilyPad components you’d like). Choose one of the following options for your project:

Option A: Protosnap LilyTwinkle

The Protosnap LilyTwinkle is a great choice if you’d like a twinkling effect and the ability to test out the circuit before you get to sewing.

ProtoSnap - LilyTwinkle

DEV-11590
$19.95
1

Option B: LilyTiny

The LilyTiny is pre-programmed with four different modes to choose from - blink, heartbeat, breathing, and random fade, depending on which numbered petal you sew the LEDs to.

Option C: LilyPad Arduino

For the most flexibility in your project’s behavior, try programming a LilyPad Arduino Simple Board to light LEDs up in a twinkle, blink, or whatever pattern you choose. We will not be covering the programming side of things in this tutorial, for an introduction to programming take a look at one of our Programming tutorials.

Additional Supplies:

  • Felt pennant - either store bought or hand cut. We found the pennants used in this tutorial at a craft store for 99 cents each.
  • Scissors
  • Hot Glue Gun
  • Optional: Heat-N-Bond and iron for adhering shapes and letters
  • Insulating material (examples: acrylic/fabric paint, fabric glue, iron-on interfacing, or extra fabric)
  • Decorative craft supplies: extra felt, fabric, embroidery floss, beads, jewels, etc.

Step 1: Design Time

Time to get creative and decide what image you’d like to light up. Take time to sketch out ideas or collect pieces of felt, fabric, or other crafts to help you devise an interesting design. There are lots of photos online of retro pennants for sports teams to help inspire you if you are looking for a vintage style.

Planning Tips:

  • Decide if you want to see the components and stitching - strategic use of lettering or shapes can cover up the LilyPad pieces and let only the light from the LED shine through.

  • Leave enough room to sew - plan your design with enough space to attach components without crowding them too close together and risking conductive thread short circuits.

  • Lay out the pieces of your project first before attaching anything to the felt - this allows you to move things around and redesign as needed.

  • Leave easy access to the battery and/or power switch. For projects using a LilyPad Coin Cell Battery Holder - Switched, you’ll need to leave some room on the side to slide the battery in/out when replacing. For LilyPad Arduino Simple projects, the battery can stay attached, but make sure to leave space to plug in the FTDI to recharge and access to the slide switch on the LilyPad for powering on/off.


An example of a brainstorming sketch:

alt text



For the example circuit diagrams in this tutorial, we’ll be using this key:

alt text

Protosnap LilyTwinkle Example

Maya chose to make a pennant with the University of Colorado Buffaloes logo sparkle using the ProtoSnap LilyTwinkle. Here’s the layout she used to plan around the buffalo’s outline. She also decided to put the battery holder on the back of the pennant, making sure there was enough space to sew without hitting the LilyTwinkle or LEDs on the reverse side.

alt text

alt text

LilyTiny Example

Angela decided to use the LilyTiny’s breathing fade mode programmed on petal 0 to light up a robot’s eyes. She planned her circuit with two LEDs attached to one petal to make them light up at the same time. She also planned the battery holder to be attached to the reverse side of the felt.

alt text

alt text

alt text

LilyPad Arduino Example

For her mountain scene pennant, Maya planned the LEDs to light up the peaks and the LilyPad Arduino to be on the reverse side of the felt, again for easy access. She designed as much of the stitching as possible to be hidden by the felt mountain cut outs she created.

alt text

alt text

Step 2: Attach Components

Once you’ve settled on a final design layout, arrange your components on the felt. Make sure to double check that the positive (+) side of the LED is set up to connect to a numbered petal on the LilyTwinke, LilyTiny, or LilyPad Arduino, and the negative (-) sides are arranged to easily connect to the negative petal on the boards.

If you’d like to double check everything before sewing, use alligator clips to connect the components together and power up. Make sure to remove the battery before you begin sewing with conductive thread.

alt text

Using a small dab of hot glue, attach the components in place on the felt so they don’t move around while you sew. Be careful not to accidentally fill the sewing holes with glue, especially on smaller components like the LilyPad LEDs.

alt text

To help plan your stitching, use tailor’s chalk or a pen to draw stitch lines.

alt text

Step 3: Conductive Thread Connections

Make sure to unplug any power source or battery while sewing with conductive thread to avoid accidental short circuits.

alt text

Carefully sew the connections you planned, making sure to loop snugly around the sewing holes at least four times for a good electrical connection.

alt text

Step 4: Power It Up

Once all the stitching is done, check for any loose knot tails or places where the conductive thread may be accidentally touching components or other stitches. After everything checks out, insert (or plug in) the battery for the project and power it up using the ON/OFF slide switch. Examine your project for any LEDs that are not lighting up properly and recheck your connections if necessary.

alt text

alt text

Step 5: Decoration and Finishing Touches

Add some felt loops for authentic pennant feel and hang on a wall or use a wooden dowel to make a wave-able piece of team spirit.

alt text

For extra protection of your project, or if there are any spots on where thread is exposed and could use some insulation, take a look at our Insulation Techniques for e-Textiles. Since this project will most likely be hanging on a wall and not being worn, you will not have to worry as much about the project bending and short circuiting. Be aware if you plan to hang it near any metallic surface, the exposed thread or components could touch the metal and short circuit.

Resources and Going Further

Now that you’ve finished up a decorative project, how about trying a costume or wearable project?


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

SHT15 Humidity and Temperature Sensor Hookup Guide

$
0
0

SHT15 Humidity and Temperature Sensor Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t429

SHT15 Overview

The SHT15 is an easy to use, highly accurate, digital temperature and humidity sensor. All you need is two lines for 2-wire communication, and you’ll have relative humidity and temperature readings to help you sense the world around you!

SHT15 Breakout Board

Features you should know about this sensor:

  • Operating Voltage: 2.4V min - 5.5V max
  • 2 factory calibrated sensors for relative humidity & temperature
  • Digital 2-wire interface (Not I2C, but similar)
  • Measurement range: 0-100% RH
  • Absolute RH accuracy: +/- 2% RH (10…90% RH)
  • Repeatability RH: +/- 0.1% RH
  • Temp. accuracy: +/- 0.3°C @ 25°C
  • Precise dewpoint calculation possible
  • Fast response time
  • Low power consumption (typ. 30 µW)
  • Here’s the datasheet

This sensor is ideal for environmental sensing and data logging and can be used in applications ranging from a weather station to a humidor control system.

Suggested Reading

Things you might need to know:

Hooking It Up

Wiring up the SHT15 is very easy! We recommend soldering four male headers to the breakout board. You can then attach it to an Arduino or Arduino-Compatible Board, such as our RedBoard.

SHT15 with Headers

Connections: Breakout board to Arduino

There are only four pins that need to be hooked up in order to start using this sensor in a project. One for VCC, one for GND, and two data lines.

  • VCC → 3.3V or 5V
  • GND → GND
  • Data → Any I/O Pin
  • SCK → Any I/O Pin

You can connect this sensor directly to the female headers on your Arduino like so…

SHT15 Arduino Hookup

A SHT15 connected to a SparkFun RedBoard, using pins A4 and A5 as communication and pins A2 and A3 as power.

Or, you can wire it up on a breadboard.

SHT15 Circuit

A SHT15 connected to the SDA(A4) and SCL(A5) lines on a SparkFun RedBoard.

2-Wire Interface

Please note that the SHT15 has a 2-wire interface that is similar to I2C but is NOT I2C. You may use the SDA and SCL lines to communicate with this sensor so long as they are connected to A4 and A5 on your Arduino or Arduino compatible board. You may use any other I/O pins for the Data and SCK lines as well.

Heads up! The datasheet states: “The sensor cannot be addressed by I2C protocol; however, the sensor can be connected to an I2C bus without interference with other devices connected to the bus. The controller must switch between the protocols.” We have experimented with this with mixed results. Make sure you test extensively if adding this to an I2C bus containing other sensors. Should bus contention be a problem, simply use two other I/O pins to communicate with the SHT15.

Multiple Sensors

Unfortunately, the SHT15 has its address hard-coded, making it non-addressable. As a result, you may not have more than one SHT15 hooked up to any two pins at a given time. The 2-wire protocol used for this sensor does not allow for multiple of the same sensor to be on the bus, but it does allow multiple I2C devices to share the same bus as the SHT15, as mentioned above. In order to use more than one SHT15, you will need to use a dedicated pin for each sensor’s Data line. You may share the Clock (SCK) line between multiple sensors. For example, you could have one sensor’s Data line connected to pin 8 and another connected to pin 7, while both are sharing pin 9 as the SCK line. Declaring this using the library mentioned in the next section would look like this:

language:c
//Create two instances of the SHT1X sensor
SHT1x sht15(8, 9);//Data, SCK
SHT1x sht15_2(7, 9);//Data, SCK

Pull-up Resistor

As suggested in the datasheet, a 10K&ohm; pull-up resistor was added to the Data line. Should you find yourself in a situation where you want this sensor and another I2C sensor with pull-up resistors to share the I2C lines, you may cut the trace in between the solder jumper labeled PU. If you ever need that pull-up again, simply place a blob of solder between the two pads.

Pull-up

Codebender Example

To get started immediately, here is a Codebender example that uses the SHT1x library.

Once you’ve uploaded the code, connect using this serial terminal to see the output.

If you prefer to install the Arduino library and work within the Arduino IDE, read on.

SHT1X Arduino Library and Example

We’ve written an Arduino library and some example code to make using the SHT15 easy to get up and running. Grab the SHT15 library for Arduino from the SHT15 GitHub Repository , or you can download the files directly from the button below.

SHT15 Arduino Library

Need help using the Library Manager or want to install the library the old fashioned way? Visit our Arduino Library tutorial, for more information.

Once the library is installed, open Arduino, and expand the examples menu. You should see the SparkFun SHT_1X submenu.

Example Sketch

Load this example onto the Arduino. Open the serial terminal at 9600bps. You will see the current humidity and temperature in the room!

Here is the same example sketch if you would rather copy and paste:

language:c
/******************************************************************************
SHT15 Example
Joel Bartlett @ SparkFun Electronics
16 Sept 2015
https://github.com/sparkfun/SparkFun_ISL29125_Breakout_Arduino_Library

This example shows how to get the temperature in F or C and humidity
Developed/Tested with:
SparkFun RedBoard
Arduino IDE 1.6.5

Connections:
GND  -> A2
Vcc  -> A3
DATA -> A4
SCK  -> A5

Requires:
SparkFun_SHT1X Arduino Library
https://github.com/sparkfun/SHT15_Breakout/

This code is beerware.
Distributed as-is; no warranty is given.
******************************************************************************/
#include <SparkFun_SHT1X.h>

//variables for storing values
float tempC = 0;
float tempF = 0;
float humidity = 0;

//Create an instance of the SHT1X sensor
SHT1x sht15(A4, A5);//Data, SCK

//delacre output pins for powering the sensor
int power = A3;
int gnd = A2;

void setup()
{
  Serial.begin(9600); // Open serial connection to report values to host

  pinMode(power, OUTPUT);
  pinMode(gnd, OUTPUT);

  digitalWrite(power, HIGH);
  digitalWrite(gnd, LOW);
}
//-------------------------------------------------------------------------------------------
void loop()
{
  readSensor();
  printOut();
  delay(1000);
}
//-------------------------------------------------------------------------------------------
void readSensor()
{
  // Read values from the sensor
  tempC = sht15.readTemperatureC();
  tempF = sht15.readTemperatureF();
  humidity = sht15.readHumidity();
}
//-------------------------------------------------------------------------------------------
void printOut()
{
  Serial.print(" Temp = ");
  Serial.print(tempF);
  Serial.print("F, ");
  Serial.print(tempC);
  Serial.println("C");
  Serial.print(" Humidity = ");
  Serial.print(humidity);
  Serial.println("%");
}

Resources and Going Further

You should now have a good idea of how to add humidity and temperature sensing into your next project. Need some inspiration? Check out these other tutorials:

Resources:


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

SX1509 I/O Expander Breakout Hookup Guide

$
0
0

SX1509 I/O Expander Breakout Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t434

Introduction

Is your Arduino running low on GPIO? Looking to control the brightness of 16 LEDs individually? Maybe blink or breathe a few autonomously? Want to delegate scanning an 8x8 matrix of 64 buttons to another controller? These are all tasks the for which the SX1509 16-IO Expander was made!

SX1509 IO Expander Breakout

The SX1509 is a 16-channel GPIO expander with an I2C interface– that means with just two wires, your microcontroller can interface with 16 fully configurable digital input/output pins.

But, the SX1509 can do so much more than just simple digital pin control. It can produce PWM signals, so you can dim LEDs. It can be set to blink or even breathe pins at varying rates. And, with a built-in keypad engine, it can interface with up to 64 buttons set up in an 8x8 matrix.

SX1509 Demo circuit

An SX1509 controlling three LEDs, monitoring three buttons and a 12-button keypad, and producing SPI signals to drive a Serial 7-Segment Display.

It’s a really cool chip and a great tool for expanding the capability of your Arduino or any other I2C-capable microcontroller.

Covered In this Tutorial

This tutorial will serve to familiarize you with all things SX1509 and the SparkFun Breakout. Then we’ll demonstrate how take advantage of all of the I/O expander’s features using an Arduino-compatible microcontroller and our SX1509 Arduino Library.

The tutorial is split into the following sections:

Suggested Reading

Before delving into this tutorial, there are a few concepts you should already be somewhat familiar with. Check out these related tutorials:

  • I2C Communication– The SX1509 is controlled over an I2C interface. Learn all about this powerful 2-wire interface.
  • Logic Levels– While most Arduino’s operate at 5V, the SX1509 works at 3.3V. The GPIO are, at least, 5V tolerant!
  • Pulse-Width Modulation (PWM)– All of the SX1509’s output pins are capable of producing a PWM signal. That means you can control the brightness of LEDs!

Pulse-width Modulation

An introduction to the concept of pulse width modulation.

Logic Levels

Learn the difference between 3.3 V and 5 V devices.

Light-emitting Diodes (LEDs)

Learn the basics about LEDs as well as some more advanced topics to help you calculate requirements for projects containing many LEDs.

I2C

An introduction to I2C, one of the main embedded communications protocols in use today.

SX1509 Breakout Board Overview

There’s a lot going on on the SX1509 Breakout. GPIO and power buses are broken out in every-which direction, and configurable jumpers cover most of the rest of the board.

Top side of the SX1509 Breaokut

This section will cover all things SX1509 Breakout, so you can get the most out of the board’s features.

I2C and Power Input Headers

These two headers at the top and bottom of the breakout board are the input and control headers to the board. This is where you can supply power to the SX1509, and where your I2C signals – SDA and SCL – will terminate.

Input headers

These headers break out the following pins:

Pin LabelTypeDescription
INTOutputActive low programmable interrupt
RSTInputActive low reset (pulled high on-board)
GNDPowerGround (0V)
3V3PowerMain supply voltage (1.425-3.6V)
SDAI2CI2C serial data line
SCLI2CI2C serial clock line
OSCClock In/OutOptional clock input, or programmable clock signal output

The SDA and SCL pins each have 10kΩ resistors pulling them up to 3.3V. These resistors can be disconnected by cutting the SJ1 jumpers.

RST– the SX1509’s active-low reset input– works just like an Arduino reset pin. If the pin is pulled LOW, the SX1509 will power down. When RST rises, the SX1509 will turn back on, but all of its settings will be cleared out. The breakout board includes a 10kΩ resistor pulling RST HIGH, so you ignore this pin if you don’t need the reset functionality.

INT is a very handy interrupt output, especially if you’re using any SX1509 pins as inputs. It can be configured to go LOW whenever a pin state changes. The breakout board includes a 10kΩ resistor pulling INT HIGH.

Finally, OSC breaks out the SX1509’s OSCIO pin – the oscillator input/output. This highly-configurable pin can be used as either the clock input for the SX1509 (if you don’t want to use its internal 2MHz clock), a clock output (producing an up to 2MHz square wave signal), or a simple digital I/O.

Required and optional pins: The pairs of power and I2C pins are the only ones required for interfacing with the SX1509. RST, INT, and OSC are all optional, they can be left disconnected if you don't need the feature they provide.

I/O and GND/VCC Breakouts

The real meat of the breakout board are the pairs of rows breaking out all sixteen I/O pins plus the power rails.

GPIO Breakouts

The SX1509 breaks its 16 I/O into two banks– bank A and bank B. Each bank can operate on a separate power supply, but by default they’re both set to 3.3V. Bank A is powered by VCC1, and bank B is supplied by VCC2. VCC1 and VCC2 can range between 1.2V and 3.6V, if you want to supply them externally. Check out the “Jumpers” section for more information on that.

Every I/O pin is capable of PWM and blink outputs, but only half of them can be set to “breathe” (blink with smooth transitions from on to off). Also, if you plan on using the SX1509 keypad driver, each I/O is relegated to either a row or column interface.

LED DriverKeypad
I/OPWMBlinkBreatheRowColumn
0&check;&check;&check;
1&check;&check;&check;
2&check;&check;&check;
3&check;&check;&check;
4&check;&check;&check;&check;
5&check;&check;&check;&check;
6&check;&check;&check;&check;
7&check;&check;&check;&check;
8&check;&check;&check;
9&check;&check;&check;
10&check;&check;&check;
11&check;&check;&check;
12&check;&check;&check;&check;
13&check;&check;&check;&check;
14&check;&check;&check;&check;
15&check;&check;&check;&check;

Ground (or Power) Rails

Running alongside the I/O breakouts are a pair of power rails. These rails can be distinguished by the bars of white silkscreen running between each pad.

GND or VCC rails

By default, these rails are both set to ground– handy if you want to fan out some active-low buttons, or current-sourced LEDs. Jumpers on the back side allow you to switch the rails from GND to either VCC1 or VCC2. You’ll need to cut the jumper between GND and the rail, then blob solder between the rail and VCC.

This bus is completely optional. Just don’t solder male pins into both rows of headers if you plan on using the breakout in a breadboard!

Address-Select Jumpers

Up to four SX1509’s can be connected to a single I2C bus, by configuring them to different addresses. The SX1509 has two pins devoted to I2C address selection: ADD0 and ADD1. Each of those pins are broken out to a jumper on the bottom of the board.

Address select jumpers

The board defaults each of those pins to GND, which sets the I2C address to 0x3E. To set either jumper to “1” (HIGH), grab a hobby knife, cut the trace connecting to “0”, and blob some solder between the center pad and “1”.

The four configurable addresses are listed on the back of the board, but for quick reference, they are:

ADD1ADD0I2C address
000x3E
010x3F
100x70
110x71

VCC1 and VCC2 Jumpers

SJ1 and SJ2 on the back-side of the board connect VCC2 and VCC1, respectively, to the 3V3 voltage supply input. So, if you’re delivering 3.3V to the board, each of the I/O banks will operate at 3.3V.

If you want to take advantage of the SX1509’s level-shifting capabilities by powering these banks at something other than VCC, cut the jumpers and plug any voltage between 1.2V and 3.6V into the VCC1 and/or VCC2 pins.

These supply buses are completely independent – so they can operate at different voltages.

Hardware Assembly

You’ll need to solder something into the SX1509 Breakout to use it, whether that something is male or female headers or wire is completely up to you and your intended application. If you’ve never soldered before, check out our PTH soldering tutorial.

One option we like, which keeps the board as breadboard-compatible as can be, is soldering male headers on the I/O banks, and female headers on either (or both) of the power/I2C headers.

Headers soldered into the SX1509

Then you can use male-to-male jumper wires to connect between your microcontroller and the breakout, and breadboard the rest of the I/O.

Installing the SparkFun SX1509 Arduino Library

Now that you’ve got the hardware all mostly figured out, it’s time to start programming! To help make using the SX1509 as painless as possible, we’ve written an Arduino library to help interface with it. Visit the SparkFun SX1509 Arduino Library GitHub repository, or click the button below to download the latest version of the library.

Download the SX1509 Arduino Library!

For help installing the library, check out our Installing an Arduino Library tutorial. If you downloaded the library as a ZIP, you can use Arduino’s Sketch>Include Library>Add .ZIP Library tool to automatically add it to your Arduino sketchbook.

The SparkFun SX1509 Arduino library includes all sorts of examples, which demonstrate specific features of the I/O expander. Navigate to File>Examples>SparkFun SX1509 IO Expander to check them out.

SX1509 Library examples

Quickly, we’ll walk you through a few quick examples that show off the I/O expander’s range of features.

Example: Digital In/Out and PWM

As with almost any I/O expander, each of the SX1509’s GPIO can be configured as simple digital inputs or outputs. So you can toggle LEDs on or off, monitor for button presses, or even bit-bang more advanced digital interfaces like SPI (probably nothing that’s timing-dependent though).

Here’s a quick example that shows how you can digitalWrite or digitalRead using the SX1509. If you want to follow along, hook up a circuit like below:

Fritzing circuit

Match up 3.3V, GND, SDA, and SCL between your Arduino and the SX1509 Breakout. Then connect an LED to I/O 15 – you can either configure it to source or sink current. And connect an active-low button to I/O 0.

Then throw this code onto your Arduino:

language:c
#include <Wire.h> // Include the I2C library (required)
#include <SparkFunSX1509.h> // Include SX1509 library

SX1509 io; // Create an SX1509 object

// SX1509 pin definitions:
// Note: these aren't Arduino pins. They're the SX1509 I/O:
const int SX1509_LED_PIN = 15; // LED connected to 15 (source ing current)
const int SX1509_BTN_PIN = 7; // Button connected to 0 (active-low)

bool ledState = false;

void setup()
{
  pinMode(13, OUTPUT); // Use pin 13 LED as debug output
  digitalWrite(13, LOW); // Start it as low
  // Call io.begin(<I2C address>) to initialize the I/O
  // expander. It'll return 1 on success, 0 on fail.
  if (!io.begin(0x3E))
  {
    // If we failed to communicate, turn the pin 13 LED on
    digitalWrite(13, HIGH);
    while (1)
      ; // And loop forever.
  }

  // Call io.pinMode(<pin>, <mode>) to set any SX1509 pin as
  // either an INPUT, OUTPUT, INPUT_PULLUP, or ANALOG_OUTPUT
  io.pinMode(SX1509_LED_PIN, OUTPUT);
  io.pinMode(SX1509_BTN_PIN, INPUT_PULLUP);

  // Blink the LED a few times before we start:
  for (int i=0; i<5; i++)
  {
    // Use io.digitalWrite(<pin>, <LOW | HIGH>) to set an
    // SX1509 pin either HIGH or LOW:
    io.digitalWrite(SX1509_LED_PIN, HIGH);
    delay(100);
    io.digitalWrite(SX1509_LED_PIN, LOW);
    delay(100);
  }
}

void loop()
{
  // Use io.digitalRead() to check if an SX1509 input I/O is
  // either LOW or HIGH.
  if (io.digitalRead(SX1509_BTN_PIN) == LOW)
  {
    // If the button is pressed toggle the LED:
    ledState = !ledState;
    io.digitalWrite(SX1509_LED_PIN, ledState);
    while (io.digitalRead(SX1509_BTN_PIN) == LOW)
      ; // Wait for button to release
  }
}

When you press the button down, the LED state should toggle. Check through the code to see how easy it is! Not all that different from Arduino code you may already be familiar with.

Getting Started with the SX1509 Library

To begin, include the “SparkFunSX1509.h” library (and the “Wire.h” library as well), and create an SX1509 object in the global area:

language:c
#include <Wire.h> // Include the I2C library (required)
#include <SparkFunSX1509.h> // Include SX1509 library

SX1509 io; // Create an SX1509 object

You’ll use that io object from here on out. To initialize the I/O expander – and to make sure it’s communicating correctly – call io.begin(<address>), where <address> is the I2C address of the expander (0x3E by default). Check the return value of begin() to make sure everything is hunky-dory.

language:c
if (!io.begin(0x3E))
{
  // If we failed to communicate, turn the pin 13 LED on
  digitalWrite(13, HIGH);
  while (1)
    ; // And loop forever.
}

Then you can use functions you should already be mostly familiar with to control the I/O. Just tag the io object onto the beginning of pinMode, digitalWrite and digitalRead, and go about your Arduino-business as normal!

Analog Output (PWM)

You can also use any I/O as an “analog” (PWM) output by using the analogWrite(<pin>, <0-255>) function – just like Arduino analog output! There are just a couple differences to be aware of:

  • ANALOG_OUTPUT: If you want a pin to produce PWM signals, call pinMode(<pin>, ANALOG_OUTPUT) in your setup. That will tell the SX1509 to initialize the pin as an “LED driver”.
  • Sinking Current: analogWrite(<pin>, <0-255>) assumes that the LED is hooked up in a current-sinking fashion – meaning the LED’s cathode (negative pin) is terminated into the SX1509. Thus, analogWriteing to 255 will actually pull the pin LOW, and 0 will set it HIGH.

Here’s some example code:

language:c
#include <Wire.h> // Include the I2C library (required)
#include <SparkFunSX1509.h> // Include SX1509 library

SX1509 io; // Create an SX1509 object

// SX1509 pin definitions:
// Note: these aren't Arduino pins. They're the SX1509 I/O:
const int SX1509_LED_PIN = 15; // LED connected to 15 (sourcing current)

void setup()
{
  io.begin(0x3E); // Initialize the SX1509

  // Set up a pin as an ANALOG_OUTPUT, if you want to use
  // pwm or other LED driver functions.
  io.pinMode(SX1509_LED_PIN, ANALOG_OUTPUT);
}

void loop()
{
  for (int i=0; i<256; i++)
  {
    // PWM the LED from 0 to 255
    io.analogWrite(SX1509_LED_PIN, i);
    delay(2); // Delay 2ms between each
  }
  delay(500); // Delay half-second at the top.
  for (int i=255; i>=0; i--)
  {
    // PWM the LED from 255 to 0
    io.analogWrite(SX1509_LED_PIN, i);
    delay(2); // Delay 2ms between each
  }
  delay(500); // Delay half-second at the bottom.
}

That’s a real fine breathing LED! But the SX1509 is so much more than a simple digital I/O expander. Its LED-driving capabilities mean you can offload all of that breathing to the SX1509, leaving your loop() for more important tasks!

Example: LED Driving

One of the SX1509’s coolest features is its built-in LED-driving support. Beyond digital or even PWM output, the SX1509 can also autonomously blink or breathe LEDs! Just tell it how long to blink, or how fast to rise/fall, and it’ll do the rest for you.

Driving an LEDs with the SX1509

For this example, grab four LEDs and wire them up to pins 8, 13, 14, and 15.

Source vs. Sink: The SX1509 can either source or sink current, but it has a much higher capacity for sinking current. It can source up to 8mA per I/O, or sink up to 15mA. If you're driving LEDs, we recommend hooking them up to sink current.

Here’s an example that sets an LED tied to pin 8 to blink:

language:c
#include <Wire.h> // Include the I2C library (required)
#include <SparkFunSX1509.h> // Include SX1509 library

const byte SX1509_ADDRESS = 0x3E;  // SX1509 I2C address (00)
SX1509 io; // Create an SX1509 object

const byte SX1509_LED_PIN = 8; // LED connected to pin 8

void setup()
{
  if (!io.begin(SX1509_ADDRESS))
  {
    while (1)
      ;
  }

  // Set up the SX1509's clock to use the internal 2MHz
  // oscillator. The second parameter divides the oscillator
  // clock to generate a slower LED clock.
  // [4] divides the 2Mhz clock by 2 ^ (4-1) (8, ie. 250kHz)
  // The divider parameter can be anywhere between 1-7.
  io.clock(INTERNAL_CLOCK_2MHZ, 4);
  io.pinMode(SX1509_LED_PIN, OUTPUT); // Set LED pin to OUTPUT
  // Blink the LED pin -- ~1000 ms LOW, ~500 ms HIGH:
  io.blink(SX1509_LED_PIN, 1000, 500);
  // The timing parameters are ms delays, and aren't 100%
  // exact. The library will estimate to try to get them as
  // close as possible. Play with the clock divider to maybe
  // get more accurate timing.
}

void loop()
{
}

The io.blink(<pin>, <low_ms>, <high_ms>) function works most of the magic in this example – setting the LED pin to blink LOW for 1000ms and HIGH for 500ms. Those timing values will not end up being exact. The SX1509’s timing mechanism is dependent on dividing the clock (we’re using the internal 2MHz oscillator), and doesn’t always divide down perfectly.

Before configuring the pin to blink, we call io.clock(<source>, <divider>) to set the clock that drives our LEDs. In this example we use the SX1509’s internal 2MHz clock as the source, and divide that down to 250kHz for the LED clock. Play with that second parameter to see just how much the blink timing depends on it.

Try using the blink function to blink the other LEDs!

LED Breathing

Half of the SX1509’s I/O pins are capable of producing “breathing” outputs – where a pin fades in and out at a set rate. Pins 4-7 and 12-15 have this capability.

Using the same circuit as before, here’s a quick example showing off the SX1509’s breathe feature:

language:c
#include <Wire.h> // Include the I2C library (required)
#include <SparkFunSX1509.h> // Include SX1509 library

const byte SX1509_ADDRESS = 0x3E;  // SX1509 I2C address (00)
SX1509 io; // Create an SX1509 object

// RGB LED connected to pins 13, 14, and 15:
const byte SX1509_RED_LED = 13; // Red LED on 13
const byte SX1509_GRN_LED = 14; // Green LED on 14
const byte SX1509_BLU_PIN = 15; // Blue LED on 15

void setup()
{
  if (!io.begin(SX1509_ADDRESS))
  {
    while (1)
      ;
  }

  // Use the internal 2MHz oscillator.
  // Set LED clock to 500kHz (2MHz / (2^(3-1)):
  io.clock(INTERNAL_CLOCK_2MHZ, 3);
  // To breathe an LED, make sure you set it as an
  // ANALOG_OUTPUT, so we can PWM the pin:
  io.pinMode(SX1509_RED_LED, ANALOG_OUTPUT);
  // Breathe an LED: 1000ms LOW, 500ms HIGH,
  // 500ms to rise from low to high
  // 250ms to fall from high to low
  io.breathe(SX1509_RED_LED, 1000, 500, 500, 250);

  // Set up Green LED:
  io.pinMode(SX1509_GRN_LED, ANALOG_OUTPUT);
  io.breathe(SX1509_GRN_LED, 500, 500, 500, 500);

  // Set up blue LED:
  io.pinMode(SX1509_BLU_PIN, ANALOG_OUTPUT);
  io.breathe(SX1509_BLU_PIN, 1509, 500, 1000, 250);
}

void loop()
{
}

Make sure you set the pin as an ANALOG_OUTPUT using the pinMode() function. Then call io.breathe(<pin>, <low_ms>, <high_ms>, <rise_ms>, <fall_ms) to set the LOW and HIGH time as well as the number of milliseconds it takes to rise from LOW to HIGH and fall from HIGH to LOW.

Easy-peasy! And now you have the loop() left free for more important tasks. (As if! Nothing’s more important than blinking LEDs.)

Example: Button Matrices

Blinking and breathing LEDs can be fun, but the SX1509’s real power lies in its keypad engine. By wiring up buttons in a row/column matrix, you can connect up to 64 buttons to the SX1509.

Keypad matrices are very common – they allow you to save immensely on GPIO. You could monitor a 16-button, 4x4 keypad pad with 8 I/O, or four of those keypads (a 64-button/8x8 matrix) with just 16 I/O.

In this example, we’ll use seven SX1509 I/O to monitor a 12-button Keypad– which is a matrix of four rows and three columns. We’ll also use the SX1509’s interrupt output, so we don’t constantly have to poll the I/O expander. Here’s the circuit:

Keypad fritzing example

There isn’t a lot of flexibility in the SX1509’s keypad engine. The rows of you matrix have to be connected, sequentially, to pins 0-7, and the columns wire up to pins 8-15. Our four row buses must route to pins 0-3, and the three columns are connected to 8-10. That still leaves plenty of pins for LED driving!

Here’s the example code:

language:c
#include <Wire.h> // Include the I2C library (required)
#include <SparkFunSX1509.h> // Include SX1509 library

const byte SX1509_ADDRESS = 0x3E;  // SX1509 I2C address (00)
SX1509 io; // Create an SX1509 object

#define KEY_ROWS 4
#define KEY_COLS 3

// Handy array we'll use to map row/column pairs to
// character values:
char keyMap[KEY_ROWS][KEY_COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}};

// ARDUINO pin 2 connected to SX1509 interrupt
#define INTERRUPT_PIN 2

void setup()
{
  Serial.begin(9600); // Use serial to print output
  if (!io.begin(SX1509_ADDRESS))
  {
    Serial.println("Failed to communicate.");
    while (1)
      ;
  }
  // To initialize the keypad engine, you at least need
  // to tell it how many rows and columns are in the matrix.
  // io.keypad(KEY_ROWS, KEY_COLS);
  // You can customize the keypad behavior further, by
  // defining scan time, debounce time, and sleep time:
  // Sleep time range: 128 ms - 8192 ms (powers of 2) 0=OFF
  unsigned int sleepTime = 0;
  // Scan time range: 1-128 ms, powers of 2
  byte scanTime = 16; // Scan time per row, in ms
  // Debounce time range: 0.5 - 64 ms (powers of 2)
  byte debounceTime = 8; // Debounce time
  io.keypad(KEY_ROWS, KEY_COLS, sleepTime, scanTime, debounceTime);

  // Set the ARDUINO pin as an input, to monitor the interrupt
  pinMode(INTERRUPT_PIN, INPUT_PULLUP);
  Serial.println("Row | Col | Key");
}

void loop()
{
  // If the interrupt pin goes active-low, a keypad button
  // is begin pressed:
  if (!digitalRead(INTERRUPT_PIN))
  {
    // Use readKeypad() to get a binary representation for
    // which row and column are pressed
    unsigned int keyData = io.readKeypad();

    // Use the getRow, and getCol helper functions to find
    // which row and column keyData says are active.
    byte row = io.getRow(keyData);
    byte col = io.getCol(keyData);
    char key = keyMap[row][col];
    Serial.print(String(row) + " | " + String(col) + " | ");
    Serial.println(key);
  }
}

After uploading the code, open the serial monitor and press some keys!

Example serial monitor output

Now just hook up a cellular shield and go make some prank calls!


Keep in mind any of these SX1509 features can be combined, as long as you don’t run out of I/O (then just cascade another expander!). Check out the library’s examples for demonstrations of other features – like the clock output, or input debouncing.

Resources & Going Further

Here are a few SX1509 and SX1509 Breakout-related resources you may find handy, as you begin to build your I/O-expanding project:

What are you going to build with the SX1509 I/O Expander? Any project that needs 16 or more outputs is bound to be blog-worthy. Let us know what you build with it! If you need any inspiration, here are a few tutorials you may find enlightening:


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado


Capacitor Kit Identification Guide

$
0
0

Capacitor Kit Identification Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t425

Introduction

You never know when you’ll need a capacitor. Sometimes you need a little more power supply decoupling, an output coupling cap, or careful tuning of a filter circuit – all applications where capacitors are critical. The SparkFun Capacitor Kit contains a wide range of capacitor values, so you will always have them on hand when you need them.

Capacitor Kit

This tutorial will help you identify the contents of your kit, and show you a couple tricks to expand the range of values even further.

Suggested Reading

Kit Contents

The Capacitor Kit contains caps on decade intervals from 10 picofarads to 1000 microfarads.

Kit Contents

Capacitor Kit Contents
ValueTypeMarkingQuantityVoltage Rating
10pFCeramic1001050V
22pFCeramic2201050V
100pFCeramic1011050V
1nFCeramic1021050V
10nFCeramic1031050V
100nFCeramic1042550V
1 µFElectrolytic1µF/50V1050V
10 µFElectrolytic10µF/25V1025V
100 µFElectrolytic100µF/25V1025V
1000 µFElectrolytic1000µF/25V1025V

There are ten pieces of most values, but 25 pieces of 100 nanofarads, which are commonly used for local supply decoupling near ICs. There are also ten pieces of 22pf, which are frequently used as load capacitors when building crystal oscillators.

Capacitor Identification

Capacitor Marking Review

Let’s face it, a Farad is a lot of capacitance. Capacitor values are usually tiny – often in the millionths or billionths of a Farad. To express those small values succinctly, we use the metric system. The following prefixes are the modern convention*.

Capacitor Metric Prefixes
PrefixSI NotationFractionSymbol
Microfarad10-6One Millionthµf
Nanofarad10-9One Billionth nf
Picofarad10-12One Trillionth pf
* These units are the modern convention, and mostly follow the guidelines for applying the metric system, but it isn't universally consistent.

Mu (µ), the symbol for micro, can be an issue for typesetting. It's hard to type, and not every font has the symbol. At SparkFun, we often use the letter 'u' as a substitute. Sometimes the letter 'm' is used instead, resuiting in micro-Farads being abbreviated as 'mF.' Technically, there's also a "milli-Farad," but in practice, milli-Farads are almost never seen, with thousands of micro-Farads being much more common.

Time and geography have an influence as well. In olderNorth American designs, nano-Farads are uncommon, with BOMs and schematics instead using only µF and pF, padded with leading or trailing zeros. One example of this convention is Digikey'scapacitor search page.

The Ceramic Caps

The smaller values in the kit are 50V rated ceramic capacitors. These are small, nonpolarized caps with yellow blob for their body.

ceramic caps

From Left to Right: 10 pF, 22 pF, 100 pF, 1 nF, 10 nF, 100 nF

The value is printed on each in a three-digit code. This code is similar to the color code on resistors, but uses digits instead of colors. The first two digits are the two most significant digits of the value, and the third digit is the exponent on the 10. The value is expressed in terms of pico-Farads.

alt text

To decode the value, take the first two digits, then follow them with the number of zeros indicated by the third digit. 104 becomes “10” followed by “0000,” or 100000 pF, more succinctly written as 100 nF.

Electrolytic Caps

Electrolytic caps have larger, cylindrical bodies that look like small soda cans. They typically offer higher capacitance than ceramic caps. Unlike ceramics, they are polarized.

electrolytic caps

From Left to Right: 1µF, 10µF, 100µF, 1000µF

The markings on the ‘lytic caps are easily legible – the value and units are printed right on the body.

The value is followed with the voltage rating, indicating the maximum DC potential that the cap can withstand without damage. In this kit, the 1 µF is rated to 50V, the others are rated to 25V.

Polarized

The higher capacitance of electrolytics comes with a somewhat tedious detail – they are polarized. The positive leg needs to be kept at a higher DC potential than the negative leg. If they’re installed backwards, they’re prone to explode.

Thankfully, the leads are clearly marked.

polarity markings

There are two polarity indicators on an electrolytic cap:

  1. The stripe painted on the body usually denotes the negative lead.
  2. The positive lead is longer than the negative lead.

Clever Applications

Crystal Oscillators

The kit specifically includes 22 pF ceramic caps for building cyrstal oscillators, commonly required by microcontroller ICs.

Crystal Oscillator

The crystal oscillator circuit from the ProMicro

Value Combinations

This kit offers a wide array of values, but the decade-by-decade selection leaves some gaps in between. There are a couple of tricks that can be used to bridge those gaps, by combining caps in series or parallel.

Parallel

The values of capacitors wired in parallel are added together. You can gang up smaller caps to effectively form a larger cap.

parallel

Series

Capacitors wired in series combine in an inverse sum – take the reciprocal of each value, and add them together, then take the reciprocal of that sum.

series


Restated as a simplified guidelines while you’re at your workbench:

  • If you want half the value of a cap in the kit, put two of that value in series.
  • If you want double the value of a cap in the kit, put two in parallel.

Resources and Going Further


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

SparkFun Blocks for Intel® Edison - ADC V20

$
0
0

SparkFun Blocks for Intel® Edison - ADC V20 a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t440

Introduction

SparkFun’s ADC Block for the Intel Edison allows you to add four channels of I2C controlled ADC input to your Edison stack. These four channels can be used as single-ended inputs, or in pairs as differential inputs. A ground reference is provided for each channel.

The maximum resolution of the converters is 12 bits, or 11 bits bipolar in differential mode. Step sizes range from 125uV per count to 3mV per count.

ADC Block

ADC Block

Suggested Reading

If you are unfamiliar with Blocks, take a look at the General Guide to Sparkfun Blocks for Intel Edison.

Other tutorials that may help you on your Edison adventure include:

Board Overview

Labeled image of the board

ADC Block Functional Diagram

  • Signal Inputs - Four single inputs are available. The reference voltage for each is produced internal to the ADC under software control; do not exceed 3.3V input to these pins!

  • Differential Channel Setting Table - Use two inputs to create a differential pair. Useful for eliminating noise in some sensors or measuring very small signals. This table shows the channel options for the “getDiffResult(channel)” function.

  • I2C Address Select - Apply solder to only one of the four jumpers to select the address. Do not short two of these at once. Bad stuff will happen.

  • I2C Bus Select - Change both of these jumpers to select between routing the I2C signals to bus 6 or bus 1. Bus 1 is the default (and preferred) channel, as it has no other system devices on it. Bus 6 is shared with some internal devices, but if you wish to use this block with the Arduino IDE, you’ll want to change these jumpers so the solder blobs connect the bottom pad with the center pad.

  • 3.3V 150mA Supply - This supply provides an on-board reference for the ADC, and can power small sensors (for example, potentiometers or temperature sensors).

Using the ADC Block

To use the ADC Block simply attach an Intel Edison to the back of the board or add it to your current stack. Blocks can be stacked without hardware but it leaves the expansion connectors unprotected from mechanical stress.

Installed block image

ADC Block Installed

We have a nice Hardware Pack available that gives enough hardware to secure three blocks and an Edison.

Edison hardware kit

Intel Edison Hardware Pack

NOTE: The ADC Block does not have console access or a voltage regulator. It is recommended to use a console communication block in conjunction with this block like ones found in the General Guide to Sparkfun Blocks for Intel Edison.

C++ Code Examples

We’re assuming that you’re using the Eclipse IDE as detailed in our Beyond Arduino tutorial. If you aren’t, you’ll need to go to that tutorial to get up to speed.

Getting Started

Follow the instructions in the programming tutorial to create a new project named “SparkFun_ADC_Edison_Block_Example”. Once you’ve created the project, open the project files on disk (hint: you can find the path to the project by choosing “Properites” from the project menu) and copy the three source files found in the Edison ADC Block CPP library GitHub repository into the “src” directory.

Download a zip file of the repository

Hardware Connection

For this example, we’ve just got two 5k potentiometers connected between 3.3V and GND, with the wipers connected to channels 0 and 1.

V20 of the board adds a 3.3V reference supply (capable of sourcing up to 150mA, so it can power small sensors directly!).

Example circuit

Of course, you can connect any other analog voltage signal in place of the potentiometers; we’re using them because they’re convenient to demonstrate the concepts.

Code

Everything you need to know is in the comments.

language:cplusplus
/****************************************************************
Example file for SparkFun ADC Edison Block Support

1 Jun 2015- Mike Hord, SparkFun Electronics
Code developed in Intel's Eclipse IOT-DK

This code requires the Intel mraa library to function; for more
information see https://github.com/intel-iot-devkit/mraa

This code is beerware; if you use it, please buy me (or any other
SparkFun employee) a cold beverage next time you run into one of
us at the local.
****************************************************************/

#include "mraa.hpp"

#include <iostream>
#include <unistd.h>
#include "SparkFunADS1015.h"

using namespace std;

// Declare a variable for our i2c object. You can create an
//  arbitrary number of these, and pass them to however many
//  slave devices you wish.
mraa::I2c* adc_i2c;

int main()
{
    // The ADC is (by default) connected to I2C channel 1. Here, we create
    //  a device to pass to the ads1015 object constructor.
    adc_i2c = new mraa::I2c(1);

    // Now, pass that I2C object and the address of the ADC block in your
    //  system to the ads1015 object constructor. Note that there are up to
    //  four different addresses available here, settable by jumper on the
    //  board. You'll need to create an ads1015 object for each one.
    ads1015 adc(adc_i2c, 0x48);

    // There are 6 settable ranges:
    //  _0_256V - Range is -0.256V to 0.255875V, and step size is 125uV.
    //  _0_512V - Range is -0.512V to 0.51175V, and step size is 250uV.
    //  _1_024V - Range is -1.024V to 1.0235V, and step size is 500uV.
    //  _2_048V - Range is -2.048V to 2.047V, and step size is 1mV.
    //  _4_096V - Range is -4.096V to 4.094V, and step size is 2mV.
    //  _6_144V - Range is -6.144V to 6.141V, and step size is 3mV.
    // The default setting is _2_048V.
    // NB!!! Just because FS reading is > 3.3V doesn't mean you can take an
    //  input above 3.3V! Keep your input voltages below 3.3V to avoid damage!
    adc.setRange(_0_512V);
    // getResult() returns a normalized floating point value representing the
    //  current voltage of the passed channel. User is responsible for
    //  logic to determine whether the value is at min or max.
    cout<<"Ch 0: "<<adc.getResult(0)<<endl;
    cout<<"Ch 1: "<<adc.getResult(1)<<endl;
    // getDiffResult() returns a normalized fp value representing the
    //  difference between two channels. Options are
    //  0 - Ch0 - Ch1
    //  1 - Ch0 - Ch3
    //  2 - Ch1 - Ch3
    //  3 - Ch2 - Ch3
    cout<<"Ch 0 - ch 1: "<<adc.getDiffResult(0)<<endl;

    // If you want to do the math yourself, you can determine the current gain
    //  setting by using the getScaler() command.
    cout<<"Current scaler: "<<adc.getScaler()<<"V per bit"<<endl;
    // The current voltage is the scaler/1000 multiplied by the raw value. You
    //  can get the raw ADC readings using the getRawResult() and
    //  getRawDiffResult() functions.
    cout<<"Ch 0 raw: "<<adc.getRawResult(0)<<endl;
    cout<<"Ch 1 raw: "<<adc.getRawResult(1)<<endl;
    cout<<"Ch 0 - ch 1 raw: "<<adc.getRawDiffResult(0)<<endl;

    // If you want to get *really* crazy, you can always go look up the
    //  datasheet and read and write the configuration register directly.
    cout<<"Config register: "<<hex<<adc.getConfigRegister()<<endl;
    // There's a "setConfigRegister()" function, too, which expects an
    //  unsigned 16-bit integer. Just FYI.

    return MRAA_SUCCESS;
}

Resources and Going Further

Now that you have had a brief overview of the ADC Block, take a look at some of these other tutorials. These tutorials cover programming, Block stacking, and interfacing with the Intel Edison ecosystems.

Edison General Topics:

Block Specific Topics:

Check out these other Edison related tutorials from SparkFun:

SparkFun Blocks for Intel® Edison - OLED Block

A quick overview of the features of the OLED Block for the Edison.

SparkFun Blocks for Intel® Edison - Dual H-Bridge

A quick overview of the features of the Dual H-bridge Block.

Edison Getting Started Guide

An introduction to the Intel® Edison. Then a quick walk through on interacting with the console, connecting to WiFi, and doing...stuff.
New!

SparkFun Blocks for Intel® Edison - ADC V20

A quick overview of the features of the ADC Block.

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

MIDI Shield Hookup Guide

$
0
0

MIDI Shield Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t409

Introduction

The Sparkfun MIDI Shield allows you to add MIDI ports to your R3-compatible Arduino board.

MIDI Shield In Use

The shield provides the standard MIDI port circuits, including 5-pin DIN connectors and an opto-isolated MIDI input. The shield also has some extra input and output devices. It had LEDs on D6 and D7, pushbuttons on D2, D3 and D4, and rotary potentiometers on A0 and A1.

The V1.5 revision also adds several configurable features, such as converting the MIDI output to a MIDI thru, and the option to use a software serial port for MIDI, leaving the hardware serial for programming and debugging. It also buffers the output, making it compatible with the Arduino Pro without needing to circumvent the protection resistors on the serial TX and RX lines.

This guide will show you how to put the shield together, then explore several example projects, demonstrating how you can apply MIDI to your projects.

Suggested Reading

Assembly

Before assembling your MIDI shield, consider how you’re going to use it. If you don’t need buttons and potentiometers, you can leave them off. Similarly, if you only need the MIDI input or output port, you can leave the other port off. Finally, there are a couple of options for headers – you can select headers that fit your application.

Materials

The MIDI shield kit contains the following parts.

kit parts

  • The MIDI Shield PCB
  • 2x 5-pin DIN conectors
  • 2x 10K rotary potentiometer
  • 3x 12mm tactile pushbutton switches

You’ll also need to select headers that fit your application.

  • You can use the R3 stackable header kit, but they’re a bit tall and make the buttons hard to reach.
  • If you’re using an Arduino board that predates R3, such as the Arduino Pro, you can use the regular stackable header kit.
  • Alternately, you can use regular snappable headers, which don’t stick as far above the board, leaving plenty of room to access the switches and potentiometers.

Pick your headers

Stackable Headers (left) and Snappable Headers

Tools

The following tools are recommended.

Building The MIDI Shield

If you’re using stackable headers, they’re easiest to put on at the very beginning (if you’re using the snappable headers, we’ll save them until the end). To install the stackable headers, put them through the board from the top side

Stacking headers Insertion

Then flip the board over so it’s supported by the bodies of the headers. Adjust the alignment until the headers stand squarely in your work surface, and solder them in place.

Stacking Headers Soldered In

Next, we’ll install the components on the top of the board in order from the shortest to the tallest. We’ll start with the pushbuttons – snap them through the holes, then solder them in place.

Button Insertion

Following that, install the MIDI jacks, taking care to keep them seated to the PCB while you solder.

MIDI Sockets

As the tallest component, the potentiometers go on next. Getting them into the PCB can take some effort – you might need to nudge the tabs a little bit to get them through the board. Before you solder, doublecheck that the shafts are perpendicular to the surface of the board, because it takes a lot of desoldering effort to straighten them if they’re crooked.

Install Potentiometer

Finally, if you’re using snappable headers instead of the stacking ones, install them. It’s easiest if you use an Arduino board as an assembly jig. Snap the headers into suitable lengths (6, 8, 8 and 10 for an R3 board), and push them into the sockets on the Arduino. Then lay the MIDI shield PCB over them, as shown below.

Fixture for Snappable Headers

Solder them from the top of the board.

In Operation

One it’s assembled, place the MIDI shield on top of your Arduino, then connect your MIDI devices to the ports.

MIDI Shield In Use

RUN vs. PROG

There’s one last thing to mention: near the MIDI input jack is a small slide switch.

Program/Run Switch

By default, the shield uses the hardware serial port on the Arduino for MIDI communication – but that port is shared with the bootloader, which is initiated when you press the “load” button in the Arduino IDE. The switch allows them to share the port politely, avoiding output contention.

If you’re using the hardware serial port, set the switch to the PROG position, before you load your sketch. Once it’s loaded and verified, set it back to RUN.

In the next section we’ll discuss using a software serial port. One advantage of software serial is that you don’t need to remember to flip the switch every time you load!

Configuration

The MIDI Shield has a number of solder jumpers on the bottom side, so that it can be customized for different situations.

Hardware vs. Software Serial Port

The first set of jumpers are SJ1 and SJ2, near digital pins 10-13. These allow you to swap between the hardware serial port on pins D0 and D1, or a software serial port on pins 8 and 9.

Serial Selection Jumpers

By default, these are connected with a copper trace between the center and right-hand pads, selecting the hardware UART. If you’d rather use the software serial port, cut that trace using a hobby knife, and flow some solder the bridge the center pad to the left one.

Why would you want to do this? A couple of reasons jump to mind.

  1. If you’re using the software serial port for MIDI, the hardware port is still available to the bootloader. You don’t need to toggle the RUN/PROG switch every time you try to load.
  2. The hardware serial port is still available for other communication. In particular, you can use Serial.print() statements to debug your code.

MIDI Out or Thru

SJ3 is on the bottom of the board, near the center.

Out/Thru Jumper

It allows the MIDI output port to be repurposed as a MIDI thru. Instead of being tied to the TX line of the selected serial port, the MIDI out jack will retransmit a copy of bytes that arrive at the input. If you don’t need the MIDI output to transmit data, MIDI thru might be useful, especially if you’re daisy chaining MIDI devices.

Like the serial port selection jumpers, the default path is connected with a copper trace. If you want to switch the jumper, cut the trace, and bridge the other two pads with solder.

The MIDI library we’ll be using also has the option to enable software thru functionality.

Power Over MIDI

The final set of jumpers is SJ4, 5, 6 and 7. These are under the MIDI ports and implement for a form of power-over-MIDI.

Power over MIDI isn’t actually defined by the MIDI Standard, and a numberofmanufacturers have proposed and implemented different schemes for providing power to a device over the MIDI cable.

There isn’t much agreement on how this is implemented. Some vendors add two extra pins, using a 7-pin connector. Some vendors simply sip a tiny bit of current off pin 4, and some use the pins that are otherwise not used in standard. Similarly, the voltage available differs – it might be 5 VDC, 12 VDC, or sometimes even 12 VAC!

Since Arduinos don’t have a 12 VDC supply, the MIDI shield allows you to route 5V to the unused pins of the 5-pin DIN connector: pin 1 is tied to 5V, and pin 3 gets grounded. This is mainly useful if you want to use a pair of MIDI shields to build a system with a subordinate device (like a MIDI enabled footpedal) that is powered by the larger system it’s connected to.

Power To The Pedal

In a configuration like this, the shield on the pedal end would have the jumpers on the output closed (SJ6 and 7), and the shield on the host system end would have the input jumpers closed (SJ4 and 5).

MIDI Power Jumpers

This configuration allows you to power one MIDI Shield from another, but it may not be interoperable with other gear. Carefully consult the user manual of other devices before connecting them. Additionally, it requires a cable that has all five pins connected. The average MIDI cable has no connections to pins 1 and 3; sometimes MIDI cables are sold as having all five pins connected, and sometimes such a cable is simply sold as a “five pin DIN” cable.

There are some other technical concerns when powering devices over MIDI.

  • It’s most useful when the power provider and recipient are designed as a system. It’s hard to make power over MIDI universally applicable, because you need to be able to anticipate voltage and current requirements.
  • The original MIDI hardware implementation uses optocouplers to avoid ground loops. Power over MIDI invites ground loop-related problems by tying the grounds of different devices together.

Firmware Foundations

Arduino Compatible I/O

The MIDI shield features the MIDI circuitry, plus a few extra input and output devices. It has three pushbuttons, two potentiometers, and two LEDs.

Shield Callouts

These devices are legended with their pin assignments, and can be interfaced using standard Arduino functions.

  • The pushbuttons are on D2, D3, and D4. To use them, enable the corresponding pins as inputs with pullup pinMode(<pin number>, INPUT_PULLUP); and read them with digitalRead(<pin number>);. The inputs are active low - they will normally read as a logic HIGH, going LOW while the button is pressed.
  • The potentiometers are connected to A0 and A1. You can read them using analogRead(<pin number>).
  • Finally, the LEDs are on D6 (Green) and D7 (Red). The outputs are enabled using pinMode(<pin number>, OUTPUT), and set using digitalWrite(<pin number>, <HIGH or LOW>). Like the buttons, these are active low – writing HIGH turns the LED off.

You can find a quick example sketch to test the buttons, pots and LEDs in the MIDI Shield GitHub Repository.

Arduino MIDI Library

If you’ve read the implementation section of our MIDI tutorial, then you’ve seen that generating and parsing MIDI messages can be a little tricky. Thankfully, Franky at 47 Effects has written a stable and flexible MIDI library for Arduino, which he has released under the MIT license.

The library handles the communication aspects of MIDI, allowing you to send and receive MIDI commands. It implements the communication layer, but it does not make any implications about how the library is applied. It is a suitable basis for almost any type of MIDI device, from simple message filters and splitters, to complex applications like synthesizers, sequencers, and drum machines.

The library is robust and flexible, with some well-designed features.

  • It can use hard or soft serial ports – you don’t need to lose Serial.print() for debugging when you use it.
  • It can handle MIDI in Omni mode or be set to a specific channel.
  • You can enable a soft-thru feature, which merges incoming bytes with the output, when you don’t have a hardware thru port available.
  • It uses a object-oriented template instantion, which allows you to declare multiple MIDI ports on the same device, as you might do in a merger or splitter.
  • You can enable or disable some more esoteric features, like sending running status and implicit note off, or using a nonstandard baud rate.

The MIDI library is hosted on GitHub. There is also extensive documentation in Doxygen format, including several sample applications.

Library Install

To install the library, download the files from GitHub. Unzip them, and put the contents of the /src/ folder into your Arduino library path. On the authors PC, the library was placed in C:\Users\author\Documents\Arduino\libraries\MIDI.

If you need more guidance on installing the library, please consult our Installing an Arduino Library tutorial.

We’ll be using this library as the basis for the following examples. We’ll discuss some specifics of its application in each example.

Example #1: Clock Generator & Receiver

The first example we’ll demonstrate is synchronizing multiple devices using MIDI clock commands.

For this example, we’ll be building two different variants of the clock code. The first variant is the master clock, which generates periodic MIDI timing clock (0xF8) bytes, and start(0xFA), stop (0xFC), and continue (0xFB) messages. These messages are used to transmit musical timing from one device to another, acting as a high resolution metronome.

Clock Block Diagram

The other end of the link listens for those messages and responds to them.

For this demonstration, we’ll be using a pair of RedBoards, each with a MIDI shield. You can substitute either end of the link for a device that implements MIDI clock, as we’ll show below.

The Firmware

Each of these boards gets loaded with a different sketch. The master clock will get loaded with the clock-gen.ino sketch.


/******************************************************************************
clock-gen.ino
Use SparkFun MIDI Shield as a MIDI clock generator.

Byron Jacquot, SparkFun Electronics
October 8, 2015
https://github.com/sparkfun/MIDI_Shield/tree/V_1.5/Firmware/clock-gen

Generate MIDI clock messages at the tempo indicated by  A1.
Send start/stop messages when D2 is pressed, and continue when D3 is pressed.

Resources:

  This sketch has a clock receiving counterpart in clock-recv.ino

  This code is dependent on the FortySevenEffects MIDI library for Arduino.
  https://github.com/FortySevenEffects/arduino_midi_library
  This was done using version 4.2, hash fb693e724508cb8a473fa0bf1915101134206c34
  This library is now under the MIT license, as well.
  You'll need to install that library into the Arduino IDE before compiling.


Development environment specifics:
  It was developed for the Arduino Uno compatible SparkFun RedBoard, with a  SparkFun
  MIDI Shield.

  Written, compiled and loaded with Arduino 1.6.5

This code is released under the [MIT License](http://opensource.org/licenses/MIT).

Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact techsupport@sparkfun.com.

Distributed as-is; no warranty is given.
******************************************************************************/

#include 
#include 
#include 

#define PIN_LED_PLAYING 6
#define PIN_LED_TEMPO 7
#define PIN_PLAY_INPUT 2
#define PIN_CONTINUE_INPUT 3

#define PIN_TEMPO_POT 1

static const uint16_t DEBOUNCE_COUNT = 50;

//SoftwareSerial SoftSerial(8,9);

/* Args:
   - type of port to use (hard/soft)
   - port object name
   - name for this midi instance
*/
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);
//MIDI_CREATE_INSTANCE(SoftwareSerial, SoftSerial, MIDI);

bool running;
bool send_start;
bool send_stop;
bool send_continue;
bool send_tick;
uint32_t tempo_delay;


void play_button_event()
{
  // toggle running state,
  // send corresponding responses
  running = !running;

    if(running)
    {
      send_start = true;
      digitalWrite(PIN_LED_PLAYING, LOW);
    }
    else
    {
      send_stop = true;
      digitalWrite(PIN_LED_PLAYING, HIGH);
    }
}

void cont_button_event()
{
  // ignore continue if running
  if(!running)
  {
    send_continue = true;
    running = true;
    digitalWrite(PIN_LED_PLAYING, LOW);
  }
}

void timer_callback()
{
  send_tick = true;
}

void check_pots()
{
  uint32_t pot_val;
  uint32_t calc;

  pot_val = analogRead(PIN_TEMPO_POT);

  // Result is 10 bits
  calc = (((0x3ff - pot_val) * 75)/1023) + 8;

  tempo_delay = calc  ;//* 5;
}

void check_buttons()
{
  uint8_t val;
  static uint16_t play_debounce = 0;
  static uint16_t cont_debounce = 0;

  // First the PLAY/STOP button
  val = digitalRead(PIN_PLAY_INPUT);

  if(val == LOW)
  {
    play_debounce++;

    if(play_debounce == DEBOUNCE_COUNT)
    {
      play_button_event();
    }
  }
  else
  {
    play_debounce = 0;
  }

  // Then the continue button
  val = digitalRead(PIN_CONTINUE_INPUT);

  if(val == LOW)
  {
    cont_debounce++;

    if(cont_debounce == DEBOUNCE_COUNT)
    {
      cont_button_event();
    }
  }
  else
  {
    cont_debounce = 0;
  }



}


void setup()
{
  // put your setup code here, to run once:

  // LED outputs
  pinMode(PIN_LED_PLAYING, OUTPUT);
  pinMode(PIN_LED_TEMPO, OUTPUT);
  digitalWrite(PIN_LED_PLAYING, HIGH);
  digitalWrite(PIN_LED_TEMPO, HIGH);

  // button inputs
  pinMode(PIN_PLAY_INPUT, INPUT_PULLUP);
  pinMode(PIN_CONTINUE_INPUT, INPUT_PULLUP);

//  Serial.begin(9600);
//  Serial.println("Setting up");

//  SoftSerial.begin(31250);

  // do I need to init the soft serial port?

#if 1
  MIDI.begin(MIDI_CHANNEL_OMNI);
  MIDI.turnThruOff();
#endif

  running = false;
  send_start = false;
  send_stop = false;
  send_tick = false;

  // prime the tempo pump
  check_pots();

//  check_timing();

  MsTimer2::set(tempo_delay, timer_callback);
  MsTimer2::start();

}

void loop()
{
  static uint32_t loops = 0;
  static uint8_t  ticks = 0;
  static uint8_t  prev_ticks = 0;
  bool reset_timer = false;

  // put your main code here, to run repeatedly:

  // turn the crank...
  MIDI.read();

  // Check buttons
  check_buttons();

  // process inputs
  if(send_start)
  {
    MIDI.sendRealTime(MIDI_NAMESPACE::Start);
    send_start = false;
//    Serial.println("Starting");

    ticks = 0;

    // Next tick comes immediately...
    // it also resets the timer
    send_tick = true;

  }
  if(send_continue)
  {
    MIDI.sendRealTime(MIDI_NAMESPACE::Continue);
    send_continue = false;
//    Serial.println("continuing");

    // Restore the LED blink counter
    ticks = prev_ticks;

    // Next tick comes immediately...
    // it also resets the timer
    send_tick = true;
  }

  if(send_stop)
  {
    MIDI.sendRealTime(MIDI_NAMESPACE::Stop);
    send_stop = false;
    prev_ticks = ticks ;
//    Serial.println("Stopping");
  }

  if(send_tick)
  {
    MIDI.sendRealTime(MIDI_NAMESPACE::Clock);
    send_tick = false;

    ticks++;
    if(ticks < 6)
    {
      digitalWrite(PIN_LED_TEMPO, LOW);
    }
    else if(ticks == 6)
    {
      digitalWrite(PIN_LED_TEMPO, HIGH);
    }
    else if(ticks >= 24)
    {
      ticks = 0;
    }

    check_pots();

    reset_timer = true;
  }

  if(reset_timer)
  {
    MsTimer2::stop();
    MsTimer2::set(tempo_delay, timer_callback);
    MsTimer2::start();

    reset_timer = false;
  }

  loops++;
}

The clock chasing board is loaded with the clock-recv.ino sketch.


/******************************************************************************
clock-recv.ino
Use SparkFun MIDI Shield as a MIDI clock receiver.

Byron Jacquot, SparkFun Electronics
October 8, 2015
https://github.com/sparkfun/MIDI_Shield/tree/V_1.5/Firmware/clock-recv

Listenn for clock/start/stop/continue messages on the MIDI input

Resources:

  This sketch has a clock generating counterpart in clock-gen.ino

  This code is dependent on the FortySevenEffects MIDI library for Arduino.
  https://github.com/FortySevenEffects/arduino_midi_library
  This was done using version 4.2, hash fb693e724508cb8a473fa0bf1915101134206c34
  This library is now under the MIT license, as well.
  You'll need to install that library into the Arduino IDE before compiling.


Development environment specifics:
  It was developed for the Arduino Uno compatible SparkFun RedBoard, with a  SparkFun
  MIDI Shield.

  Written, compiled and loaded with Arduino 1.6.5

This code is released under the [MIT License](http://opensource.org/licenses/MIT).

Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact techsupport@sparkfun.com.

Distributed as-is; no warranty is given.
******************************************************************************/
#include 
#include 
#include 

#define PIN_LED_PLAYING 6
#define PIN_LED_TEMPO 7
#define PIN_PLAY_INPUT 2
#define PIN_CONTINUE_INPUT 3

#define PIN_TEMPO_POT 1

static const uint16_t DEBOUNCE_COUNT = 50;

//SoftwareSerial SoftSerial(8,9);

/* Args:
   - type of port to use (hard/soft)
   - port object name
   - name for this midi instance
*/
//MIDI_CREATE_INSTANCE(SoftwareSerial, SoftSerial, MIDI);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);

void setup()
{
  // put your setup code here, to run once:

  // LED outputs
  pinMode(PIN_LED_PLAYING, OUTPUT);
  pinMode(PIN_LED_TEMPO, OUTPUT);
  digitalWrite(PIN_LED_PLAYING, HIGH);
  digitalWrite(PIN_LED_TEMPO, HIGH);

  // button inputs
//  Serial.begin(9600);
//  Serial.println("Setting up");

  //  SoftSerial.begin(31250);
  // do I need to init the soft serial port?
  // No - MIDI will do it.

#if 1
  MIDI.begin(MIDI_CHANNEL_OMNI);
  MIDI.turnThruOff();
#endif

}

void loop()
{
  static uint32_t loops = 0;
  static uint8_t  ticks = 0;
  static uint8_t  prev_ticks = 0;

  // put your main code here, to run repeatedly:

  // turn the crank...
  if(  MIDI.read())
  {
    switch(MIDI.getType())
    {
      case midi::Clock :
      {
        ticks++;

        //Serial.print('.');
//        Serial.println(ticks);

        if(ticks < 6)
        {
          digitalWrite(PIN_LED_TEMPO, LOW);
          //Serial.print('#');
        }
        else if(ticks == 6)
        {
          digitalWrite(PIN_LED_TEMPO, HIGH);
        }
        else if(ticks >= 24)
        {
          ticks = 0;
//          Serial.print('\n');
        }
      }
      break;

      case midi::Start :
      {
        digitalWrite(PIN_LED_PLAYING, LOW);
        ticks = 0;
//        Serial.println("Starting");
      }
      break;

      case midi::Stop :
      {
        digitalWrite(PIN_LED_PLAYING, HIGH);
        prev_ticks = ticks;
//        Serial.println("Stopping");
      }
      break;
      case midi::Continue :
      {

        digitalWrite(PIN_LED_PLAYING, LOW);

        // Restore the LED blink counter
        ticks = prev_ticks;
//        Serial.println("continuing");
      }
      break;

      default:
      break;
    }
  }

  loops++;
}

Controls

Before we test the system, lets review how the sketches use the extra I/O on the MIDI shield.

The clock generator uses the following controls:

Clock Generator Controls

  • The A1 pot controls the tempo.
  • The button on D2 acts as a start/stop button.
  • The button on D3 is a continue button

The LEDs on both ends of the link serve the same functions.

  • D7, the red LED, blinks in time, indicating the tempo.
  • D6, the green LED, is illuminated when the system is running.

Testing

Once both boards are loaded, the red (D7) LED on the generator should be blinking, and the receiver should be dark. Connect the MIDI Out of the generator to the MIDI In of the receiver, and its red LED should start blinking. The blink rates will be the same, but they may be skewed relative to each other, not yet in perfect sync.

Now, press the play button on the generator. The green LEDs on both boards should illuminate, and the red LEDs should now blink in synch with each other.

2 Shields in Sync

You can adjust the A1 potentiometer to speed up and slow down the tempo. The blink rate will change, and both units will stay in sync as it is adjusted.

The continue button has some special behavior. MIDI defines Start (0xFA) as instructing recipients to reset to the beginning of the song, then start playing. Continue (0xFB) omits the reset, and starts from whereever it was last stopped.

With Other Devices

We can replace either end of the chain with other devices. For purposes of demonstration, we’ll be using a Willzyx x0xb0x, an analog synthesizer with an onboard sequencer. The x0xb0x is connected as a receiver.

x0xb0x in Sync

In order for this to work, the x0xb0x needs to be configured to follow incoming clock messages – on the x0xb0x, this is as simple as setting the mode rotary switch to PATT MIDI SYNC. It follows the tempo, starts and stops properly. However, it doesn’t appear to properly obey the continue messages.

Example #2: MIDI-to-control-voltage

If you’re interested in building a MIDI synthesizer, the MIDI Shield is quite capable. There is an example in the Forty Seven Effects documentation that uses the Arduino tone library as a simple MIDI instrument. We’re going to build a little more ambitious system, that allows us to play the Moog Werkstatt with real keys. This is useful because the Werkstatt is usually played with an array of tiny tactile switches.

The Werkstatt is a good candidate for this project because it has a header on which it receives external control voltages. We’ll be generating the following voltages:

  • The pitch control voltage (CV) is a DC voltage that represents which key is currently pressed. The oscillator on the Werkstatt raises its pitch an octave for every volt present on this input; in other words, a semitone is 1/12th of a volt.
  • The gate signal is an on/off indicator – when one or more keys on the MIDI controller are held, the gate is high. When no keys are pressed, the gate is low. The Werkstatt responds to the gate by triggering the envelope generator, which in turn drives the VCF and VCA.
  • There’s a second analog control voltage from the converter that represents modulation wheel (continuous controller #0) position. It can be patched into other inputs on the Werkstatt, such as LFO rate or filter cutoff.

Materials

This project requires the following parts.

It also needs a MIDI keyboard controller and a MIDI cable.

Construction

Construction and testing is a little more involved that the other two projects.

First, we need a ground wire in the Werkstatt. In this case, a green wire is wrapped around one of the screw posts inside and out through a gap in the front of the chassis.

Ground Wire

DAC Board Assembly

After that, prepare the DACs, and put them on the proto shield. The DACs come configured using the same I2C address (0x60) – on one of the DAC breakouts, cut the traces on the pullup resistor jumper on the back of the board, and switch its address to 0x61 by switching the ADDR jumper to VCC.

DAC Block Diagram

Block Diagram for DAC assembly on Protoshield

The two DACs are soldered onto strips of breakaway headers and then onto the proto shield. The following connections were made on the shield with wire:

  • Their VCC pins were tied to the 5V pin.
  • The SDA pins were tied to A4.
  • The SCL pins were tied to A5.
  • Longer wires were connected to the DAC outputs – we selected a blue one, a white one, and a black one.
    • The white wire is the output of the DAC at the default address.
    • The black wire is the output of the other DAC.
    • The blue wire was connected to D10.
  • The green ground wire from inside the Werkstatt is tied to a GND pin on the proto shield.

DACs on Protoshield

Testing the DACs

Once the DACs are wired onto the protoshield, put the shield on the RedBoard to test them. Loading the DAC Integration Test sketch to verify that they’re working. It verifies that they’re working correctly and communicating on the correct addresses by generating opposing full-scale sawtooth waves.

Testing Waveforms

Connections

Once you’re confident that the DACs are working, you can put the MIDI shield on top of the stack, and connect the Werkstatt and MIDI controller. The MIDI controller is connected to the MIDI input on the shield. The wires from the protoshield are connected to the Werkstatt as follows:

  • The white wire gets plugged into the VCO EXP input.
  • The black wire goes into the VCF IN.
  • The blue wire is connected to the GATE OUT .

With a MIDI Keyboard

Firmware

The firmware for this project is a little more complex, requiring ancillary *.CPP and *.H files. Get all three files from theGitHub folder for the project, and store them in a directory named MIDI-CV. When you open MIDI-CV.ino, it should also open notemap.h and .cpp. Compile and load the sketch, then press some keys on your MIDI controller.

This example uses a more sophisticated interface to the 47 Effects library: rather than polling the library for new messages, it installs callback routines for the relevant message categories. It’s also configured to listen in Omni mode - it should respond to messages on any MIDI channel.

In Operation

With a range of 5V to work with, the design target was to have 4 octaves of control voltage, plus roughly +/-½ octave bend range. If multiple keys are held, the CV represents the lowest note.

The MIDI-CV sketch includes a fun bonus – an arpeggiator! If you hold more than one controller key at a time, the converter will periodically cycle between the keys.

Panel Controls

  • Button D2 enables the arpeggiator in up mode.
  • Button D3 enables the arpeggiator in Down mode.
  • Button D4 changes the gate to follow the arpeggiator clock.
  • Pot A1 controls the rate of arpeggiation.
  • The red LED (D7) displays arpeggiator tempo.
  • The green LED (D6) illuminates when the arpeggiator is enabled.

You can switch between up and down modes while the arpeggiator is running. To disable the arpeggiator, press the button for the current mode a second time.

Calibration

The convention for control voltage is that increasing the voltage by one volt will cause the pitch to raise by an octave; 1/12th of a volt (0.08333V) corresponds to a semitone.

The DACs are also referenced to VCC on the board, nominally 5V in this case. Different power supplies will change the scaling of the CV – the test unit behaved a little differently when powered from the USB connection (yielding VCC of 5.065 V) and from a 9V wall-wart supply plugged into the barrel jack (resulting in VCC of 5.008 V). When you adjust the scaling, it’s important that you’re powering the system as it will be deployed!

The intonation can be adjusted at both ends of the interface described here. Trimpot VR5 in the Werkstatt allows for Volt-to-Octave adjustments. In the Sketch, the constant DAC_CAL is also used to change the CV scaling. Since it’s adjustable at both ends, it can lead to confusion if you start adjusting them at the same time.

The key to preventing that confusion is remembering the Volt/Octave convention. Independently adjust each end of the interface so that volts and octaves correspond. We calibrated the system using the following procedure:

  • First the Arduino side is calibrated to produce a 1V step for an octave between note-on messages
    • A DC Voltmeter was connected to the CV output of the DAC board.
    • Ascending octaves were played on the keyboard.
    • The DAC_CAL value was adjusted until those octaves resulted in 1VDC changes to the CV output.
      • In this case, the default value of 6826 worked fairly well.
      • The resulting voltages were 0.483V, 1.487V, 2.485V, 3.484V, and 4.485V.
  • Once the CV has been adjusted to 1V/8ve, VR5 in the Werkstatt can be adjusted to volt/octave response.
    • We used a frequency counter function in an oscilloscope to check the frequency, measured at the VCO out patch point. It was doublechecked with a digital guitar tuner.
    • We started by hopping back and forth between low C and the next C up, then moving to higher octaves as the intonation got better.
    • If you tune the high A to 440 Hz, working backwards to the lowest A on a 4-octave keyboard gives you 440 Hz, 220 Hz, 110 Hz and 55 Hz.
    • The Werkstatt is most accurate in the lower registers, and tends to run a little flat in the upper registers.
    • The tuning pot on the panel is also somewhat touchy. While you’re building this interface, you might also consider adding Moog’s Fine Tuning Mod.

Troubleshooting

This project was developed using a soft serial port for MIDI and the hardware serial port to print debug messages. Once it was working, the debug printing was disabled, and MIDI was reverted to the hardware serial port.

If you want to enable debug printing:

  1. Start by adjusting SJ1 and SJ2 for MIDI on the soft serial pins.
  2. Amend the declaration of the MIDI instance to create and use the soft serial port.
  3. Change the VERBOSE macro to 1

Example #3: MIDI Analyzer

Sometimes, when you’re developing MIDI code, you want to be able to see the messages being sent on the line. Exactly what values are being sent? Is a byte getting lost somewhere?

If you’ve got an old toaster Mac sitting around, you can use MIDI Scope, or MIDI Ox on Windows systems.

This example is a MIDI sniffer along those lines. It receives MIDI messages via the library, and interprets them on the serial port.

Hardware Configuration

This sketch requires two serial ports – one for MIDI communication, and one for printing the interpreted messages. The shield needs to be configured to use a software serial port for the MIDI communications.

Sniffer Block Diagram

The Sketch


/******************************************************************************
MIDI-sniffer.ino
Use SparkFun MIDI Shield as a MIDI data analyzer.

Byron Jacquot, SparkFun Electronics
October 8, 2015
https://github.com/sparkfun/MIDI_Shield/tree/V_1.5/Firmware/MIDI-sniffer

Reads all events arriving over MIDI, and turns them into descriptive text.
If you hold the button on D2, it will switch to display the raw hex values arriving,
which can be useful for viewing incomplete messages and running status.

Resources:

Requires that the MIDI Sheild be configured to use soft serial on pins 8 & 9,
so that debug text can be printed to the hardware serial port.

This code is dependent on the FortySevenEffects MIDI library for Arduino.
https://github.com/FortySevenEffects/arduino_midi_library
This was done using version 4.2, hash fb693e724508cb8a473fa0bf1915101134206c34
This library is now under the MIT license, as well.
You'll need to install that library into the Arduino IDE before compiling.


Development environment specifics:
It was developed for the Arduino Uno compatible SparkFun RedBoard, with a  SparkFun
MIDI Shield.

Written, compiled and loaded with Arduino 1.6.5

This code is released under the [MIT License](http://opensource.org/licenses/MIT).

Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact techsupport@sparkfun.com.

Distributed as-is; no warranty is given.
******************************************************************************/


#include 
#include 
#include 

#define PIN_RAW_INPUT 2

#define PIN_POT_A0 0
#define PIN_POT_A1 1

static const uint16_t DEBOUNCE_COUNT = 50;

// Need to use soft serial, so we can report what's happening
// via messages on hard serial.
SoftwareSerial SoftSerial(8, 9);

/* Args:
   - type of port to use (hard/soft)
   - port object name
   - name for this midi instance
*/
MIDI_CREATE_INSTANCE(SoftwareSerial, SoftSerial, MIDI);
// This doesn't make much sense to use with hardware serial, as it needs
// hard serial to report what it's seeing...

void setup()
{
  // put your setup code here, to run
  once:

  // LED outputs
  Serial.begin(19200);
  Serial.println("Setting up");

  // do I need to init the soft serial port?
  // No - MIDI Lib will do it.

  // We want to receive messages on all channels
  MIDI.begin(MIDI_CHANNEL_OMNI);

  // We also want to echo the input to the output,
  // so the sniffer can be dropped inline when things misbehave.
  MIDI.turnThruOn();

  pinMode(PIN_RAW_INPUT, INPUT_PULLUP);

}

void loop()
{
  static uint8_t  ticks = 0;
  static uint8_t  old_ticks = 0;


  // put your main code here, to run repeatedly:

  if(digitalRead(PIN_RAW_INPUT) == LOW)
  {
    // If you hold button D2 on the shield, we'll print
    // the raw hex values from the MIDI input.
    //
    // This can be useful if you need to troubleshoot issues with
    // running status

    byte input;
    if(SoftSerial.available() != 0)
    {
      input = SoftSerial.read();

      if(input & 0x80)
      {
        Serial.println();
      }
      Serial.print(input, HEX);
      Serial.print(' ');
    }
  }
  else
  {
    // turn the crank...
    if (  MIDI.read())
    {
      switch (MIDI.getType())
      {
        case midi::NoteOff :
          {
            Serial.print("NoteOff, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" Note#: ");
            Serial.print(MIDI.getData1());
            Serial.print(" Vel#: ");
            Serial.println(MIDI.getData2());
          }
          break;
        case midi::NoteOn :
          {
            uint8_t vel;

            Serial.print("NoteOn, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" Note#: ");
            Serial.print(MIDI.getData1());
            Serial.print(" Vel#: ");
            vel = MIDI.getData2();
            Serial.print(vel);
            if (vel == 0)
            {
              Serial.print(" *Implied off*");
            }
            Serial.println();
          }
          break;
        case midi::AfterTouchPoly :
          {
            Serial.print("PolyAT, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" Note#: ");
            Serial.print(MIDI.getData1());
            Serial.print(" AT: ");
            Serial.println(MIDI.getData2());
          }
          break;
        case midi::ControlChange :
          {
            Serial.print("Controller, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" Controller#: ");
            Serial.print(MIDI.getData1());
            Serial.print(" Value: ");
            Serial.println(MIDI.getData2());
          }
          break;
        case midi::ProgramChange :
          {
            Serial.print("PropChange, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" program: ");
            Serial.println(MIDI.getData1());
          }
          break;
        case midi::AfterTouchChannel :
          {
            Serial.print("ChanAT, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" program: ");
            Serial.println(MIDI.getData1());

          }
          break;
        case midi::PitchBend :
          {
            uint16_t val;

            Serial.print("Bend, chan: ");
            Serial.print(MIDI.getChannel());

            // concatenate MSB,LSB
            // LSB is Data1
            val = MIDI.getData2() << 7 | MIDI.getData1();

            Serial.print(" value: 0x");
            Serial.println(val, HEX);


          }
          break;
        case midi::SystemExclusive :
          {
            // Sysex is special.
            // could contain very long data...
            // the data bytes form the length of the message,
            // with data contained in array member
            uint16_t length;
            const uint8_t  * data_p;

            Serial.print("SysEx, chan: ");
            Serial.print(MIDI.getChannel());
            length = MIDI.getSysExArrayLength();

            Serial.print(" Data: 0x");
            data_p = MIDI.getSysExArray();
            for (uint16_t idx = 0; idx < length; idx++)
            {
              Serial.print(data_p[idx], HEX);
              Serial.print(" 0x");
            }
            Serial.println();
          }
          break;
        case midi::TimeCodeQuarterFrame :
          {
            // MTC is also special...
            // 1 byte of data carries 3 bits of field info
            //      and 4 bits of data (sent as MS and LS nybbles)
            // It takes 2 messages to send each TC field,

            Serial.print("TC 1/4Frame, type: ");
            Serial.print(MIDI.getData1() >> 4);
            Serial.print("Data nybble: ");
            Serial.println(MIDI.getData1() & 0x0f);
          }
          break;
        case midi::SongPosition :
          {
            // Data is the number of elapsed sixteenth notes into the song, set as
            // 7 seven-bit values, LSB, then MSB.

            Serial.print("SongPosition ");
            Serial.println(MIDI.getData2() << 7 | MIDI.getData1());
          }
          break;
        case midi::SongSelect :
          {
            Serial.print("SongSelect ");
            Serial.println(MIDI.getData1());
          }
          break;
        case midi::TuneRequest :
          {
            Serial.println("Tune Request");
          }
          break;
        case midi::Clock :
          {
            ticks++;

            Serial.print("Clock ");
            Serial.println(ticks);
          }
          break;
        case midi::Start :
          {
            ticks = 0;
            Serial.println("Starting");
          }
          break;
        case midi::Continue :
          {
            ticks = old_ticks;
            Serial.println("continuing");
          }
          break;
        case midi::Stop :
          {
            old_ticks = ticks;
            Serial.println("Stopping");
          }
          break;
        case midi::ActiveSensing :
          {
            Serial.println("ActiveSense");
          }
          break;
        case midi::SystemReset :
          {
            Serial.println("Stopping");
          }
          break;
        case midi::InvalidType :
          {
            Serial.println("Invalid Type");
          }
          break;
        default:
          {
            Serial.println();
          }
          break;
      }
    }
  }
}

The library instantiation has a couple of specific details worth noting:

  • It uses a software serial port on pins 8 and 9. This leaves the regular serial port available for displaying the output messages.
  • It enables the soft-thru feature of the library, so it retransmits the messages it receives. We can insert the sniffer between two other devices without breaking the link.

To use this code:

  • Load the sketch
  • Connect the MIDI Shield inline, between other MIDI devices
  • Start a terminal application at 19200 baud.
  • Send MIDI message & observe the output on the terminal.

This sniffer has a hidden feature: if you hold down the D2 button on the shield, it will read and display the raw bytes from the serial port, rather than the output of the MIDI library.

Sniffer Button Callout

In Use

As a test, the sniffer was connected to a MIDI keyboard, and notes were occasionally pressed and released. In the screen capture that follows, the first few lines are shown in “cooked” format, and the next few are in “raw” format. We learn something interesting from the raw values.

Captured Events

The first few messages are note on and off pairs for low C on the keyboard. It turns on, it turns off.

Then the “raw mode” button is held, and the key is pressed a few more times. The first few articulations are somewhat rapid, the next few are a bit slower. The display moves to a new line for each new status byte received.

You’ll notice that the rapid articulations are sent using a single status byte (0x90), followed by pairs of data bytes (0xC, 0x41, then 0xC, 0x0, etc ). The controller is using running status to reduce the number of bytes sent, and using note on with velocity of 0 to take advantage of running status. The next few lines in the trace are the same articulation of low C, just played more slowly. Each articulation gets a status byte (0x90), followed by two data bytes. Even without running status, it’s still using note-on with velocity of 0, instead of note-off status bytes.

We have learned that this controller uses running status when the same message is sent in rapid succession, but sends fresh status bytes when the messages occur more slowly. The threshold for using running status seems to be somewhere at around ½ of a second.

Resources and Going Further

Resources

Going Further

  • The WAV Trigger implements MIDI on it’s serial port, allowing you to control up to 2048 velocity-sensitive samples.
  • If the shield form-factor isn’t right for you, we also sell the MIDI Connector all by itself.
  • The latest revision of the Bleep Drum kit now includes a MIDI input
  • Hairless MIDI allows you to open a virtual MIDI port over a standard serial bridge (like an FTDI adapter).

learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

CAN-Bus Shield Hookup Guide

$
0
0

CAN-Bus Shield Hookup Guide a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t380

Introduction

The CAN-Bus Shield provides your Arduino or Redboard with CAN-Bus capabilities and allows you to hack your vehicle!

CAN-Bus Shield

CAN-Bus Shield connected to a RedBoard.

This shield allows you to poll the ECU for information including coolant temperature, throttle position, vehicle speed, and engine rpms. You can also store this data or output it to a screen to make an in-dash project.

Materials Required

You will need the CAN-Bus Shield in order to follow along with this hookup guide.

CAN-BUS Shield

DEV-13262
$24.95

We also recommend you have access to the following materials.

Suggested Reading

If you aren’t familiar with the following concepts, you may want to review these tutorials before attempting to work with the CAN-Bus Shield.

Hardware Overview

There are several features to be aware of on the CAN-Bus Shield.

Shield-Labeled

CAN-Bus Shield with features labeled.

1. DB9 Connector

The primary hardware feature on this shield is the DB9 connector. This allows you to interface to OBD-II ports with a DB9 to OBD-II cable.

2. GPS Connector

The GPS connector on-board is a 6-pin, JST SH compatible connector. The board is designed to interface with either the EM-506 GPS Receiver, or the GP-735 GPS Receiver. The GND jumper allows the user to modify the GPS connector for units that do not have a GND connection on pin 5 of the connector.

3. LCD Connector

The LCD footprint on the shield is compatible with a male 3-pin JST connector and can interface with any of our serial LCD screens. The connection is designed for 5V LCDs, so don’t accidentally plug in a 3.3V option! The pin order is 5V, GND, and RX/D6 when looking at the shield straight on.

4. JoyStick

The joystick included on the shield provides a basic user interface for controlling screen displays or selecting CAN scan settings. The connector gives 5 basic user options:

  • Up
  • Down
  • Left
  • Right
  • Click selection

5. microSD Slot

This slot provides the user with the option of storing collected data onto a microSD card. Data collected can include user input on the joystick, CAN-Bus information collected, LCD outputs, or general I/O data.

6. Jumpers

There are six jumpers present on the CAN-Bus Shield.

  • 6a. SJ1 and SJ2 - These two jumpers allow the user to select between UART and Software Serial for the GPS unit to communicate with the Arduino.

  • 6b. SJ3 - This allows the user to separate pin 5 on the GPS connector from the GND line. This jumper comes closed by default.

  • 6c. SJ4, SJ5, and SJ6 - These three jumpers allow the user to select the DB9 pin configuration between OBD-II and CAN. The jumpers are defaulted to the OBD-II configuration that matches SparkFun’s OBD-II to DB9 cable.

    Note: Though the pin configuration is labeled as OBD-II, this is still a CAN-specific device. The jumpers are simply for configuring the shield to work with other OBD-II/CAN-Bus cables if necessary.

For reference, here are the configuration options showing which pins are selected on the DB9 connector for each setting.

Jumper Configurations for DB9 Pins
Bus LinesCAN PinsOBD-II PinsSolder Jumper
CAN-HPin 7Pin 3SJ4
CAN-LPin 2Pin 5SJ6
GNDPin 3Pin 2SJ5

7. CAN Pins

4 CAN lines are broken out to allow you direct access to the raw CAN data coming off of the DB9 connector. These pins are:

  • 5V
  • GND
  • CAN H (CAN HIGH)
  • CAN L (CAN LOW)

Again, this data is raw coming off of the CAN-Bus. It has not been filtered through the MCP2515 or the MCP2551 ICs.

Communication Methods

Because of all of the different hardware features on the shield, there are a couple different communication methods used.

  • SPI - The MCP2515 IC and the microSD slot both communicate with the Arduino via the SPI lines. The CAN Chip Select line is located on D10. The SD Chip Select line is connected to D9.

  • Analog In - The joystick is connected to pins A1-A5 on the Arduino. Each direction of the joystick has its own analog input.

  • Software Serial/UART - The LCD and GPS both communicate over serial lines with the Arduino. The LCD’s RX line connects to D6. The GPS either connects via Software Serial to D4 and D5, or to the UART port on D0 and D1.

Hardware Hookup

Solder Connectors

To get your CAN-Bus shield hooked up, solder on the Arduino Stackable Headers.

Headers Inserted

You can use the RedBoard to hold the headers in place while soldering them to the shield.

Once those are soldered, consider how you want to connect your LCD screen. You can use either male or female headers with 0.1" spacing, or the JST connector. Solder your interface choice onto the shield at this time as well.

LCD

Make sure you solder the connector onto the top of the shield, so you can access it while the shield is inserted in the RedBoard.

Connect the Brain!

In our case, the brain will be the RedBoard. Insert your shield into the RedBoard. Take your time and go slowly to prevent bending the header pins.

Connect the Extras

We recommend plugging in the GPS unit, LCD screen, and microSD card now. If you don’t plan to use any of these features, you can skip this step.

If you’re planning on putting your CAN-Bus/RedBoard combination into an enclosure, you may want to consider using an extension cable for the GPS unit. Enclosures can block the satellites from view and lead to spotty GPS functionality, so placing the GPS unit outside of any enclosures should alleviate those issues.

Note: If you are not using the EM-506, verify the pinout of your GPS unit and make sure the GND jumper is in the proper configuration for your unit.

We also recommend connecting your LCD screen at this time. Your method of connecting the LCD screen will depend on what connector you soldered onto the shield previously. Looking the shield straight on, the connections are 5V, GND, and TX, if you are not using the JST connector.

Make sure you use a formatted microSD card. Once all the extras are connected, your circuit should look like the following:

GPS Inserted

Connect to your CAN-enabled device

This can be a simulator or a vehicle. Plug the DB9 connector into the shield, and plug the DLC connector into the device to which you plan on talking. If your shield+Arduino turns on now, that’s ok. The vehicle/simulator can power the board over the cable.

Final Circuit

Once everything is inserted, your circuit should look similar to the following:

Connected to Simulator

In this case, we show the circuit connected to a CAN simulator. However, you could instead have your circuit connected to a DLC in a CAN-enabled vehicle.

Connected to Car

Here we see the circuit connected to Pete Dokter’s VW.

Arduino Code

Download the Library

There’s a really great library available for working with the CAN-Bus shield. You will need to download this and install it in your Arduino IDE. You can either search for it in the Arduino Library Manager or download the most recent version from the GitHub repository.

Download CAN-Bus Shield Library

If you aren’t sure how to install the Arduino library, please take a look at our tutorial here.

Example Sketches

There are several different example sketches included in the library, each with different functionality.

1.SparkFun_CAN_Demo - This sketch allows you test the CAN functionality of the board by itself.

2. SparkFun_ECU_Demo - This sketch runs all hardware on the shield together, and logs CAN data and GPS data to the SD card, while outputting data over the serial LCD. You will need to instally the TinyGPS library and the SD library for this to work.

3.SparkFun_GPS_Demo- This sketch runs through using the GPS module. You will need to instally the TinyGPS library for this to work.

4.SparkFun_Joystick_Demo - This quick sketch allows you to test the functionality of the on board joystick.

5.SparkFun_SD_Demo - This sketch allows you to verify and test functionality of the microSD socket on board. You will need to install the SD library for this to work.

6.SparkFun_SerialLCD_Demo - A quick sketch to make sure your serial LCD screen is functioning properly.

For our example, we are going to run through the ECU_Demo sketch, but feel free to use or modify the other sketches. If you decided to not plug in the microSD card, GPS unit and LCD screen, you should instead run the CAN_Demo.

ECU_Demo

This sketch shows off the basic functionality of each part of the shield. Once you’ve installed the library, open up Arduino and upload this code to your RedBoard.

Check through the comments in the code for details of what each section does, but the general flow of the sketch is as follows:

  1. The Arduino initializes the pins, variables, and baud rates for the GPS, LCD, uSD card, and CAN-Bus.
  2. In the setup loop, each device is started, and verified that everything is connected as it should. Both the CAN-Bus and uSD card will print either success or failure messages to the LCD screen.
  3. The shield will wait for the user to click the joystick to begin collecting data off of the GPS module and the CAN-Bus.
  4. Once the user has clicked to begin logging, the CAN-Bus will poll for the engine RPM, and will write the latitude, longitude, and GPS speed. A message that the unit is logging will appear on the LCD screen, and the actual engine RPM will be printed to the Serial monitor. The data collected is written to the uSD card.
  5. Each loop, the code checks if the user has clicked the joystick. If so, the unit stops logging.

    /****************************************************************************
    ECU CAN-Bus Reader and Logger

    Toni Klopfenstein @ SparkFun Electronics
    September 2015
    https://github.com/sparkfun/CAN-Bus_Shield

    This example sketch works with the CAN-Bus shield from SparkFun Electronics.

    It enables reading of the MCP2515 CAN controller and MCP2551 CAN-Bus driver.
    This sketch also enables logging of GPS data, and output to a serial-enabled LCD screen.
    All data is logged to the uSD card.

    Resources:
    Additional libraries to install for functionality of sketch.
    -SD library by William Greiman. https://github.com/greiman/SdFat

    Development environment specifics:
    Developed for Arduino 1.65

    Based off of original example ecu_reader_logger by:
    Sukkin Pang
    SK Pang Electronics www.skpang.co.uk

    This code is beerware; if you see me (or any other SparkFun employee) at the local,
    and you've found our code helpful, please buy us a round!

    For the official license, please check out the license file included with the library.

    Distributed as-is; no warranty is given.
    *************************************************************************/

    //Include necessary libraries for compilation
    #include 
    #include 
    #include 
    #include 
    #include 

    //Initialize uSD pins
    const int chipSelect = 9;


    //Initialize lcd pins
    SoftwareSerial lcd(3, 6);

    //Initialize GPS pins
    SoftwareSerial uart_gps(4,5);

    // Define Joystick connection pins
    #define UP     A1
    #define DOWN   A3
    #define LEFT   A2
    #define RIGHT  A5
    #define CLICK  A4

    //Define LED pins
    #define LED2 8
    #define LED3 7

    //Define baud rates. GPS should be slower than serial to ensure valid sentences coming through
    #define GPSRATE 4800
    #define LCD_Rate 115200

    //Create instance of TinyGPS
    TinyGPS gps;

    //Declare prototype for TinyGPS library functions
    void getgps(TinyGPS &gps);

    //Declare GPS variables
    float latitude;
    float longitude;
    int year;
    byte month;
    byte day;
    byte hour;
    byte minute;
    byte second;
    byte hundredths;
    float gps_speed;


    //Declare SD File
    File dataFile;

    //Declare CAN variables for communication
    char *EngineRPM;
    char buffer[64];  //Data will be temporarily stored to this buffer before being written to the file

    //Define LCD Positions
    #define COMMAND 0xFE
    #define CLEAR   0x01
    #define LINE1  0x80
    #define LINE2  0xC0


    //********************************Setup Loop*********************************//
    void setup() {
      //Initialize Serial communication for debugging
     // Serial.begin(9600);
      //Serial.println("ECU Demo");

      //Begin LCD serial communication
      lcd.begin(9600);

      //Begin GPS communcation
      uart_gps.begin(GPSRATE);

      //Initialize pins as necessary
      pinMode(chipSelect, OUTPUT);
      pinMode(CLICK,INPUT);
      pinMode(LED2, OUTPUT);
      pinMode(LED3, OUTPUT);

      //Pull analog pins high to enable reading of joystick movements
      digitalWrite(CLICK, HIGH);

      //Write LED pins low to turn them off by default
      digitalWrite(LED2, LOW);
      digitalWrite(LED3, LOW);

      //Initialize CAN Controller
      if(Canbus.init(CANSPEED_500))  /* Initialize MCP2515 CAN controller at the specified speed */
      {
        clear_lcd();
        lcd.print("CAN Init ok");
        //Serial.println("CAN Init Ok");
        delay(1500);
      }
      else
      {
        lcd.print("Can't init CAN");
        //Serial.println("Can't init CAN");
        return;
      }

       //Check if uSD card initialized
      if (!SD.begin(chipSelect)) {
        //Serial.println("uSD card failed to initialize, or is not present");
        clear_lcd();
        lcd.print("uSD failed.");
        return;
      }
      else{
          //Serial.println("uSD card initialized.");
          clear_lcd();
          lcd.print("uSD success!");
          delay(1500);
      }

      //Print menu to LCD screen
      clear_lcd();
      lcd.print("Click to begin");
      lcd.write(COMMAND);
      lcd.write(LINE2);
      lcd.print("Logging Data");

      while(digitalRead(CLICK)==HIGH)
      {
         //Wait for user to click joystick to begin logging
      }

      delay(1000);

    }

    //********************************Main Loop*********************************//
    void loop(){

      while(digitalRead(CLICK)==HIGH){

          digitalWrite(LED3, HIGH); //Turn on LED to indicate CAN Bus traffic

          Canbus.ecu_req(ENGINE_RPM,buffer); //Request engine RPM
          EngineRPM = buffer;
          //Serial.print("Engine RPM: "); //Uncomment for Serial debugging
          //Serial.println(buffer);
          delay(100);


          digitalWrite(LED3, LOW); //Turn off LED3
          delay(500);

        File  dataFile = SD.open("data.txt", FILE_WRITE); //Open uSD file to log data

          //If data file can't be opened, throw error.
          if (!dataFile){
              clear_lcd();
            lcd.print("Error opening");
            lcd.write(COMMAND);
            lcd.write(LINE2);
            lcd.print("data.txt");
            while(1);
            }

            clear_lcd();
            lcd.print("Logging.Click");
            lcd.write(COMMAND);
            lcd.write(LINE2);
            lcd.print("to stop logging");

            if(uart_gps.available())     // While there is data on the RX pin...
               {
                 digitalWrite(LED2, HIGH); //Signal on D8 that GPS data received.

                 //Print Latitude/Longitude to SD card
                 dataFile.print("Lat/Long: ");
                 dataFile.print(latitude,5);
                 dataFile.print(", ");
                 dataFile.println(longitude,5);

                  // Print data and time to SD card
                 dataFile.print("Date: "); dataFile.print(month, DEC); dataFile.print("/");
                 dataFile.print(day, DEC); dataFile.print("/"); dataFile.print(year);
                 dataFile.print("  Time: "); dataFile.print(hour, DEC); dataFile.print(":");
                 dataFile.print(minute, DEC); dataFile.print(":"); dataFile.print(second, DEC);
                 dataFile.print("."); dataFile.println(hundredths, DEC);

                  //Print GPS speed to SD card
                  dataFile.print("GPS Speed(kmph): ");
                  dataFile.println(gps_speed);

                 digitalWrite(LED2, LOW); //Turn off D8 LED.
               }

            dataFile.print("Engine RPM: ");
            dataFile.println(EngineRPM);

            dataFile.println();
            dataFile.flush();
            dataFile.close();   //Close data logging file
          }
          clear_lcd();
          lcd.print("Logging stopped.");
          while(1); //Stop logging if joystick is clicked

        }

        //********************************LCD Functions*********************************//
        void clear_lcd(void)
        {
          lcd.write(COMMAND);
          lcd.write(CLEAR);
        }

        //********************************GPS Functions*********************************//
        void getgps(TinyGPS &gps)
        {
          // Receive GPS latitude/longitude
          gps.f_get_position(&latitude, &longitude);

          //Call function to receive date and time from GPS
          gps.crack_datetime(&year,&month,&day,&hour,&minute,&second,&hundredths);

          //Also collect gps_speed
          gps_speed = gps.f_speed_kmph();

        }

If you’ve uncommented the lines for serial debugging, you should see something like this:

Engine RPM

Engine RPM readings from CAN-Bus shield hooked up to a simulator.

Once you have collected some readings, you can pull your uSD card out and take a look at the data recorded. There should be a file on your uSD card called “DATA.TXT”, and it should include information like the following:

Recorded Data

Note: If you’re only recording blank readings for your GPS, as shown above, make sure you have your GPS unit in an area with a good satellite view.

Once you’ve verified data is being stored to the uSD card, you’re good to go! You’ve successfully interfaced with your vehicle’s CAN-Bus and can now start digging into diagnostic codes and building projects around your engine’s data.

Resources and Going Further

Going Further

Once you’ve gotten the basic functionality of the CAN-Bus shield working, you can start hacking your car and interfacing your own electronics to your vehicle. Try checking out different PIDs on the CAN-Bus with your vehicle, or see if you can interface the CAN-Bus to control LEDs, speakers, and more!

If you have any feedback, please visit the comments or contact our technical support team at TechSupport@sparkfun.com.

Additional Resources

You can use these resources for more project ideas or troubleshooting.


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

Getting Started with OBD-II

$
0
0

Getting Started with OBD-II a learn.sparkfun.com tutorial

Available online at: http://sfe.io/t415

Introduction

Eventually on your journey into the world of embedded electronics, you will want to “hack” a vehicle for data. As with many other integrated systems, there is a specific ‘language’ for talking with vehicles. This tutorial will give a basic introduction to the On-Board Diagnostics (OBD) specification that vehicles and other industrial machines use to communicate with the outside world.

Warning! Modifying your OBD-II system to a non-certified state is considered a Federal Offense. The information provided is only intended for reading from the OBD-II spec. Hack at your own risk!

The Definition

So what exactly is the OBD specification, and why do we care? According to the Environmental Protection Agency’s website:

On-Board Diagnostics, or “OBD,” is a computer-based system built into all 1996 and later light-duty vehicles and trucks, as required by the Clean Air Act Amendments of 1990. OBD systems are designed to monitor the performance of some of an engine’s major components including those responsible for controlling emissions.

In other words, OBD is the language of the Engine Control Unit (ECU), and it was designed to help fight emissions and engine failures.

Saving the planet is great (shout out to you citizen scientists!), but what this also means is we can access other features of the car and collect information from and on those parts. Learning how to work with those protocols also means that you can determine what that Malfuction Indicator Light (MIL) (aka the Check Engine Light) on your dash is referring to when it tells you there’s an engine problem. If you or your mechanic has ever read the DTCs (Diagnostic Trouble Codes) on your vehicle, they are using OBD-II.

Unfortunately, the actual protocols themselves are not available publicly (if only they’d open source!), but we’ve attempted to collect and clarify as much as possible.

The Hardware

Any vehicle manufacture from 1996 or later is required by law to have the OBD-II computer system. You can access this system through the Data Link Connector (DLC). It is a 16 pin connector that can tell you which protocol your car communicates with, depending on which pins are populated in it.

DLC-Pins Labeled

Data Link Connector in a 1998 Jeep Cherokee, with the pins labeled.

In cars, it will be located under the dash, near the driver’s seat, or in the vicinity of the ashtray – somewhere easily accessible from the driver’s seat without the use of tools to access it (i.e., you don’t need a screw driver to pull off a panel to get to it).

Terminology

Before we get too much farther, let’s make sure we understand all the keywords used in these protocols.

Engine/Electronic Control Unit (ECU)

The ECU can refer to a single module or a collection of modules. These are the brains of the vehicle. They monitor and control many functions of the car. These can be standard from the manufacturer, reprogrammable, or have the capability of being daisy-chained for multiple features. Tuning features on the ECU can allow the user to make the engine function at various performance levels and various economy levels. On new cars, these are all typically microcontrollers.

Some of the more common ECU types include:

  • Engine Control Module (ECM) - This controls the actuators of the engine, affecting things like ignition timing, air to fuel ratios, and idle speeds.
  • Vehicle Control Module (VCM) - Another module name that controls the engine and vehicle performance.
  • Transmission Control Module (TCM) - This handles the transmission, including items like transmission fluid temperature, throttle position, and wheel speed.
  • Powertrain Control Module (PCM) - Typically, a combination of an ECM and a TCM. This controls your powertrain.
  • Electronic Brake Control Module (EBCM) - This controls and reads data from the anti-lock braking system (ABS).
  • Body Control Module (BCM) - The module that controls vehicle body features, such as power windows, power seats, etc.

Diagnostic Trouble Code (DTC)

These codes are used to describe where an issue is occurring on the vehicle and are defined by SAE (you can find the whole spec here for a cost). These codes, can either be generic or unique to the vehicle manufacturer.

These codes take the following format:

XXXXX

  • First unit identifies the type of error code:

    • Pxxxx for powertrain
    • Bxxxx for body
    • Cxxxx for chassis
    • Uxxxx for class 2 network
  • Second digit shows whether the code is manufacturer unique or not:

    • x0xxx for government-required code
    • x1xxx for manufacturer-specific code
  • Third digit shows us what system the trouble code references:

    • xx1xx/xx2xx show air and fuel measurements
    • xx3xx shows ignition system
    • xx4xx shows emissions systems
    • xx5xx references speed/idle control
    • xx6xx deals with computer systems
    • xx7xx/xx8xx involve the transmission
    • xx9xx notates input/output signals and controls
  • Digits four and five show the specific failure code.

    • xxx00 to xxx99 - these are based on the systems defined in the third digit.

You can find some incomplete lists of DTCs here and here.

Parameter Identification (PID)

These are the actual meat and potatoes of the information you can pull off of an OBD-II system. The PIDs are the definitions of the different parameters you could be interested in checking out. These are similar to the third digit in the DTCs.

Not all PIDs are supported on all protocols, and there can be several unique, custom PIDs for each manufacturer. Unfortunately, these also are not generally published, so you may need to do a lot of hunting and/or reverse engineering to determine to which system each PID relates.

There are different modes available, and each mode has several options of PIDs available in that mode. For more general information on that, please check out the PID wiki page.

Malfunction Indicator Lamp (MIL)

The MIL is that terrible little light in the dash that indicates a problem with the car. There are a few variations, but they all indicate an error found by the OBD-II protocol.

Check Engine Light

“Check-Engine-Light” by IFCAR - Own work. Licensed under Public Domain via Commons

Another possibility you might find on your dash includes this option:

Malfunction Indicator Light

“Motorkontrollleuchte” by Benutzer:chris828 - Own work by the original uploader. Licensed under Public Domain via Commons

No matter which one it is, these usually aren’t great lights to see, unless you feel like hacking!

OBD-II Protocols

There are five different communication protocols available under the OBD-II spec. Like so many things, manufacturers tend to have their preferences and think their protocol is best, hence the variation. Here’s a quick overview of each and a description of the pins used on the DLC for each.

SAE J1850 PWM

This signal is Pulse Width Modulation, which runs at 41.6 kbps. This protocol is generally used on Ford vehicles.

SAE J1850 PWM
FeatureDescription
BUS + Pin 2
BUS - Pin 10
12V Pin 16
GND Pins 4, 5
Bus State:Active when BUS + is pulled HIGH, BUS - is pulled LOW
Maximum Signal Voltage:5V
Minimum Signal Voltage:0V
Number of bytes:12
Bit Timing:'1' bit - 8uS, '0' bit - 16uS, Start of Frame - 48uS

SAE J1850 VPW

This protocol is Variable Pulse Width, which runs at 10.4 kbps. GM vehicles typically use this version.

SAE J1850 VPW
FeatureDescription
BUS +Pin 2
12V Pin 16
GND Pins 4, 5
Bus State:Bus idles low
Maximum Signal Voltage:+7V
Decision Signal Voltage:+3.5V
Minimum Signal Voltage:0V
Number of bytes:12
Bit Timing:'1' bit -HIGH 64uS, '0' bit -HIGH 128uS, Start of Frame - HIGH 200uS

ISO 9141-2

If you have a Chrysler, European, or Asian vehicle, this is your protocol. It runs at 10.4 kbps and is asynchronous serial communication.

ISO 9141-2
FeatureDescription
K Line (bidirectional)Pin 7
L Line (unidirectional, optional)Pin 15
12V Pin 16
GND Pins 4, 5
Bus State:K Line idles HIGH. Bus is active when driven LOW.
Maximum Signal Voltage:+12V
Minimum Signal Voltage:0V
Number of bytes:Message: 260, Data: 255
Bit Timing:UART: 10400bps, 8-N-1

ISO 14230 KWP2000

This is the Keyword Protocol 2000, another asynchronous serial communication method that also runs at up to 10.4 kbps. This also is used on Chrsyler, European, or Asian vehicles.

ISO 14230 KWP2000
FeatureDescription
K Line (bidirectional)Pin 7
L Line (unidirectional, optional)Pin 15
12V Pin 16
GND Pins 4, 5
Bus State:Active when driven LOW.
Maximum Signal Voltage:+12V
Minimum Signal Voltage:0V
Number of bytes:Data: 255
Bit Timing:UART: 10400bps, 8-N-1

ISO 15765 CAN

This protocol has been mandated in all vehicles sold in the US from 2008 and later. However, if you have a European car from 2003 or later, the vehicle may have CAN. It’s a two-wire communication method and can run at up to 1Mbps.

ISO 15765 CAN
FeatureDescription
CAN HIGH (CAN H)Pin 6
CAN LOW (CAN L)Pin 14
12V Pin 16
GND Pins 4, 5
Bus State:Active when CANH pulled HIGH, CANL pulled LOW. Idle when signals are floating.
CANH Signal Voltage:+3.5V
CANL Signal Voltage:+1.5V
Maximum Signal Voltage:CANH = +4.5V, CANL = +2.25V
Minimum Signal Voltage:CANH = +2.75V, CANL = +0.5V
Number of bytes:L
Bit Timing: 250kbit/sec or 500kbit/sec

Using a Simulator

While these protocols are great for collecting data from your vehicle, it can be a real pain when prototyping to have to sit with a computer, various electronics, and cables running all over the place in the front of your car. Luckily, there are many simulators out there that allow basic prototyping and testing of OBD-II systems.

We have a few different simulators laying around here that are useful for working with these protocols. We’ll update this section if/when we get our hands on any additional ones.

ECUsim 2000

ECUsim 2000

This ECU simulator is designed and manufactured by the lovely folks over at ScanTool. You can view all of the product information over at their product page here.

To get started using this simulator, you must make the following connections:

  1. Plug a USB cable in to the simulator and the computer. Install the necessary drivers.
  2. Plug in the OBD-II cable to the simulator.
  3. Power your simulator off of the supplied 12V power supply.
  4. Open up a serial terminal at 115200 bps, 8,N,1 connecting to the serial port the simulator is configured to.
  5. Configure the simulator to the protocol you desire to test.
  6. Connect to your ECU device (OBD-II board, CAN-Bus Shield, Raspberry Pi, etc.)

Now, you can leverage the power of the simulator by verifying that the data being transmitted over the bus is what your ECU reader is receiving and vice verse.

Several different programming options are available for configuring the simulator. Check out the programming manual for more information. The version we currently have has firmware compatible with several different OBD-II protocols, which will vary depending on what you order.

The programming manual also includes all of the commands that you can use for the simulator.

For example, if we need to determine what protocol our simulator is currently set to, we would use the SPI command. In our terminal, that would look like the following:

SPI command

Reading ECUsim 2000 protocol settings.

This shows that the simulator is currently set to the ISO 15765-4 protocol (a.k.a CAN), with an 11 bit ID type and is running at 500 kbps.

If you then need to send data from your simulator to a device such as the SparkFun OBD-II UART Board or CAN-Bus Shield for testing, you can use the transmit command SOMT <header>, <data>. For example, if we want to send the command that the engine fuel pressure is 100kPa, we would send SOMT followed by the Parameter ID (PID) for fuel pressure, which is 0A, and follow that with the hex value for 100 (64) in this case.

Transmitting Fuel Pressure

Transmitting fuel pressure via the ECUsim 2000.

If we initially leave the connection floating (by forgetting to tighten the anchor screws on the DB9 connector) in order to simulate a connection problem, we receive the CAN ERROR message the first time we send the command. On this simulator, that means that there is a problem between the simulator and our CAN reader. Once we fix the connection however, the simulator is able to send the data, and tells us exactly what it transmitted. Pretty neat!

Resources and Going Further

Going Further

Now that you have a basic understanding of the OBD-II protocols and how to work with the various communication tools available, it’s time to make your own project!

If you have any feedback, please visit the comments or contact our technical support team at TechSupport@sparkfun.com.

Additional Resources

Check out these products and projects for more OBD-II inspiration!


learn.sparkfun.com |CC BY-SA 3.0 | SparkFun Electronics | Niwot, Colorado

Viewing all 1123 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>