Cell PhonesComputersConsumer ElectronicsGraphic Design & Video EditingHome Theater & AudioIndustrial TechnologyInternet

The Ultimate Arduino Tutorial

Updated on March 28, 2017

What is Arduino?

The Flagship UNO

Arduino is a brand for a family of simple micro-controller boards, which are like cut-down computer processors, meant for rapid execution of very specific tasks, related to electo-mechanical and mechatronics systems. Basically, you use these things to interface with sensors, motors, lights, switches, other digital electronics and to make decisions based on inputs regarding how to produce outputs (for example, if the ambient temperature is greater than 30 degrees C, turn on a cooling fan). Many 3rd parties have made electrically identical clones which are extremely cheap, making micro-controller based projects extremely economical and popular with people of any economic demographic.

To access the micro-controller's features, the chip itself is soldered onto boards which expose all of the chip's pins. The Arduino family of boards vary in form factor (size/shape) as well as feature-set, in case you want to scale your project up or down in complexity. Because there are so many options I will be focusing on the most popular chip used in the UNO and NANO, which is the ATMEGA328P. That's just the name of the "CPU" so to speak but it's common to the UNO and NANO. Thus the features are the same and so are the software commands need to implement the many features.

I really like the NANO because it's super compact and breadboard friendly so whenever I reference the board itself I will reference the NANO but keep in mind the pin layout is almost identical on the UNO so you can use this guide for both (and even on some other similar boards like the Leonardo, with some exceptions).

As a university student we used older PIC micro-controllers to do some pretty cool things and since then I always knew I wanted to get into micro-controllers and all sorts of exciting projects that were possible with them. Problem was, I didn't really know which chip to buy, how to communicate with the hardware, whether I needed to learn some exotic assembly language, etc. When I stumbled on Arduino, it was like the holy grail. Not only did it have all the hardware features I could ever want (and some I didn't even know I wanted), but it used a compiler based on C, which I already knew and which is very commonly taught. It also requires nothing more than a USB cable to upload code to the chip so no fancy hardware necessary. Aside from the board, all the software is free and open source and with ample example programs provided to you by default, the learning curve is quite low so as I said, this was like magic. Just buy a board and jump right into things!

This the 328 microcontroller
This the 328 microcontroller

ATMEGA328P Specs and Features

As I said, this chip, which I will refer to simply as the 328, is the heart of the most popular Arduino board. Everything else you'll see on the board is just supporting circuitry or headers coming from the chip's many pins so you can easily connect to them with wires. The 328 is where all of the work is done. We'll begin by simply listing the specs and features so you can see, at a glance, what the chip is capable of. You may be inclined to be overly-critical of some of the more familiar numbers below but believe me: Once you understand at a deeper level why these specs are what they are, you'll have an appreciation for why we all don't just use super-fast computer-like boards, such as the Raspberry Pi, a popular competing product. So without further ado, the specs:

16MHz Clock Speed (16 MIPS throughput)

8 Bit Processing

5V Supply (3.3V Minimum)

40mA Maximum Current Draw

32KB Program Space (for your code)

1KB EEPROM (for saving stuff at runtime, permanently)

2KB RAM (for all your runtime variables and calculations)

0.1 uA - 19mA Current Consumption Range

You'll note that these values are rather unimpressive on the scale you're familiar with. If you own a PC, you're used to GigaHertz and GigaBytes and 64 bits. Try to suppress the urge to be overly critical of these "pathetic" figures. Don't forget that your car and the space shuttle run on similarly "pathetic" computers. Why? Because sometimes you can do a job better and faster with less if the hardware is specifically designed for the task at hand. Remember the Super Nintendo Entertainment System (SNES)? The chip that ran that game console was far less powerful than the 486 computer that ran Windows 3.1. However, if you try to emulate a Super Nintendo on a 486 computer, the 486 can't cope. It's unplayable. This is why computers are good for everything and anything because they are un-specialized but a micro-controller is good for only 1 thing: Controlling electro-mechanical systems and it does this far more efficiently than a computer with superior speed and memory. When you consider the fact that an Arduino board is the size of your thumb, can run off a watch battery and requires no operating system, you start to realize how much you can actually do with 16MHz and 2KB of RAM.

Also noteworthy is something akin to a hard drive on the 328. The 32KB of program space can only be used to upload stuff you already know about: Code, reference tables, etc. but if you want to save data you don't know about... data that is generated at run-time, like sensor readings you later plan to graph in Excel, you can save that data on 1KB of EEPROM like a mini hard drive and read it later. How cool is that? If not for this tiny block of storage, you'd have no way of getting any feedback from your device without buying some sort of add-on (like a display screen)... unless you're keen on using the single LED on the board to blink morse code at you... and that can be kinda fun :)

Now, in addition to the 328's basic specs, here is a list of all the amazing features it has:

3 Timers (2 x 8 bit and 1 x 16 bit)

8 ADC Channels with 10 bit Resolution per Channel

6 PWM Channels on Select Digital Pins

Analog Out via PWM

Internal Temperature Sensor (a 9th channel on the ADC multiplexer)

USART

SPI

I2C

Watchdog Timer

2 Hardware Interrupts

6 Power Saving Modes

22 Digital I/O Pins

Analog Comparator


Please note that this is not an exhaustive list. The 328 has a few more tricks up its sleeve BUT these ones are ready to use, "out of the box" as it were, with little to no setup. The others are a bit more obscure and are beyond the scope of this article.

The 328's Features and Benefits EXPLAINED!

Now that you've seen a bunch of cryptic specs and features, let's go through what it all means and how to use it. For each feature I'm going to answer 3 questions:

What is it?

What's a common example?

How do I use it?

That way it won't just be technobabble. You'll actually finish reading this tutorial and be able to use the info in a real project the same day! As you read through these examples you will understand the procedure and the meaning behind the features being explained but keep in mind that you will need to do some work to adapt this to your own project. Often we will be modifying registers and it's too messy to explain all that in depth here for every possible scenario so what you'll have to do is use the example as a guide to know where to look in the 328's data sheet. In there you'll find maps of all the registers that are modified in the examples below. You simply need to find the tables for each relevant register and change the bits according to the modes and values YOU want. These examples are therefore meant to get you started in the right direction, as you'll already know which registers to look for and which tables to check.


1KB EEPROM

The EEPROM is a bit of an oddball because it's not really a feature but it's not obligatory like the RAM or the CPU either. It just sits there and you can use it or not use it. EEPROM stands for Electrically Erasable Programmable Read Only Memory. Basically it's non-volatile memory (it doesn't forget what you put in it when the power goes off, like RAM does), so you can store things in there and they will persist after the Arduino is powered off. This makes it the equivalent of a mini hard drive! Sure, 1KB isn't much of a hard drive but for Arduino projects, it's often more than enough for what you need.

For example, if your program is designed to detect the acceleration due to a fall, you might employ an accelerometer and try to analyze the signature of a fall by looking at the x, y and z acceleration values. Since a fall is a very transient, brief event, and since the sensor itself is falling, chances are you won't be able to collect any useful data from visual inspection in real-time. What you need to do is record the sensor data somewhere. Again, because your sensor is in a free fall, you likely won't have the sensor or Arduino board connected to anything to transmit the data via serial, SPI, I2C or the like. Really the best option is to keep everything completely portable and isolated and store the data locally, in the EEPROM. Simply sample the data at a certain frequency and write it to the EEPROM memory. Later on you can dump all the data off the EEPROM onto your computer via serial terminal and put that in excel to graph and look at it! EEPROM is really cool.

The code below reads a sensor, stores each value in the next available byte of EEPROM and then when it's full, the program reads all the values in order and prints them to the serial terminal. Be aware that each BYTE of EEPROM only holds a number up to 256 (a byte is an 8-bit number) so you either need to store numbers lower than that or use more than 1 byte of EEPROM to store a bigger number (or other data type). The EEPROM write function works by first specifying the byte number you are writing to in memory and then the value being written. Make sure you know how many bytes of memory your Arduino has (the latest Uno/Nano has 1024) and which byte you want to write to. Also here's another trick you'll need... Start your program with a delay of several seconds. Why? Because after you store the data, the next thing you want to do is read it but you have to load the "reading sketch" first and as soon as you plug in your USB cable to do that, the old program will start overwriting your data from the last session. If you put in a delay to start, you have a few seconds to hit the upload button before the old program can ruin your data.

#include <EEPROM.h>

int addr = 0;

void setup() {

  Serial.begin(9600);

}

void loop() {


  while(addr < 1024) {

    int val = analogRead(0);
    EEPROM.write(addr, val);
    addr++;
    delay(100);

  }

  addr = 0;
  while(addr < 1024) {

    Serial.println(EEPROM.read(addr));
    addr++;
    delay(100);

  }

}

3 Timers

What is it?

A timer is a device that uses the system clock as a means of counting with each clock tick. As the microcontroller does its business, the clocks act independently and will count up to whatever number you want, at the same speed as the rest of the chip is performing its calculations. Therefore each of the 3 timers can count independently, though their counting speeds are all based on the system clock speed so if the clock speed is 16MHz (which it is), the clock ticks 16 million times per second and each timer can potentially count 16 million numbers per second, if you want them to but since they are independent, they can do this whether the CPU is busy or not, and they can start/stop whenever you want them to. Timers work on the simple premise that all they do is count but you can use this feature to do a ton of different things. By picking the number they count to and telling them what to do when they reach that number (stop, count the other way, interrupt the program, etc.) you can do a wide array of tasks, from telling time, to driving a switched power supply. Of the 3 timers available on the Arduino chip, 2 are 8 bit, meaning they can only count to 256 and the 3rd one is 16 bit, meaning it can count to about 65 thousand.

What's a common example?

Say you want to program a machine to water your flowers once a week but in the meantime you want to collect data on soil moisture levels and ambient light levels. You need a timer to keep track of the watering schedule without interrupting the main program with a huge 1 week delay.

How do I use it?


This program uses Timer1 (16 bit) to count 1 second intervals and flash the LED on and off at that interval. In order to do that, the counter has to count to a certain number that takes 1 second to do. Because the clock runs at 16MHz and the timer can only count to 65000 or so (that's the size of a 16 bit number), it would normally count all the way to the end well before 1 second of CPU time. To fix this, we use a prescaler factor to drop the 16 MHz down to something lower, so that the counter ticks slower than the system clock. Then we can actually count 1 second.

There's really nothing to do in the loop function. Simply by setting up the timer, it begins running. The first thing to do is configure the 2 timer registers TCCR1A and B. First we set both to 0 so we can only enable the pieces we want. Then we set OCR1A, which is what determines the number we want to count to. If we prescale the clock by 1024, we get 16000000 / 1024 = 15625. Since this is the Timer's clock speed, 1 second contains that many ticks and since we want 1 second anyway, we want to keep this number as our upper limit to count to. Had we wanted 2 seconds, we'd simply double this number. Since we start counting from 0, the actual limit in this case is 15624. This goes into OCR1A. Now we set the timer "mode" by WGM12. That's because the timer can do a lot of things but all we want it to do is count to a maximum number and trigger an interrrupt. This is called CTC mode and is selected through WGM12. Then CS10 and CS12 set the prescaler to 1024 as discussed above. TIMSK1 is the register we need to configure so that interrupts will be enabled and with that, we're done. The timer will automatically start counting and once every second it will reach the target value in OCR1A, interrupt and "toggle" the LED pin.

void setup() {
  
    pinMode(13, OUTPUT);
 
    cli();          
    TCCR1A = 0;     
    TCCR1B = 0;     
 
    OCR1A = 15624;
    TCCR1B |= (1 << WGM12);

    TCCR1B |= (1 << CS10);
    TCCR1B |= (1 << CS12);

    TIMSK1 |= (1 << OCIE1A);
    sei(); 
}

void loop() {


  
}

ISR(TIMER1_COMPA_vect) {
  
    digitalWrite(13, !digitalRead(13));
    
}

8 ADC Channels with 10 bit Resolution

What is it?


An ADC is an Analog to Digital Converter, which basically means it takes in variable information in the form of a voltage, which could be anything from 0 to 5 volts, and converts it into a digital 10 bit number from 0 to 1023. The size of the number then represents the size of the signal that was read, where 1023 = 5V and 0 = 0V. This is extremely useful for reading sensors of all kinds. There are a ton of sensors that convey what they are sensing by reporting back a voltage from 0-5V. By being able to read this voltage and turn it into something the micro-controller can understand and manipulate mathematically, like an integer, you can manipulate what your program does based on input from the environment.

The ADC multiplexer also has a built-in band-gap reference voltage of 1.1V. All this means is that you have a built-in option to change your measurement range to 1.1V at any time to get better resolution for small measurements that you know won't go over 1.1V. Just keep in mind that after you switch to this mode, you have to wait a bit for the voltage reference to drop from its default of 5V or your readings will be wrong. A simple delay can accomplish this.

What's a common example?


Suppose you want to measure the rate at which you're getting fatter. You could put on a belt containing strain gauges (sensors that detect change in length) and connect the sensor to an amplifier, which then connects to one of your ADC pins. With every inch of lard you add to your waist, the voltage from the strain gauge will increase and become amplified. The ADC pin will then detect the increasing voltage and convert it to an increasing integer. Make sure your code can handle overflowing the 10 bit range of the ADC!

How do I use it?


This is really easy. Analog pins are always inputs and don't need to be configured in setup. However unlike digital pins that are identified by only their number, analog pins need to be called out by their full name written on the board (such as A3, not just 3). Any time you want to measure the voltage on an analog pin, simply use the code below and then take the integer stored in "value", divide by 255 and multiply by 5. This will give you the analog voltage on that pin at the time it was measured. In the example below the A0 pin is being measured. Just for the heck of it, let's calculate the voltage and print it to the serial terminal too.

int value;
double voltage;

void setup() {

  Serial.begin(9600);

}

void loop() {

  value = analogRead(A0);
  voltage = value/255*5;
  Serial.println(voltage);

}

6 PWM Channels

What is it?

PWM stands for Pulse Width Modulation and is basically an alternating square wave of a fixed frequency. The square wave has a high level and a low level. For any given cycle (1 wave in duration), the amount of time the wave is high vs the amount of time the wave is low, determines the "duty" or the ratio of high time to low time. This is useful because it provides a means of timing events that need to switch between different states. A power supply that takes a high voltage and produces many lower voltages from it, works on such a principle. The PWM duty tells the power supply how much to drop the voltage because the duty represents a ratio, so a 50% duty is the same as saying, I want 50% of the maximum voltage.

For everything you ever wanted to know about PWM, check the video at the top of the page.

What's a common example?


You want to learn how to use an oscilloscope so you use your Arduino nano to provide a test signal to measure. You configure a PWM capable digital pin as output and issue the analogWrite() command with different duty values, confirming the waveform changes shape as expected on the scope screen.

How do I use it?

Extremely simple. After picking a digital pin that supports PWM (D3, D5, D6, D9, D10, D11), just use the following code to set the pin (D9 in this case) as an output and then produce the PWM. The "duty" parameter dictates the "on time" as a ratio to the total pulse interval. Thus a 25% duty cycle would be 63/255 so "duty" would be 63. See below:

int duty = 0;

void setup() {

  pinMode(9, OUTPUT);

}

void loop() {

  duty = 63;
  analogWrite(9, duty);

}

Analog Out

What is it?

Analog Out is the ability for a digital source like an Arduino micro-controller to produce an analog signal to drive something external, like headphones, which produce sound from analog voltage signals. This is extremely valuable because lots of electro-mechanical devices actually require analog signals to work properly. Only digital electronics can and prefer to work using discrete voltage levels like 0 or 5V. Everything else from motors to speakers and radio transmitters require an arbitrary voltage pattern to work as intended. Therefore there must be special circuitry which can create these analog signals from digital information in your program.

What's a common example?

Suppose you want to create a power inverter. For that you need to generate a sine wave, which is an analog voltage with a 170V peak and frequency of 60 Hz. You need to produce this wave with digital logic so a DAC (Digital to Analog Converter) is required to output the analog voltage that makes up the sine wave.

How do I use it?

Well first of all, there is no "analog out" on an Arduino board but I'm not trying to trick you or anything. The hardware is almost all there. You just have to add a simple low-pass filter to one of the digital pins, drive it with PWM and boom - analog out capabilities. So to do this, first pick a digital pin, say D9, and connect a series resistor and a parallel capacitor to the pin. There are many tools you can google which will help you choose the values but for the sake of argument let's say 10000 ohms and 10 uF. Once you've connected these 2 external elements, try the code below and then just vary the value of "duty" to increase or decrease the output voltage from 0 to 5V (the example below gives 2.5V). The voltage you get will be: 5 x (duty / 255) so change the value of duty accordingly in your code.

int duty = 0;

void setup() {

  pinMode(9, OUTPUT);

}

void loop() {

  duty = 127;
  analogWrite(9, duty);

}

Internal Temperature Sensor

What is it?

The Arduino chip has a built-in temperature sensor, which measure the temperature inside the chip. This is useful when you want to add a layer of protection to your board, to save it from damage due to drawing too much power from it or from operating it at high power in a relatively hot environment. You can also use it to get an approximate guess of the ambient temperature if you put the Arduino to sleep for several minutes and then measure the temperature immediately when you wake it up.

What's a common example?


You are using an Arduino board in your car to measure various under-hood sensors, which you drive in Arizona in the summer. Temperatures inside the car can melt your face off in a hurry if you leave the car derelict in the open sun. The internal temp sensor can act as an alarm to notify the driver of unsafe operating temperatures or can throw the chip into one of its sleep modes to prevent additional heat generation from damaging the micro-controller.

How do I use it?

This block of code will check and print the chip temperature to the serial monitor. To make this useful for ambient temperature checks, you would have to execute this immediately after your board had been sitting unpowered for several minutes in the environment of interest. As soon as the board turns on, the 328 will heat up and throw off your ambient temperature value so check this right away.

void setup() {
  
  Serial.begin(9600);

}

void loop() {

  unsigned int wADC;
  double t;

  ADMUX = B11001000;
  ADCSRA |= _BV(ADEN);  

  delay(50);          

  ADCSRA |= _BV(ADSC);
  while (bit_is_set(ADCSRA,ADSC));
  wADC = ADCW;
  t = (wADC - 324.31 ) / 1.22;

  Serial.println(t);
  delay(1000);
}

The first several lines of the loop function configure the ADC multiplexer to read from channel 8, which is where the temperature sensor is. It also drops the reference voltage from 5V to 1.1V to provide better resolution. Then the temperature 't' is calculated by adjusting the raw measurement wADC by an offset (324.31) and a gain (1.22). These are default values and might be wrong for your board so use a thermometer to check against and adjust the offset until the program output matches the thermometer. Then if the value is still off at a completely different temperature, you'll have to adjust the gain as well. Play with these values until the sensor reads within +/- 2 degrees C at all temperatures. Also, if you want to make the code more compact and don't need to print the temperature to the serial monitor, just cut out everything in the setup block and cut out the Serial.println(t) from the loop block.

Arduino Car !

USART

What is it?

USART is basically serial communication... the sort that is required for USB devices to talk to the computer. When you upload a program to your Arduino board it uses the USART to convey the data from the computer to the chip and vice versa when the chip wants to report data from the code you are running, provided you set up your code to do so. Think of it as a serial communication interface.

What's a common example?

You've just purchased a magnetometer chip, which gives you a heading in degrees based on the magnetic poles of the Earth. You want to get live readings from the sensor to ensure that it is working correctly and that there is no interference throwing it off. Therefore you issue print commands in your code to display the magnetometer readout twice per second on the serial terminal which is integrated into the Arduino compiler. This is in lieu of obtaining and setting up a portable display screen or similar output device.

How do I use it?

The Arduino language allows you to set up serial communication very easily to send data to your PC. You can also read user input from the serial terminal. And there's no setup at all when you simply want to use it to upload code to the chip since that is all handled by the Arduino software when you hit the upload button. The following code is the most basic starting point for ANY project, as it provides you with the most rudimentary feedback from your code, without needing to hook up graphical displays or other output devices. It also has basic user input capabilites, as it requires you to type the letter X to the Arduino board in order to get an analog measurement value from it. In this way it satisfies the most universal need for input and output capabilities by virtue of the USART.

Take note that once you upload this code, you need to click on the serial monitor in your compiler, select the baud rate that matches your code (9600 in this case) and then close and open the monitor again. Then you will need to type X in the bar at the top and hit enter to see if the output works. If the baud rates don't match you will see garbage come out.

void setup() {

  Serial.begin(9600);
  
}

void loop() {

  byte input = Serial.read();
  if(input == 'X') {
    int sensorValue = analogRead(A0);
    Serial.println(sensorValue);
  }
  
}

SPI

What is it?

It's a communications protocol that allows you to control 1 or more peripheral devices using your Arduino. Many sensors and displays require this protocol to transfer data back and forth with the Arduino. It is simple in execution and fast but eats up a lot of separate wires on your 328.

What's a common example?


You want to create a user interface screen for a multi-purpose battery charger that allows you to enter all your battery parameters and select your charge or discharge mode. You want to display real-time sensor data on another screen at the same time. You could use the SPI protocol to access both displays according to their chip select pins so that each can display different info at essentially the same time and sharing most of the same connection points.

How do I use it?

At a minimum you need to connect matching wires between your Arduino and peripheral device for: VCC, GND, MISO, MOSI, SS (or CS), SCK. If you're only driving 1 device sometimes you can skip the SS/CS pin and if the device is only capable of slave operation, sometimes you can skip the MISO pin too. These pins can be arbitrarily assigned to different I/O pins on your Arduino board via your sketch, which will run SPI in software mode, or you can use the predefined hardware pins on the ICSP header, which will run SPI in hardware mode (faster). The ICSP pinout will tell you which pin does what. With regards to your sketch, you'll need to include the SPI.h library and whatever device-specific library is required to interface with it. Then you'll need to issue some sort of initialization command, which varies across devices but usually defines the aforementioned pin locations if running in software. Below is a trimmed down sample sketch to drive an SPI OLED display. This program is going to appear later in the I2C section so have a close look at how they differ.

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// If using software SPI (the default case):
#define OLED_MOSI   9
#define OLED_CLK   10
#define OLED_DC    11
#define OLED_CS    12
#define OLED_RESET 13
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 

void setup()   {                

  display.begin(SSD1306_SWITCHCAPVCC);

  display.display();
  delay(2000);
  display.clearDisplay();

  display.drawPixel(10, 10, WHITE);
 
  display.display();
  delay(2000);
  display.clearDisplay();

}


void loop() {
  
}

I2C (I "squared" C)

What is it?

Another communications protocol similar to SPI. The difference is, fewer wires are needed and you can connect far more devices by sharing the same 2 wires, whereas SPI requires more wires for additional devices. The tradeoff is that I2C is slower than SPI. Some devices come in both SPI and I2C flavours such as OLED displays.

What's a common example?

You purchase an IMU (Inertial Measurement Unit) sensor, which is a single board solution containing 3 different sensor chips: 1 magnetometer, 1 gyroscope and 1 accelerator. While this may appear to be 1 sensor from the outside, it is actually 3 as far as your code is concerned. You would use the I2C protocol to communicate with the specific hardware functions of each chip because I2C allows you to gang multiple devices on the same data and clock lines, using software addressing to distinguish and enable 1 device at a time on the same shared pipeline.

How do I use it?

Whatever device you are running from your 328 will have its pinout labelled (I would hope). You only need to find 4 pins: VCC, GND, SDA, SCL. Sometimes VCC will say 5V or 3.3V, but whatever voltage it takes, there's a pin on your Arduino to supply it so just use the appropriate power pin. GND will always be the same and you need that, SDA is analog pin A4 and SCL is analog pin A5. Just match up all 4 pins on both your Arduino board and your device. Now as for the code... it completely depends on what sort of device you are running so there's no universal answer. The only universal part of it is that you'll need the Wire.h library and you'll need to issue some sort of initialization command, whether it be native to the device's library, or a simple wire.begin() or whatever. I can't predict what form your code will take because it varies with the device but if you look at the below example, it's a stripped down program that drives an OLED display. Pay particular attention to the fact that it calls the wire library, it identifies the device by its HEX address (0x3D) and prints a single pixel to the screen using commands that are native to that OLED device. I2C may not have universal execution but it's not too hard to figure it out if you just read up on the device you're trying to run.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 

void setup()   {                

  display.begin(SSD1306_SWITCHCAPVCC, 0x3D);  
  display.display();
  delay(2000);
  display.clearDisplay();

  display.drawPixel(10, 10, WHITE);

  display.display();
  delay(2000);
  display.clearDisplay();

}


void loop() {
  
}

Watchdog Timer

What is it?

It's a special timer that monitors the CPU and looks for regular status updates to make sure everything is ok. If the code malfunctions and hangs the CPU or if there's a hardware failure causing a similar fault in execution, the watchdog timer will time-out and is capable of resetting the Arduino board. Because the watchdog runs independently of the CPU, it has its own separate oscillator and can also wake up the CPU when it's sleeping. Watchdog timers are very useful when you are running code for long periods of time with sleep intervals, or when you expect uncertain program behaviour may occur and don't want the unit to hang forever. When your code doesn't hang, it needs to "kick" the dog in order to prevent it from resetting your board, which basically amounts to sending an "OK" signal to the dog so it will not time out.

What's a common example?

You are attempting to use 1 Arduino board to communicate with another. The second board is connected to various sensors in various states of readiness. At program initialization, you want to check the second board and all of its sensors to ensure they are ready to transmit data before you continue with your code. Each check could potentially hang your program, if the sensor is not properly powered on and initialized. You want to ensure that your main program doesn't hang forever on a non-responsive sensor but instead resets itself after a certain amount of time. You program the watchdog timer therefore to wait only for so long for all the sensors to report ready. If any one of them hangs while being polled, the watchdog will time out and reset the main program so that it goes through its checks again. If the checks are all successful, the watchdog will be "kicked" so as to prevent a program reset and the main program can proceed as planned.

How do I use it?


The code below is a basic example of how to use the watchdog timer to interrupt and then reset the 328 due to an infinite loop (which can essentially hang the program forever).

It works by first resetting and then configuring the watchdog timer. The 2 registers being assigned binary values both set the WDT register bits so that the watchdog will time out in 8 seconds, enable interrupts and enable reset. In the loop function, we then enter a loop which simulates "correct" program execution, where the dog is kicked after each loop via wdt_reset(), indicating the program is working correctly. This lasts for about 5 seconds. After that, the program enters a second loop, this time with no exit condition. This simulates faulty execution resulting in an infinite loop. After 8 seconds of this loop, the dog doesn't get kicked so it automatically interrupts the infinite loop, which is noticeable by the LED being set to LOW. Finally the dog resets the 328 and the code starts all over again. This is just a prototype for your own personal application but it demonstrates both correct and erroneous program execution, and how the watchdog timer works when configured correctly. You can build your code around this example, simply changing the WDTCSR register bits according to the 328 datasheet, to get your desired watchdog time out and time out behaviour.

***NOTE***

Some Nano boards (the clones in particular) don't work with the WDT. What happens is, the program will execute fine, the WDT will interrupt and reset the board fine when it hangs but then the board will not restart. This is because of a flaw in the boot loader that was never fixed. The solution to this problem is to change the boot loader from the Nano version to the Uno version, essentially making the Nano think it's an Uno. Since they are so similar this is not an issue in 99.9% of cases but in order to make the change you need to have 2 boards, an Uno acting as programmer and the board to be programmed. You also need to connect the ICSP header wires between the 2 boards and upload a special sketch that will burn the new boot loader.

#include <avr/wdt.h>

void setup() {

  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH); 

  cli();
  wdt_reset();

  WDTCSR |= B00011000;
  WDTCSR = B01101001;

  sei();

}

void loop() {

  int i = 0;

  while(i < 5) {

    delay(1000);
    wdt_reset();
    i++;

  }

  while(1)
    delay(50);

}

ISR(WDT_vect) {

  digitalWrite(13, LOW);

}

2 Hardware Interrupts

What is it?

An interrupt is a feature that is capable of stopping code execution, no matter where it happens to be, so that something else can be done in its place. This is most often needed when you are waiting for something to occur but you don't know when it is going to happen and you can't afford to wait to check it in a looping structure. If you are monitoring a sensor that is usually not active and you want to take immediate action when its state changes, you need an interrupt so that when the sensor gives the right signal, you stop what you're doing and execute a little piece of code to deal with the sensor reading and then resume where you left off. Any pin can be configured as an interrupt but there are 2 special hardware interrupts assigned to specific pins. These are much faster because they are hardwired into the chip and don't rely on software to emulate the interrupt feature.

What's a common example?

You want to measure the RPM of a motor based on an optical encoder attached to the shaft. The interval of the pulses from the encoder varies with motor speed so you cannot simply check for the pulses at regular intervals or you may miss them. Therefore you get the pulses to tell the program when they arrive by connecting the encoder line to one of the hardware interrupt pins. When the pin detects a pulse, it prompts the program to halt, check the time and do a quick calculation to measure the duration since the last pulse. The inverse of this duration is the speed of the motor.

How do I use it?

There is an Arduino function called attachInterrupt(pin, ISR, mode) which will first designate a specific pin as an interrupt pin, which routine to run when the interrupt occurs and how the interrupt will be triggered (for example when the pin changes state from HIGH to LOW). The routine you run is called an ISR. You can give it a name of your choosing and simply reference it in the attachInterrupt line. The contents of the ISR should be EXTREMELY SHORT. This is because interrupting your code stops everything until the ISR is done. If the main code was doing something time sensitive, you want to resume that code ASAP, so often ISRs simply change a boolean flag and let the main code handle it in the next loop by checking the flag and doing something. In the example below, a flag is toggled, which prompts the main program to turn on the nano's LED. Another thing is that each Arduino board has different hardware interrupt pins and IDs. On the nano pins 2 and 3 are INT.0 and INT.1 respectively. Make sure you reference the latter in your attachInterrupt function (0 in the example). However when you setup the pin as an input, you need to use the normal pin ID (which is 2). Just be careful of that. Also note that we use a special input mode in the pin setup called INPUT_PULLUP. This is like INPUT except that it forces the pin to stick at 5V unless a real signal drives it to some other level. This is important when you're not sure of the quality of the input on a given pin. Sometimes if a signal is absent or weak, the pin can float around due to static or electric fields in the air and it can display any value randomly. This could cause all sorts of false interrupts and other wonky behaviour so particularly with interrupts set up to detect pin level changes, you want to use the INPUT_PULLUP mode.

boolean turnon = false;

void setup() {

  pinMode(2, INPUT_PULLUP);
  attachInterrupt(0, interruptRoutine, CHANGE);

}

void loop() {

  if(turnon == true) {
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);
  }

}

void interruptRoutine() {
  turnon = !turnon;
}

Power Saving States

What is it?

Basically all the features on the Arduino chip require power to operate. The more features you use, the more power is needed. Even when features are not being used, the fact that they are enabled means they waste power. You can turn various features off, including the CPU itself, leaving the chip in various states of low power. The main difference between low power modes and disconnecting the power altogether is that in every low power mode there is always something left on, which is capable of waking up the board and restoring full power. The power consumption of these minimal keep-alive components is extremely small however and can preserve portable battery sources for days/weeks/months where they may have died in a matter of hours otherwise.

What's a common example?

You are using an Arduino board to control a servo-mechanism which aims solar panels at the sun. Obviously at night, there's no need to control the servo so you want the board to go into a low-power state so it lasts longer and doesn't waste power. You program the board to enter a power saving state when the panel voltage remains lower than a certain value for a certain amount of time, indicating it is night. When the voltage rises the next morning, you direct that voltage to the gate of a MOSFET, which grounds out a hardware interrupt pin. Since that pin will be normally pulled up to 5V, the sudden drop to 0V due to the transistor gate being energized, will activate the interrupt system and bring the 328 out of sleep.

How do I use it?


The following code will demonstrate 1 of 6 different sleep modes, called Power Down. This is not only a commonly used sleep mode but it also saves the most power by shutting down just about everything on the board (except for the WDT and the 2 hardware interrupts of the 328). As such, the WDT and interrupt pins are the only way to wake from this sleep mode. Below you will see that the code first defines pin 2 as INPUT_PULLUP, which is an input where the voltage level is normally high (5V). Pin2 is 1 of the special hardware interrupt pins. See that section for more details. Next, we enable sleep mode capabilities in general, check to make sure the interrupt pin is high (so we can start checking for it to drop low), "attach" the interrupt so that it is actively being monitored and then set the sleep mode to Power Down mode. Then we just need to actually put the 328 to sleep. You may notice that the next line disables sleep but remember... the 328 is already sleeping at this point so program execution halts and that instruction is not executed until you wake it up. At this point, everything is frozen and we are just waiting for pin 2 to wake the 328 up. When it does, it will disable sleep mode and detach the interrupt. This is important because otherwise the CPU could get stuck in a loop for as long as pin 2 remains grounded and it's also possible the 328 could go to sleep before the interrupt system is ready to wake it again. Timing is everything so you need to build in fail-safe code to ensure nothing weird happens if conditions are not as you expect. With this sketch you can test sleep mode by shorting pin 2 to ground for a brief instant after the LED goes out. The LED should come back on indicating that the 328 has woken successfully.

One more thing: Don't do what I did and turn on LEDs in your interrupt routine. I did that for demonstration purposes but it is bad form to do anything in an interrupt routine that isn't lightning fast. In real life, other events (other interrupts or critically timed functions) may be waiting to occur while the code is interrupted so you want to keep it short and quick.

#include <avr/interrupt.h>
#include <avr/sleep.h>

byte pin2_interrupt_flag = 0;

void setup() {
  
  pinMode(2, INPUT_PULLUP);
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);

}

void loop() {

  delay(5000);
  sleep_enable();
  if(digitalRead(2))
    pin2_interrupt_flag = 0;
  if(pin2_interrupt_flag == 0)
    attachInterrupt(0, pin2_isr, LOW);




  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  cli();
  sleep_bod_disable();
  sei();
  digitalWrite(13, LOW);
  sleep_cpu();

  sleep_disable();

}

void pin2_isr() {
  
  sleep_disable();
  detachInterrupt(0);
  pin2_interrupt_flag = 1;
  digitalWrite(13, HIGH);
  
}

22 Digital I/O Pins

What is it?

This is the most basic function of any microcontroller. These pins can be inputs or outputs and only recognize 2 states: High and Low. High is the voltage corresponding to logic High (in this case 5V) and low corresponds to logic low voltage (0V). As an input, these pins are capable of returning a 0 or a 1 depending on whather they detect a 0V or 5V signal on the pin. As outputs you can commend them high or low and they will express a voltage of 5V or 0V on the pin. This is extremely useful for activating switches or indicating state change to other digital devices that have logic level inputs.

What's a common example?

You're designing a technicolour toilet seat with LEDs so you can pee in the dark AND have a seizure. Assuming each LED of the same colour were ganged together, you could activate them by sending a single digital HIGH signal to a logic-level transistor, which controlled the flow of electricity from a power supply to the LEDs. Each colour would have its own transistor, gated by a different digital I/O pin. The Arduino code would simply decide which digital pin to set HIGH or LOW, at what time and for how long, in order to produce a choreographed light show.

How do I use it?

You can set a given pin to input or output in the setup() {} block and then depending on which assignment you gave that pin, you can either command the logic level of that pin or you can read what the logic level is on that pin. For example to set pin number D8 on the nano to an input and D9 on the nano to an output and then read the level of D8 and set D9 HIGH, do this:

int pin8level;

void setup() {

  pinMode(8, INPUT);
  pinMode(9, OUTPUT);

}

void loop() {

  digitalWrite(9, HIGH);
  pin8level = digitalRead(8);

}

Analog Comparator

What is it?


A comparator, as the name suggests, compares 2 voltage inputs to each other and outputs a digital state (often represented by a logic level voltage) that depends on which of the inputs is greater. If for example, the first input is of a greater voltage than the second, the output may be "HIGH", and stick at whatever voltage HIGH represents. If that first input then falls below the second, even slightly, the output suddenly swings "LOW" to whatever voltage that represents (often 0V), in a step fashion with no smooth transition. Comparators are often used for logic operations, where you want a TRUE or a FALSE to indicate the magnitude of 1 input exceeding another. There are many applications for this sort of function.

What's a common example?

You are adding an indicator light to a battery charger, which will inform the user if and when the power supply voltage, controlled by a knob, has reached or exceeded the voltage of the battery being charged. Once the battery is connected to the charger, a wire connected to the positive terminal is connected to the D7 Arduino pin, while the charger output voltage is connected to D6. Thus when the user turns the dial and increases the charger voltage, the voltage on D6 increases until it exceeds the reference voltage on D7. Only at that point does the comparator return a HIGH result, triggering the indicator light on the charger to illuminate.

How do I use it?


If you examine the code below you will see that the execution is very straighforward and not much different than other 328 features we have enabled before. It amounts to configuring a single register in this case and changing the bits (according to the 328 data sheet) to enable the behaviour you want. In this case, we want to turn the comparator on, enable interrupts and enable them on a "rising edge". What this means is, when your D6 voltage reference exceeds your D7, the comparator returns a HIGH logic level which means the 0V line suddenly spikes up or "rises" to the high logic level. This is a rising edge. You can enable interrupts on a falling edge too but not in this example. As you might expect there is an interrupt routine as well, which gets called on that rising edge and in this case it causes the LED to turn on. This basic code template can be easily adapted to do whatever your application calls for. You may be confused as to why D6 and D7 are used to compare analog voltage inputs. All I can say is, the 328 works in mysterious ways and the internal wiring, like some complex plumbing in a building, is full of valves that can re-route the flow of electricity to different pins depending on the setup of its registers. D6 and D7 were specifically chosen to serve as + and - input channels for the comparator. Anyway, after you upload the code below, try touching pins D6 and D7 simultaneously with your finger. Run your finger back and forth and watch the LED flicker. What you're actually doing is shorting the pins across a large resistor (your finger) and causing the voltages to be almost identical. It confuses the 328, as it tries to figure out which voltage is larger and will flicker the light, as the comparator flips back and forth between LOW and HIGH.

void setup() {

 ACSR =
   (0 << ACD) |    
   (0 << ACBG) |   
   (0 << ACO) |   
   (1 << ACI) |   
   (1 << ACIE) |   
   (0 << ACIC) |  
   (1 << ACIS1) | 
   (1 << ACIS0);   

 pinMode(13, OUTPUT);
 
}

void loop() {
                  
}

ISR(ANALOG_COMP_vect ) {
  
 digitalWrite(13, !digitalRead(13));
 
}

The Board Layout

Of the Arduino product line, I like the Nano because it's got the full feature set of the 328 exposed but in a very small package, which is breadboard friendly, has native USB functionality for uploading your sketches AND a built-in power regulator so you can feed it a fairly arbitrary voltage without worrying too much about blowing up the board.

The Nano has many pins that are user-accessible, of which some deal with power, some with digital I/O, some with analog input and some with communications. Quite a few pins have more than 1 function, depending on the programming. For example many of the digital and analog pins are also used to communicate with peripheral devices, where applicable but unless the code you upload indicates that the board is to be used in that way, the pins will fulfill their primary function only. Even then, most pins you want to use, have to be set up first before they will actually become active. Let's have a closer look at the Nano's pinout.

First you've got your power pins. You have VIN (Regulated + voltage supply), 2 GND pins (Ground or 0V), 5V (a common logic level HIGH which can be used to supply the board itself OR supply other peripherals from the board), 3V3 (3.3V in or out, which is a common supply voltage for processors of all kinds) and 2 RST pins (Grounding this pin via a built-in push button will reset the program).

Next is all your digital I/O. These are denoted by the letter D followed by a number. The Nano has pins D2 through D13 but in reality, all the analog pins + TX and RX can also behave as digital pins bringing the total up to 22 (per the specs above).

Moving along we have the aforementioned analog input pins. These are appropriately prefixed by the letter A. You have A0 through A7 for a total of 8 and these are all multiplexed so only 1 can be checked at a time.

TX and RX are digital I/O pins that are used primarily for transmitting and receiving serial data. These pins can be used instead of the USB socket to communicate with the 328.

Then we have an unusual cluster of 6 pins which are collectively known as the ISCP header and are basically dedicated hardware lines for SPI communication with peripheral devices. It's convenient when you have to power an OLED display for example, as all the power and signal lines you need are provided.

The only oddball is the AREF pin, which is a voltage reference you can arbitrarily pick to be the maximum value for your analog input pins. So if you want your analog input pins to measure from 0 to 4V for some reason, this pin is where you set that upper voltage.

The Arduino Software Environment

In order to use your micro-controller board you have to upload "sketches" to it, which is just a fancy name for a program. The programming environment is free and easy to use. The low-level instructions that the chip understands are automatically converted from the high level and familiar C language that you will use to write your program. Therefore you don't need to know anything about the 328's assembly language. In addition to C, there are some other snippets from Java, C++ and native Arduino functions, which allow you to exploit all of the 328's potential. Don't let this scare you though. The Arduino functions are well documented in help pages on the net and are really dummy obvious in their syntax. All other code you'll write to dictate the variables, logic and looping structures are just ripped directly from C.

One little quirk is that all Arduino programs have to have 2 specific pieces to them: A setup block and a loop block. Aside from the fact that they have to physically be there in your code, you don't have to worry about why they're there. It's just a formality. You can write your entire program within the void loop() {} block if you like. That block WILL loop when it finishes so if you don't want that to happen you can just design your code in such a way that it doesn't end. A simple while(1){} will do that. However often times we do want the code to loop because we need to constantly sample and monitor the environment with sensors for example. So the loop function is handy and available for you to use.

Here's what a completely empty sketch that is capable of compiling and uploading (ie. it's a complete sketch) would look like:

void setup() {

}

void loop() {

}

Setup is meant to be the place where you configure your ins and outs, configure registers and basically prepare the board and chip functions to work as the program will later dictate. The loop function is meant to be the place where your code tells the pre-configured pins what to do and when. You don't strictly need to follow this philosophy but it's a good idea. Setup will always run first and only once. Loop, by design, will run continuously forever unless you do something to stop it.

Once you download the Arduino SDK and install it, the quickest way to jump into your first program is to open an example file such as BLINK. You can find it from the menu (File>Examples>01.Basics). Just delete the code inside the setup and loop blocks and replace it with your own. Remember to include all necessary libraries at the top and declare your global variables. If you don't know what libraries you might need, again, consult the example sketches pertaining to what you want to do. The libraries will be written there.

On the subject of libraries, even if you add the correct ones to your code, you may not actually have them on your computer. It's easy to google a library however and download it (often from github). Once you do, just dump the folder into the Arduino\Libraries folder in your Arduino installation. Restart the compiler and the library will be available as well as any example sketches.

Once you have finished writing your sketch, you can "Verify" (check mark button at the top left) and that will check your code for errors and then "Upload" the sketch to your board (arrow button next to "Verify"). However before you do that, first go to Tools in the menu and select your Board, Processor and Port. An example of the Board would be Arduino Nano. The Processor might be ATMEGA328 and the port... well that depends on which port you plug your USB cable into. To determine the correct port, go to your computer's CONTROL PANEL, go to SYSTEM, then HARDWARE, then DEVICE MANAGER and scroll down until you see PORTS. Now, simply plug (or un-plug and then re-plug) in your arduino board to any USB port and watch the list to see which new item appears. The COM number associated with the new item is your PORT number. Go back to the compiler and select that number, then upload. Wait a few seconds until AVR-DUDE says Thank you (lol) and you're done! The next time you power the board, the program you uploaded will execute.

Making Your Project Portable

For the Uno and Nano specifically, there is circuitry onboard to regulate the power supply so you don't have to worry TOO much about your supply voltage. To make your project mobile, you have the option of supplying the power via the USB socket (which means you'll need one of those power banks, commonly used to recharge phones), via the VIN and GND pins (which prefer a voltage of 9-12V) or directly through the 5V and GND pins (in which case you need between 4.5 and 5.5V). If you choose the 5V option rather than the VIN or USB options, you MUST provide a voltage that is very close to 5V or the board either won't work or will burn up because the 5V pin is NOT regulated.

In many cases, you'll already have a power supply available... perhaps for a motor or something else needing a decent amount of voltage. If it's within the 9-12V range, you're good already. If it's higher, cut it down a bit with a voltage divider or a zener diode. If you are powering multiple sensors, chances are they run on 5V too so you can share a common supply for all your electronics on the 5V pin.

If you must supply your board directly from a battery, try to pick a configuration that has a pack voltage between 9-12V. Seven AA alkalines will do. So will 3 lithium ion batteries in series. For that matter, any 12V lead acid is fine too but those are heavy and big.

Some Arduino boards have no regulating circuitry on them (to save space) so you must supply 5V. In this case you have only 2 options. Either build a voltage regulator yourself (voltage divider, regulator IC chip or zener diode) OR you could use Ni-Cd or Ni-MH batteries because the voltages just happen to be the right multiples for the job. Four cells of either chemistry will give you a bit more than 5V on a full charge and will remain steady at 4.8V until they are almost dead. Ni-MH has the advantage of a larger capacity. Ni-Cd has the advantage of greater longevity, durability and lower lifetime cost.

Why Use Arduino Instead of Raspberry Pi?

The Pi is an impressive piece of hardware, make no mistake. However there are times when all you wanna do is throttle a motor or make a thermostat for a small wine cooler.

Boards like the Pi are:

- More expensive

- Require an operating system (Linux in the case of the Pi)

- Require more power and more precise power management

- Are not as simple to make immediately useful for simple projects

- Are wasteful of resources for simple projects (inefficient)

Remember the Pi is a computer. The Arduino boards are not. Use computers for what they are good for like rendering video or connecting you to the internet. Using them to flip a switch is not only overkill but it's a lot of trouble just to get that sort of basic functionality to work. Consider this. If I told you to use your PC at home to turn on a lightbulb, could you do it? I couldn't (at least not in the same day). With an Arduino you could do this in about 5 minutes or less and for only a few bucks worth of parts that could fit in 1 hand.

Arduino is Funduino!!! (lame...)

Arduino boards really are super cool in an ironic sort of way. We've been surrounded by powerful computers for decades and what do we do with them? Look at pictures, surf the web, manage our taxes maybe? What a waste of processing power for such a pathetic yield of productivity!

When you pick up an Arduino, you're getting sh** done! You're making thermostats, motor controllers, drone guidance systems, power inverters and all sorts of crazy awesome stuff. Stuff you would have never been inclined or capable of doing with 50 lbs worth of PC sitting on your desk, what with its 16 GB of RAM and 3D graphics... so pick up one of these things and win a Nobel prize or something :D

Comments

    0 of 8192 characters used
    Post Comment

    No comments yet.

    Click to Rate This Article