The Arduino Nano 33 IoT is a useful little microcontroller board. It can do the things that the Arduino Uno can, and it has a number of additional features for physical computing projects. In 2019, we started using it as the standard for Intro to Physical Computing. This page introduces some of the functions that this board supports.
Form Factor
The Nano 33 IoT is based on the original Arduino Nano pin layout, so if you’ve used the Nano in past projects, the layout is the same. It’s a dual-inline package (DIP) format, meaning it’s got two rows of pins spaced 0.1 inches apart, so it fits nicely on a solderless breadboard. You can get it with or without header pins, and it’s small enough that you can incorporate it in handheld projects as well.
The physical pin numbering for DIP devices goes in a U shape. Holding the micro USB connector at the top, the numbering starts with physical pin 1 on the upper left, counting down the left side to pin 14 on the lower left, then counting from pin 15 on the lower right, to pin 28 on the upper right. For the most part, the left side of the board is power and analog inputs, and the right side is digital I/O pins.
In brief, the Nano pins are as follows, counting from physical pin 1 (upper left) to pin 15 (lower left) across to pin 16 (lower right) to pin 30 (upper right):
Pin 1: Digital I/O 13
Pin 2: 3.3V output
Pin 3: Analog Reference
Pin 4-11: Analog in A0-A7; Digital I/O 14-21 Pin 12: VUSB (not normally connected)
Pin 13: Reset
Pin 14: Ground
Pin 15:Vin
Pin 16: Digital I/O pin 1; Serial1 UART TX
Pin 17: Digital I/O pin 0; Serial1 UART RX
Pin 18: Reset
Pin 19: Ground
Pin 20-30: Digital I/O pins 2-12
The microcontroller pin functions page details the functions of each pin for the Nano and other Arduino boards. The full specifications of the Nano 33 IoT and an official pin diagram can be found on its Getting Started page.
Typical Breadboard Layout
In a typical breadboard layout, The +3.3 volts and ground pins of the Nano are connected by red and black wires, respectively, to the left side rows of the breadboard. +3.3 volts is connected to the left outer side row (the voltage bus) and ground is connected to the left inner side row (the ground bus). The side rows on the left are connected to the side rows on the right using red and black wires, respectively, creating a voltage bus and a ground bus on both sides of the board.Other board layouts can be found on the Breadboard Layouts page.
Handling the Board
You should be careful when handling the Nano 33 IoT as there are two delicate parts on it: the MicroUSB connector at the top, and the WiFi/Bluetooth antenna at the bottom. Figure 2 shows a picture of the board.
Like many microcontrollers these days, the Nano 33 IoT uses a MicroUSB connector. This is a delicate connector, and you shouldn’t handle the board by the connector. If you are mounting the board in a project box or as a wearable and you are using the USB connection, make sure the cable and the board are mounted so that they won’t move relative to each other.
The WiFi/Bluetooth Antenna is the small rectangular part at the bottom center. If it is broken off, your WiFi and Bluetooth range will be significantly reduced. For more on caring for it, see the Care of your Nano 33 IoT video and Breadboard Basics.
Power
The Nano 33’s first difference from the Uno is that it operates on 3.3 volts instead of 5 volts. This might be an issue for some older sensors or actuators, but most modern ones will operate on 3.3V. For most projects, you’ll supply 3.3V from the Nano’s +3V3 pin (physical pin 2) and ground from one of the ground pins (physical pins 13 or 18).
If you’re powering the Nano 33 IoT from USB, then the Vin pin (physical pin 14) will supply 5V from the USB connection. You can also supply power the Nano 33 IoT on this pin, up to 21V. If power is fed through this pin, the USB power source is disconnected. If you need 5V power when you’re powered via the Vin pin, you can solder the VUSB jumper on the back, behind pin 19. When you do this, then the VUSB pin will supply 5V whenever the board is powered via the Vin pin.
Processor
The Nano 33 IoT has an ARM Cortex-M0 32-bit SAMD21 processor. It’s considerably faster than the Uno’s processor (48MHz clock speed compared to the Uno’s 16MHz, and a 32-bit processor compared to the Uno’s 8-bit processor) and has more memory (32KB SRAM/256KB flash compared to the Uno’s 2KB/32KB). That makes for more programming space at a faster speed.
Uploading to the Nano 33 IoT
If you’ve used an Uno before, and are migrating to the Nano board, you may notice that the serial connection behaves differently. When you reset the MKR, Nano, or Leonardo boards, or upload code to them, the serial port seems to disappear and re-appear. Here’s why:
There is a difference between the Uno and most of the newer boards like the MKR boards, the Nano 33 IoT and BLE, the Leonardo: the Uno has a USB-to-serial chip on the board which is separate from the microcontroller that you’re programming. The Uno’s processor, an ATMega328, cannot communicate natively via USB, so it needs the separate processor. That USB-to-serial chip is not reset when you upload a new sketch, so the port appears to be there all the time, even when your Uno is being reset.
The newer boards can communicate natively using USB. They don’t need a separate USB-to-serial chip. Because of this, they can be programmed to operate as a mouse, as a keyboard, or as a USB MIDI device. Since they are USB-native, their USB connection gets reset when you upload new code or reset the processor. That’s normal behavior for them; it’s as if you turned off the device, then turned it back on. Once it’s reset, it will let your computer’s operating system know that it’s ready for action, and your serial port will reappear. This takes a few seconds. It means you can’t reset the board and then open the serial port in the next second. You have to wait those few seconds until the Arduino board has made itself visible to the computer’s operating system again.
If you’re doing MIDI or keyboard or mouse, the serial port number will also change when you add those functions. You’ll still be able to send and receive serial data as usual, but you’ll have to re-choose the port in the Boards -> Port submenu after you program your Nano to be a MIDI or HID device.
If you have trouble getting the Nano 33 IoT to appear in the Arduino IDE, double-tap the reset button at the top center of the board. This will put the board into bootloader mode, meaning that it will show up as a serial device, but not start running the sketch yet. This mode also makes it easier to recover your board if you write a sketch you can’t control, such as a runaway mouse sketch.
Input and Output (GPIO) Pins
The Nano 33 IoT’s got 14 digital I/O pins and 8 analog input pins. The analog in pins can also be used for digital in and out, for a total of 22 digital I/O pins. Of those, 11 can be used for PWM out (pseudo-analog out): digital pins 2, 3, 5, 6, 9, 10, 11, 12, A2, A3, and A5. One pin A0, can also be used as a true analog out, because it has a digital-to-analog converter (DAC) attached (here’s an example of how to use it). There are also more hardware interrupt pins than the Uno; pins 2, 3, 9, 10, 11, 13, A1, A5, and A7 can be used as hardware interrupts. Hardware interrupts make it possible to read very fast changes in digital input and output. For example, rotary encoders work best when attached to interrupt pins.
Serial and USB
The Nano 33 IoT is USB-native. That means it can operate as a few different USB devices: asynchronous serial, keyboard or mouse (also known as Human Interface Device, or HID), and USB MIDI. This is different than the Uno, which has a dedicated USB-to-serial chip on the board, but can only operate as a USB serial device.
There’s also a second asynchronous serial port on pins 0 and 1 that you can use for connecting to other serial devices while still connecting to your personal compuuter. The serial port on pins 0 and 1 is called Serial1, so you’d type Serial1.begin(9600) to initialize it, for example.
Synchronous Serial
Like the other Arduinos, the Nano 33 IoT can communicate via Synchronous serial communications using I2C or SPI. The SPI pins are:
SDI- 12
SDO – 11
SCK – 13
CS – 10
The I2C pins are:
SDA – A4
SCL – A5
Inertial Measurement Unit (IMU)
There is an Inertial Measurement Unit (IMU) on the board, combining a 3-axis accelerometer with a 3-axis gyrometer. This enables gesture-based sensing or tap sensing with no extra hardware. The Arduino_LSM6DS3 library supports this sensor. There are some notes at this link and a lab at this link introducing the IMU.
Real-Time Clock
The Nano 33 IoT also has a real-time clock module built into the processor, which is accessible using the RTCZero library. With this, you can keep track of hours, minutes and seconds much easier. As long as the board is powered, the realtime clock will keep time. Like all libraries, it comes with examples when you install it. You can find several additional examples in this gitHub repository.
WiFi and Bluetooth
WiFi and Bluetooth connectivity are available on the Nano 33 IoT via a low-power 2.4GHz radio. Secure communication is ensured through an on-board crypto chip as well. The WiFiNINA library supports the WiFi on this board, and it’s compatible with the WiFi101 library written for the MKR1000. Any examples written for WiFi101 should be able to run just by changing WiFi101.h to WiFiNINA.h. You can find additional WiFi examples at these links:
The Nano 33 IoT can run multiple loops at once, using the Scheduler library. When you’ve got an application that needs two or more independent loop functions, this can be a quick way to do it.
For more on using the Nano 33 IoT, see the various microcontroller Labs on this site, for example:
We use a few different microcontroller boards in this class over the years, but for each one, there is a standard way we lay out the breadboard. This page details those layouts. In any lab exercise, you can assume the microcontroller and breadboard are laid out in this way, depending on the controller you are using. Figures 1, 2, and 3 show the layouts of an Arduino Nano 33 IoT, an Arduino Uno, an Arduino MKR.
Nano Layout
Figure 1. An Arduino Nano mounted on a solderless breadboard. The Nano is mounted at the top of the breadboard, straddling the center divide, with its USB connector facing up. The top pins of the Nano are in row 1 of the breadboard.
The Nano, like all Dual-Inline Package (DIP) modules, has its physical pins numbered in a U shape, from top left to bottom left, to bottom right to top right. The Nano’s 3.3V pin (physical pin 2) is connected to the left side red column of the breadboard. The Nano’s GND pin (physical pin 14) is connected to the left side black column.
These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus. The blue columns (ground buses) are connected together at the bottom of the breadboard with a black wire. The red columns (voltage buses) are connected together at the bottom of the breadboard with a red wire.
Uno Layout
Figure 2. An Arduino Uno on the left connected to a solderless breadboard, right. The Uno’s 5V output hole is connected to the red column of holes on the far left side of the breadboard. The Uno’s ground hole is connected to the blue column on the left of the board. The red and blue columns on the left of the breadboard are connected to the red and blue columns on the right side of the breadboard with red and black wires, respectively. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.
MKR Layout
Figure 3. An Arduino MKR series board mounted on a solderless breadboard. The MKR is mounted at the top of the breadboard, straddling the center divide, with its USB connector facing up. The top pins of the MKR are in row 1 of the breadboard.
The Nano, like all Dual-Inline Package (DIP) modules, has its physical pins numbered in a U shape, from top left to bottom left, to bottom right to top right. The MKR’s Vcc pin (physical pin 26) is connnected to the right side red column of the breadboard. The MKR’s GND pin (physical pin 25) is connected to the right side black column. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.
The blue columns (ground buses) are connected together at the bottom of the breadboard with a black wire. The red columns (voltage buses) are connected together at the bottom of the breadboard with a red wire.
This page explains the basic pin functions that most microcontrollers share, and offers some tips for switching from one microcontroller to another. Since the tutorials on this site are all written with the Arduino Uno in mind, and students may be using other controllers, you may need to know how to “convert” a tutorial from the controller it’s written for to your own controller. In order to get the most out of it, you should know something about electrical circuits, and what a microcontroller is and what it can do. This video might help: Hardware functions in a microcontroller
What Do All These Pins Do?
A typical microcontroller can have between 6 and 60 pins on it, to which you’re expected to attach power connections, input and output connections, and communications connections. Every microcontroller has different configurations for its pins, and often one pin will have more than one function. This combining of functions on one pin is called pin multiplexing.
Every microcontroller has names for the pins specific to its hardware, but the Arduino application programming interface (API) provides a set of names for pins and their functions that should work across all microcontrollers that are programmable with the API. So, for example, A0 will always be the analog input pin 0, whether you’re on an Uno, 101, MKRZero, MKR1000, or other Arduino-compatible board. When you connect to the pin with the same function on another board, your code should operate the same, even though the physical layout of pins is different.
Every board has an operating voltage that affects its pins as well. The operating voltage, which is the same as the voltage of the GPIO pins, is labeled below. If you’re connecting a component to a board with a lower voltage than the component, you’ll need to do some level shifting.
Pin Diagrams
Microcontrollers typically come in a variety of physical forms, or packages. Sparkfun has a nice tutorial on integrated circuit package types if you want to know more. Pin numbering on any integrated circuit, including microcontrollers, starts at top left corner, which is usually marked with a dot. From there, you count around the chip counter-clockwise. For modules like the Arduino Uno, this numbering doesn’t hold up, since the board has several pin headers. The pin headers are usually numbered, and the pins of each header are counted. Unfortunately, header numbering does not always follow the same patterns as IC numbering.
Microcontroller pins are referred to by their physical pin (where they are physically on the board) and their functional pin names (what they do). For example, the Arduino Nano’s physical pin 20 is digital I/O pin 2.
Arduino Nano Series
The Arduino Nano boards, like the Nano Every, Nano 33 IoT, Nano 33 BLE, Nano 33 BLE Sense, and Nano RP2040 Connect have a number of similar features. The Nano 33 IoT is shown in Figure 1, but the other Nanos have the same pin layout. The pin numbering follows the U-shaped pattern of a typical integrated circuit as described above; pin 1 is on the top left, and pin 30 is on the top right. The pins, summarized, are as follows:
Physical pin 1: Digital I/O 13
Physical pin 2: 3.3V output
Physical pin 3: Analog Reference
Physical pin 4-11: Analog in A0-A7; Digital I/O 14-21
Physical pin 12: VUSB (not normally connected)
Physical pin 13: Reset
Physical pin 14: Ground
Physical pin 15:Vin
Physical pin 16: Digital I/O pin 1; Serial1 UART TX
Physical pin 17: Digital I/O pin 0; Serial1 UART RX
Physical pin 18: Reset
Physical pin 19: Ground
Physical pin 20-30: Digital I/O pins 2-12
PWM Pins: on the Nano 33 IoT, the following pins can be used as PWM pins: digital pins 2, 3, 5, 6, 9, 10, 11, 12, A2, A3, and A5. One pin A0, can also be used as a true analog out, because it has a digital-to-analog converter (DAC) attached. On the Nano Every, only pins 3, 5, 6, 9, and 10 can be used as PWM pins. On the Nano 33 BLE, all digital pins can be used as PWM pins.
SPI Pins:
SDI- digital pin 12 (physical pin 30)
SDO – digital pin 11 (physical pin 29)
SCK – digital pin 13 (physical pin 1)
CS – digital pin 10 (physical pin 28)
I2C Pins:
SDA – pin A4 (physical pin 8)
SCL – pin A5 (physical pin 9)
UART Pins:
Serial 1 TX: digital pin 1 (physical pin 16)
Serial1 RX: digital pin 0 (physical pin 17)
Notes on the Nano Series
The Nano Every operates on 5V. The Nano 33 IoT, 33 BLE, and RP2040 Connect operate on 3.3V.
The Nano 33 IoT is based on the SAMD21 processor, just like the MKR boards. It has a NINA W102 radio that can communicate using Bluetooth 4.0 or WiFi, just like the MKR 1010. The RP2040 Connect has the same radio. It also has a real-time clock and an IMU sensor. For more on this board, see the Introduction to the Nano 33 IoT.
The Nano 33 BLE is based on the nRF 52840 microcontroller. It can communicate using Bluetooth 5.0
The Nano Every is based on the ATMega4809 microcontroller. It is functionally most similar to the Uno’s Atmega328 processor.
The Nano RP2040 Connect is based on the Raspberry Pi RP2040 processor, a dual SAMD21 processor.
On the Nano Every, pins 16 and 17 (TX and RX) are the serial port called Serial; they are also attached to the USB-Serial microcontroller on board. On the 33 IoT and 33 BLE, they are the serial port called Serial1 and are not attached to the USB serial port.
On the Nano 33 IoT as opposed to other Arduino Nano boards, pins A4 and A5 have an internal pull-up resistor and default to the I2C functions.
On the Nano 33 IoT and Nano 33 BLE, the VUSB pin does NOT supply voltage but is connected through a jumper, to the USB power input. You need to solder the jumper on the bottom of the board to use VUSB.
Interrupts: All the Nano 33 IoT’s, Nano 33 BLE’s, and Nano Every’s digital pins can be used as hardware interrupts. However, some repeat, so check the AttachInterrupt guide for the best pins to use as interrupts.
Arduino Uno Rev 3
The Arduino Uno Rev 3, shown in Figure 1, is the classic Arduino model. The pin numbering follows the U-shaped pattern of a typical integrated circuit as described above; pin 1 is on the top left, and pin 30 is on the top right. The pins, summarized, are as follows:
Physical pin 1: not connected
Physical pin 2: I/O reference voltage
Physical pin 3: Reset
Physical pin 4: 3.3V output
Physical pin 5: 5V output
Physical pin 6-7: Ground
Physical pin 8: Vin
Physical pin 9-14: Analog in A0-A5; Digital I/O 14-19
Physical pin 15-28: Digital I/O pin 0-13
Physical pin 29: Ground
Physical pin 30:Analog Reference
Physical pin 31: I2C SDA; Digital I/O pin 18; Analog in A4
Physical pin 32: I2C SCL; Digital I/O pin 19; Analog in A5
PWM Pins: on the Uno Rev 3, the following pins can be used as PWM pins: digital pins 3, 5, 6, 9, 10, 11.
SPI Pins:
SDI- digital pin 12 (physical pin 27; digital I/O pin 12)
SDO – digital pin 11 (physical pin 26; digital I/O pin 11)
SCK – digital pin 13 (physical pin 28; digital I/O pin 13)
CS – digital pin 10 (physical pin 25; digital I/O pin 10)
I2C Pins:
SDA – pin A4 (physical pin 13 or 31; Analog in pin A4)
SCL – pin A5 (physical pin 14 or 32; Analog in pin A5)
UART Pins:
Serial TX: digital pin 1 (physical pin 16)
Serial RX: digital pin 0 (physical pin 15)
Notes on the Uno Rev 3
At the bottom center of the Uno board is a six-pin connector called the In-Circuit Serial Programming connector (ICSP). It has two rows of pins, labeled as follows:
Top row (left to right): Reset, SCK, SDI
Bottom row (left to right): Ground, SDO, +5V
On the top right of the Uno is another six-pin connector. The Uno has a second microcontroller on board to handle USB-to-serial communications. This is the ICSP header for that microcontroller.
The Serial port called Serial is attached to pins 0 and 1, and to the USB-Serial micrcontroller on board.
Interrupts: on the Uno rev. 3, only digital pins 2 and 3 can be used as interrupts.
Arduino MKR Series
The Arduino MKR series are intended for advanced RF applications. They have the same SAMD Cortex M0+ as the Nano 33 IoT, and a built-in rechargeable battery connector and charging circuit. Like most of the Nanos, the MKRs are 3.3V boards. There are several boards in the MKR line for different connectivity needs:
The pin numbering follows the U-shaped pattern of a typical integrated circuit as described above; pin 1 is on the top left, and pin 28 is on the top right. The pins, summarized, are as follows:
Physical pin 1: Analog Reference
Physical pin 2-8: Analog in A0-A6
Physical pin 9-14: Digital I/O pin 0-5
Physical pin 15-23: Digital I/O pin 6-14
Physical pin 24: Reset
Physical pin 25: Ground
Physical pin 26: 3.3V output
Physical pin 27: Vin
Physical pin 28: 5V output
PWM Pins: on the MKR series, the following pins can be used as PWM pins: digital pins 0 – 8, 10, 12, analog pins A3, A4.
SPI Pins:
SDI- digital pin 12 (physical pin 19; digital I/O pin 10)
SDO – digital pin 11 (physical pin 17; digital I/O pin 8)
SCK – digital pin 13 (physical pin 18; digital I/O pin 9)
CS – any other digital pin
I2C Pins:
SDA – Digital I/O pin 11 (physical pin 19)
SCL – Digital I/O pin 12 (physical pin 20 )
UART Pins:
Serial TX: digital pin 14 (physical pin 21)
Serial RX: digital pin 13 (physical pin 20)
Notes on the MKR Series
Serial: The MKR series boards have two hardware UARTs.The first one, UART0 (aka Serial in your sketches) is attached directly to the USB port not to any pins. GPIO pins 13 and 14 are Serial1
Battery in: LiPo, 3.7V, 700mAh min Recharging circuit on board.
Interrupts: On the MKR series, pins 0, 1, 4, 5, 6, 7, 8, 9, A1, and A2 can be used as interrupts.
Pin Functions Explained
In order to make sense of all of this, it helps to know the general functions of a microcontroller. There are a few common functions:
Power: Every microcontroller will have connections for power (often labeled Vcc, Vdd, or Vin) and ground. A bare microcontroller will have only those, but modules like the Arduino, the Raspberry Pi, and others also have voltage regulators and other components on board. On these, it’s common to see an unregulated voltage input (Vin) and a regulated voltage output (5V and 3.3V on the Uno, for example).
Clock: Every microcontroller needs a clock. The bare microcontroller chip usually has two pins for this. On a module, the clock is usually built onto the board, and the pins are not exposed.
General Purpose Input and Output (GPIO): Most pins on a microcontroller can operate as either a digital input or digital output.
Hardware Interrupts: Many microcontrollers have a subset of their GPIO pins attached to hardware interrupt circuits. A hardware interrupt can interrupt the flow of a program when a given pin changes its state, so you can read it immediately. Some higher level functions like asynchronous serial and PWM sometimes use these interrupts. They’re also good for very responsive reading of digital inputs.
Analog Input (ADC): Not all microcontrollers have an analog-to-digital converter (ADC), but those that do have a number of pins connected to it and act as inputs to the ADC. If there are analog inputs, include analog reference pin as well, that tells the microcontroller what the default high voltage of the ADC is.
Pulse Width Modulation (PWM): Few microcontrollers have a true analog voltage output (though the MKR1000 does), but most have a set of pins connected to an internal oscillator that can produce a pseudo-analog voltage using PWM. This is how the analogWrite() function in Arduino works.
Communications:
Universal Asynchronous Receiver/Transmitter (UART): Asynchronous serial communication is managed by a Universal Asynchronous Receiver/Transmitter, or UART, inside the processor. The UART pins are usually attached to internal hardware interrupts that can interrupt the program flow when new serial data arrives, so you never miss a byte. It’s possible to manage serial communication in software alone, but at high speeds, you’ll see more errors.
Synchronous Serial: SPI and I2C: Most microcontrollers also have dedicated modules in the processor to handle the two most common forms of synchronous serial communication.
The Serial-Peripheral Interface (SPI) bus has four dedicated pins: Serial Data Out (SDO), also called Controller In, Peripheral Out (CIPO); Serial Data In (SDI), or Controller Out, Peripheral In (COPI); Serial Clock (SCK) and Chip Select (CS). Many miccrocontrollers are programmed via SPI through an In-Circuit Serial Programming header (ICSP) as well.
The Inter-Integrated Circuit (I2C) bus has two pins: Serial Data (SDA) and Serial Clock (SCL).
Reset: All microcontrollers have a pin which resets the program. Usually you take this pin low to reset the controller.
IORef: this is the operating voltage of the board. The Uno and 101 have this pin so that shields can read this voltage to adjust their own output voltages as needed. Not all shields have this functionality.
In this lab you’ll learn some methods for determining when a sensor’s reading changes significantly.
Introduction
Microcontrollers can sense what’s going on in the physical world using digital and analog sensors, but a single sensor reading doesn’t tell you much. In order to tell when something significant happens, you need to know when that reading changes. For example, when a digital input changes from LOW to HIGH or the reverse, you can tell that a person closed or opened a switch. When a force-sensing resistor reaches a peak reading, you know that something has hit the sensor. In this lab, you’ll learn how to program your microcontroller to look for three common changes in sensor readings that give you information about events in the physical world: state change detection on digital sensors, and threshold crossing and peak detection on analog sensors. You’ll use these three techniques all the time when you’re designing to read users’ actions.
What You’ll Need to Know
To get the most out of this lab, you should be familiar with the following concepts. You can check how to do so in the links below:
Before you start trying to detect specific sensor change events, you should know what your sensor’s changes look like over time. You might want to start by viewing the change on an oscilloscope, or by using the Serial Plotter in the Arduino IDE Tools menu (command-shift-L), or a graphing program like the one shown in the WebSerial input to p5.js Lab or Serial Output From Arduino to Processing lab to understand how your sensors change. Figure 7 shows a typical sensor change graph.
Figure 7. Graphing a sensor in Processing
Sensor changes are described in terms of the change in the sensor’s property, often a voltage output, over time. The most important cases to consider for sensor change are the rising and falling edges of a digital or binary sensor, and the rising and falling edges and the peak of an analog sensor. The graphs in Figures 8 and 9 of sensor voltage over time illustrate these conditions:
Figure 8. Digital sensors change from high voltage to low and vice versa. The change from low voltage to high is called the rising edge, and the change from high voltage to low is called the falling edge
Figure 9. The three general states of an analog sensor are when it’s rising (current state > previous state), when it’s falling (current state < previous state), and when it’s at a peak.
Prepare the breadboard
Connect power and ground on the breadboard to power and ground from the microcontroller. On the Arduino module, use the 5V or 3.3V (depending on your model) and any of the ground connections, as shown in Figures 10 and 11.
Figure 10. Breadboard view of an Arduino Uno on the left connected to a solderless breadboard, right.
Figure 10 shows an Arduino Uno on the left connected to a solderless breadboard, right. The Uno’s 5V output hole is connected to the red column of holes on the far left side of the breadboard. The Uno’s ground hole is connected to the blue column on the left of the board. The red and blue columns on the left of the breadboard are connected to the red and blue columns on the right side of the breadboard with red and black wires, respectively. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.
Figure 11. Breadboard view of Arduino Nano mounted on a breadboard.
Figure 11 shows an Arduino Nano mounted on a solderless breadboard. The Nano is mounted at the top of the breadboard, straddling the center divide, with its USB connector facing up. The top pins of the Nano are in row 1 of the breadboard.
The Nano, like all Dual-Inline Package (DIP) modules, has its physical pins numbered in a U shape, from top left to bottom left, to bottom right to top right. The Nano’s 3.3V pin (physical pin 2) is connected to the left side red column of the breadboard. The Nano’s GND pin (physical pin 14) is connected to the left side black column. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus. The blue columns (ground buses) are connected together at the bottom of the breadboard with a black wire. The red columns (voltage buses) are connected together at the bottom of the breadboard with a red wire.
Connect a pushbutton to digital input 2 on the Arduino. Figures 12 and 13 show the schematic and breadboard views of this for an Arduino Uno, and Figure 14 shows the breadboard view for an Arduino 33 IoT.
Figure 12. Schematic view of an Arduino connected to a pushbutton.
Figure 13. Breadboard view of an Arduino connected to a pushbutton.
Figure 14. Breadboard view of an Arduino Nano connected to a pushbutton
The +3.3 volts and ground pins of the Arduino are connected by red and black wires, respectively, to the left side rows of the breadboard. +3.3 volts is connected to the left outer side row (the voltage bus) and ground is connected to the left inner side row (the ground bus). The side rows on the left are connected to the side rows on the right using red and black wires, respectively, creating a voltage bus and a ground bus on both sides of the board. The pushbutton is mounted across the middle divide of the solderless breadboard. A 10-kilohm resistor connects from the same row as pushbutton’s bottom left pin to the ground bus on the breadboard. There is a wire connecting to digital pin 2 from the same row that connects the resistor and the pushbutton. The top left pin of the pushbutton is connected to +3.3V.
Program the Microcontroller to Read the Pushbutton’s State Change
In the Digital Lab you learned how to read a pushbutton using the digitalRead() command. To tell when a pushbutton is pushed, you need to determine when the button’s state changes from off to on. With the button wired as you have it here, the button’s state will change from 0 to 1. In order to know that, you need to know not only what the current state of the button is, but you also need to remember the state of the button the previous time you read it. This is called state change detection. To do this, set up a global variable to store the button’s previous state. Initialize the button in your program’s setup() function using the pinMode() command. Then, in the loop() function, write a block of code that reads the button and compares its state to the previous state variable. To do this, you need to read the button, check the current button state against the last state, then save the current state of the button in a variable for the next time through the loop like so:
int lastButtonState = LOW; // state of the button last time you checked
void setup() {
// make pin 2 an input:
pinMode(2, INPUT);
}
void loop() {
// read the pushbutton:
int buttonState = digitalRead(2);
// check if the current button state is different than the last state:
if (buttonState != lastButtonState) {
// do stuff if it is different here
}
// save button state for next comparison:
lastButtonState = buttonState;
}
If buttonState is not equal to lastButtonState, then the button has changed. Then you want to check if the current state is HIGH. If it is, then you know the button has changed from LOW to HIGH. That means your user pressed it. Print out a message to that effect.
int lastButtonState = LOW; // state of the button last time you checked
void setup() {
// initialize serial communication:
Serial.begin(9600);
// make pin 2 an input:
pinMode(2, INPUT);
}
void loop() {
// read the pushbutton:
int buttonState = digitalRead(2);
// if it's changed and it's high, toggle the mouse state:
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
Serial.println("Button was just pressed.");
}
}
// save button state for next comparison:
lastButtonState = buttonState;
}
Your code should only print out a message when the button changes state. For every button press, you should get one line of code. You can use this technique any time you need to tell when a digital input changes state.
Count Button Presses
One of the many things you can do with state change detection is to count the number of button presses. Each time the button changes from off to on, you know it’s been pressed once. By adding another global variable and incrementing it when you detect the button press, you can count the number of button presses. Add a global variable at the top of your program like so:
int lastButtonState = LOW; // state of the button last time you checked
int buttonPresses = 0; // count of button presses
Then in the if statement that detects the button press, add one to the button press:
if (buttonState == HIGH) {
buttonPresses++;
Serial.print("Button has been pressed ");
Serial.print(buttonPresses);
Serial.println(" times.");
}
The key to detecting state change is the use of a variable to save the current state for comparison the next time through the loop. This is a pattern you’ll see below as well:
int lastSensorState = LOW; // sensor's previous state
// other globals and the setup go here
void loop() {
// read the sensor:
int sensorState = digitalRead(2);
// if it's changed:
if (sensorState != lastSensorState) {
// take action or run a more detailed check
}
// save sensor state for next comparison:
lastSensorState = sensorState;
}
Long Press, Short Press
Sometimes you want to take a different action on a short button press than you do on a long button press. To do this, you need to know now only when the button changes, but also how long it stays in a pressed state after it changes. Here’s how you might do that.
Start with some global variables for the button pin number, and the length of a long press or a short press, in milliseconds. You also need a variable to track how long the button has been pressed, and as in the code above, you need a variable to track the last button state. Add another variable called pressTime, which will keep track of the last time the button went from LOW to HIGH:
// the input pin:
int buttonPin = 2;
// the length of the presses in ms:
int longPress = 750;
int shortPress = 250;
// variable for how long the user actually presses:
long pressTime = 0;
// previous state of the button:
int lastButtonState = LOW;
In the setup(), set the button pin mode and initialize serial as you did before:
void setup() {
// initialize serial and I/O pin:
Serial.begin(9600);
pinMode(buttonPin, INPUT);
}
In the loop, look for the button to change state, and when it does, note the press time in the pressTime variable. When the button is released (goes from HIGH to LOW), calculate how ling it was pressed, and print it:
void loop() {
// read the button:
int buttonState = digitalRead(buttonPin);
// if the button has changed:
if (buttonState != lastButtonState) {
// if the button is pressed, start a timer:
if (buttonState == HIGH) {
pressTime = millis();
}
// if it's released, stop the timer:
if (buttonState == LOW) {
long holdTime = millis() - pressTime;
// take action for long press, short press, or tap:
if (holdTime > longPress) {
Serial.println("long press");
} else if (holdTime > shortPress) {
Serial.println("short press");
} else {
Serial.println("Tap");
}
}
}
// save button state for next time:
lastButtonState = buttonState;
}
You can see from this that you’ve actually got three states now, long press (> 750ms), short press (250-750ms), and tap (>250ms). With this, you can make one button do three things.
Analog Sensor Threshold Detection
When you’re using analog sensors, binary state change detection like you saw above is not usually effective, because your sensors can have multiple states. Remember, an analog sensor on an Arduino can have up to 1024 possible states. The simplest form of analog state change detection is to look for the sensor to rise above a given threshold in order to take action. However, if you want the action be triggered only once when your sensor passes the threshold, you need to keep track of both its current state and previous state.
Change the Breadboard
To build this example, you’ll need an analog sensor attached to your microcontroller, as shown in the Analog Input lab. Figures 15-17 show how to connect it.
Figure 15. Schematic of Arduino connected to an FSR on pin 2
Figure 16. Breadboard view of Arduino connected to an FSR on pin 2. The FSR is connected to two rows in the left center section of the breadboard. One of its pins is wired to voltage. The other is connected to ground through a 10-kilohm resistor. The row connecting the two resistors is wired to analog input 0.
Figure 17. Breadboard view of Arduino Nano connected to an FSR on pin 2. The FSR is connected to two rows in the left center section of the breadboard, below the Nano. One of its pins is wired to voltage. The other is connected to ground through a 10-kilohm resistor. The row connecting the two resistors is wired to analog input 0.
Program the Microcontroller to Read a Sensor Threshold Crossing
This example is very similar to the one above:
int lastSensorState = LOW; // sensor's previous state
int threshold = 512; // an arbitrary threshold value
void setup() {
Serial.begin(9600);
}
void loop() {
// read the sensor:
int sensorState = analogRead(A0);
// if it's above the threshold:
if (sensorState >= threshold) {
// check that the previous value was below the threshold:
if (lastSensorState < threshold) {
// the sensor just crossed the threshold
Serial.println("Sensor crossed the threshold");
}
}
// save button state for next comparison:
lastSensorState = sensorState;
}
This program will give you an alert only when the sensor value crosses the threshold when it’s rising. You won’t get any reading when it crosses the threshold when it’s falling, and you’ll only get one message when it crosses the threshold. It is possible to sense a threshold crossing when the sensor is falling, by reversing the greater than and less than signs in the example above. The threshold you set depends on your application. For example, if you’re using a light sensor to detect when it’s dark enough to turn on artificial lighting, you’d use the example above, and turn on the light when the threshold crossing happens. But you might also need to check for the falling threshold crossing to turn off the light.
Detecting a Peak
There are times when you need to know when an analog sensor reaches its highest value in a given time period. This is called a peak. To detect a peak, you first set an initial peak value at zero. Pick a threshold below which you don’t care about peak values. Any time the sensor value rises above the peak value, you set the peak value equal to the sensor value. When the sensor value starts to fall, the peak will remain with the highest value:
int peakValue = 0;
int threshold = 50; //set your own value based on your sensors
void setup() {
Serial.begin(9600);
}
void loop() {
//read sensor on pin A0:
int sensorValue = analogRead(A0);
// check if it's higher than the current peak:
if (sensorValue > peakValue) {
peakValue = sensorValue;
}
}
You only really know you have a peak when you’ve passed it, however. When the current sensor value is less than the last reading you saved as the peak value, you know that last value was a peak. When the sensor value falls past below threshold after you have a peak, but your peak value is above the threshold, then you know you’ve got a significant peak value. after you use that peak value, you need to reset the variable to 0 to detect other peaks :
int peakValue = 0;
int threshold = 50; //set your own value based on your sensors
void setup() {
Serial.begin(9600);
}
void loop() {
//read sensor on pin A0:
int sensorValue = analogRead(A0);
// check if it's higher than the current peak:
if (sensorValue > peakValue) {
peakValue = sensorValue;
}
if (sensorValue <= threshold) {
if (peakValue > threshold) {
// you have a peak value:
Serial.println(peakValue);
// reset the peak variable:
peakValue = 0;
}
}
}
Dealing with Noise
Quite often, you get noise from sensor readings that can interfere with peak readings. Instead of a simple curve, you get a jagged rising edge filled with many local peaks, as shown in Figure 18:
Figure 18. Graph of local peaks
You can smooth out the noise and ignore some of these local peaks by adding in a noise variable and checking to see if the sensor’s change is different than the previous reading and the noise combined, like so:
int peakValue = 0;
int threshold = 50; //set your own value based on your sensors
int noise = 5; //set a noise value based on your particular sensor
void setup() {
Serial.begin(9600);
}
void loop() {
//read sensor on pin A0:
int sensorValue = analogRead(A0);
// check if it's higher than the current peak:
if (sensorValue > peakValue) {
peakValue = sensorValue;
}
if (sensorValue <= threshold - noise ) {
if (peakValue > threshold + noise) {
// you have a peak value:
Serial.println(peakValue);
// reset the peak value:
peakValue = 0;
}
}
}
Most sensor change cases can be described using a combination of state change detection, threshold crossing, and peak detection. When you start to write sensor change detection routines, make sure you understand these three basic techniques, and make sure you have a good idea what your sensor’s readings look like over time. With that combination, you should be able to detect most simple sensor change events.
In this tutorial, you’ll learn how to control a servomotor’s position from a microcontroller using the value returned from an analog sensor.
In this tutorial, you’ll learn how to control a servomotor’s position from a microcontroller using the value returned from an analog sensor.
Introduction
Servos are the easiest way to start making motion with a microcontroller. Servos can turn through a range of 180 degrees and you can use them to create all sorts of periodic or reciprocating motions. Check out some of the mechanisms at Rob Ive’s site for ideas on how to make levers, cams, and other simple machines for making motion. The resources section of this site has links to other sites on construction, mechanics, and kinetics as well.
What You’ll Need to Know
To get the most out of this lab, you should be familiar with the following concepts. You can check how to do so in the links below:
Flexible jumper wires. These wires are quick for breadboard prototyping, but can get messy when you have lots of them on a board.
A solderless breadboard with two rows of holes along each side. The . board is turned sideways so that the side rows are on top and bottom in this view. There are no components mounted on the board.
Potentiometer
10-kilohm resistors. These ones are 5-band resistors
RC Servomotor
Force Sensing Resistor (FSR)
Phototransistors. The short leg goes to voltage, and the long leg goes to the input pin of a microcontroller.
Figures 1-8. The parts you’ll need for this exercise. Click on any image for a larger view.
Prepare the breadboard
Connect power and ground on the breadboard to power and ground from the microcontroller. On the Arduino module, use the 5V or 3.3V (depending on your model) and any of the ground connections, as shown in Figures 9 and 10.
Figure 9 Breadboard view of an Arduino Uno on the left connected to a solderless breadboard, right.
Figure 9. An Arduino Uno on the left connected to a solderless breadboard, right. The Uno’s 5V output hole is connected to the red column of holes on the far left side of the breadboard. The Uno’s ground hole is connected to the blue column on the left of the board. The red and blue columns on the left of the breadboard are connected to the red and blue columns on the right side of the breadboard with red and black wires, respectively. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.
Figure 10. An Arduino Nano mounted on a solderless breadboard. The Nano is mounted at the top of the breadboard, straddling the center divide, with its USB connector facing up. The top pins of the Nano are in row 1 of the breadboard.
The Nano, like all Dual-Inline Package (DIP) modules, has its physical pins numbered in a U shape, from top left to bottom left, to bottom right to top right. The Nano’s 3.3V pin (physical pin 2) is connected to the left side red column of the breadboard. The Nano’s GND pin (physical pin 14) is connected to the left side black column. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus. The blue columns (ground buses) are connected together at the bottom of the breadboard with a black wire. The red columns (voltage buses) are connected together at the bottom of the breadboard with a red wire.
Connect an analog input to analog pin 0 as you did in the Analog Input Lab covered previously. A force-sensing resistor is shown in Figure 11-14 below, but you can also use a potentiometer, phototransistor, or any analog input you prefer. Then connect an RC servomotor to digital pin 9. The yellow wire of the servo goes to the pin, and the red and black wires go to +5V and ground, respectively.
Most RC servomotors are rated for 4-6 volt power input. When you’re using a 3.3V microcontroller like the Nano 33 IoT, you can use the Vin pin to power the motor if you’re running off USB power, or off a 5V source connected to the Vin.
Safety Warning! Not all servos have the same wiring colors. For example, the Hextronik servos that come with Adafruit’s ARDX kit use red for +5V, brown for ground, and mustard yellow for control. Check the specifications on your particular servomotor to be sure.
Figure 11. Schematic view of a servomotor and an analog input attached to an Arduino Uno.
Figure 12. Breadboard view of a servomotor and an analog input attached to an Arduino Uno.
Figure 13. Schematic view of a servomotor and an analog input attached to an Arduino Nano 33 IoT.Figure 14. Breadboard view of a servomotor and an analog input attached to an Arduino Nano 33 IoT.
When you attach the servo, you’ll need a row of three male headers to attach it to a breadboard. You may find that the pins don’t stay in the servo’s connector holes. Put the pins in the servo’s connector, then push them down on a table gently. They will slide up inside their plastic sheaths, and fit better in your servo’s connector.
Different RC servomotors will have different current requirements. The Tower SG5010 model servo sold by Adafruit draws more current than the HiTec HS311 and HS318 sold by ServoCity, for example. The Tower Pro servo draws 100-300 mA with no load attached, while the HiTec servos draw 160-180mA. The decoupling capacitor in the circuit will smooth out any voltage dips that occur when the servo turns on, but you will need an external 5V supply if you are using more than one servomotor.
Figure 15. Attaching header pins to a servomotor connector. If your header pins are too short, as shown here, you can lengthen them.
Figure 16. Push the short ends of the header pins into the servomotor connector’s holes and then brace the long ends against a tabletop while you push down on the connector. Do this gently and the header pins will move in their plastic mount.
Figure 17. Now your header pins will be longer on top and shorter on bottom, and will stay firmly in the servomotor connector.
Program the Microcontroller
First, find out the range of your sensor by using analogRead() to read the sensor and printing out the results.
void setup() {
Serial.begin(9600); // initialize serial communications
}
void loop()
{
int analogValue = analogRead(A0); // read the analog input
Serial.println(analogValue); // print it
}
Now, map the result of the analog reading to a range from 0 to 179, which is the range of the sensor in degrees. Store the mapped value in a local variable called servoAngle.
void setup() {
Serial.begin(9600); // initialize serial communications
}
void loop()
{
int analogValue = analogRead(A0); // read the analog input
Serial.println(analogValue); // print it
// if your sensor's range is less than 0 to 1023, you'll need to
// modify the map() function to use the values you discovered:
int servoAngle = map(analogValue, 0, 1023, 0, 179);
}
Finally, add the servo library at the beginning of your code, then make a variable to hold an instance of the library, and a variable for the servo’s output pin. In the setup(), initialize your servo using servo.attach(). Then in your main loop, use servoAngle to set the servo’s position.
#include "Servo.h" // include the servo library
Servo servoMotor; // creates an instance of the servo object to control a servo
int servoPin = 9; // Control pin for servo motor
// time when the servo was last updated, in ms
long lastMoveTime = 0;
void setup() {
Serial.begin(9600); // initialize serial communications
servoMotor.attach(servoPin); // attaches the servo on pin 9 to the servo object
}
void loop() {
int analogValue = analogRead(A0); // read the analog input
Serial.println(analogValue); // print it
// if your sensor's range is less than 0 to 1023, you'll need to
// modify the map() function to use the values you discovered:
int servoAngle = map(analogValue, 0, 1023, 0, 179);
// move the servo using the angle from the sensor every 20 ms:
if (millis() - lastMoveTime > 20) {
servoMotor.write(servoAngle);
lastMoveTime = millis();
}
}
Servo motors give you the power to do all kinds of things.
They can be used to push a remote control button, in a pinch, as shown in Figure 18.
Figure 18. A servomotor can press remote control buttons The remote control is mounted in a wooden frame, and the servo is mounted on the side of the frame. The servo horn moves down to press the power button.
You can play music with found objects like in this Project by Nick Yulman. You can build a frisking machine like in this project by Sam Lavigne and Fletcher Bach. If you’ve got 800 or so of them and a lot of time, you can build a wooden mirror like this Project by Daniel Rozin.
In this lab, you’ll learn how to connect a variable resistor to a microcontroller and read it as an analog input. You’ll be able to read changing conditions from the physical world and convert them to changing variables in a program.
Introduction
In this lab, you’ll learn how to connect a variable resistor to a microcontroller and read it as an analog input. You’ll be able to read changing conditions from the physical world and convert them to changing variables in a program.
Many of the most useful sensors you might connect to a microcontroller are analog input sensors. They deliver a variable voltage, which you read on the analog input pins using the analogRead() command.
To get the most out of this lab, you should be familiar with the following concepts and you should install the Arduino IDE on your computer. You can check how to do so in the links below:
Flexible jumper wires. These wires are quick for breadboard prototyping, but can get messy when you have lots of them on a board.
A solderless breadboard with two rows of holes along each side. The . board is turned sideways so that the side rows are on top and bottom in this view. There are no components mounted on the board.
Photo of an 8 ohm speaker
LEDs. The long leg goes to voltage and the short leg goes to ground
220-ohm resistors. These ones are 4-band resistors
10-kilohm resistors. These ones are 5-band resistors
Pushbuttons
Potentiometer
Force Sensing Resistor (FSR)
Thermistor
Phototransistors. The short leg goes to voltage, and the long leg goes to the input pin of a microcontroller.
Figure 1-12. The parts you’ll need for this exercise. Click on any image for a larger view.
Figures 1-12 show the parts you’ll need for this exercise. Click on any image for a larger view.
Set Up the Breadboard
Connect power and ground on the breadboard to power and ground from the microcontroller. On the Arduino module, use the 5V or 3.3V (depending on your model) and any of the ground connections. Figures 13 and 14 show how to do this for an Arduino Uno and an Arduino Nano 33 IoT.
As shown in Figure 13, the Uno’s 5V output hole is connected to the red column of holes on the far left side of the breadboard. The Uno’s ground hole is connected to the blue column on the left of the board. The red and blue columns on the left of the breadboard are connected to the red and blue columns on the right side of the breadboard with red and black wires, respectively. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.
Figure 13. An Arduino Uno on the left connected to a solderless breadboard, right.
Figure 14. Breadboard view of an Arduino Nano mounted on a breadboard
In Figure 14, the Nano is mounted at the top of the breadboard, straddling the center divide, with its USB connector facing up. The top pins of the Nano are in row 1 of the breadboard.
The Nano, like all Dual-Inline Package (DIP) modules, has its physical pins numbered in a U shape, from top left to bottom left, to bottom right to top right. The Nano’s 3.3V pin (physical pin 2) is connected to the left side red column of the breadboard. The Nano’s GND pin (physical pin 14) is connected to the left side black column. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus. The blue columns (ground buses) are connected together at the bottom of the breadboard with a black wire. The red columns (voltage buses) are connected together at the bottom of the breadboard with a red wire.
Add a Potentiometer and LED
Connect the wiper of a potentiometer to analog in pin 0 of the module and its outer connections to voltage and ground. Connect a 220-ohm resistor to digital pin 9. You can replace the LED with a speaker if you prefer audible output. Connect the anode of an LED to the other side of the resistor, and the cathode to ground as shown below. See Figure 15 and Figure 16 to learn how to do this with an Arduino Uno. Figure 17 shows a breadboard view of an Arduino Nano for the same circuit.
Figure 15. Schematic view of a potentiometer connected to analog in 0 of an Arduino and an LED connected to digital pin 9. Connect the voltage lead of the potentiometer to 5V for Uno, 3.3V for Nano 33 IoT.
Figure 16. Breadboard view of a potentiometer connected to analog in 0 of an Arduino and an LED connected to digital pin 9.
Figure 17. Breadboard view of Arduino Nano with an analog input and LED output.
Figure 17 shows the breadboard view of an Arduino Nano connected to a potentiometer and an LED. The +3.3 volts and ground pins of the Arduino are connected by red and black wires, respectively, to the left side rows of the breadboard. +3.3 volts is connected to the left outer side row (the voltage bus) and ground is connected to the left inner side row (the ground bus). The side rows on the left are connected to the side rows on the right using red and black wires, respectively, creating a voltage bus and a ground bus on both sides of the board. The potentiometer is mounted in the left center section of the solderless breadboard. Its outside pins are connected to the voltage and ground buses, respectively There is a wire connecting to analog in 0 of the nano (physical pin 4) to the the center pin of the potentiometer. An LED is mounted in the right center section of the board, with a 220-ohm resistor attached to its anode (long leg). The other end of the resistor connects to the Nano’s digital pin 9 (physical pin 27). The cathode of the LED (short leg) connects to ground. If you’re using a speaker instead of the LED, connect it to the same connections as the LED.
Program the Module
Now that you have the board wired correctly, program your Arduino as follows:
First, establish some global variables: one to hold the value returned by the potentiometer, and another to hold the brightness value. Make a global constant to give the LED’s pin number a name.
const int ledPin = 9; // pin that the LED is attached to
int analogValue = 0; // value read from the pot
int brightness = 0; // PWM pin that the LED is on.
In the setup() method, initialize serial communications at 9600 bits per second, and set the LED’s pin to be an output.
// initialize serial communications at 9600 bps:
Serial.begin(9600);
// declare the led pin as an output:
pinMode(ledPin, OUTPUT);
}
In the main loop, read the analog value using analogRead() and put the result into the variable that holds the analog value. Then divide the analog value by 4 to get it into a range from 0 to 255. Then use the analogWrite() command to face the LED. Then print out the brightness value. An alternate loop function for the speaker follows right after the first one.
void loop() {
analogValue = analogRead(A0); // read the pot value
brightness = analogValue /4; //divide by 4 to fit in a byte
analogWrite(ledPin, brightness); // PWM the LED with the brightness value
Serial.println(brightness); // print the brightness value back to the serial monitor
}
If you’re replacing the LED with a speaker, here’s an alternate loop function that will play a changing tone on the speaker:
void loop() {
analogValue = analogRead(A0); // read the pot value
frequency = (analogValue /4) * 10; // divide by 4 to fit in a byte, multiply by 10 for a good tonal range
tone(pinNumber, frequency); // make a changing tone on the speaker
Serial.println(brightness); // print the brightness value back to the serial monitor
}
When you run this code, the LED should dim up and down as you turn the pot, and the brightness value should show up in the serial monitor.
Other variable resistors
You can use many different types of variable resistors for analog input. For example, the pink monkey in the photo below has his arms wired with flex sensors. These sensors change their resistance as they are flexed. When the monkey’s arms move up and down, the values of the flex sensors change the brightness of two LEDs. The same values could be used to control servo motors, change the frequency on a speaker, or move servo motors.
Figure 18. A stuffed pink monkey with flex sensors attached
Note on Soldering Sensor Leads
Flex sensors and force-sensing resistors melt easily, so unless you are very quick with a soldering iron, it’s risky to solder directly to their leads. See Figure 19-21 to learn about three better solutions:
Figure 19. Wire-wrapped connections of a force-sensing resistor
Figure 20. Screw terminal connection for force sensing resistor
Figure 21. Force sensing resistor connected to breakaway socket headers
If you’d like to read a changing light level, you can use a phototransistor for the job. Phototransistors are not variable resistors like photoresistors (which are shown in this video), but they perform similarly are made from less toxic materials. They are actually transistors in which the light falling on the sensor acts as the transistor’s base. Like photoresistors, they are sensitive to changes in light, and they work well in the same voltage divider circuit. Figure 22 shows how to connect a phototransistor and a 10-kilohm resistor as an analog input:
Figure 22. Breadboard view of an Arduino Nano connected to a phototransistor as an analog input. The long leg of the phototransistor connects to voltage, and the long leg connects to the input pin. The 10-kilohm fixed resistor then connects from the input pin to ground.
Different phototransistors will have different sensitivities to light. For example, this model from Everlight, which has a clear top, is most sensitive to 390 – 700 nm light range, with a peak at 630nm (orange-red). This model from Excelitas has a colored top to block IR light, and has a range from 450 -700nm, with a peak at 585nm (yellow). For the frequencies of the visible light spectrum, see this chart from Wikipedia.
Figure 23 and 24 shows an example circuit much like the pink monkey circuit (Figure 18) above, but with force-sensing resistors instead of flex sensors.
On the breadboard, two force-sensing resistors are mounted in rows 16 and 17 and 24 and 25, respectively, in the left center section of the board. Two red wires connects rows 16 and 24 in the left center section to the voltage bus on the left side. Two 10-kilohm resistors (orange, black, brown, and gold bands) connect rows 17 and 25 to the ground bus on the left hand side. Two blue wires connect from rows 17 and 25 to analog in pins 0 and 1 of the Arduino, respectively. Two 220-ohm resistors straddle the center divide of the breadboard, connecting to row 7 on both sides and 11 on both sides, respectively. In the left center section of the breadboard, two blue wires connect rows 7 and 11 to pins D10 and D9 of the Arduino, respectively. In the right center section, the anodes of two LEDs are connected to rows 7 and 11, respectively. The cathodes of the LED are in rows 6 and 10, respectively. Two black wire connects row 6 and 10 to the ground bus on the right side of the board.
Figure 23. Schematic view of two force-sensing resistors and two LEDs attached to an Arduino.
Figure 24. Breadboard view of two force-sensing resistors and two LEDs attached to an Arduino.
The circuit above works for any variable resistor. You could replace the force-sensing resistors with flex sensors to use the monkey toy above with this circuit. Two resistors placed in series like this are called a voltage divider. There are two voltage dividers in the circuit shown, one on analog in 0 and one on analog in 1. The fixed resistor in each circuit should have the same order of magnitude as the variable resistor’s range. For example, if you’re using a flex sensor with a range of 50 – 100 kilohms, you might use a 47-kilohm or a 100-kilohm fixed resistor. If you’re using a force sensing resistor that goes from infinity ohms to 10 ohms, but most of its range is between 10 kilohms and 10 ohms, you might use a 10-kilohm fixed resistor.
The code above assumed you were using a potentiometer, which always gives the full range of analog input, which is 0 to 1023. Dividing by 4 gives you a range of 0 to 255, which is the full output range of the analogWrite() command. The voltage divider circuit, on the other hand, can’t give you the full range. The fixed resistor in the circuit limits the range. You’ll need to modify the code or the resistor if you want a different range.
To find out your range, open the serial monitor and watch the printout as you press the FSR or flex the flex sensor. Note the maximum value and the minimum value. Then you can map the range that the sensor actually gives as input to the range that the LED needs as output.
For example, if your photocell gives a range from 400 to 900, you’d do this:
// map the sensor value from the input range (400 - 900, for example) to the output range (0-255):
int brightness = map(sensorValue, 400, 900, 0, 255);
analogWrite(ledPin, brightness);
You know that the maximum input range of any analog input is from 0 to 5 volts. So if you wanted to know the voltage on an analog input pin at any point, you could do some math to extrapolate it in your loop() like so:
void loop() {
// read the sensor on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// for 0-3.3V use the line below:
// voltage = sensorValue * (3.3 / 1023.0);
// print out the value you read:
Serial.println(voltage);
}
Now write a sketch to control the red LED with the first sensor (we’ll call it the right hand sensor) and the green LED with the second sensor (we’ll call it the left hand sensor). First, make two constants for the LED pin numbers, and two variables for the left and right sensor values.
const int redLED = 10; // pin that the red LED is on
const int greenLED = 11; // pin that the green LED is on
int rightSensorValue = 0; // value read from the right analog sensor
int leftSensorValue = 0; // value read from the left analog sensor
In the setup(), initialize serial communication at 9600 bits per second, and make the LED pins outputs.
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
// declare the led pins as outputs:
pinMode(redLED, OUTPUT);
pinMode(greenLED, OUTPUT);
}
Start the main loop by reading the right sensor using analogRead(). Map it to a range from 0 to 255. Then use analogWrite() to set the brightness of the LED from the mapped value. Print the sensor value out as well.
void loop() {
rightSensorValue = analogRead(A0); // read the pot value
// map the sensor value from the input range (400 - 900, for example)
// to the output range (0-255). Change the values 400 and 900 below
// to match the range your analog input gives:
int brightness = map(rightSensorValue, 400, 900, 0, 255);
analogWrite(redLED, brightness); // set the LED brightness with the result
Serial.println(rightSensorValue); // print the sensor value back to the serial monitor
Finish the main loop by doing the same thing with the left sensor and the green LED.
// now do the same for the other sensor and LED:
leftSensorValue = analogRead(A1); // read the pot value
// map the sensor value to the brightness again. No need to
// declare the variable again, since you did so above:
brightness = map(leftSensorValue, 400, 900, 0, 255);
analogWrite(greenLED, brightness); // set the LED brightness with the result
Serial.println(leftSensorValue); // print the sensor value back to the serial monitor
}
Mapping works for audible tones as well. Human hearing is in a range from 20Hz to 20 kHz, with 100 – 10000 Hz being a reasonable middle ground so if your input is in a range from 0 to 255, you can quickly get audible tones by mapping like so:
int pitch = map(input, 0, 255, 100, 10000);
When you run this, you should see the LEDs changing in brightness, or hear the speaker changing in pitch, as you press the sensors. This is the central function of analog sensors on a microcontroller: to allow for a variable range of input to control a variable range out output. Whether your sensor is read through an analog sensor like this, or through synchronous serial interfaces as you’ll see in the SPI and I2C labs, you always need to find out how the range of action from the user relates to the range of values that the sensor produces. Once you’re comfortable with this concept, get to know how to read the change in a sensor’s readings as well.
In this lab, you’ll connect a digital input circuit and a digital output circuit to a microcontroller. Though this is written for the Arduino microcontroller module, the principles apply to any microcontroller.
Introduction
In this lab, you’ll connect a digital input circuit and a digital output circuit to a microcontroller. Though this is written for the Arduino microcontroller module, the principles apply to any microcontroller.
Digital input and output are the most fundamental physical connections for any microcontroller. The pins to which you connect the circuits shown here are called General Purpose Input-Output, or GPIO, pins. Even if a given project doesn’t use digital in and out, you’ll often use LEDs and pushbuttons or switches during the development for testing whether everything’s working.
What You’ll Need to Know
To get the most out of this lab, you should be familiar with the following concepts and you should install the Arduino IDE on your computer. You can check how to do so in the links below:
Figures 1-8 show the parts you’ll need for this exercise. Click on any image for a larger view.
Figure 1. Arduino Nano 33 IoT
Figure 2. Jumper wires. You can also use pre-cut solid-core jumper wires.
Figure 3. A solderless breadboard
Figure 4. An 8 ohm speaker (optional).This is a good alternate to the LED if you prefer audible output.
Figure 5. LEDs. The long leg goes to voltage and the short leg goes to ground
Figure 6. 220-ohm resistors. These ones are 4-band resistors. They are colored red, red, brown and gold, which signifies 2, 2 (red, red), times 10 (brown), with a 5% tolerance (gold).
Figure 7. 10-kilohm resistors. These ones are 5-band resistors. They are colored brown, black, black, red, brown, which signifies 1 (brown), 0, 0 (black, black), times 100 (red), with a 1% tolerance (brown). Four-band 10-kilohm resistors are colored brown, black, orange (1, 0, times 1000), gold (5% tolerance).
Figure 8. A pushbutton. Any switch will do the job as well.
If you’re using a brand new breadboard, you might want to check out these videos before you get started, to prep your board and care for your microcontroller.
Connect power and ground on the breadboard to power and ground from the microcontroller. On the Arduino Uno, use the 5V or 3.3V (depending on your model) and any of the ground connections. Figures 9 and 10 show how to do this for an Arduino Uno and for an Arduino Nano 33 IoT.
Figure 9. Breadboard view of an Arduino Uno on the left connected to a solderless breadboard, right.
As shown in Figure 9, the Uno’s 5V output hole is connected to the red column of holes on the far left side of the breadboard. The Uno’s ground hole is connected to the blue column on the left of the board. The red and blue columns on the left of the breadboard are connected to the red and blue columns on the right side of the breadboard with red and black wires, respectively. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.
Figure 10 Breadboard view of an Arduino Nano mounted on a solderless breadboard.
As shown in Figure 10, the Nano is mounted at the top of the breadboard, straddling the center divide, with its USB connector facing up. The top pins of the Nano are in row 1 of the breadboard.
The Nano, like all Dual-Inline Package (DIP) modules, has its physical pins numbered in a U shape, from top left to bottom left, to bottom right to top right. The Nano’s 3.3V pin (physical pin 2) is connected to the left side red column of the breadboard. The Nano’s GND pin (physical pin 14) is connected to the left side black column. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus. The blue columns (ground buses) are connected together at the bottom of the breadboard with a black wire. The red columns (voltage buses) are connected together at the bottom of the breadboard with a red wire.
Connect a pushbutton to digital input 2 on the Arduino. Figures 11 and 12 show the schematic and breadboard views of this for an Arduino Uno, and Figure 13 shows the breadboard view for an Arduino 33 IoT. The pushbutton shown below is a store-bought momentary pushbutton, but you can use any pushbutton. Try making your own with a couple of pieces of metal as shown in the Switches lab.
If you’re not sure what pins are the inputs and outputs of your board, check the Microcontroller Pin Functions page for more information. The reference page on the standard breadboard layouts for the Uno, Nano series, and MKR series might be useful as well.
Figure 11. Schematic view of an Arduino connected to a pushbutton.
Figure 12. Breadboard view of an Arduino connected to a pushbutton.
Figure 13. Breadboard view of an Arduino Nano connected to a pushbutton.
Figure 13 shows the breadboard view of an Arduino Nano connected to a pushbutton. The +3.3 volts and ground pins of the Arduino are connected by red and black wires, respectively, to the left side rows of the breadboard. +3.3 volts is connected to the left outer side row (the voltage bus) and ground is connected to the left inner side row (the ground bus). The side rows on the left are connected to the side rows on the right using red and black wires, respectively, creating a voltage bus and a ground bus on both sides of the board. The pushbutton is mounted across the middle divide of the solderless breadboard. A 10-kilohm resistor connects from the same row as pushbutton’s bottom left pin to the ground bus on the breadboard. There is a wire connecting to digital pin 2 from the same row that connects the resistor and the pushbutton. The top left pin of the pushbutton is connected to +3.3V.
Note on The Pulldown Resistor
What happens if you don’t include the resistor connecting the pushbutton to ground? The resistor connecting the pushbutton is a pulldown resistor. It provides the digital input pin with a connection to ground. Without it, the input will behave unreliably.
If you don’t have a 10-kilohm resistor for the pushbutton, you can use any reasonably high value. 4.7K, 22K, and even 1 Megohm resistors have all been tested with this circuit and they work fine. See the digital input and output notes for more about the digital input circuit.
If you’re not sure about the resistor color codes, use a multimeter to measure the resistance of your resistors in ohms, and check this resistor color code calculator.
Add Digital Outputs (LEDs)
Connect a 220-ohm resistor and an LED in series to digital pin 3 and another to digital pin 4 of the Arduino. Figures 14, 15, and 16 below show the schematic view as well as the breadboard view for both the Uno and the Nano. If you prefer an audible tone over a blinking LED, you can replace the LEDs with speakers or buzzers. The 220-ohm resistor will work with LED, speaker, or buzzer.
Figure 14. Arduino connected to pushbutton and two LEDs, Schematic view.
Figure 15. Arduino Uno connected to pushbutton and two LEDs, Breadboard view.
Figure 16. Arduino Nano connected to pushbutton and two LEDs, Breadboard view.
Note on LED Resistor Values
For the resistor on the LED, the higher the resistor value, the dimmer your LED will be. So 220-ohm resistors give you a nice bright LED, 1-kilohm will make it dimmer, and 10K or higher will likely make it too dim to see. Similarly, higher resistor values attenuate the sound on a speaker, so a resistor value above 220-ohm will make the sound from your speaker or buzzer quieter.
Make sure you’re using the Arduino IDE version 1.8.19 or later. If you’ve never used the type of Arduino module that you’re using here (for example, a Nano 33 IoT), you may need to install the board definitions. Go to the Tools Menu –> Board submenu –> Board Manager. A new window will pop up. Search for your board’s name (for example, Nano 33 IoT), and the Boards manager will filter for the correct board. Click install and it will install the board definition.
Connect the microcontroller to your computer via USB. When you plug the Arduino into your computer, you’ll find a new serial port in the Tools–>Port menu (for details on installing the software, and USB-to-serial drivers for older Arduino models on Windows, see the Arduino Getting Started Guide). In the MacOS, the name look like this: /dev/tty.usbmodem-XXXX (Board Type) where XXXX are part of the board’s unique serial number and Board Type is the board type (for example, Arduino Uno, Nano 33 IoT, MKRZero, etc.) In Windows it will be called COM and a number. Figure 17 shows the tools men and its Port submenu.
Figure 17. The Arduino Tools menu showing the Ports submenu
Now it’s time to write a program that reads the digital input on pin 2. When the pushbutton is pressed, turn the yellow LED on and the red one off. When the pushbutton is released, turn the red LED on and the yellow LED off.
In your setup() method, you need to set the three pins you’re using as inputs or outputs, appropriately.
void setup() {
pinMode(2, INPUT); // set the pushbutton pin to be an input
pinMode(3, OUTPUT); // set the yellow LED pin to be an output
pinMode(4, OUTPUT); // set the red LED pin to be an output
}
In the main loop, first you need an if-then-else statement to read the pushbutton. If you’re replacing the LED with a buzzer, the code below will work as is. If you’re using a speaker, there’s an alternative main loop just below the first one:
void loop() {
// read the pushbutton input:
if (digitalRead(2) == HIGH) {
// if the pushbutton is closed:
digitalWrite(3, HIGH); // turn on the yellow LED
digitalWrite(4, LOW); // turn off the red LED
}
else {
// if the switch is open:
digitalWrite(3, LOW); // turn off the yellow LED
digitalWrite(4, HIGH); // turn on the red LED
}
}
Here’s an alternate loop function for an audible output on two speakers. If you want to use only one speaker, try alternating the tone frequency from 440Hz (middle A) to 392Hz (middle G):
void loop() {
// read the pushbutton input:
if (digitalRead(2) == HIGH) {
// if the pushbutton is closed:
tone(3, 440); // turn on the first speaker to 440 Hz
noTone(4); // turn off the second speaker
}
else {
// if the switch is open:
noTone(3); // turn off the first speaker
tone(4, 440); // turn on the second speaker to 440 Hz
}
}
Once you’re done with that, you’re ready to compile your sketch and upload it. Click the Verify button to compile your code. Then click the Upload button to upload the program to the module. After a few seconds, the following message will appear in the message pane to tell you the program was uploaded successfully. Related video: Upload the code to the Arduino
Binary sketch size: 5522 bytes (of a 7168 byte maximum)
Press the pushbutton and watch the LEDs change until you get bored. That’s all there is to basic digital input and output!
The Uno vs Newer Boards
If you’ve used an Uno r3 board before (the “classic Uno”), and are migrating to the Nano or a newer board, you may notice that the serial connection behaves differently. When you reset the MKR, Nano, Uno R4, or Leonardo boards, or upload code to them, the serial port seems to disappear and re-appear. Here’s why:
There is a difference between the Uno R3 and most of the newer boards like the MKR boards, the Nano 33 IoT and BLE, the Leonardo and the Uno R4: the Uno R3 has a USB-to-serial chip on the board which is separate from the microcontroller that you’re programming. The Uno R3’s processor, an ATMega328, cannot communicate natively via USB, so it needs the separate processor. That USB-to-serial chip is not reset when you upload a new sketch, so the port appears to be there all the time, even when your Uno R3 is being reset.
The newer boards can communicate natively using USB. They don’t need a separate USB-to-serial chip. Because of this, they can be programmed to operate as a mouse, as a keyboard, or as a USB MIDI device. Since they are USB-native, their USB connection gets reset when you upload new code or reset the processor. That’s normal behavior for them; it’s as if you turned off the device, then turned it back on. Once it’s reset, it will let your computer’s operating system know that it’s ready for action, and your serial port will reappear. This takes a few seconds. It means you can’t reset the board, and then open the serial port in the next second. You have to wait those few seconds until the Arduino board has made itself visible to the computer’s operating system again.
If you have problems with the UBS-native boards’ serial connection, tap the reset button once, then wait a few seconds, then see if the port shows up again once the board has reset itself. You can also double-tap the reset on the MKR and Nano boards to cause the processor to reset and go into a sleep mode. In this mode, the USB connection will reset itself, but your sketch won’t start running. The built-in LED will glow softly. Then upload a blank sketch. From there, you can start as if your board was brand new.
Many projects can be made with just digital input and output. For example, a combination lock is just a series of pushbuttons that have been pushed in a particular sequence. Consider the cymbal-playing monkey in Figures 18-20:
Figure 18. A mechanical toy monkey that plays cymbals. The cymbals are covered with aluminum foil. The foil is connected to wires, and the wires are connected to an Arduino and breadboard. The two wires from the cymbals act as a switch when they are hit together.
The monkey’s cymbals can be turned into a switch by lining them with tin foil and screwing wires to them:
Figure 19. Detail of the cymbal monkey’s cymbal. It is covered with aluminum foil, as described above.
Figure 20. Detail of the other cymbal. This one is also covered with aluminum foil.
Those wires can be run to a breadboard and used as a switch. Then the microcontroller could be programmed to listen for pattern of cymbal crashes, and if it sees that pattern, to open a lock by turning on a digital output.
Consider the project ideas from Project 1 for more applications you can do with simple input and output.
Flexible jumper wires. These wires are quick for breadboard prototyping, but can get messy when you have lots of them on a board.
A solderless breadboard with two rows of holes along each side. The . board is turned sideways so that the side rows are on top and bottom in this view. There are no components mounted on the board.
Photo of an 8 ohm speaker
10-kilohm resistors. These ones are 5-band resistors
100-ohm to 220-ohm resistors. The ones shown here are are 4-band 220-ohm resistors. They are colored red, red, brown and gold, which signifies 2, 2 (red, red), times 10 (brown), with a 5% tolerance (gold).
Force Sensing Resistor (FSR)
Figures 1-7. The parts you’ll need for this exercise. Click on any image for a larger view.
Sound is created by vibrations in air. When those vibrations get fast enough, above about 20 times a second, you hear them as an audible pitch. The number of vibrations per second is called the frequency and frequency is measured in Hertz (Hz). So 20 times a second is 20Hz. Humans can hear pitches from about 20Hz to about 20,000Hz, or 20 kiloHertz (kHz).
What vibrations are we talking about? A speaker can vibrate. The paper cone of the speaker moves forward, then backward, then back to its resting place many times a second. The length of time it takes to move from rest through one back-and-forth motion, is called the period of the vibration. For example, if a speaker is vibrating at 20Hz, then it moves forward, backward, and back to rest 2in 1/20 of a second, or 0.05 seconds. 0.05 seconds is the period of the sound. Period and frequency are related inversely: frequency = 1/period.
A microcontroller makes sound by sending pulses of electrical energy from an output pin through a wire that’s connected to the paper cone of a speaker. That wire is wrapped in a coil, and mounted inside a magnet. The electrical energy generates a magnetic field, and that field is either attracted to the magnet or repelled by it, depending on which direction the electrical energy is flowing. The magnetic energy moves the coil, and since the coil is attached to the cone, the speaker moves.
So in summary: how do you make sound from an Arduino? Attach a speaker to an output pin and turn it on and off at a set frequency. You can write this code yourself, or you can use the tone() command.
Pulsewidth Modulation vs. Frequency Modulation
When you use analogWrite() to create pulsewidth modulation (PWM) on an output pin, you’re also turning the pin on and off, so you might think, “why not use analogWrite() to make tones?” That command can change the on-off ratio of the output (also known as the duty cycle) but not the frequency. If you have a speaker connected to an output pin running analogWrite(), you’ll get a changing loudness, but a constant tone. To change the tone, you need to change the frequency. The tone() command does this for you.
Prepare the breadboard
Connect power and ground on the breadboard to the microcontroller. On the Arduino module, use the 5V or 3.3V (depending on your model) and any of the ground connections. Figures 8 and 9 show connections for an Arduino Uno and a Nano, respectively.
Figure 8. Breadboard view of an Arduino Uno on the left connected to a solderless breadboard, right.
The Uno’s 5V output hole is connected to the red column of holes on the far left side of the breadboard (Figure 8). The Uno’s ground hole is connected to the blue column on the left of the board. The red and blue columns on the left of the breadboard are connected to the red and blue columns on the right side of the breadboard with red and black wires, respectively. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.
Figure 9. Breadboard view of an Arduino Nano mounted on a solderless breadboard.
In Figure 9, the Nano is mounted at the top of the breadboard, straddling the center divide, with its USB connector facing up. The top pins of the Nano are in row 1 of the breadboard.
The Nano, like all Dual-Inline Package (DIP) modules, has its physical pins numbered in a U shape, from top left to bottom left, to bottom right to top right. The Nano’s 3.3V pin (physical pin 2) is connected to the left side red column of the breadboard. The Nano’s GND pin (physical pin 14) is connected to the left side black column. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus. The blue columns (ground buses) are connected together at the bottom of the breadboard with a black wire. The red columns (voltage buses) are connected together at the bottom of the breadboard with a red wire.
Connect a variable resistor such as a force-sensing resistor or photosensor to analog pin 0 in a voltage divider circuit as shown below. The 8-ohm speaker connects to pin 8 of the Arduino. You can use any digital I/O pin if you don’t like 8. The other end of the speaker connects to ground. Figures 10 through 12 show the schematic drawing and the breadboard layouts for an Uno and a Nano, respectively.
Note: Although the circuit shown in Figures 10-12 has a 100-ohm resistor with the speaker, you can use a larger resistor. The larger the resistor, the quieter the speaker. A 220-ohm resistor works reasonably well if you don’t have a 100-ohm resistor.
Figure 10. Schematic view of an Arduino connected to a force sensing resistor (FSR), a 10-kilohm resistor, and a speaker. One leg of the FSR is connected to voltage. The other leg is connected simultaneously to the first leg of a 10-kilohm resistor and the Arduino’s analog input pin A0. The second leg of the 10-kilohm resistor is connected to ground. The red positive wire of the speaker is connected to digital pin 8 of the Arduino. The black ground wire of the speaker is connected to one leg of a 100 ohm resistor. The other leg of the resistor connects to ground.
Figure 11. Breadboard view of an Arduino connected to a force-sensing resistor, a fixed resistor, and a speaker. The Arduino’s voltage out and ground pins are connected to the voltage and ground buses of the breadboard as usual. The FSR is mounted in the left center section of the breadboard. One leg of the FSR is connected to 5 volts. The other leg is connected simultaneously to the first leg of a 10-kilohm resistor and the Arduino’s analog input pin A0. The second leg of the 10-kilohm resistor is connected to ground. The red positive wire of the speaker is connected to digital pin 8 of the Arduino. The black ground wire of the speaker is connected to one leg of a 100 ohm resistor. The other leg of the resistor connects to ground.
Figure 12. Breadboard view of an Arduino Nano connected to two force sensing resistors (FSRs) and a speaker. The Nano’s 3.3 Volts (physical pin 2) and ground (physical pin 14) are connected to the voltage and ground buses of the breadboard as usual. The FSR is mounted below the Nano in the left center section of the breadboard. One leg of the FSR is connected to voltage. The other leg is connected simultaneously to the first leg of a 10-kilohm resistor and the Arduino’s analog input pin A0. The second leg of the 10-kilohm resistor is connected to ground. The red positive wire of the speaker is connected to digital pin 8 of the Arduino. The black ground wire of the speaker is connected to one leg of a 100 ohm resistor. The other leg of the resistor connects to ground.
Once you’ve got the circuit connected, check the range of the analog input and note the highest and lowest values you can reach.
void setup() {
Serial.begin(9600); // initialize serial communications
}
void loop() {
int analogValue = analogRead(A0); // read the analog input
Serial.println(analogValue); // print it
}
Check that the Speaker Works
You can check that the speaker works by playing a single tone over and over. Here’s a short sketch to play middle A, 440Hz:
void setup() {
// nothing to do here
}
void loop() {
// play the tone for 1 second:
tone(8, 440,1000);
// do nothing else for the one second you're playing:
delay(1000);
}
When you run this sketch, you should hear a tone of 44oHz, or middle A, continually. If you don’t, check to see that your speaker wiring is as shown above. If it’s too soft, try changing the 100 Ohm resistor to a smaller resistor.
Play Tones
Write a sketch to read the analog input and map the result to a range from 100 to 1000. The example below assumes your analog input circuit ranges from 200 to 900, so adjuct for your actual values. Store the result in a local variable called frequency. This will be the frequency you play on the speaker Then use the tone() command to set the frequency of the speaker on pin 8.
void setup() {
// nothing to do here
}
void loop() {
// get a sensor reading:
int sensorReading = analogRead(A0);
// map the results from the sensor reading's range
// to the desired pitch range:
float frequency = map(sensorReading, 200, 900, 100, 1000);
// change the pitch, play for 10 ms:
tone(8, frequency, 10);
delay(10);
}
Once you’ve uploaded this, move your hands over the photocells, and listen to the frequency change. It will change from 100 Hz to 1000 HZ, because that’s what you set in the map() command. If you want to change the frequency range, change those two numbers. See if you can get it to play a little tune.
If you want to know how to generate a tone without the tone() command, here’s the basic algorithm to play one wavelength:
void makeTone(float frequency) {
// set the period in microseconds:
int period = (1 / frequency) * 1000000;
// turn the speaker on:
digitalWrite(speakerPin, HIGH);
// delay half the period:
delayMicroseconds(period / 2);
// turn the speaker off:
digitalWrite(speakerPin, LOW);
// delay half the period:
delayMicroseconds(period / 2);
}
Play it Loud
If you’d like to amplify the speaker, modify the speaker circuit by adding a transistor. Most any NPN transistor circuit or N-channel MOSFET will do. Figure 13 shows a speaker getting power from a transistor. This example uses a TIP120 transistor, but an NPN transistor or N-channel MOSFET should work. When using a transistor to amplify the power going to the speaker, you should definitely use a resistor to limit the current. A 100-ohm resistor is shown in this example.
Figure 13. Schematic view of a speaker connected to a transistor. The first leg of the speaker connects to the Arduino’s voltage supply (3.3V). The second leg of the speaker connects to a10-0-ohm resistor. The other side leg of the resistor connects to the collector pin of an NPN transistor (for a TIP120 transistor, this is the middle pin). The transistor’s base pin (the pin to the left when the transistor’s body is facing you) connects to the Arduino’s tone output pin (pin 8 in the examples above). The transistor’s collector pin (on the right when the transistor is facing you) connects to ground. Figure 14. Breadboard view of a speaker connected to a transistor. The speaker and transistor are connected exactly as described in the schematic view caption in Figure 13.
Use Headphones
You can also connect the tone output of an Arduino to headphones. Using a standard 3.5mm audio jack, connect the center pin of the jack to ground through a 10-kilohm resistor. Then connect the two sides to each other and to your tone output pin. The 10-kilohm resistor here is important, because without it the audio signal will be too strong for your headphones, and could cause you ear damage. Figure 15 shows an audio jack connected to an Arduino Nano 33 IoT for the examples shown here.
Figure 15. Breadboard view of a 3.5mm Audio jack connected to an Arduino Nano 33 IoT. The center pin of the audio jack is connected to ground through a 10-kilohm resistor. The two sides are connected to the Nano 33’s tone output pin, which is pin 8 in this example.
A more complex example
There’s an example in the Arduino IDE examples that can play a melody for you. Look in the File Menu under Examples -> Digital -> ToneMelody. You can find the code online at this link. In this example, you’ll see a second tab of text called pitches.h. This file includes constants that give you the pitches for a standard western scale.
You can add your own extra tabs using the new Tab menu to the right of the IDE window. Figure 16 shows the new Tab dropdown menu. Tabs make it easier to separate frequently copied code from custom code for a sketch, as you see in this example.
Figure 16. A dropdown menu located in the upper right hand corner of the Arduino IDE showing the tab options. You can also type cmmand-shift-N to get a new tab.
You’ll notice that pitches.h lists a number of note names and numbers. Those numbers are the frequency of each note. For example:
This tells you that that first note in the list, B0 (which is the second lowest note on a piano keyboard, 4 octaves below middle A) has a frequency of 31 Hz. The values listed in pitches.h allow you to play notes from B0 to D#8.
For this sketch, you’ll play a simple melody. A melody consists of notes played in a sequence, and rests between the note. Each note and rest has its own particular duration. You’re going to play a seven note sequence, as shown in Figure 17 (in C Major):
Figure 17. Two measures of a musical notation. The melody is “Shave and a haircut, two bits”
image from seventhstring.com
The sequence is: C4, G3, G3, G#3, G3, rest, B3, C4
The sketch starts with two global variables. Using pitches.h, make an array variable holding those notes. Make a second array to hold the note durations, marking quarter notes as 4 and eighth notes as 8.
// notes in the melody:
int melody[] = {
NOTE_C4, NOTE_G3,NOTE_G3, NOTE_GS3, NOTE_G3,0, NOTE_B3, NOTE_C4};
// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
4, 8, 8, 4,4,4,4,4 };
How can you use the durations to play notes? Well, imagine that a quarter note is one quarter of a second, and eighth note is one eighth of a second, and so forth. In that case, the actual duration for each note is 1000 milliseconds divided by the value for it in the durations array. For example, the first note is 1000/4, the second is 1000/8, and so forth.
Now, you only want to play the song once, so everything will happen in the setup(). You’ll make a for loop in the setup to iterate over the seven notes. For each time through the loop, use tone() to play the next note in the array. Use the formula in the last paragraph to determine how long each note should play for. After you start the note, delay for as long as the note plays, plus 30 milliseconds or so, to separate each note.
#include "pitches.h"
// notes in the melody:
int melody[] = {
NOTE_C4, NOTE_G3,NOTE_G3, NOTE_GS3, NOTE_G3,0, NOTE_B3, NOTE_C4};
// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {4,8,8,4,4,4,4,4 };
void setup() {
// iterate over the notes of the melody:
for (int thisNote = 0; thisNote < 8; thisNote++) {
// to calculate the note duration, take one second
// divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
int noteDuration = 1000/noteDurations[thisNote];
tone(8, melody[thisNote],noteDuration);
//pause for the note's duration plus 30 ms:
delay(noteDuration +30);
}
}
void loop() {
// no need to repeat the melody.
}
That’s the whole tune!
The Relation Between Pitches
For those interested in a little music theory, here’s where those frequency values in pitches.h came from. If you’re not interested in the details, feel free to skip to the next section.
You may have noticed in pitches.h that the value of B1 was twice that of B0, and that C1 is twice C0, and so forth. In European classical music tradition, the most common tuning system in the last few hundred years has been the 12-tone equal temperament system. This system divides an octave into twelve pitches, in which the frequency interval between every pair of adjacent notes has the same ratio. The pitches are arranged on a logarithmic scale, and the ratio between pitches is equal to to the 12th root of 2, or 21/12, or approximately 1.05946. The whole tuning system is based off a reference frequency, 440 Hz, or middle A (a in the 4th octave, or A4). The step between each adjacent note is called a semitone. Wikipedia’s page on equal temperament gives a longer explanation if you want to know more. Musictheory.net is also a great source of information.
You can use this information to dynamically generate notes in this scale:
frequency = 440 * 2^((noteValue - 69) / 12.0));
This formula, taken from the MIDI tuning standard, works for a range of notes. Using this formula, note 27 is A0, the lowest note on a piano, and note 108 is C8, the highest note on a piano. 440 is the reference frequency, middle A, and 69 is the note number of middle A in MIDI. With this formula, you can cover a wide range of human hearing with note values 0 to 127. And you could generate note frequencies without needing pitches.h. This formula can be handy if you decide to dive into building MIDI devices later on as well. The Arduino API includes a function called the pow() function that raises a number to a power. So that formula, expressed as a line of Arduino programming code, would look like this:
And the “shave and a haircut” tune from above, would be:
int melody[] = {40, 35,35, 36, 35, 0, 39, 40};
Expressed in note values like this, it would be simple to convert a musical sketch from playing using tone() to playing using a MIDI synthesizer.
Next, try making a musical instrument.
A Musical Instrument
Playing a tune like you just doesn’t allow for much user interaction, so you might want to build more of a musical instrument.
Here’s an example of how to use the note constants to make a simple keyboard. Figures 18-20 show the circuit.
Figure 18. Schematic view of an Arduino connected to three force sensing resistors and a speaker.
Figure 19. Breadboard view of an Arduino connected to three force sensing resistors (FSR) and a speaker. Each of the three FSRs have one of their respective legs connected to +5 volts. Each of the other legs connect to one leg of a 10-kilohm resistor and simultaneously connect to one of the Arduino’s analog input pins. In this case the pin connections are A0, A1, and A2. Each of the respective 10-kilohm resistors then connect to ground. The red positive wire of the speaker is connected to digital pin 8 of the Arduino. The black ground wire of the speaker is connected to one leg of a 100 ohm resistor. The other leg of the 100 ohm resistor then connects to ground.
Figure 20. Breadboard view of an Arduino Nano connected to three force sensing resistors (FSR) and a speaker. Each of the three FSRs have one of their respective legs connected to the voltage bus of the breadboard. Each of the other legs connect to one leg of a 10-kilohm resistor and simultaneously connect to one of the Arduino’s analog input pins. In this case the pin connections are A0, A1, and A2. Each of the respective 10-kilohm resistors then connect to ground. The red positive wire of the speaker is connected to digital pin 8 of the Arduino. The black ground wire of the speaker is connected to one leg of a 100 ohm resistor. The other leg of the 100 ohm resistor then connects to ground.
Program it
Make a sketch that plays a note on each sensor when the sensor is above a given threshold.
Start with a few constants: one for the threshold, one for the speaker pin number, and one for the duration of each tone. To make this instrument work, you need to know what note corresponds to each sensor. Include pitches.h as you did above, then make an array that contains three notes, A4, B4, and C3 as well. Set all of this up at the beginning of your sketch, before setup().
#include "pitches.h"
const int threshold = 10; // minimum reading of the sensors that generates a note
const int speakerPin = 8; // pin number for the speaker
const int noteDuration = 20; // play notes for 20 ms
// notes to play, corresponding to the 3 sensors:
int notes[] = {
NOTE_A4, NOTE_B4,NOTE_C3 };
You don’t need anything in your setup(), but in your loop, you need a for loop that iterates over the first three analog inputs, 0, 1, and 2. For each one, read the sensor, and if it’s above a threshold, play the corresponding note in the notes array for the note duration.
#include "pitches.h"
const int threshold = 10; // minimum reading of the sensors that generates a note
const int speakerPin = 8; // pin number for the speaker
const int noteDuration = 20; // play notes for 20 ms
// notes to play, corresponding to the 3 sensors:
int notes[] = {
NOTE_A4, NOTE_B4,NOTE_C3 };
void setup() {
}
void loop() {
for (int thisSensor = 0; thisSensor < 3; thisSensor++) {
// get a sensor reading:
int sensorReading = analogRead(thisSensor);
// if the sensor is pressed hard enough:
if (sensorReading > threshold) {
// play the note corresponding to this sensor:
tone(speakerPin, notes[thisSensor], noteDuration);
}
}
}
When you upload this sketch, you should have a three-key keyboard.
Get Creative
Now that you’ve got the basics, think about making an instrument for one of your projects. For more background on musical structure, see these notes on tone and generating a melody.
Doing More with Sound Output
If you’re interested going deeper into using sound outputs using speakers, take a look at the Mozzi Libary to produce more complex sound. In a future session, you’ll learn to play and control .wav files from Arduino, which is based on the I2S Library that connect digital audio devices together.
This is an introduction to basic analog output on a microcontroller. In order to get the most out of it, you should know something about the following concepts. You can check how to do so in the links below:
Just as with input, there are times when you want greater control over a microcontroller’s output than a digital output affords. You might want to control the brightness of a lamp, for example, or the turn of a pointer on a dial, or the speed of a motor. In these cases, you need an analog output. The most likely things that you might want to vary directly from a microcontroller are lights, sound devices, or things controlled by motors. For many of these, there will be some other controller in between your microcontroller and the final output device. There are lighting dimmers, motor controllers, and so forth, most of which can be controlled using some form of serial digital communication. What’s covered here are simple electrical devices that can be controlled by a changing voltage. The Arduino and other digital microcontrollers generally can’t produce a varying voltage, they can only produce a high voltage or low voltage. Instead, you “fake” an analog voltage by producing a series of voltage pulses at regular intervals, and varying the width of the pulses. This is calledpulse width modulation (PWM). The resulting average voltage is sometimes called a pseudo-analog voltage. The graph in Figure 1 shows how PWM works. You pulse the pin high for the same length of time that you pulse it low. The time the pin is high (called the pulsewidth) is about half the total time it takes to go from low to high to low again. This ratio is called the dutycycle and the total time from off through on to off again is the period. The duty cycle in this case 50%, and the effective voltage is half the total voltage.
Figure 1. PWM with a 50% duty cycle has an effective voltage of 50% of the maximum output voltage. Over time, the voltage is on half the time and off half the time.
If you make the duty cycle less than 50% by pulsing for a shorter amount of time than you pause, you get a lower effective voltage as shown in Figure 2:
Figure 2. Graph of pulse-width-modulation (PWM) with a 33% duty cycle. Effective voltage is a third of the maximum voltage. Over time, the voltage is on one third the time and off two thirds of the time.
The period is usually a very small time, on the order of a few microseconds or milliseconds at most. The Arduino boards have a few pins which can generate a continuous PWM signal. On the Arduino Nano 33 IoT. they’re pins 2, 3, 5, 6, 9, 10, 11, 12, A2, A3, and A5. On the Arduino Uno, they’re pins 3, 5, 6, 9, 10, and 11. To control them, you use the analogWrite() command like so:
analogWrite(pin, duty);
pin refers to the pin you’re going to pulse
duty is a value from 0 – 255. 0 corresponds to 0 volts, and 255 corresponds to 5 volts. Every change of one point changes the pseudo-analog output voltage by 5/255, or 0.0196 volts.
Applications of Pulse Width Modulation
LED dimming
The simplest application of analogWrite() is to change the brightness of an LED. Connect the LED as you did for a digital output, as shown in Figure 3, then use analogWrite() to change its brightness. You’ll notice that it doesn’t change on a linear scale, however.
Figure 3. You can dim an LED with the same circuit as you used for digital output. Just use analogWrite() on the pin to which the LED is connected.
DC Motor Speed Control
You can vary the speed of a DC motor using the analogWrite() command as well. The schematic is in Figure 4. You use the same transistor circuit as you would to turn on and off the motor, shown in Figure 4, but instead of setting the output pin of the microcontroller high or low, you use the analogWrite() on it. The transistor turns on and off at a rate faster than the motor can stop and start, so the result is that the motor appears to smoothly speed up and slow down.
For more on DC motor control, see the following links:
Figure 4. Schematic of motor control with an Arduino, using a MOSFET. One terminal of the motor is connected to a high-current power supply and the other is connected to the MOSFET’s drain pin. The MOSFET’s source pin is connected to ground and its gate is connected to a microcontroller output pin. A protection diode’s cathode is attached to the source of the MOSFET, and the anode is connected to the drain.
Note: Filter circuits
Filter circuits are circuits which allow voltage changes of only a certain frequency range to pass. For example, a low-pass filter would block frequencies above a certain range. This means that if the voltage is changing more than a certain number of times per second, these changes would not make it past the filter, and only an average voltage would be seen. Imagine, for example, that your PWM is operating at 1000 cycles per second, or 1000 Hertz (Hz). If you had a filter circuit that blocked frequencies above 1000 Hz, you would see only an average voltage on the other side of the filter, instead of the pulses. A basic low-pass filter consists of a resistor and a capacitor, connected as shown in Figure 5:
Figure 5. Schematic: A basic low-pass filter. An LED’s anode is connected to voltage and its cathode is attached to one terminal of a capacitor. The capacitor’s other terminal is connected to ground. A resistor connects to the junction where the the LED and the capacitor meet. The other end of the resistor is connected to a microcontroller’s output pin.
The relationship between frequency blocked and the values of the capacitor and resistor is as follows:
frequency = 1/ (2π *resistance * capacitance)
A 1.5-kilohm resistor and a 0.1-microfarad capacitor will cut off frequencies above around 1061 Hz. If you’re interested in filters, experiment with different values from there to see what works best.
Servomotors
Perhaps the most exciting thing you can do as analog output is to control the movement of something. One simple way to do this is to use a servomotor. Servomotors are motors with a combination of gears and an embedded potentiometer (variable resistor) that allows you to set their position fairly precisely within a 180-degree range. They’re very common in toys and other small mechanical devices. They have three wires:
power (usually +5V)
ground
control
Connect the +5V directly to a 5V power source (the Arduino’s 5V or 3.3V output will work for one servo, but not for multiple servos). Ground it to the same ground as the microcontroller. Attach the control pin to any output pin on the microcontroller. Then you need to send a series of pulses to the control pin to set the angle. The longer the pulse, the greater the angle.
To pulse the servo, you generally give it a 5-volt, positive pulse between 1 and 2 milliseconds (ms) long, repeated about 50 times per second (i.e. 20 milliseconds between pulses). The width of the pulse determines the position of the servo. Since servos’ travel can vary, there isn’t a definite correspondence between a given pulse width and a particular servo angle, but most servos will move to the center of their travel when receiving 1.5-ms pulses. This is a special case of pulse width modulation, in that you’re modifying the pulse, but the period remains fixed at 20 milliseconds. You could write your own program to do this, but Arduino has a library for controlling servos. See the Servo lab for more on this.
Pulse width modulation can generate a pseudo-analog voltage for dimming and motor control, but can you use it to generate pitches on a speaker? Remember that you’re changing the duty cycle but not the period of the signal, so the frequency doesn’t change. If you were to connect a speaker to a pin that’s generating a PWM signal, you’d hear one steady pitch.
If you want to generate a changing tone on an Arduino microcontroller, however, there is a tone() command that will do this for you:
tone(pin, frequency);
This command turns the selected pin on and off at a frequency that you set. With this command, you can generate tones reasonably well. For more on this, see the Tone Output lab.
As a summary, Table 1 below shows the ranges of values for digital input/output and analog input/output, which have been discussed in Digital Input & Output, Analog Input, and this page.
Digital
Input
(Digital Pins)
0 [LOW] or 1 [HIGH] (2^0)
0V or 3.3V (newer microcontrollers)
0V or 5V (older microcontrollers)
Output
(Digital Pins)
0 [LOW] or 1 [HIGH] (2^0)
0V or 3.3V (newer microcontrollers)
0V or 5V (older microcontrollers)
Analog
Input
(Analog Input Pins)
0 ~ 1023 (<210)
3.3 / 210
Output
(Digital PWM Pins)
0 ~ 255 (<28)
3.3 / 28
Table 1. The Ranges of Values for Digital/Analog Input/Output