Arduino for Kids Young and Old

March 17, 2018 | Author: denydi | Category: Parameter (Computer Programming), Arduino, Fahrenheit, Microcontroller, Data Type
Share Embed Donate


Short Description

manuale per giovani e meno giovani...

Description

Copyright

From legal to ISBN

Arduino® for Kids Young and Old ©2014 Daniel W. Milligan All content is copyright 2014, Daniel Milligan. All rights reserved. No part of this publication or the files that it is comprised of shall be legally produced, reproduced, or transmitted by any form or any means without the express permission of the publisher. Published by Daniel Milligan 2014, all photo's and images are copyright Milligan Photography unless otherwise noted. Limit of Liability and Disclaimer of Warranty: The publisher has made every effort in preparing this book to ensure that the content is accurate however the information provided herein is provided "as is" and without warranty. Daniel Milligan et al makes no representation or warranties with respect to the accuracy or completeness of the contents of this book and specifically disclaims any implied warranties of merchantability or fitness for any particular purpose and shall in no event be liable for any loss of profit or any other commercial damage, including but not limited to special, incidental, consequential, or other damages. Trademarks: This book identifies product names and services known or suspected to be trademarks, registered trademarks, or service marks of their respective holders. They are used throughout this book in an editorial fashion only. While great care has been taken to appropriately identify these items, there is potential that some may have not been identified as such and Daniel Milligan cannot vouch for the accuracy of this information. Use of a term in this book should not be regarded as affecting the validity of any trademark, registered trademark, or service mark. Daniel Milligan is not associated with any product or vendor mentioned in this book other then being the primary of Milligan Photography. First edition 2014 ISBN-10: 0985855630 ISBN-13: 978-0-9858556-3-5

Thanks go out to all of my friends and family, and especially my wife Sylvine for all of the support and love through the years. The resources linked to at the end of this eBook were valid as of the time of the writing of this eBook and include links to various tools helpful to those getting started with open source hardware and hobby programming / engineering.

Introduction

Getting started with Ardunio®

First I am going to make some assumptions both knowingly and unknowingly. I work with a lot of this technology on a regular basis and may inadvertently leave something out. It's not that I don't feel it's important or needed but more that I might not think it needs explaining or requires additional detail. With that, if you should have any questions please do not hesitate to email me at Email Dan . This book is intended for those wanting to start working with open source hardware and software in order to build their own creations using tools and components readily available. While I will point you to specific links to learn more about various software and/or hardware modules, I am not going to get into too much of programming skills 101 or hardware design 101. My assumption is that you either have some knowledge in this area or are willing to go and learn it as you go. I will try to cover everything in enough detail that you won't have to dig too far to get additional information however it will vary depending on the individual. As the title of the book states, this book is focused on developing with the Arduino® platform. While a number of the examples and overall concepts will be applicable to a number of micro-controllers, I will be focusing on the Arduino® family of modules and specifically those built around the Atmel® AVR family of devices. The following are some basic terms I will be using that you should be aware of.

An experimental design (software/hardware) used to prove a theory or realize an idea An open source hardware/software solution used for prototyping Arduino® An add on card i.e. expander board to be used in conjunction with an Shield Arduino®. This may also be called a daughter board. Direct Current which is what most prototyping boards utilize DC Alternating Current which is the standard current coming into a house and AC delivered to each outlet within the house * A high level programming language and environment originally designed by Java™ Sun® Computer and now owned by Oracle® C A high level programming language designed at Bell Labs[ 1 ] An Arduino® term for describing the code to be executed on the Arduino® Sketch Universal Serial Bus - standard connection for peripherals such as mice, USB keyboards, card readers, etc. Light Emitting Diode LED A software application which generates executable code from a source file Compiler such as a sketch. A data type found in a sketch which can be assigned a value. Variables can Variable be of different types to represent different data types such as integers, characters, and floating point numbers A value that can represent both positive and negative numbers Signed value Unsigned value A value that can only represent positive numbers A value passed into a method during program execution Parameter A value returned by a method once it has finished processing Return Value A software construct utilized in high level programming languages such as Object Java™ and C++ Baud, Baud Rate The speed at which a communication device operates at Prototype

* As I am based in the United States, a typical AC current coming into each house would be in the 120V range while in Europe, the typical value would be in the 240V range. [ 2 ]. While most items purchased operate via 120V's AC, in actuality they operate on a DC voltage and have transformers inside or as part of the power cord to convert from AC to DC. Having the right tool is always beneficial when working on any kind of project. The same is true when working with both software and hardware. Before you get started you will want to make sure you have the following items:

a functioning computer (Macintosh® OSX, Windows® based PC, or Linux®) an available USB host port. The port should be of the type A which will look

something like this: a clean area to work in and spread out a bit internet access to look up information the Java Runtime Environment which can be downloaded: here the Arduino® development environment which can be downloaded: here ** an Arduino® kit such as this one from MakerSHED *** a breadboard for prototyping circuits *** jumper wires for connecting the various parts of your circuits together and to the Arduino® A +5v power supply able to deliver 2 Amps of current such as: 5v 2A Supply . and a comfortable chair ** Electronic components are very susceptible to static electricity. This means that any static electricity that builds up on yourself could damage the component, like the micro-controller, when you touch it and transfer that energy from yourself to the component. In some cases, this damage cannot be detected and the component will work most of the time but not always. Consider it like a crack in a bowl. It may be fine most of the time however once in a while, some liquid may spill. And then if it happens to be hot, you will get burned.

*** If you happen to purchase the Arduino® kit as specified above then there will be a breadboard and jumpers included with it. The following list are some additional items which are not required however I regularly use during development.

a soldering iron for building circuits a solder sucker for removing the excess solder applied with the soldering iron solid strand wire in the 22 gauge (AWG) area of thickness wire strippers wire cutters small pliers a multimeter for measuring the voltage at a given point and checking continuity within a circuit.

Finally a list of some items that you will probably not need however knowing they exist is always good.

a logic analyzer for debugging protocol issues such as those that might come up when using serial peripheral interfaces (SPI) among other things an oscilloscope for debugging issues related to communication signals or Pulse Width Modulation (PWM) output

Most if these items are available at your local Radio Shack , Fry's , or whichever local

electronics store catering to hobbyists in your corner of the world. In some cases, the devices used will only be available from on-line merchants which I will attempt to provide various links to companies I have dealt with successfully. All circuit diagrams included in this book are provided using the creative commons license for Attribution - Share Alike 3.0 Unported[ 3 ]. The complete set of circuit diagrams can be found here: Circuit Diagrams . Each of the circuit diagrams were created using the Fritzing development tool which can be found on the web at Fritzing.org . For each image that is bound by the creative commons license, the following icon will be associated with it: . In addition, I have followed standard practice in coloring the wires at least in terms of +5v (red) and GND (black). For the schematics, opposed to using traditional schematics, I have chosen to use the breadboard images out of Fritzing as I feel they better convey the required information in this context. With all of that said, lets dive into it and have some fun!

Setup

Getting ready to develop

Setting up your computer for development can be a challenge depending on the tools and the support available. Hopefully that will not be the case with the Arduino® toolset. However, just in case, I will walk you through the process. If you are already up and going then please skip over this chapter and proceed to developing your first sketch in the Arduino® Platform chapter. While you may already have Java™ installed on your computer, it wouldn't hurt to ensure you have the latest version so please download the following package from Oracle® here . This page should detect your current system and give you a download button to start. Once downloaded, follow the directions provided by Oracle® to install this on your system. Now that Java™ is ready to go, you can download the Arduino® environment for development. This software can be found here . For this package, there isn't an installer* per se however you do need to unzip it. Navigate to your download folder or the destination that you selected when downloading the package to get access to the application. If you are on a Macintosh OSX® platform than it will unzip the package for you. If you are on a Windows® platform than the Explorer application will be able to unzip it for you. For the Linux® platform, when you download it, I expect it will ask you if it should open it with the archive manager which you can choose to or from the terminal you can utilize the unzip application. For my own development, I moved the package to my Development folder which is located on my desktop. I than unzipped it in that location so it will be centrally located with other things I am working on. * As of the 1.05 version of the Arduino™ software for Windows® there is an installer available to assist with installing the Arduino™ software. I have left this chapter in for reference and for those not on a Windows® based platform. The following sequence is the platform extraction and placement on my Windows® based machine and the initial loading of the platform. The first image shows the file once it has been downloaded from the Arduino™ website.

This image shows the Windows® wizard for extracting a zipped file. I have selected my Development folder on my desktop for the placement of the platform.

This image shows the Windows® extraction in progress which on my system is fairly slow as it is not the fastest system in the world.

This image shows the extracted file folder which has inside it the actual Arduino® folder. Because I didn't want to have to go too deep into the folder hierarchy, I elected to move it up one.

This image shows the extracted folder once I moved it up one level, removing the arduino1.0.3-windows folder.

In this image, I have elected to display everything as large icons. At this point you will be ready to launch the development platform.

When you first launch the application, you will most likely see the following security warning. This is expected and is just noting to you, the end user, that the file was downloaded and the publisher is unknown. While I don't expect you to have any issues running this software, I do advise that you take the necessary precautions with your system and ensure that you have up to date virus protection installed and running. Also note the checkbox which will allow you to skip the security warning the next time you open the application.

At this point, you should see the following main screen for the Arduino® environment.

The Arduino® Platform

What it is

The Arduino® package is a cost effective, easy to use, prototyping board which can be enhanced to do many tasks. The micro-controller that this board utilizes has been created with a number of general purpose input and output (GPIO) pins. These GPIO pins have many functions that you can utilize to create a number of designs. While the controller and overall design are fairly simple in todays terms, the community that has developed around the platform is where it really shines. With a simple internet search you can find a number of forums and stores promoting and selling various products to enhance your Arduino®, from Ethernet port shields to LCD shields to a number of other's in between.[ 4 ] Considering all of these expansion options are readily available, the Arduino® becomes a very useful micro-controller. In this book I will focus on the abilities of one of the Arduino® boards, the Arduino® UNO. It does everything that we need and is based on a 5 volt system which can be used with a variety of external devices. This will also limit the discussion to one of the more popular Atmel® AVR micro-controllers, the ATmega328.

What it isn't

This is not a full fledged computer with monitor, keyboard, and mouse as you have probably already noticed. While it is fast doing simple tasks, it will not break any super computer processing speeds. With today's processors running at 100x or 200x the speed of this little controller, it isn't surprising that it isn't as fast as a full fledged computer. The overall Arduino® environment is based on approachable and easy to use tools. The environment does it's best to prevent mishaps when using it and the target devices. With this extra security comes limitations and potential speed losses. There is also potential for conflicting devices due to the overloading of processor pins to internal functions. However for the kinds of projects that we will want to do, it is more than capable. Getting Started So where do we start? Assuming you have already purchased an Arduino® and have the development environment installed, our first order of business is to make sure everything is working ok. If you haven't already downloaded and configured your system, take a look at the chapter entitled Setup for information regarding setting up your development environment. When ready, open up the development platform and you should see something like this:

Now we need to make sure your Arduino® is properly registered with your development environment. If you have not connected your Arduino® to your development system than you may need to load the drivers in order for the board to communicate with your system. On the Windows™ platform, you can find the necessary drivers in the drivers folder under the Arduino® software folder. If you are running on the Macintosh® or Linux® then you will want to visit the USB chip manufacturer web page to download the drivers for your particular system. They can be found here . You will also find the latest Windows® drivers there. The first thing to notice in the previous image is the currently selected board and

communication port i.e. COM port. This is shown in the lower right hand side of the image. This development board is an Arduino® UNO connected to COM3. In order to work with your Arduino® you will need to ensure the correct COM port is selected along with the correct hardware board. To do this, select the tools menu item on the menu bar. Note: the menu can take some time to actually pop up depending upon the user's environment. From the tools menu, select board and then select the type of board that you have. The following image shows the board selection where I have selected the Arduino® UNO as my board.

Once the proper board has been selected, the corresponding COM port that the operating system has assigned to the development board can be selected. The following image shows the selection of the COM port for the attached Arduino® board.

Before we go any further, it will be good to look at some of the different variable types that you may find within an Arduino® sketch. The following are some of the more common variable types you may come across:

char unsigned char byte int unsigned int long unsigned long float

double void

a signed value representing the values from -128 to 127. Char's are stored using 8 bits of data an unsigned value representing the values from 0 to 255. Like a char, an unsigned char is stored using 8 bits of data Analogous to an unsigned char and used within Arduino® sketches a signed integer type representing the values from -32,768 to 32,767. Int's are stored using 16 bits of data* an unsigned integer type representing the values from 0 to 65,535. Int's are stored using 16 bits of data* a signed value representing the values from -2,147,483,648 to 2,147,483,647. Long's are stored using 32 bits of data.* an unsigned value representing the values from 0 to 4,294,967,295. Unsigned long's are stored using 32 bits of data.* a float value represents a wide range of signed values in decimal notation and is primarily used to represent fractions and other non integer values. Float's are stored as 32 bits of data.** For the Arduino® platform, the double is analogous to a float. the void type typically indicates a non-value however is dependent on its usage. For method definitions, a return value of void indicates that no value will be returned by the method.

* the size of an int or long may be different based on the processor in use. For the Atmel® micro-controller we are using an integer will be 16 bits i.e. 2 bytes while the long will be 32 bits i.e. 4 bytes. Other micro-controller's may have larger integer sizes. You should verify the sizes of the data types of your target hardware to ensure you do not make any false assumptions. ** floats are stored as 32 bits of data which represents the mantissa and the exponent. Floats have about 7 decimal digits of precision which should be considered when processing data. Every Arduino® sketch will consist of two functions i.e. methods, setup and loop. As you can probably imagine, setup is where you will configure your Arduino® for operation such as specifying the direction of data flow on a certain pin etc. The loop function is where all of the processing is done and will be executed basically as fast as the micro-controller can go. Let's take a look at each of these methods in some detail and see how they work. Setup As mentioned above, this is where the configuration of the micro-controller is performed. This isn't to say that the micro-controller configuration cannot be changed outside of this method however for consistency it is best to do any configuration here. There will be exceptions to this rule but for now this will be good advice to follow. So what kind of things should be configured when you start executing code on your Arduino®? Primarily it will be proper to configure your digital pins as either

input or output and start any sub-systems that may need to be started such as the serial communications port. The main reason is that this method is only called once while the loop is called every pass through the code. It wouldn't be very productive to keep initializing the serial communications and the like every time through the main loop. Consider the following code snippet from this sketch :

/* * Setup - main configuration point of our sketch to configure * the Arduino for operation * * Params - none * * Returns - nothing */ void setup() { Serial.begin(9600); pinMode(13, OUTPUT); }

The first portion are the comments describing what the intention of the method is. I say intention as with all things, they start as good intentions and then veer off from there. For Arduino® sketches, there are two acceptable types of comments, the block as shown above and the single line [ 5 ]. The block comment is everything between the opening token /* and the closing token */ which indicates to the Arduino® compiler that what lies between the tokens including the tokens themselves can be ignored. While the above example shows a block comment spanning multiple lines, there is nothing preventing it from being a set of single line comments such as:

// // Setup - main configuration point of our sketch to configure // the Arduino for operation // // Params - none // // Returns - nothing //

I would be reluctant to put the whole thing on one line as I prefer to have some whitespace i.e. blank area around the comments for readability. You could do something like the following:

// Setup - main configuration point of our sketch to configure the Arduino

for operation

The other things of note inside of the comment block is the Params and Returns lines. The first tells the viewer of your code what parameters need to be passed into the method. As shown above, not all methods require parameters. The second line indicates what values one might expect to be returned from the method when it finishes. In this case, nothing is returned. Later on we will explore methods that take both parameters and return values to the calling application. This convention is something I tend to use most often and part of the reason why I don't use a single line comment at the beginning of functions i.e. methods. Even though nothing is passed in as a parameter nor returned, have it spelled out never hurts. Following the comment block is the name of the method which declares what the return value type will be, if any, and also details any parameters that need to be supplied when the application calls the method. In this case, the method does not take any parameters and does not return any value. This is indicated by the word void. Next comes the opening brace, { which indicates the start of the method definition. There is also a closing brace, } which indicates the end of the method definition. The opening and closing braces are not limited to method definitions and will be used in additional constructs which will be explored later on. Now we get to the actual code. The first line is as follows:

Serial.begin(9600);

This code, which is part of the standard Arduino® platform, calls the begin method which is part of the Serial object. An object is a collection of related methods and variables that work together to perform a task. The C++ term for an object is a class which is fundamental to C++ and object oriented programming in general. We will be looking at various objects and constructs as we progress through this book. As you can see, the begin method takes a single parameter which in this case is a value of 9600. This value represents the speed at which the serial port of the Arduino® will operate at. While the speed can be a number of values [ 6 ], 9600 is one that I remember easily and typically use. In terms of speed, this value is typically given as a baud rate which is the number of symbols transmitted per second. This value happens to be the same as the bit rate resulting in 9600 bits per second in transmission speed. The end data rate however will be somewhat less due to some of the bits being used to indicate the start and ending of each packet. The main thing to note is that when you open up a serial monitor application such as the one included with the Arduino® software, you will need to ensure that it is set for the same speed that was specified in the sketch. So why would we want to start the serial port? In general this is the simplest way to debug your application. You upload your code, it executes and you monitor the results. It can be real useful when things are not working as expected and you need to dig in and figure out what the issue is. If application speed is a concern, than once you have completed your project, you can remove all of this code resulting in a smaller package and faster execution time. A few things to be careful of though is that for time sensitive applications, the inclusion or exclusion of this code could change your results. Also care should be taken when placing any types of debug lines as you may end up with more information than

you can use.

pinMode(13, OUTPUT);

The second line of code, also a standard part of the Arduino® platform, is where the digital pin, 13, is configured as an output. On the Arduino®, there is a LED connected to the digital pin number 13 which can be used as a visual indicator of sorts. The first parameter is the pin that will be configured while the second parameter is the mode which in this case is OUTPUT. The word OUTPUT is a constant value defined by the Arduino® platform. Typically it is good practice to create a constant value to specify what the particular pin will be doing opposed to a raw number. In this case, I left the number in the code to highlight what pin I was using and to keep the sketch simple. In the future examples, I will use more meaningful identifiers for these types of values. Loop Loop is where all of the main processing of your sketch will be done. This method will be executed soon after the Setup method has completed and will continue to be executed for the life of your application that is to say until you power down your Arduino®. While this keeps the complexity of your code down, there are a few things to be careful of when writing code for the loop. The first thing to remember is that the speed at which loop is called can be variable based on the speed of the micro-controller and how much work is being done inside of your loop. Another thing to note is that if you need to receive input from the user, you may need to do that over multiple passes of the loop opposed to waiting until the input arrives otherwise you will prevent other activities from happening. Building upon the above code, I will demonstrate the basic sketch of turning the pin 13 LED on and off periodically causing it to flash. This is also included in the sketch noted previously. /* * loop - main method which will be called each time the * Arduino goes through its main loop * * Params - none * * Returns - nothing */ void loop() { digitalWrite(13, HIGH); delay(500); digitalWrite(13, LOW); delay(500); Serial.println("Led 13 has been toggled on and off."); }

Again the method starts with a comment block describing what the method is intended to do along with the name of the method declaration indicating that no parameters are passed to the method and the method doesn't return any value.

digitalWrite(13, HIGH);

This first line of code calls the method digitalWrite which instructs the micro-controller to set the output value on pin 13 to HIGH. The word HIGH is another predefined value which you can use throughout your sketch as needed. Because we are writing a digital value out to this pin, it can only be one of two values, LOW or HIGH. These values correspond to 0 volts when set to LOW and 5* volts when set to HIGH. With this, the LED connected to pin 13 will turn on as there is now a positive voltage emitting from the given pin. When set to LOW, the voltage is set to 0 volts resulting in the LED turning off. * the Arduino® UNO runs on 5 volts hence the value when set to HIGH will be 5 volts. Other boards, such as the Arduino Due , run at a lower voltage such as 3.3v so the output voltage when set to HIGH will be equivalent to that lower voltage. When using a board with a lower overall voltage, care must be taken that the resistors and other components recommended in this book are adjusted accordingly. Because of the smaller voltage, less current will be generated as the current is equal to the voltage divided by the resistance. While this may be alright in most cases, you should also verify the maximum amount of current that each output pin can supply as they may differ across micro-controller devices.

delay(500);

The delay method is used to stop execution for a period of time. When I say stop execution, I really mean stopping execution of your code. The micro-controller is still processing data however your code will be paused until the delay method is finished. For this example, using delay is fine however later on we will explore better approaches to handle this. So why would I want a delay in my code anyways? Remember that the loop method is executed quite frequently and we want to be able to see the LED turn on an off. The delay that is specified is 500. This value is expressed in milliseconds which is to say how many 1/1000's (0.001) of a second the micro-controller should pause. For the above example, we are pausing for 500 milliseconds which is 1/2 second. The end result is the LED will turn on for 1/2 second and then back off for another 1/2 second or so. I say so because I have an additional line of code after the second delay which will take some time to execute and will skew the time. I could try and compensate by reducing the second delay to 499 milliseconds however that would be a guess unless I knew for sure how long the last line of code would take to execute. I would also have to factor in which micro-controller was executing the code along with the speed of that micro-controller. All in all it would be a significant challenge to do correctly. And

while were are speaking of the last line of code, it's purpose is to just give us feedback that the loop is working properly and our board is functional. Typically I wouldn't do this as it will just fill up the communications buffer and if I didn't have the delay(s), we would quickly fill our monitor buffer with the same not so helpful information. This is an example of where you would want to be careful with where you place your debugging i.e. informational output data.

Serial.println("Led 13 has been toggled on and off.");

The serial class has a number of methods of which one of them is shown in the above code snippet. This particular version is for printing out a full line of text including the line feed so the next line will start on a new line. There is a related method Serial.print that will print out text much the same way however will not include a line feed. It is useful when you want to print out a number of items all on one line such as:

Serial.print("Led: "); Serial.print(13); Serial.println(" has been toggled on and off.");

The above code will result in the same output however I could have easily replaced the number 13 with a variable in order to use this code for any pin. Summary So pulling it all together, an Arduino® sketch consists of two methods, the setup and loop methods. The setup method is used to configure your Arduino® for how it should operate while the loop method is where all of the work will be done for the life of the application. In addition, there are built in constants and classes such as the Serial class which can be used to assist with debugging or relaying information to the user.

Input

Monitoring our surroundings

Now that we have the Ardunio® setup and ready, we can dig into more interesting topics. The first of these is the input system that allows us to monitor our surroundings utilizing various analog and digital devices. In the following sections we will look at analog and digital inputs from a variety of devices which allow us to interpret our surroundings and act upon the various events when they happen. Analog Input The Arduino® has several analog pins that can be used to read in the voltage level of a particular device to determine its value. The actual number of pins available to you will be dependent upon which AVR micro-controller is built into the board you have purchased. Regardless though, the basic operation will be the same. The analog input pins have a 10 bit resolution which is to say that each pin can detect up to 1024 levels of voltage. The values are based on ground as being the bottom i.e. no voltage coming in and up to five (5)* volts which is the maximum voltage for the microcontroller. You can also limit the top of the voltage range to a value less then 5 volts however for the following examples that will not be necessary. When the analog input is read in, it is actually using a scale much like a balance scale to determine the value. Imagine you have a basement that is flooding and there is a staircase coming up to the first floor. One thing you might want to know is how deep the water is and how fast is it rising? For the depth of the water it would be very easy if you happened to know how far apart each step on the staircase is. In this case, you could simply count how many steps were covered in water and do some simple arithmetic to determine the waters depth. Oddly enough the Arduino® is doing something very similar. It has a built in scale that compares its internal value with the value coming in on the analog pin. Each cycle, the device will increase the voltage and compare it to the incoming voltage. It knows how many cycles it has been since it started the comparison so once they are equal, the micro-controller knows the value of the external voltage. The micro-controller has 10 bits of resolution which means that it can count all the way from zero (0) to 1023 to find a value matching that which is being read at the analog input pin. Using the above analogy, it is like the micro-controller has a staircase with 1024 steps counting the floor as step number one (1). In the case, step number one is equal to zero (0) volts and step number 1024 is equal to five (5) volts. This corresponds to a voltage of 0 volts being equivalent to a digital value of 0 and a voltage of 5 volts being equivalent to a digital value of 1023. As you can see, there is a fair amount of precision when using the 10 bit analog to digital converter. The downside to this type of converter is that it takes time to count from zero (0) up to the value that represents the incoming analog voltage. Just like the rate that the water rises in the basement, the comparison of each voltage value to the next highest value takes time as the micro-controller determines the equivalent voltage to the input voltage. The worst case of course is for 5 volts which is at the top of the range resulting in the microcontroller having to count 1024 times to get to a comparative value. In terms of computing time, this can be quite slow and could be as much as 260 microseconds for the given device. In human time, this

is about 1/4 of a millisecond which in turn is 1/1000's (0.001) of one second. When looked at in this way, it is plenty fast enough for what we want to do. Speaking of what we want to do, the first thing is to determine what the temperature in the room is. * For the Arduino UNO and MEGA, the maximum voltage that can be read in is 5 volts however on some systems, the maximum may be lower such as 3.3 volts or even 1.8 volts. TEMPERATURE READING The first thing we will look at is how to read in a temperature value and then be able to react to it based on its value and/or delta from it's initial value. Luckily there are specific devices available that will increase in voltage as the surrounding temperature rises. Knowing this, we can connect one of these devices to our Arduino® and read in the value to determine the current temperature. REQUIRED COMPONENTS

Arduino® Board 4.7K Ohm Resistor LM335 temperature sensor[ 7 ] or similar such as the NTE7225[ 8 ] TOOLS

breadboard jumper wires voltage meter SCHEMATIC For this circuit, you will need to run 5 volts from the Arduino® to the first lead of the 4.7K Ohm resistor. The second lead of the 4.7K Ohm resistor is connected to pin two (2) of the LM335. The third pin of the LM335 is connected to ground from the Arduino®. The final wire is connected from pin two (2) of the LM335 back to analog input zero (0) of the Arduino® in order to read the voltage drop across the LM335 as it pertains to the current temperature.

Image created using: Fritzing SOFTWARE The following is the code that is necessary to read in the analog voltage across the LM335 and display it to the serial port for review. Once we have our circuit working and the temperature values available, we will be able to further enhance the software and hardware to perform some useful functions. For this sketch, I am using the well known formulas to convert from Celsius to Fahrenheit and from Fahrenheit to Celsius. These formulas are as follows:

Celsius = ((Fahrenheit - 32) x 5) / 9 Fahrenheit = ((Celsius x 9) / 5) + 32

/* * The temperature pin is where the input from the LM335 will be read from. * The base voltage is what the device (LM335) will measure at 0°C * The voltage reference is the max voltage that can be read in on the * analog input pin * The max sample count is the highest value that could be read in on the * analog input pin which will equal the voltage reference * The c to f conversion is the well known formula of multiplying the * C temp by 9 then dividing by 5 * The f to c conversion is the well known formula of multiplying the * F temp by 5 then dividing by 9 * The c to f delta is the difference between C and F at the freezing temperature */ const int temperaturePin = 0; const float baseVoltage = 2.730f; /* 2.730V at 0°C */ const float voltageReference = 5.0f; const float maxSampleCount = 1023.0f; const float CtoFConversion = 9.0f / 5.0f; const float FtoCConversion = 5.0f / 9.0f;

const float CtoFDelta = 32.0f; /* * Method declarations */ float getTemperature(float temperatureVoltage); float convertTemperatureToF(float temperatureInCelsius); float convertTemperatureToC(float temperatureInFahrenheit); /* * getTemperature - determines the temperature in celsius based on the * incoming voltage value assuming 2.73v equals 0°C * * Params - temperatureVoltage - the incoming voltage representing the temperature * * Returns - float - the actual temperature in celsius based on the incoming voltage */ float getTemperature(float temperatureVoltage) { float currentTemperature = 0.0f; // From the datasheet, every 10 mv is equal to 1 °K - 0°C is 273°K equal to 2.73v // Temperature is scaled by 100 to bring it into the celsius scale from kelvin currentTemperature = (temperatureVoltage - baseVoltage) * 100; return currentTemperature; } /* * convertTemperatureToF - converts the temperature from Celsius to Fahrenheit * * Params - temperatureInCelsius - the incoming temperature in Celsius * * Returns - float - the temperature expressed in Fahrenheit */ float convertTemperatureToF(float temperatureInCelsius) { float convertedTemperature = 0.0f; convertedTemperature = (temperatureInCelsius * CtoFConversion) + CtoFDelta; return convertedTemperature; } /* * convertTemperatureToC - converts the temperature from Fahrenheit to Celsius

* * Params - temperatureInCelsius - the incoming temperature in Fahrenheit * * Returns - float - the temperature expressed in Celsius */ float convertTemperatureToC(float temperatureInFahrenheit) { float convertedTemperature = 0.0f; convertedTemperature = (temperatureInFahrenheit - CtoFDelta) * FtoCConversion; return convertedTemperature; } /* * Setup - main configuration point of our sketch to configure * the Arduino for analog input * * Params - none * * Returns - nothing */ void setup() { analogReference(DEFAULT); Serial.begin(9600); } /* * loop - main method which will be called each time the * Arduino goes through its main loop to read the * current temperature value * * Params - none * * Returns - nothing */ void loop() { int temperatureValue = 0; float currentTempVoltage = 0.0f; float currentTemperature = 0.0f; // Read in the temperature value which will be in a range from 0 to 1023 temperatureValue = analogRead(temperaturePin); // Convert our incoming voltage representation to a value we can utilize currentTempVoltage = ((float)temperatureValue * voltageReference) / maxSampleCount; // We now have the current temperature expressed in celsius currentTemperature = getTemperature(currentTempVoltage);

// Print out the current temperature in both C and F Serial.print("The current temperature is: "); Serial.print(currentTemperature); Serial.print("°C which is "); Serial.print(convertTemperatureToF(currentTemperature)); Serial.println("°F"); // Rest for a second and check again delay(1000); }

CONSTANTS

In the beginning of this sketch I have declared some constant values which I will use later on in my code. As I mentioned previously, it is better to declare these values as constant that way you get the benefit of meaningful names in your code which helps in the overall readability of your sketch along with the ability to change the value as needed later on without trying to find everywhere it is used in your code. It should be noted that you could also declare these values as defines which act as constants and are replaced in your code by the Pre-processor when you compile the code.

const const const const const const const

int temperaturePin = 0; float baseVoltage = 2.730f; /* 2.730V at 0°C */ float voltageReference = 5.0f; float maxSampleCount = 1023.0f; float CtoFConversion = 9.0f / 5.0f; float FtoCConversion = 5.0f / 9.0f; float CtoFDelta = 32.0f;

#define #define #define #define #define #define #define

temperaturePin baseVoltage voltageReference maxSampleCount CtoFConversion FtoCConversion CtoFDelta

0 2.730f /* 2.730V at 0°C */ 5.0f 1023.0f 9.0f / 5.0f 5.0f / 9.0f 32.0f

In the above code snippet, I have declared each variable as const indicating that the value will remain constant for the life of the application. Basically I am telling the compiler that I want to be able to read the value stored in the variable however I never want to change it during the lifetime of my application by writing to it. Given the type of values I am using, there is no reason to change them while the application is being executed. My main goal with these variables is to have human readable names assigned to the values so that my application is easier to understand. As shown in the second section, I could have also declared these constants using the #define pre-processor directive which would also make my code readable and maintainable. In doing so, however, I lose the type information of each of the values which may or may not be of interest to you the developer. On the flip side, the use of #define could reduce the amount of RAM that is used for your sketch.

Another thing you may have noticed is that for every variable of type float, I added an f to the end of the declaration. This is called a suffix which I tend to use on any value I assign to a float. By default, the compiler will consider a decimal value of type double and therefore convert the value from double to float when it builds the application from the sketch. For the Arduino® this isn't really a problem given float's and double's are the same thing. On systems where the double is indeed different, the compiler may generate a compilation error as it will appear that you are trying to assign a double value to a float variable. That would be the best case as you will at least get a chance to look at it and determine if there is a mistake in your code or not. Some compilers may automatically convert the value to float for you resulting in the possibility of a loss of precision. METHOD DECLARATIONS

The next portion of my sketch consists of the method declarations or function prototypes as they would be named in the C language. These declarations inform the compiler of what methods will be available for use along with what types of variables are passed into the method when it is called along with the type of value that is returned if any. While technically you do not have to give the variables i.e. parameters names in the method declaration, it is good practice to do so in order for someone looking at the code to better understand the values being passed into the method. The only exception to this is the setup and loop methods which are declared for you outside of your sketch.

float getTemperature(float temperatureVoltage); float convertTemperatureToF(float temperatureInCelsius); float convertTemperatureToC(float temperatureInFahrenheit);

In this sketch, I have declared three methods that I may want to use. In the end I am only going to use two of them however I declared all three for completeness. Because I am going to be looking at values that change in the order of micro-volts, I will need a data type that can represent a decimal value such as a float. The first will convert the raw voltage reading into a temperature in celsius while the other two will convert temperatures between Fahrenheit and Celsius. USER METHODS

The methods are declared above now appear in the code and this is where I will define what each method actually does. My end goal is that this code has enough comments to make it clear as to what its intended behavior is.

/* * getTemperature - determines the temperature in celsius based on the * incoming voltage value assuming 2.73v equals 0°C * * Params - temperatureVoltage - the incoming voltage representing the

temperature * * Returns - float - the actual temperature in celsius based on the incoming voltage */ float getTemperature(float temperatureVoltage) { float currentTemperature = 0.0f; // From the datasheet, every 10 mv is equal to 1 °K - 0°C is 273°K equal to 2.73v // Temperature is scaled by 100 to bring it into the celsius scale from kelvin currentTemperature = (temperatureVoltage - baseVoltage) * 100; return currentTemperature; }

In the above code, I have a method, getTemperature which takes a temperature reading in volts and converts it to a temperature value in degrees celsius. I could just convert the value read in directly to Fahrenheit but not everyone is familiar with the Fahrenheit scale and Celsius is closer to the original value making debugging easier if the values look odd from the expected values. The raw temperature value is the value I will be reading from the analog input pin of the Arduino®. While I could keep it as a voltage reading, it will be easier to for me to relate to the value if it is in unit of measure that I am familiar with. In this case having it in a temperature unit is much easier then a raw voltage value.

/* * convertTemperatureToF - converts the temperature from Celsius to Fahrenheit * * Params - temperatureInCelsius - the incoming temperature in Celsius * * Returns - float - the temperature expressed in Fahrenheit */ float convertTemperatureToF(float temperatureInCelsius) { float convertedTemperature = 0.0f; convertedTemperature = (temperatureInCelsius * CtoFConversion) + CtoFDelta; return convertedTemperature; }

This method converts a temperature in Celsius to Fahrenheit.

/* * convertTemperatureToC - converts the temperature from Fahrenheit to Celsius * * Params - temperatureInCelsius - the incoming temperature in Fahrenheit * * Returns - float - the temperature expressed in Celsius */ float convertTemperatureToC(float temperatureInFahrenheit) { float convertedTemperature = 0.0f; convertedTemperature = (temperatureInFahrenheit - CtoFDelta) * FtoCConversion; return convertedTemperature; }

This method converts a temperature in Fahrenheit to Celsius. SETUP

Again, the setup method is where I will configure my Arduino® to operate as I need. In this case I will want to set the analog reference voltage to the default which in the case of the Arduino® UNO or the Arduino® MEGA, will be 5.0v. This informs the micro-controller I want to measure voltages in the range of 0v to 5.0v. For our purposes, this resolution will be fine.

void setup() { analogReference(DEFAULT); Serial.begin(9600); }

So what do I mean regarding resolution? Well the analog to digital converter in the Atmel® micro-controller has 10 bits of data for precision. For a 5.0v voltage range, this means that there will be 5.0v / 1024 or 0.0048828125 volts per step of the analog to digital converter. For each increment of the analog to digital converter, the voltage that it represents increases by 0.0048828125 volts. I have also configured my serial port here so I can write sample data out to my development system to verify that the data looks appropriate. LOOP

The loop method once again is where all of the real work is done. Here I am also using comments in the code to explain what everything is doing so the next person looking at this code can understand it without any assistance from me.

void loop() { int temperatureValue = 0; float currentTempVoltage = 0.0f; float currentTemperature = 0.0f; // Read in the temperature value which will be in a range from 0 to 1023 temperatureValue = analogRead(temperaturePin); // Convert our incoming voltage representation to a value we can utilize currentTempVoltage = ((float)temperatureValue * voltageReference) / maxSampleCount; // We now have the current temperature expressed in celsius currentTemperature = getTemperature(currentTempVoltage); // Print out the current temperature in both C and F Serial.print("The current temperature is: "); Serial.print(currentTemperature); Serial.print("°C which is "); Serial.print(convertTemperatureToF(currentTemperature)); Serial.println("°F"); // Rest for a second and check again delay(1000); }

The first thing that is done is to read in the current temperature value in volts as a digital representation. Once a value has been read, it can then be converted for display to the end user. In the above code, I first convert the temperature from a digital representation to an actual temperature. Given the LM335 is designed to be sensitive to 1°Kelvin which represents 10 milli-volts (mV) for each one degree Kelvin of change, I will convert from the digital representation to a Celsius temperature value. So how did I go from Kelvin to Celsius? Kelvin has the same scale as Celsius however it starts at absolute zero and continues up on the same scale. With absolute zero being -273.0°C, I simply use this value to convert from Kelvin to Celsius. Once I have done that, I can then use one of my methods I declared above to convert from the Celsius value to Fahrenheit. Once I have my values in the units of measure that I want, I can then print them to the serial port for verification on my development system. A sample of the output generated by this sketch is as follows:

The The The The

current current current current

temperature temperature temperature temperature

is: is: is: is:

26.61C 26.12C 26.12C 26.12C

which which which which

is is is is

79.90F 79.02F 79.02F 79.02F

The The The The The The

current current current current current current

temperature temperature temperature temperature temperature temperature

is: is: is: is: is: is:

28.08C 29.05C 29.54C 28.56C 28.08C 27.59C

which which which which which which

is is is is is is

82.54F 84.30F 85.17F 83.42F 82.54F 81.66F

As you can see in the above data, my temperature readings are in the expected range i.e. room temperature or close to it depending on the room etc. Also you should note that I placed my finger on the device during the output display which is why the temperature started to rise and then began to fall once I removed my finger from the device. I did this to ensure that the circuit and code were working as expected. The only thing I have not done is to calibrate my temperature device to ensure it is reading the correct value. This is done by connecting the ADJ pin of the temperature sensor to a potentiometer and than fine tuning the input to the current room temperature.

Image created using: Fritzing In the above circuit diagram you can see where I have placed a potentiometer attached to the ADJ input of the temperature sensor to allow me to calibrate it to the current room temperature. While not overly critical for what we are doing, having the ability to do this will be crucial when we want to use the same basic circuit to monitor temperatures with a suitable level of accuracy. For analog inputs, that covers it for the most part. There are more fine details that could be discussed such as over voltage prevention, static electricity, etc. however as an introduction, I think we have the basics covered. One thing you may have noticed is that under tools, I mentioned a voltage meter however did not mention when it should be used. The voltage meter is one of those tools that I have handy whenever something doesn't look right. For sanity sake, I can use the voltage meter to ensure that I have wired my LM335 correctly and it is measuring a voltage in the general range I expect. Typical voltage meters are actually multimeters which can measure in addition to voltage, current and resistance. One such feature on my multimeter is the continuity check where I can ensure that I have wired things properly by checking two points and audibly verifying that I have continuity between them. So while I may mention that a voltage meter or multimeter is one of the tools recommended, I may not indicate when I use it. Basically it is one of those tools that you should have for whatever circumstances come up.

Digital Input For digital inputs, the Arduino® has a number of pins available to accomplish this. When we say digital input, we are referring to the micro-controller being able to recognize an incoming signal as either HIGH or LOW. For the devices we are looking at, this would be equivalent to zero (0) volts representing a LOW value and five (5) volts representing a HIGH* value. The main thing to remember with digital inputs is that the value to trigger either a HIGH or LOW is not exact. There is a range of values that will cause the input to be HIGH or LOW. The datasheet indicates that a LOW signal is anything from -0.5v through 1.5v when the micro-controller is powered by 5v. The datasheet also indicates that a HIGH signal is anything from 3.0v through 5.5v when the micro-controller is powered by 5v. The main thing to note is that any voltage between 1.5v and 3.0v will be undefined and should be avoided to prevent unexpected application execution. Using our last example, I am now going to create a circuit and sketch that will only trigger when the temperature reaches a certain value such as 80°F which would be 26.67°C. * the Arduino® UNO runs on 5 volts hence the value when set to HIGH will be 5 volts. Other boards, such as the Arduino Due , run at a lower voltage such as 3.3v so the output voltage when set to HIGH will be equivalent to that lower voltage. TEMPERATURE COMPARISON First we will develop a circuit that will trigger when a certain temperature is reached. For this circuit, we will want it to trigger at 80°F which would be 26.67°C. Using what we know about the LM335, 0°C is equal to 2.73v and each 1°C increases the voltage by 10mV. If we divide the 26.67°C by 100 to get it into the 10mV range, we will see that we need a voltage increase from 0°C of 0.2667v. So our target voltage across the LM335 will be 2.9967v. Oddly enough this is almost enough voltage to trigger our digital input however due to the variations found in components, doing this would be problematic and result in flaky operation of our program and circuit. To overcome this, we will create a comparison circuit that will trigger the digital HIGH value in the required range which is above 3.0v. REQUIRED COMPONENTS

Arduino® Board 4.7K Ohm Resistor (2) 10K Trim potentiometers LM335 temperature sensor[ 7 ] or similar such as the NTE7225[ 8 ] LM311 voltage comparator[ 9 ] or similar such as the NTE943M[ 10 ] TOOLS

breadboard jumper wires voltage meter SCHEMATIC For this schematic we will be using the circuit from before however this time we will be using a voltage comparator to compare our temperature voltage with our target voltage. As noted above, our target is 2.9967v. First we will need to wire up a voltage dividing circuit. We will want to use a 4.7K Ohm resistor in series with a 10K potentiometer. This will allow us to adjust the voltage across the potentiometer to our target voltage. In order to fine tune the target voltage, we can do one of two things. First we can use the analog pin of our Arduino® to read in the voltage drop across our trim potentiometer. This will require a little coding but will work fine. The other option is to use a multimeter to monitor the voltage across the potentiometer and fine tune it using that reading. Either is fine depending on what tools you have available. This connection will be wired into the voltage comparator. For the second half of our circuit, will be connecting the LM335 temperature sensor into the other input of the voltage comparator which will trigger an input of HIGH when our target voltage is reached. It will drop back to LOW once the temperature cools down.

Image created using: Fritzing SOFTWARE The following is the code that is necessary to monitor the input signal to determine if our temperature reading is greater than our set point, and if so, take an appropriate action such as turning a fan on.

/* * This circuit is designed to toggle between a HIGH signal and a LOW * based on the current temperature in the room. The potentiometer * and the resistor are used to divide the 5v signal into two values

* giving us the comparison we need to trigger the comparator output. * 2.73v is 0°C so to get the comparator to trigger we will want * to ensure that there is 2.73v + X across the potentiometer where * X is the temperature we want to toggle on. * The temperaturePin (now called setPointPin) will again read in the * temperature however it will actually be the voltage across the * potentiometer so we can use that as our setpoint. * The temperatureTogglePin will be used to determine if we are above * or below our target temperature. */ const int setPointPin = 0; const int temperatureTogglePin = 7; const float baseVoltage = 2.730f; /* 2.730V at 0°C */ const float voltageReference = 5.0f; const float maxSampleCount = 1023.0f; const float CtoFConversion = 9.0f / 5.0f; const float FtoCConversion = 5.0f / 9.0f; const float CtoFDelta = 32.0f; /* * Method declarations */ float getTemperature(float temperatureVoltage); float convertTemperatureToF(float temperatureInCelsius); float convertTemperatureToC(float temperatureInFahrenheit); /* * getTemperature - determines the temperature in celsius based on the * incoming voltage value assuming 2.73v equals 0°C * * Params - temperatureVoltage - the incoming voltage representing the temperature * * Returns - float - the actual temperature in celsius based on the incoming voltage */ float getTemperature(float temperatureVoltage) { float currentTemperature = 0.0f; // From the datasheet, every 10 mv is equal to 1 °K - 0°C is 273°K equal to 2.73v // Temperature is scaled by 100 to bring it into the celsius scale from kelvin currentTemperature = (temperatureVoltage - baseVoltage) * 100; return currentTemperature; } /* * convertTemperatureToF - converts the temperature from Celsius to Fahrenheit * * Params - temperatureInCelsius - the incoming temperature in Celsius

* * Returns - float - the temperature expressed in Fahrenheit */ float convertTemperatureToF(float temperatureInCelsius) { float convertedTemperature = 0.0f; convertedTemperature = (temperatureInCelsius * CtoFConversion) + CtoFDelta; return convertedTemperature; } /* * convertTemperatureToC - converts the temperature from Fahrenheit to Celsius * * Params - temperatureInCelsius - the incoming temperature in Fahrenheit * * Returns - float - the temperature expressed in Celsius */ float convertTemperatureToC(float temperatureInFahrenheit) { float convertedTemperature = 0.0f; convertedTemperature = (temperatureInFahrenheit - CtoFDelta) * FtoCConversion; return convertedTemperature; } /* * Setup - main configuration point of our sketch to configure * the Arduino for analog input * * Params - none * * Returns - nothing */ void setup() { pinMode(temperatureTogglePin, INPUT); analogReference(DEFAULT); Serial.begin(9600); } /* * loop * * * * * Params

main method which will be called each time the Arduino goes through its main loop to read the setPoint temperature value and check our digital input pin to see if it is high or not. - none

* * Returns - nothing */ void loop() { int rawSetPointValue = 0; float setPointVoltage = 0.0f; float temperatureSetPoint = 0.0f; // Read in the temperature value which will be in a range from 0 to 1023 rawSetPointValue = analogRead(setPointPin); // Convert our incoming voltage representation to a value we can utilize setPointVoltage = ((float)rawSetPointValue * voltageReference) / maxSampleCount; // We now have the current temperature expressed in celsius temperatureSetPoint = getTemperature(setPointVoltage); // Print out the current temperature in both C and F Serial.print("The temperature set point is: "); Serial.print(temperatureSetPoint); Serial.print("C which is "); Serial.print(convertTemperatureToF(temperatureSetPoint)); Serial.println("F"); if (digitalRead(temperatureTogglePin) == HIGH) { Serial.println("We have reached our set point."); } else { Serial.println("Set point has not been reached."); } // Rest for a second and check again delay(1000); }

A significant amount of the code above is straight from the earlier example which will now be used to monitor the set point temperature and allow us to tweak the value as the program is executing. Once we have the set point established we can than vary the temperature that our LM335 is reading by holding our finger on it to warm it up or using a fan to cool it off. While I won't rehash everything that was done before, I will highlight the main differences that were made.

const int setPointPin = 0; const int temperatureTogglePin = 7;

I changed the the temperaturePin variable name to setPointPin which better describes what this constant variable is doing i.e. defining the pin on the Arduino® that we will read for the set point temperature. The second constant variable is temperatureTogglePin which is the pin on the

Arduino® which we will monitor to detect if the LM335 has reached the setPoint or not.

/* * Setup - main configuration point of our sketch to configure * the Arduino for analog input * * Params - none * * Returns - nothing */ void setup() { pinMode(temperatureTogglePin, INPUT); analogReference(DEFAULT); Serial.begin(9600); }

The next major change is in the setup method where I am now configuring the temperature toggle pin to be a digital input pin. The method, pinMode, instructs the Arduino® to configure this pin for digital input data. Remember that most pins on the Atmel® micro-controller, which is what the Arduino® utilizes, can be configured to perform a number of disparate functions. In order to use these functions, we must ensure the micro-controller is configured properly before we start running our code.

/* * loop - main method which will be called each time the * Arduino goes through its main loop to read the * setPoint temperature value and check our digital * input pin to see if it is high or not. * * Params - none * * Returns - nothing */ void loop() { int rawSetPointValue = 0; float setPointVoltage = 0.0f; float temperatureSetPoint = 0.0f; // Read in the temperature value which will be in a range from 0 to 1023 rawSetPointValue = analogRead(setPointPin); // Convert our incoming voltage representation to a value we can utilize setPointVoltage = ((float)rawSetPointValue * voltageReference) / maxSampleCount;

// We now have the current temperature expressed in celsius temperatureSetPoint = getTemperature(setPointVoltage); // Print out the current temperature in both C and F Serial.print("The temperature set point is: "); Serial.print(temperatureSetPoint); Serial.print("C which is "); Serial.print(convertTemperatureToF(temperatureSetPoint)); Serial.println("F"); if (digitalRead(temperatureTogglePin) == HIGH) { Serial.println("We have reached our set point."); } else { Serial.println("Set point has not been reached."); } // Rest for a second and check again delay(1000); }

In the main loop of our sketch, I have changed the name of a number of variables to better describe their function. I have also added in code to read the state of the temperature toggle pin to determine if we have reached our set point or not. This is done by calling the aptly named digitalRead method. This method takes one argument which is the value of the pin to be read. It will return either a HIGH or LOW value depending upon the voltage being read at the input pin of the device. For now I am simply displaying that I have either reached the set point or not. Later on we can take additional actions to do something a little more useful with this information. We will also look at another form of digital input when working with interrupts later on. Summary The Arduino® provides pins to read in both analog and digital data. The analog data can be any signal such as a temperature reading, photo-resistor, or any other such item that provides a range of values based on an environmental stimulus. Digital inputs can be used to determine button presses, on and off states, and other binary types of data. Together, both of these types of inputs can be used to monitor the world around us and allow us to react to events when they occur.

Output

what goes in must come out

Previously we looked at some of the inputs that the Arduino® can process. Now we will look at the other side which is outputs. So what kind of signals can the Arduino® output? Here is a summary of them. Analog * Digital Timer

A variable value based on how the output is configured within the microcontroller A signal that can be either HIGH or LOW A signal, HIGH or LOW, is generated when a timer reaches a certain point

* The Atmel® micro-controller that we are using on the Arduino® doesn't actually output a true analog signal but instead uses a feature called pulse width modulation (PWM) to create an analog output value. For a true analog output, we would need to wire up a digital to analog converter DAC to do the conversion for us. Also note that while the micro-controller that we are using doesn't have a DAC built into it, there are other micro-controllers that do have this functionality such as the micro-controller found on the Arduin® Due. Analog Output The Arduino® UNO supports analog outputs in the form of PWM. This is not a true analog output but more a percentage of a signal that remains HIGH for a portion of a period of time and LOW for the remainder. For the Atmel® chip found on the Arduino® UNO, there are two forms of PWM, Fast PWM and Phase correct PWM. In general, the fast PWM will look like a sawtooth where the output rises from the starting value, zero (0), up to the top value and then dropping abruptly back to zero (0). The phase correct PWM will rise up like the fast PWM however it will then count back down from the top value back to zero (0) opposed to the abrupt drop resulting in a better output waveform. This output is passed through a comparator circuit to make it more of a square wave on the output. For a lot of the devices we will encounter, this type of output will be fine however it is good to note that it isn't a true analog output such as that a DAC would bring. The Arduino® combines the analog outputs on the some of the same pins as the digital outputs which is why it is a very good idea to plan your designs accordingly up front to minimize potential issues later on in your design. The reason behind this, is that the Atmel® micro-controller packs a significant amount functionality into a small package. The resulting package only has so many physical pins to the outside world resulting in overlap between certain functions. For the most part, if you are using your micro-controller to do a specific task, this will not be an issue. When you start getting into more complicated designs then you may find that this overlapping of features becomes an obstacle that you will have to overcome one

way or another. So what good is this type of analog output? In general, this is a fine output which can be used to drive the brightness of LED's, motors, servos's and a number of other items. For LED's, the length of time that the output signal remains HIGH correlates to the brightness of the LED in some implementations. The same would hold true for basic motors where the length of time that the output signal is HIGH translates to the speed of that the motor runs at. For servo's, the length of time that the output signal remains HIGH may correlate to the direction that the servo turns. All of these are useful applications which we will look into in more detail as we proceed. PULSE WIDTH MODULATION - LED'S The first project will be to read in a temperature value and then turn on a tricolor LED to represent the temperature visually. To do this we will need to re-use our circuit from before with the LM335 to measure the current temperature and then will add to it a tricolor LED to represent that temperature value. For the tricolor LED that we are going to utilize, it doesn't come with any PWM support so we will use some of our Arduino® PWM outputs to accomplish this. There are some lengthy strips of LED's that can be found on the internet that have PWM support built into them. These can be interfaced through other means such as SPI which we will cover in the Communications chapter. REQUIRED COMPONENTS*

Arduino® Board (4) 4.7K Ohm Resistor (3) 470 Ohm Resistors (3) 2N2222(A) PNP Transistor LM335 temperature sensor[ 7 ] or similar such as the NTE7225[ 8 ] Tricolor LED * The part list includes those parts required for the temperature reading circuit along with the parts for the additional components used in this circuit. TOOLS

breadboard jumper wires voltage meter Canned air *

hair dryer or heat gun * * I added canned air and hair dryer to the tools portion of the above list as they can be used to quickly cool down or heat up the temperature sensor. They are not required, just convenient. SCHEMATIC In this circuit, you will need to connect the ground and +5v signals from the Arduino® over to the TRI-Color LED. Each of the LED connections will utilize a NPN transistor in line to control current flow through the LED. Each transistor consists of a base, collector, and emitter. The collector for each transistor will be connected to the LED pin for each individual color. The base of each transistor will be connected to a PWM output of the Arduino® which will be used to vary the intensity of the LED it is connected to. A 4.7K resistor will be used inline between the Arduino® outputs and the base of each transistor. Finally the emitter of each transistor will be connected to ground through a 470 Ohm resistor. For the temperature input side of this we will again utilize the circuit we created in the previous chapter, Temperature Reading , using an analog input to read the temperature.

Image created using: Fritzing SOFTWARE The following code incorporates the analog reading for the temperature sensor and adds in the code to drive the LED.

/* * * * * * *

The temperature pin is where the input from the LM335 will be read from. The redLedPin is the where the PWM output of the Arduino will connect to the circuit. The greenLedPin is the where the PWM output of the Arduino will connect

to * the circuit. * * The blueLedPin is the where the PWM output of the Arduino will connect to * the circuit. * * The base voltage is what the device (LM335) will measure at 0°C * The voltage reference is the max voltage that can be read in on the * analog input pin * The max sample count is the highest value that could be read in on the * analog input pin which will equal the voltage reference * */ const int temperaturePin = 0; const int redLedPin = 9; const int greenLedPin = 10; const int blueLedPin = 11; const float baseVoltage = 2.730f; /* 2.730V at 0°C */ const float voltageReference = 5.0f; const int maxSampleCount = 1023; /* * Method declarations */ /* No additional methods in this sketch */ /* * Setup - main configuration point of our sketch to configure * the Arduino for analog input and PWM output * * Params - none * * Returns - nothing */ void setup() { analogReference(DEFAULT); // Because we are using digital pins as analog outputs i.e. PWM // we do not need to configure them using pinMode. } /* * loop - main method which will be called each time the * Arduino goes through its main loop to read the * current temperature value and then turn on the * three LED's based on the value of said temperature * * Params - none * * Returns - nothing */ void loop()

{ int temperatureValue = 0; int red = 0; int green = 0; int blue = 0; float colorPercent = 0.0f; // Read in the temperature value which will be in a range from 0 to 1023 temperatureValue = analogRead(temperaturePin); // The closer we are to 0, the bluer it will be colorPercent = 100.0f - (((float)temperatureValue / (float)maxSampleCount) * 100.0f); blue = (int)(colorPercent * 255.0f); // The closer to 1023 i.e. max of device, the redder it will be colorPercent = ((float)temperatureValue / (float)maxSampleCount) * 100.0f; red = (int)(colorPercent * 255.0f); // And green will vary accordingly peeking @ midpoint if (temperatureValue > (maxSampleCount / 2)) { colorPercent = (float)(maxSampleCount - temperatureValue) * 100.0f; } else { colorPercent = ((float)temperatureValue / (float)(maxSampleCount / 2)) * 100.0f; } green = (int)(colorPercent * 255.0f); analogWrite(redLedPin, red); analogWrite(greenLedPin, green); analogWrite(blueLedPin, blue); // Rest for a second and check again delay(1000); }

CONSTANTS

In the beginning of this sketch I have declared some constant values which I will use later on in my code. As I mentioned previously, it is better to declare these values as constant that way you get the benefit of meaningful names in your code which helps in the overall readability of your sketch along with the ability to change the value as needed later on without trying to find everywhere it is used in your code. METHOD DECLARATIONS

For this sketch, we do not have any user specific methods to declare.

USER METHODS

Given we do not have any user method declarations, we also do not have any user method implementations to look at. SETUP

The setup method is where I will configure my Arduino® to operate as I need. In this case I will want to set the analog reference voltage to the default which in the case of the Arduino® UNO or the Arduino® MEGA, will be 5.0v. This informs the micro-controller I want to measure voltages in the range of 0v to 5.0v. For our purposes, this resolution will be fine.

void setup() { analogReference(DEFAULT); // Because we are using digital pins as analog outputs i.e. PWM // we do not need to configure them using pinMode. }

LOOP

The loop method once again is where all of the real work is done. It is here that I will read the temperature value periodically and update the color of the TriColored LED to match the temperature that was read in. As noted in the code above, we do not have to call pinMode for each of the analog output pins.

void loop() { int temperatureValue = 0; int red = 0; int green = 0; int blue = 0; float colorPercent = 0.0f; // Read in the temperature value which will be in a range from 0 to 1023 temperatureValue = analogRead(temperaturePin); // The closer we are to 0, the bluer it will be colorPercent = 100.0f - (((float)temperatureValue / (float)maxSampleCount) * 100.0f); blue = (int)(colorPercent * 255.0f); // The closer to 1023 i.e. max of device, the redder it will be

colorPercent = ((float)temperatureValue / (float)maxSampleCount) * 100.0f; red = (int)(colorPercent * 255.0f); // And green will vary accordingly peeking @ midpoint if (temperatureValue > (maxSampleCount / 2)) { colorPercent = (float)(maxSampleCount - temperatureValue) * 100.0f; } else { colorPercent = ((float)temperatureValue / (float)(maxSampleCount / 2)) * 100.0f; } green = (int)(colorPercent * 255.0f); analogWrite(redLedPin, red); analogWrite(greenLedPin, green); analogWrite(blueLedPin, blue); // Rest for a second and check again delay(1000); }

The first thing we do in the loop method is to declare some variables to be used during the operation of the loop. The first one is the temperature value which will be read from the analog to digital converter pin. Following that is our potential color values based on the read in temperature value. Finally we have the color percentage variable which is of type float to handle the percentage calculations for each color.

// Read in the temperature value which will be in a range from 0 to 1023 temperatureValue = analogRead(temperaturePin);

In the above code, we are reading in the current temperature value from the pin we have defined as temperaturePin. Once we know what the current temperature is, we can then calculate how much of the three colors we want to turn on.

// The closer we are to 0, the bluer it will be colorPercent = 100.0f - (((float)temperatureValue / (float)maxSampleCount) * 100.0f); blue = (int)(colorPercent * 255.0f); // The closer to 1023 i.e. max of device, the redder it will be colorPercent = ((float)temperatureValue / (float)maxSampleCount) * 100.0f; red = (int)(colorPercent * 255.0f); // And green will vary accordingly peeking @ midpoint if (temperatureValue > (maxSampleCount / 2)) {

colorPercent = (float)(maxSampleCount - temperatureValue) * 100.0f; } else { colorPercent = ((float)temperatureValue / (float)(maxSampleCount / 2)) * 100.0f; } green = (int)(colorPercent * 255.0f);

In the above code we determine what percentage of the color should be shown i.e. how intense the color should be. For this sketch, I have decided to make the color more blue as we get closer to a zero (0) reading and more red as we get closer to the maximum value of the analog to digital converter (ADC). For the green channel I am increasing the intensity as the value goes from 0 to the midpoint of the total range of the ADC. Once the midpoint of the total range has been hit, I will then decrease the percentage. Finally after I have calculated how intense each of the LED's should be, I will write out the values to each of the PWM pins that connect to the corresponding LED's.

analogWrite(redLedPin, red); analogWrite(greenLedPin, green); analogWrite(blueLedPin, blue);

This code is fairly straight forward in that each value for red, blue, and green is written out to the corresponding pins which results in turning on the transistor connected to the LED channel for that given color. The value we write out is the amount of time during the time period of the PWM signal that it will be HIGH with the remainder of the time with the signal being LOW. What I haven't mentioned so far is how long is this time overall? From the Arduino® documentation, we can see that the overall frequency of the PWM signal is approximately 490 Hz. The Hz acronym stands for hertz which is the frequency of the signal i.e. the number of times the signal varies from low to high for one complete second. Basically a single hertz is equal to one second. The overall time of the signal is one (1) second divided by the number of hertz. In our case this is 1 divided by 490, resulting in 0.00204081632653 seconds per cycle. In normalized terms, the cycle of the PWM signal is approximately 2 milliseconds. If you were to write a value of 127 out to the analog output pin, you would get a near perfect square wave out of the pin with it being HIGH half the time i.e. for 1 millisecond and then LOW for the remainder of the period i.e. the second millisecond. A value of 64 which is one quarter of the total value for the PWM signal would result in a HIGH signal for 500 microseconds followed by 1500 microseconds of a LOW signal. Once you have your circuit built and ready to test with the provided sketch, you can heat and/or cool the LM335 to see the TriColor LED change. Given the space between each pixel of the TriColor LED being as small as it is, when we look at the LED as a whole, we will see the combined color of each individual segment opposed to three distinct colors. This is also due in part to the opaque housing around the LED causing the colors to blend. The final part of the code is the delay method which is again being utilized to let things happen before checking the analog input for a new temperature reading. Later on, when looking at Interrupts , we will see how we can remove the delay

and be more reactive to outside stimuli opposed to continuously monitoring the values. PULSE WIDTH MODULATION - FANS Now that we have a handle on PWM, I will look at other ways to utilize it. For this circuit, I will use a potentiometer connected to an analog input which will control the speed of the fan connected to a PWM output. The goal is to adjust the speed of the fan based on the analog input read in from the potentiometer. For practical uses, I would most likely put a temperature sensor on the analog input in order to adjust the fan based on the current temperature. However for this example, I will simply utilize the potentiometer to better exercise the fan from off to full on. Arduino® Board 10K potentiometer 2N2222(A) Transistor 100 Ohm Resistor 1K Ohm Resistor 150 Ohm Resistor Fan, +12v, 200mAmp TOOLS

breadboard jumper wires voltage meter SCHEMATIC The fan connects to the board using a four (4) pin header. A three (3) pin fan can also be used however it will not have the ability to be controlled via a PWM output pin unless you hook this value up to a transistor such as Q1 in the diagram below. The voltage for the fan should be applied to the resistor on the top of the board to flow into the transistor. This assumes of course that the fan you are using is designed to run off of +12v. If you are using the four (4) pin fan type then you will want to change the yellow wire from the transistor to connect to the +12v voltage input for the fan, bypassing the transistor all together. The main thing to note is to not connect the fan power to the Arduino® as it may overload the Arduino® circuit board and damage it. You will also need to consider the current draw, Ic, to ensure it doesn't overload the transistor. For the transistor specified, the maximum current, Ic, is 1.0 Amps.

Image created using: Fritzing SOFTWARE The following code is used to read in the voltage across the potentiometer and in turn write out to the PWM pin in order to control the speed of the fan.

/* * FanPWM - the purpose of this sketch is to read in a value from a potentiometer and then * use that value to determine how fast the fan should be spinning. * * The fan control pin is where the input from the potentiometer will be read from. * * fanTachPin is connected to read in the current speed of the fan * * The fan speed pin is the where the PWM output of the Arduino will connect to * the interface circuit in order to drive the fan. * * The voltage reference is the max voltage that can be read in on the * analog input pin * * The max sample count is the highest value that could be read in on the * analog input pin which will equal the voltage reference */ const const const const const

int fanControlPin = 0; int fanTachPin = 7; int fanSpeedPin = 9; float voltageReference = 5.0f; int maxSampleCount = 1023;

/* * Method declarations */

/* No additional methods in this sketch */ /* * Setup - main configuration point of our sketch to configure * the Arduino for analog input and PWM output * also set up the tach pin for input along with * enabling the serial port to monitor fan speed * * Params - none * * Returns - nothing */ void setup() { analogReference(DEFAULT); pinMode(fanTachPin, INPUT); Serial.begin(9600); } /* * loop - main method which will be called each time the * Arduino goes through its main loop to read the * current fan control value and then turn on the * fan based on the voltage across the potentiometer * * Params - none * * Returns - nothing */ void loop() { int fanControlValue = 0; int fanSpeed = 0; int fanTach = 0; // Read in the temperature value which will be in a range from 0 to 1023 fanControlValue = analogRead(fanControlPin); // Our range for output for PWM is 0 to 255 so we will divide by // 4 in order to get the read in value into the proper range // the output pin expects. fanSpeed = fanControlValue / 4; analogWrite(fanSpeedPin, fanSpeed); // Read in the fan tachometer value which will be a high signal // for a period X fanTach = pulseIn(fanTachPin, HIGH); char buffer[256]; int rpm = 1000000 / fanTach; // 1 Million as the value is in microseconds

// Target and tach shouldn't be expected to be the same sprintf(buffer, "Target: %d, Tach: %d", fanSpeed, rpm); Serial.println(buffer); // Rest for a second and check again delay(1000); }

CONSTANTS

In the beginning of this sketch I have declared some constant values which I will use later on in my code. As I mentioned previously, it is better to declare these values as constant that way you get the benefit of meaningful names in your code which helps in the overall readability of your sketch along with the ability to change the value as needed later on without trying to find everywhere it is used in your code. The constants in this sketch define which pins are being used as analog and digital input, and which pin is being used for analog out i.e. PWM output. METHOD DECLARATIONS

There are not any additional methods declared for this sketch. SETUP

The setup method is where I will configure my Arduino® to operate as I need. For this sketch we will ensure that our analog reference voltage is five (5) volts.

void setup() { analogReference(DEFAULT); pinMode(fanTachPin, INPUT); Serial.begin(9600); }

In addition to the analog reference we will also set the fanTachPin as a digital input which is much like we have done before however there will be a slight twist in how we use it. Finally the serial port is started and set for 9600 baud. This will allow us to observe the speed of the fan that is being read. LOOP

As before, the loop method is where all of the work is done. In this case we will be reading in a value from a potentiometer and writing out a value to the fan which represents how fast the fan should be spinning. In addition we will be reading in the pulse width of the fan tachometer pin and presenting that as a speed to the serial port interface.

void loop() { int fanControlValue = 0; int fanSpeed = 0; int fanTach = 0; // Read in the temperature value which will be in a range from 0 to 1023 fanControlValue = analogRead(fanControlPin); // Our range for output for PWM is 0 to 255 so we will divide by // 4 in order to get the read in value into the proper range // the output pin expects. fanSpeed = fanControlValue / 4; analogWrite(fanSpeedPin, fanSpeed); // Read in the fan tachometer value which will be a high signal // for a period X fanTach = pulseIn(fanTachPin, HIGH); char buffer[256]; int rpm = 1000000 / fanTach; // 1 Million as the value is in microseconds // Target and tach shouldn't be expected to be the same sprintf(buffer, "Target: %d, Tach: %d", fanSpeed, rpm); Serial.println(buffer); // Rest for a second and check again delay(1000); }

The voltage is read in from the fan control pin and converted to a value that is proper for our PWM output. The input range is from 0 to 1023 while the output can only be from 0 to 255. Because of this, I will scale the incoming value by dividing the value read by four (4). Once we have converted our value, we will then write that speed out to set the fan for that speed. Note that the actual value written will not be an actual speed but a value from 0 to 255. The fan most likely will turn off well before we reach the lowest value i.e. 0 and will turn on when not zero (0). The reason for this is that it takes a bit of voltage in order to actually switch on the transistor and depending on what components that you use along with which fan and what voltage you select. Once the value has been written to the fan speed pin, I will read in the value from the tachometer in order to determine the speed that the fan is rotating.

// Read in the fan tachometer value which will be a high signal // for a period X fanTach = pulseIn(fanTachPin, HIGH);

For this sketch, I have included a new input method for digital pins. This is the pulseIn method which is designed to read in the length of either a HIGH or LOW signal. This method will return a value representing the amount of time a pulse was either HIGH or LOW. This value will represent the number of microseconds that the pulse endured. In the above example, I am measuring how long the pulse is HIGH in order to determine the rotational speed of the fan. The return value from the pulseIn method is in microseconds. So our rotations will be one divided by the number of microseconds. I was reading around 500 microseconds on my display which puts us at an RPM of around 2000 which is inline with what I would expect based on the documentation for the fan. Once we know what the rotational speed is of the fan, we will display the target speed and tachometer value to the user. Following this we will sleep for one (1) second before checking the potentiometer again and adjusting the fan speed accordingly. You might notice that the fan tach is not stable and can rise and fall quite significantly. We will rectify this in the chapter regarding Interrupts . This type of circuit would be similar to what your mother board in your computer might do in terms of controlling the CPU temperature. Basically the system will want to measure the current temperature of the CPU and increase the fan speed if it is getting above a certain point and decrease it when it drops back down to a certain threshold. If we were to take our circuit from the chapter on Inputs and add the fan interface to that circuit, we could turn the fan on whenever the temperature reached our set point. PULSE WIDTH MODULATION - SERVOS The last area we will look at is a servo which we can control to perform some type of work. Servos are used for controlling robotic arms and can also be used for steering remote controlled cars among other things. Arduino® Board 4.7K Ohm Resistor LED +5v DC power supply 5v servo such as this LS-3006 TOOLS

breadboard

jumper wires voltage meter SCHEMATIC

Image created using: Fritzing The above drawing shows the servo attached to the Arduino® with the control signal connected to pin 11 which is a PWM output pin. The main thing to note is that I am connecting the servo to the Arduino® Vin pin. It is very important to connect the servo to this pin and NOT the +5v pin. This also requires that you connect a +5v DC power supply to the Arduino®. The reason for this is that the servo will draw more current then is safe for the Arduino® pins to supply. By connecting it to the Vin pin, you will be using the DC power supply to provide current to the servo and protecting your Arduino® from potential harm. This also means that you shouldn't use just the USB port as your power supply as it will supply around 500 milliamps maximum which will probably not be enough for both the servo and the Arduino® board. SOFTWARE The following code is used to control the servo. The main goal is to be able to turn the servo both forward and backward. Another thing to note is that servo's come in a variety of formats. Some will turn full circle while others may only turn a certain amount. The one I have, the LS-3006, is able to turn a full 360 degrees.

/* * ServoPWM - the purpose of this sketch is to control a servo in order to do some work. * the led is used to indicate which direction the servo is going with it being * off when the servo is going forward and on when it is going backward. *

* The ledPin is used to drive the LED to turn it on or off. * * The servoPin is used to drive the servo motor. * */ const int ledPin = 8; const int servoPin = 11;

/* * The frequency of the arduino PWM signal is around 490 Hz * This translates into 2 milliseconds per cycle. */ const int pwmFrequency = 490; typedef enum { FORWARD = 0, BACKWARD = 1 } eDirection; eDirection servoDirection = FORWARD;

/* * Motor speeds for the LS-3006 * which are defined in microseconds. */ typedef enum { FORWARD_SPEED = 128, // (1000 microseconds) which is equal to half of our cycle BACKWARD_SPEED = 250, // (2000) microseconds which is basically the full cycle NEUTRAL_SPEED = 192, // (1500 microseconds) which is equal to three quarters of the cycle } eMotorSpeed; eMotorSpeed motorSpeed = FORWARD_SPEED; /* * Method declarations */ /* No additional methods in this sketch */ /* * Setup - main configuration point of our sketch to configure * the Arduino for PWM output * * Params - none * * Returns - nothing */

void setup() { pinMode(ledPin, OUTPUT); } /* * loop - main method which will be called each time the * Arduino goes through its main loop to change the * direction of the servo * * Params - none * * Returns - nothing */

void loop() { // Set our LED to the proper state based on the direction if (servoDirection == FORWARD) { motorSpeed = FORWARD_SPEED; digitalWrite(ledPin, LOW); servoDirection = BACKWARD; // set it for next time } else { motorSpeed = BACKWARD_SPEED; digitalWrite(ledPin, HIGH); servoDirection = FORWARD; // set it for next time } analogWrite(servoPin, motorSpeed); // Rest for a second and then change directions delay(1000); }

CONSTANTS

Constants. What would we do without them? So I have again declared a few as follows:

const int ledPin = 8; const int servoPin = 11;

This just defines the pin which the LED is to be connected to along with the pin which will drive the signal (PWM) to the servo.

const int pwmFrequency = 490; typedef enum { FORWARD = 0, BACKWARD = 1 } eDirection; eDirection servoDirection = FORWARD;

In this section I have declared what the frequency of the PWM signal will be i.e. how often it will cycle from on to off. I have also defined an enumeration to indicate when the servo is moving forward or backward i.e. clockwise or counter-clockwise.

typedef enum { FORWARD_SPEED = 128, // (1000 microseconds) which is equal to half of our cycle BACKWARD_SPEED = 250, // (2000) microseconds which is basically the full cycle NEUTRAL_SPEED = 192, // (1500 microseconds) which is equal to three quarters of the cycle } eMotorSpeed; eMotorSpeed motorSpeed = FORWARD_SPEED;

The above definitions are the most important in this sketch as they define the values to be sent to the PWM control pin to represent the signals to control the direction of the servo. METHOD DECLARATIONS

There are not any additional methods declared for this sketch. SETUP

The setup method is where I will configure my Arduino® to operate as I need. For this sketch the only pin to configure is the pin connected to the onboard LED which will be declared as an output.

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

This code will set the ledPin to act as an output signal. This will allow us to control the LED to indicate the direction of the servo. LOOP

As before, the loop method is where all of the work is done. For this sketch we are toggling the direction of the servo every second from forward to backward. While this code isn't really useful, the idea is to show how it could be used. The applications for this type of circuit are endless however it would be a really good fit for a robotic arm or perhaps a conveyor belt where you need a little more torque and not necessarily the speed.

void loop() { // Set our LED to the proper state based on the direction if (servoDirection == FORWARD) { motorSpeed = FORWARD_SPEED; digitalWrite(ledPin, LOW); servoDirection = BACKWARD; // set it for next time } else { motorSpeed = BACKWARD_SPEED; digitalWrite(ledPin, HIGH); servoDirection = FORWARD; // set it for next time } analogWrite(servoPin, motorSpeed); // Rest for a second and then change directions delay(1000); }

For this circuit, we are just moving the servo forward and backward using the PWM signals from the Arduino®. The LED pin is used to indicate which direction the servo should be moving in i.e. forward or backward. This is useful to ensure that the device is operating as expected as it is sometimes difficult to see which direction the servo is going depending on its speed. To make this really useful, I would use an input to trigger an action such as a proximity sensor where I would stop the servo from moving when the proximity sensor was triggered. Digital Output The Arduino® supports a number of digital outputs that are capable of producing either a

HIGH or LOW signals representing five (5) volts and zero (0) volts accordingly. So where are digital output's used? In one case you could use them as an electronic switch to activate an external circuit such as an LED to indicate something is currently active such as a power on LED. Of course it could also be used for the reverse where the LED comes on when the power to the main board is lost or the like. When a number of the digital outputs are used in conjunction with each other, they can be used to interface with external devices such as LCD's and other wide data path devices. POWER ON LED For our first look at digital outputs, we will create a simple power on circuit that will light up a LED when the system has reached an operating state. I am going to utilize a tricolor LED in order to have state information. The LED will be red when the board is powered down, yellow when the board is booting up, and finally green when the board is functional. Arduino® Board (3) 470 Ohm Resistor (2) 4.7K Ohm Resistor (2) 2N2222(A) Transistor tricolor LED 3.0 v battery TOOLS

breadboard jumper wires voltage meter SCHEMATIC In this circuit, I am going to utilize an external battery that will turn on the RED channel of the tricolor LED when there isn't any power to the Arduino®. Once the board starts powering up, we will turn the LED to YELLOW, and then finally we will turn it to GREEN once we enter our LOOP method. The main thing to note is that we need to connect the battery ground terminal to the ground pin on our Arduino® in order to establish a common ground amongst our components. For this circuit, the lack of signal coming from the Arduino® digital output pin will result in the LED being lit up RED. I am going to utilize the two transistors to act like switches in order to prevent the GREEN and BLUE channels from lighting up when the Arduino® isn't powered on. Once we have power, the transistors, will allow the Arduino® to apply voltage to the base of each transistor turning the switch on to allow

the flow of electricity through the LED.

Image created using: Fritzing SOFTWARE The following code is used to manage the power indicator circuit to give a visual representation of the state of the circuit.

/* * Power Indicator - the purpose of this script/circuit is to visually display * the health of the system via a tricolor RGB LED. * * The redPin will be used to control the red portion of the LED. * Note: this pin will act different then the other two as * we will be turning this pin high to turn off the LED. * To make the code cleaner, this change is only * handled in the LightLed routine. * The greenPin will be used to control the green portion of the LED. * The bluePin will be used to control the blue portion of the LED. * * For this circuit, there will not be any additional inputs or outputs as this circuit is * intended as the basis for a project opposed to a complete project. */ /* * Enumeration of values to turn on/off the 3 channels of the tricolor LED */ typedef enum

{ RED_ON = 0x1, GREEN_ON = 0x2, BLUE_ON = 0x4 } eLedSettings; /* * Constants used in this sketch */ const int redPin = 9; const int greenPin = 10; const int bluePin = 11; const int const int const int yellow const int

POWER_OFF = RED_ON; POWER_ON = GREEN_ON; SETUP = GREEN_ON | RED_ON;

// Turn both red and green on to get

BLANK = 0; // no lED's lit

/* * Method declarations */

void LightLed(int colors); /* * LightLed * * This method checks the passed in value to determine which parts of the LED should be on and * which ones shouldn't be. Only the lower 3 bits are used to indicate the colors starting with * red as the LSB (least significant bit) and blue the MSB (most significant bit) * * Params - colors - a set of bits (bit mask) indicating which LED colors should be on and * which ones should not be. * * Returns - none */ void LightLed(int colors) { /* * Red acts differently where a HIGH output turns off the LED and a LOW output * turns it on. */ if (colors & RED_ON) { digitalWrite(redPin, LOW); } else

{ digitalWrite(redPin, HIGH); } if (colors & GREEN_ON) { digitalWrite(greenPin, HIGH); } else { digitalWrite(greenPin, LOW); } if (colors & BLUE_ON) { digitalWrite(bluePin, HIGH); } else { digitalWrite(bluePin, LOW); } } /* * Setup - main configuration point of our sketch to configure * the Arduino for writing digital values out on the defined pins * * Params - none * * Returns - nothing */

void setup() { pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); LightLed(SETUP); delay(1000); } /* * loop - main method which will be called each time the * Arduino goes through its main loop to turn the * LED on to green and then off blinking every * half second * * Params - none * * Returns - nothing */ void loop() { LightLed(POWER_ON);

delay(500); LightLed(BLANK); delay(500); }

ENUMERATIONS

In this sketch I have used a new feature of the C programming language which is called an enumeration i.e. enum. Much like a const integer or a definition such as #define VARIABLE, an enumeration allows the developer to assign certain values to names which makes our code easier to read. For this enumeration, I have defined three values indicating if each of the parts of the LED are on or off. I have assigned values for each of the enumerations such that each one is a specific bit out of the total bytes. Consider the following:

const int myValue = 42; // decimal representation (base 10) const int myValueHex = 0x2A; // hexadecimal representation (base 16) const int myValueBinary = 0b00101010; // binary representation (base 2)

Each of the constants are the same value however I have shown them in various formats. As you can see, the decimal version is probably the easiest to understand while the binary version is cumbersome but explicit in terms of what bits are on and off. Binary numbers are the heart of computer computation. I am using this feature of computer numbers to indicate if an LED should be on or off. What makes this nice is that I can use one variable to indicate which parts of the LED is on or off opposed to a separate value for each of the colors. You may have noticed that each value assigned in my enumeration has unique bits to represent it. The reason is that I can combine them to create additional colors such as red and green creating yellow. CONSTANTS

In the beginning of this sketch I have declared some constant values which I will use later on in my code. As I mentioned previously, it is better to declare these values as constant that way you get the benefit of meaningful names in your code which helps in the overall readability of your sketch along with the ability to change the value as needed later on without trying to find everywhere it is used in your code. The constants in this sketch define which pins are being used as digital outputs and assign values to certain states such as POWER_OFF, SETUP, and POWER_ON.

const int const int const int yellow const int

POWER_OFF = RED_ON; POWER_ON = GREEN_ON; SETUP = GREEN_ON | RED_ON; BLANK = 0; // no lED's lit

// Turn both red and green on to get

In the above four lines I have declared each of my color states as either a single or combination of values from my enumeration. In the enumeration I defined the values as:

typedef enum { RED_ON = 0x1, GREEN_ON = 0x2, BLUE_ON = 0x4 } eLedSettings;

In the above enumeration, I have RED_ON assigned the hexadecimal value, 0x01. This becomes 0b0001 in binary. For the second value, GREEN_ON, I have assigned it the hexadecimal value 0x02. This becomes 0b0010 in binary. You might notice that each one sets a different bit to on. Finally I have assigned the third value, BLUE_ON the hexadecimal value 0x04 which is 0b0100 in binary. If I want to create a yellow light, I can simply combine the value for GREEN_ON with the value for RED_ON which will equal the hexadecimal value 0x03 which will be the binary value 0b0011. When I pass this combined value to my function LightLed it will check each bit and turn on the proper portion of the LED corresponding to the set bits. METHOD DECLARATIONS

I declared one method in this sketch to light up the LED based on the color passed into the method which can be any combination of the three colors. This allows for up to eight (8) total colors with all colors on representing WHITE and all colors off indicating BLACK. While there isn't a true black, the lack of light i.e. the LED in the off state will represent black. USER METHODS

As mentioned above, I have created one method which will turn on each of the parts of the LED based on the incoming color value. The color value is called a bit mask which indicates which colors are on and which ones are off. The passed in color value is anded i.e. & with each of my constants, RED_ON, GREEN_ON, and BLUE_ON, to determine if that part of the LED should be on or off. The AND operation is a fundamental operation of binary numbers. This will allow us to test if a color should be lit or not. SETUP

The setup method is where I will configure my Arduino® to operate as I need. In this case I will enable each of the pins being used as digital outputs prior to using them which is done when I

call the method LightLed.

void setup() { pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); LightLed(SETUP); delay(1000); }

The main thing of note in this setup code is that I am delaying for one (1) second to allow the user to see the yellow LED before changing it to green. LOOP

Normally the loop method is where all of the work is done however since this is only a partial sketch i.e. I am showing how to use a digital output and not much more, it is fairly limited in its functionality.

void loop() { LightLed(POWER_ON); delay(500); LightLed(BLANK); delay(500); }

In this loop, the LED is turned on to show GREEN for 500 milliseconds and then turn off for another 500 milliseconds. Summary In this chapter we looked at various ways to output data from our Arduino® to control the world around us. This ranges from simple visual indicators to alert us when an issue arises, to automated control systems that can keep a device cool based on the surrounding temperature. We also looked at servo's which could be used for simple movement or could as part of a system to control a robotic arm or other sophisticated machinery. Overall, the number of possibilities are only limited by the imagination of the developer.

Program Interrupted

Stop! Stop! Wait

Interrupts are a very important aspect of program implementation and can be leveraged to provide clarity to an application. Interrupts allow you to focus on the task at hand while knowing that other operations are going to happen when they should. They can also be the source of all sorts of trouble and make it difficult to determine what is happening in your application. With proper planning and good coding practices though, you can keep them under control. I imagine your first question is what is an interrupt? An interrupt is a signal to your microcontroller indicating that an event has occurred. The reason this is beneficial is that it tells the microcontroller that there is something to be done opposed to having it waiting around checking periodically for something to do. Consider if you had a pet dog that you needed to take outside during the day to do their business. Your first option is to jump up and check every five (5) minutes or so to see if he/she needs to go out. While this would be a successful approach, it does waste a lot of time. The other option is to train your pet to notify you when it needs to go out. With this approach you can go about your business, and when needed, take your pet outside to do theirs. Your day remains productive while your pet gets relief when they need it. Sort of a win/win situation. An interrupt is the same thing overall. Instead of checking to see if something needs to be done, the interrupt notifies your micro-controller that something needs attention and then allows it to handle the distraction when needed in the way it should be done. Once the interrupt has been satisfied, the micro-controller can go back to doing whatever else it needs to do and not waste time continuously checking input pins. In one of the previous chapters we looked at various types of inputs such as analog signals and digital values. For one circuit we carefully set up the circuit to provide an input when the temperature reached a certain point. For our purposes, this worked well and we were able to determine when an action should be taken such as turning a fan on. While that did the trick and worked decent enough, we wasted a lot of time monitoring the input pin to see if it was time to take action. That was a lot of wasted effort and we can do better. Imagine having a tropical fish tank where the fish have to be kept at 78°F exactly. When the temperature gets too cold, the fish get chilled and die. When the temperature gets to warm, the oxygen in the water goes down and the fish die. So what do we do? We can use the circuit from before however we wasted a lot of effort checking to see if the temperature was above a certain level. Now we want to also check if it is below a certain level. And what happens if we check for it to be above 78°F and its actually below that temperature? We end up standing around the toilet in the bathroom saying good bye to our fish. Not good. What we need is a way to signal the micro-controller at anytime that there is an important action that needs to take place. This is where interrupts come in. In the example below, I will build a circuit that will trigger off of one temperature circuit. The second half of the circuit to detect the temperature dropping below a specific point can be added and wired to a second interrupt pin to be handled accordingly. The Arduino® and most importantly, the Atmel® micro-controller, is designed to allow

external events to signal the micro-controller that there is an important action to be taken. The Ardunio® provides a number of pins that can be used for external interrupts. The following table details which pins of the Arduino® can be used for external interrupts. Arduino® Board

Interrupt Interrupt Interrupt Interrupt Interrupt Interrupt 0 1 2 3 4 5

NANO

Pin 2

Pin 3

N/A

N/A

N/A

N/A

UNO

Pin 2

Pin 3

N/A

N/A

N/A

N/A

MEGA 2560

Pin 2

Pin 3

Pin 21

Pin 20

Pin 19

Pin 18

Due

*

*

*

*

*

*

Yun

Pin 3

Pin 2

Pin 0

Pin 1

Pin 7

N/A

* The Arduino® Due can have interrupts mapped to all available pins. When using this particular board, you will have to specify the pin number opposed to interrupt number; For these examples, I will only be using pins two (2) and three (3) as they are consistent across most of the various boards** I have been working with and will make the examples easier to follow. ** The Yun appears to have the interrupts, 0 and 1, swapped in terms of pins based on the documentation that I could find. Temperature Trigger The first thing we will do is to take our temperature circuit ( Temperature Trigger ) and put the input on pin 2 which will correlate with Interrupt 0. Next we will have to instruct the Arduino® to expect an interrupt on that pin. REQUIRED COMPONENTS Arduino® Board (2) 4.7K Ohm Resistor (2) 10K Trim potentiometers LM335 temperature sensor[ 7 ] or similar such as the NTE7225[ 8 ] LM311 voltage comparator[ 9 ] or similar such as the NTE943M[ 10 ] TOOLS breadboard

jumper wires voltage meter SCHEMATIC

Image created using: Fritzing SOFTWARE The following code incorporates the analog reading for the temperature set point that we wish to trigger off of. Prior to connecting the analog pin zero (0) to the LM311 comparator input, you may want to place it on pin two (2) of the LM335 to adjust the temperature sensor to the current room temperature.

/* * This circuit is designed to toggle between a HIGH signal and a LOW * based on the current temperature in the room. The potentiometer * and the resistor are used to divide the 5v signal into two values * giving us the comparison we need to trigger the comparator output. * 2.73v is 0°C so to get the comparator to trigger we will want * to ensure that there is 2.73v + X across the potentiometer where * X is the temperature we want to toggle on. * The temperaturePin (now called setPointPin) will again read in the * temperature however it will actually be the voltage across the * potentiometer so we can use that as our setpoint. * The temperatureTogglePin will be used to determine if we are above * or below our target temperature. */ const int setPointPin = 0; const int temperatureTogglePin = 2; // Use the interrupt attached to digital pin 2 const float baseVoltage = 2.730f; /* 2.730V at 0°C */ const float voltageReference = 5.0f; const float maxSampleCount = 1023.0f; const float CtoFConversion = 9.0f / 5.0f; const float FtoCConversion = 5.0f / 9.0f;

const float CtoFDelta = 32.0f; volatile boolean setPointReached = false; /* * Method declarations */ float getTemperature(float temperatureVoltage); float convertTemperatureToF(float temperatureInCelsius); float convertTemperatureToC(float temperatureInFahrenheit);

/* * Interrupt declarations */ void TemperatureTrigger();

/* * getTemperature - determines the temperature in celsius based on the * incoming voltage value assuming 2.73v equals 0°C * * Params - temperatureVoltage - the incoming voltage representing the temperature * * Returns - float - the actual temperature in celsius based on the incoming voltage */ float getTemperature(float temperatureVoltage) { float currentTemperature = 0.0f; // From the datasheet, every 10 mv is equal to 1 °K - 0°C is 273°K equal to 2.73v // Temperature is scaled by 100 to bring it into the celsius scale from kelvin currentTemperature = (temperatureVoltage - baseVoltage) * 100; return currentTemperature; }

/* * convertTemperatureToF - converts the tempertature from celsius to fahrenheit * * Params - temperatureInCelsius - the incoming temperature in celsius * * Returns - float - the temperature expressed in fahrenheit */ float convertTemperatureToF(float temperatureInCelsius)

{ float convertedTemperature = 0.0f; convertedTemperature = (temperatureInCelsius * CtoFConversion) + CtoFDelta; return convertedTemperature; }

/* * convertTemperatureToC - converts the tempertature from fahrenheit to celsius * * Params - temperatureInCelsius - the incoming temperature in fahrenheit * * Returns - float - the temperature expressed in celsius */ float convertTemperatureToC(float temperatureInFahrenheit) { float convertedTemperature = 0.0f; convertedTemperature = (temperatureInFahrenheit - CtoFDelta) * FtoCConversion; return convertedTemperature; }

/* * TemperatureTrigger - is triggered by an interrupt when * the temperature setpoint is reached. * * Params - none * * Returns - none */ void TemperatureTrigger() { setPointReached = true; }

/* * Setup - main configuration point of our sketch to configure * the Arduino for serial communications and interrupt usage. * * Note: We will be using the rising edge of the interrupt to trigger * * Params - none * * Returns - nothing */

void setup() { attachInterrupt(0, TemperatureTrigger, RISING); Serial.begin(9600); }

/* * loop - main method which will be called each time the * Arduino goes through its main loop to read the * setPoint temperature value abd check our digital * input pin to see if it is high or not. * * Params - none * * Returns - nothing */ void loop() { int rawSetPointValue = 0; float setPointVoltage = 0.0f; float temperatureSetPoint = 0.0f; // Read in the temperature value which will be in a range from 0 to 1023 rawSetPointValue = analogRead(setPointPin); // Convert our incoming voltage representation to a value we can utilize setPointVoltage = ((float)rawSetPointValue * voltageReference) / maxSampleCount; // We now have the current temperature expressed in celsius temperatureSetPoint = getTemperature(setPointVoltage); // Print out the current setpoint temperature in both C and F Serial.print("The temperature set point is: "); Serial.print(temperatureSetPoint); Serial.print("C which is "); Serial.print(convertTemperatureToF(temperatureSetPoint)); Serial.println("F"); if (setPointReached == true) { Serial.println("We have reached our set point."); setPointReached = false; } // Rest for a second and read the current setpoint temperature delay(1000); }

As I mentioned before, this code is not a lot different from the earlier code so I won't go into great detail regarding it, however I will point out the highlighted pieces.

The first difference is the use of a volatile boolean type. Volatile is a hint to the compiler that the variable it is used in conjunction with could change by means other than directly by the program.

volatile boolean setPointReached = false;

Because the interrupt routine is what causes the boolean variable setPointReached to change from false to true, we must give the compiler a hint that even though no one is calling the method TemperatureTrigger directly the boolean variable can still be changed via the interrupt routine which calls TemperatureTrigger. The next part of the code is the interrupt declaration. While it is just like the other method declarations, I kept it separate in order to point out that the interrupt routine doesn't return any values and does not take any parameters.

/* * Interrupt declarations */ void TemperatureTrigger();

Next is the definition of the TemperatureTrigger code where we set the setPointReached flag to true indicating to the main loop that the interrupt has been triggered.

/* * TemperatureTrigger - is triggered by an interrupt when * the temperature setpoint is reached. * * Params - none * * Returns - none */ void TemperatureTrigger() { setPointReached = true; }

Following this is our setup routine where we enable the interrupt and assign it to the appropriate pin. In this case I am using the Arduino® digital pin two (2). The call to the attachInterrupt routine enables the interrupt zero (0). Also note that we are triggering the interrupt on the rising edge. This indicates that when the signal coming in goes from low to high i.e. rises then the

interrupt triggers. It can also trigger off any of the following values: triggers when the input is LOW. LOW CHANGE triggers whenever a change occurs on the input pin. triggers when the input rises from low to high. RISING FALLING triggers when the input falls from high to low. triggers when the input is HIGH.** HIGH ** The HIGH value is only available on the Arduino® Due platform. attachInterrupt(0, TemperatureTrigger, RISING);

In this example, the main goal was to simply be able to detect the input trigger, the interrupt, indicating that our set point was reached. We could go a little further in cleaning up the code by not doing any work unless the interrupt was triggered. However for testing, I wanted to monitor the values being read to ensure things were working as expected. As mentioned above, I didn't build the second half of the circuit which would trigger when a temperature fell below a certain value. To do this, you could use the same circuit as shown however in this case you would calibrate it for the lower temperature and change the interrupt routine to trigger off of the FALLING edge. In general the input to the interrupt would be high and would go low once the temperature dropped below the target value. Rotary Encoder For this project we will be looking at rotary encoders to determine the amount of distance a user wants to traverse in relation to a signal. For instance, I may want to use the rotary encoder to brighten or dim an LED. I can use a potentiometer for this however a rotary encoder might do the job better given its behavior. In addition to this I store my current setting in the onboard EEPROM in order to return to that value at start up. This also prevents any potential issues with the brightness changing during movement and such while the device is turned off. This could happen with a potentiometer as it has a fixed length of travel i.e. it can only go so far in either direction and can be turned while the device is not powered on. A rotary encoder, however, is designed to spin continuously in either direction so it will not matter if it is turned while the device is powered off. The Arduino® will only act upon it when it is powered on. I will configure my Arduino® to trigger off of inputs from the rotary encoder to determine how bright the LED should be. REQUIRED COMPONENTS Arduino® Board

(4) 10K Ohm Resistors (2) 0.01uF capacitors ALPS STE1207 Rotary Encoder Red LED TOOLS breadboard jumper wires voltage meter SCHEMATIC

Image created using: Fritzing SOFTWARE For this code, we will configure our interrupt to trigger when one signal of the rotary encoder is active. When that happens we will read the value on the secondary pin to see which direction the encoder is being turned. The decoder is designed such that one signal will trigger and at the trigger time the other signal will be either high or low depending on the direction it is being turned. So the one input triggers our software to indicate movement while the other input gives us the direction. We will then have to determine how many signals we have received to determine the number of turns the user has made. Extra circuitry, resistors and capacitors, have been added to the rotary encoder to allow for better signal integrity. This information is provided by the manufacturer of the rotary encoder and can be found usually on their website. Typically, I will download the manufacturer's data sheet when a component is unfamiliar to me as they will usually have sample circuits that you can utilize when creating your own circuits.

/* * RotaryEncoder - the purpose of this sketch is to handle the input of * a rotary encoder to adjust the brightness of an LED. * * The rotary encoder output will be tied to one interupt and one normal input * line in order to detect movement and determine which direction. * * One output line will be connected to the LED and we will use PWM * to adjust the brightness based on the amount the rotary encoder is * turned. * * We will store the current brightness of the LED (0 - 255) so that we can * restore it after a reboot. */ const int ledOutput = 3; const int directionPin = 4; const long signature = 0xFACE; // some non-arbritary value

/* Module includes */ #include /* Structures used in this sketch */ typedef struct { long signature; int brightness; } EE_Structure; /* * Variables used by this sketch */ volatile EE_Structure StoredData; /* * Method declarations */ int ReadInteger(int offset); void WriteInteger(int offset, int value); long ReadLong(int offset); void WriteLong(int offset, long value); void HandleDecoderTrigger(); /* * ReadInteger - a helper function to read in an integer from the nonvolatile storage * * Params - offset - the offset into the EEPROM where data is stored

* * Returns - int - the value read from the specified location */ int ReadInteger(int offset) { int returnValue = 0; // Read in EEPROM for (int count = 0; count < sizeof(int); count++) { returnValue |= EEPROM.read(offset + count) > ((intSize - 1 - count) * 8); // We need to shift from left to right so upper first then on down EEPROM.write(offset + count, dataValue); } } /* * ReadLong - a helper function to read in a long from the nonvolatile storage * * Params - offset - the offset into the EEPROM where data is stored * * Returns - long - the value read from the specified location */ long ReadLong(int offset) { long value = 0; int storedValue = ReadInteger(offset);

value = (storedValue > 16); int lowerValue = value && 0xFFFF; WriteInteger(offset, upperValue); WriteInteger(offset + 2, lowerValue); } /* * HandleDecoderTrigger - called when the encoder is turned * * Params - none * * Returns - nothing */ void HandleDecoderTrigger(void) { byte directionData = 0; noInterrupts(); directionData = digitalRead(directionPin); if (directionData == HIGH) { // clockwise if (StoredData.brightness < 256) { StoredData.brightness++; } } else { // counter clockwise if (StoredData.brightness > 0)

{ StoredData.brightness--; } } interrupts(); } /* * Setup - main configuration point of our sketch to configure * the Arduino for reading signals from a rotary encoder * and setting the brightness of the LED based on the * amount the rotary encoder is turned and which direction. * * Params - none * * Returns - nothing */ void setup() { StoredData.signature = ReadLong(0); if (StoredData.signature == signature) { // Have we been here before? StoredData.brightness = ReadInteger((char *)&StoredData.brightness (char *)&StoredData.signature); } else { // Store signature and default brightness of 0 WriteLong(0, signature); WriteInteger((char *)&StoredData.brightness - (char *)&StoredData.signature, StoredData.brightness); } pinMode(directionPin, INPUT); attachInterrupt(0, HandleDecoderTrigger, FALLING); edge }

// trigger on falling

/* * loop - main method which will be called each time the * Arduino goes through its main loop to change the state * of the headlights and update the 7 segment displays * * Params - none * * Returns - nothing */ void loop() { static int lastBrightness = 0;

static int brightnessOffset = (char *)&StoredData.brightness - (char *)&StoredData.signature; if (lastBrightness != StoredData.brightness) { analogWrite(ledOutput, StoredData.brightness); WriteInteger(brightnessOffset, StoredData.brightness); } // Pause for 500 milliseconds i.e. 1/2 second. delay(500); }

For this sketch we have introduced a number of new items which I will go over as they appear within the sketch. The first item is the signature that I am using. While it is the same as other constant variables I have used in the past, I am defining this one in hexadecimal format i.e. base 16 format. Base 16 format allows the user to specify a number using 16 unique tokens which are typically the numbers zero (0) through nine (9) and the letters A through F. Because in hexadecimal we have this selection of letters to choose from, I opted to spell out a word that is easily recognizable. Also note that this is a long integer data variable which will be 4 bytes on this platform.

const long signature = 0xFACE; // some non-arbritary value

For this sketch, I chose the word FACE as it stands out. The likelihood of this particular letter order coming up randomly in memory is fairly low so I can be fairly certain that if I do find this sequence of letters in memory then my persistent storage i.e. the EEPROM is initialized. Speaking of EEPROM's, I should probably explain what they are and what the following line of code does.

#include

An EEPROM is a special device that can store data even when the power to the system is turned off. This is called non-volatile memory or sometimes persistent memory. One of the special things about this memory is that it can be written to at runtime and is built into the micro-controller that we are using i.e. the Atmel® device. This allows us to store data between user sessions and restore that data at runtime in order to pick back up where we left off. In this case we will be storing the last known brightness level of the LED in order to restore the LED to the same brightness level when we turn the circuit back on. This can be very handy depending on your application. If this was a television and the encoder was used to set the volume of the television, you would want it to come back on at the same volume level as when you turned it off. While you could just leave the television running all of the time, that would be very wasteful. In this case, we can store the current volume in persistent memory and then restore it when we power back up making the television more user friendly. So now that we have an idea of what an EEPROM can be used for, we should look at that

line of code that mentions it. In this sketch, I am including a secondary file, which the Arduino® calls a library. This allows me to utilize functionality that other's have provided in order to access various features of the Arduino® that might not normally be readily accessible. In traditional C and C++, common functionality is provided in the form of libraries which are described in header files. The .H is the common file extension indicating that this is a header file. The header file will typically describe the functionality that is made available by the particular library without actually showing how it is done. This can be very useful to extend the functionality of sketches and allow user's to share their code with others. In order to take advantage of libraries inside of the Arduino® platform, select the Sketch menu item and then the Import library option which will show a selection of libraries that you can use. Select the EEPROM library to include it with the current sketch that you have open. While I will not go into detail regarding each of the libraries, it is good to know that they are there and ready to be used when you need them. So now back to that #include statement. This statement is a special keyword of the C language that is used to add functionality to an application.

While you could retype everything in the file in order to make it available for use, this can be very error prone, tedious, and quite frankly a waste of time. The #include statement does this for you allowing you to build your sketches faster and more reliably. Note that the # character at the beginning of the include statement is required to indicate to the compiler that this is a special command. Once you have included the library that you need, in this case, the EEPROM library, you will be able to use the functionality that it provides in your sketch. So we included some additional functionality in our code. That is pretty cool and something we will want to remember in the future to save time when creating more complex sketches. It is also good to note that you, as an end user, can make your own libraries of routines that you use a lot to limit the amount of coding that you need to do. The temperature conversion functions that we used previously would be good candidates for a library as we will probably use them again from time to time and we probably will not want to have to retype that information in again. Now we come to the next new feature of the C programming language which is the structure. A structure allows the programmer to group a set of variables together into one container making it easier to pass around the data and to work with it. The following is our structure declaration which we will use in this sketch.

typedef struct { long signature;

int brightness; } EE_Structure;

This structure is fairly simple and consists of the signature block and a brightness value. I have called it EE_Structure as this will be the structure of the data we will store in the EEPROM. It is important to note that this is the name of the structure definition and not the actual variable name I will use in my sketch. I am combining the brightness and a signature in the data so that I can determine if I have a valid value stored in the EEPROM when I look at it during future runs of my sketch. When I first run my sketch, I cannot guarantee what values will be in the EEPROM. The signature helps me determine if my sketch has ever been executed before. The second part of this structure is the brightness of the LED which will range from zero (0) to two hundred fifty five (255). This could fit into one unsigned character i.e. a byte however I reserved extra space in case I need it later on. So where does a structure come in handy when programming? Well, now I can use this structure just like I would a regular variable type such as an integer. I am now able to do the following:

volatile EE_Structure StoredData;

This allows me to declare both the signature and the brightness at the same time. It also keeps them together as one unit which I can pass around to methods as needed. I didn't take full advantage of all of the benefits of a structure however it still comes in handy to keep my data contained within one type. If I had multiple encoders with multiple ID's, I could create one of these structures for each of the LED's. I could also associate the output pin for each LED with the brightness to keep each data set together. This can be very useful when your data needs increase and the complexity of your sketches increase. This is also a fundamental premise of object oriented design and C++ which is an object oriented programming language. Keeping your data and the methods that work with that data combined allows you to create building blocks to work with in your later sketches. The next few line of code are the function declarations and the various helper functions that I wrote to make my program easier to understand and to reduce the code size. I knew I would need to write data to the EEPROM and be able to restore it at a later time. With this in mind, I created a few functions, ReadInteger, WriteInteger, ReadLong, and WriteLong which will read and write an integer or long to a specific location within the EEPROM. The first one is the ReadInteger function which looks like this:

int ReadInteger(int offset) { int returnValue = 0; // Read in EEPROM for (int count = 0; count < sizeof(int); count++) { returnValue |= EEPROM.read(offset + count) 0) { StoredData.brightness--; } } interrupts(); }

This code is written with the assumption that a turn of the rotary encoder will trigger it indicating it is time to read in the value on the secondary pin which I am calling directionPin. The rotary encoder that I am using outputs 2-bit quadrature code which consists of two (2) inputs to the Arduino®. The first input is used to detect movement of the device. This is mapped to the interrupt pin so I don't have to keep checking to see if the pin has changed. The signal is considered on when the trigger input reaches a LOW value. If the encoder is being turned clockwise, the other input will be a HIGH signal. If the encoder is being turned counter-clockwise then it will be a LOW signal. This allows me to both know that the encoder is being turned and to detect the its direction of travel. Based on the direction that the encoder is being turned, I will either increment or decrement the brightness of the LED. In order to be safe and not have multiple overlapping calls to my interrupt routine, I disable interrupts as I enter my decoder handler and re-enable them when I exit. The key here is to make sure that during the time interrupts are disabled, I take whatever necessary action there is to take, quickly. Generally it is not wise to keep interrupts turned off for any length of time. For our setup this is probably a moot point however it is best practice to keep interrupt routines short and to the point. As an interesting side note, key presses on a keyboard are typically processed as interrupts. During this time when determining which key was pressed, you can imagine it would be good to keep the routine short and simple. The main goal is to determine what key was pressed and pass that on to the application to handle. Typing would be very frustrating if you had to wait between each key press to have the computer determine what should be done with the key value. Fan Tachometer For this project we will be utilizing an interrupt to determine the speed of a fan's rotation. There are a number of ways to detect the speed of the fan however for this project we will be using an infrared emitter and detector much like what would be found in a remote control device for a television or similar electronic device. In order to do this right, we will need to have the infrared emitter on one side of the fan and the detector on the other side such that the fan blades will cause the detector to change state as the blades block it from receiving the output of the transmitter. Because each fan blade will cause a pulse to occur on the detector, we will need to know how many fan blades are on the fan and use this number to determine how many interrupts we will be seeing for each revolution of the fan. For the fan, we can actually use anything that will break the connection between the emitter and the detector. A computer fan is convenient as a number of them can accept various voltages in order to change the speed at which it turns. However given the nature of the detection circuit, anything that breaks the line of site between the emitter and the detector will cause the interrupt to trigger. This can be useful in other applications such as motion detection or obstacle detection such as you might find with an automatic garage door opener.

REQUIRED COMPONENTS Arduino® Board Infrared Emitter such as the Model: 276-142 Infrared Detector such as the one included with the emitter linked above 100 Ohm resistor 15K Ohm resistor PC 5v fan TOOLS breadboard jumper wires voltage meter SCHEMATIC

Image created using: Fritzing SOFTWARE For this code, we will configure our interrupt to trigger when the infrared detector is activated by a fan blade moving out of the way of the infrared transmitter. In the above image, the fan would sit between the two infrared devices. In order to see the connections clearly, I opted to not show the fan itself on the breadboard. When the blade of the fan stops blocking the infrared emitter, the detector is charged which then reduces the voltage drop across the detector resulting in a transition on the interrupt line. As the voltage across the detector drops, the voltage into the Arduino® drops from a high signal to a low signal. When the next fan blade again blocks the detector the voltage into the

Arduino® rises triggering our interrupt which we will configure to trigger on a RISING edge.

/* * Infrared RPM - the purpose of this sketch is to configure an interrupt to trigger * when the infrared detector changes state. * * fanBlades is the number of blades that the fan being used has. Each fan blade triggers * the interrupt so to get an accurate reading we will need to divide the number * we obtain by the number of blades. * * interruptId is the identifier of which interrupt we are using. While the interrupt itself is attached to * pin two (2) of the Arduino, the interrupt identifier is actually zero (0). * * updateFrequency is how often we update the display with our calculated RPM's * * speedCount is the number of times that the interrupt has been triggered since we last looked at. * * lastCycle is the value of millis() at the last cycle so we can accurately determine the speed based on a * one second target */

const int fanBlades = 7; const int interruptId = 0; const int updateFrequency = 1000; volatile int speedCount = 0; // volatile as it will be changed by both the interrupt and the main loop unsigned long lastCycle = 0;

/* * Method declarations */

void infrared_trigger();

/*

* infrared_trigger - this method is short as it is handled during an interrupt * it's sole purpose is to update the speed counter * * * Params - none * * Returns - nothing */

void infrared_trigger() { speedCount++; }

/* * Setup - main configuration point of our sketch to configure * the Arduino for serial port reporting, setting our * default values, and enabling our interrupt. * * Params - none * * Returns - nothing */

void setup() { // Setup the serial port to report our fan RPM value Serial.begin(9600); lastCycle = millis(); interrupt on

// get a starting value prior to turning the

// We will use the rising edge which correlates to the fan blade blocking the infrared detector attachInterrupt(interruptId, infrared_trigger, RISING); }

/* * loop - main method which will be called each time the * Arduino goes through its main loop to check the * amount of time since it last looked an if our * update frequency has been exceeded, we will then * report the rotations per minute scaled by 60 * since we are reporting the value every second. *

* Params - none * * Returns - nothing */

void loop() { /* * Grab the current millis to see if our target delta has been reached */ unsigned long currentCycle = millis(); unsigned long delta = currentCycle - lastCycle; // If we have passed our update frequency, update and start again if (delta > updateFrequency) { //-------------------------------------------// Have my seconds (in milliseconds) // convert to RPM's i.e. rotations per minute // // I am using floats to be more accurate //-------------------------------------------float variableRate = ((float)delta / 1000.0f) * 60; unsigned long currentSpeed = (speedCount * (variableRate)) / fanBlades; // Reset our speed count and make our current cycle the last cycle lastCycle = currentCycle; speedCount = 0; //-------------------------------------------// Write out the current speed once we have reset our values above // this is because the interrupt will be still running while we // are taking care of calculating the RPM's //-------------------------------------------Serial.print("Current speed is: "); Serial.print(currentSpeed); Serial.print("\n"); } }

This sketch is fairly straight forward with the primary feature being the interrupt routine. This routine is where we keep track of the number of times the fan blades have triggered the interrupt pin. The bulk of the work is done in the main loop where we determine the number of times the interrupt has been triggered per second and convert the values into an RPM value. To do this, I get the current millisecond count by calling the function millis(). This returns an unsigned long value representing the number of milliseconds that have elapsed since the Arduino® was powered on. I do this so that I can compare it withe value I read the previous time through the loop function. Once I reach a delta i.e. time span greater than my update frequency, I will determine the speed that the fan is rotating at by multiplying the number of times that the fan blocked the infrared emitter. Because the fan has multiple

blades, I must factor that into my equation. While I tried to ensure that I only did this once per second, I can never be sure how often my loop function will be called. Given that, I assigned the variable, variableRate a value representing my actual time delta divided by 1000.0f which is how many milliseconds in one second. I then multiplied by sixty (60) in order to convert the value to minutes from seconds. Once done with that I could take the total times that the fan blocked the infrared emitter and multiple it by amount of time that passed along with dividing the value by the number of blades on the fan to get a final rotations per minute (RPM) value. In converting the value from floating point to unsigned long integer, I will be losing some precision however for the purpose of this sketch it should be fine. Summary In this chapter I looked at interrupts and how to be more reactive to external events opposed to polling. Polling is very time consuming and depending on what is going on, could result in missed events. Interrupts alleviate this by making your design able to react when an event occurs. The downside is that you must be careful that what you do in the interrupt doesn't conflict with what you are doing in your main application loop. It is also wise to ensure that the actual interrupt routine be fairly quick and not prevent the system from doing what it needs to do.

Communications

Talking to the outside world one protocol at a time

Communication can take a number of forms, from simple speech, to visual indications such as traffic lights, to complex protocols between sophisticated devices. In this chapter we are going to explore the protocols used to communicate between sophisticated devices. Our Arduino® will be one end of the communication channel while various devices and machines will be the other end communicating with the Arduino®. Serial Port (RS-232) In past chapters, we have looked at the initial communications between the Arduino® and a host computer using the COMM Port. This port has been used for a number of different applications, from keyboards to mice to a whole array of disparate devices. A typical RS-232 communication port will utilize the following signals: Signal Name

Purpose

DB-9 Pinout DB-25 Pinout

Rx

Receive data from the sending device

2

2

Tx

Transmit data to the sending device

3

3

Gnd

Common ground between the two devices

5

7

While the standard communications between the Arduino® and the host computer is a valid use case and important in order for us to debug our sketches, I want to explore other applications for which it can be used for. In this case, I happen to have a Bluetooth® shield that uses serial communications between the Arduino® and itself. Depending on which Arduino® module that you are using, you can use either a hardware serial port or a software serial port. The software serial port is designed to mimic the real hardware serial ports allowing you more flexibility as to which pins of your Arduino® are used. The downside is that it is an emulation and can be subject to anomalies and can be somewhat slower. Overall though, using a software serial port should be transparent to your sketch and the micro-controller for the most part. The shield I am using is from Seeed Studio and is flexible in that it can act as both a master device or a slave device. The other nice feature is that it can use any digital pins between zero (0) and seven (7) for its serial interface. In addition to this, it also has two Grove Ports to allow the system to interface using mainstream connectors to external components which can be useful for remote monitoring and the like. Care has to be taken when using the hardware communication port(s) in your own projects as the first serial port is also used for the USB communication port to your computer system. When you utilize the Serial class of functionality, the data sent and received travels over this hardware communication port to your host

system. It is also the communication port which is used to program your microcontroller. Arduino®

Serial 0

Serial 1

Serial 2

Serial 3

UNO R3

0 (RX), 1 (TX)

N/A

N/A

N/A

Leonardo

USB (CDC)

0 (RX), 1 (TX)

N/A

N/A

DUE YUN

0 (RX), 1 (TX) 19 (RX), 18 (TX) 17 (RX), 16 (TX) 15 (RX), 14 (TX) USB (CDC)

0 (RX), 1 (TX)

N/A

N/A

MEGA 2560 0 (RX), 1 (TX) 19 (RX), 18 (TX) 17 (RX), 16 (TX) 15 (RX), 14 (TX) There are a number of other Arduino® or compatible boards available not listed above however most of them are based off of one of the above listed boards and, in most cases, will have very similar features i.e. the number of serial ports and the pins they are found on. With that all out of the way, lets look at some code to communicate with the Bluetooth® shield. For this sketch, I will be communicating with the shield using pins six (6) and seven (7). The Bluetooth® shield will be acting as the master device and connecting to another device which will be the slave. For this example, I will be communicating with an OBDII Interface to look at diagnostic codes on my vehicle. REQUIRED COMPONENTS Arduino® Board Seeed Studio Bluetooth® shield[ 16 ] Generic OBDII Connector[ 17 ] TOOLS None. SOFTWARE The following code demonstrates communicating with a Bluetooth® shield and then using that device to communicate with a remote device to send and receive data.

/* * This sketch is designed to establish a connection over Bluetooth to transfer * data between a host system and a target microcontroller.

*/ #include #include #include #include SeeedStudio BT module

// // // //

sprintf storage of connection data communication between BT module and arduino arduino library for communicating with the

// Status pin for onboard led #define BT_CONNECTED_STATUS_PIN

13

// The BT shield can use two digital pins for the serial communications from D0 - D7 #define #define #define #define

BT_COMM_SPEED 38400 BT_PIN_CODE 1234 BT_RX_PIN 6 BT_TX_PIN 7

#define BT_CONNECTED_PIN 1 // analog pin // EEPROM SIGNATURE const long signature = 0xACED; // some non-arbritary value #define MAX_BT_ADDRESS 32 /* Structures used in this sketch */ typedef struct { long signature; // data is only valid when 0xACED is stored here. int valid; // 0 if not valid, non-zero if valid byte bt_address[MAX_BT_ADDRESS]; // bluetooth address of previously discovered device(s) } EE_Structure;

// State machine for bluetooth communications enum StateMachine { Initialize = 0, // if we store the device addr, we can jump to connecting RestoreDeviceAddress, DiscoveringDevice, DeviceDiscovered, // once discovered, we can reconnect without all of the setup ConnectingToDevice, ActiveCommunications, ErrorState, EndStates }; /*

* Method declarations */ int ReadInteger(int offset); void WriteInteger(int offset, int value); long ReadLong(int offset); void WriteLong(int offset, long value); void ReadBytes(int offset, byte *data); void WriteBytes(int offset, byte *data); /* * ReadInteger - a helper function to read in an integer from the nonvolatile storage * * Params - offset - the offset into the EEPROM where data is stored * * Returns - int - the value read from the specified location */ int ReadInteger(int offset) { int returnValue = 0; // Read in EEPROM for (int count = 0; count < sizeof(int); count++) { returnValue |= EEPROM.read(offset + count) > ((intSize - 1 - count) * 8); // We need to shift from left to right so upper first then on down EEPROM.write(offset + count, dataValue); } }

/* * ReadLong - a helper function to read in a long from the nonvolatile storage * * Params - offset - the offset into the EEPROM where data is stored * * Returns - long - the value read from the specified location */ long ReadLong(int offset) { long value = 0; int storedValue = ReadInteger(offset); value = (storedValue > 16); int lowerValue = value & 0xFFFF; WriteInteger(offset, upperValue); WriteInteger(offset + 2, lowerValue); } /* * ReadBytes - a helper function to read in a number of bytes from the nonvolatile storage * * Params - offset - the offset into the EEPROM where data is stored * - data - the array to hold the data being read in * - maxLength - the maximum length to read * * Returns - none */ void ReadBytes(int offset, byte *data, int maxLength)

{ if (data != NULL) { for (int index = 0; index < maxLength; index++) { data[index] = EEPROM.read(offset + index); } } } /* * WriteBytes - a helper function to write out a number of bytes to the nonvolatile storage * * Params - offset - the offset into the EEPROM where data is to be stored * - data - the array of bytes to be stored * - length - the length of the data to write * Returns - none */ void WriteBytes(int offset, byte *data, int length) { if (data != NULL) { for (int index = 0; index < length; index++) { EEPROM.write(offset + index, data[index]); } } } /* * Variables used by this sketch */ volatile EE_Structure StoredData; BTModule *g_pBTModule = NULL; // Our interface to the Bluetooth module StateMachine g_connectionState = Initialize; /* * Setup - main configuration point of our sketch to configure * the Arduino for setting up the bluetooth module * and restoring the connection data if present * * Params - none * * Returns - nothing */ void setup() { Serial.begin(9600); // for monitoring Serial.println("Starting setup...\n"); g_pBTModule = BTModule::GetInstance(BT_RX_PIN, BT_TX_PIN);

pinMode(BT_CONNECTED_STATUS_PIN, OUTPUT); StoredData.signature = ReadLong(0); // Have we been here before? if (StoredData.signature == signature) { StoredData.valid = ReadInteger((char *)&StoredData.valid - (char *)&StoredData.signature); if (StoredData.valid != 0) { ReadBytes((char *)&StoredData.bt_address[0] - (char *)&StoredData.signature, (byte *)&StoredData.bt_address[0], MAX_BT_ADDRESS); } } else { // Store signature and default valid as 0 to indicate the address isn't valid WriteLong(0, signature); WriteInteger((char *)&StoredData.valid - (char *)&StoredData.signature, 0); // default to 0 } // If we are still connected then jump right in if (analogRead(BT_CONNECTED_PIN) != 0) { g_connectionState = ActiveCommunications; } Serial.println("Setup Completed."); } /* * Loop - the main loop which runs our state machine and progresses * through each state establishing a connection with the remote * device and then sending data to the device and receiving data back * from it. * * Params - none * * Returns - nothing */ void loop() { char *pRemoteAddress = NULL; switch (g_connectionState) { case Initialize: { Serial.println("Initializing device..."); digitalWrite(BT_CONNECTED_STATUS_PIN, LOW); // turn off when not

connected g_pBTModule->begin(BT_COMM_SPEED, true, "ArduinoBT", BT_PIN_CODE); // do we need to block on this? g_connectionState = RestoreDeviceAddress; } break; case RestoreDeviceAddress: { if (StoredData.valid != 0) { pRemoteAddress = (char *)&StoredData.bt_address[0]; g_connectionState = DeviceDiscovered; } else { pRemoteAddress = NULL; g_connectionState = DiscoveringDevice; } } break; case DiscoveringDevice: { Serial.println("Discovering devices..."); // discover our device, do not block, we will come back to try again if (g_pBTModule->discoverDevice(false) == true) { g_connectionState = DeviceDiscovered; } } break; case DeviceDiscovered: { Serial.println("Connecting to device..."); if (g_pBTModule->connectToDevice(pRemoteAddress, true) == true) { g_connectionState = ActiveCommunications; digitalWrite(BT_CONNECTED_STATUS_PIN, HIGH); // we have a connection // Update eeprom data if (StoredData.valid == 0) { WriteInteger((char *)&StoredData.valid - (char *)&StoredData.signature, 1); // it is now valid g_pBTModule->getRemoteAddress((char *)&StoredData.bt_address[0]); WriteBytes((char *)&StoredData.bt_address[0] - (char *)&StoredData.signature, (byte *)&StoredData.bt_address[0], MAX_BT_ADDRESS); } } } break;

case ActiveCommunications: { char dataValue = 0; if (Serial.available()) { g_pBTModule->sendByte(Serial.read()); } if (g_pBTModule->readByte(&dataValue) == true) { Serial.print(dataValue); } } break; case ErrorState: { // Reset and come back up g_connectionState = DeviceDiscovered; } break; } }

A lot of this code, such as the EEPROM library, has already been covered in previous chapters. There are two key items to look at here. The first is the inclusion of a library that I wrote to work with the Bluetooth® module by Seeed Studio which can be found on GitHub . This library is available for free on github for other's to use and to improve upon. The second key item is the State Machine which I created to track the state of my Bluetooth® connection. This allows the software developer to create a list of instructions i.e. states to follow as the system is processing in order to accomplish a complete task. Basically each step of the state machine is a task which cannot be done in most cases until the previous task is done. The beauty of the state machine is the ability to jump from one state to another in order to redo something or handle error cases and the like. It also fits in nicely with the Arduino® loop function. Given the loop is called continuously, it is quite easy to create a state machine that can progress from one state to the next as the loop is run. In general, the serial communications library has been covered with the Serial functions which stay the same for the software serial port that we are using with this sketch. In particular we will be setting the starting baud rate with the begin function and then sending data using the print function. Likewise, we will be receiving data using the read function.

g_pBTModule = BTModule::GetInstance(BT_RX_PIN, BT_TX_PIN);

The above line of code initializes my Bluetooth® module and returns a pointer that I can use in my code. While this isn't rocket science, I did want to explain a little in more detail the pointer concept and usage. So far we have covered a number of variables such as integers (int), characters (char), and others. A pointer is a little different. It is not so much a data type in itself but more a link

to a data type. Imagine a row of mail boxes for a group of houses each with a name on it. The actual mailbox isn't as important as the name beside it since your mail will be placed in the box where your name is. The pointer is similar in that it points to a specific mailbox i.e. memory location that holds the data you are interested in. The beauty of the mailboxes is that if another person builds a house between you and your neighbor, you can simply move the names on the mailboxes around so that the new person's mailbox is in the right spot. It may have been the one you used previously however now your name has been moved over one and points to a new mailbox. This is the same idea behind a pointer in programming. This variable points to a specific spot in memory however you can have multiple spots in memory containing the same type of data. The pointer can point to any of them as needed. To continue the example, imagine each mailbox is actually a Bluetooth® module as defined above. I could have the one pointer variable which could be assigned to any of the Bluetooth® modules and be able to access all of its functionality. This is done by simply changing which module the pointer points to. For example I could do the following (not working code):

BTModule BTModule BTModule BTModule ...

g_BTModule1(3, g_BTModule2(5, g_BTModule3(7, g_BTModule4(9,

4); // Our interface to the Bluetooth module 1 6); // Our interface to the Bluetooth module 2 8); // Our interface to the Bluetooth module 3 10); // Our interface to the Bluetooth module 4

byte nextChar = Serial.read(); g_BTModule1.sendByte(nextChar); g_BTModule2.sendByte(nextChar); g_BTModule3.sendByte(nextChar); g_BTModule4.sendByte(nextChar);

This works but is a little tedious in that for every specific instance you have to access it directly and if you add or remove an instance then you have to go down through the code to determine every where it has been used and remove it. There is a better way and that way is with a pointer. Again this is just a code snippet and not a complete sketch.

#define BT_MODULE_COUNT 4 // receive and transmit pin assignments int g_btModulePins[BT_MODULE_COUNT][2] = { {3, 4}, {5, 6}, {7, 8}, {9, 10} }; // Array of pointers to my modules BTModule *g_pBTModules[BT_MODULE_COUNT] = {0}; void setup()

{ for (int index = 0; index < BT_MODULE_COUNT; index++) { g_pBTModules[index] = BTModule::GetInstance(g_btModulePins[index][0], g_btModulePins[index][1]); } // Verify that each instance is valid i.e. not NULL. } void loop() { ... byte nextChar = Serial.read(); // Blast the value out to every instance for (int index = 0; index < BT_MODULE_COUNT; index++) { g_pBTModules[index]->sendByte(nextChar); } ... }

Granted I could still define my number of modules and create the module pin array and utilize it in the first example however the main thing here is that in the first example I had four specific instances of my Bluetooth® module which is rigid in that to add or remove an instance, I have to go back through my sketch and locate all of the places it is used and make the changes which takes time and is error prone. In the above example, I have to make two changes. The first change is to update the definition of BT_MODULE_COUNT to another positive value i.e. something greater than zero (0). The second change is to add or remove pin assignments to match the BT_MODULE_COUNT change. The compiler will warn you if the number of pin declarations doesn't match the value of BT_MODULE_COUNT because I use BT_MODULE_COUNT as part of my pin array definition. Once everything is defined, I can process each instance of the Bluetooth® module to configure it in the for loop. In the main loop function when I read in a value, instead of having to send it to each individual instance, I can simply walk my array of pointers and send to each instance of the Bluetooth® module. This reduces the amount of code that is needed and also makes it quite expandable as only a few changes are required in order to increase or decrease the number of modules to use. The following is my state machine values which the sketch will progress through in order to initialize and utilize the Bluetooth® module. By default I always start at the Initialize state unless the BT_CONNECTED_PIN indicates the module is already connected. If that is the case then I can jump right to the ActiveCommunications state.

enum StateMachine {

Initialize = 0, // if we store the device addr, we can jump to connecting RestoreDeviceAddress, DiscoveringDevice, DeviceDiscovered, // once discovered, we can reconnect without all of the setup ConnectingToDevice, ActiveCommunications, ErrorState, EndStates };

Each of the above states should be fairly self explanatory. You might notice there is one state which I do not use which is the EndStates. This is really just a habit of mine in that for every enumeration I add a final state indicating the end of the defined states. The main reason for this is so I can do something like the following:

for (int state = Initialize; state < EndStates; state++) { ... // do something fabulous ... }

Again this isn't rocket science by any means. I find it useful in that if I add or remove a state, I do not have to go back through my code changing any loops to reflect the added or removed state. The exception of course would be a switch statement where each state is declared so a removal or addition would have to be updated. In this sketch I transition through my states until I reach the ActiveCommunications state. Once in active communications I can read in commands from the USB serial port and send them to the Bluetooth® module for transmission to the OBDII connector. Then when a response is sent back, I can display it back on my computer. For a list of commands that can be used with an ELM based OBDII adapter, see the following link: ELM Data Sheet . SPI Serial Peripheral Interface, (SPI), is a high speed serial interface used to communicate between devices. This is a full duplex interface meaning that it can transmit and receive at the same time in a number of configurations. A nice feature of SPI is that it can be connected to a number of devices where a chip select is used to select the recipient and/or source of the data. The chip select signal activates the desired device allowing multiple devices to share the same bus. The SPI interface consists of the following connections: Signal Alternate Name Names

Purpose

SCLK

None

The clock signal to synchronize the SPI devices

SDO

MOSI, DO, Serial data out from the primary device to the subordinate recipients SO, etc.

SDI

MISO, DI, SI, etc.

Serial data in to the primary device from the subordinate devices

CS

SS, STE, etc.

Chip select from the primary device which enables i.e. directs the subordinate device(s) to accept the data

In general usage, you will find SPI referred to as a four (4) wire interface given all of the signals above. In some cases, SPI may be used in a three (3) wire configuration where the SDO and SDI lines are combined. In addition there will be the chip select (CS) line indicating the direction of data flow along with the clock signal to keep everything aligned. And in some cases, i.e. the three (3) wire mode, the two data lines are integrated together at the device and on the host system i.e. the Arduino® in this case, a resistor is used to connect the input and output pins. The data flows according to commands issued on the SPI bus. In this case, the host controller will issue a read or a write command and will then expect the data to flow back based on the command written. And finally in some cases, there is never any data coming back from the recipient device(s) resulting in only three (3) wires needed to perform communications. In addition to the signal names defined above, there are different modes of operation of the SPI bus. In particular you can set the speed of the communications, the polarity of the clock signal in respect to the data along with the phase of the clock in respect to the data. These may sound complicated however it basically means when to capture the data i.e. trigger the reading of the data on the rising edge of the clock signal or the falling edge. This is defined as the phase and it will be dependent upon the polarity of the clock signal which is to say trigger when the clock is either low or the clock is high. For complete details visit: SPI Wiki .

The following table details the configurations per the Arduino® documentation. Mode

Clock Polarity

Clock Phase

0

with 0 indicating a low value as with 0 indicating data is captured when the clock the base of the clock and data valid transitions from low to high which is at the during its high value state beginning of the clock high value state

1

with 0 indicating a low value as with 1 indicating data is captured when the clock the base of the clock and data valid transitions from high to low which is at the end during its high value state of the clock high value state

2

with 0 indicating a high value as with 0 indicating data is captured when the clock the base of the clock and data valid transitions from low to high which is at the during its low value state beginning of the clock low value state

3

with 0 indicating a high value as with 1 indicating data is captured when the clock the base of the clock and data valid transitions from high to low which is at the end during its low value state of the clock low value state

Note: This table is valid for Atmel® micro-controllers and some others but may be different depending on what micro-controller you are using. Another consideration to take into account is the ordering of the data bits going along the SPI bus. Because it is serial, the bits will have to flow in order and the order can be important. When looking at a byte of data, there is a significant bit i.e. the high bit (most significant bit) and a low bit i.e. the least significant bit. The SPI bus can transfer data in most significant bit order where the largest bit is sent first or it can send it in least significant bit order where the lowest significant bit is sent first. Typically you will be sending data most significant bit first however you will want to be aware of the order of any devices that you communicate with over SPI. In some cases it can be difficult to determine the proper order and some trial and error will have to be done to determine the proper settings. The last configurable piece of the SPI puzzle is the speed at which the clock transitions from low to high. This is the frequency of the device and can be as fast as 25 MHz and perhaps more depending on the device. The speed of the SPI bus in terms of the Arduino® is flexible to allow interfacing with a variety of devices. Again, you will need to look at the documentation for the device that you are connecting to in order to configure your Arduino® for the correct speed. The following are the valid clock dividers for the SPI bus and the bus speeds assuming a 16 Mhz clock. Name*

Value

Speed

SPI_CLOCK_DIV2

2

8 Mhz

SPI_CLOCK_DIV4 (default value)

4

4 Mhz

SPI_CLOCK_DIV8

8

2 Mhz

SPI_CLOCK_DIV16

16

1 Mhz

SPI_CLOCK_DIV32

32

500 Khz

SPI_CLOCK_DIV64

64

250 Khz

SPI_CLOCK_DIV128

128

125 Khz

The Arduino® DUE is a little different in that you specify the slave select pin and the divider value which is any value between 0 and 255. The default value for the DUE is 21 which sets the speed to 4 Mhz which is the default of the other Arduino® boards. So SPI seems to be fairly straight forward in terms of a small number of wires to connect along with a ready to use library to interface with it. However, where would we use it? There are any number of uses for the SPI bus and any number of devices that support it which can be beneficial when the number of data lines on your micro-controller is limited. For our use case, we will be interfacing to a digital potentiometer which will allow us to control the intensity of light being displayed by our tricolor LED's. A lot of LED's that are available control brightness by using Pulse Width Modulation (PWM) however that is a simulation by pulsing the LED on and off real quickly to trick your eye into seeing a dimmer color. Using a potentiometer will limit the amount of voltage being dropped across the LED therefore reducing the amount of current flowing through the LED resulting in a lower intensity glow. Typically you will not be able to see the difference however if you were to use a camera you might catch the LED in the off state and if taking video of an LED most likely will catch it both on and off. REQUIRED COMPONENTS Arduino® Board (2) 150 Ohm Resistor 270 Ohm Resistor 2N2222A Transistor Standard 5mm red light emitting diode (LED) 10K digital potentiometer [ 18 ] TOOLS breadboard jumper wires voltage meter

SCHEMATIC

Image created using: Fritzing SOFTWARE For this sketch we will be utilizing the SPI bus to communicate with a digital potentiometer in order to control the brightness of an LED. We will use a transistor to control the current through the LED and will wire the digital potentiometer such that it will control the saturation of the transistor based on its voltage level which will be programmed. This method of control is a little more complicated then using PWM however you have the ability of controlling a number of different devices with the same communication bus.

/* * SpiDigitalPot - the purpose of this sketch is control the brightness of an LED * by configuring a digital potentiometer to set the brightness. * * potCS pin is the pin I am using as the chip select for the potentiometer. * * spi * - spClk - is the pin that the SPI clock is configured for * - spiMosi - is the pin that the master device (Arduino) transmits data to the slave device * - spiMiso - is the pin that the slave device sends data to the master device (Arduino) * - spiCS - is the chip select for the SPI circuit in the Arduino * - I could use the spiCS chip select also for my POT given I only * I only have one SPI device in use at this time. Various shields will * use specific pins as the chip select which most likely will not be pin 10 * * tested with MCP4261 digital potentiometer from Microchip

*/ // include the SPI library: #include /* constant defines */ const const const const const

int int int int int

potCS = 6; spiClk = 13; spiMosi = 11; spiMiso = 12; spiCS = 10;

/* Variables for this sketch */ byte g_potValue = 0; /* * Data structures to send commands to * the digital potentiometer */ /* The command Byte is the main address/data bits to send to the pot */ typedef struct _CommandByte { byte Address:4; /* the address to select */ byte Command:2; /* the actual command to send */ byte DataBits:2; /* the upper data bits */ } CommandByte;

/* The combination command byte with data to send */ typedef struct _CommandData { union { /* The command byte as defined above */ CommandByte cmd; /* The cmd byte in standard byte format for easy access */ byte cmdByte; } command; /* The data to be sent with the command as needed. */ byte data; } CommandData; /* * Method declarations */ void UpdatePot(int selectPin, struct _CommandData *pData); int ReadPot(int selectPin, struct _CommandData *pData);

/* * UpdatePot - sends commands and data to the potentiometer * which is activated by the specified chip select * * Params * selectPin - the pin to use as the select pin * pData - pointer to the command data stucture to be sent * * Return * none */ void UpdatePot(int selectPin, struct _CommandData *pData) { digitalWrite(selectPin, LOW); delay(100); // required delay SPI.transfer(pData->command.cmdByte); SPI.transfer(pData->data); digitalWrite(selectPin, HIGH); } /* * ReadPot - sends commands and data to the potentiometer * which is activated by the specified chip select * * Params * selectPin - the pin to use as the select pin * pData - pointer to the command data stucture to be sent * * Return * int - the value read back from the pot */ int ReadPot(int selectPin, struct _CommandData *pData) { int dataRead = 0; digitalWrite(selectPin, LOW); delay(100); dataRead = SPI.transfer(pData->command.cmdByte) data); digitalWrite(selectPin, HIGH); dataRead &= 0x1FF; return dataRead; } /* * Enumerations for clarity of code */

/* * This enum is a definition of each address that can * be accessed on the potentiometer */ typedef enum { Wiper0 = 0, Wiper1, NVWiper0, NVWiper1, TConReg, StatusReg, Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, } Addresses; /* * This enum is a definition of each command that can * be sent to the potentiometer */ typedef enum { WriteData = 0, Increment, Decrement, ReadData } Commands;

/* * Setup - main configuration point of our sketch to configure * the Arduino for SPI communications with the potentiometer * * Params - none * * Returns - nothing */ void setup() { Serial.begin(9600); pinMode(spiCS, OUTPUT); pinMode(potCS, OUTPUT);

digitalWrite(potCS, HIGH); /* Configure SPI */ SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV128); // Configure pot outputs CommandData command; command.command.cmd.Command = WriteData; command.command.cmd.Address = TConReg; command.command.cmd.DataBits = 0; command.data = 0xFF; // Configure the pot to enable each output UpdatePot(potCS, &command); command.command.cmd.Command = ReadData; // Read back in the tcon register int tconValue = ReadPot(potCS, &command); Serial.print("TCON: "); Serial.println(tconValue); Serial.println("Setup completed"); } /* * loop - main method which will be called each time the * Arduino goes through its main loop which will set * the value of the digital potentiometer which * will vary the intensity of the LED based on the * value written to the potentiometer. The larger * the value written to the potentiometer, the dimmer * the LED as the transistor saturation is lower. * * Params - none * * Returns - nothing * * Note: The pot is 9 bits but we are only using the lower 8 */ void loop() { CommandData command; command.command.cmd.Command = WriteData; command.command.cmd.Address = Wiper0; command.command.cmd.DataBits = 0; // clear the upper bit(s) command.data = g_potValue; // Send the command to the pot updating the value UpdatePot(potCS, &command);

// Note the current value that was sent to the pot Serial.println(g_potValue); // Delay 250 ms so we can see the changes delay(250); // let it roll g_potValue += 25; }

In this sketch we have introduced a few new things, namely the SPI bus. First I have defined two structures to assist in sending data to the potentiometer. The structures are as follows:

/* The command Byte is the main address/data bits to send to the pot */ typedef struct _CommandByte { byte Address:4; /* the address to select */ byte Command:2; /* the actual command to send */ byte DataBits:2; /* the upper data bits */ } CommandByte; /* The combination command byte with data to send */ typedef struct _CommandData { union { /* The command byte as defined above */ CommandByte cmd; /* The cmd byte in standard byte format for easy access */ byte cmdByte; } command; /* The data to be sent with the command as needed. */ byte data; } CommandData;

The first structure defines one byte which will consist of the address we are interested in accessing on the device, the command we are sending, and the upper two data bits of which the highest one is not used. Although I have defined what looks like three (3) bytes of data in this structure, CommandByte, I have actually only defined one (1) byte which is made up of the three defined fields. You will notice that the total bits defined is equal to the same size as a byte i.e. eight (8) bits. I could have written a macro of the like to set the value for the address and the command however it isn't very clear to someone coming along after what is happening. By using the power of the language and the compiler, I can write code that is clear and easy to work with. The second structure is basically two bytes, the first byte is the command byte which contains the address and the command. The second byte is the data byte to send to the device. I am also using a union to allow

easy access to the CommandByte which also allows me to simply pass the cmdByte value to the SPI transfer routine. A union is programming construct that allows you to declare multiple ways of accessing the same data. In this case the command byte, CommandByte, can be accessed via the individual bit fields i.e. Address, Command, and DataBits or it can be accessed all at once via the cmdByte field. The union allows you, the developer, to access the value as needed. The enumerations are provided for code clarity and being able to easily specify what command I want to issue and what address I want to issue it to. I could use simple integer values however the benefits of the enumerations are much easier to read and in some high level programming languages provide additional benefits such as compile time type validation. The next thing to look at is the configuration of the SPI bus.

pinMode(spiCS, OUTPUT); pinMode(potCS, OUTPUT); digitalWrite(potCS, HIGH); /* Configure SPI */ SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(SPI_CLOCK_DIV128);

In the above code, the first thing we do is configure the SPI chip select line as an output. Regardless of whether you will be using spiCS pin itself or another pin, this pin must be configured as an OUTPUT pin otherwise the SPI bus will not work. I am also setting the potentiometer chip select to HIGH to ensure that nothing spurious on the SPI bus is transferred to the digital potentiometer. Once the selection pins are configured, I start the SPI communications and then configure it for use with my device. In this case, my device, the MCP4621, utilizes the most significant bit (MSB) data format. I have also configured the SPI bus to use data mode zero (0) which was defined in the table above. I then specify the clock divider to lower the speed of the SPI bus. In this case I am dividing my base system clock by 128 resulting in a 125KHz clock speed. I could push the device much faster, up to 10MHz however for what I am doing, speed isn't a great concern. Finally, once I have configured the SPI bus, I can read and write data to it. The following code demonstrates transferring data via the SPI bus.

void UpdatePot(int selectPin, struct _CommandData *pData) { digitalWrite(selectPin, LOW); delay(100); // required delay SPI.transfer(pData->command.cmdByte);

SPI.transfer(pData->data); digitalWrite(selectPin, HIGH); }

The first thing we must do is select the proper device that will be receiving the data that we are going to write out on the SPI bus. I have added a delay after the selectPin has been driven to a low state i.e. selected. This delay, while not overly necessary, just ensures that the device to receive the data has time to prepare to receive it. Typically there will be a delay needed that is in the 10's of nanoseconds however the delay I am using is more than sufficient and for what we are doing here, not noticeable. If you were writing data to a strip of LED's which had to change color very rapidly, I would reduce this delay and would also raise the clock speed to as high as the LED strip could handle. Each application requires different configuration settings. Once I have the select pin in the proper state, I then transfer the command byte and the data to the digital potentiometer. Once the data has been transferred, I then place the select pin back to the HIGH state disabling the SPI interface of the recipient device. One thing to note, that each transfer of data on the SPI bus may result in data being read back in as part of the transfer. It will be up to the programmer to determine if this is the case or not based on what is being sent to the recipient device i.e. the digital potentiometer. All in all, the digital potentiometer is a decent device to control the intensity of an LED among other things. In this particular case, the MCP4621 can also be used to store a small amount of data that could be associated with the device itself in order to persist values that could be used regardless of which micro-controller the component was connected to. In addition to it being a decent device, the fact that it can be accessed with the SPI bus makes it easy to interface with. So what happens when you don't have an SPI bus to interface with? You make your own! This will work with a number of SPI bus devices however if the particular device that you are using requires a high speed bus, than this method may not work properly. In highly technical terms, this method is fondly called Bit Banging. This is where you programmatically simulate the SPI bus using standard I/O pins.

/* * TogglePin quickly toggles a pin from low to high back to low * Assumes the pin is starting off as low */ void TogglePin(int pinId) { digitalWrite(pinId, HIGH); digitalWrite(pinId, LOW); } /* * SendData sends a byte of data out a specific pin. * * Params * selectPin - the pin to select the device to receive the data * dataPin - the pin which is where the data will be serialized out over

* clockPin * data * * Returns * None */

- the pin which we will toggle to simulate the clock - the data to be sent out

void SendData(int selectPin, int dataPin, int clockPin, byte data) { // Select the device - assumes device is active low i.e. // it is listening when this pin is at a LOW state digitalWrite(selectPin, LOW); pinMode(dataPin, OUTPUT); // Send MSB first for (int index = 7; index >= 0; index--) { // Shift the 1 into position to see if that bit in the passed in data is high or low if (data & (1 = 0; index--) { TogglePin(clockPin); bitValue = digitalRead(dataPin); if (bitValue == HIGH) { response |= (1
View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF