HTML Other Lists


HTML also supports description lists.


HTML Description Lists

A description list is a list of terms, with a description of each term.

The tag defines the description list, the tag defines the term (name), and the tag describes each term:

Example

<dl>
  <dt>Coffee</dt>
  <dd>- black hot drink</dd>
  <dt>Milk</dt>
  <dd>- white cold drink</dd>
</dl>

Chapter Summary

  • Use the HTML <dl> element to define a description list
  • Use the HTML <dt> element to define the description term
  • Use the HTML <dd> element to describe the term in a description list

HTML Exercises

Test Yourself With Exercises

Exercise:

Add a list item with the text "Coffee" inside the <ul> element.

<ul>Coffee</ul>


HTML List Tags

Tag Description
Defines an unordered list
Defines an ordered list
Defines a list item
Defines a description list
Defines a term in a description list
Describes the term in a description list

For a complete list of all available HTML tags, visit our .


Give your next Arduino project bat-powers with the HC-SR04 Ultrasonic Distance Sensor that can report the range of objects up to 13 feet away. This is a good thing to know when you’re trying to save your robot from hitting a wall.

They are low power (suitable for battery operated devices), affordable, easy to interface and extremely popular with hobbyists.

What is Ultrasound?

Ultrasound is a high-pitched sound wave whose frequency exceeds the audible range of human hearing.

Ultrasonic Frequency Range Spectrum

Humans can hear sound waves that vibrate in the range of about 20 times a second (a deep rumbling noise) to 20,000 times a second (a high-pitched whistle). However, ultrasound has a frequency of more than 20,000 Hz and is therefore inaudible to humans.

HC-SR04 Hardware Overview

An HC-SR04 ultrasonic distance sensor actually consists of two ultrasonic transducers.

One acts as a transmitter that converts the electrical signal into 40 KHz ultrasonic sound pulses. The other acts as a receiver and listens for the transmitted pulses.

When the receiver receives these pulses, it produces an output pulse whose width is proportional to the distance of the object in front.

This sensor provides excellent non-contact range detection between 2 cm to 400 cm (~13 feet) with an accuracy of 3 mm.

Since it operates on 5 volts, it can be connected directly to an Arduino or any other 5V logic microcontroller.

Technical Specifications

Here are the specifications:

Operating VoltageDC 5V
Operating Current15mA
Operating Frequency40KHz
Max Range4m
Min Range2cm
Ranging Accuracy3mm
Measuring Angle15 degree
Trigger Input Signal10µS TTL pulse
Dimension45 x 20 x 15mm

HC-SR04 Ultrasonic Sensor Pinout

Let’s take a look at its pinout.

HC-SR04 Ultrasonic Distance Sensor Pinout

VCC supplies power to the HC-SR04 ultrasonic sensor. You can connect it to the 5V output from your Arduino.

Trig (Trigger) pin is used to trigger ultrasonic sound pulses. By setting this pin to HIGH for 10µs, the sensor initiates an ultrasonic burst.

Echo pin goes high when the ultrasonic burst is transmitted and remains high until the sensor receives an echo, after which it goes low. By measuring the time the Echo pin stays high, the distance can be calculated.

GND is the ground pin. Connect it to the ground of the Arduino.

How Does HC-SR04 Ultrasonic Distance Sensor Work?

It all starts when the trigger pin is set HIGH for 10µs. In response, the sensor transmits an ultrasonic burst of eight pulses at 40 kHz. This 8-pulse pattern is specially designed so that the receiver can distinguish the transmitted pulses from ambient ultrasonic noise.

These eight ultrasonic pulses travel through the air away from the transmitter. Meanwhile the echo pin goes HIGH to initiate the echo-back signal.

If those pulses are not reflected back, the echo signal times out and goes low after 38ms (38 milliseconds). Thus a pulse of 38ms indicates no obstruction within the range of the sensor.

HC-SR04 Ultrasonic Sensor Working - Echo when no Obstacle

If those pulses are reflected back, the echo pin goes low as soon as the signal is received. This generates a pulse on the echo pin whose width varies from 150 µs to 25 ms depending on the time taken to receive the signal.

HC-SR04 Ultrasonic Sensor Working - Echo reflected from Obstacle

Calculating the Distance

The width of the received pulse is used to calculate the distance from the reflected object. This can be worked out using the simple distance-speed-time equation we learned in high school. An easy way to remember the equation is to put the letters in a triangle.

Distance Speed Time Formula Triangle

Let us take an example to make it more clear. Suppose we have an object in front of the sensor at an unknown distance and we receive a pulse of 500µs width on the echo pin. Now let’s calculate how far the object is from the sensor. For this we will use the below equation.

Distance = Speed x Time

Here we have the value of time i.e. 500 µs and we know the speed. Of course it’s the speed of sound! It is 340 m/s. To calculate the distance we need to convert the speed of sound into cm/µs. It is 0.034 cm/μs. With that information we can now calculate the distance!

Distance = 0.034 cm/µs x 500 µs

But we’re not done yet! Remember that the echo pulse indicates the time it takes for the signal to be sent and reflected back. So to get the distance, you have to divide your result by two.

Distance = (0.034 cm/µs x 500 µs) / 2

Distance = 8.5 cm

Now we know that the object is 8.5 cm away from the sensor.

Wiring an HC-SR04 Sensor to an Arduino

Now that we have a complete understanding of how the HC-SR04 ultrasonic sensor works we can start connecting it to our Arduino!

Connecting the HC-SR04 to Arduino is very easy. Start by placing the sensor on your breadboard. Connect the VCC pin to the 5V pin on the Arduino and the GND pin to the ground pin. Now connect the trig and echo pins to digital pins #9 and #10 respectively.

The following table lists the pin connections:

HC-SR04 SensorArduino
VCC5V
Trig9
Echo10
GNDGND

When you are done you should have something that looks similar to the image shown below.

Arduino Wiring Fritzing Normal Mode Connections with HC-SR04 Ultrasonic Sensor
Wiring HC-SR04 Ultrasonic Sensor to Arduino UNO – Normal Mode

Library Installation

Triggering the ultrasonic sensor and measuring the received signal pulse width manually is a lot of work but luckily there are many libraries available to us. One of the popular libraries is the NewPing library. This is the library we will use in our examples.

The NewPing library is quite advanced. It supports up to 15 ultrasonic sensors at once and can output directly in centimeters, inches, or time periods.

This library is not included in the Arduino IDE, so you will need to install it first.

To install the library navigate to Sketch > Include Libraries > Manage Libraries… Wait for Library Manager to download the library index and update the list of installed libraries.

manage libraries

Filter your search by typing ‘newping’. Click on the first entry and then select Install.

newping library installation

Arduino Example Code

Here is a simple sketch that uses the serial monitor to display a distance measured in centimeters. Give this sketch a try before we start a detailed analysis of it.

// Include NewPing Library
#include "NewPing.h"

// Hook up HC-SR04 with Trig to Arduino Pin 9, Echo to Arduino pin 10
#define TRIGGER_PIN 9
#define ECHO_PIN 10

// Maximum distance we want to ping for (in centimeters).
#define MAX_DISTANCE 400	

// NewPing setup of pins and maximum distance.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

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

void loop() {
	Serial.print("Distance = ");
	Serial.print(sonar.ping_cm());
	Serial.println(" cm");
	delay(500);
}

Once the sketch is uploaded, open your serial monitor, set the baud rate to 9600 bps. Try pointing the sensor at objects lying around you. You should see the measured distance begin to stream by.

HC-SR04 Ultrasonic Sensor Arduino Distance Measurement Sketch Output on Serial Monitor
Output on Serial Monitor

Code Explanation:

The sketch starts by including the newly installed NewPing library.

#include "NewPing.h"

First the Arduino pins are defined to which the Trig and Echo pins of the HC-SR04 are connected. We have also defined a constant called MAX_DISTANCE. It will set a maximum distance where pings beyond that distance are read as no ping “clear”. MAX_DISTANCE is currently set to 400 [default = 500cm].

#define TRIGGER_PIN 9
#define ECHO_PIN 10
#define MAX_DISTANCE 400

After this, an instance of NewPing library named sonar is created.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

In the setup, we initialize the serial communication with PC.

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

In the loop, we simply call the ping_cm() function and print the result on the serial monitor. This function sends a ping and returns the distance in centimeters.

void loop() {
	Serial.print("Distance = ");
	Serial.print(sonar.ping_cm());
	Serial.println(" cm");
	delay(500);
}

Other useful functions in NewPing Library

There are a few useful functions you can use with NewPing object.

  • Above sketch returns the distance in centimeters. If you want result to be in inches, use sonar.ping_in() function.

    Serial.print(sonar.ping_in());
  • The sketch above only has a resolution of one centimeter. If you want to get the result in decimal form you can use NewPing in duration mode instead of distance mode. You need to change this line:

    Serial.print(sonar.ping_cm());

    with below line

    Serial.print((sonar.ping() / 2) * 0.0343);
  • There is a method called ping_median(iterations) in the NewPing library to improve the accuracy of your HC-SR04. This method takes multiple measurements instead of just one, discards out-of-range readings, and then averages the remaining readings. By default it only takes 5 readings but you can specify as many as you want.

    int iterations = 5;
    Serial.print((sonar.ping_median(iterations) / 2) * 0.0343);

Arduino Project – Contactless Distance Finder

Let’s create a quick project to demonstrate how a simple ultrasonic sensor can be turned into a sophisticated contactless distance finder. In this project we will be using a 16×2 Character LCD which displays a horizontal bar to represent the distance from the object.

If you’re not familiar with 16×2 character LCDs, consider reading the tutorial below.

Wiring

Next we need to make the connection to the LCD as shown below.

Arduino Wiring Fritzing Connections with HC-SR04 Ultrasonic Sensor and 16x2 LCD
Wiring HC-SR04 Ultrasonic Sensor and 16×2 LCD to Arduino UNO

Library Installation

Before we upload the code and start playing with the sensor we need to install a library called LCDBarGraph. This library will help in drawing a horizontal bar on the LCD, where the length of the bar will represent the distance to the object.

To install the library navigate to Sketch > Include Libraries > Manage Libraries… Wait for Library Manager to download the library index and update the list of installed libraries. Filter your search by typing ‘lcdbargraph’. Click on the first entry and then select Install.

lcdbargraph library installation

Arduino Code

Once you have installed the library, try the below sketch.

// includes the LiquidCrystal Library
#include <LiquidCrystal.h> 

// includes the LcdBarGraph Library
#include <LcdBarGraph.h>

// Maximum distance we want to ping for (in centimeters).
#define max_distance 200

// Creates an LCD object. Parameters: (rs, enable, d4, d5, d6, d7)
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); 

LcdBarGraph lbg(&lcd, 16, 0, 1); // Creates an LCD Bargraph object.

const int trigPin = 9;
const int echoPin = 10;
long duration;
int distance;

void setup() 
{
	lcd.begin(16,2); // Initializes the interface to the LCD screen
	
	pinMode(trigPin, OUTPUT);
	pinMode(echoPin, INPUT);
}

void loop() 
{
	// Write a pulse to the HC-SR04 Trigger Pin
	digitalWrite(trigPin, LOW);
	delayMicroseconds(2);
	digitalWrite(trigPin, HIGH);
	delayMicroseconds(10);
	digitalWrite(trigPin, LOW);
	
	// Measure the response from the HC-SR04 Echo Pin
	duration = pulseIn(echoPin, HIGH);
	
	// Determine distance from duration
	// Use 343 metres per second as speed of sound
	distance= duration*0.034/2;
	
	// Prints "Distance: <value>" on the first line of the LCD
	lcd.setCursor(0,0);
	lcd.print("Distance: "); 
	lcd.print(distance);
	lcd.print(" cm");

	// Draws bargraph on the second line of the LCD
	lcd.setCursor(0,1);
	lbg.drawValue(distance, max_distance);
	delay(500);
}

The output looks like this.

HC-SR04 Ultrasonic Sensor Arduino Distance Measurement Bargraph Output on 16x2 LCD
Bargraph Output on 16×2 Character LCD

Code Explanation:

First of all, you have to set up the Liquid Crystal Library as usual. Next, create an LcdBarGraph instance with the LiquidCrystal instance you just created. You should reference LiquidCrystal to the constructor of LcdBarGraph.

The constructor of LcdBarGraph takes three more parameters. The second is the number of the Character column in the LCD (in our case it is 16). The last two parameters are optional and allow custom positioning of the bar.

// creating bargraph instance
LcdBarGraph lbg(&lcd, 16, 0, 1);

After calculating the distance from the sensor we use drawValue(value, maxValue) function to display the bargraph. It draws a bargraph with a value between 0 and maxValue.

//display bargraph
lbg.drawValue(distance, max_distance);

Interfacing HC-SR04 with 3-Wire Mode

When you have a limited number of digital I/O pins on your Arduino, you can take advantage of 3-pin mode. Normally when you connect HC-SR04 sensor to Arduino you need two I/O pins. However in 3-wire mode you only need a single I/O pin instead of two.

In this mode a single I/O pin is used as both input and output. This is possible because both Trig and Echo are not used at the same time.

The following table lists the pin connections:

HC-SR04 SensorArduino
VCC5V
Trig + Echo9
GNDGND

Here is how you can hook up the HC-SR04 sensor to Arduino using 3-wire mode.

Arduino Wiring Fritzing 3 Wire Mode Connections with HC-SR04 Ultrasonic Sensor
Wiring HC-SR04 Ultrasonic Sensor to Arduino UNO – 3 Wire Mode

All you have to do is connect both the TRIG and ECHO pins to digital pin #9 and define pin 9 for both pin values in the code. Rest of the code is same.

#define TRIGGER_PIN 9 // Trigger and Echo both on pin 9
#define ECHO_PIN 9

What are the limitations?

The HC-SR04 ultrasonic sensor is really good in terms of accuracy and overall usability especially compared to other low cost ultrasonic sensors. This does not mean that the HC-SR04 sensor will work all the time. The following pictures show some of the limitations of the HC-SR04:

  • The distance between the sensor and the object/obstacle is greater than 13 feet.

    HC-SR04 Limitation - cannot measure distance more than 13 feet
  • The object has its reflective surface at a shallow angle so that the sound is not reflected back to the sensor.

    HC-SR04 Limitation - cannot detect object at a shallow angle
  • The object is too small to reflect enough sound back to the sensor. Also, if your HC-SR04 sensor is mounted low on your device, you are likely to get sound reflecting off the floor.

    HC-SR04 Limitation - cannot detect small objects
  • Some objects with soft, irregular surfaces (such as stuffed animals) absorb sound rather than reflect it, so the HC-SR04 sensor may find it difficult to detect such objects.

    HC-SR04 Limitation - cannot detect soft irregular surface object

Whether you want to build a home burglar alarm or a trail camera, or perhaps you want to wake up animated Halloween props when trick-or-treaters come to your door, then you should definitely consider getting an HC-SR501 Passive Infrared (PIR) sensor for yourself.

The PIR sensor allows you to detect when a person or animal moves in or out of sensor range. This sensor is what you’ll find in most modern security systems, automatic light switches, garage door openers and similar applications where we want to react to motion.

Before getting into the nitty-gritty, let’s first understand how a PIR sensor actually works.

How does a PIR sensor work?

All objects, including the human body, at temperatures above absolute zero (0 Kelvin / -273.15 °C) emit heat energy in the form of infrared radiation. The hotter an object is, the more radiation it emits. This radiation is not visible to the human eye because it is emitted at infrared wavelengths. The PIR sensor is specifically designed to detect such levels of infrared radiation.

A PIR sensor consists of two main parts:

  1. A pyroelectric sensor, which you can see in the image below as a round metal with a rectangular crystal in the center.
  2. A special lens called a fresnel lens which Focuses the infrared signals on the pyroelectric sensor.
hc sr501 pir sensor with lens

The Pyroelectric Sensor

A pyroelectric sensor consists of a window with two rectangular slots and is made of a material (typically coated silicon) that allows infrared radiation to pass through. Behind the window, there are two separate infrared sensor electrodes, one responsible for producing the positive output and the other for producing the negative output.

The two electrodes are wired such that they cancel each other out. This is because we are looking for changes in IR levels and not ambient IR levels. That’s why when one half sees more or less IR radiation than the other, we get the output.

PIR Sensor Working Pyroelectric Sensor Two Detection Slots

When there is no movement around the sensor, both slots detect the same amount of infrared radiation, resulting in a zero output signal.

But when a warm body like a human or an animal passes by, it first intercepts half of the sensor. This causes a positive differential change between the two halves. When the warm body intercepts the other half of the sensor (leaves the sensing region), the opposite happens, and the sensor produces a negative differential change. By reading this change in voltage, motion is detected.

PIR Sensor Working Animation Differential Output

The Fresnel Lens

You may feel that the Fresnel lens used here is not really doing anything. In fact, this is what increases the range and field of view of the PIR sensor. Its slim, lightweight construction and excellent light gathering capability make it extremely useful for making PIRs small in size yet powerful.

A Fresnel lens consists of a series of concentric grooves carved into the plastic. These contours act as individual refracting surfaces, gathering parallel light rays at a focal point. As a result a Fresnel lens, although smaller in size, is able to focus light similarly to a conventional optical lens.

fresnel lens working

In reality, to increase the range and field of view of the PIR sensor, the lens is divided into several facet-sections, each section of which is a separate Fresnel lens.

fresnel lens actual macro photo

The different faceting and sub-lenses create a range of detection areas/zones, interleaved with each other. That’s why the centers of the lenses are ‘inconsistent’ in the image above – every other one points to a different half of the PIR sensing element.

pir sensor detection zone areas pattern

HC-SR501 PIR Sensor Hardware Overview

For most of our Arduino projects that require detecting whether someone has left or entered the area, the HC-SR501 PIR sensor is a great choice. It is low power, low cost, easy to interface and extremely popular among hobbyists.

This PIR sensor itself is pretty straightforward and works out of the box. Simply apply power 5V – 12V and ground. The sensor output goes HIGH when motion is detected and goes LOW when idle (no motion detected).

By connecting this output to the microcontroller, you can react to motion by turning lights ON/OFF, enabling a fan, enabling a Halloween prop, or perhaps taking a picture of an intruder.

And the best part is that it consumes less than 2mA of current and can detect motion up to 7 meters (21 ft) with sensitivity control.

BISS0001 PIR Controller

At the heart of the module is a passive infrared (PIR) controller IC – BISS0001. Because of the noise immunity it provides, the BISS0001 is one of the most stable PIR controllers available.

This chip takes the output from the Pyroelectric sensor and does some minor processing on it to emit a digital output pulse.

hcsr501 pir sensor biss0001 ic

You can find out more about BISS0001 from the datasheet.

Power

The module comes with a 3.3V precision voltage regulator, so it can be powered by any DC voltage from 4.5 to 12 volts, although 5V is commonly used.

The module comes with a protection diode (also known as a safety diode) to protect the module from reverse voltage and current. So even if you accidentally connect the power with incorrect polarity, your module will not be damaged.

hcsr501 pir sensor diode and regulator

Sensitivity Adjustment

The PIR sensor has a potentiometer on the back to adjust the sensitivity.

hcsr501 pir sensor sensitivity adjustment

This potentiometer sets the maximum detection range. Sensitivity can be adjusted over a range of approximately 3 meters to 7 meters (9 to 21 feet). However the topology of your room can affect the actual range you get. Rotating the pot clockwise will increase the sensitivity and thus the range, and vice versa.

Time-Delay Adjustment

There is another potentiometer on the back of the PIR sensor to adjust the Time-Delay.

hcsr501 pir sensor time delay adjustment

This potentiometer sets how long the output will remain HIGH after motion is detected. It can be adjusted from 1 second to about 3 minutes. Turning the potentiometer clockwise increases the delay, while turning the potentiometer counter-clockwise decreases the delay.

Trigger Selection Jumper

There are two trigger modes that determine how the sensor will react when motion is detected.

Single Trigger Mode: The constant motion will cause a single trigger.

Multiple Trigger Mode: The constant motion will cause a series of triggers.

The board comes with a berg jumper (some modules have a solder bridge jumper) allowing you to choose one of two modes:

hcsr501 pir sensor trigger select jumper

L – Selecting this will set the single trigger mode. In this mode the output goes HIGH as soon as motion is detected and remains HIGH for a period determined by the Time-Delay potentiometer. Further detection is blocked until the output returns to LOW at the end of the time delay. If there is still motion, the output will go HIGH again. As you can see in the image below, Motion #3 is completely ignored.

hcsr501 pir sensor single trigger mode

H – Selecting this will set the multiple trigger mode. In this mode the output goes HIGH as soon as motion is detected and remains HIGH for a period determined by the Time-Delay potentiometer. Unlike single trigger mode, further detection is not blocked, so the time delay is reset each time motion is detected. Once the motion stops, the output returns to LOW only after a time delay. Hence the name multiple trigger mode.

hcsr501 pir sensor multiple trigger mode

Optional Components – Thermistor and LDR

The HC-SR501 module has solder pads for two additional components. These are usually labeled as ‘RT’ and ‘RL’. Note that on some boards the label may be covered by a Fresnel len on the opposite side of the components.

hcsr501 pir sensor place for thermistor and ldr

RT – This connection is for a thermistor or temperature-sensitive resistor. Adding this allows the HC-SR501 to be used in extreme temperatures. This also increases the accuracy of the detector to some extent.

RL – This connection is for Light Dependent Resistor (LDR) or Photoresistor. Adding this component allows the HC-SR501 to operate in the dark. This is useful for building motion-sensitive lighting systems.

These additional components can be soldered directly to the module or extended to remote locations using wires and connectors.

Technical Specifications

Here are the specifications:

Operating Voltage4.5 – 20V (typically 5V)
Maximum Current Draw< 2mA
Time Delay~ 1 sec to 3 min
Detection Distance3 – 7 meters (9 – 21 feet)
Detection Angle120 degrees (typically)

HC-SR501 PIR Sensor Pinout

The HC-SR501 has a 3-pin connector. The markings are hidden by the Fresnel lens, so refer to the following image for pinout.

Passive Infrared PIR Sensor Pinout Diagram

VCC is the power supply for the sensor. You can connect an input voltage anywhere between 5 to 12V to this pin, although 5V is commonly used.

Output pin is the 3.3V TTL logic output. It goes HIGH when motion is detected and goes LOW when idle (no motion detected).

GND is the ground pin.

Using PIR Sensor as a standalone unit

One of the reasons why the HC-SR501 PIR sensor is extremely popular is that the HC-SR501 is a very versatile sensor that is quite capable on its own. You can increase its versatility even further by connecting it to a microcontroller like Arduino.

For our first experiment, we’ll use the HC-SR501 to show how useful it is on its own.

The wiring for this experiment is very simple. Connect the batteries to the sensor’s VCC and GND and a small red LED to the output pin via a 220Ω current limiting resistor. That’s all!

Now when the PIR detects motion, the output pin will go “HIGH” and light up the LED!

Testing PIR Sensor Wiring Fritzing Connections without Arduino
This illustrates how a PIR Sensor can be used in standalone applications.

Remember that once you power up the circuit you need to wait 30-60 seconds for the PIR to adapt to the infrared energy in the room. The LED may blink a bit during that time. Wait until the LED is completely off and then walk around in front of it or wave a hand and watch the LED light up accordingly.

This PIR output can be connected directly to a relay module if you want to turn something ON/OFF based on motion.

Wiring a PIR Sensor to an Arduino

Now that we have a complete understanding of how the PIR sensor works, we can start connecting it to our Arduino!

Connecting the PIR sensor to the Arduino is really simple. Power the PIR with 5V and connect ground to ground. The PIR acts as a digital output, so all you have to do is listen to the output pin. So connect the output to Arduino’s digital pin #8.

For the HC-SR501 to function correctly, set the jumper to the H (Multiple Trigger Mode) position. You will also need to set the Time-Delay to at least 3 seconds, turn the Time-Delay potentiometer counterclockwise as far as it will go. Finally set the sensitivity potentiometer to any position you like or, if you are not sure, set it to the midpoint.

The following table lists the pin connections:

HC-SR501 PIR SensorArduino
VCC5V
GNDGND
OUT8

The image below shows how to connect HC-SR501 PIR sensor to the Arduino.

Arduino Wiring Fritzing Connections with PIR Sensor Module
Wiring PIR Sensor to Arduino UNO

Now you are ready to upload some code and get the PIR working.

Arduino Example Code

The code is very simple. It basically just keeps track of whether the input to pin #8 is HIGH or LOW.

int ledPin = 13;                // choose the pin for the LED
int inputPin = 8;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
 
void setup() {
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input
 
  Serial.begin(9600);
}
 
void loop(){
  val = digitalRead(inputPin);  // read input value
  
  if (val == HIGH)	// check if the input is HIGH
  {            
    digitalWrite(ledPin, HIGH);  // turn LED ON
	
    if (pirState == LOW) 
	{
      Serial.println("Motion detected!");	// print on output change
      pirState = HIGH;
    }
  } 
  else 
  {
    digitalWrite(ledPin, LOW); // turn LED OFF
	
    if (pirState == HIGH)
	{
      Serial.println("Motion ended!");	// print on output change
      pirState = LOW;
    }
  }
}

With the sensor pointing up, swipe your hand over the sensor. You should see a “Motion Detected” message printed on the serial terminal.

Passive Infrared PIR Sensor Arduino Sketch Output Serial Monitor
PIR Sensor output on serial monitor

Things to consider before designing PIR based applications

When designing a system based on the HC-SR501 you need to keep the following delay periods in mind.

Lockout Time

When the sensor output goes LOW, it will remain LOW for about 2 seconds. During this, the motion sensing is locked-out.

hcsr501 pir sensor blocked lockout time

For example, let’s say you have set the sensor for a time-delay of 4 seconds and set the jumper to ‘L’. So when you wave your hand in front of the sensor, the output will go HIGH for 4 seconds and then LOW for about 2 seconds. Any motion in this period is completely ignored; as you can see Motion #2 is ignored here.

Power On Delay

Like most PIR sensors, the HC-SR501 takes approximately 30 to 60 seconds after being turned on to go through the initialization sequence. At that time it learns the ambient infrared signature of the environment. Basically it’s calibrating itself to the environment to determine what constitutes motion.

False triggers are likely to occur during this calibration time, so any triggers during this time should be ignored. Also make sure that there is not too much movement in front of the sensor when it is undergoing self-calibration as this may interfere with the calibration process.

Want to keep a log of the climate in your greenhouse, build a humidor control system, or track temperature and humidity data for a weather station project? AOSONG’s DHT11 or DHT22 Temperature and Humidity Sensor could be the perfect fit for you!

These sensors are factory-calibrated and do not require any external components to function. With just a few connections and a bit of Arduino code, you can begin measuring relative humidity and temperature right away.

They provide temperature and humidity readings accurate to within one decimal place, which is a plus. The only drawback is that they only provide new data every second or two, but for the price and performance, it’s hard to complain.

DHT11 vs DHT22

The DHT11 and the DHT22 are the two most widely used sensors in the DHTxx series. They look kind of the same and have the same pinout, but their specs are different.

Of the two, the DHT22 is more expensive and, undoubtedly, has better specifications. The DHT22 can measure temperatures from -40°C to +125°C with an accuracy of ±0.5°C, while the DHT11 can measure temperatures from 0°C to 50°C with an accuracy of ±2°C. In addition, the DHT22 sensor can measure relative humidity from 0 to 100% with an accuracy of 2-5%, while the DHT11 sensor can only measure relative humidity from 20 to 80% with an accuracy of 5%.

Here are the specifications:

DHT11 Temperature Humidity Sensor Fritzing part Illustration
DHT22 Temperature Humodoty Sensor Fritzing part Illustration
DHT11DHT22
Operating Voltage3 to 5V3 to 5V
Max Operating Current2.5mA max2.5mA max
Humidity Range20-80% / 5%0-100% / 2-5%
Temperature Range0-50°C / ± 2°C-40 to 80°C / ± 0.5°C
Sampling Rate1 Hz (reading every second)0.5 Hz (reading every 2 seconds)
Body size15.5mm x 12mm x 5.5mm15.1mm x 25mm x 7.7mm
AdvantageUltra low costMore Accurate

Despite the fact that the DHT22 is more accurate, precise, and capable of operating in a wider range of temperature and humidity, there are three areas where the DHT11 completely outperforms the DHT22 – It is more affordable, more compact, and has a higher sampling rate. DHT11 takes a reading once per second (or 1Hz sampling rate), while DHT22 takes a reading once every two seconds (or 0.5Hz sampling rate).

Despite these differences, the operating voltage of both sensors ranges from 3 to 5 volts, with a maximum current of 2.5mA (during conversion). The best part is that DHT11 and DHT22 sensors are swappable, which means that if you build your project with one, you can simply unplug it and replace it with another. Your code may need to be tweaked slightly, but the wiring remains the same!

Inside the DHT Sensor

If you remove the sensor’s casing, you will find an NTC thermistor and a humidity sensing component inside.

Inside DHT11 DHT22 AM2302 Temperature Humidity Sensor

The humidity sensing component has two electrodes with a moisture-holding substrate (usually a salt or conductive plastic polymer) in between. As the humidity rises, the substrate absorbs water vapor, resulting in the release of ions and a decrease in the resistance between the two electrodes. This change in resistance is proportional to the humidity, which can be measured to estimate relative humidity.

Internal Structure of Humidity Sensor in DHT11 DHT22
Internal Structure of Humidity Sensor

The sensor also includes a NTC thermistor for measuring temperature. A thermistor is a type of resistor whose resistance varies with temperature.

Technically, all resistors are thermistors in the sense that their resistance changes slightly with temperature, but the change is typically very small and difficult to measure.

Thermistors are designed so that their resistance changes dramatically with temperature (by 100 ohms or more per degree). The term “NTC” stands for “Negative Temperature Coefficient,” which means that resistance decreases as temperature rises.

NTC Thermistor Temperature Resistance Characteristic Curve
NTC Thermistor with Characteristic Curve

The sensor also includes an 8-bit SOIC-14 packaged IC. This IC measures and processes the analog signal using stored calibration coefficients, converts the analog signal to digital, and outputs a digital signal containing the temperature and humidity.

DHT11 and DHT22 Pinout

The DHT11 and DHT22 sensors are both relatively simple to connect. They have four pins:

DHT11 DHT22 AM2302 Temperature Humidity Sensor Pinout

VCC pin provides power to the sensor. Despite the fact that the supply voltage ranges from 3.3V to 5.5V, a 5V supply is recommended. With a 5V power supply, the sensor can be placed up to 20 meters away. With 3.3V supply voltage, the sensor can be placed up to 1 meter away; otherwise, the line voltage drop will cause measurement errors.

Data pin is used for communication between the sensor and the microcontroller.

NC Not connected

GND is the ground pin.

Wiring DHT11 and DHT22 Sensors to an Arduino

Now it’s time to connect the sensor to the Arduino!

Connecting DHT sensors to Arduino is straightforward. They have fairly long 0.1′′-pitch pins, allowing them to be easily plugged into any breadboard. Connect the VCC pin to the Arduino’s 5V and the GND pin to ground. Finally, connect the Data pin to digital pin #8.

To ensure proper communication between the sensor and MCU, you must also add a 10K pull-up resistor between the Data line and VCC (to keep the signal HIGH). If you have a breakout board for the sensor, you do not need to add an external pull-up resistor, as it already contains one.

Arduino Wiring Fritzing Connections with DHT11
Wiring DHT11 to Arduino UNO

Arduino Wiring Fritzing Connections with DHT22
Wiring DHT22 to Arduino UNO

You’re now ready to upload some code and get it working.

Library Installation

The DHTxx sensors have their own proprietary single-wire data transfer protocol. This protocol requires precise timing. We don’t have to worry too much about this, though, because we’ll be using the DHTlib library, which handles almost everything.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the libraries index and update the list of installed libraries.

manage libraries

Filter your search by entering ‘dhtlib’.There should only be a single entry. Click on that and then choose Install.

dhtlib library installation

Arduino Example 1 – Displaying Readings on Serial Monitor

After installing the library, copy and paste this sketch into the Arduino IDE. The following test sketch will print the temperature and relative humidity values to the serial monitor. Try out the sketch, and then we’ll go over it in more detail.

#include <dht.h>
#define dataPin 8 // Defines pin number to which the sensor is connected
dht DHT; // Creats a DHT object

void setup() 
{
	Serial.begin(9600);
}
void loop() 
{
	//Uncomment whatever type you're using!
	int readData = DHT.read22(dataPin); // DHT22/AM2302
	//int readData = DHT.read11(dataPin); // DHT11

	float t = DHT.temperature; // Gets the values of the temperature
	float h = DHT.humidity; // Gets the values of the humidity

	// Printing the results on the serial monitor
	Serial.print("Temperature = ");
	Serial.print(t);
	Serial.print(" ");
	Serial.print((char)176);//shows degrees character
	Serial.print("C | ");
	Serial.print((t * 9.0) / 5.0 + 32.0);//print the temperature in Fahrenheit
	Serial.print(" ");
	Serial.print((char)176);//shows degrees character
	Serial.println("F ");
	Serial.print("Humidity = ");
	Serial.print(h);
	Serial.println(" % ");
	Serial.println("");

	delay(2000); // Delays 2 secods
}

After uploading the sketch, you should see the following output on the serial monitor.

DHT11 DHT22 AM2302 Sensor DHTlib library Output on Serial Monitor
Output on Serial Monitor

Code Explanation:

The sketch begins by including the DHT library. Following that, we specify the Arduino pin number to which our sensor’s Data pin is connected and create a DHT object.

#include <dht.h>
#define dataPin 8 // Defines pin number to which the sensor is connected
dht DHT; // Creats a DHT object

In the setup, we initialize the serial communication.

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

In the loop, we use the read22(dataPin) function to read the DHT22. This function takes as a parameter the sensor’s Data pin number. When working with DHT11, you must use the read11() function; to do so, you just need to uncomment the second line.

//Uncomment whatever type you're using!
int readData = DHT.read22(dataPin); // DHT22/AM2302
//int readData = DHT.read11(dataPin); // DHT11

We can now retrieve the humidity and temperature values by accessing the DHT object’s properties using dot . notation.

float t = DHT.temperature; // Gets the values of the temperature
float h = DHT.humidity; // Gets the values of the humidity

The DHT object returns the temperature in degrees Celsius (°C). It is easy to convert to Fahrenheit (°F) using the following formula:

T(°F) = T(°C) × 9/5 + 32

//print the temperature in Fahrenheit
Serial.print((t * 9.0) / 5.0 + 32.0);

Arduino Example 2 – Displaying Readings on LCD

If you’re constructing your own incubator or a similar project, you’ll need a 16×2 character LCD rather than a serial monitor to display the current temperature and humidity levels. So, in this example, we’ll also connect the LCD to the Arduino in addition to the DHT11 and DHT22 sensors.

This is what the output looks like.

DHT11 DHT22 Arduino Sketch Temperature Humidity Measurements Output on LCD
Temperature & humidity measurements on LCD

If you are unfamiliar with 16×2 character LCDs, consider reading the tutorial below.

Wiring

Following that, connect the LCD as shown below.

Arduino Wiring Fritzing Connections with DHT11 and 16x2 Character LCD
Wiring DHT11 and 16×2 Character LCD to Arduino UNO
Arduino Wiring Fritzing Connections with DHT22 and 16x2 Character LCD
Wiring DHT22 and 16×2 Character LCD to Arduino UNO

Arduino Code

The sketch below will display the temperature and relative humidity values on the 16×2 character LCD. This sketch is similar to the previous one, except that the values are printed on the LCD.

#include <LiquidCrystal.h> // includes the LiquidCrystal Library
#include <dht.h>
#define dataPin 8

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // Creates an LCD object. Parameters: (rs, enable, d4, d5, d6, d7)
dht DHT;
bool showcelciusorfarenheit = false;

void setup() 
{
	lcd.begin(16,2); // Initializes the interface to the LCD screen, and specifies the dimensions (width and height) of the display
}

void loop() 
{
	int readData = DHT.read22(dataPin);
	float t = DHT.temperature;
	float h = DHT.humidity;
	lcd.setCursor(0,0); // Sets the location at which subsequent text written to the LCD will be displayed
	lcd.print("Temp.: "); // Prints string "Temp." on the LCD

	//Print temperature value in Celcius and Fahrenheit every alternate cycle
	if(showcelciusorfarenheit)
	{
		lcd.print(t); // Prints the temperature value from the sensor
		lcd.print(" ");
		lcd.print((char)223);//shows degrees character
		lcd.print("C");
		showcelciusorfarenheit = false;
	}
	else
	{
		lcd.print((t * 9.0) / 5.0 + 32.0); // print the temperature in Fahrenheit
		lcd.print(" ");
		lcd.print((char)223);//shows degrees character
		lcd.print("F");
		showcelciusorfarenheit = true;
	}
	
	lcd.setCursor(0,1);
	lcd.print("Humi.: ");
	lcd.print(h);
	lcd.print(" %");
	delay(5000);
}

Want to keep a log of the climate in your greenhouse, build a humidor control system, or track temperature and humidity data for a weather station project? AOSONG’s DHT11 Temperature and Humidity sensor module could be the perfect fit for you!

This sensor is factory-calibrated and does not require any external components to function. With just a few connections and a bit of Arduino code, you can begin measuring relative humidity and temperature right away.

DHT11 Module Hardware Overview

At the heart of the module is the digital temperature and humidity sensor manufactured by AOSONG – DHT11.

DHT11 Sensor

DHT11 can measure temperature from 0°C to 50°C with a ±2.0°C accuracy, and humidity from 20 to 80% with a 5% accuracy.

DHT11 Module Hardware Overview Front

Note that the DHT11 has a sampling rate of 1Hz, which means it can provide new data once every second.

Supporting Circuitry

The module includes all of the necessary supporting circuitry, so it can be used straight out of the box.

DHT11 Module Hardware Overview Back

DHT11 sensors typically require an external 10K pull-up resistor on the output pin for proper communication between the sensor and the Arduino. However, because the module already includes a pull-up resistor, you do not need to add one.

The module also includes a decoupling capacitor for filtering power supply noise.

Inside the DHT11 Sensor

If you remove the sensor’s casing, you will find an NTC thermistor and a humidity sensing component inside.

Inside DHT11 Sensor Front

The humidity sensing component has two electrodes with a moisture-holding substrate (usually a salt or conductive plastic polymer) in between.

As the humidity rises, the substrate absorbs water vapor, resulting in the release of ions and a decrease in the resistance between the two electrodes.

This change in resistance is proportional to the humidity, which can be measured to estimate relative humidity.

Internal Structure of Humidity Sensor in DHT11 DHT22

DHt11 also includes a NTC thermistor for measuring temperature. A thermistor is a type of resistor whose resistance varies with temperature.

Technically, all resistors are thermistors in the sense that their resistance changes slightly with temperature, but this change is typically very small and difficult to measure. Thermistors are designed so that their resistance changes dramatically with temperature (by 100 ohms or more per degree). The term “NTC” stands for “Negative Temperature Coefficient,” which means that resistance decreases as temperature rises.

NTC Thermistor Temperature Resistance Characteristic Curve

The sensor also includes an 8-bit SOIC-14 packaged IC. This IC measures and processes the analog signal using stored calibration coefficients, converts the analog signal to digital, and outputs a digital signal containing the temperature and humidity.

Inside DHT11 Sensor Back

DHT11 Module Pinout

The DHT11 module is relatively simple to connect. There are only three pins:

DHT11 Module Pinout

+ (VCC) pin provides power to the sensor. Despite the fact that the supply voltage of the module ranges from 3.3V to 5.5V, a 5V supply is recommended. With a 5V power supply, the sensor can be placed up to 20 meters away. With 3.3V supply voltage, the sensor can be placed just 1 meter away; otherwise, the line voltage drop will cause measurement errors.

Out pin is used for communication between the sensor and the microcontroller.

– (GND) is the ground pin.

Wiring DHT11 Module to Arduino

Now it’s time to connect the DHT11 module to the Arduino!

Connections are relatively simple. Begin by connecting the + (VCC) pin to the Arduino’s 5V output and the – (GND) pin to ground. Finally, connect the Out pin to digital pin #8.

The diagram below shows how to connect everything.

Wiring DHT11 Module with Arduino

Installing DHT library

The DHT sensors has their own proprietary single-wire data transfer protocol. This protocol requires precise timing. We don’t have to worry too much about this, though, because we’ll be using the DHTlib library, which handles almost everything.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the libraries index and update the list of installed libraries.

manage libraries

Filter your search by entering ‘dhtlib’.There should only be a single entry. Click on that and then choose Install.

dhtlib library installation

Arduino Example 1 – Displaying Readings on Serial Monitor

After installing the library, copy and paste this sketch into the Arduino IDE.

The following test sketch will print the temperature and relative humidity values to the serial monitor. Try out the sketch, and then we’ll go over it in more detail.

#include <dht.h>        // Include library
#define outPin 8        // Defines pin number to which the sensor is connected

dht DHT;                // Creates a DHT object

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

void loop() {
	int readData = DHT.read11(outPin);

	float t = DHT.temperature;        // Read temperature
	float h = DHT.humidity;           // Read humidity

	Serial.print("Temperature = ");
	Serial.print(t);
	Serial.print("°C | ");
	Serial.print((t*9.0)/5.0+32.0);        // Convert celsius to fahrenheit
	Serial.println("°F ");
	Serial.print("Humidity = ");
	Serial.print(h);
	Serial.println("% ");
	Serial.println("");

	delay(2000); // wait two seconds
}

After uploading the sketch, you should see the following output on the serial monitor.

DHT11 Module Output

Code Explanation:

The sketch begins by including the DHT library. Following that, we specify the Arduino pin number to which our sensor’s Data pin is connected and create a DHT object.

#include <dht.h>
#define outPin 8

dht DHT;

In the setup, we initialize the serial communication.

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

In the loop, we use the read11() function to read the DHT11 module. This function takes as a parameter the sensor’s Data pin number.

int readData = DHT.read11(outPin);

We can now retrieve the humidity and temperature values by accessing the DHT object’s properties using dot . notation.

float t = DHT.temperature;        // Read temperature
float h = DHT.humidity;           // Read humidity

The DHT object returns the temperature in degrees Celsius (°C). It is easy to convert to Fahrenheit (°F) using the following formula:

T(°F) = T(°C) × 9/5 + 32

Serial.print((t * 9.0) / 5.0 + 32.0);

Arduino Example 2 – Displaying Readings on LCD

If you’re constructing your own incubator or a similar project, you’ll need a 16×2 character LCD rather than a serial monitor to display the current temperature and humidity levels. So, in this example, we’ll also connect the LCD to the Arduino in addition to the DHT11 module.

This is what the output looks like.

DHT11 Module Output on LCD

If you are unfamiliar with 16×2 character LCDs, consider reading the tutorial below.

Wiring

Following that, connect the LCD as shown below.

Wiring DHT11 Module with Arduino and LCD

Arduino Code

The sketch below will display the temperature and relative humidity values on the 16×2 character LCD. This sketch is similar to the previous one, except that the values are printed on the LCD.

#include <LiquidCrystal.h>      // Include LiquidCrystal Library
#include <dht.h>

#define outPin 8

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // Create an LCD object.
dht DHT;                // Create a DHT object

void setup() {
	lcd.begin(16,2); 	// Initialize the LCD
}

void loop() {
	int readData = DHT.read11(outPin);
	
	float t = DHT.temperature;
	float h = DHT.humidity;
	
	lcd.setCursor(0,0);
	lcd.print("Temp.: ");
	lcd.print(t);
	lcd.print((char)223);	//shows degrees character
	lcd.print("C");

	lcd.setCursor(0,1);
	lcd.print("Humi.: ");
	lcd.print(h);
	lcd.print("%");
	
	delay(2000);
}

Want to keep a log of the climate in your greenhouse, build a humidor control system, or track temperature and humidity data for a weather station project? The AM2320 Temperature & Humidity Sensor may be the right choice for you!

AM2320 sensor is factory-calibrated and requires few external components to work. So with just a few connections and some Arduino code, you can start measuring relative humidity and temperature right away.

Hardware Overview

The AM2320 is a low-cost, easy to use, fairly accurate, digital temperature & humidity sensor, from AOSONG. It looks like the popular DHT11/DHT22 temperature and humidity sensors, but unlike classic DHT sensors, it has an I2C interface!

am2320 sensor

The AM2320 sensor is capable of reading humidity over the full range of 0 to 100% RH with a typical accuracy of ±3% over the range of 20% to 80% RH (0.024% RH resolution).

It has a maximum temperature range of -40 to 80°C and a typical accuracy of ±0.5°C at 25°C (0.01°C resolution).

The AM2320 can output data at a maximum sampling rate of 0.5Hz i.e. one reading every two seconds.

Power Requirement

The sensor itself uses 3.3V to 5.5V which makes it 3V or 5V compliant. So, you can use it with your favorite 3.3V or 5V microcontroller without worry.

The AM2320 consumes less than 0.95mA during measurements and less than 10µA during sleep mode. This low power consumption allow the implementation in battery driven devices such as handsets, wearables or smart watches.

I2C Interface

The AM2320 is a I2C sensor, meaning it uses the two I2C data/clock wires available on most microcontrollers, and can share those pins with other I2C sensors as long as they don’t have an address collision.

The sensor has a fixed I2C address and is set to 0x5CHEX. A multiplexer/Mux is required to communicate to multiple AM2320 sensors on a single bus.

Technical Specifications

Here are the complete specifications:

Supply Voltage3.3V to 5.5V
Current draw~0.95mA (during measurements)
~10µA (during sleep mode)
Humidity Range0 to 100 %RH
Humidity Accuracy±3% over the range of 20% to 80% RH
Temperature Range-40°C to +80°C
Temperature Accuracy±0.5°C at 25°C
Sampling Rate0.5Hz (one reading every two seconds)

For more details, please refer below datasheet.

AM2320 Sensor Pinout

Now let’s have a look at the pinout.

am2320 sensor pinout

VDD is the power pin. Since the sensor uses 3.3 to 5.5VDC, give it the same power as the logic level of your microcontroller – e.g. for a 5V micro like Arduino, use 5V.

SDA is the I2C data pin, requires a pullup of 2.2K – 10K to VDD.

GND is the common ground for power and logic.

SCL is the I2C clock pin, requires a pullup of 2.2K – 10K to VDD.

Wiring up a AM2320 to an Arduino

As it makes use of the I2C bus, connecting the AM2320 to an Arduino is pretty simple!

There are only four pins that need to be hooked up in order to start using the sensor. One for VCC, one for GND, and two data lines for I2C communication.

Connect the SCL pin to the I2C clock pin and the SDA pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

The AM2320 does not have internal pullup resistors for the I2C bus. So, you need to add them externally. Any value from 2.2K to 10K should work fine. The resistors go from VDD to SCL and SDA each.

The following illustration shows the wiring.

wiring am2320 sensor with arduino

Library Installation

To get your sensor up and running, you will need to install the Adafruit AM2320 library. It is available from the Arduino library manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing ‘am2320‘ and install the library.

adafruit am2320 sensor library installation

The Adafruit_AM2320 library uses the Adafruit Sensor support backend. So, search the library manager for Adafruit Unified Sensor and install that too (you may have to scroll a bit)

adafruit unified sensor library installation

You also need to install the Adafruit Bus IO helper library. It abstracts away I2C & SPI transactions and registers.

adafruit busio library installation

Arduino Code – Reading Temperature and Humidity

Below is a basic Arduino sketch. Go ahead and upload it to your Arduino. You will see the current temperature and humidity in your room!

#include "Adafruit_Sensor.h"
#include "Adafruit_AM2320.h"

Adafruit_AM2320 am2320 = Adafruit_AM2320();

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    delay(10); // hang out until serial port opens
  }

  am2320.begin();
}

void loop() {
  Serial.print("Temp: ");
  Serial.print(am2320.readTemperature());
  Serial.print(" C");
  Serial.print("\t\t");
  Serial.print("Humidity: ");
  Serial.print(am2320.readHumidity());
  Serial.println(" \%");

  delay(2000);
}

Once your code is uploaded, open the serial terminal at 9600bps. You should see something like the output below. Try breathing on the sensor to see both humidity and temperature values change!

am2320 sensor arduino output

Code Explanation:

This is probably about as simple as a sketch can get. At the beginning, Adafruit_Sensor.h and Adafruit_AM2320.h libraries are included and an Adafruit_AM2320 object is created in the global space.

#include "Adafruit_Sensor.h"
#include "Adafruit_AM2320.h"

Adafruit_AM2320 am2320 = Adafruit_AM2320();

In the setup, we initialize the serial communication with PC and call the begin() function to initialize the object.

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    delay(10); // hang out until serial port opens
  }

  am2320.begin();
}

Once the object is initialized, you can access object’s (am2320) methods using the dot operator.

am2320.readTemperature() returns floating point (decimal + fractional) temperature reading in °C. You can convert to Fahrenheit by multiplying by 1.8 and adding 32.

am2320.readHumidity() returns humidity reading, also as a floating point value between 0 and 100 (this reads % humidity).

void loop() {
  Serial.print("Temp: ");
  Serial.print(am2320.readTemperature());
  Serial.print(" C");
  Serial.print("\t\t");
  Serial.print("Humidity: ");
  Serial.print(am2320.readHumidity());
  Serial.println(" \%");

  delay(2000);
}

One of the easiest and inexpensive ways to add temperature sensing in your Arduino project is to use LM35 Temperature Sensor. These sensors are fairly precise and needs no external components to work. So, with just a few connections and some Arduino code you’ll be sensing temperature in no time!

LM35 Temperature Sensor

The LM35 is a low voltage, precision centigrade temperature sensor manufactured by Texas Instruments. It is a chip that provides a voltage output that is linearly proportional to the temperature in °C and is, therefore, very easy to use with an Arduino.

lm35 temperature sensor

The LM35 temperature sensor is fairly precise, never wears out, works under many environmental conditions and requires no external components to work. In addition, the LM35 sensor does not require calibration and provides a typical accuracy of ±0.5°C at room temperature and ±1°C over a full −55°C to +155°C temperature range.

The sensor can be powered with a 4V to 30V power supply and consumes less than 60µA during active temperature conversions, providing very low self-heating (less than 0.08°C in still air).

Here are the complete specifications:

Power supply4V to 30V
Current draw60µA
Temperature range−55°C to +155°C
Accuracy±0.5°C
Output scale factor10mV/°C
Output at 25°C250mV

For more information, please refer below datasheet.

The only disadvantage of the LM35 sensor is that it requires a negative bias voltage to measure negative temperature. So if you are planning to use the sensor to measure negative temperature, it is recommended that you use TMP36 temperature sensor. The TMP36 by Analog Devices is fairly accurate (-40°C to 125°C) and has the advantage of being able to measure negative temperatures without the need for negative bias voltage. You can find a dedicated tutorial for the TMP36 below.

A better alternative to the LM35 is to use a digital temperature sensor like the DS18B20 which comes in the same package. Digital temperature sensors have better noise immunity which is useful when the sensor is placed at a distance or in an electrically noisy environment.

Working Principle

The LM35 uses a solid-state technique to measure the temperature. It makes use of the fact that the voltage drop between the base and emitter (forward voltage – Vbe) of the Diode-connected transistor decreases at a known rate as the temperature increases. By precisely amplifying this voltage change, it is easy to generate an analog signal that is directly proportional to temperature.

relationship between forward voltage and temperature

This linear relationship between forward voltage and temperature is the reason why diode-connected transistors are used as temperature measurement devices. Essentially this is how temperature is measured, although there have been some improvements in this technique over the years. More information about this technique can be found here.

The good news is that all these complex calculations are done inside the LM35. It just outputs a voltage that is linearly proportional to temperature.

How to Measure Temperature

The LM35 is easy to use; just connect the left pin to power (4V to 30V) and the right pin to ground (assuming the flat side of the sensor is facing you). Then the middle pin will have an analog voltage that is directly proportional (linear) to the temperature in °C. This can be easily seen in the output voltage vs temperature characteristic. Note that the analog output voltage is independent of the power supply.

lm35 temperature sensor output relationship curve

To convert the voltage to temperature, simply use the basic formula:

Temperature (°C) = Vout * 100

For example, if the voltage out is 0.5V that means that the temperature is 0.5 * 100 = 50 °C

Testing the LM35 Sensor

Testing the LM35 is pretty easy, just connect the left pin to 4V to 30V power supply (Four AA batteries work great) and the right pin to ground (assuming the flat side of the sensor is facing you). Now connect your multimeter in DC voltage mode to ground and the middle pin. At the room temperature (25°C), the voltage should be about 0.25V.

Try squeezing the plastic case of the sensor gently to see a rise in temperature.

try squeezing lm35 to see rise in temperature

Or try touching the sensor with an ice cube (in a plastic bag so your circuit doesn’t come into contact with water) and watch the temperature drop.

try touching lm35 with ice to watch temperature drop

LM35 Sensor Pinout

The LM35 comes in three different form factors, but the most common type is the 3-pin TO-92 package, which looks just like a transistor. Let’s take a look at its pinout.

lm35 temperature sensor pinout

+Vs is the power supply for the sensor which can be anywhere between 4V to 30V.

Vout pin produces an analog voltage that is directly proportional (linear) to the temperature. It should be connected to an Analog (ADC) input.

GND is a ground pin.

Connecting the LM35 Temperature Sensor to an Arduino

Hooking up the LM35 to an Arduino is super simple. You only need to connect three pins: two for power and one for reading the sensor value.

The sensor can be powered from 5V. The positive voltage connects to ‘+Vs’ and ground connects to ‘GND‘. The middle pin ‘Vout’ is the analog signal output from the sensor and connects to the A0 analog input of an Arduino.

Below is the hookup for the experiments with the LM35:

wiring lm35 temperature sensor to arduino

To measure air temperature leave the sensor in the open air or attach it to an object you want to measure the temperature of, such as a heat sink.

Reading the Analog Temperature Data

As you can see in the wiring diagram above, the output of the LM35 is connected to one of the analog inputs of the Arduino. The value of this analog input can be read with the analogRead() function.

However, the analogRead() function does not actually return the output voltage of the sensor. Instead it maps the input voltage between 0 and the ADC reference voltage (technically it is the operating voltage i.e. 5V or 3.3V unless you change it) to 10-bit integer values ​​ranging from 0 to 1023. To convert this value back to the sensor’s output voltage, use this formula:

Vout = (reading from ADC) * (5 / 1024)

This formula converts the number 0-1023 from the ADC into 0-5V

Then, to convert volts into temperature, use this formula:

Temperature (°C) = Vout * 100

Arduino Code – Simple Thermometer

The following sketch shows a quick way to read LM35 temperature sensor and can serve as the basis for more practical experiments and projects. It simply reads the value from the LM35 using analog port A0 and prints the current temperature (in both °C and °F) on the serial monitor. Go ahead and upload it to your Arduino.

// Define the analog pin, the LM35's Vout pin is connected to
#define sensorPin A0

void setup() {
  // Begin serial communication at 9600 baud rate
  Serial.begin(9600);
}

void loop() {
  // Get the voltage reading from the LM35
  int reading = analogRead(sensorPin);

  // Convert that reading into voltage
  float voltage = reading * (5.0 / 1024.0);

  // Convert the voltage into the temperature in Celsius
  float temperatureC = voltage * 100;

  // Print the temperature in Celsius
  Serial.print("Temperature: ");
  Serial.print(temperatureC);
  Serial.print("\xC2\xB0"); // shows degree symbol
  Serial.print("C  |  ");
  
  // Print the temperature in Fahrenheit
  float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
  Serial.print(temperatureF);
  Serial.print("\xC2\xB0"); // shows degree symbol
  Serial.println("F");

  delay(1000); // wait a second between readings
}

You should see the following output in the serial monitor.

lm35 arduino output

Code Explanation:

The sketch starts by defining the Arduino pin to which the sensor’s Vout pin is connected.

#define sensorPin A0

In the setup, we initialize the serial connection with the computer.

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

In the loop, we first read in the analog signal from the LM35 using the analogRead() function.

int reading = analogRead(sensorPin);

Next, we will use the formulas we discussed earlier in the article to convert the analog reading into voltage and then into temperature.

float voltage = reading * (5.0 / 1024.0);

float temperatureC = voltage * 100;

Next, the results are printed on the Serial Monitor.

Serial.print("Temperature: ");
Serial.print(temperatureC);
Serial.print("\xC2\xB0"); // shows degree symbol
Serial.print("C  |  ");

The temperature value we get is in Celsius (°C). It is converted in to Fahrenheit (°F) using a simple formula and printed on the Serial Monitor.

T(°F) = T(°C) × 9/5 + 32

float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
Serial.print(temperatureF);
Serial.print("\xC2\xB0"); // shows degree symbol
Serial.println("F");

Arduino Project – Standalone Thermometer with LM35 and an I2C LCD

Sometimes you come up with an idea where you want to display the temperature readings in real time and show an alert when the temperature is outside the specified range. Then you’ll probably need a 16×2 character LCD instead of a serial monitor.

In this example, we’ll hook the I2C LCD up to the Arduino along with the LM35.

Connecting the I2C LCD is quite easy as you can see in the wiring diagram below. If you’re not familiar with an I2C LCDs, consider reading (at least skimming) below tutorial.

The following diagram shows you how to wire everything.

wiring lm35 temperature sensor to arduino and i2c lcd

The following sketch will print the temperature values on the I2C LCD. The code is similar to the previous example, except that the values are printed on the I2C LCD.

// Include the LiquidCrystal_I2C library
#include <LiquidCrystal_I2C.h>

// Create a new instance of the LiquidCrystal_I2C class
LiquidCrystal_I2C lcd(0x3F, 16, 2);

// Define a custom degree character
byte Degree[] = {
  B00111,
  B00101,
  B00111,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};

// Define the analog pin, the LM35's Vout pin is connected to
#define sensorPin A0

void setup() {
  // Start the LCD and turn on the backlight
  lcd.init();
  lcd.backlight();

  // Create a custom character
  lcd.createChar(0, Degree);
}

void loop() {
  // Get the voltage reading from the LM35
  int reading = analogRead(sensorPin);

  // Convert that reading into voltage
  // Replace 5.0 with 3.3, if you are using a 3.3V Arduino
  float voltage = reading * (5.0 / 1024.0);

  // Convert the voltage into the temperature in Celsius
  float temperatureC = voltage * 100;

  // Print the temperature on the LCD;
  lcd.setCursor(0, 0);
  lcd.print("Temperature:");
  lcd.setCursor(0, 1);
  lcd.print(temperatureC, 1);
  lcd.write(0); // print the custom degree character
  lcd.print("C ");
  
  // Print the temperature in Fahrenheit
  float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
  lcd.print(temperatureF, 1);
  lcd.write(0); // print the custom degree character
  lcd.print("F ");

  delay(1000); // wait a second between readings
}

You should see the following output on the LCD:

lm35 sensor output on i2c lcd   copy

Give your next Arduino project a nose for gasses by including the MQ2 gas sensor module. It is a versatile sensor that can detect LPG, smoke, alcohol, propane, hydrogen, methane, and carbon monoxide concentrations in the air.

This makes the MQ2 Gas Sensor Module an excellent choice for building an indoor air quality monitoring system, a breathalyzer, or an early fire detection system.

MQ2 Gas Sensor

The MQ2 sensor is one of the most widely used in the MQ sensor series. It is a MOS (Metal Oxide Semiconductor) sensor. Metal oxide sensors are also known as Chemiresistors because sensing is based on the change in resistance of the sensing material when exposed to gasses.

MQ2 Gas Sensor

The MQ2 gas sensor operates on 5V DC and consumes approximately 800mW. It can detect LPG, Smoke, Alcohol, Propane, Hydrogen, Methane and Carbon Monoxide concentrations ranging from 200 to 10000 ppm.

What does the concentration of 1 ppm mean?

Parts-per-million, or ppm for short, is the most commonly used unit for measuring gas concentration. ppm is simply the ratio of one gas to another. For example, 500ppm of carbon monoxide means that if you could count a million gas molecules, 500 would be carbon monoxide and the remaining 999500 would be other gasses.

Note that the MQ2 gas sensor detects multiple gases, but cannot identify them! That is normal; most gas sensors operate in this manner. Therefore, it is best suited for measuring changes in a known gas density rather than detecting which one is changing.

Internal structure of MQ2 Gas Sensor

The MQ2 is a heater-driven sensor. It is therefore covered with two layers of fine stainless steel mesh known as an “anti-explosion network”. It ensures that the heater element inside the sensor does not cause an explosion because we are sensing flammable gasses.

MQ2 Gas Sensor Parts Hardware Overview

It also protects the sensor and filters out suspended particles, allowing only gaseous elements to pass through the chamber. A copper-plated clamping ring secures the mesh to the rest of the body.

Inside Gas Sensor Internal Structure with Sensing Element & Connecting Legs

When the outer mesh is removed, the sensor looks like this. The sensing element and six connecting legs that extend beyond the Bakelite base form the star-shaped structure. Two (H) of the six leads are in charge of heating the sensing element and are linked together by a Nickel-Chromium coil (a well-known conductive alloy).

The remaining four signal-carrying leads (A and B) are connected with platinum wires. These wires are connected to the body of the sensing element and convey slight variations in the current flowing through the sensing element.

Sensing Element - Aluminium Oxide Ceramic with Tin Dioxide Coating

The tubular sensing element is made of Aluminum Oxide (AL2O3) based ceramic with a Tin Dioxide coating (SnO2). Tin Dioxide is the most important material because it is sensitive to combustible gasses. The ceramic substrate, on the other hand, improves heating efficiency and ensures that the sensor area is continuously heated to the working temperature.

MQ2 Gas Sensor Internal Structure Sensing Element

To summarize, the Heating System is composed of a Nickel-Chromium coil and an Aluminum Oxide-based ceramic, while the Sensing System is composed of Platinum wires and a Tin Dioxide coating.

How Does a Gas Sensor Work?

When a SnO2 semiconductor layer is heated to a high temperature, oxygen is adsorbed on the surface. When the air is clean, electrons from the conduction band of tin dioxide are attracted to oxygen molecules. This creates an electron depletion layer just beneath the surface of the SnO2 particles, forming a potential barrier. As a result, the SnO2 film becomes highly resistive and prevents electric current flow.

In the presence of reducing gasses, however, the surface density of adsorbed oxygen decreases as it reacts with the reducing gasses, lowering the potential barrier. As a result, electrons are released into the tin dioxide, allowing current to freely flow through the sensor.

MQ2 Gas Sensor Working

MQ2 Gas Sensor Module Hardware Overview

The MQ2 gas sensor is simple to use and has two different outputs. It not only provides a binary indication of the presence of combustible gasses, but also an analog representation of their concentration in air.

MQ2 Gas Sensor Module

The sensor’s analog output voltage (at the A0 pin) varies in proportion to the concentration of smoke/gas. The higher the concentration, the higher the output voltage; the lower the concentration, the lower the output voltage. The animation below shows the relationship between gas concentration and output voltage.

MQ2 Gas Sensor Module Output

This analog signal is digitized by an LM393 High Precision Comparator and made available at the Digital Output (D0) pin.

mq2 sensor lm393 comparator with sensitivity adjustment pot

The module includes a potentiometer for adjusting the sensitivity of the digital output (D0). You can use it to set a threshold so that when the gas concentration exceeds the threshold value, the module outputs LOW otherwise HIGH.

Rotating the knob clockwise increases sensitivity and counterclockwise decreases it.

mq2 sensor power and status leds

In addition, the module has two LEDs. The Power LED illuminates when the module is turned on, and the Status LED illuminates when the gas concentration exceeds the threshold value.

Technical Specifications

Here are the specifications:

Operating voltage5V
Load resistance20 KΩ
Heater resistance33Ω ± 5%
Heating consumption<800mw
Sensing Resistance10 KΩ – 60 KΩ
Concentration Range200 – 10000ppm
Preheat TimeOver 24 hour

MQ2 Gas Sensor Module Pinout

Let’s take a look at the pinout now.

MQ2 Gas Sensor Module Pinout

VCC supplies power to the module. Connect it to the 5V output of your Arduino.

GND is the ground pin.

D0 indicates the presence of combustible gasses. D0 becomes LOW when the gas concentration exceeds the threshold value (as set by the potentiometer), and HIGH otherwise.

A0 produces an analog output voltage proportional to gas concentration, so a higher concentration results in a higher voltage and a lower concentration results in a lower voltage.

Calibrating the MQ2 Gas Sensor

Because the MQ2 is a heater-driven sensor, the calibration of the sensor may drift if it is left in storage for an extended period of time.

When first used after a long period of storage (a month or more), the sensor must be fully warmed up for 24-48 hours to ensure maximum accuracy.

If the sensor has recently been used, it will only take 5-10 minutes to fully warm up. During the warm-up period, the sensor typically reads high and gradually decreases until it stabilizes.

Experiment 1 – Measuring Gas Concentration using Analog Output (A0)

In our first experiment, we will read the analog output to determine the concentration of the gas and see if it is within acceptable limits.

Wiring

Let us connect the MQ2 gas sensor to the Arduino.

Begin by connecting the VCC pin to the Arduino’s 5V pin and the GND pin to the Arduino’s Ground pin. Finally, connect the module’s A0 output pin to Analog pin #0 on the Arduino.

The following image shows the wiring.

arduino wiring mq2 gas sensor analog output

Finding the threshold value

To determine whether the gas concentration is within acceptable limits, you need to record the values your sensor outputs when exposed to various amounts of smoke/gas.

Simply run the sketch below and take your readings.

#define MQ2pin 0

float sensorValue;  //variable to store sensor value

void setup() {
	Serial.begin(9600); // sets the serial port to 9600
	Serial.println("MQ2 warming up!");
	delay(20000); // allow the MQ2 to warm up
}

void loop() {
	sensorValue = analogRead(MQ2pin); // read analog input pin 0

	Serial.print("Sensor Value: ");
	Serial.println(sensorValue);
	
	delay(2000); // wait 2s for next reading
}

When you run the sketch, you should see readings similar to the ones below:

  • In the absence of smoke/gas (around 100)
  • In the presence of smoke/gas (around 400)
finding the threshold value for mq2

This test may require some trial and error. Once you have the readings, you can use them as a threshold to trigger an action.

Arduino Code

The sketch below determines whether the gas concentration is within acceptable limits.

/* Change the threshold value with your own reading */
#define Threshold 400

#define MQ2pin 0

float sensorValue;  //variable to store sensor value

void setup() {
	Serial.begin(9600); // sets the serial port to 9600
	Serial.println("MQ2 warming up!");
	delay(20000); // allow the MQ2 to warm up
}

void loop() {
  sensorValue = analogRead(MQ2pin); // read analog input pin 0
  
  Serial.print("Sensor Value: ");
  Serial.print(sensorValue);

  if(sensorValue > Threshold)
  {
    Serial.print(" | Smoke detected!");
  }
  
  Serial.println("");
  delay(2000); // wait 2s for next reading
}

If everything is fine, you should see something similar on the serial monitor.

mq2 arduino example analog output

Experiment 2 – Detecting the Presence of Smoke/Gas using Digital Output (D0)

In our second experiment, we will use digital output to detect the presence of smoke/gas.

Wiring

We’ll reuse the previous experiment’s circuit. Simply disconnect the connection to the ADC pin and connect the D0 pin on the module to the Arduino’s digital pin #8.

The following image shows the wiring.

arduino wiring mq2 gas sensor digital output

Setting the threshold

The module has a built-in potentiometer for setting the gas concentration threshold above which the module outputs LOW and the status LED lights up.

mq2 sensor lm393 comparator with sensitivity adjustment pot

Now, to set the threshold, place the gas sensor near the smoke/gas you want to detect and turn the pot until the Status LED begins to glow. Then, turn the pot the other way just until the LED goes off.

That’s all there is to it; your module is now ready to use.

Arduino Code

Now, upload the sketch below to your Arduino.

#define MQ2pin 8

int sensorValue;  //variable to store sensor value

void setup() {
	Serial.begin(9600); // sets the serial port to 9600
	Serial.println("MQ2 warming up!");
	delay(20000); // allow the MQ2 to warm up
}

void loop() {
	sensorValue = digitalRead(MQ2pin); // read digital output pin
	Serial.print("Digital Output: ");
	Serial.print(sensorValue);
	
	// Determine the status
	if (sensorValue) {
		Serial.println("  |  Smoke: -");
	} else {
		Serial.println("  |  Smoke: Detected!");
	}
	
	delay(2000); // wait 2s for next reading
}

You should see similar output on the serial monitor.

mq2 arduino example digital output

Give your next Arduino project a nose for alcohol by including the MQ3 alcohol sensor module. This sensor detects the presence of alcohol in the air as well as its concentration. So, if you want to build your own breathalyzer to determine how much alcohol is in someone’s breath, the MQ3 alcohol sensor module is an excellent choice.

MQ3 Alcohol Sensor

The MQ3 sensor is one of the most widely used in the MQ sensor series. It is a MOS (Metal Oxide Semiconductor) sensor. Metal oxide sensors are also known as Chemiresistors because sensing is based on the change in resistance of the sensing material when exposed to alcohol.

mq3 alcohol sensor

The MQ3 alcohol sensor operates on 5V DC and consumes approximately 800mW. It can detect alcohol concentrations ranging from 25 to 500 ppm.

What does the concentration of 1 ppm mean?

Parts-per-million, or ppm for short, is the most commonly used unit for measuring gas concentration. ppm is simply the ratio of one gas to another. For example, 500ppm of alcohol means that if you could count a million gas molecules, 500 would be alcohol and the remaining 999500 would be other gases.

Internal structure of MQ3 Alcohol Sensor

The MQ3 is a heater-driven sensor. It is therefore covered with two layers of fine stainless steel mesh known as an “anti-explosion network”. It ensures that the heater element inside the sensor does not cause an explosion because we are sensing flammable gas (alcohol).

mq3 alcohol sensor parts hardware overview

It also protects the sensor and filters out suspended particles, allowing only gaseous elements to pass through the chamber.

mq3 alcohol sensor internal structure

When the outer mesh is removed, the sensor looks like this. The sensing element and six connecting legs that extend beyond the Bakelite base form the star-shaped structure. Two (H) of the six leads are in charge of heating the sensing element and are linked together by a Nickel-Chromium coil (a well-known conductive alloy).

The remaining four signal-carrying leads (A and B) are connected with platinum wires. These wires are connected to the body of the sensing element and convey slight variations in the current flowing through the sensing element.

mq3 sensing element aluminium oxide ceramic with tin dioxide coating

The tubular sensing element is made of Aluminum Oxide (AL2O3) based ceramic with a Tin Dioxide coating (SnO2). Tin Dioxide is the most important material because it is sensitive to alcohol. The ceramic substrate, on the other hand, improves heating efficiency and ensures that the sensor area is continuously heated to the working temperature.

mq3 alcohol sensor internal sensing element structure

To summarize, the Heating System is composed of a Nickel-Chromium coil and an Aluminum Oxide-based ceramic, while the Sensing System is composed of Platinum wires and a Tin Dioxide coating.

How Does the MQ3 Alcohol Sensor Work?

When a SnO2 semiconductor layer is heated to a high temperature, oxygen is adsorbed on the surface. When the air is clean, electrons from the conduction band of tin dioxide are attracted to oxygen molecules. This creates an electron depletion layer just beneath the surface of the SnO2 particles, forming a potential barrier. As a result, the SnO2 film becomes highly resistive and prevents electric current flow.

In the presence of alcohol, however, the surface density of adsorbed oxygen decreases as it reacts with the alcohol, lowering the potential barrier. As a result, electrons are released into the tin dioxide, allowing current to freely flow through the sensor.

mq3 alcohol sensor working

MQ3 Alcohol Sensor Module Hardware Overview

The MQ3 alcohol sensor is simple to use and has two different outputs. It not only provides a binary indication of the presence of alcohol, but also an analog representation of its concentration in air.

mq3 alcohol sensor module

The sensor’s analog output voltage (at the A0 pin) varies in proportion to the alcohol concentration. The higher the concentration of alcohol in the air, the higher the output voltage; the lower the concentration, the lower the output voltage. The animation below shows the relationship between alcohol concentration and output voltage.

mq3 alcohol sensor module working animation

This analog signal is digitized by an LM393 High Precision Comparator and made available at the Digital Output (D0) pin.

mq3 sensor lm393 comparator with sensitivity adjustment pot

The module includes a potentiometer for adjusting the sensitivity of the digital output (D0). You can use it to set a threshold so that when the alcohol concentration exceeds the threshold value, the module outputs LOW otherwise HIGH.

Rotating the knob clockwise increases sensitivity and counterclockwise decreases it.

mq3 sensor power and status leds

In addition, the module has two LEDs. The Power LED illuminates when the module is turned on, and the Status LED illuminates when the alcohol concentration exceeds the threshold value.

Technical Specifications

Here are the specifications:

Operating voltage5V
Load resistance200 KΩ
Heater resistance33Ω ± 5%
Heating consumption<800mw
Sensing Resistance1 MΩ – 8 MΩ
Concentration Range25 – 500 ppm
Preheat TimeOver 24 hour

MQ3 Alcohol Sensor Module Pinout

Let’s take a look at the pinout now.

mq3 alcohol sensor pinout

VCC supplies power to the module. Connect it to the 5V output of your Arduino.

GND is the ground pin.

D0 indicates the presence of alcohol. D0 becomes LOW when the alcohol concentration exceeds the threshold value (as set by the potentiometer), and HIGH otherwise.

A0 produces analog output voltage proportional to alcohol concentration, so a higher concentration results in a higher voltage and a lower concentration results in a lower voltage.

Calibrating the MQ3 Alcohol Sensor

Because the MQ3 is a heater-driven sensor, the calibration of the sensor may drift if it is left in storage for an extended period of time.

When first used after a long period of storage (a month or more), the sensor must be fully warmed up for 24-48 hours to ensure maximum accuracy.

If the sensor has recently been used, it will only take 5-10 minutes to fully warm up. During the warm-up period, the sensor typically reads high and gradually decreases until it stabilizes.

Experiment 1 – Measuring Alcohol Concentration using Analog Output (A0)

In our first experiment, we will read the analog output to measure the alcohol concentration and estimate the level of alcohol intoxication.

Wiring

Let us connect the MQ3 alcohol sensor to the Arduino.

Begin by connecting the VCC pin to the Arduino’s 5V pin and the GND pin to the Arduino’s Ground pin. Finally, connect the module’s A0 output pin to Analog pin #0 on the Arduino.

The following image shows the wiring.

arduino wiring mq3 alcohol sensor to read analog output

Finding the threshold values

To estimate the level of alcohol intoxication, you need to record the values your sensor outputs when you blow on it before and after consuming alcohol.

Simply run the sketch below and take your readings.

Note:

If you are not legally permitted to consume alcoholic beverages, use an isopropyl alcohol bottle or any hand sanitizer bottle for your testing. Don’t get alcohol on the sensor! Simply squeeze the bottle to allow the alcohol vapors to enter the sensor and take your readings.

#define MQ3pin 0

float sensorValue;  //variable to store sensor value

void setup() {
	Serial.begin(9600); // sets the serial port to 9600
	Serial.println("MQ3 warming up!");
	delay(20000); // allow the MQ3 to warm up
}

void loop() {
	sensorValue = analogRead(MQ3pin); // read analog input pin 0

	Serial.print("Sensor Value: ");
	Serial.println(sensorValue);
	
	delay(2000); // wait 2s for next reading
}

When you run the sketch, you should see readings similar to the ones below:

  • In the absence of alcohol (around 120)
  • In the presence of alcohol (around 500)
calibrating mq3 alcohol sensor

This test may require some trial and error. Once you have the readings, you can use them as a threshold to trigger an action.

Arduino Code

The sketch below estimates the level of alcohol intoxication using the following threshold values:

  • < 120 is sober
  • 120-400 is drinking – but within legal limits
  • > 400 is drunk
/* Replace these values with your own readings */
#define Sober 120   // Define max value that we consider sober
#define Drunk 400   // Define min value that we consider drunk

#define MQ3pin 0

float sensorValue;  //variable to store sensor value

void setup() {
	Serial.begin(9600); // sets the serial port to 9600
	Serial.println("MQ3 warming up!");
	delay(20000); // allow the MQ3 to warm up
}

void loop() {
	sensorValue = analogRead(MQ3pin); // read analog input pin 0

	Serial.print("Sensor Value: ");
	Serial.print(sensorValue);
	
	// Determine the status
	if (sensorValue < Sober) {
		Serial.println("  |  Status: Stone Cold Sober");
	} else if (sensorValue >= Sober && sensorValue < Drunk) {
		Serial.println("  |  Status: Drinking but within legal limits");
	} else {
		Serial.println("  |  Status: DRUNK");
	}
	
	delay(2000); // wait 2s for next reading
}

If everything is fine, you should see something similar on the serial monitor.

simple breathalyzer using analog output

Experiment 2 – Detecting the Presence of Alcohol using Digital Output (D0)

In our second experiment, we will use digital output to detect the presence of alcohol.

Wiring

We’ll reuse the previous experiment’s circuit. Simply disconnect the connection to the ADC pin and connect the D0 pin on the module to the Arduino’s digital pin #8.

The following image shows the wiring.

arduino wiring mq3 alcohol sensor to read digital output

Setting the threshold

The module has a built-in potentiometer for setting an alcohol concentration threshold above which the module outputs LOW and the status LED lights up.

digital output of mq3 alcohol sensor

Now, to set the threshold, let the alcohol vapors enter the sensor and turn the pot clockwise until the Status LED is on. Then, turn the pot back counterclockwise just until the LED goes off.

That’s all there is to it; your module is now ready to use.

Arduino Code

Now, upload the sketch below to your Arduino.

#define MQ3pin 8

int sensorValue;  //variable to store sensor value

void setup() {
	Serial.begin(9600); // sets the serial port to 9600
	Serial.println("MQ3 warming up!");
	delay(20000); // allow the MQ3 to warm up
}

void loop() {
	sensorValue = digitalRead(MQ3pin); // read digital output pin
	Serial.print("Digital Output: ");
	Serial.print(sensorValue);
	
	// Determine the status
	if (sensorValue) {
		Serial.println("  |  Alcohol: -");
	} else {
		Serial.println("  |  Alcohol: Detected!");
	}
	
	delay(2000); // wait 2s for next reading
}

You should see similar output on the serial monitor.

alcohol detection using digital output

For most of our Arduino projects that require knowing if someone has left or entered the area, the PIR sensor is an excellent choice. However, because they only detect movement from living things, they will generate fewer false alarms.

This is where a microwave sensor like the RCWL-0516 comes in handy. The RCWL-0516 microwave sensor detects any movement from any object and does not rely on heat signatures, making it more reliable in hot environments where a PIR sensor may not be as effective.

Before going into the nitty-gritty, let’s first understand how the RCWL-0516 sensor actually works.

How does Doppler radar work?

The RCWL-0516 module employs “Doppler Radar” – a specialized radar that makes use of the Doppler Effect (also known as Doppler shift) to detect motion and trigger proximity alerts.

What is the Doppler effect?

The Doppler effect, named after the Austrian physicist Christian Doppler who proposed it in 1842, describes the change in frequency observed by a stationary observer when the source of the frequency is moving. This holds true for all sorts of waves, such as water, light, radio, and sound.

This effect is something you’ve heard many times, perhaps without even realizing it, of the siren dropping in pitch as an ambulance passes by.

doppler effect animation

As the ambulance approaches you, the sound waves from the siren are squeezed into a shorter distance, increasing their frequency, which we hear as a higher pitch. The opposite happens when the ambulance moves away from you, causing the sound waves to become lower in frequency and lower in pitch. As a result, you hear a noticeable drop in the siren’s pitch as it passes.

A Doppler radar works by bouncing a microwave signal off a desired target and reading the frequency of the returned signal. By analyzing how the target’s motion has altered the frequency of the transmitted signal, the target’s velocity can be measured.

You may have seen police officers using radar speed guns to catch people driving too fast. These radar speed guns, like other types of radar, consist of a microwave transmitter and receiver. They send out a microwave signal and then receive it after it bounces off the target.

Due to the Doppler effect, if the object is moving towards or away from the gun, the frequency of the reflected microwave signal is different from that of the transmitted signal.

doppler effect radar gun car approaching

When a car approaches the radar, the frequency of the returned signal is greater than the frequency of the transmitted signal; when the car moves away, the frequency is lower.

doppler effect radar gun car receding

Based on that difference, the radar speed gun calculates the speed of the car from which the signal bounced.

The RCWL-0516 Hardware Overview

The RCWL-0516 is an active sensor, not a passive one like the HC-SR501 Passive Infrared (PIR). It sends out microwaves at a frequency of about 3.18 GHz and measures the radiation that is reflected back.

The RCWL-0516 itself is pretty straightforward and works out of the box. Simply apply power between 4V–28V and ground. The sensor output goes HIGH for two seconds when motion is detected and goes LOW when idle (no motion detected).

And the best part is that it can detect motion up to 7 meters away while only consuming less than 3 mA of current.

RCWL-9196 IC

At the heart of the sensor is a doppler radar controller IC – RCWL-9196. This IC is very similar to the BISS0001 IC that is used in PIR sensors.

rcwl0516 microwave radar sensor rcwl9196 ic

The chip also supports repeat triggers and has a 360-degree detection area without blind spots.

Microwave Antenna and RF Power Amplifier

At the heart of the RF circuitry is a MMBR941M RF power amplifier. It takes a low-power RF signal and boosts it to a higher power level. Typically, this RF power amplifier drives the sensor’s antenna.

rcwl0516 microwave radar sensor mmbr941m rf power amplifier

Because the microwave antenna is integrated on the PCB itself, the RCWL-0516 has become a completely self-contained unit.

rcwl0516 microwave radar sensor microwave antenna

Power

The RCWL-0516 uses less than 3 mA of current and works between 4 and 28 volts, making it an ideal component for a battery-powered design.

In addition to this, it features a 3.3V voltage regulator that can supply up to 100 mA of current to power external circuits.

Technical Specifications

Here are the specifications:

Operating Voltage4-28V (typically 5V)
Detection Distance5-7 meters
Maximum Current Draw~ 2.7mA
Operating Frequency~3.18GHz
Transmission Power30mW (max.)
Signal length~ 2s
Regulated Output3.3V, 100mA

Optional Component – LDR

The RCWL-0516 also has support for an optional light-dependent resistor (LDR), which enables the device to operate only in the dark. This is useful for building motion-sensitive lighting systems.

rcwl0516 microwave radar sensor ldr connections

There are actually two ways you can connect an LDR to the sensor.

  • By using the two CDS pads on the top of the sensor.
  • By using the CDS pin on the bottom and connecting your LDR between it and ground.

What does CDS stand for?

CDS stands for cadmium sulphide, which is the photoactive component in the majority of LDRs. Because of this, LDRs are sometimes called CDS photoresistors.

RCWL-0516 Jumper Settings

If you look closely at the module, you’ll notice three solder jumpers on the back.

rcwl0516 microwave radar sensor jumper settings

By populating these jumpers with appropriate resistors and capacitors, you can change the sensor’s default settings:

C-TM (Pulse length Adjustment): By populating C-TM with a suitable SMD capacitor, you can extend the output pulse length. The default pulse length is 2 seconds. Higher capacitor values result in longer pulses. For example, 0.2µF extends the output pulse to 50s, while 1µF extends it to 250s.

R-GN (Detection Range Adjustment): By populating R-GN with a suitable resistor, you can reduce the sensor’s detection range. By default, the detection range is set to 7m. A 1M resistor reduces the detection range to 5m, while a 270K resistor reduces it to 1.5m.

R-CDS (Light Sensitivity Adjustment): This is an alternative to soldering the LDR. Any resistor in the 47K – 100K range will suffice. The lower the value, the brighter the light must be in order to disable the trigger.

RCWL-0516 Sensor Pinout

The RCWL-0516 sensor brings out the following connections:

rcwl0516 microwave radar sensor module pinout

3V3 is the output from the onboard 3.3V regulator, not the power supply input. If you need a clean 3.3V output to power external logic circuitry, you can use it. It can provide up to 100mA of current.

GND is the ground pin.

OUT pin is the 3.3V TTL logic output. It goes HIGH for two seconds when motion is detected and goes LOW when idle (no motion detected).

VIN is the power supply for the sensor. You can connect an input voltage anywhere between 4 to 28V to this pin, although 5V is commonly used.

CDS pins are where you can attach a light dependent resistor (LDR). Adding this component allows the RCWL-0516 to operate only in the dark.

Experiment 1: Using the RCWL-0516 Sensor as a Standalone Unit

One of the reasons why the RCWL-0516 sensor is extremely popular is that it is a very versatile sensor that is quite capable on its own. You can increase its versatility even further by connecting it to a microcontroller like an Arduino.

For our first experiment, we’ll use the RCWL-0516 to show how useful it is on its own.

The wiring for this experiment is very simple. Connect the batteries to the sensor’s VIN and GND and a small red LED to the output pin via a 220Ω current limiting resistor. That’s all!

Now when the RCWL-0516 detects motion, the output pin will go “HIGH” and light up the LED!

rcwl0516 microwave radar sensor wiring for standalone application

You can connect this output directly to the relay module if you want to turn something ON/OFF based on motion.

Radar signals can penetrate non-conductive materials such as plastic. This means you can place one of these sensors inside a plastic enclosure if you want to hide it or protect it from accidental damage.

Experiment 2: Adding an LDR

For our next experiment, we will be using an LDR (or Light Dependent Resistor).

It’s very easy to hook up an LDR. You can either use the two CDS pads on the top of the sensor made specifically to attach the LDR, or use the CDS pin on the bottom and connect your LDR between it and ground.

You may use any LDR that you can get your hands on. Also, LDRs don’t have polarity, so you can connect them in any direction you want.

rcwl0516 microwave radar sensor with ldr for motion sensitive lighting

When the LDR is exposed to light, you will notice that the sensor produces no output. However, once the room is darkened, it resumes normal operation.

This has several real-world uses, such as controlling room lighting or spotting intruders at night.

Experiment 3: Reading RCWL-0516 with an Arduino

In our next experiment, we will use Arduino to poll the sensor pin continuously to see if it has detected any motion.

This project may, of course, be expanded to respond to motion in a variety of ways, such as turning lights on and off, activating a fan or a Halloween prop, or even taking a picture of an intruder.

Wiring

Connecting the RCWL-0516 sensor to the Arduino is really simple. Power the RCWL-0516 with 5V and connect ground to ground. Because the RCWL-0516 functions as a digital output, all you need to do is listen to the output pin. So connect the output to the digital pin #8 on the Arduino.

rcwl0516 microwave radar sensor arduino wiring

Now you are ready to upload some code and get the RCWL-0516 working.

Arduino Code

The code is very simple. It basically just keeps track of whether the input to pin #8 is HIGH or LOW.

int ledPin = 13;                // choose the pin for the LED
int inputPin = 8;               // choose the input pin (for Radar sensor)
int motionState = LOW;          // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
 
void setup() {
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input
 
  Serial.begin(9600);
}
 
void loop() {
  val = digitalRead(inputPin);  // read input value
  
  if (val == HIGH) {  // check if the input is HIGH
    digitalWrite(ledPin, HIGH);  // turn LED ON
    
    if (motionState == LOW) {
      Serial.println("Motion detected!"); // print on output change
      motionState = HIGH;
    }
  }
  else {
    digitalWrite(ledPin, LOW); // turn LED OFF
    
    if (motionState == HIGH) {
      Serial.println("Motion ended!");  // print on output change
      motionState = LOW;
    }
  }
}

With the sensor pointing up, swipe your hand over the sensor. You should see a “Motion Detected” message printed on the serial terminal.

rcwl0516 microwave radar sensor arduino sketch output serial monitor

One of the simplest and cheapest ways to incorporate temperature sensing into your Arduino project is to use a DS18B20 1-Wire Temperature Sensor. These sensors are fairly precise and require no external components to function. So, with just a few connections and some Arduino code, you’ll be able to sense temperature in no time!

DS18B20 1-Wire Temperature Sensor

The DS18B20 is a 1-Wire® temperature sensor manufactured by Dallas Semiconductor (acquired by Maxim Integrated). Because it is a 1-wire device, it only needs one digital pin to communicate with the microcontroller.

The sensor is typically available in two form factors. One comes in a TO-92 package, which resembles a simple transistor. The other comes in the form of a waterproof probe, which is more useful when measuring something far away, underwater, or beneath the ground.

Types Of DS18B20 Temperature Sensor
Types Of DS18B20 Temperature Sensor

The DS18B20 temperature sensor is fairly precise and does not require any external components to function. It has a temperature range of -55°C to +125°C and an accuracy of ±0.5°C.

The temperature sensor’s resolution can be set to 9, 10, 11, or 12 bits. The default resolution at power-up, however, is 12-bit (i.e., 0.0625°C precision).

The sensor operates on a 3V to 5.5V power supply and draws only 1mA during active temperature conversions.

Here are the specifications:

Power Supply3V to 5.5V
Current Consumption1mA
Temperature Range-55 to 125°C
Accuracy±0.5°C
Resolution9 to 12 bit (selectable)
Conversion Time< 750ms

Multiple DS18B20 On a Single Bus

One of the DS18B20’s features is that multiple DS18B20s can coexist on the same 1-Wire bus. Because each DS18B20 is pre-programmed with a unique 64-bit serial code, they can be distinguished from one another.

This feature can be extremely useful when you need to control multiple DS18B20s spread across a large area.

Check out this tutorial to learn how to read multiple DS18B20 temperature sensors.

DS18B20 Sensor Pinout

DS18B20 Pinout Including Waterproof Temperature Sensor

GND is the ground pin.

DQ is a 1-Wire Data Bus that should be connected to a digital pin on the microcontroller.

VDD pin provides power to the sensor, which can range from 3.3V to 5V.

Wiring a DS18B20 Temperature Sensor to an Arduino

Let’s connect the DS18B20 to the Arduino.

The connections are straightforward. Begin by connecting VDD to the Arduino’s 5V pin and GND to ground.

Connect the signal pin DQ to Arduino’s digital pin 2. To keep the data transfer stable, you’ll also need to connect the 4.7k pull-up resistor between the signal and power pins (Note: internal pull-ups on the arduino do not work here).

To avoid overheating and damage, make sure the DS18B20 is connected properly.

Wiring DS18B20 Temperature Sensor to Arduino
Wiring DS18B20 Temperature Sensor to Arduino

If you’re using the waterproof version of the DS18B20, connect the red wire to 5V, the black wire to ground, and the yellow wire to digital pin 2 on the Arduino. You still have to connect a 4.7K pullup resistor between the data and the 5V.

Wiring Waterproof DS18B20 Temperature Sensor to Arduino
Wiring Waterproof DS18B20 Temperature Sensor to Arduino

Installing Library For DS18B20

The 1-Wire protocol is a bit complicated, and it takes a lot of code to make it work. DallasTemperature.h was written to abstract away this unnecessary complexity, allowing us to issue simple commands to obtain temperature readings from the sensor.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the library index and update the list of installed libraries.

Arduino Library Installation - Selecting Manage Libraries in Arduino IDE

Filter your search by entering ‘ds18b20’. There should be a couple of entries. Look for DallasTemperature by Miles Burton. Click on that entry and then choose Install.

Installing Dallas Temperature Library In Arduino IDE

Dallas Temperature is a hardware-specific library that handles lower-level functions. It must be paired with the One Wire Library in order to communicate with any one-wire device, not just the DS18B20. Install this library as well.

Installing OneWire Library In Arduino IDE

Arduino Example Code

The sketch below will provide you with a thorough understanding of how to read temperature readings from a DS18B20 Temperature Sensor and can serve as the foundation for more practical experiments and projects.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into digital pin 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);	

// Pass oneWire reference to DallasTemperature library
DallasTemperature sensors(&oneWire);

void setup(void)
{
  sensors.begin();	// Start up the library
  Serial.begin(9600);
}

void loop(void)
{ 
  // Send the command to get temperatures
  sensors.requestTemperatures(); 

  //print the temperature in Celsius
  Serial.print("Temperature: ");
  Serial.print(sensors.getTempCByIndex(0));
  Serial.print((char)176);//shows degrees character
  Serial.print("C  |  ");
  
  //print the temperature in Fahrenheit
  Serial.print((sensors.getTempCByIndex(0) * 9.0) / 5.0 + 32.0);
  Serial.print((char)176);//shows degrees character
  Serial.println("F");
  
  delay(500);
}

Here’s what the output looks like on the serial monitor.

DS18B20 Temperature Sensor Output On Serial Monitor

Code Explanation:

The sketch begins by including the OneWire.h and DallasTemperature.h libraries and declaring the Arduino pin to which the sensor’s signal pin is connected.

#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2

To communicate with the DS18B20 sensor, we do two things. First, we create a one-wire object and pass the sensor’s signal pin as a parameter. Second, we create a DallasTemperature library object and pass the reference of the one-wire object (that we just created) as a parameter.

OneWire oneWire(ONE_WIRE_BUS);	
DallasTemperature sensors(&oneWire);

In the setup section, we establish serial communication with the PC and call the begin() function. The begin() function scans the bus for connected DS18B20 sensors and sets the bit resolution (12 bits) for each one.

void setup(void) {
  sensors.begin();	// Start up the library
  Serial.begin(9600);
}

In the loop section, we call the requestTemperatures() function, which instructs all sensors on the bus to perform a temperature conversion.

Then we call the getTempCByIndex(deviceIndex) function, where deviceIndex is the location of the sensor on the bus. This function reads the temperature reading from the corresponding sensor and returns it. 

If you only have one DS18B20 on the bus, set deviceIndex to 0.

void loop(void) { 
  // Send the command to get temperatures
  sensors.requestTemperatures(); 

  //print the temperature in Celsius
  Serial.print("Temperature: ");
  Serial.print(sensors.getTempCByIndex(0));
  Serial.print((char)176);//shows degrees character
  Serial.print("C  |  ");
  
  //print the temperature in Fahrenheit
  Serial.print((sensors.getTempCByIndex(0) * 9.0) / 5.0 + 32.0);
  Serial.print((char)176);//shows degrees character
  Serial.println("F");
  
  delay(500);
}

Other useful functions in the DallasTemperature.h library

There are many useful functions you can use with the DallasTemperature object. Some of them are listed below:

  • setResolution() function sets the resolution of the DS18B20’s internal ADC to 9, 10, 11, or 12-bits, which correspond to 0.5°C, 0.25°C, 0.125°C, and 0.0625°C, respectively.
  • bool getWaitForConversion() function returns the value of the waitForConversion flag, which is useful when determining whether or not a temperature conversion is complete.
  • setHighAlarmTemp() and setLowAlarmTemp() functions configure a device’s internal high and low temperature alarms in degrees Celsius. The acceptable temperature range is -55 to 125°C.
  • bool hasAlarm() function returns true when the temperature exceeds either the high or low alarm set point.

One of the DS18B20’s features is that multiple DS18B20s can coexist on the same 1-Wire bus. Because each DS18B20 is pre-programmed with a unique 64-bit serial code, they can be distinguished from one another.

The following tutorial will show you how to read the temperature from multiple DS18B20s connected to the same bus. This feature can be extremely useful when you need to control multiple DS18B20s spread across a large area.

Before proceeding with this tutorial, you should be familiar with the basics of the DS18B20 one-wire temperature sensor. Please consider reading the tutorial below first.

Without further ado, let’s connect the DS18B20s to our Arduino.

Wiring Multiple DS18B20 Sensors to Arduino

Connections are pretty straightforward.

Begin by connecting all of the DS18B20s in parallel, sharing all of the VDD, GND, and signal pins. Then connect VDD to the Arduino’s 5V output, GND to the Arduino’s ground, and signal pin to digital pin 2 on the Arduino.

To keep the data transfer stable, add one 4.7k pull-up resistor for the entire bus between the signal and power pin (Note: internal pull-ups on the Arduino do not work here).

Wiring Multiple DS18B20 Temperature Sensors With Arduino
Wiring Multiple DS18B20 Temperature Sensors With Arduino

Installing Library For DS18B20

The 1-Wire protocol is a bit complicated, and it takes a lot of code to make it work. DallasTemperature.h was written to abstract away this unnecessary complexity, allowing us to issue simple commands to obtain temperature readings from the sensor.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the library index and update the list of installed libraries.

Arduino Library Installation - Selecting Manage Libraries in Arduino IDE

Filter your search by entering ‘ds18b20’. There should be a couple of entries. Look for DallasTemperature by Miles Burton. Click on that entry and then choose Install.

Installing Dallas Temperature Library In Arduino IDE

Dallas Temperature is a hardware-specific library that handles lower-level functions. It must be paired with the One Wire Library in order to communicate with any one-wire device, not just the DS18B20. Install this library as well.

Installing OneWire Library In Arduino IDE

Methods for Reading Multiple DS18B20s

There are two methods for reading multiple DS18B20s connected to the same bus.

  • By Index – In this method, we use an index (assigned by the library) to pinpoint each sensor and read its temperature.
  • By Address – In this method, we read each sensor individually by taking advantage of the fact that each sensor has its own 64-bit address (preprogrammed).

Let’s go over each method one by one.

Method 1: Reading DS18B20 By Index

When the Dallas Temperature library is initialized, it scans the bus for all sensors that share the same bus. It considers the entire bus to be an array of sensors and assigns each one an index. As a result, we can identify each sensor by its index and read temperature.

Here is a simple sketch that locates all the DSB1820s on the bus and then shows the temperature of each one.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into digital pin 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);	

// Pass oneWire reference to DallasTemperature library
DallasTemperature sensors(&oneWire);

int deviceCount = 0;
float tempC;

void setup(void)
{
  sensors.begin();	// Start up the library
  Serial.begin(9600);
  
  // locate devices on the bus
  Serial.print("Locating devices...");
  Serial.print("Found ");
  deviceCount = sensors.getDeviceCount();
  Serial.print(deviceCount, DEC);
  Serial.println(" devices.");
  Serial.println("");
}

void loop(void)
{ 
  // Send command to all the sensors for temperature conversion
  sensors.requestTemperatures(); 
  
  // Display temperature from each sensor
  for (int i = 0;  i < deviceCount;  i++)
  {
    Serial.print("Sensor ");
    Serial.print(i+1);
    Serial.print(" : ");
    tempC = sensors.getTempCByIndex(i);
    Serial.print(tempC);
    Serial.print((char)176);//shows degrees character
    Serial.print("C  |  ");
    Serial.print(DallasTemperature::toFahrenheit(tempC));
    Serial.print((char)176);//shows degrees character
    Serial.println("F");
  }
  
  Serial.println("");
  delay(1000);
}

The result looks like this:

Output of Multiple DS18B20 - By Index Method

Code Explanation:

The sketch begins by including libraries, declaring the pin to which the sensor bus is connected, and creating a DallasTemperature library object. In the same global scope, two variables are defined: deviceCount stores the number of sensors found on the bus, and tempC stores the temperature read from each sensor.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into digital pin 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);	

// Pass oneWire reference to DallasTemperature library
DallasTemperature sensors(&oneWire);

int deviceCount = 0;
float tempC;

In the setup section of the code, we first call the begin() function. It initializes the bus and detects all connected DS18B20s. Each sensor is then assigned an index, and the bit resolution is set to 12-bit.

The getDeviceCount() function is then called to print the number of devices discovered on the bus.

void setup(void)
{
  sensors.begin();	// Start up the library
  Serial.begin(9600);
  
  // locate devices on the bus
  Serial.print("Locating devices...");
  Serial.print("Found ");
  deviceCount = sensors.getDeviceCount();
  Serial.print(deviceCount, DEC);
  Serial.println(" devices.");
  Serial.println("");
}

In the looping section of the code, we use the requestTemperatures() function to instruct all sensors on the bus to perform a temperature conversion.

Finally, we use a simple for loop to iterate through the array of sensors and read the temperature of a specific DS18B20 by calling the function getTempCByIndex(i)

void loop(void)
{ 
  // Send command to all the sensors for temperature conversion
  sensors.requestTemperatures(); 
  
  // Display temperature from each sensor
  for (int i = 0;  i < deviceCount;  i++)
  {
    Serial.print("Sensor ");
    Serial.print(i+1);
    Serial.print(" : ");
    tempC = sensors.getTempCByIndex(i);
    Serial.print(tempC);
    Serial.print((char)176);//shows degrees character
    Serial.print("C  |  ");
    Serial.print(DallasTemperature::toFahrenheit(tempC));
    Serial.print((char)176);//shows degrees character
    Serial.println("F");
  }
  
  Serial.println("");
  delay(1000);
}

Method 2: Reading DS18B20 By Address

Each DS18B20 is programmed with a unique 64-bit address at the time of manufacture, allowing them to be distinguished from one another. So we’ll use one sketch to find and record the address of each sensor, and then another sketch to read each sensor individually.

Finding the Addresses of DS18B20s on a Bus

The sketch below detects all DS18B20s on the bus and prints their one-wire addresses on the serial monitor.

It is recommended that you connect only one sensor at a time so that you can find each sensor’s address and assign it a name.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// variable to hold device addresses
DeviceAddress Thermometer;

int deviceCount = 0;

void setup(void)
{
  // start serial port
  Serial.begin(9600);

  // Start up the library
  sensors.begin();

  // locate devices on the bus
  Serial.println("Locating devices...");
  Serial.print("Found ");
  deviceCount = sensors.getDeviceCount();
  Serial.print(deviceCount, DEC);
  Serial.println(" devices.");
  Serial.println("");
  
  Serial.println("Printing addresses...");
  for (int i = 0;  i < deviceCount;  i++)
  {
    Serial.print("Sensor ");
    Serial.print(i+1);
    Serial.print(" : ");
    sensors.getAddress(Thermometer, i);
    printAddress(Thermometer);
  }
}

void loop(void)
{}

void printAddress(DeviceAddress deviceAddress)
{ 
  for (uint8_t i = 0; i < 8; i++)
  {
    Serial.print("0x");
    if (deviceAddress[i] < 0x10) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
    if (i < 7) Serial.print(", ");
  }
  Serial.println("");
}

Now, open up Serial Monitor. You’ll get to see something like this.

Finding One-Wire Address Of All DS18B20 On the Bus

Make a note of all the addresses because we’ll need them in the next sketch.

Reading DS18B20s By Address

The sketch below uses the addresses of the DS18B20s to read the temperatures. Before you begin uploading the sketch, you must replace the addresses of the DS18B20s with the ones found in the previous sketch.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// Addresses of 3 DS18B20s
uint8_t sensor1[8] = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC };
uint8_t sensor2[8] = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27 };
uint8_t sensor3[8] = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6 };

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

void loop(void)
{
  sensors.requestTemperatures();
  
  Serial.print("Sensor 1: ");
  printTemperature(sensor1);
  
  Serial.print("Sensor 2: ");
  printTemperature(sensor2);
  
  Serial.print("Sensor 3: ");
  printTemperature(sensor3);
  
  Serial.println();
  delay(1000);
}

void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  Serial.print(tempC);
  Serial.print((char)176);
  Serial.print("C  |  ");
  Serial.print(DallasTemperature::toFahrenheit(tempC));
  Serial.print((char)176);
  Serial.println("F");
}

The output of the above sketch looks like:

Output of Multiple DS18B20 - By Address Method

Code Explanation:

The sketch begins as usual by including libraries, declaring the pin to which the sensor bus is connected, and creating an object of the DallasTemperature library.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

Next, we enter the previously discovered addresses for each temperature sensor.

uint8_t sensor1[8] = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC };
uint8_t sensor2[8] = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27 };
uint8_t sensor3[8] = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6 };

In the setup section, we initialize serial communication with the PC and call the begin() function. The begin() function initializes the bus, detects all connected DS18B20s, and sets the bit resolution for each to 12-bit.

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

In the looping section of the code, we use the requestTemperatures() function to instruct all sensors on the bus to perform a temperature conversion.

Finally, we call the custom function printTemperature(deviceAddress) to print the temperature of the sensor whose deviceAddress is passed as a parameter.

void loop(void)
{
  sensors.requestTemperatures();
  
  Serial.print("Sensor 1: ");
  printTemperature(sensor1);
  
  Serial.print("Sensor 2: ");
  printTemperature(sensor2);
  
  Serial.print("Sensor 3: ");
  printTemperature(sensor3);
  
  Serial.println();
  delay(1000);
}

The custom function printTemperature() simply calls the library’s getTempC(deviceAddress) function to display temperature in Celsius and DallasTemperature::toFahrenheit() function to display temperature in Fahrenheit.

void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  Serial.print(tempC);
  Serial.print((char)176);
  Serial.print("C  |  ");
  Serial.print(DallasTemperature::toFahrenheit(tempC));
  Serial.print((char)176);
  Serial.println("F");
}

If you want to measure temperature with an Arduino, there are many sensors to choose from. Some of the popular options include the DHTxx series, DS18B20, LM35, and TMP36, among others, each offering unique features and capabilities to suit your specific project needs.

But what if you want to measure the temperature of something as hot as a volcano (which can reach well over 1000°C or 1800°F) or something super-cold like liquid nitrogen (which can be around −195.8 °C or −320 °F)? In such cases, the normal temperature sensor will either melt or freeze completely.

So, how do you measure extremely hot or cold things? With a cunning pair of electric cables called a Thermocouple. A thermocouple is a type of temperature sensor that uses the thermoelectric effect to measure temperatures ranging from -330°F to +2460°F.

This tutorial will guide you on how to interface the MAX6675 thermocouple module—one of the most commonly used, inexpensive, yet accurate thermocouple modules—to an Arduino. But first, let’s take a quick refresher on the basics of thermocouples.

Basics of Thermocouples

A thermocouple is made up of two dissimilar metal wires (the term “dissimilar” simply means different).

a typical thermocouple

The metal wires are connected together in only one place, typically the tip of the thermocouple, which is known as the Hot Junction, Measurement Junction, Sensing Point, or Sensing Junction. As the name implies, this junction is exposed to the heat source of interest.

The opposite end of the metal wires is known as the Cold Junction and is connected to the measuring device. Typically, the cold junction is not exposed to the same level of thermal energy as the hot junction.

Thermoelectric Effect

All thermocouples work the same way—they generate a small voltage when they are exposed to heat.

When you heat up a piece of metal, the heat excites the electrons in the metal, causing them to jiggle around. As the metal gets hotter, more electrons tend to “diffuse” and move towards the cooler end of the metal.

thermoelectric effect animation

This makes the hotter end slightly positively charged and the cooler end slightly negatively charged, creating a voltage difference. This is known as the Thermoelectric Effect or Seebeck Effect, named after the German scientist Thomas Seebeck, who discovered this phenomenon in 1821.

Thermocouple Working

A thermocouple operates based on the movement of electrons in its metal wires caused by the heat difference between the hot and cold junctions.

If the two wires of the thermocouple were made up of the same type of metal, say copper, electrons in both wires would move away from the heat and accumulate at the cold ends in equal amounts, resulting in no measurable voltage difference.

thermocouple working similar metals

But if you recall, thermocouples are made up of two different types of metal wire. So if two wires of the thermocouple were made up of different materials, say one of copper and one of iron, the metals would conduct heat differently, resulting in a distinct temperature gradient. This causes varying electron buildup at the cold ends, resulting in a measurable voltage difference.

thermocouple working dissimilar metals

This voltage difference is very small. The actual change in voltage per degree Celsius is minuscule. For example, for a Type-K thermocouple, the change is about 41 µV/°C.

Thermocouple Wire Leads

When exposed to heat, the electrons in each of the thermocouple wires react differently and move at different rates.

The wire in which more electrons are accumulated at the cold junction is called negative wire lead, while the wire in which fewer electrons are accumulated at the cold junction is called positive wire lead.

This difference in charge between the positive and negative wire leads can be measured and used to determine the temperature at the hot junction.

Type-K Thermocouple

There are different types of thermocouples, such as Type-J, Type-K, Type-E, Type-T, etc., based on the combination of metals or alloys used for the two wires. Each type of thermocouple has its own characteristic function, temperature range, accuracy, and application.

thermocouple voltage temperature characteristic

However, the most widely used thermocouple in industrial applications is Type-K, because it responds predictably over a wide temperature range (around -328 °F to +2300 °F) and has a sensitivity of approximately 41 μV/°C. It consists of a positive wire made of Chromel (nickel-chromium alloy) and a negative wire made of Alumel (nickel-aluminum alloy).

Thermocouple Digitizer

To make the thermocouple useful, it is necessary to calibrate it by testing it against known temperatures and recording the voltages generated. A formula can then be used to calculate the temperature based on the measured voltage.

This is where thermocouple digitizer ICs like the MAX6675 IC come into play. These integrated circuits (ICs) are designed to perform cold-junction compensation and digitize the signal received from a thermocouple.

MAX6675 Thermocouple Module

The MAX6675 thermocouple module typically includes a MAX6675 breakout board and a Type-K thermocouple probe. Let’s learn more about them.

MAX6675 Breakout

At the heart of the breakout is a cold-junction-compensated Type-K thermocouple digitizer IC from Microchip, the MAX6675.

max6675 breakout

The breakout takes a standard Type-K thermocouple in one end, digitizes the temperature measured and sends that data out the other end via a SPI interface, thereby interpreting the data and translating it for you to read!

The MAX6675 IC includes a 12-bit analog-to-digital converter (ADC), which means the IC can resolve temperatures to 0.25°C (12-bit resolution).

The MAX6675 can measure temperatures ranging from 0 °C to +1024 °C with an accuracy of ±3 °C. However, keep in mind that the range is dependent on the type of probe you use.

In addition to its low cost, small size, and wide range, the MAX6675 operates on +3.0V to +5.5V and draws approximately 700 µA. The maximum current that it can draw is approximately 1.5 mA.

Type-K Thermocouple Probe

The thermocouple probe that comes with the module is approximately 18 inches in length and has a measurement range of 0 °C to 80 °C.

type k thermocouple probe

The red terminal of the probe is the positive lead made of Chromel (nickel-chromium alloy), and the blue terminal is the negative lead made of Alumel (nickel-aluminum alloy).

The probe has fiberglass insulation, a material known for its ability to withstand high temperatures and harsh conditions. This makes it a suitable choice for a wide range of projects.

The probe terminates in an M6 threaded connection. This type of connection allows for the thermocouple to be attached to an object such as a heatsink, where it can be screwed in or secured with a nut.

Technical Specifications

Here are the specifications:

Operating Voltage3.0 to 5.5V
InterfaceHigh-Speed SPI
Current Consumption700µA (typ.), 1.5mA (max)
Temperature Range0 – 1024 °C (of MAX6675)0 – 80 °C (of supplied probe)
Accuracy±3 °C
Resolution12-Bit (0.25 °C)
Conversion Time~170 ms

For more information about the MAX6675 IC, please refer to the datasheet below.

MAX6675 Module Pinout

Now let’s have a look at the pinout.

max6675 module pinout

Input Connector

VCC is the power pin. Connect it to a power supply ranging from 3V to 5.5V.

GND is the ground pin.

SCK is the SPI clock pin.

CS is the chip select pin. Pull this pin low and apply a clock signal at SCK to read the results at SO. Pulling it low immediately stops any conversion process. Initiate a new conversion process by pulling it high.

SO is the Serial data Out / MISO pin, for 12-bit data sent from the module to your Arduino. A sequence of all zeros means the thermocouple reading is 0°C. A sequence of all ones means the thermocouple reading is +1023.75°C.

Thermocouple Connector

On the other side of the module, there is a 2-pin terminal block for connecting a Type-K thermocouple probe.

is where you attach Alumel Lead (blue) of Type-K thermocouple.

+ is where you attach Chromel Lead (red) of Type-K thermocouple.

Wiring MAX6675 Module to an Arduino

Let’s connect the MAX6675 module to the Arduino. The connections are straightforward.

Begin by connecting the VCC pin on the module to 5V on the Arduino and the GND pin to ground.

Now connect the three digital pins to use as the SPI interface. We are using pins 4, 5, and 6 in the example.

Finally, attach the thermocouple probe to the module. Connect the red lead (Chromel Lead) of the thermocouple to the ‘+’ terminal of the module, and the blue lead (Alumel Lead) to the ‘-‘ terminal.

The following table lists the pin connections:

MAX6675 ModuleArduino
VCC5V
GNDGND
SCK6
CS5
SO4

The image below shows how to build the circuit.

wiring max6675 thermocouple module to arduino

Because the module consumes very little power (less than 1.5ma), it is possible to power it using a digital output pin on the microcontroller. If you decide to go with this method and power down the MAX6675 in between readings, you should wait a few seconds after turning the power back on before attempting a reading.

Library Installation

There’s a really great library available for working with the MAX6675 module. You will need to download and install it in your Arduino IDE.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the libraries index and update the list of installed libraries.

manage libraries 2

Filter your search by entering ‘MAX6675’. Look for the MAX6675 library by Adafruit. Click on that entry and then choose Install.

max6675 library installation

Arduino Example Code

Now that we have everything hooked up, let’s run a simple sketch to quickly test the MAX6675 module. Go ahead and upload it to your Arduino. You should see the ambient temperature printed to the serial interface.

#include "max6675.h"

// Define the Arduino pins, the MAX6675 module is connected to
int SO_PIN = 4;  // Serail Out (SO) pin
int CS_PIN = 5;  // Chip Select (CS) pin
int SCK_PIN = 6; // Clock (SCK) pin

// Create an instance of the MAX6675 class with the specified pins
MAX6675 thermocouple(SCK_PIN, CS_PIN, SO_PIN);

void setup() {
  Serial.begin(9600);
  delay(500);
}

void loop() {
  // Read the current temperature and print it to the serial monitor

  // Read the temperature in Celsius
  Serial.print("Temperature: ");
  Serial.print(thermocouple.readCelsius());
  Serial.print("\xC2\xB0"); // shows degree symbol
  Serial.print("C  |  ");

  // Read the temperature in Fahrenheit
  Serial.print(thermocouple.readFahrenheit());
  Serial.print("\xC2\xB0"); // shows degree symbol
  Serial.println("F");

  delay(1000);
}

Once the sketch is uploaded, open your serial monitor, setting the baud rate to 9600 bps. Try placing the thermocouple in contact with the material to be measured. You should see the measured temperature begin to stream by.

max6675 output

Code Explanation:

The sketch begins by including the MAX6675 header file. It allows the Arduino code to interact with the MAX6675 module.

#include "max6675.h"

In that same global area, three integers SO_PIN, CS_PIN and SCK_PIN are declared, which specify the Arduino pin you’ve connected to the module’s serial out (SO) pin, chip select (CS) pin, and clock (SCK) pin.

int SO_PIN = 4;  // Serail Out (SO) pin
int CS_PIN = 5;  // Chip Select (CS) pin
int SCK_PIN = 6; // Clock (SCK) pin

Next, an instance of the MAX6675 class called thermocouple is created. That’s what we’ll be referring to from now on to read the module. It initializes the thermocouple object with the defined SCK, CS, and SO pins.

MAX6675 thermocouple(SCK_PIN, CS_PIN, SO_PIN);

The setup section of the code initializes the serial communication with the computer at a baud rate of 9600.

void setup() {
  Serial.begin(9600);
  delay(500);
}

In the loop(), we read from the MAX6675 thermocouple and print it to the Serial Monitor. For this, two library functions are used:

  • thermocouple.readCelsius(): returns temperature in Celsius.
  • thermocouple.readFahrenheit(): returns temperature in Fahrenheit.
Serial.print("Temperature: ");
Serial.print(thermocouple.readCelsius());
Serial.print("\xC2\xB0"); // shows degree symbol
Serial.print("C  |  ");

// Read the temperature in Fahrenheit
Serial.print(thermocouple.readFahrenheit());
Serial.print("\xC2\xB0"); // shows degree symbol
Serial.println("F");

The loop continues executing indefinitely, repeatedly reading and printing the temperature every second.

Want to keep a log of the climate in your greenhouse, build a humidor control system, or track temperature and humidity data for a weather station project? The SHT31 Temperature & Humidity Sensor may be the right choice for you!

SHT31 sensor is factory-calibrated and requires no external components to work. So with just a few connections and some Arduino code, you can start measuring relative humidity and temperature right away.

Hardware Overview

The module carries a low-cost, easy to use, highly accurate, digital temperature & humidity sensor, from Sensirion – SHT31.

sht31 module hardware overview chip

The small size of the module allows it to be used for almost anything, such as thermostats, humidistats, indoor weather stations and similar devices, for monitoring or controlling humidity and/or temperature.

The SHT31 sensor is capable of reading humidity over the full range of 0 to 100% RH with a typical accuracy of ±2% over the range of 20% to 80% RH (0.01% RH resolution).

The maximum temperature range of the SHT31 is -40 to 125°C. It has a typical accuracy of ±0.3°C at 25°C (0.015°C resolution).

Warning:

The SHT31 sensor has a small window that exposes the polymer sensing film responsible for measuring temperature and humidity. It is advisable to prevent liquids, dust or other contaminants from coming into contact with it as it may affect the accuracy of the sensor.

Power Requirement

The sensor itself uses 2.4V to 5.5V which makes this module 3V or 5V compliant. So, you can use it with your favorite 3.3V or 5V microcontroller without worry.

The SHT31 consumes less than 0.8mA during measurements and less than 0.2µA during single shot mode(not measuring). This low power consumption allow the implementation in battery driven devices such as handsets, wearables or smart watches.

I2C Interface

The SHT31 is a I2C sensor, meaning it uses the two I2C data/clock wires available on most microcontrollers, and can share those pins with other I2C sensors as long as they don’t have an address collision.

It supports two separate I2C addresses: 0x44Hex and 0x45Hex. This allows two SHT31 modules to be used on the same bus or to avoid address conflicts with another device on the bus.

sht31 module i2c address selection pin

The AD pin determines the I2C address of the module. This pin has a built-in pull-down resistor. Therefore, when you leave the AD pin unconnected, the default I2C address is 0x44Hex and when you connect it to a high voltage signal, the I2C address becomes 0x45Hex.

Alert Mode

The SHT31 has an alert (AL) output pin that can trigger when the environmental condition (humidity and/or temperature) exceeds user-defined limits. This enables measurements to be interrupt driven instead of using polling, allowing the host microcontroller to perform other tasks while the data is collected by the sensor.

sht31 module alert output pin

When the humidity and/or temperature crosses the upper limit, the Alert pin goes HIGH and remains HIGH until the temperature drops below the clear limit. Similarly when the humidity and/or temperature exceeds the lower limit, the alert pin goes HIGH and remains HIGH until the temperature rises above the clear limit.

Below image shows the different limits for the Alert Mode.

different limits for the sht31 alert mode

You can read more about it in a separate application note.

Technical Specifications

Here are the complete specifications:

Power supply2.4V to 5.5V
Current draw~0.8mA (during measurements)
~0.2µA (during single shot mode)
Humidity Range0 to 100 %RH
Humidity Accuracy±2% over the range of 20% to 80% RH
Temperature Range-40˚C to +125˚C
Temperature Accuracy±0.3˚C at 25°C

For more details, please refer below datasheet.

SHT31 Module Pinout

Now let’s have a look at the pinout.

sht31 module pinout

VCC is the power pin. Since the sensor uses 2.4-5.5VDC, give it the same power as the logic level of your microcontroller – e.g. for a 5V micro like Arduino, use 5V.

GND is the common ground for power and logic.

SCL is the I2C clock pin, connect to your microcontrollers I2C clock line.

SDA is the I2C data pin, connect to your microcontrollers I2C data line.

AD pin determines the I2C address of the module. When you leave the AD pin unconnected, the default I2C address is 0x44Hex and when you connect it to a high voltage signal, the I2C address becomes 0x45Hex.

AL pin triggers when the environmental condition (humidity and/or temperature) exceeds user-defined limits.

Wiring up a SHT31 Module to an Arduino

Wiring up the SHT31 sensor is very easy!

There are only four pins that need to be hooked up in order to start using the sensor. One for VCC, one for GND, and two data lines for I2C communication.

Connect the SCL pin to the I2C clock pin and the SDA pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

The following illustration shows the wiring.

wiring connecting sht31 module with arduino

Library Installation

To get your sensor up and running, you will need to install the Adafruit SHT31 library. It is available from the Arduino library manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing ‘SHT31‘ and install the library.

adafruit sht31 library installation

The Adafruit_SHT31 library uses the Adafruit Bus IO helper library internally to abstract away I2C & SPI transactions and registers. So, search the library manager for adafruit bus and install that as well.

adafruit busio library installation

Arduino Code – Reading Temperature and Humidity

Below is a basic Arduino sketch. Go ahead and upload it to your Arduino. You will see the current temperature and humidity in your room!

#include <Arduino.h>
#include <Wire.h>
#include "Adafruit_SHT31.h"

Adafruit_SHT31 sht31 = Adafruit_SHT31();

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

  if (! sht31.begin(0x44)) {   // Set to 0x45 for alternate I2C address
    Serial.println("Couldn't find SHT31");
    while (1) delay(1);
  }
}

void loop() {
  float t = sht31.readTemperature();
  float h = sht31.readHumidity();

  if (! isnan(t)) {  // check if 'is not a number'
    Serial.print("Temp *C = "); Serial.print(t); Serial.print("\t\t");
  } else { 
    Serial.println("Failed to read temperature");
  }
  
  if (! isnan(h)) {  // check if 'is not a number'
    Serial.print("Hum. % = "); Serial.println(h);
  } else { 
    Serial.println("Failed to read humidity");
  }

  delay(1000);
}

Once your code is uploaded, open the serial terminal at 9600bps. You should see something like the output below. Try breathing on the sensor to see both humidity and temperature values change!

sht31 sensor arduino output

Code Explanation:

The code is quite straightforward. At the beginning, Arduino.h, Wire.h and Adafruit_SHT31.h libraries are included and an Adafruit_SHT31 object is created in the global space.

#include <Arduino.h>
#include <Wire.h>
#include "Adafruit_SHT31.h"

Adafruit_SHT31 sht31 = Adafruit_SHT31();

In the setup, we initialize the serial communication with PC and call the begin() function.

The begin(<address>) function initializes the sensor, where <address> is the I2C address of the sensor. By default its 0x44Hex, you can also adjust the sensor for 0x45Hex and then pass that value in. This function returns True if the sensor was found and responded correctly and False if it was not found.

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

  if (! sht31.begin(0x44)) {   // Set to 0x45 for alternate I2C address
    Serial.println("Couldn't find SHT31");
    while (1) delay(1);
  }
}

Once initialized, you can access object’s (sht31) methods using the dot operator.

sht31.readTemperature() returns floating point (decimal + fractional) temperature reading in °C. You can convert to Fahrenheit by multiplying by 1.8 and adding 32.

sht31.readHumidity() returns humidity reading, also as a floating point value between 0 and 100 (this reads % humidity)

void loop() {
  float t = sht31.readTemperature();
  float h = sht31.readHumidity();

  if (! isnan(t)) {  // check if 'is not a number'
    Serial.print("Temp *C = "); Serial.print(t); Serial.print("\t\t");
  } else { 
    Serial.println("Failed to read temperature");
  }
  
  if (! isnan(h)) {  // check if 'is not a number'
    Serial.print("Hum. % = "); Serial.println(h);
  } else { 
    Serial.println("Failed to read humidity");
  }

  delay(1000);
}

Want to keep a log of the climate in your greenhouse, build a humidor control system, or track temperature and humidity data for a weather station project? The HTU21D temperature and humidity sensor may be the right choice for you!

This sensor is factory-calibrated and needs no external components to work. So with just a few connections and some Arduino code, you can start measuring relative humidity and temperature right away.

Hardware Overview

The module carries a low-cost, easy to use, highly accurate, digital temperature & humidity sensor, from MEAS Switzerland – HTU21D.

htu21d module hardware overview

The small size of the module allows it to be mounted into just about anything, such as thermostats, humidistats, indoor weather stations and similar devices, for monitoring and controlling temperature/humidity.

The HTU21D sensor is capable of reading humidity over the full range of 0 to 100% RH with a typical accuracy of ±2% over the range of 5% to 95% RH.

The maximum temperature range of the HTU21D is -40 to 125°C. It has a typical accuracy of ±0.3°C over the range of 0 to 70°C.

In its normal mode for humidity and temperature measurement, the sensor has a resolution of 0.7% RH and 0.040°C with conversion times of 2ms and 11ms, respectively. For more demanding requirements the sensor allows you to increase the resolution at the expense of increased conversion time. In maximum resolution mode the HTU21D can provide 0.04% RH with a conversion time of 14ms and 0.01°C with a conversion time of 44ms.

Warning:

The HTU21D sensor has a small window that exposes the polymer sensing film responsible for measuring temperature and humidity. It is advisable to prevent liquids, dust or other contaminants from coming into contact with it as it may affect the accuracy of the sensor.

Some breakout boards include the HTU21D-F (filtered version) which come equipped with a hydrophobic PTFE filter (a tiny white cover on the IC). This filter blocks contaminants but allows water vapor to pass through, keeping your sensor safe from water damage while still proving accurate sensor readings.

Power Requirement

The module comes with a 3.3V precise voltage regulator and voltage level translator, so you can use it with your favorite 3.3V or 5V microcontroller without worry.

htu21d module voltage regulator translator

The HTU21D consumes less than 0.5mA during measurements and less than 0.14µA during sleep mode. This low power consumption allow the implementation in battery driven devices such as handsets, wearables or smart watches.

I2C Interface

The HTU21D is a I2C sensor, meaning it uses the two I2C data/clock wires available on most microcontrollers, and can share those pins with other I2C sensors as long as they don’t have an address collision.

The sensor has a fixed I2C address and is set to 0x40HEX. A multiplexer/Mux is required to communicate to multiple HTU21D sensors on a single bus.

Technical Specifications

Here are the complete specifications:

Power supply3.3V to 5.5V
Current draw~0.5mA (during measurements)
~0.14µA (during sleep mode)
Minimum Sampling Period50ms
Humidity Range0 to 100 %RH
Humidity Accuracy±2% over the range of 5% to 95% RH
Temperature Range-40˚C to +125˚C
Temperature Accuracy±0.3˚C at 25°C

For more details, please refer below datasheet.

HTU21D Module Pinout

Now let’s have a look at the pinout.

htu21d module pinout

VCC is the power pin. You can connect it to 3.3V or 5V output from your Arduino.

GND is the common ground for power and logic.

SCL is also the I2C clock pin, connect to your microcontrollers I2C clock line.

SDA is also the I2C data pin, connect to your microcontrollers I2C data line.

Wiring up a HTU21D Module to an Arduino

Wiring up the HTU21D humidity sensor is very easy!

There are only four pins that need to be hooked up in order to start using the sensor. One for VCC, one for GND, and two data lines for I2C communication.

Connect the SCL pin to the I2C clock pin and the SDA pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

The following illustration shows the wiring.

wiring connecting htu21d module with arduino

Library Installation

To get your sensor up and running, you will need to install the Adafruit HTU21DF. It is available from the Arduino library manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing ‘HTU21D‘ and install the library.

adafruit htu21d library installation

The Adafruit_HTU21DF library uses the Adafruit Bus IO helper library internally to abstract away I2C & SPI transactions and registers. So, search the library manager for adafruit bus and install that as well.

adafruit busio library installation

Arduino Code – Reading Temperature and Humidity

Below is a basic Arduino sketch. Go ahead and upload it to your Arduino. You will see the current temperature and humidity in your room!

#include <Wire.h>
#include "Adafruit_HTU21DF.h"

Adafruit_HTU21DF htu = Adafruit_HTU21DF();

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

  if (!htu.begin()) {
    Serial.println("Couldn't find sensor!");
    while (1);
  }
}

void loop() {
    float temp = htu.readTemperature();
    float rel_hum = htu.readHumidity();
    Serial.print("Temp: "); Serial.print(temp); Serial.print(" C");
    Serial.print("\t\t");
    Serial.print("Humidity: "); Serial.print(rel_hum); Serial.println(" \%");
    delay(500);
}

Once your code is uploaded, open the serial terminal at 9600bps. You should see something like the output below. Try breathing on the sensor to see both humidity and temperature values change!

htu21d sensor arduino output

Code Explanation:

The code is quite straightforward. At the beginning, Wire.h and Adafruit_HTU21DF.h libraries are included and an Adafruit_HTU21DF object is created in the global space.

#include <Wire.h>
#include "Adafruit_HTU21DF.h"

Adafruit_HTU21DF htu = Adafruit_HTU21DF();

In the setup, we initialize the serial communication with PC and call the begin() function.

The htu.begin() function initializes the sensor. This function returns True if the sensor was found and responded correctly and False if it was not found.

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

  if (!htu.begin()) {
    Serial.println("Couldn't find sensor!");
    while (1);
  }
}

Once initialized, you can access object’s (htu) methods using the dot operator.

htu.readTemperature() returns floating point (decimal + fractional) temperature reading in °C. You can convert to Fahrenheit by multiplying by 1.8 and adding 32.

htu.readHumidity() returns humidity reading, also as a floating point value between 0 and 100 (this reads % humidity)

void loop() {
    float temp = htu.readTemperature();
    float rel_hum = htu.readHumidity();
    Serial.print("Temperature: "); Serial.print(temp); Serial.print(" C");
    Serial.print("\t");
    Serial.print("Humidity: "); Serial.print(rel_hum); Serial.println(" \%");
    delay(500);
}

If you’ve seen a laptop or a cellphone that flips open like a clamshell, you’ve probably noticed that when you open and close it, it turns on or off accordingly. But how does it know? If you think there is some kind of switch attached to the hinge that detects this opening and closing movement, you are right!

These devices use a cheap and very reliable sensor called a “Reed Switch” that turns on or off when the magnet is nearby.

Reed switches are used in all kinds of creative applications like door sensors, anemometers (determine the wind speed) etc. They are perfect for any project that requires non-contact control. So let’s take a closer look at what they are and how they work!

Who invented reed switches?

Like many other great inventions, the reed switch was born at Bell Laboratories, invented by Walter B. Ellwood in the mid-1930s. His patent application for an electromagnetic switch was filed on June 27, 1940, and was officially granted on December 2, 1941.

reed switch patent diagram

However, the design of his original reed switch is slightly different from the one we use these days.

Reed Switch Overview

A typical reed switch contains a pair of metal reeds made of a ferromagnetic material (something that gets magnetized easily but loses magnetism when it leaves a magnetic field). The surfaces of the reed contacts are plated with hardwearing metals such as rhodium, ruthenium, palladium or iridium to give them a longer life as they switch on and off millions of times.

The reeds are hermetically sealed inside a tubular glass envelope to keep them free of dust and dirt. The hermetic sealing of reed switches makes them suitable for use in explosive environments where small sparks from conventional switches would constitute a hazard. The glass tube is filled with an inert gas, usually nitrogen, or a vacuum to prevent oxidation of the contacts.

reed switch construction

Typically, contacts are made of a nickel-iron alloy that is easy to magnetize (has high magnetic permeability) but does not stay that way for long (has low magnetic retentivity). Being a mechanical device, they take some time to respond to changes in the magnetic field – in other words, their switching speed is low (typically 0.6 ms turn-on time, 0.2 ms turn-off time) compared to electronic switches.

In the presence of a magnetic field, both contacts move (instead of just one) and they form a flat, parallel area of ​​contact with each other. This helps to increase the life and reliability of the reed switch.

A reed switch only detects the presence of a magnetic field and does not measure its strength. If you are interested in measuring strength, an analog Hall Effect sensor is a better option to consider.

How Does a Reed Switch Work?

The key to understanding how reed switches work is to realize that they are part of a magnetic circuit, as well as an electrical one – magnetism flows through them as well as electricity.

As you bring a magnet closer to the reed switch, the entire switch becomes a part of the “magnetic circuit” including the magnet (the dotted line in the image shows part of the magnetic field).

reed switch magnetic and electric circuit

The two contacts of a reed switch become opposite magnetic poles, which is why they attract and snap together. It doesn’t matter which end of the magnet you bring closer: the contacts still polarize in opposite ways and attract each other.

When you take the magnet away, the contacts separate and return back to their original position.

reed switch working animation

A reed switch like this is normally open (NO). This means ‘normally’ when the switch is unaffected by the magnetic field, the switch is open and does not conduct electricity. When a magnet comes close enough to activate the switch, the contacts close and current flows through.

In these illustrations, the movement of the contacts is largely exaggerated. Real reed switches have contacts that are only a few microns apart (about ten times thinner than a human hair). So the movement is not visible to the naked eye. Thanks to Zátonyi Sándor for sharing macroscopic photos of the reed switch.

actual reed switch working

Wiring up a Reed Switch to an Arduino

The circuit set up for our example is as simple as it can be. First bend both legs of the switch to point perpendicularly away from the body of the switch, so that they form a “U” shape.

Insert the reed switch into the breadboard. Then use jumper wires to connect one end of the switch to ground and the other end to the D2 pin of the Arduino.

If you connect the switch this way, you will need to activate the arduino’s ‘built-in’ pull-up resistor for the input pin. Otherwise, you must use an external 10K pull-up resistor in your circuit.

Here’s an example circuit:

wiring reed switch with an arduino

Caution:

The glass envelope that encloses the reed switch breaks easily if twisted, so use caution when bending the legs.

Arduino Code – Reading a Reed Switch

Here is a very basic Arduino sketch based on the circuit above, that will switch-on the built-in LED (attached to pin 13) when you bring a magnet close to the switch, and switch-off when you move it away.

const int REED_PIN = 2;	// Pin connected to reed switch
const int LED_PIN = 13;	// LED pin

void setup() {
	Serial.begin(9600);
	pinMode(REED_PIN, INPUT_PULLUP);	// Enable internal pull-up for the reed switch
	pinMode(LED_PIN, OUTPUT);
}

void loop() {
	int proximity = digitalRead(REED_PIN); // Read the state of the switch
	
	// If the pin reads low, the switch is closed.
	if (proximity == LOW) {
		Serial.println("Switch closed");
		digitalWrite(LED_PIN, HIGH);	// Turn the LED on
	}
	else {
		Serial.println("Switch opened");
		digitalWrite(LED_PIN, LOW);		// Turn the LED off
	}
}

With the sketch uploaded, grab your magnet, and bring it closer to the switch. It should trigger when the magnet reaches a distance of 1 cm from the body of the reed switch. Try mapping the entire activation region of the reed switch. See how far you can take the magnet!

reed switch arduino code output

Code Explanation:

The code is quite self-explanatory. Initially two constants are defined which declare the Arduino pins to which the reed switch and built-in LED are connected.

const int REED_PIN = 2;
const int LED_PIN = 13;

In the setup(), the reed switch pin is configured as an input while the LED pin is configured as an output. Also the internal pull-up is enabled for the reed switch pin.

void setup() {
	Serial.begin(9600);
	pinMode(REED_PIN, INPUT_PULLUP);
	pinMode(LED_PIN, OUTPUT);
}

In the loop(), the built-in LED is turned ON if the reed switch pin reads LOW, otherwise turned OFF.

void loop() {
	int proximity = digitalRead(REED_PIN);

	if (proximity == LOW) {
		Serial.println("Switch closed");
		digitalWrite(LED_PIN, HIGH);
	}
	else {
		Serial.println("Switch opened");
		digitalWrite(LED_PIN, LOW);
	}
}

If you want to detect when something is moved, tilted, or shaken without getting into the complexities of an accelerometer, the ball tilt sensor might be the cheapest option.

Overview

A ball tilt sensor is more of a switch that can detect basic motion, orientation or inclination. These switches are designed in such a way that a sufficient level of inclination makes or breaks the electrical connection. Such a signal can either be used as an indicator or can be used to turn something ON or OFF.

They are small, cheap, easy to use and never wear out. Their simplicity makes them popular for use in toys, gadgets, robots, and other devices whose functioning depends on inclination. This is why they are sometimes called the “poor man’s accelerometer“.

ball tilt switch sensor

Although all tilt sensors work pretty much the same, their sizes and specifications may differ slightly.

Dimensions5.5mm (0.22″) diameter & 13.5mm (0.53″) long
Maximum operating voltage (VCC)Up to 20V
Maximum operating current (Imax)30mA
Sensitivity rangeMovements of around 5 to 10 degrees
Lifetime50,000+ cycles (switches)

Tilt Sensor vs. Accelerometer

While not as accurate or flexible as an accelerometer, the tilt sensor can detect motion or orientation quite well. Another advantage of using a tilt sensor is that it can be used as a standalone sensor. The accelerometer, on the other hand, outputs a digital or analog voltage that must be analyzed with additional circuitry.

How Do Ball Tilt Sensors Work?

A ball tilt sensor is typically made up of a metal tube with a little metal ball that rolls around in it. One end of the cavity has two conductive elements (poles). The sensor is designed in such a way that a sufficient level of tilt allows the ball to roll, making or breaking an electrical connection.

ball tilt switch sensor working illustration

When the sensor is upright the ball touches the poles and makes an electrical connection. And when the sensor is tilted the ball rolls off the poles and the connection is broken.

Testing a Ball Tilt Sensor

Testing a ball tilt sensor is very simple. Put your multimeter in ‘continuity-test’ mode and touch the probes to the two leads. Then tilt it to determine the angle at which the switch opens and closes.

When pointing up, the switch is closed (full continuity).

testing ball tilt switch sensor with multimeter upright position

When pointing down, the switch is open (no continuity).

testing ball tilt switch sensor with multimeter tilted position

Wiring up a Ball Tilt Sensor to an Arduino

Wiring the tilt sensor up to your Arduino is pretty straightforward. All you need to do is connect one pin to any digital pin of the Arduino and the other to GND.

If you connect the sensor this way, you will need to activate the arduino’s ‘built-in’ pull-up resistor for the input pin. Otherwise, you must use an external 10K pull-up resistor in your circuit.

The following illustration shows the wiring.

arduino wiring with ball tilt switch sensor

Arduino Code – Reading a Ball Tilt Sensor

Below is a very basic Arduino sketch that will switch-on the built-in LED (attached to pin 13) when the tilt sensor is tilted one way, and switch-off when it is tilted the other way.

const int tiltPin = 2;		// tilt sensor pin is connected to pin 2
const int ledPin = 13;		// built-in LED is connected to pin 13

void setup() {
	pinMode(tiltPin, INPUT);		// set sensor pin as an INPUT pin
	digitalWrite(tiltPin, HIGH);	// turn on the built in pull-up resistor
	pinMode(ledPin, OUTPUT);		// set LED pin as an OUTPUT pin
}

void loop() {
	if (digitalRead(tiltPin)) { // check if the pin is high
		digitalWrite(ledPin, HIGH);	// turn on the LED
	}
	else { // if it isn't
		digitalWrite(ledPin, LOW);	// do the opposite
	}
}

The code is quite self-explanatory. Initially two constants are defined which declare the Arduino pins to which the tilt sensor and built-in LED are connected. In the setup, the sensor pin is configured as an input while the LED pin is configured as an output. Also the internal pull-up is enabled for the sensor pin. In the loop, the built-in LED is turned ON if the sensor pin is HIGH, otherwise turned OFF.

It’s a pretty short sketch, works great, but there is a problem. If you have looked at the LED while tilting the sensor, you may have noticed it flickering. That’s because something called ‘switch bounce‘.

What is switch bounce and How to Deal with It?

When the sensor is pointing up, the ball rolls onto the poles and shorts them, acting as a switch throw. In an ideal world, if we hooked this up to a signal analyzer we should get a signal that would look like this:

ball tilt switch sensor debounce ideal signal

That’s the way a ball tilt sensor should work and that’s what most people think. However, in reality it looks more like this:

ball tilt switch sensor debounce bounce signal

Ideally the contact should be instant. Instead the ball bounces around a bit, making the contact open, closed, open, closed, etc. It’s purely a mechanical phenomenon known as ‘Switch Bounce‘, like dropping a ball – it bounces before finally landing on the ground.

So the signal goes up and down several times before finally stabilizing. It does this over a period of time (t). While this period of time is very fast, and seems almost instantaneous to us, but for an Arduino, it is a huge period of time. It can execute multiple instructions in that time period.

The process of eliminating switch bounces is called ‘Debouncing‘ which can be achieved programmatically. Usually the switch bounce occurs within a 50ms window. So, a 50mS delay after a switch change is detected, is enough to eliminate it.

Arduino Code – Reading a Ball Tilt Sensor (More Reliably)

Here’s a new sketch rewritten to demonstrate how to debounce an input. In this sketch we read the tilt sensor twice in a short period of time to make sure it is definitely tilted.

Go ahead and upload it to your Arduino. Now you can see that the LED no longer flickers when you tilt the sensor (if the problem persists just try increasing the debounce time).

const int tiltPin = 2;		// tilt sensor pin is connected to pin 2
const int ledPin = 13;		// built-in LED is connected to pin 13

int ledState = HIGH;		// the current state of the LED
int tiltState;				// the current reading from the sensor
int lastTiltState = LOW;	// the previous reading from the sensor

unsigned long time = 0;		// the last time the output pin was toggled
unsigned long debounceDelay = 50;	// the debounce time, increase if the output flickers

void setup() {
	pinMode(tiltPin, INPUT);		// Set sensor pin as an INPUT pin
	digitalWrite(tiltPin, HIGH);	// turn on the built in pull-up resistor
	pinMode(ledPin, OUTPUT);		// Set LED pin as an OUTPUT pin
}

void loop() {
	// read the state of the tilt sensor
	tiltState = digitalRead(tiltPin);

	// If the sensor really tilted?
	if (tiltState != lastTiltState) {
		// reset the debouncing timer
		time = millis();
	}

	if ((millis() - time) > debounceDelay) {
		// whatever the switch is at, its been there for a long time
		// so lets settle on it!
		ledState = tiltState;
	}
	digitalWrite(ledPin, ledState);

	// Save the last tiltState so we keep a running tally
	lastTiltState = tiltState;
}

Code Explanation:

The sketch starts by declaring the Arduino pins to which the tilt sensor and built-in LED are connected.

const int tiltPin = 2;
const int ledPin = 13;

Three integers viz. ledState, tiltState and lastTiltState are defined to store the current state of the LED, the current reading from the sensor and the previous reading from the sensor resp.

int ledState = HIGH;
int tiltState;
int lastTiltState = LOW;

In that same global area, two variables are defined to store the last-tilt-event and the debounce time. Note that these variables are unsigned longs because the time (measured in milliseconds) quickly becomes a bigger number and overflows an integer.

unsigned long time = 0;
unsigned long debounceDelay = 50;

The setup section is the same as before where we configure the sensor pin as an input and the LED pin as an output; and enable the internal pull-up for the sensor pin.

void setup() {
	pinMode(tiltPin, INPUT);
	digitalWrite(tiltPin, HIGH);
	pinMode(ledPin, OUTPUT);
}

In the loop, we first read the state of the tilt sensor and check if this state is different from the last tilt-state (either due to tilt or noise). If it is different, we log this state-change event by storing the current time. For this purpose we use the millis() function which keeps track of the time elapsed since the Arduino board began running the current program.

tiltState = digitalRead(tiltPin);

if (tiltState != lastTiltState) {
	time = millis();
}

Next, we see if the sensor changes its state within a 50ms (debounceDelay) window, if it doesn’t, it means the sensor is indeed tilted (and not due to switch bounce). Then we toggle the LED and save the last tilt-state so that we can keep a running tally.

if ((millis() - time) > debounceDelay) {
	ledState = tiltState;
}
digitalWrite(ledPin, ledState);

lastTiltState = tiltState;

Your plants, like you, require food. In order to be healthier and more productive, plants need the “big three” essential nutrients nitrogen, phosphorus, and potassium, also known as NPKs.

If the soil in your garden doesn’t have enough of these nutrients, plants won’t grow to their full potential. Therefore, it is essential to measure the soil’s current N, P, and K levels to determine how much additional nutrient content must be added to increase crop fertility.

Using an NPK Soil Sensor and an Arduino, you can quickly determine the levels of these nutrients in the soil.

What Is NPK? and Why It’s So Important?

The letters NPK stand for the three major nutrients that plants require to grow and thrive: nitrogen, phosphorus, and potassium.

Nitrogen is responsible for the growth and greenness of plant leaves.

Phosphorus helps the plant grow strong roots, fruit, and flowers.

Potassium improves the overall health and hardiness of a plant.

JXCT Soil NPK sensor

The JXCT Soil NPK sensor is a low-cost, quick-responding, reasonably accurate, and portable sensor. It aids in the real-time monitoring of NPK nutrient content in soil for smart agriculture.

The soil NPK sensor can detect the levels of nitrogen, phosphorus, and potassium in the soil (not in water). It helps determine soil fertility, allowing for a more systematic assessment of soil condition.

soil npk sensor

The sensor operates on 5-30V and consumes very little power. According to the datasheet, it is capable of measuring nitrogen, phosphorus, and potassium with a resolution of up to 1 mg/kg (mg/l).

The sensor includes a stainless steel probe that is rust-proof, electrolytic resistant, and salt-alkali resistant. It can therefore be used with any type of soil, including alkaline soil, acid soil, substrate soil, seedling bed soil, and coconut bran soil.

The probe is sealed to the body with high-density epoxy resin to prevent moisture from entering the body.

The best part is that the sensor has an IP68 rating, which means it is protected against dust and moisture, allowing it to operate normally for a very long time.

To be used effectively over long distances, the sensor features the RS485 communication interface and supports the standard Modbus-RTU communication protocol.

It should be noted that the sensor cannot be used with an Arduino directly. To communicate with Arduino, you’ll need an RS-485 transceiver module that converts a UART serial stream to RS-485.

Technical Specifications

Here are the specifications:

Power5V-30V
Measuring Range0-1999 mg/kg (ml/l)
Operating Temperature5-45 °C
Resolution1mg/kg (ml/l)
Precision±2% F.S.
Output SignalRS485
Protection ClassIP68

Heads-up

The JXCT soil NPK sensor is an electrical conductivity sensor (EC sensor). It does not directly measure the soil’s NPK content, but rather estimates it based on the electrical conductivity of the soil.

Be aware that this method is tricky and prone to producing inaccurate results.

For your information, there are inexpensive sensors out there that work on the same principle.

Soil NPK sensor Pinout

The sensor comes with a 2m cable with tinned copper wires. The pinout is shown in the figure below.

soil npk sensor pinout

VCC is the VCC pin. Connects to 5V – 30V.

A is a differential signal that is connected to the A pin of the MAX485 Modbus Module.

B is another differential signal that is connected to the B pin of the MAX485 Modbus Module.

GND is the Ground pin.

Wiring a Soil NPK Sensor to an Arduino

As previously stated, the NPK sensor cannot be used directly with an Arduino. To communicate with Arduino, you’ll need an RS-485 transceiver module that converts a UART serial stream to RS-485, such as the one shown below.

rs485 transceiver module

Okay, so let’s get to the wiring.

The soil NPK Sensor has four wires. The power wire is brown and should be connected to the 5V-30V power supply. The ground wire is black and should be connected to a common ground.

The yellow wire of the NPK sensor should be connected to the RS485 module’s A pin, and the blue wire should be connected to the RS485 module’s B pin.

Connect the RS485 module’s R0 and DI pins to the Arduino’s digital pins 2 and 3, respectively. These digital pins will be used as virtual RX and TX serial lines.

Note that if you are using a Mega or Mega 2560, you should use digital pins 10 and 11, as digital pins 2 and 3 do not support change interrupts, which is a known limitation of the softwareSerial library.

The RS485 module’s VCC pin should be connected to the Arduino’s 5V output, and the DE and RE pins should be connected to digital pins 7 and 8, respectively.

Finally, make sure your circuit and Arduino share a common ground.

The wiring is shown in the image below.

arduino wiring soil npk sensor

Usage Instructions

Choose an appropriate measuring location, avoid stones, make sure the steel probe does not come into contact with any hard objects, and insert the sensor vertically into the soil.

soil npk sensor vertical insertion

Optionally, the sensor can be inserted horizontally into the pit, in which case the pit is excavated vertically with a diameter greater than 20 cm before being tightly backfilled.

soil npk sensor horizontal insertion

What is Modbus?

Just in case you don’t know what Modbus is, it’s probably a good idea to brush up on it before we move on.

Modbus is a de-facto standard for industrial communication protocols because it is open source and royalty-free. For data transfer, it uses RS-485, RS-422, and RS-232 interfaces, as well as Ethernet TCP/IP networks (the Modbus TCP protocol).

There are various implementation types of Modbus. The following are the most popular protocol types:

  • Modbus RTU (The one we are going to configure)
  • Modbus ASCII
  • Modbus TCP

Modbus RTU Protocol

Modbus RTU is a master-slave protocol. In this protocol, only the master device (in our case, an Arduino) is allowed to initiate communication. The other devices on the network are known as slaves (in our case, NPK sensors), and they can only respond to requests. Modbus RTU can support up to 247 devices on the same physical network. 

Modbus RTU Data Frame

Over the Modbus bus, messages are exchanged between the master and slave in the form of data frames. There are Request and Response frames. A request is a message sent by the master to one of the slaves. A response is a message sent by the slave to the master. 

The request also includes a checksum which is used to make sure the messaged is not corrupted on the way to the slave.

A typical Modbus RTU message contains the address of the SlaveID device, the function code, the data based on the function code, and the CRC of the checksum.

typical modbus rtu data frame

The following is an example of a Modbus RTU Request frame instructing slave #1 to return the value of a single register beginning at address 2.

sample modbus rtu request frame

All slaves except slave #1 ignore this message. Slave #1 then sends a response message that looks like this:

sample modbus rtu response frame

Modbus RTU requests for reading NPK Sensor

The following are three distinct Modbus RTU requests for reading the Nitrogen (N), Phosphorous (P), and Potassium (K) values from the NPK Sensor. If the request is successful, the sensor sends a Response message containing the reading.

For Reading Nitrogen

The Modbus RTU request for reading the Nitrogen level is:

modbus rtu request for reading nitrogen

You will receive a similar response:

modbus rtu response with nitrogen reading

From the Response, you can obtain the nitrogen level. For instance, if the response contains the value 0x0020 in the data field, the nitrogen level can be calculated as follows:

Nitrogen = 0x0020HEX = 32DEC => 32 mg/kg

For Reading Phosphorous

Similarly, the Modbus RTU request for reading the phosphorus level is:

modbus rtu request for reading phosphorous

You will receive a similar response:

modbus rtu response with phosphorous reading

From the Response, you can obtain the phosphorus level. For instance, if the response contains the value 0x0025 in the data field, the phosphorus level can be calculated as follows:

Phosphorous = 0x0025HEX = 37DEC => 37 mg/kg

For Reading Potassium

Similar to the previous two requests, the Modbus RTU request for reading the Potassium level is:

modbus rtu request for reading potassium

You will receive a similar response:

modbus rtu response with potassium reading

From the Response, you can obtain the potassium level. For instance, if the response contains the value 0x0030 in the data field, the potassium level can be calculated as follows:

Potassium = 0x0030HEX = 48DEC => 48 mg/kg

Arduino Example Code

The following test sketch reads the nitrogen, phosphorus, and potassium values from the soil NPK sensor and prints them to the serial monitor.

#include <SoftwareSerial.h>
#include <Wire.h>

// RE and DE Pins set the RS485 module
// to Receiver or Transmitter mode
#define RE 8
#define DE 7

// Modbus RTU requests for reading NPK values
const byte nitro[] = {0x01,0x03, 0x00, 0x1e, 0x00, 0x01, 0xe4, 0x0c};
const byte phos[] = {0x01,0x03, 0x00, 0x1f, 0x00, 0x01, 0xb5, 0xcc};
const byte pota[] = {0x01,0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xc0};

// A variable used to store NPK values
byte values[11];

// Sets up a new SoftwareSerial object
// Digital pins 10 and 11 should be used with a Mega or Mega 2560
SoftwareSerial mod(2, 3);
//SoftwareSerial mod(10, 11);
 
void setup() {
  // Set the baud rate for the Serial port
  Serial.begin(9600);

  // Set the baud rate for the SerialSoftware object
  mod.begin(9600);

  // Define pin modes for RE and DE
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
  
  delay(500);
}
 
void loop() {
  // Read values
  byte val1,val2,val3;
  val1 = nitrogen();
  delay(250);
  val2 = phosphorous();
  delay(250);
  val3 = potassium();
  delay(250);

  // Print values to the serial monitor
  Serial.print("Nitrogen: ");
  Serial.print(val1);
  Serial.println(" mg/kg");
  Serial.print("Phosphorous: ");
  Serial.print(val2);
  Serial.println(" mg/kg");
  Serial.print("Potassium: ");
  Serial.print(val3);
  Serial.println(" mg/kg");
  
  delay(2000);
}
 
byte nitrogen(){
  digitalWrite(DE,HIGH);
  digitalWrite(RE,HIGH);
  delay(10);
  if(mod.write(nitro,sizeof(nitro))==8){
    digitalWrite(DE,LOW);
    digitalWrite(RE,LOW);
    for(byte i=0;i<7;i++){
    //Serial.print(mod.read(),HEX);
    values[i] = mod.read();
    Serial.print(values[i],HEX);
    }
    Serial.println();
  }
  return values[4];
}
 
byte phosphorous(){
  digitalWrite(DE,HIGH);
  digitalWrite(RE,HIGH);
  delay(10);
  if(mod.write(phos,sizeof(phos))==8){
    digitalWrite(DE,LOW);
    digitalWrite(RE,LOW);
    for(byte i=0;i<7;i++){
    //Serial.print(mod.read(),HEX);
    values[i] = mod.read();
    Serial.print(values[i],HEX);
    }
    Serial.println();
  }
  return values[4];
}
 
byte potassium(){
  digitalWrite(DE,HIGH);
  digitalWrite(RE,HIGH);
  delay(10);
  if(mod.write(pota,sizeof(pota))==8){
    digitalWrite(DE,LOW);
    digitalWrite(RE,LOW);
    for(byte i=0;i<7;i++){
    //Serial.print(mod.read(),HEX);
    values[i] = mod.read();
    Serial.print(values[i],HEX);
    }
    Serial.println();
  }
  return values[4];
}

You will see something like this.

soil npk sensor output

When you hear the term “smart garden,” one of the first things that comes to mind is a system that monitors the moisture level of the soil and automatically supplies the necessary amount of water to the plants.

With this system, plants can be watered only when required, avoiding over- or under-watering.

If you want to build such a system, you will undoubtedly require a Soil Moisture Sensor.

How Does a Soil Moisture Sensor Work?

The soil moisture sensor operates in a straightforward manner.

The fork-shaped probe with two exposed conductors acts as a variable resistor (similar to a potentiometer) whose resistance varies with the soil’s moisture content.

soil moisture sensor working.gif

This resistance varies inversely with soil moisture:

  • The more water in the soil, the better the conductivity and the lower the resistance.
  • The less water in the soil, the lower the conductivity and thus the higher the resistance.

The sensor produces an output voltage according to the resistance, which by measuring we can determine the soil moisture level.

Hardware Overview

A typical soil moisture sensor consists of two parts.

The Probe

The sensor includes a fork-shaped probe with two exposed conductors that is inserted into the soil or wherever the moisture content is to be measured.

As previously stated, it acts as a variable resistor, with resistance varying according to soil moisture.

soil moisture sensor probe

The Module

In addition, the sensor includes an electronic module that connects the probe to the Arduino.

The module generates an output voltage based on the resistance of the probe, which is available at an Analog Output (AO) pin.

The same signal is fed to an LM393 High Precision Comparator, which digitizes it and makes it available at a Digital Output (DO) pin.

soil moisture sensor sensitivity adjustment

The module includes a potentiometer for adjusting the sensitivity of the digital output (DO).

You can use it to set a threshold, so that when the soil moisture level exceeds the threshold, the module outputs LOW otherwise HIGH.

This setup is very useful for triggering an action when a certain threshold is reached. For example, if the moisture level in the soil exceeds a certain threshold, you can activate a relay to start watering the plant.

Rotate the knob clockwise to increase sensitivity and counterclockwise to decrease it.

soil moisture sensor power and status leds

The module also includes two LEDs. The Power LED illuminates when the module is turned on, and the Status LED illuminates when the soil moisture level exceeds the threshold value.

Soil Moisture Sensor Pinout

The soil moisture sensor is extremely simple to use and only requires four pins to connect.

soil moisture sensor pinout

AO (Analog Output) generates analog output voltage proportional to the soil moisture level, so a higher level results in a higher voltage and a lower level results in a lower voltage.

DO (Digital Output) indicates whether the soil moisture level is within the limit. D0 becomes LOW when the moisture level exceeds the threshold value (as set by the potentiometer), and HIGH otherwise.

VCC supplies power to the sensor. It is recommended that the sensor be powered from 3.3V to 5V. Please keep in mind that the analog output will vary depending on the voltage supplied to the sensor.

GND is the ground pin.

Experiment 1 – Measuring Soil Moisture using Analog Output (A0)

In our first experiment, we will read the analog output to estimate the level of soil moisture.

Wiring

Let’s hook up the soil moisture sensor to the Arduino.

Let’s begin by powering up the sensor. For this, you can connect the VCC pin of the module to Arduino’s 5V pin.

However, one well-known issue with these sensors is that they have a shorter lifespan because they are constantly exposed to moisture. Moreover, constantly applying power to the sensor while buried in soil significantly accelerates the rate of corrosion.

To avoid this, it is recommended that the sensor be turned on only when taking readings.

One easy way to do this is to connect the sensor’s power pin to a digital pin on an Arduino and set it to HIGH or LOW as needed. Also, the total power drawn by the module (with both LEDs lit) is about 8 mA, so powering the module from a digital pin is fine. So, we’ll connect the VCC pin to the Arduino’s digital pin #7.

Finally, connect the A0 pin to the Arduino’s A0 ADC pin.

The wiring is shown in the image below.

wiring soil moisture sensor with arduino for reading analog output

Finding the threshold values

To estimate the soil moisture level, record the values of your sensor output when the soil is as dry as possible and when it is completely saturated.

Keep in mind that your sensor may be more or less sensitive depending on the type of soil you use. Also, minerals dissolved in water from fertilizers and other sources can affect the sensor output.

Simply run the sketch below and take your readings.

// Sensor pins
#define sensorPower 7
#define sensorPin A0

void setup() {
	pinMode(sensorPower, OUTPUT);
	
	// Initially keep the sensor OFF
	digitalWrite(sensorPower, LOW);
	
	Serial.begin(9600);
}

void loop() {
	//get the reading from the function below and print it
	Serial.print("Analog output: ");
	Serial.println(readSensor());
	
	delay(1000);
}

//  This function returns the analog soil moisture measurement
int readSensor() {
	digitalWrite(sensorPower, HIGH);	// Turn the sensor ON
	delay(10);							// Allow power to settle
	int val = analogRead(sensorPin);	// Read the analog value form sensor
	digitalWrite(sensorPower, LOW);		// Turn the sensor OFF
	return val;							// Return analog moisture value
}

When you run the sketch, you should see readings similar to the ones below:

  • When the soil is dry (around 850)
  • When the soil is completely saturated (around 400)
calibrating soil moisture sensor

This test may require some trial and error. Once you have the readings, you can use them as a threshold to trigger an action.

Arduino Code

The sketch below estimates the level of soil moisture using the following threshold values:

  • < 500 is too wet
  • 500-750 is the target range
  • > 750 is dry enough to be watered
/* Change these values based on your calibration values */
#define soilWet 500   // Define max value we consider soil 'wet'
#define soilDry 750   // Define min value we consider soil 'dry'

// Sensor pins
#define sensorPower 7
#define sensorPin A0

void setup() {
	pinMode(sensorPower, OUTPUT);
	
	// Initially keep the sensor OFF
	digitalWrite(sensorPower, LOW);
	
	Serial.begin(9600);
}

void loop() {
	//get the reading from the function below and print it
	int moisture = readSensor();
	Serial.print("Analog Output: ");
	Serial.println(moisture);

	// Determine status of our soil
	if (moisture < soilWet) {
		Serial.println("Status: Soil is too wet");
	} else if (moisture >= soilWet && moisture < soilDry) {
		Serial.println("Status: Soil moisture is perfect");
	} else {
		Serial.println("Status: Soil is too dry - time to water!");
	}
	
	delay(1000);	// Take a reading every second for testing
					// Normally you should take reading perhaps once or twice a day
	Serial.println();
}

//  This function returns the analog soil moisture measurement
int readSensor() {
	digitalWrite(sensorPower, HIGH);	// Turn the sensor ON
	delay(10);							// Allow power to settle
	int val = analogRead(sensorPin);	// Read the analog value form sensor
	digitalWrite(sensorPower, LOW);		// Turn the sensor OFF
	return val;							// Return analog moisture value
}

If everything is fine, you should see something similar on the serial monitor.

soil moisture sensor analog output

Experiment 2 – Measuring Soil Moisture using Digital Output (D0)

In our second experiment, we will use digital output to determine whether the soil moisture level is within acceptable limits.

Wiring

We’ll reuse the previous experiment’s circuit. Simply disconnect the connection to the ADC pin and connect the D0 pin on the module to the Arduino’s digital pin #8.

The following image shows the wiring.

wiring soil moisture sensor with arduino for reading digital output

Setting the threshold

The module has a built-in potentiometer for setting the moisture level threshold above which the module outputs LOW and the status LED lights up.

digital output of soil moisture sensor

Now, to set the threshold, stick the probe into the soil when your plant needs watering and turn the pot clockwise until the Status LED is on. Then, turn the pot back counterclockwise just until the LED goes off.

That’s all there is to it; your module is now ready to use.

Arduino Code

Now, upload the sketch below to your Arduino.

// Sensor pins
#define sensorPower 7
#define sensorPin 8

void setup() {
	pinMode(sensorPower, OUTPUT);

	// Initially keep the sensor OFF
	digitalWrite(sensorPower, LOW);

	Serial.begin(9600);
}

void loop() {
	//get the reading from the function below and print it
	int val = readSensor();
	Serial.print("Digital Output: ");
	Serial.println(val);

	// Determine status of our soil moisture situation
	if (val) {
		Serial.println("Status: Soil is too dry - time to water!");
	} else {
		Serial.println("Status: Soil moisture is perfect");
	}

	delay(1000);	// Take a reading every second for testing
					// Normally you shoul take reading perhaps every 12 hours
	Serial.println();
}

//  This function returns the analog soil moisture measurement
int readSensor() {
	digitalWrite(sensorPower, HIGH);  // Turn the sensor ON
	delay(10);              // Allow power to settle
	int val = digitalRead(sensorPin); // Read the analog value form sensor
	digitalWrite(sensorPower, LOW);   // Turn the sensor OFF
	return val;             // Return analog moisture value
}

You should see similar output on the serial monitor.

soil moisture sensor digital output

Keeping a houseplant alive can be a tough job, right? And often, it’s just about remembering to water it. Fortunately, soil moisture sensors can help us remember to water our plants and help them live longer.

However, the majority of inexpensive soil moisture sensors are resistive style, where there’s two prongs and the sensor measures the water content in the soil based on the conductivity between them. They work fine initially, but over time, they start to rust, even if they’re gold-plated! This rusting messes with the readings, so you have to keep adjusting your code to get it right. Also, they don’t work that well in loose soil.

Luckily, there’s a better option: Capacitive Soil Moisture Sensors. These sensors operate based on capacitive measurement, which offers significant advantages over resistive measurement. These sensors have only one probe, no exposed metal to rust, and don’t harm your plants by introducing electricity into the soil.

In this tutorial, you will learn how to effectively use capacitive soil moisture sensors with Arduino. By the end of this tutorial, you’ll have the knowledge and skills to keep your plants thriving, without having to worry about under or overwatering. Let’s get started!

Hardware Overview

Capacitive Soil Moisture Sensors are quite amazing. Just stick them into the ground and they output an analog signal that is proportional to how wet the soil is.

These sensors make use of a 555 timer IC and operate by measuring how quickly (or slowly) a capacitor charges through a resistor, but in these sensors the capacitor is not a literal component, but is formed by two PCB traces that are near one another. Their capacitance, and therefore their charging rate, changes in response to how much water is around them.

capacitive soil moisture sensor hardware overview

The sensor includes an on-board 3.3V voltage regulator, making it suitable for 3.3V and 5V MCUs. Plus, it consumes less than 5mA of current.

Note that this sensor can only provide a qualitative measurement of soil moisture. As the soil gets wetter, the output value decreases, and as it gets drier, the output value increases. When powered at 5V, the output ranges from about 1.5V (for wet soil) to 3V (for dry soil).

capacitive soil moisture sensor output animation

However, the final output value is affected by probe insertion depth and how tight the soil packed around it is.

Technical Specifications

Here are the specifications:

Operating Voltage3.3 to 5.5V
Operating Current< 5mA
Output Voltage at 5V1.5V to 3V (approx.)
Sensor Probe L x W (PCB)98 x 23mm (3.86 x 0.91″)
Cable Length20cm (8″)

How Does a Capacitive Soil Moisture Sensor Work?

To understand how a capacitive soil moisture sensor works, you need to first understand the behavior of the capacitor in an RC circuit.

simple rc circuit

In a simple RC circuit like this, when a positive voltage is applied to the Input, the capacitor (C) begins to charge through the resistor (R). As it does, the voltage across the capacitor changes. Over time, the capacitor voltage rises to equal input voltage. Here, you can see a graph plotting voltage against time for the charging capacitor.

capacitor charging curve

The time it takes for the capacitor to fully charge depends on the values of the resistor and the capacitor. If you keep the R constant and try two different capacitance values for C, you will observe that a capacitor with a larger capacitance requires more time to charge,

large value capacitor charging curve

whereas a capacitor with a smaller capacitance requires less time to charge.

small value capacitor charging curve

Now coming back to our sensor, the capacitor C on the sensor board is not a literal component, but simply two copper traces that act like a capacitor. This effect, known as Parasitic Capacitance, happens often in circuits and is usually negligible. However, by deliberately making the two copper traces larger, we can use this effect to our advantage.

The capacitance of this parasitic capacitor is determined by the shape of the traces and the environment surrounding it (technically known as the dielectric constant). As the sensor is inserted into the soil, the environment around the capacitor changes depending on whether the soil becomes wetter or drier. This alters its capacitance, and consequently, affects its charging time.

When the soil is dry, the capacitor has a smaller capacitance and therefore charges up quickly. Conversely, when the soil is wet, the capacitor has a larger capacitance and therefore charges up more slowly.

To better understand how this is implemented in the sensor, let’s examine the circuit diagram.

capacitive soil moisture sensor schematic

The sensor uses a 555 configured as an astable oscillator. The square waves generated by the 555 are fed into the RC integrator, of which the capacitor is formed by the soil probe. The signal from the integrator is more of a triangular wave, which is fed into the rectifier and a smoothing capacitor to produce a DC output.

This output is proportional to the moisture content of the soil. So if the soil is dry, the capacitor charges quickly, resulting in a greater amplitude of the triangular wave and subsequently producing a higher output voltage. Conversely, when the soil is wet, the capacitor charges more slowly, resulting in a smaller amplitude of the triangular wave, which in turn generates a lower output voltage.

Capacitive Soil Moisture Sensor Pinout

The capacitive soil moisture sensor features a 3-pin JST PH2.0 type connector. One end of the provided cable plugs into this connector, while the other end is a standard Dupont style 3-pin female connector. The cable is color-coded so you can easily identify which wire is which: black represents ground, red represents VCC, and yellow represents AOUT.

capacitive soil moisture sensor pinout

VCC is the power supply pin. It is recommended that the sensor be powered from 3.3V to 5V. Please keep in mind that the analog output will vary depending on the voltage supplied to the sensor.

GND is the ground pin.

AOUT pin gives an analog voltage output that is proportional to the amount of moisture in the soil. The output can be read using an analog input on your microcontroller. As the moisture level increases, the output voltage decreases and vice versa.

Usage Instructions

When using the sensor, keep in mind the following:

  • It‘s recommended that the probe should not be placed on the depth which crosses the limit line on the sensor.
  • The components on this board are NOT waterproof, so make sure they don’t come in contact with water or splashes. For additional protection, consider using a piece of wide heat shrink tubing around the upper section of the board.
  • Note that the edges of the PCB could absorb moisture over time, shortening the lifespan of the sensor. To increase its durability, consider applying a protective coating, such as clear epoxy, which won’t affect the sensor’s performance.

Wiring a Capacitive Soil Moisture Sensor to an Arduino

Connecting a capacitive soil moisture sensor to an arduino is a breeze. You only need to connect three wires.

Start by connecting the sensor’s red wire (VCC) to the power supply, 3.3V-5V is fine. Use the same voltage that your microcontroller logic is based off of. For most Arduinos, that is 5V. For 3.3V logic devices, use 3.3V. Now connect the black wire (GND) to ground.

Finally, connect the yellow wire (AOUT) to one of the analog input pins on your Arduino. In our case, it is connected to the A0 pin.

The following table lists the pin connections:

Soil Moisture SensorArduino
VCC5V
GNDGND
AOUTA0

The wiring is shown in the image below.

wiring capacitive soil moisture sensor to arduino

Finding the Threshold Values

It is not possible to determine the actual percentage of moisture in the soil directly from the measurements taken. However, it is relatively easy to define basic ranges for what is considered “too dry,” “too wet,” and “just right.”

Simply run the sketch below and record your sensor output under three basic conditions:

  • When the soil is dry enough that the plant needs watering.
  • When the soil has been watered to its ideal moisture level for the plant.
  • When the soil has been heavily watered and is too wet, which is not ideal for the plant.
// Define analog input
#define sensorPin A0

void setup() {
  // Setup Serial Monitor
  Serial.begin(9600);
}

void loop() {
  // Read the Analog Input
  int value = analogRead(sensorPin);
  
  // Print the value to the serial monitor
  Serial.print("Analog output: ");
  Serial.println(value);
  
  // Wait for 1 second before the next reading
  delay(1000);
}

Upon running the sketch, you should expect readings similar to the ones listed below:

  • In open air: approximately 590
  • Dry soil that needs watering: approximately 380
  • Ideal soil moisture: between 277 and 380
  • Soil that has just been watered: approximately 277
  • In cup of water: approximately 273

This test may require some trial and error. Once you have the readings, you can use them as a threshold to trigger an action.

Arduino Example Code

The sketch below estimates the level of soil moisture using the following threshold values:

  • < 277 is too wet
  • 277 – 380 is the target range
  • > 380 is too dry
/* Change these values based on your observations */
#define wetSoil 277   // Define max value we consider soil 'wet'
#define drySoil 380   // Define min value we consider soil 'dry'

// Define analog input
#define sensorPin A0

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

void loop() {
  // Read the Analog Input and print it
  int moisture = analogRead(sensorPin);
  Serial.print("Analog output: ");
  Serial.println(moisture);
  
  // Determine status of our soil
  if (moisture < wetSoil) {
    Serial.println("Status: Soil is too wet");
  } else if (moisture >= wetSoil && moisture < drySoil) {
    Serial.println("Status: Soil moisture is perfect");
  } else {
    Serial.println("Status: Soil is too dry - time to water!");
  }
  Serial.println();
  
  // Take a reading every second
  delay(1000);
}

If everything is fine, you should see similar output on the serial monitor.

capacitive soil moisture sensor output

Add an ear to your project with the MAX4466 microphone amplifier module.

It’s a well-designed module with flexible supply voltage and adjustable gain, making it suitable for a wide range of audio applications. This module is particularly suited for projects like real-time voice changers, audio recording/sampling, and audio-reactive projects that use Fast Fourier Transform (FFT) algorithms.

Do you know how Electret Microphones work?

Inside a microphone, there is a thin diaphragm and a backplate. Together, they form a capacitor.

electret microphone working.gif

When you speak into the microphone, the sound waves produced by your voice hit the diaphragm, making it vibrate.

As the diaphragm vibrates, it moves closer or farther away from the backplate. This movement changes the capacitance between them. This change in capacitance produces a voltage across the plates, which we can measure to determine the amplitude of the sound.

Hardware Overview

At the core of the module is a micropower op-amp, the Maxim MAX4466, specifically designed for use as a microphone amplifier. Due to its high common-mode rejection and outstanding power-supply rejection ratio, the MAX4466 is an excellent choice for noisy environments. As a result, this amplifier delivers exceptional sound quality, free from the noise or scratchiness often found in other microphone amplifier breakouts.

max4466 module ic

The module also includes an electret microphone capable of capturing sound waves ranging from 20 Hz to 20 kHz, as well as the necessary supporting circuitry to drive the MAX4466.

max4466 module electret

Moreover, the module includes SMD ferrite beads that are connected in series with the power supply rails. These beads play a crucial role in suppressing electromagnetic emissions; they block low-frequency noise and absorb high-frequency noise, further enhancing the common-mode rejection ratio.

max4466 module ferrite beads

On the back of the module, there is a small trimmer potentiometer used to adjust the gain. The gain determines how much the module amplifies the sound signal. Rotating the potentiometer counterclockwise (CCW) increases the gain, while turning it clockwise (CW) decreases the gain.

max4466 module gain trimmer

The gain can be adjusted anywhere from 25x to 125x. At its lowest setting, it amplifies to roughly 200 mVpp (suitable for normal speaking volume about 6 inches away)—ideal for connecting to devices that expect ‘line level’ input without clipping. On the higher end, it can amplify up to about 1Vpp, which is ideal for reading from a microcontroller’s ADC. It’s worth noting that the output is rail-to-rail, meaning that if the sounds get loud enough, the output can reach up to 5 Vpp!

Using the module is straightforward: connect the GND pin to ground and the VCC pin to 2.4-5VDC. The audio waveform will come out of the OUT pin, which can be directly connected to the microcontroller’s ADC pin. Please note that the output will have a DC bias equal to VCC/2, so when it’s perfectly quiet, the voltage will remain steady at a VCC/2 level.

MAX4466 Module Pinout

The module has three pins.

max4466 module pinout

VCC is the power supply pin that accepts 2.4V to 5.5V DC voltage.

GND is the ground pin.

OUT is the analog output pin that delivers the amplified sound signal. Please note that the output will have a DC bias equal to VCC/2, so when it’s perfectly quiet, the voltage will remain steady at a VCC/2 level.

Wiring a MAX4466 Module to an Arduino

The wiring is relatively simple. There are only three wires to connect: two for power and one for output.

The VCC of the MAX4466 module can range between 2.4-5VDC. For optimal performance, it’s recommended to use the 3.3V pin as it provides the “quietest” supply on the Arduino. Therefore, power the sensor with 3.3V and connect ground to ground. Then connect the output to an analog input pin. In this example we’ll use pin A0.

The following table lists the pin connections:

MAX4466 ModuleArduino
VCC3.3V
GNDGND
OUTA0

The wiring is shown in the image below.

wiring max4466 module to arduino

Arduino Example Code – Measuring Sound Levels

The output from the MAX4466 amplifier is simply a varying voltage. To measure the sound level, we need to take multiple measurements to find the minimum and maximum values, or, in technical terms, the ‘peak-to-peak amplitude,’ of the signal.

In the example code below, we chose a sample window of 50 milliseconds. This duration is sufficient to measure sound levels for frequencies as low as 20 Hz, which is the lowest frequency that humans can hear. After finding the minimum and maximum samples, we compute the difference (to get the peak-to-peak amplitude of the sound) and convert it to volts. The output is then printed to the serial monitor.

const int sampleWindow = 50;  // Sample window width in mS (50 mS = 20Hz)
int const AMP_PIN = A0;       // Preamp output pin connected to A0
unsigned int sample;

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

void loop()
{
  unsigned long startMillis = millis(); // Start of sample window
  unsigned int peakToPeak = 0;   // peak-to-peak level

  unsigned int signalMax = 0;
  unsigned int signalMin = 1024;

  // collect data for 50 mS and then plot data
  while (millis() - startMillis < sampleWindow)
  {
    sample = analogRead(AMP_PIN);
    if (sample < 1024)  // toss out spurious readings
    {
      if (sample > signalMax)
      {
        signalMax = sample;  // save just the max levels
      }
      else if (sample < signalMin)
      {
        signalMin = sample;  // save just the min levels
      }
    }
  }
  peakToPeak = signalMax - signalMin;  // max - min = peak-peak amplitude
  Serial.println(peakToPeak);
  //double volts = (peakToPeak * 5.0) / 1024;  // convert to volts
  //Serial.println(volts);
}

Once you have uploaded the sketch, open the serial monitor and set the baud rate to 9600. Now, try snapping your fingers near the sensor; you will observe a change in the readings.

max4466 serial monitor output

Serial data can be hard to visualize if you’re only looking at values. If you are using the Arduino IDE v1.6.6+, there is an option to view the data on a graph using the Arduino Serial Plotter.

In the Arduino IDE, choose Tools > Serial Plotter. You should observe a waveform similar to the image below, when you snap your fingers near the sensor.

max4466 serial plotter output

Code Explanation:

The code begins by defining a few constants and variables.

First, a constant named sampleWindow is defined, which sets the sample window width in milliseconds. It’s set to 50, which corresponds to 20 Hz. Next, another constant AMP_PIN is defined, which represents the analog pin A0 where the output from the MAX4466 module is connected. An unsigned integer variable named sample is also defined to hold the values read from the AMP_PIN.

const int sampleWindow = 50;  // Sample window width in mS (50 mS = 20Hz)
int const AMP_PIN = A0;       // Preamp output pin connected to A0
unsigned int sample;

In setup(), the Serial Monitor is initialized for debugging purposes.

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

At the beginning of the loop() function, the current time (in milliseconds since the Arduino started) is stored in the variable startMillis.

unsigned long startMillis= millis();

After that, the variables peakToPeak, signalMax, and signalMin are initialized. peakToPeak will hold the difference between the maximum and minimum values read from the microphone, representing the amplitude of the sound. signalMax and signalMin are used to store the highest and lowest values read from the microphone during the sampling window, respectively.

unsigned int peakToPeak = 0;
unsigned int signalMax = 0;
unsigned int signalMin = 1024;

The while loop runs as long as the difference between the current time and startMillis is less than sampleWindow (50 milliseconds in this case). This loop is used to read the microphone’s output and find the highest and lowest values during this time frame. Inside the loop, the voltage at AMP_PIN is read using analogRead() and stored in the sample variable. If the sample is valid (and not spurious) and greater than signalMax, it updates signalMax; if the sample is less than signalMin, it updates signalMin.

while (millis() - startMillis < sampleWindow)
{
  sample = analogRead(AMP_PIN);
  if (sample < 1024)  // toss out spurious readings
  {
    if (sample > signalMax)
    {
      signalMax = sample;  // save just the max levels
    }
    else if (sample < signalMin)
    {
      signalMin = sample;  // save just the min levels
    }
  }
}

Once the sampling window is over, the difference between signalMax and signalMin is calculated to get the peak-to-peak amplitude of the sound. This value is stored in peakToPeak.

peakToPeak = signalMax - signalMin;

The peakToPeak value is then printed to the Serial monitor.

Serial.println(peakToPeak);

The commented-out lines at the bottom of the code converts the peak-to-peak amplitude of the sound into volts. This can be useful if you want to see the voltage representation of the amplitude.

//double volts = (peakToPeak * 5.0) / 1024;  // convert to volts
//Serial.println(volts);

Arduino Project – Scrolling Sound Level Meter

Okay, that was not very exciting. So, now let’s take the peak-to-peak measurement and use it to control a MAX7219-based LED Matrix to display the sound level. To make it more interesting, we will scroll the display to graph the last 8 measurements in real-time.

If you’re not familiar with the MAX7219 LED Dot Matrix Display, you may want to consider reading the tutorial below.

Connecting the Hardware

The image below shows how to build the circuit.

wiring max4466 and max7219 modules to arduino

The following tables list the pin connections:

MAX4466 ModuleArduino
VCC3.3V
GNDGND
OUTA0
MAX7219 ModuleArduino
VCC5V
GNDGND
DIN11
CS/LOAD10
CLK13

Installing the LedControl Library

To work with the MAX7219 chip, you’ll need to use a library. The LedControl library by Eberhard Fahle is an excellent library. This library provides a standard set of functions to control each LED individually, which will be extremely useful for this project.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the libraries index and update the list of installed libraries.

manage libraries 2

Filter your search by entering ‘ledcontrol’. Look for LedControl by Eberhard Fahle. Click on that entry and then choose Install.

ledcontrol library installation

Uploading the Code

Copy and paste the following code into the Arduino IDE and upload it.

#include "LedControl.h"

int displayCnt = 1;  // # of displays connected (between 1-8)
int intensity = 1;   // Brightness of displays (between 0-15)

LedControl lc = LedControl(11, 13, 10, displayCnt);  // Pins: DIN, CLK, CS, # of Displays

const int maxScale = 8;
const int sampleWindow = 50; // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;

byte columnData[8] = {0, 0, 0, 0, 0, 0, 0, 0};  // To store the 8 column data

void setup() {
  for (int idx = 0; idx < displayCnt; idx++) {
    lc.shutdown(idx, false);  // Wake up displays
    lc.setIntensity(idx, intensity);  // Set intensity levels
    lc.clearDisplay(idx);  // Clear Displays
  }
}

void loop() {
  unsigned long startMillis = millis();
  unsigned int peakToPeak = 0;
  unsigned int signalMax = 0;
  unsigned int signalMin = 1024;

  while (millis() - startMillis < sampleWindow) {
    sample = analogRead(0);
    if (sample < 1024) {
      if (sample > signalMax) {
        signalMax = sample;
      } else if (sample < signalMin) {
        signalMin = sample;
      }
    }
  }
  peakToPeak = signalMax - signalMin;
  int displayPeak = map(peakToPeak, 0, 512, 0, maxScale);

  // Shift column data in the array
  for (int i = 0; i < 7; i++) {
    columnData[i] = columnData[i + 1];
  }

  // Create new measurement column
  byte newData = 0;
  for (int i = 0; i < displayPeak; i++) {
    newData |= (1 << i);
  }
  columnData[7] = newData;

  // Update the display
  for (int col = 0; col < 8; col++) {
    lc.setColumn(0, col, columnData[col]);
  }

  delay(100);

Demonstration

Now, try speaking in a normal voice from a distance of about 6–8 inches away from the microphone, and you should see the sound level meter matrix display start scrolling.

max4466 scrolling sound level meter output

Ready to give your next project an ear?

These sound sensors are inexpensive, simple to use, and capable of detecting voice, claps, or door knocks.

You can use them for a variety of sound-reactive projects, such as making your lights clap-activated or monitoring your pets while you’re away.

Do you know how Electret Microphones work?

Inside a microphone is a thin diaphragm and a backplate. Collectively, they function as a capacitor.

electret microphone working.gif

When you speak into the microphone, your voice generates sound waves that strike the diaphragm, causing it to vibrate.

When the diaphragm vibrates in response to sound, the plates move closer or farther apart, causing the capacitance to change. As a result, a voltage is generated across the plates, which we can measure to determine the amplitude of the sound.

Hardware Overview

The sound sensor is a small board that incorporates a microphone (50Hz-10kHz) and some processing circuitry to convert the sound wave into an electrical signal.

This electrical signal is fed to the on-board LM393 High Precision Comparator, which digitizes it and makes it available at the OUT pin.

sound sensor sensitivity adjustment and comparator

The module includes a potentiometer for adjusting the sensitivity of the OUT signal.

You can use it to set a threshold, so that when the amplitude of the sound exceeds the threshold, the module outputs LOW, otherwise HIGH.

This setup is very useful for triggering an action when a certain threshold is reached. For example, when the amplitude of the sound exceeds a threshold (a knock is detected), you can activate a relay to control the light.

Rotate the knob counterclockwise to increase sensitivity and clockwise to decrease it.

sound sensor power and status leds

The module also includes two LEDs. The Power LED illuminates when the module is turned on, and the Status LED illuminates when the sound level exceeds the threshold value.

Sound Sensor Pinout

The sound sensor only has three pins:

sound sensor module pinout

VCC supplies power to the sensor. It is recommended that the sensor be powered from 3.3V to 5V.

GND is the ground pin.

OUT pin outputs HIGH under quiet conditions and LOW when sound is detected. You can connect it to any digital pin on an Arduino or to a 5V relay directly.

Wiring a Sound Sensor to an Arduino

Let’s hook the sound sensor to the Arduino.

Connections are fairly simple. Start by connecting the module’s VCC pin to the Arduino’s 5V pin and the GND pin to ground.

Finally, connect the OUT pin to Arduino digital pin #8. That’s it!

The wiring is shown in the image below.

wiring sound sensor with arduino

Setting the Threshold

The module has a built-in potentiometer for setting the sound level threshold above which the module outputs LOW and the status LED lights up.

digital output of sound sensor

Now, to set the threshold, click your finger close to the microphone and adjust the potentiometer until the module’s Status LED blinks in response to your clicks.

That’s all there is to it; your module is now ready to use.

Example 1 – Basic Sound Detection

The following simple example detects claps or snaps and displays a message on the serial monitor. Go ahead and try out the sketch; we’ll go over it in detail later.

#define sensorPin 8

// Variable to store the time when last event happened
unsigned long lastEvent = 0;

void setup() {
	pinMode(sensorPin, INPUT);	// Set sensor pin as an INPUT
	Serial.begin(9600);
}

void loop() {
	// Read Sound sensor
	int sensorData = digitalRead(sensorPin);

	// If pin goes LOW, sound is detected
	if (sensorData == LOW) {
		
		// If 25ms have passed since last LOW state, it means that
		// the clap is detected and not due to any spurious sounds
		if (millis() - lastEvent > 25) {
			Serial.println("Clap detected!");
		}
		
		// Remember when last event happened
		lastEvent = millis();
	}
}

If everything is working properly, you should see the following output on the serial monitor when the clap is detected.

sound sensor output

Code Explanation:

The sketch starts by declaring the Arduino pin to which the sensor’s OUT pin is connected.

#define sensorPin 8

Next, we define a variable called lastEvent that stores the time when a clap was previously detected. It will help us reduce accidental sound detection.

unsigned long lastEvent = 0;

In the Setup section, we configure the sensor’s OUT pin to behave as an input and establish serial communication.

pinMode(sensorPin, INPUT);
Serial.begin(9600);

In the loop section, we first read the sensor output.

int sensorData = digitalRead(sensorPin);

When the sensor detects a sound loud enough to exceed the threshold value, the output goes LOW. However, we must ensure that the sound is caused by clapping and not by background noise. Therefore, we wait 25 milliseconds after the output goes low. If the output remains LOW for more than 25 milliseconds, the message “Clap detected” is printed on the serial monitor.

if (sensorData == LOW) {
	if (millis() - lastEvent > 25) {
		Serial.println("Clap detected!");
	}
	lastEvent = millis();
}

Example 2 – Controlling Devices With a Clap

For our next project, we will use the sound sensor to create a “Clapper” that activates AC-powered devices with a handclap.

This project controls AC-powered devices using One Channel Relay Module. If you are unfamiliar with the relay module, consider reading the tutorial below.

Wiring

The wiring for this project is straightforward.

Warning:
This board interacts with HIGH AC voltage. Improper or incorrect use could result in serious injury or death. Therefore, it is intended for people who are familiar with and knowledgeable about HIGH AC voltage.

Let’s begin by providing power to the sensor and relay module. Connect their VCC pins to the Arduino’s 5V pin and GND to ground.

Connect the sound sensor’s output pin (OUT) to digital pin #7 on your Arduino, and the relay module’s control pin (IN) to digital pin #8.

You’ll also need to connect the relay module to the AC-powered device you are attempting to control. You’ll need to cut your live alternating current line and connect one end of the cut wire (coming from the wall) to COM and the other to NO.

The wiring is shown in the diagram below.

wiring sound sensor and relay with arduino

Arduino Code

Here’s the code for controlling devices with a clap.

#define sensorPin 7
#define relayPin 8

// Variable to store the time when last event happened
unsigned long lastEvent = 0;
boolean relayState = false;    // Variable to store the state of relay

void setup() {
	pinMode(relayPin, OUTPUT);  // Set relay pin as an OUTPUT pin
	pinMode(sensorPin, INPUT);  // Set sensor pin as an INPUT
}

void loop() {
	// Read Sound sensor
	int sensorData = digitalRead(sensorPin);

	// If pin goes LOW, sound is detected
	if (sensorData == LOW) {

	// If 25ms have passed since last LOW state, it means that
	// the clap is detected and not due to any spurious sounds
	if (millis() - lastEvent > 25) {
		//toggle relay and set the output
		relayState = !relayState;
		digitalWrite(relayPin, relayState ? HIGH : LOW);
	}

	// Remember when last event happened
	lastEvent = millis();
	}
}

When you’re done, the sensor should turn the device on or off every time you clap.

Code Explanation:

If you compare this sketch to our previous one, you’ll notice many similarities, with a few differences.

At the start, we declare the Arduino pin to which the relay’s control pin (IN) is connected. In addition, we have defined a new variable relayState to keep track of the relay’s status.

#define relayPin 7

boolean relayState = false;

In the Setup, we configure the relayPin as an output.

pinMode(relayPin, OUTPUT);

When we detect the sound of the clap, instead of printing the message on the serial monitor, we simply toggle the state of the relay.

relayState = !relayState;
digitalWrite(relayPin, relayState ? HIGH : LOW);

Troubleshooting

If the Sound Sensor is misbehaving, try the steps below.

  • Make sure that the power supply is clean. Because the sound sensor is an analog circuit, it is more sensitive to power supply noise.
  • The sensor is also sensitive to mechanical vibration and wind noise. Mounting the sensor to a solid substrate can reduce some of these vibrations.
  • This sound sensor has a very short sensing range, perhaps only 10 inches, so you’ll need to make a noise much closer to it in order to get a reliable reading.

If you’ve ever had a water heater explode or attempted to build submersible electronics, you know how important it is to detect the presence of water.

That is exactly what this Water Level Sensor allows you to do! It can measure the water level, monitor a sump pit, detect rainfall, and detect leaks.

Let’s look into this sensor in more detail.

Hardware Overview

The sensor has ten exposed copper traces, five of which are power traces and the remaining five are sense traces. These traces are interlaced so that there is one sense trace between every two power traces.

Normally, power and sense traces are not connected, but when immersed in water, they are bridged.

water level sensor hardware overview

There is a Power LED on the board, which will light up when the board is powered.

How Does a Water Level Sensor Work?

The operation of the water level sensor is fairly simple.

The power and sense traces form a variable resistor (much like a potentiometer) whose resistance varies based on how much they are exposed to water.

water level sensor working.gif

This resistance varies inversely with the depth of immersion of the sensor in water:

  • The more water the sensor is immersed in, the better the conductivity and the lower the resistance.
  • The less water the sensor is immersed in, the poorer the conductivity and the higher the resistance.

The sensor generates an output voltage proportional to the resistance; by measuring this voltage, the water level can be determined.

Water Level Sensor Pinout

The water level sensor is extremely simple to use and only requires three pins to connect.

water level sensor pinout

S (Signal) is an analog output pin that will be connected to one of your Arduino’s analog inputs.

+ (VCC) pin provides power to the sensor. It is recommended that the sensor be powered from 3.3V to 5V. Please keep in mind that the analog output will vary depending on the voltage supplied to the sensor.

– (GND) is the ground pin.

Wiring a Water Level Sensor to an Arduino

Let’s hook up the water level sensor to the Arduino.

To begin, connect the + (VCC) pin on the module to 5V on the Arduino and the – (GND) pin to ground.

One well-known issue with these sensors is that they have a shorter lifespan because they are constantly exposed to moisture. Moreover, constantly applying power to the sensor while immersed in water significantly accelerates the rate of corrosion.

To avoid this, it is recommended that the sensor be turned on only when taking readings.

One easy way to do this is to connect the sensor’s power pin to a digital pin on an Arduino and set it to HIGH or LOW as needed. So, we’ll connect the + (VCC) pin to the Arduino’s digital pin #7.

Finally, connect the S (Signal) pin to the Arduino’s A0 ADC pin.

The wiring is shown in the image below.

wiring water level sensor with arduino

Basic Arduino Example

After constructing the circuit, upload the following sketch to your Arduino.

// Sensor pins
#define sensorPower 7
#define sensorPin A0

// Value for storing water level
int val = 0;

void setup() {
	// Set D7 as an OUTPUT
	pinMode(sensorPower, OUTPUT);
	
	// Set to LOW so no power flows through the sensor
	digitalWrite(sensorPower, LOW);
	
	Serial.begin(9600);
}

void loop() {
	//get the reading from the function below and print it
	int level = readSensor();
	
	Serial.print("Water level: ");
	Serial.println(level);
	
	delay(1000);
}

//This is a function used to get the reading
int readSensor() {
	digitalWrite(sensorPower, HIGH);	// Turn the sensor ON
	delay(10);							// wait 10 milliseconds
	val = analogRead(sensorPin);		// Read the analog value form sensor
	digitalWrite(sensorPower, LOW);		// Turn the sensor OFF
	return val;							// send current reading
}

After uploading the sketch, open the Serial Monitor window to view the output. When the sensor is dry, it will output a value of 0; however, as the sensor is immersed in water, the output will gradually increase.

water level sensor output

This sensor is not intended to be fully submerged. So, when installing it, make sure that only the exposed traces on the PCB come into contact with the water.

Code Explanation:

The sketch begins with the declaration of the Arduino pins to which the sensor’s + (VCC) and S (signal) pins are connected.

#define sensorPower 7
#define sensorPin A0

Following that, we define a variable val to store the current water level.

int val = 0;

In the Setup section, we first configure the sensor’s power connection to behave as an output, then we set it low to keep the sensor off initially. We establish serial communication as well.

pinMode(sensorPower, OUTPUT);
digitalWrite(sensorPower, LOW);
Serial.begin(9600);

In the loop section, we call the readSensor() custom function once every one second and print the result.

Serial.print("Water level: ");
Serial.println(readSensor());
delay(1000);

The readSensor() custom function simply turns on the sensor, waits 10 milliseconds, reads the analog value from the sensor, turns it off, and returns the analog value.

int readSensor() {
	digitalWrite(sensorPower, HIGH);
	delay(10);
	val = analogRead(sensorPin);
	digitalWrite(sensorPower, LOW);
	return val;
}

Finding the threshold values

To estimate the water level, record the values of your sensor output when the sensor is completely dry, partially immersed, and fully immersed in water.

Simply run the above sketch and take your readings.

Keep in mind that your sensor may be more or less sensitive depending on the type of water you use. As you may know, pure water is not conductive; it is the minerals and impurities in water that make it conductive.

When you run the sketch, you should see readings similar to the ones below:

  • when the sensor is dry (0)
  • when the sensor is partially immersed in water (~420)
  • when the sensor is fully immersed in water (~520)
water level sensor calibration

This test may require some trial and error. Once you have the readings, you can use them as a threshold to trigger an action.

Arduino Project – Water Level Indicator

For our next example, we’ll build a simple water level indicator that will light up LEDs based on the level of the water.

Wiring

We’ll reuse the previous example’s circuit. We only need to add some LEDs this time.

Connect three LEDs to digital pins #2, #3, and #4 using 220 ohm resistors.

Connect your circuit as shown below:

determining water level with led indicators

Arduino Code

After constructing the circuit, upload the following sketch to your Arduino.

This sketch defines two variables: lowerThreshold and upperThreshold. These variables represent our thresholds.

If the output falls below the lower threshold, the red LED will light up, if it rises above the upper threshold, the green LED will light up, and if it falls somewhere in the middle, the yellow LED will light up.

/* Change these values based on your calibration values */
int lowerThreshold = 420;
int upperThreshold = 520;

// Sensor pins
#define sensorPower 7
#define sensorPin A0

// Value for storing water level
int val = 0;

// Declare pins to which LEDs are connected
int redLED = 2;
int yellowLED = 3;
int greenLED = 4;

void setup() {
	Serial.begin(9600);
	pinMode(sensorPower, OUTPUT);
	digitalWrite(sensorPower, LOW);
	
	// Set LED pins as an OUTPUT
	pinMode(redLED, OUTPUT);
	pinMode(yellowLED, OUTPUT);
	pinMode(greenLED, OUTPUT);

	// Initially turn off all LEDs
	digitalWrite(redLED, LOW);
	digitalWrite(yellowLED, LOW);
	digitalWrite(greenLED, LOW);
}

void loop() {
	int level = readSensor();

	if (level == 0) {
		Serial.println("Water Level: Empty");
		digitalWrite(redLED, LOW);
		digitalWrite(yellowLED, LOW);
		digitalWrite(greenLED, LOW);
	}
	else if (level > 0 && level <= lowerThreshold) {
		Serial.println("Water Level: Low");
		digitalWrite(redLED, HIGH);
		digitalWrite(yellowLED, LOW);
		digitalWrite(greenLED, LOW);
	}
	else if (level > lowerThreshold && level <= upperThreshold) {
		Serial.println("Water Level: Medium");
		digitalWrite(redLED, LOW);
		digitalWrite(yellowLED, HIGH);
		digitalWrite(greenLED, LOW);
	}
	else if (level > upperThreshold) {
		Serial.println("Water Level: High");
		digitalWrite(redLED, LOW);
		digitalWrite(yellowLED, LOW);
		digitalWrite(greenLED, HIGH);
	}
	delay(1000);
}

//This is a function used to get the reading
int readSensor() {
	digitalWrite(sensorPower, HIGH);
	delay(10);
	val = analogRead(sensorPin);
	digitalWrite(sensorPower, LOW);
	return val;
}

With the weather being as unpredictable as ever, it’s easy to leave your skylights open, only for it to suddenly start raining, leaving the interior below at risk. With this rain sensor, however, you can stop this from happening.

You can use this sensor to monitor rain or slushy snow/hail and send closure requests to electronic shutters, windows, awnings or skylights whenever the rain is detected.

How Rain Sensor works?

The working of the rain sensor is pretty straightforward.

The sensing pad with series of exposed copper traces, together acts as a variable resistor (just like a potentiometer) whose resistance varies according to the amount of water on its surface.

rain sensor working.gif

This resistance is inversely proportional to the amount of water:

  • The more water on the surface means better conductivity and will result in a lower resistance.
  • The less water on the surface means poor conductivity and will result in a higher resistance.

The sensor produces an output voltage according to the resistance, which by measuring we can determine whether it’s raining or not.

Hardware Overview

A typical rain sensor has two components.

The Sensing Pad

The sensor contains a sensing pad with series of exposed copper traces that is placed out in the open, possibly over the roof or where it can be affected by rainfall.

Usually these traces are not connected but are bridged by water.

rain sensor probe

The Module

The sensor also contains an electronic module that connects the sensing pad to the Arduino.

The module produces an output voltage according to the resistance of the sensing pad and is made available at an Analog Output (AO) pin.

The same signal is fed to a LM393 High Precision Comparator to digitize it and is made available at an Digital Output (DO) pin.

rain sensor sensitivity adjustment

The module has a built-in potentiometer for sensitivity adjustment of the digital output (DO).

You can set a threshold by using a potentiometer; So that when the amount of water exceeds the threshold value, the module will output LOW otherwise HIGH.

Rotate the knob clockwise to increase sensitivity and counterclockwise to decrease it.

rain sensor power and status leds

Apart from this, the module has two LEDs. The Power LED will light up when the module is powered. The Status LED will light up when the digital output goes LOW.

Rain Sensor Pinout

The rain sensor is super easy to use and only has 4 pins to connect.

rain sensor module pinout

AO (Analog Output) pin gives us an analog signal between the supply value (5V) to 0V.

DO (Digital Output) pin gives Digital output of internal comparator circuit. You can connect it to any digital pin on an Arduino or directly to a 5V relay or similar device.

GND is a ground connection.

VCC pin supplies power for the sensor. It is recommended to power the sensor with between 3.3V – 5V. Please note that the analog output will vary depending on what voltage is provided for the sensor.

Wiring Rain Sensor with Arduino

Let’s hook the rain sensor up to the Arduino.

First you need to supply power to the sensor. For that you may connect the VCC pin on the module to 5V on the Arduino.

However, one commonly known issue with these senors is their short lifespan when exposed to a moist environment. Having power applied to the sensing pad constantly, speeds the rate of corrosion significantly.

To overcome this, we recommend that you do not power the sensor constantly, but power it only when you take the readings.

An easy way to accomplish this is to connect the VCC pin to a digital pin of an Arduino and set it to HIGH or LOW as as per your requirement.

Also the total power drawn by the module (with both LEDs lit) is about 8 mA, so it is okay to power the module off a digital pin on an Arduino.

So, let’s connect the VCC pin on the module to the digital pin #7 of an Arduino and GND pin to ground.

Finally, connect the DO pin on the module to the digital pin #8 on your Arduino.

The following illustration shows the wiring.

wiring rain sensor with arduino

Calibrating Rain Sensor

To get accurate readings out of your rain sensor, it is recommended that you first calibrate it.

The module has a built-in potentiometer for calibrating the digital output (DO).

By turning the the knob of the potentiometer, you can set a threshold. So that when the amount of water exceeds the threshold value, the Status LED will light up and the digital output (DO) will output LOW.

Now to calibrate the sensor, sprinkle some water on the sensing pad and adjust the pot clockwise so that the Status LED is ON and then adjust the pot back counterclockwise just until the LED goes OFF.

That’s it your sensor is now calibrated and ready for use.

Detecting Rain – Arduino Code

Once the circuit is built, upload the following sketch to your Arduino.

Now place the rain sensor in a location such that precipitation can fall directly into the sensor, possibly over the roof. Also place it slightly tilted (~20°) to facilitate the flow of water.

Note that the electronic module is not designed to be waterproof, be careful to install it so that only the sensing pad will come in contact with water.

// Sensor pins
#define sensorPower 7
#define sensorPin 8

void setup() {
	pinMode(sensorPower, OUTPUT);

	// Initially keep the sensor OFF
	digitalWrite(sensorPower, LOW);

	Serial.begin(9600);
}

void loop() {
	//get the reading from the function below and print it
	int val = readSensor();
	Serial.print("Digital Output: ");
	Serial.println(val);

	// Determine status of rain
	if (val) {
		Serial.println("Status: Clear");
	} else {
		Serial.println("Status: It's raining");
	}

	delay(1000);	// Take a reading every second
	Serial.println();
}

//  This function returns the sensor output
int readSensor() {
	digitalWrite(sensorPower, HIGH);	// Turn the sensor ON
	delay(10);							// Allow power to settle
	int val = digitalRead(sensorPin);	// Read the sensor output
	digitalWrite(sensorPower, LOW);		// Turn the sensor OFF
	return val;							// Return the value
}

Once the sketch is uploaded, open a Serial Monitor window to see the output from the Arduino. You should see a digital output HIGH when the weather is clear. To see it sense water, you can sprinkle some water on the sensing pad.

rain sensor output

Code Explanation:

The sketch begins with the declaration of the Arduino pins to which the sensor’s VCC and DO pins are connected.

#define sensorPower 7
#define sensorPin 8

Now in the the Setup section, we first declare the power connection to the sensor as output, then we set it low so no power flows through the sensor initially. We also setup the serial monitor.

pinMode(sensorPower, OUTPUT);
digitalWrite(sensorPower, LOW);
Serial.begin(9600);

In the loop section, we call readSensor() function repeatedly at the interval of one second and print the returned value along with the status.

int val = readSensor();
Serial.print("Digital Output: ");
Serial.println(val);

if (val) {
	Serial.println("Status: Clear");
} else {
	Serial.println("Status: It's raining");
}

delay(1000);

The readSensor() function is used to get the current digital output of the sensor. It turns the sensor ON, waits for 10 milliseconds, reads the digital value from the sensor, turns the sensor OFF and then returns the result.

int readSensor() {
	digitalWrite(sensorPower, HIGH);
	delay(10);
	int val = digitalRead(sensorPin);
	digitalWrite(sensorPower, LOW);
	return val;
}

A Force Sensing Resistor, also known as a Force Sensor, or simply an FSR, is a simple and inexpensive sensor designed to measure physical pressure, squeeze, and weight.

It can be found in a variety of portable electronics, including electronic drums, handheld gaming devices, and mobile phones.

This sensor is excellent at measuring pressure, but not so accurate at estimating how much weight is on it. So, if you just want to know “whether the sensor has been squeezed or pressed and how hard,” it could be a good choice for your next force-sensing project.

FSR Overview

The patent for the technology used in FSRs is owned by Interlink Electronics, which has been in business since 1985. The most common types of FSR that you will encounter are Interlink FSR-402 and FSR-406.

Construction

An FSR is simply a variable resistor whose resistance varies in response to pressure applied to the sensing area.

It is made up of several thin, flexible layers. When squeezed, more of the carbon elements that normally offer resistance are brought into contact with the conductive traces, thereby lowering the resistance.

how fsrs made

Shape and Size

There is a wide selection of FSRs available, each with its own unique size, shape, and sensing range.

The majority of FSRs have circular or rectangular sensing areas. Rectangular FSRs are ideal for wide-area sensing, whereas small circular sensors can provide higher accuracy.

types of fsr sensors

Sensing Range

The sensing range of the FSR is another important aspect because it determines the lowest and highest pressures that it can detect.

The shorter the FSR’s sensing range, the greater its sensitivity. But! any pressure above the maximum range of the sensor is unmeasurable (which can also damage the sensor).  For example, a smaller 1kg rated FSR will provide more sensitive readings from 0 to 1kg, but cannot distinguish between 2kg and 5kg.

How Does FSR Work?

As previously stated, an FSR is basically a resistor whose resistive value varies based on the amount of pressure applied to it.

fsr working animation

In the absence of pressure, the sensor will read infinite resistance (larger than 1MΩ). Applying more pressure to the sensor’s head reduces the resistance between its terminals, while releasing the pressure restores the resistance to its original value.

The graph below shows the approximate resistance of the FSR-402 sensor at various applied forces. Here, logarithmic scales are used to plot the data.

fsr 402 resistance vs force curve

Observe that the graph is linear above 50g. This is due to the fact that these sensors have a turn-on threshold. It’s a force that must exist before the resistance drops below 100k, after which the relationship becomes more linear.

Reading an FSR

The simplest way to read the FSR is to combine it with a static resistor to form a voltage divider, which produces a variable voltage that can be read by the analog-to-digital converter of a microcontroller.

fsr voltage divider

It is important to note that the output voltage you measure is the voltage drop across the pull-down resistor, not the voltage drop across the FSR.

We can use this equation to calculate the output voltage (Vo).

fsr1

In this configuration, the output voltage increases as the applied force increases.

For example, with a 5V supply and a 10K pull-down resistor, when there is no pressure, the FSR resistance is extremely high (around 10M). This produces the following output voltage:

fsr2

If you apply significant force to the FSR, the resistance will drop to approximately 250 Ω. As a result, the output voltage becomes:

fsr3

As you can see, the output voltage varies from 0 to 5V depending on the amount of force applied to the sensor.

The table below gives a rough idea of the analog voltage you can expect from an FSR at various applied forces.

Force (lb)Force (N)FSR ResistanceVoltage across R
NoneNoneInfinite0V
0.04lb0.2N30KΩ1.3V
0.22lb1N6KΩ3.1V
2.2lb10N1KΩ4.5V
22lb100N250Ω4.9V

Wiring an FSR to an Arduino

It is quite easy to connect FSR to an arduino.

You need to connect a 10kΩ pull-down resistor in series with the FSR to create a voltage divider circuit. Next, the A0 ADC input of an Arduino is wired to the junction of the pull-down resistor and the FSR.

wiring fsr to arduino

Keep in mind that FSRs are really just resistors, which means you can connect them either way and they will still work.

Arduino Example 1 – Simple Analog FSR Measurements

In our first experiment, we will read sensor data from the Arduino’s ADC pin and display it on the serial monitor.

The code is pretty straightforward. It simply prints out what it interprets as the amount of pressure in a qualitative manner. For most projects, this is pretty much all that is needed.

int fsrPin = 0;     // the FSR and 10K pulldown are connected to a0
int fsrReading;     // the analog reading from the FSR resistor divider
 
void setup(void) {
  Serial.begin(9600);   
}
 
void loop(void) {
  fsrReading = analogRead(fsrPin);  
 
  Serial.print("Analog reading = ");
  Serial.print(fsrReading);     // print the raw analog reading
 
  if (fsrReading < 10) {
    Serial.println(" - No pressure");
  } else if (fsrReading < 200) {
    Serial.println(" - Light touch");
  } else if (fsrReading < 500) {
    Serial.println(" - Light squeeze");
  } else if (fsrReading < 800) {
    Serial.println(" - Medium squeeze");
  } else {
    Serial.println(" - Big squeeze");
  }
  delay(1000);
}

If everything is fine, you should see the following output on the serial monitor.

fsr analog output

Code Explanation:

The sketch begins with the declaration of the Arduino pin to which FSR is connected. Next, we define the fsrReading variable to store the FSR’s raw analog reading.

int fsrPin = 0;
int fsrReading;

In the setup function, serial communication is established.

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

In the loop, we read the FSR’s analog output and show it on the serial monitor.

As mentioned previously, the sensor’s output voltage varies from 0V (no pressure applied) to approximately 5V (maximum pressure applied). When the Arduino converts this analog voltage to a digital value, it converts it to a 10-bit number between 0 and 1023. Therefore, the value displayed on the serial monitor will range from 0 to 1023 based on how hard you squeeze the sensor.

fsrReading = analogRead(fsrPin);  
 
Serial.print("Analog reading = ");
Serial.print(fsrReading);

Finally, we print the amount of pressure measured qualitatively.

if (fsrReading < 10) {
	Serial.println(" - No pressure");
} else if (fsrReading < 200) {
	Serial.println(" - Light touch");
} else if (fsrReading < 500) {
	Serial.println(" - Light squeeze");
} else if (fsrReading < 800) {
	Serial.println(" - Medium squeeze");
} else {
	Serial.println(" - Big squeeze");
}

Arduino Example 2 – Advanced Analog FSR Measurements

The following Arduino sketch is quite advanced. It measures the approximate Newton force measured by the FSR. This can be quite helpful for calibrating the forces you expect the FSR will experience.

int fsrPin = 0;     // the FSR and 10K pulldown are connected to a0
int fsrReading;     // the analog reading from the FSR resistor divider
int fsrVoltage;     // the analog reading converted to voltage
unsigned long fsrResistance;  // The voltage converted to resistance
unsigned long fsrConductance; 
long fsrForce;       // Finally, the resistance converted to force
 
void setup(void) {
  Serial.begin(9600);   // We'll send debugging information via the Serial monitor
}
 
void loop(void) {
  fsrReading = analogRead(fsrPin);  
  Serial.print("Analog reading = ");
  Serial.println(fsrReading);
 
  // analog voltage reading ranges from about 0 to 1023 which maps to 0V to 5V (= 5000mV)
  fsrVoltage = map(fsrReading, 0, 1023, 0, 5000);
  Serial.print("Voltage reading in mV = ");
  Serial.println(fsrVoltage);  
 
  if (fsrVoltage == 0) {
    Serial.println("No pressure");  
  } else {
    // The voltage = Vcc * R / (R + FSR) where R = 10K and Vcc = 5V
    // so FSR = ((Vcc - V) * R) / V        yay math!
    fsrResistance = 5000 - fsrVoltage;     // fsrVoltage is in millivolts so 5V = 5000mV
    fsrResistance *= 10000;                // 10K resistor
    fsrResistance /= fsrVoltage;
    Serial.print("FSR resistance in ohms = ");
    Serial.println(fsrResistance);
 
    fsrConductance = 1000000;           // we measure in micromhos so 
    fsrConductance /= fsrResistance;
    Serial.print("Conductance in microMhos: ");
    Serial.println(fsrConductance);
 
    // Use the two FSR guide graphs to approximate the force
    if (fsrConductance <= 1000) {
      fsrForce = fsrConductance / 80;
      Serial.print("Force in Newtons: ");
      Serial.println(fsrForce);      
    } else {
      fsrForce = fsrConductance - 1000;
      fsrForce /= 30;
      Serial.print("Force in Newtons: ");
      Serial.println(fsrForce);            
    }
  }
  Serial.println("--------------------");
  delay(1000);
}

Here’s what the output looks like in the serial monitor.

fsr analog output2

A flex sensor, also known as a bend sensor, is a low-cost, simple-to-use sensor used to measure the amount of deflection or bending.

It gained popularity in the 1990s due to its inclusion in the Nintendo Power Glove. People have been using it ever since as a goniometer to measure joint movement, a door sensor, a bumper switch to detect walls, and a pressure sensor on robotic grippers.

Flex Sensor Overview

A flex sensor is basically a variable resistor, whose resistance varies when bent. Because the resistance is directly proportional to the amount of bending, it is often referred to as a Flexible Potentiometer.

Flex sensors are typically available in two sizes: 2.2′′ (5.588cm) long and 4.5′′ (11.43cm) long.

types of flex sensors

Construction

A conductive ink based flex sensor is made of a phenolic resin substrate onto which conductive ink is applied. A segmented conductor is then placed on top to create a flexible potentiometer.

flex sensor construction

Directions to Use

The flex sensor is only designed to be flexed in one direction, away from the ink, as shown in the image below. If you bend the sensor in the opposite direction, you will not receive accurate data and you may even damage it.

directions to use flex sensor

Also, avoid bending the sensor too close to the base (where the pins are crimped), as this can cause it to kink and fail.

How Do Flex Sensors Work?

The conductive ink on the sensor serves as a resistor. When the sensor is straight, this resistance is around 25k.

flex sensor working

When the sensor is bent, the conductive layer is stretched, resulting in a reduced cross section (imagine stretching a rubber band) and increased resistance. At a 90° angle, this resistance is approximately 100K.

When the sensor is straightened out again, the resistance returns to its original value. By measuring the resistance, you can determine how much the sensor is bent.

Reading a Flex Sensor

The simplest way to read the flex sensor is to combine it with a static resistor to form a voltage divider, which produces a variable voltage that can be read by the analog-to-digital converter of a microcontroller.

flex voltage divider

It is important to note that the output voltage you measure is the voltage drop across the pull-down resistor, not the voltage drop across the flex sensor.

We can use this equation to calculate the output voltage (Vo).

flex1

In this configuration, the output voltage decreases as the bend radius increases.

For example, with a 5V supply and a 47K pull-down resistor, when the sensor is flat (0°), the resistance is relatively low (around 25k). This produces the following output voltage:

flex2

When bent to its full extent (90°), the resistance increases to approximately 100K. As a result, the output voltage becomes:

flex3

Wiring a Flex Sensor to an Arduino

Connecting a flex sensor to an Arduino is very simple.

You need to connect a 47kΩ pull-down resistor in series with the flex sensor to create a voltage divider circuit. The A0 ADC input of an Arduino is then wired to the junction of the pull-down resistor and the flex sensor.

wiring flex sensor to arduino

Keep in mind that flex sensors are really just resistors, which means you can connect them either way and they will still work.

Arduino Example Code

Here’s a simple sketch that reads sensor data from the Arduino’s ADC pin and displays it on the serial monitor. For most projects, this is pretty much all that is needed.

const int flexPin = A0;			// Pin connected to voltage divider output

// Change these constants according to your project's design
const float VCC = 5;			// voltage at Ardunio 5V line
const float R_DIV = 47000.0;	// resistor used to create a voltage divider
const float flatResistance = 25000.0;	// resistance when flat
const float bendResistance = 100000.0;	// resistance at 90 deg

void setup() {
	Serial.begin(9600);
	pinMode(flexPin, INPUT);
}

void loop() {
	// Read the ADC, and calculate voltage and resistance from it
	int ADCflex = analogRead(flexPin);
	float Vflex = ADCflex * VCC / 1023.0;
	float Rflex = R_DIV * (VCC / Vflex - 1.0);
	Serial.println("Resistance: " + String(Rflex) + " ohms");

	// Use the calculated resistance to estimate the sensor's bend angle:
	float angle = map(Rflex, flatResistance, bendResistance, 0, 90.0);
	Serial.println("Bend: " + String(angle) + " degrees");
	Serial.println();

	delay(500);
}

If everything is fine, you should see the following output on the serial monitor.

flex sensor analog output

Code Explanation:

The sketch begins with the declaration of the Arduino pin to which the flex sensor is connected.

const int flexPin = A0;	

Then, a few constants are defined, including the system voltage (VCC), the resistor used to make a voltage divider (R_DIV), and the resistance offered by the flex sensor in its flat and bent configurations (flatResistance and bendResistance, respectively). Make sure these constants are set correctly.

const float VCC = 5;
const float R_DIV = 47000.0;
const float flatResistance = 25000.0;
const float bendResistance = 100000.0;

In the setup, we establish serial communication and configure the flexPin as an INPUT.

void setup() {
	Serial.begin(9600);
	pinMode(flexPin, INPUT);
}

In the loop, we start by reading the ADC.

int ADCflex = analogRead(flexPin);

When the Arduino converts the analog output voltage of the sensor to a digital value, it converts it to a 10-bit number between 0 and 1023. Therefore, to calculate the actual output voltage, we use the following formula:

float Vflex = ADCflex * VCC / 1023.0;

The resistance of the flex sensor is then calculated using the formula derived from the voltage divider formula and displayed on the serial monitor.

float Rflex = R_DIV * (VCC / Vflex - 1.0);
Serial.println("Resistance: " + String(Rflex) + " ohms");

We then use the calculated resistance to estimate the bend angle of the sensor. For this, we use the IDE’s built-in map() function.

The map() function maps and converts the sensor’s resistance to its bend angle. When we call map(Rflex, flatResistance, bendResistance, 0, 90.0), the value of flatResistance is mapped to 0°, the value of bendResistance is mapped to 90°, and the values in-between are mapped to values in-between.

float angle = map(Rflex, flatResistance, bendResistance, 0, 90.0);
Serial.println("Bend: " + String(angle) + " degrees");
Serial.println();

The Pulse Sensor is a well-designed low-power plug-and-play heart-rate sensor for the Arduino. Anyone who wants to incorporate real-time heart-rate data into their work—students, artists, athletes, makers, and game and mobile developers—can benefit from it.

The best part is that this sensor plugs right into Arduino and easily clips onto a fingertip or earlobe. It is also super small (button-shaped) and has holes for sewing into fabric.

Did you know?

Pulse Sensor is an open source device developed by PulseSensor.com. They began in 2011 as a Kickstarter project. As of 2013, 491 backers had pledged $18,418 to help bring this project to life.

Hardware Overview

The front of the sensor, with the heart logo, is where you put your finger. You’ll also notice a tiny circular opening through which the Kingbright’s reverse mounted green LED shines.

pulse sensor front side hardware overview

Just beneath the circular opening is a small ambient light photo sensor – APDS-9008 from Avago. This sensor is similar to the ones used in cell phones, tablets, and laptops to adjust the screen’s brightness based on the ambient lighting conditions.

On the back of the module are an MCP6001 Op-Amp from Microchip and a few resistors and capacitors that make up the R/C filter network. Additionally, there is a reverse protection diode to prevent damage in the event that the power leads are accidentally reversed.

pulse sensor back side hardware overview

The module requires a DC power supply ranging from 3.3 to 5V and draws less than 4mA of current.

Technical Specifications

Here are the technical specifications:

Maximum RatingsVCC3.0 – 5.5V
IMax (Maximum Current Draw)< 4mA
VOut (Output Voltage Range)0.3V to Vcc
WavelengthLED Output565nm
Sensor Input525nm
DimensionsL x W (PCB)15.8mm (0.625″)
Lead Length20cm (7.8″)

How Does a Pulse Sensor Work?

The theory behind optical heart-rate sensors is very simple. If you’ve ever shined a flashlight through your fingers and observed your heartbeat pulsing, the concept of optical heart-rate pulse sensors can be easily grasped.

light sensor led pulse detection photoplethysmogram

A pulse sensor, like any other optical heart-rate sensor, works by shining a green light (~ 550nm) on the finger and measuring the amount of reflected light with a photosensor.

This optical pulse detection technique is known as a Photoplethysmogram.

pulse detection heart rate sensor working photoplethysmogram

The oxygenated hemoglobin in arterial blood has the property of absorbing green light. The redder the blood (the higher the hemoglobin), the greater the absorption of green light. With each heartbeat, blood is pumped through the finger, causing a change in the amount of reflected light, which in turn produces a waveform at the photosensor’s output.

As you keep shining light and taking photosensor readings, you quickly begin to obtain a heart-beat pulse reading.

This signal from the photosensor is typically small and noisy; therefore, it is passed through an R/C filter network and then amplified with an Op-Amp to create a signal that is significantly larger, cleaner, and easier to detect.

Pulse Sensor Pinout

The sensor comes with a 24″ flat ribbon cable with three male header connectors. The pinout is shown in the figure below.

pulse sensor pinout

S (Signal) is the signal output. Connects to analog input of an Arduino.

+ (VCC) is the VCC pin. Connects to 3.3 or 5V.

– (GND) is the Ground pin.

Warning:

Many times, the cable is not color coded, so check the markings on the back of the module to ensure that you have the correct identification of the three wires.

Wiring a Pulse Sensor to an Arduino

Connecting the Pulse Sensor to an Arduino is a breeze. You only need to connect three wires: two for power and one for reading the sensor value.

The module can be supplied with either 3.3V or 5V. Positive voltage is connected to ‘+,’ while ground is connected to ‘-.’ The third ‘S’ wire is the analog signal output from the sensor, which will be connected to the Arduino’s A0 analog input.

The following is the wiring diagram for the Pulse Sensor experiments:

wiring connecting pulse sensor with arduino

Library Installation

To run the following sketches, you must first install the ‘PulseSensor Playground’ library.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the libraries index and update the list of installed libraries.

manage libraries

Filter your search by entering ‘pulsesensor’.There should only be a single entry. Click on that and then choose Install.

pulse sensor library installation

PulseSensor Example Sketches

The PulseSensor library includes several example sketches. We’ll go over a few of them here, but you can also experiment with the others.

To access the example sketches, navigate to File > Examples > PulseSensor Playground.

You will see a selection of example sketches. You can choose any of them to load the sketch into your IDE. Let’s start off with the GettingStartedProject.

pulse sensor example sketches

Load the GettingStartedProject sketch from the example sketches into your Arduino IDE. This is a basic Arduino sketch. Upload the code to your Arduino and clip the sensor to your earlobe or fingertip. You should see the Arduino’s onboard LED blink in time with your heartbeat!

int const PULSE_SENSOR_PIN = 0;   // 'S' Signal pin connected to A0

int Signal;                // Store incoming ADC data. Value can range from 0-1024
int Threshold = 550;       // Determine which Signal to "count as a beat" and which to ignore.

void setup() {
	pinMode(LED_BUILTIN,OUTPUT);  // Built-in LED will blink to your heartbeat
	Serial.begin(9600);           // Set comm speed for serial plotter window
}

void loop() {

	Signal = analogRead(PULSE_SENSOR_PIN); // Read the sensor value

	Serial.println(Signal);                // Send the signal value to serial plotter

	if(Signal > Threshold){                // If the signal is above threshold, turn on the LED
		digitalWrite(LED_BUILTIN,HIGH);
	} else {
		digitalWrite(LED_BUILTIN,LOW);     // Else turn off the LED
	}
	delay(10);
}

Code Explanation

The sketch is extremely simple. It starts with defining the pin used to connect the Pulse Sensor. Two variables are also defined; the Signal variable holds the incoming ADC data and the Threshold variable determines which signal to “count as a beat” and which signal to disregard.

int const PULSE_SENSOR_PIN = 0;

int Signal;
int Threshold = 550;

In the setup, we configure the onboard LED pin (pin 13) to act as an output and set up the serial monitor.

void setup() {
	pinMode(LED_BUILTIN,OUTPUT);
	Serial.begin(9600);
}

In the loop, we read the analog signal from the Pulse Sensor and activate the onboard LED when the signal exceeds a threshold value.

void loop() {
	Signal = analogRead(PULSE_SENSOR_PIN); // Read the sensor value

	if(Signal > Threshold){                // If the signal is above threshold, turn on the LED
		digitalWrite(LED_BUILTIN,HIGH);
	} else {
		digitalWrite(LED_BUILTIN,LOW);     // Else turn off the LED
	}
	delay(10);
}

Trouble Seeing a Heartbeat?

Here’s what to do if you’re having trouble seeing a heartbeat.

  1. If you hold the sensor too tightly, you will squeeze all the blood from your fingers and there will be no sign! If you hold it too lightly, you will invite noise from movement and ambient light. Sweat Spot pressure (neither too hard nor too soft) on the pulse sensor will produce a good, clean signal.
  2. Variations in pressure can affect the blood flow in your finger, resulting in inaccurate sensor readings. Try to maintain constant pressure by securing the sensor to your finger with a rubber band or other tightening device.
  3. Test the sensor on different parts of your body that have capillary tissue (such as earlobe or lower lip).
  4. Try adjusting the threshold value. You’re free to pick a value for the threshold anywhere from 0 to 1023, but it’s recommended that you tweak it in increments of 5 or 10. Lowering the threshold makes the sensor more sensitive, and vice versa. Try experimenting with different threshold values until you find one that works better.

Arduino Example 2 – Heart Beat Plotting

The previous GettingStartedProject sketch is designed to work with the Arduino Serial Plotter – a nice tool included with the Arduino IDE for visualizing analog signals in real-time.

While the sketch is running and your Arduino board is connected to your computer via USB, navigate to Plotter > Serial Plotter

The signal might take a while to stabilize, but once it does, you should see something similar.

pulse sensor heart rate plotting on arduino serial monitor

Feel your wrist pulse and watch it mimic the blips.

Arduino Example 3 – Measuring Heart-Rate (BPM)

In our third example, we will attempt to measure heart rate (Beats Per Minute or BPM). Load the Getting_BPM_to_Monitor example from the PulseSensor Playground examples menu into your Arduino IDE.

pulse sensor bpm heart ratemeasurement sketch

This sketch calculates the time between pulses to determine the heart rate and outputs the result to the Serial Monitor.

Warning:

Because this sketch detects heart rate optically, it may produce incorrect results. Please DO NOT USE IT FOR ACTUAL MEDICAL DIAGNOSIS.

#define USE_ARDUINO_INTERRUPTS true    // Set-up low-level interrupts for most acurate BPM math
#include <PulseSensorPlayground.h>     // Includes the PulseSensorPlayground Library

const int PulseWire = 0;       // 'S' Signal pin connected to A0
const int LED13 = 13;          // The on-board Arduino LED
int Threshold = 550;           // Determine which Signal to "count as a beat" and which to ignore
                               
PulseSensorPlayground pulseSensor;  // Creates an object

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

	// Configure the PulseSensor object, by assigning our variables to it
	pulseSensor.analogInput(PulseWire);   
	pulseSensor.blinkOnPulse(LED13);       // Blink on-board LED with heartbeat
	pulseSensor.setThreshold(Threshold);   

	// Double-check the "pulseSensor" object was created and began seeing a signal
	if (pulseSensor.begin()) {
		Serial.println("PulseSensor object created!");
	}
}

void loop() {
	int myBPM = pulseSensor.getBeatsPerMinute();      // Calculates BPM

	if (pulseSensor.sawStartOfBeat()) {               // Constantly test to see if a beat happened
		Serial.println("♥  A HeartBeat Happened ! "); // If true, print a message
		Serial.print("BPM: ");
		Serial.println(myBPM);                        // Print the BPM value
		}

	delay(20);
}

The readings won’t make sense right away after you upload the sketch, so try to keep your finger as steady as you can while you wait. You will see something like this.

pulse sensor heart rate bpm measurement sketch output

Processing Visualizer

The Pulse Sensor developers have created software to visualize the Pulse Sensor data on your computer. It is written in the Processing programming language. This software displays all of the data that the Arduino receives from the Pulse Sensor. It plots the user’s heart rate in real time. It also displays the BPM (Beats Per Minute) and plots IBI (Interbeat Interval) over time.

This Processing sketch does not perform any calculations! They are all done on the Arduino board, so to use the visualizer, you must have an Arduino running the PulseSensor_BPM sketch. This software simply reads the Serial Port and visualizes the data.

pulse sensor processing visualizer output

Upload Sketch

Look for PulseSensor_BPM in the File > Examples > PulseSensor Playground examples menu and load it into your Arduino IDE.

pulse sensor processing visualizer arduino sketch

Before running the PulseSensor_BPM sketch, you must change a variable called outputType in the Arduino code to allow your Arduino board to communicate with the visualizer. It is set to SERIAL_PLOTTER by default. It must be changed to PROCESSING_VISUALIZER.

changes in processing visualizer arduino sketch

Upload the Arduino Sketch now. Your board should be ready to send data in the format that the Processing Visualizer prefers!

Installation

Download the Processing code from github. Unzip the download and place the PulseSensorAmpd_Processing_Visualizer folder in your Documents/Processing folder.

Then, launch Processing to access the code through your Sketch folder. Click on File > Sketchbook…, then choose PulseSensorAmped_Processing_Visualizer

pulse sensor processing visualizer sketch

Setup

As soon as the Sketch starts, you will be prompted to select the USB port to which your Arduino board is connected. If you don’t see your Arduino, use the Refresh Serial Ports button to refresh the ports list.

pulse sensor processing visualizer port selection

You will begin to see heartbeat data once you choose the proper port!

Functions

There are a few things you can do while the sketch is running:

  1. Press the ‘s’ key to take a screenshot of the program window. The image will be saved as a .jpg in the sketch folder.
  2. Press the ‘r’ key to reset the data windows to zero.

The MAX30100 pulse oximeter and heart rate sensor is an I2C-based low-power plug-and-play biometric sensor. It can be used by students, hobbyists, engineers, manufacturers, and game & mobile developers who want to incorporate live heart-rate data into their projects.

MAX30100 Module Hardware Overview

The module features the MAX30100 – a modern, integrated pulse oximeter and heart rate sensor IC, from Analog Devices. It combines two LEDs, a photodetector, optimized optics, and low-noise analog signal processing to detect pulse oximetry (SpO2) and heart rate (HR) signals.

max30100 module hardware overview ic and led

On the right, the MAX30100 has two LEDs – a RED and an IR LED. And on the left is a very sensitive photodetector. The idea is that you shine a single LED at a time, detecting the amount of light shining back at the detector, and, based on the signature, you can measure blood oxygen level and heart rate.

Power Requirement

The MAX30100 chip requires two different supply voltages: 1.8V for the IC and 3.3V for the RED and IR LEDs. So the module comes with 3.3V and 1.8V regulators. This allows you to connect the module to any microcontroller with 5V, 3.3V, even 1.8V level I/O.

max30100 module hardware overview two regulators

One of the most important features of the MAX30100 is its low power consumption: the MAX30100 consumes less than 600μA during measurement. Also it is possible to put the MAX30100 in standby mode, where it consumes only 0.7μA. This low power consumption allows implementation in battery powered devices such as handsets, wearables or smart watches.

On-Chip Temperature Sensor

The MAX30100 has an on-chip temperature sensor that can be used to compensate for the changes in the environment and to calibrate the measurements.

This is a reasonably precise temperature sensor that measures the ‘die temperature’ in the range of -40˚C to +85˚C with an accuracy of ±1˚C.

I2C Interface

The module uses a simple two-wire I2C interface for communication with the microcontroller. It has a fixed I2C address: 0xAEHEX (for write operation) and 0xAFHEX (for read operation).

FIFO Buffer

The MAX30100 embeds a FIFO buffer for storing data samples. The FIFO has a 16-sample memory bank, which means it can hold up to 16 SpO2 and heart rate samples. The FIFO buffer can offload the microcontroller from reading each new data sample from the sensor, thereby saving system power.

Interrupts

The MAX30100 can be programmed to generate an interrupt, allowing the host microcontroller to perform other tasks while the data is collected by the sensor. The interrupt can be enabled for 5 different sources:

  • Power Ready : triggers on power-up or after a brownout condition.
  • SpO2 Data Ready : triggers after every SpO2 data sample is collected.
  • Heart Rate Data Ready : triggers after every heart rate data sample is collected.
  • Temperature Ready : triggers when an internal die temperature conversion is finished.
  • FIFO Almost Full : triggers when the FIFO becomes full and future data is about to lost.
max30100 module hardware overview interrupt pin

The INT line is an open-drain, so it is pulled HIGH by the onboard resistor. When an interrupt occurs the INT pin goes LOW and stays LOW until the interrupt is cleared.

Technical Specifications

Here are the technical specifications:

Power supply3.3V to 5.5V
Current draw~600μA (during measurements)
~0.7μA (during standby mode)
Red LED Wavelength660nm
IR LED Wavelength880nm
Temperature Range-40˚C to +85˚C
Temperature Accuracy±1˚C

You can find extensive information for the MAX30100 sensor from the datasheet.

MAX30100 vs. MAX30102

Analog Devices has stepped up their game with their new MAX30102 sensor. The MAX30102 beats the MAX30100 in these areas:

FIFO – The MAX30102 has a 32-sample memory bank, which means it can hold up to 32 SpO2 and heart rate samples. Whereas only 16 samples can be kept in MAX30100.

ADC Resolution – The MAX30102 is capable of reading up to 18-bits or values up to 262,144. Whereas the MAX30100 only has 16-bit ADC resolution. Because of that the MAX30102 can detect extremely small movements.

LED Pulse Width – The MAX30102 has a narrower LED pulse width than the MAX30100, resulting in lower power consumption.

To get started with the MAX30102 module, you can refer to the tutorial below

How MAX30100 Pulse Oximeter and Heart Rate Sensor Works?

The MAX30100, or any optical pulse oximeter and heart-rate sensor for that matter, consists of a pair of high-intensity LEDs (RED and IR, both of different wavelengths) and a photodetector. The wavelengths of these LEDs are 660nm and 880nm, respectively.

max30100 pulse detection photoplethysmogram

The MAX30100 works by shining both lights onto the finger or earlobe (or essentially anywhere where the skin isn’t too thick, so both lights can easily penetrate the tissue) and measuring the amount of reflected light using a photodetector. This method of pulse detection through light is called Photoplethysmogram.

The working of MAX30100 can be divided into two parts: Heart Rate Measurement and Pulse Oximetry (measuring the oxygen level of the blood).

Heart Rate Measurement

The oxygenated hemoglobin (HbO2) in the arterial blood has the characteristic of absorbing IR light. The redder the blood (the higher the hemoglobin), the more IR light is absorbed. As the blood is pumped through the finger with each heartbeat, the amount of reflected light changes, creating a changing waveform at the output of the photodetector. As you continue to shine light and take photodetector readings, you quickly start to get a heart-beat (HR) pulse reading.

pulse detection heart rate sensor working photoplethysmogram

Pulse Oximetry

Pulse oximetry is based on the principle that the amount of RED and IR light absorbed varies depending on the amount of oxygen in your blood. The following graph is the absorption-spectrum of oxygenated hemoglobin (HbO2) and deoxygenated hemoglobin (Hb).

absorption spectrum of hb and hbo2

As you can see from the graph, deoxygenated blood absorbs more RED light (660nm), while oxygenated blood absorbs more IR light (880nm). By measuring the ratio of IR and RED light received by the photodetector, the oxygen level (SpO2) in the blood is calculated.

Did you know?

The measurement of hemoglobin oxygen saturation (HbO2) by measuring the absorbance of red and IR light was introduced in 1935 by a German physician, Karl Matthes.

At first, there were no good photodetectors so instead of the IR band, the green band of the light spectrum was used. As technology advanced, more reliable methods were developed, and green light was replaced by IR light.

MAX30100 Module Pinout

The MAX30100 module brings out the following connections.

max30100 module pinout

VIN is the power pin. You can connect it to 3.3V or 5V output from your Arduino.

SCL is the I2C clock pin, connect to your Arduino’s I2C clock line.

SDA is the I2C data pin, connect to your Arduino’s I2C data line.

INT The MAX30100 can be programmed to generate an interrupt for each pulse. This line is open-drain, so it is pulled HIGH by the onboard resistor. When an interrupt occurs the INT pin goes LOW and stays LOW until the interrupt is cleared.

IRD The MAX30100 integrates an LED driver to drive LED pulses for SpO2 and HR measurements. Use this if you want to drive the IR LED yourself, otherwise leave it unconnected.

RD pin is similar to the IRD pin, but is used to drive the Red LED. If you don’t want to drive the red LED yourself, leave it unconnected.

GND is the ground.

MAX30100 Not Working – Problem and Solutions to Fix

There is no doubt that the MAX30100 module is cheap and very popular among hobbyists, but unfortunately the module (sold in the thousands) has a serious design problem. Well, let’s fix it.

The Problem

The MAX30100 chip requires two different supply voltages: 1.8V for the IC and 3.3V for the RED and IR LEDs. So the module comes with two linear voltage regulators – U1 and U2. The first generates 5V to 3.3V. The second regulator is connected to the output of the first and generates 1.8V.

Now take a closer look at the 4.7kΩ pull-up resistors (R1, R2 and R3) for the SCL, SDA and INT signal lines. They are connected to the 1.8V supply (highlighted with a thick red line)!

max30100 module circuit diagram

The two regulators U1, U2 and the trace that connects the 4.7kΩ pull-up resistors to the 1.8V supply are highlighted on the module as well.

max30100 module not working problem highlighted

If you connect such module to the Arduino’s 5V logic – it will not show up on the I2C bus because the logic level is too low (Arduino reports HIGH if the voltage exceeds 3.0V for 5V-boards and 2.0V for 3.3V-boards). Even with a 3.3V logic board, it will not work.

Don’t worry! We have two solutions to fix this problem.

Solution 1 (tested):

  1. Cut the trace in the place of the red line. This will disconnect all 4.7kΩ pull-up resistors from the 1.8V supply voltage.
  2. Now make a jumper as shown by the yellow line with a piece of wire or a solder blob. This will pull all the 4.7kΩ resistors up to 3.3V.

Here’s the board with and without the modifications:

max30100 module not working solution1 before
max30100 module not working solution1 after

After following these steps, connect the module to the Arduino UNO using the wiring diagram below:

max30100 arduino wiring after implementing solution1

The wiring is fairly easy. Simply connect the VIN pin to Arduino’s 3.3V and GND to ground. Connect the SCL pin to the I2C clock pin and the SDA pin to the I2C data pin on your Arduino. Finally connect the INT pin to digital pin 2.

Solution 2 (tested):

The second solution is to remove all 4.7kΩ pullup resistors from the board. We will use external pullup resistors on the SCL, SDA and INT signal lines later.

Here’s the board with and without the resistors removed:

max30100 module not working solution2 before
max30100 module not working solution2 after

After removing the resistors, connect the module to the Arduino UNO using the wiring diagram below:

max30100 arduino wiring after implementing solution2

The wiring is the same as the first one; you just need to add external 4.7kΩ pullup resistors on the SCL, SDA and INT signal lines.

Our Evaluation:

We have tested both solutions and they work great. So try one which is easier for you to implement.

Library Installation

There are several libraries available for the MAX30100 sensor. However in our example, we are using the one by OXullo Intersecans. This library exposes most of the features of the MAX30100 and offers simple and easy to use functions to calculate pulse rate and SpO2. You can download this library from within the Arduino IDE Library Manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing max30100. There should be a couple entries. Look for MAX30100lib Library by OXullo Intersecans. Click on the entry, and then select Install.

max30100 library installation

Example 1 – Measuring Heart-Rate (BPM) and Oxygen Saturation (SpO2)

This is where the fun really begins! In this example, we’ll measure heart rate (Beats Per Minute or BPM) and blood oxygen level (SpO2) of the person we’re monitoring.

Warning:

This sketch detects heart-rate and oxygen saturation optically. This method is tricky and prone to give false readings. So please DO NOT use it for actual medical diagnosis.

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"

#define REPORTING_PERIOD_MS     1000

// Create a PulseOximeter object
PulseOximeter pox;

// Time at which the last beat occurred
uint32_t tsLastReport = 0;

// Callback routine is executed when a pulse is detected
void onBeatDetected() {
    Serial.println("Beat!");
}

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

    Serial.print("Initializing pulse oximeter..");

    // Initialize sensor
    if (!pox.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }

	// Configure sensor to use 7.6mA for LED drive
	pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);

    // Register a callback routine
    pox.setOnBeatDetectedCallback(onBeatDetected);
}

void loop() {
    // Read from the sensor
    pox.update();

    // Grab the updated heart rate and SpO2 levels
    if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
        Serial.print("Heart rate:");
        Serial.print(pox.getHeartRate());
        Serial.print("bpm / SpO2:");
        Serial.print(pox.getSpO2());
        Serial.println("%");

        tsLastReport = millis();
    }
}

After uploading the sketch, keep your finger on the sensor as steady as possible and wait a few seconds for the readings to make sense. You will see a result like this.

max30100 heart rate and oxygen saturation output

Code Explanation:

Let’s start at the top of the example. Two libraries viz. MAX30100_PulseOximeter.h and Wire.h are included, a constant called REPORTING_PERIOD_MS is defined to delay the measurements, PulseOximeter object is created and a variable called tsLastReport is created to store the time at which the last beat occurred.

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"

#define REPORTING_PERIOD_MS     1000

PulseOximeter pox;

uint32_t tsLastReport = 0;

Next, a callback routine is defined which is executed when a pulse is detected. You can write some interesting code in it, for example, to watch the built-in LED blink with your heartbeat!

void onBeatDetected() {
    Serial.println("Beat!");
}

Next let’s look at the setup. There are three functions to point out. First, the pox.begin() function call makes sure that we can communicate with the sensor. begin() function returns 1 on success (or 0 if it fails). It also configures the MAX30100 to begin collecting data.

if (!pox.begin()) {
	Serial.println("FAILED");
	for(;;);
} else {
	Serial.println("SUCCESS");
}

Secondly and equally as important the setIRLedCurrent() function sets the current through the IR LED. The following line sets the current of 7.6mA through the IR LED.

pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);

By default the library sets the IR LED current to 50mA which can sometimes cause problems like the LED not turning on or getting the Initializing pulse oximeter.. FAILED message on the serial monitor. In that case, set the LED current to a lower value. Here are the other possible values you can use on the setIRLedCurrent() function.

  • MAX30100_LED_CURR_0MA
  • MAX30100_LED_CURR_4_4MA
  • MAX30100_LED_CURR_7_6MA
  • MAX30100_LED_CURR_11MA
  • MAX30100_LED_CURR_14_2MA
  • MAX30100_LED_CURR_17_4MA
  • MAX30100_LED_CURR_20_8MA
  • MAX30100_LED_CURR_24MA
  • MAX30100_LED_CURR_27_1MA
  • MAX30100_LED_CURR_30_6MA
  • MAX30100_LED_CURR_33_8MA
  • MAX30100_LED_CURR_37MA
  • MAX30100_LED_CURR_40_2MA
  • MAX30100_LED_CURR_43_6MA
  • MAX30100_LED_CURR_46_8MA
  • MAX30100_LED_CURR_50MA

Remember! The higher the current, the brighter the LED and the deeper it reaches your skin. So, you can play with it while you figure it out.

Finally setOnBeatDetectedCallback() registers the callback function.

pox.setOnBeatDetectedCallback(onBeatDetected);

In the main loop, the biometric data is collected from the MAX30100 sensor with the update() function. It actually pulls data in from sensor FIFO.

pox.update();

We then print the biometric data on the serial monitor once every second (REPORTING_PERIOD_MS is set to 1000; change it as per your requirement). For this we use millis() instead of delay(). Since delay() pauses the entire code, we cannot get the updated measurement.

if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
	Serial.print("Heart rate:");
	Serial.print(pox.getHeartRate());
	Serial.print("bpm / SpO2:");
	Serial.print(pox.getSpO2());
	Serial.println("%");

	tsLastReport = millis();
}

Trouble Seeing a Heartbeat?

If you’re having trouble seeing a heartbeat, here’s what to do.

  1. If you hold the sensor too hard, you will squeeze all the blood from your fingers and there will be no sign! If you hold it too lightly, you will invite noise from movement and ambient light. Sweatspot pressure (Not too hard, not too soft) on the pulse sensor will give a good clean signal.
  2. A varying pressure can cause blood to flow differently in your finger, causing the sensor readings to go wonky. Try to apply constant pressure by attaching the sensor to your finger using a rubber band or other tightening device.
  3. Try the sensor on different parts of your body that have capillary tissue (such as earlobe or lower lip).

Example 2 – Reading Red & IR

The second example outputs the raw values (IR and Red readings) read by the sensor. Load it onto your Arduino and open the Serial Terminal to see the printed values.

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"

// Create a MAX30100 object
MAX30100 sensor;

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

    Serial.print("Initializing MAX30100..");

    // Initialize sensor
    if (!sensor.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }

    // Configure sensor
    configureMax30100();
}

void loop() {
    uint16_t ir, red;

    sensor.update();

    while (sensor.getRawValues(&ir, &red)) {
		Serial.print("R[");
		Serial.print(red);
		Serial.print("] IR[");
		Serial.print(ir);
		Serial.println("]");
    }
}

void configureMax30100() {
  sensor.setMode(MAX30100_MODE_SPO2_HR);
  sensor.setLedsCurrent(MAX30100_LED_CURR_50MA, MAX30100_LED_CURR_27_1MA);
  sensor.setLedsPulseWidth(MAX30100_SPC_PW_1600US_16BITS);
  sensor.setSamplingRate(MAX30100_SAMPRATE_100HZ);
  sensor.setHighresModeEnabled(true);
}

With the sensor pointing up, swipe your hand over the sensor. You should see a change in values as your hand reflects different amounts of light.

max30100 raw red and ir values output

Serial data can be hard to visualize if you’re only looking at values. If you are using the Arduino IDE v1.6.6+, there is an option to view the data on a graph using the Arduino Serial Plotter.

To start, replace the while() in the code above with this code snippet:

while (sensor.getRawValues(&ir, &red)) {
	Serial.print(red);
	Serial.print(", ");
	Serial.println(ir);
}

In the Arduino IDE, choose Tools > Serial Plotter. You should see a wave similar to the image below, when you swipe your hand over the sensor.

max30100 output on serial plotter

Example 3 – Presence Sensing

Our next experiment shows how to use the MAX30100 sensor as a general purpose proximity or a reflectance sensor and can serve as the basis for more practical experiments and projects.

This example works by taking a handful of readings during setup and averaging them together. It then uses this average as a baseline. If the sensor detects a significant change from the average, “Something is there!” is printed. Go ahead and try the sketch out.

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"

#define REPORTING_PERIOD_MS     1000

int lastOccurrence = LOW;

// Create a MAX30100 object
MAX30100 sensor;

uint16_t ir, red;
uint16_t avg_ir = 0, avg_red = 0;

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

    Serial.print("Initializing MAX30100..");

    // Initialize sensor
    if (!sensor.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }

    configureMax30100();

    takeSampleReadings();
}

void loop() {
  sensor.update();
  
  while (sensor.getRawValues(&ir, &red)) {
    if (ir > 10*avg_ir && red > 10*avg_red) {
      if (lastOccurrence == LOW) {
        Serial.println("Something is there!");
        lastOccurrence = HIGH;
      }
    }
    else{
      lastOccurrence = LOW;
    }
  }
}

void configureMax30100() {
  sensor.setMode(MAX30100_MODE_SPO2_HR);
  sensor.setLedsCurrent(MAX30100_LED_CURR_50MA, MAX30100_LED_CURR_27_1MA);
  sensor.setLedsPulseWidth(MAX30100_SPC_PW_1600US_16BITS);
  sensor.setSamplingRate(MAX30100_SAMPRATE_100HZ);
  sensor.setHighresModeEnabled(true);
}

void takeSampleReadings() {
  delay(50);
  for (int i = 0; i <= 9; i++) {
    sensor.update();
    sensor.getRawValues(&ir, &red);
    avg_ir += ir;
    avg_red += red;
    delay(50);
  }
  avg_ir /= 10;
  avg_red /= 10;
}

Swipe your hand over the sensor again and look for the message “Something is there!” printed on the serial terminal. Try testing the range at which the sensor can detect something.

max30100 presence sensing output

Note that the MAX30100 is capable of reading up to 16-bits or values up to 65,536. An extremely small movement can be detected!

Example 4 – Reading Temperature

Our last example outputs readings from the on-board temperature sensor in both Celsius and Fahrenheit. Although the temperature reading should be used to calibrate HR and SpO2 measurements, it can be useful if you need a sensitive and fast-responding temp sensor.

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"

#define REPORTING_PERIOD_MS     1000

// Create a MAX30100 object
MAX30100 sensor;

// Time when the last reading was taken
uint32_t tsLastReading = 0;

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

    Serial.print("Initializing MAX30100..");

    // Initialize sensor
    if (!sensor.begin()) {
        Serial.println("FAILED");
        for(;;);
    } else {
        Serial.println("SUCCESS");
    }

    // Configure sensor
    configureMax30100();
}

void loop() {
  sensor.update();

  if (millis() - tsLastReading > REPORTING_PERIOD_MS) {
    sensor.startTemperatureSampling();
    if (sensor.isTemperatureReady()) {
      float temp = sensor.retrieveTemperature();
      Serial.print("Temperature = ");
      Serial.print(temp);
      Serial.print("*C | ");
      Serial.print((temp * 9.0) / 5.0 + 32.0);//print the temperature in Fahrenheit
      Serial.println("*F");
    }
    tsLastReading = millis();
  }
}

void configureMax30100() {
  sensor.setMode(MAX30100_MODE_SPO2_HR);
  sensor.setLedsCurrent(MAX30100_LED_CURR_50MA, MAX30100_LED_CURR_27_1MA);
  sensor.setLedsPulseWidth(MAX30100_SPC_PW_1600US_16BITS);
  sensor.setSamplingRate(MAX30100_SAMPRATE_100HZ);
  sensor.setHighresModeEnabled(true);
}

Now try heating the sensor with your finger or blowing a light breath on the sensor. You should see something like the output below.

max30100 internal temperature sensor output

The MAX30102 pulse oximeter and heart rate sensor is an I2C-based low-power plug-and-play biometric sensor. It can be used by students, hobbyists, engineers, manufacturers, and game & mobile developers who want to incorporate live heart-rate data into their projects.

MAX30102 Module Hardware Overview

The module features the MAX30102 – a modern (the successor to the MAX30100), integrated pulse oximeter and heart rate sensor IC, from Analog Devices. It combines two LEDs, a photodetector, optimized optics, and low-noise analog signal processing to detect pulse oximetry (SpO2) and heart rate (HR) signals.

max30102 module hardware overview ic and leds

Behind the window on one side, the MAX30102 has two LEDs – a RED and an IR LED. On the other side is a very sensitive photodetector. The idea is that you shine a single LED at a time, detecting the amount of light shining back at the detector, and, based on the signature, you can measure blood oxygen level and heart rate.

Power Requirement

The MAX30102 chip requires two different supply voltages: 1.8V for the IC and 3.3V for the RED and IR LEDs. So the module comes with 3.3V and 1.8V regulators.

max30102 module hardware overview regulators

On the back of the PCB you’ll find a solder jumper that can be used to select between 3.3V and 1.8V logic level. By default 3.3V logic level is selected which is compatible with logic levels for Arduino. But you can also select 1.8V logic level as per your requirement. This allows you to connect the module to any microcontroller with 5V, 3.3V, even 1.8V level I/O.

max30102 module hardware overview solder jumper

One of the most important features of the MAX30102 is its low power consumption: the MAX30102 consumes less than 600μA during measurement. Also it is possible to put the MAX30102 in standby mode, where it consumes only 0.7μA. This low power consumption allows implementation in battery powered devices such as handsets, wearables or smart watches.

On-Chip Temperature Sensor

The MAX30102 has an on-chip temperature sensor that can be used to compensate for the changes in the environment and to calibrate the measurements.

This is a reasonably precise temperature sensor that measures the ‘die temperature’ in the range of -40˚C to +85˚C with an accuracy of ±1˚C.

I2C Interface

The module uses a simple two-wire I2C interface for communication with the microcontroller. It has a fixed I2C address: 0xAEHEX (for write operation) and 0xAFHEX (for read operation).

FIFO Buffer

The MAX30102 embeds a FIFO buffer for storing data samples. The FIFO has a 32-sample memory bank, which means it can hold up to 32 SpO2 and heart rate samples. The FIFO buffer can offload the microcontroller from reading each new data sample from the sensor, thereby saving system power.

Interrupts

The MAX30102 can be programmed to generate an interrupt, allowing the host microcontroller to perform other tasks while the data is collected by the sensor. The interrupt can be enabled for 5 different sources:

  • Power Ready: triggers on power-up or after a brownout condition.
  • New Data Ready: triggers after every SpO2 and HR data sample is collected.
  • Ambient Light Cancellation: triggers when the ambient light cancellation function of the SpO2/HR photodiode reaches its maximum limit, affecting the output of the ADC.
  • Temperature Ready: triggers when an internal die temperature conversion is finished.
  • FIFO Almost Full: triggers when the FIFO becomes full and future data is about to be lost.
max30102 module hardware overview interrupt pin

The INT line is an open-drain, so it is pulled HIGH by the onboard resistor. When an interrupt occurs the INT pin goes LOW and stays LOW until the interrupt is cleared.

Technical Specifications

Here are the technical specifications:

Power supply3.3V to 5.5V
Current draw~600μA (during measurements)
~0.7μA (during standby mode)
Red LED Wavelength660nm
IR LED Wavelength880nm
Temperature Range-40˚C to +85˚C
Temperature Accuracy±1˚C

You can find extensive information for the MAX30102 sensor from the datasheet.

How MAX30102 Pulse Oximeter and Heart Rate Sensor Works?

The MAX30102, or any optical pulse oximeter and heart-rate sensor for that matter, consists of a pair of high-intensity LEDs (RED and IR, both of different wavelengths) and a photodetector. The wavelengths of these LEDs are 660nm and 880nm, respectively.

max30102 pulse detection photoplethysmogram

The MAX30102 works by shining both lights onto the finger or earlobe (or essentially anywhere where the skin isn’t too thick, so both lights can easily penetrate the tissue) and measuring the amount of reflected light using a photodetector. This method of pulse detection through light is called Photoplethysmogram.

The working of MAX30102 can be divided into two parts: Heart Rate Measurement and Pulse Oximetry (measuring the oxygen level of the blood).

Heart Rate Measurement

The oxygenated hemoglobin (HbO2) in the arterial blood has the characteristic of absorbing IR light. The redder the blood (the higher the hemoglobin), the more IR light is absorbed. As the blood is pumped through the finger with each heartbeat, the amount of reflected light changes, creating a changing waveform at the output of the photodetector. As you continue to shine light and take photodetector readings, you quickly start to get a heart-beat (HR) pulse reading.

pulse detection heart rate sensor working photoplethysmogram

Pulse Oximetry

Pulse oximetry is based on the principle that the amount of RED and IR light absorbed varies depending on the amount of oxygen in your blood. The following graph is the absorption-spectrum of oxygenated hemoglobin (HbO2) and deoxygenated hemoglobin (Hb).

absorption spectrum of hb and hbo2

As you can see from the graph, deoxygenated blood absorbs more RED light (660nm), while oxygenated blood absorbs more IR light (880nm). By measuring the ratio of IR and RED light received by the photodetector, the oxygen level (SpO2) in the blood is calculated.

Did you know?

The measurement of hemoglobin oxygen saturation (HbO2) by measuring the absorbance of red and IR light was introduced in 1935 by a German physician, Karl Matthes.

At first, there were no good photodetectors so instead of the IR band, the green band of the light spectrum was used. As technology advanced, more reliable methods were developed, and green light was replaced by IR light.

MAX30102 Module Pinout

The MAX30102 module brings out the following connections.

max30102 module pinout

VIN is the power pin. You can connect it to 3.3V or 5V output from your Arduino.

SCL is the I2C clock pin, connect to your Arduino’s I2C clock line.

SDA is the I2C data pin, connect to your Arduino’s I2C data line.

INT The MAX30102 can be programmed to generate an interrupt for each pulse. This line is open-drain, so it is pulled HIGH by the onboard resistor. When an interrupt occurs the INT pin goes LOW and stays LOW until the interrupt is cleared.

IRD The MAX30102 integrates an LED driver to drive LED pulses for SpO2 and HR measurements. Use this if you want to drive the IR LED yourself, otherwise leave it unconnected.

RD pin is similar to the IRD pin, but is used to drive the Red LED. If you don’t want to drive the red LED yourself, leave it unconnected.

GND is the ground.

Wiring up a MAX30102 Module to an Arduino

Now that we know everything about the module, we can begin hooking it up to our Arduino!

Start by connecting the VCC pin to the power supply, 3V-5V is fine. Use the same voltage that your microcontroller logic is based off of. For most Arduinos, that is 5V. For 3.3V logic devices, use 3.3V. Now connect GND to common ground.

Connect the SCL pin to the I2C clock pin and the SDA pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

The following illustration shows the wiring.

arduino wiring for max30102 pulse oximeter heart rate module

Library Installation

There are several libraries available for the MAX30102 sensor. However in our example, we are using the one by SparkFun Electronics. This library exposes most of the features of the MAX30102 and offers simple and easy to use functions to calculate pulse rate and SpO2. You can download this library from within the Arduino IDE Library Manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing MAX3010x. Look for SparkFun MAX3010x Pulse and Proximity Sensor Library. Click on that entry, and then select Install.

max30102 library installation

MAX30102 Example Sketches

The SparkFun_MAX3010x library has a number of example sketches. You can use these example sketches as a basis for developing your own code.

To access the example sketches, navigate to the File > Examples > SparkFun MAX3010x Pulse and Proximity Sensor Library. You will see a selection of example sketches.

max30102 library examples

Example 1 – Reading Red & IR

The first example outputs the raw values (IR and Red readings) read by the sensor. Load it onto your Arduino and open the Serial Terminal to see the printed values.

#include <Wire.h>
#include "MAX30105.h"

MAX30105 particleSensor;

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

	// Initialize sensor
	if (particleSensor.begin() == false) {
		Serial.println("MAX30102 was not found. Please check wiring/power.");
		while (1);
	}

	particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive
}

void loop() {
	Serial.print(" R[");
	Serial.print(particleSensor.getRed());
	Serial.print("] IR[");
	Serial.print(particleSensor.getIR());
	Serial.println("]");
}

With the sensor pointing up, swipe your hand over the sensor. You should see a change in values as your hand reflects different amounts of light.

max30102 raw red and ir values output

Serial data can be hard to visualize if you’re only looking at values. If you are using the Arduino IDE v1.6.6+, there is an option to view the data on a graph using the Arduino Serial Plotter.

To start, replace the loop() in the code above with this code snippet:

void loop() {
	Serial.print(particleSensor.getRed());
	Serial.print(", ");
	Serial.println(particleSensor.getIR());
}

In the Arduino IDE, choose Tools > Serial Plotter. You should see a wave similar to the image below, when you swipe your hand over the sensor.

max30102 output on serial plotter

Example 2 – Presence Sensing

Our next experiment shows how to use the MAX30102 sensor as a general purpose proximity or a reflectance sensor and can serve as the basis for more practical experiments and projects.

This example works by taking a handful of readings during setup and averaging them together. It then uses this average as a baseline. If the sensor detects a significant change from the average, “Something is there!” is printed. Go ahead and try the sketch out.

#include <Wire.h>
#include "MAX30105.h"

MAX30105 particleSensor;

long samplesTaken = 0; //Counter for calculating the Hz or read rate
long unblockedValue; //Average IR at power up
long startTime; //Used to calculate measurement rate

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

  // Initialize sensor
  if (particleSensor.begin(Wire, I2C_SPEED_FAST) == false) { //Use default I2C port, 400kHz speed
    Serial.println("MAX30102 was not found. Please check wiring/power. ");
    while (1);
  }

  //Setup to sense up to 18 inches, max LED brightness
  byte ledBrightness = 0xFF; //Options: 0=Off to 255=50mA
  byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
  byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
  int sampleRate = 400; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
  int pulseWidth = 411; //Options: 69, 118, 215, 411
  int adcRange = 2048; //Options: 2048, 4096, 8192, 16384
  
  //Configure sensor with these settings
  particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange);

  particleSensor.setPulseAmplitudeRed(0); //Turn off Red LED
  particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED

  //Take an average of IR readings at power up
  unblockedValue = 0;
  for (byte x = 0 ; x < 32 ; x++) {
    unblockedValue += particleSensor.getIR(); //Read the IR value
  }
  unblockedValue /= 32;

  startTime = millis();
}

void loop() {
  samplesTaken++;

  Serial.print("IR[");
  Serial.print(particleSensor.getIR());
  Serial.print("] Hz[");
  Serial.print((float)samplesTaken / ((millis() - startTime) / 1000.0), 2);
  Serial.print("]");

  long currentDelta = particleSensor.getIR() - unblockedValue;

  Serial.print(" delta[");
  Serial.print(currentDelta);
  Serial.print("]");

  if (currentDelta > (long)100) {
    Serial.print(" Something is there!");
  }

  Serial.println();
}

Swipe your hand over the sensor again and look for the message “Something is there!” printed on the serial terminal. Try testing the range at which the sensor can detect something.

max30102 presence sensing output

Note that the MAX30102 is capable of reading up to 18-bits or values up to 262,144. An extremely small movement can be detected!

Example 3 – Reading Temperature

Our next example outputs readings from the on-board temperature sensor in both Celsius and Fahrenheit. Although the temperature reading should be used to calibrate HR and SpO2 measurements, it can be useful if you need a sensitive and fast-responding temp sensor.

#include <Wire.h>

#include "MAX30105.h"
MAX30105 particleSensor;

void setup() {
  Serial.begin(9600);
  Serial.println("Initializing...");

  // Initialize sensor
  if (particleSensor.begin(Wire, I2C_SPEED_FAST) == false) { //Use default I2C port, 400kHz speed
    Serial.println("MAX30102 was not found. Please check wiring/power. ");
    while (1);
  }

  //The LEDs are very low power and won't affect the temp reading much but
  //you may want to turn off the LEDs to avoid any local heating
  particleSensor.setup(0); //Configure sensor. Turn off LEDs

  particleSensor.enableDIETEMPRDY(); //Enable the temp ready interrupt. This is required.
}

void loop() {
  float temperature = particleSensor.readTemperature();

  Serial.print("temperatureC=");
  Serial.print(temperature, 4);

  float temperatureF = particleSensor.readTemperatureF();

  Serial.print(" temperatureF=");
  Serial.print(temperatureF, 4);

  Serial.println();
}

Now try heating the sensor with your finger or blowing a light breath on the sensor. You should see something like the output below.

max30102 internal temperature sensor output

Example 4 – Measuring Heart-Rate (BPM)

This is where the fun begins! In this example, we’ll measure heart rate (Beats Per Minute or BPM) of the person we’re monitoring.

Warning:

This sketch detects heart-rate optically. This method is tricky and prone to give false readings. So please DO NOT use it for actual medical diagnosis.

#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"

MAX30105 particleSensor;

const byte RATE_SIZE = 4; //Increase this for more averaging. 4 is good.
byte rates[RATE_SIZE]; //Array of heart rates
byte rateSpot = 0;
long lastBeat = 0; //Time at which the last beat occurred

float beatsPerMinute;
int beatAvg;

void setup() {
  Serial.begin(115200);
  Serial.println("Initializing...");

  // Initialize sensor
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
    Serial.println("MAX30102 was not found. Please check wiring/power. ");
    while (1);
  }
  Serial.println("Place your index finger on the sensor with steady pressure.");

  particleSensor.setup(); //Configure sensor with default settings
  particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running
  particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED
}

void loop() {
  long irValue = particleSensor.getIR();

  if (checkForBeat(irValue) == true) {
    //We sensed a beat!
    long delta = millis() - lastBeat;
    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 20) {
      rates[rateSpot++] = (byte)beatsPerMinute; //Store this reading in the array
      rateSpot %= RATE_SIZE; //Wrap variable

      //Take average of readings
      beatAvg = 0;
      for (byte x = 0 ; x < RATE_SIZE ; x++)
        beatAvg += rates[x];
      beatAvg /= RATE_SIZE;
    }
  }

  Serial.print("IR=");
  Serial.print(irValue);
  Serial.print(", BPM=");
  Serial.print(beatsPerMinute);
  Serial.print(", Avg BPM=");
  Serial.print(beatAvg);

  if (irValue < 50000)
    Serial.print(" No finger?");

  Serial.println();
}

After uploading the sketch, keep your finger on the sensor as steady as possible and wait a few seconds for the readings to make sense. You will see a result like this.

max30102 heart rate output

Trouble Seeing a Heartbeat?

If you’re having trouble seeing a heartbeat, here’s what to do.

  • If you hold the sensor too hard, you will squeeze all the blood from your fingers and there will be no sign! If you hold it too lightly, you will invite noise from movement and ambient light. Sweatspot pressure (Not too hard, not too soft) on the pulse sensor will give a good clean signal.
  • A varying pressure can cause blood to flow differently in your finger, causing the sensor readings to go wonky. Try to apply constant pressure by attaching the sensor to your finger using a rubber band or other tightening device.
  • Try the sensor on different parts of your body that have capillary tissue (such as earlobe or lower lip).

Example 5 – Measuring Oxygen Saturation (SpO2)

In our last example, we’ll measure blood oxygen level (SpO2) of the person we’re monitoring. Go ahead and try the sketch out.

#include <Wire.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"

MAX30105 particleSensor;

#define MAX_BRIGHTNESS 255

#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
//Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format
//To solve this problem, 16-bit MSB of the sampled data will be truncated. Samples become 16-bit data.
uint16_t irBuffer[100]; //infrared LED sensor data
uint16_t redBuffer[100];  //red LED sensor data
#else
uint32_t irBuffer[100]; //infrared LED sensor data
uint32_t redBuffer[100];  //red LED sensor data
#endif

int32_t bufferLength; //data length
int32_t spo2; //SPO2 value
int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
int32_t heartRate; //heart rate value
int8_t validHeartRate; //indicator to show if the heart rate calculation is valid

byte pulseLED = 11; //Must be on PWM pin
byte readLED = 13; //Blinks with each data read

void setup()
{
  Serial.begin(115200); // initialize serial communication at 115200 bits per second:

  pinMode(pulseLED, OUTPUT);
  pinMode(readLED, OUTPUT);

  // Initialize sensor
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
  {
    Serial.println(F("MAX30105 was not found. Please check wiring/power."));
    while (1);
  }

  Serial.println(F("Attach sensor to finger with rubber band. Press any key to start conversion"));
  while (Serial.available() == 0) ; //wait until user presses a key
  Serial.read();

  byte ledBrightness = 60; //Options: 0=Off to 255=50mA
  byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
  byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
  byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
  int pulseWidth = 411; //Options: 69, 118, 215, 411
  int adcRange = 4096; //Options: 2048, 4096, 8192, 16384

  particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
}

void loop()
{
  bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps

  //read the first 100 samples, and determine the signal range
  for (byte i = 0 ; i < bufferLength ; i++)
  {
    while (particleSensor.available() == false) //do we have new data?
      particleSensor.check(); //Check the sensor for new data

    redBuffer[i] = particleSensor.getRed();
    irBuffer[i] = particleSensor.getIR();
    particleSensor.nextSample(); //We're finished with this sample so move to next sample

    Serial.print(F("red="));
    Serial.print(redBuffer[i], DEC);
    Serial.print(F(", ir="));
    Serial.println(irBuffer[i], DEC);
  }

  //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
  maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

  //Continuously taking samples from MAX30102.  Heart rate and SpO2 are calculated every 1 second
  while (1)
  {
    //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
    for (byte i = 25; i < 100; i++)
    {
      redBuffer[i - 25] = redBuffer[i];
      irBuffer[i - 25] = irBuffer[i];
    }

    //take 25 sets of samples before calculating the heart rate.
    for (byte i = 75; i < 100; i++)
    {
      while (particleSensor.available() == false) //do we have new data?
        particleSensor.check(); //Check the sensor for new data

      digitalWrite(readLED, !digitalRead(readLED)); //Blink onboard LED with every data read

      redBuffer[i] = particleSensor.getRed();
      irBuffer[i] = particleSensor.getIR();
      particleSensor.nextSample(); //We're finished with this sample so move to next sample

      //send samples and calculation result to terminal program through UART
      Serial.print(F("red="));
      Serial.print(redBuffer[i], DEC);
      Serial.print(F(", ir="));
      Serial.print(irBuffer[i], DEC);

      Serial.print(F(", HR="));
      Serial.print(heartRate, DEC);

      Serial.print(F(", HRvalid="));
      Serial.print(validHeartRate, DEC);

      Serial.print(F(", SPO2="));
      Serial.print(spo2, DEC);

      Serial.print(F(", SPO2Valid="));
      Serial.println(validSPO2, DEC);
    }

    //After gathering 25 new samples recalculate HR and SP02
    maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
  }
}

After uploading the sketch, keep your finger on the sensor as steady as possible and wait a few seconds for the readings to make sense. You will see a result like this.

max30102 heart rate and oxygen saturation output

Give your next Arduino project the ability to sense the world around it with BMP180.

It’s a basic sensor that is designed specifically for measuring atmospheric pressure, which is really useful for two things.

  • As we travel from sea level to a mountain peak, the air pressure gets lower. That means by measuring the pressure we can determine the altitude. So, we can use this sensor as an Altimeter.
  • Because the atmospheric pressure changes with the weather, we can use it to monitor changes in the weather.

These sensors are fairly simple to use, pre-calibrated and don’t require extra components so you can start measuring barometric pressure, altitude and temperature in no time.

Hardware Overview

At the heart of the module is the next-generation digital pressure and temperature sensor manufactured by Bosch – BMP180.

BMP180 Chip

BMP180 can measure barometric pressure from 300 to 1100 hPa (9000m to -500m above sea level), and temperature from -40°C to 85°C with ±1.0°C accuracy.

bmp180 chip on the module

The pressure measurements are so precise (low altitude noise of 0.25m), you can even use it as an altimeter with ±1 meter accuracy.

bmp180 sensor specifications

Power Requirement

The module comes with an on-board LM6206 3.3V regulator, so you can use it with a 5V logic microcontroller like Arduino without worry.

bmp180 module 3v3 regulator

The BMP180 consumes less than 1mA during measurements and only 5μA during idle. This low power consumption allows the implementation in battery driven devices.

I2C Interface

The module features a simple two-wire I2C interface which can be easily interfaced with any microcontroller of your choice.

This module has a hardwired I2C address and is set to 0x77HEX.

BMP180 Module Pinout

The BMP180 module has only 4 pins that interface it to the outside world. The connections are as follows:

bmp180 module pinout

VCC is the power supply for the module which can be anywhere between 3.3V to 5V.

GND should be connected to the ground of Arduino.

SCL is a serial clock pin for I2C interface.

SDA is a serial data pin for I2C interface.

Wiring BMP180 Module to Arduino

Let’s hook the BMP180 module up to the Arduino.

Connections are fairly simple. Start by connecting VIN pin to the 5V output on the Arduino and connect GND to ground.

Now we are remaining with the pins that are used for I2C communication. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

If you have a Mega, the pins are different! You’ll want to use digital 21 (SCL) and 20 (SDA). Refer below table for quick understanding.

SCLSDA
Arduino UnoA5A4
Arduino NanoA5A4
Arduino Mega2120
Leonardo/Micro32

The following diagram shows you how to wire everything.

wiring bmp180 module with arduino

Installing Necessary libraries

Calculating the altitude and barometric pressure with BMP180 module needs a lot of math. Fortunately, Adafruit BMP180 Library was written to hide away all the complexities so that we can issue simple commands to read the temperature, barometric pressure and altitude data.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing ‘bmp180’. There should be a couple entries. Look for Adafruit BMP085 Library by Adafruit. Click on that entry, and then select Install.

bmp180 arduino library installation

Arduino Code – Reading Temperature and Barometric Pressure

The following sketch will give you complete understanding on how to read temperature and barometric pressure from BMP180 module and can serve as the basis for more practical experiments and projects.

#include <Wire.h>
#include <Adafruit_BMP085.h>
#define seaLevelPressure_hPa 1013.25

Adafruit_BMP085 bmp;
  
void setup() {
  Serial.begin(9600);
  if (!bmp.begin()) {
	Serial.println("Could not find a valid BMP085 sensor, check wiring!");
	while (1) {}
  }
}
  
void loop() {
    Serial.print("Temperature = ");
    Serial.print(bmp.readTemperature());
    Serial.println(" *C");
    
    Serial.print("Pressure = ");
    Serial.print(bmp.readPressure());
    Serial.println(" Pa");

    Serial.print("Altitude = ");
    Serial.print(bmp.readAltitude());
    Serial.println(" meters");

    Serial.print("Pressure at sealevel (calculated) = ");
    Serial.print(bmp.readSealevelPressure());
    Serial.println(" Pa");

    Serial.print("Real altitude = ");
    Serial.print(bmp.readAltitude(seaLevelPressure_hPa * 100));
    Serial.println(" meters");
    
    Serial.println();
    delay(500);
}

Here’s how the output looks like in the serial monitor.

bmp180 temperature pressure altitude output

Every 1hPa off on the sea level pressure results in about 8.5 m of error in the altitude calculations. So, the altitude we are getting is close enough but not accurate.

You can get a more accurate altitude measurement, if you know the current sea level pressure which will vary with weather.

This code assumes that current sea level pressure is 1013.25 millibars that is equal to 101325 Pascals. That’s why seaLevelPressure_hPa variable is set to 1013.25

Code Explanation:

The sketch starts with including four libraries viz. Wire.h and Adafruit_BMP085.h.

#include <Wire.h>
#include <Adafruit_BMP085.h>

Next, we define seaLevelPressure_hPa variable needed to calculate the altitude. Change it to current sea level pressure at your location.

We also create a bmp object so that we can access functions related to it.

#define seaLevelPressure_hPa 1013.25

Adafruit_BMP085 bmp;

In setup function of code we initialize the serial communication with PC and call the begin() function.

The begin() function initializes I2C interface and checks if the chip ID is correct. It then resets the chip using soft-reset & waits for the sensor for calibration after wake-up.

Serial.begin(9600);

if (!bmp.begin()) {
    Serial.println("Could not find a valid BMP085 sensor, check wiring!");
    while (1) {}
}

In loop function, we use following functions to read temperature barometric pressure and altitude from the BMP180 module.

  • readTemperature() function returns the temperature from the sensor.
  • readPressure() function returns the barometric pressure from the sensor.
  • readAltitude(seaLevelPressure_hPa * 100) function calculates the altitude (in meters) from the specified atmospheric pressure (in hPa).
  • readSealevelPressure() function calculates the sea-level pressure (in hPa).
Serial.print("Temperature = ");
Serial.print(bmp.readTemperature());
Serial.println(" *C");

Serial.print("Pressure = ");
Serial.print(bmp.readPressure());
Serial.println(" Pa");

Serial.print("Altitude = ");
Serial.print(bmp.readAltitude());
Serial.println(" meters");

Serial.print("Pressure at sealevel (calculated) = ");
Serial.print(bmp.readSealevelPressure());
Serial.println(" Pa");

Serial.print("Real altitude = ");
Serial.print(bmp.readAltitude(seaLevelPressure_hPa * 100));
Serial.println(" meters");

Bosch’s BME280 is a precision sensor used in a myriad of applications ranging from weather monitoring to gaming controls to altitude measurement where accuracy of just a few feet is required.

This sensor is simple to use, comes pre-calibrated, and requires no additional components, so you can start measuring relative humidity, temperature, barometric pressure, and altitude in no time. 

So, let’s get acquainted with the BME280, which we have heard a lot about.

BME280 Capabilities

Measuring Temperature

The BME280 can measure temperatures ranging from -40°C to 85°C. Over the temperature range of 0 to 65°C, the accuracy is ±1.0°C; outside of that range, the accuracy drops to ±1.5°C.

Note that this temperature measurement is used internally to calibrate the pressure and humidity sensors. Because the sensor self-heats, the measured temperature is usually slightly higher than the actual temperature. If this is critical to your project, compare the measured temperature to the actual temperature and apply an offset if necessary.

Measuring Humidity

The BME280 can measure relative humidity over a range of 0 to 100% with an accuracy of ±3%.

According to the datasheet, the sensor can measure up to 100% humidity over a temperature range of 0 to 60°C. However, the maximum measurable humidity decreases at extremely high and low temperatures.

Measuring Pressure

The BME280 can measure pressure between 300Pa to 1100 hPa with an absolute accuracy of ±1 hPa.

Over the temperature range of 0 to 65°C, full accuracy is obtained, resulting in an altitude measurement accuracy of approximately ±1 meter. Outside of that range, the accuracy drops to 1.7 hPa.

Calculating Altitude / Elevation

The BME280 can measure pressure with such precision (low altitude noise of 0.25m) that it can also be used as an altimeter with an accuracy of ±1 meter.

Before proceeding, it is important to understand the distinction between Absolute and Relative Altitude. The term “absolute altitude” refers to the height above sea level (MSL), whereas “relative altitude” refers to the height above ground level (AGL).

Note that the BME280 cannot directly measure altitude but can estimate it using pressure readings. Because the BME280 is very good at measuring pressure, it can calculate relative altitude accurately. For example, if you know the altitude of an object sitting on a table and you move it to the floor, the BME280 will show a 2 foot decrease in height.

However, if you’re trying to measure absolute altitude, things become a bit more complicated because the BME280 needs to know the current sea level pressure.

So, to get an accurate absolute altitude measurement, the SEA_LEVEL_PRESSURE constant is provided in the example code below, which you should update with the current sea level pressure at your location.

Hardware Overview

BME280 IC

At the core of the module is the next-generation digital temperature, humidity, and pressure sensor from Bosch – BME280. It is the successor to sensors such as the BMP180, BMP085 and BMP183.

BME280 Chip On The Module

Power

The module includes an on-board LM6206 3.3V regulator and I2C Voltage Level Translator, so you can use it with a 3.3V or 5V logic microcontroller like Arduino without worry.

BME280 Module I2C Voltage Translator & 3.3V Regulator

The BME280 consumes less than 1mA during measurements and only 5μA when idle. Because of its low power consumption, it can be used in battery-powered devices such as handsets, GPS modules, and watches.

I2C Interface

The BME280 module communicates via I2C and supports two I2C addresses, 0x76 and 0x77, allowing up to two sensors to be used on the same bus.

The module’s default I2C address is 0x76HEX, which can be easily changed to 0x77HEX using the provided solder jumper.

BME280 Module I2C Address Selector Solder Jumper

To change the i2c address to 0x77, cut the trace between the middle and left copper pads with a sharp knife. Then, add a solder blob between the middle and right copper pads to short them.

BME280 I2C Address Selection Jumper Setting

Technical Specifications

Here are the specifications:

Input voltage3.3V – 5V
Current consumption1mA (typ.) and 5μA (idle)
Temperature-40°C to 85°C (±1.0°C accuracy)
Humidity0 to 100% RH (±3% accuracy)
Pressure300Pa to 1100 hPa (±1 hPa accuracy)
Altitude0 to 30,000 ft. (±1 m accuracy)

For more information, please refer to the datasheet below.

BME280 Sensor Pinout

The BME280 module has only 4 pins that interface it to the outside world. The connections are as follows:

BME280 Pinout - Temperature Humidity Barometric Pressure Sensor

VIN supplies power to the module. Connect any voltage between 3.3V and 5V to this pin.

GND is the ground pin.

SCL is a serial clock pin for the I2C interface.

SDA is a serial data pin for the I2C interface.

Wiring a BME280 Module to an Arduino

Let’s hook the BME280 module to the Arduino.

Connections are straightforward. Begin by connecting the VCC pin to the Arduino’s 5V output and the GND pin to ground.

Now we are left with the pins that are used for I2C communication. Note that each Arduino Board has different I2C pins that must be connected correctly.  On Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also referred to as A5 (SCL) and A4 (SDA).

The following table lists the pin connections:

BME280 ModuleArduino
VCC5V
GNDGND
SCLSCL or A5
SDASDA or A4

The diagram below shows how to connect everything.

Fritzing Wiring BME280 Module to Arduino

Installing Necessary libraries

To begin reading sensor data, you must first install the Adafruit BME280 Library. It is available from the Arduino library manager.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the libraries index and update the list of installed libraries.

Arduino Library Installation - Selecting Manage Libraries in Arduino IDE

Filter your search by entering ‘bme280’. Look for the Adafruit BME280 Library by Adafruit. Click on that entry and then choose Install.

Installing BME280 Library In Arduino IDE

The BME280 sensor library makes use of the Adafruit Sensor support backend. So, look for Adafruit Unified Sensor and install it as well (you may have to scroll a bit).

Adafruit Unified Sensor Library Installation

Arduino Example Code

Here is a simple program that reads the temperature, relative humidity, pressure, and approx. altitude from the BME280 module and prints them on the serial monitor.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme;

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

	if (!bme.begin(0x76)) {
		Serial.println("Could not find a valid BME280 sensor, check wiring!");
		while (1);
	}
}

void loop() {
	Serial.print("Temperature = ");
	Serial.print(bme.readTemperature());
	Serial.println("*C");

	Serial.print("Pressure = ");
	Serial.print(bme.readPressure() / 100.0F);
	Serial.println("hPa");

	Serial.print("Approx. Altitude = ");
	Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
	Serial.println("m");

	Serial.print("Humidity = ");
	Serial.print(bme.readHumidity());
	Serial.println("%");

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

You should see a similar output in the serial monitor.

BME280 Temperature Humidity Pressure & Altitude Output On Serail Monitor

Code Explanation:

The sketch begins by including three libraries, namely Wire.h, Adafruit Sensor.h, and Adafruit BME280.h.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

The variable SEALEVELPRESSURE_HPA is then defined. This variable stores the sea level pressure in millibars and is used to calculate absolute altitude for a given pressure by comparing it to the sea level pressure. The default value (1013.25) is used in this sketch, but for accurate results, replace it with the current sea level pressure at your location.

An object of the Adafruit BME280 library is also created so that we can access its functions.

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme;

In the setup section of the code, we initialize the serial communication with the PC and call the begin() function.

The begin(I2C_ADDR) function takes the module’s I2C address as a parameter. If you changed the I2C address of your module, you must specify it correctly. This function initializes the I2C interface with the given I2C address and validates the chip ID. It then soft-resets the chip and waits for the sensor to calibrate after wake-up.

Serial.begin(9600);

if (!bme.begin(0x76)) {
	Serial.println("Could not find a valid BME280 sensor, check wiring!");
	while (1);
}

In the loop section of the code, we use the following functions to read temperature, relative humidity, and barometric pressure from the BME280 module.

readTemperature() function returns the temperature.

readPressure() function returns the barometric pressure.

readAltitude(SEALEVELPRESSURE_HPA) function computes the altitude (in meters) by comparing the specified atmospheric pressure (in hPa) to the sea-level pressure (in hPa).

readHumidity() function returns the relative humidity.

Serial.print("Temperature = ");
Serial.print(bme.readTemperature());
Serial.println("*C");

Serial.print("Pressure = ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println("hPa");

Serial.print("Approx. Altitude = ");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println("m");

Serial.print("Humidity = ");
Serial.print(bme.readHumidity());
Serial.println("%");

Want to build a personal air quality tracker that tells if your bedroom, your workshop, or workplace has adequate ventilation? Install the BME680 environmental sensor module on an Arduino and monitor every aspect of your indoor environment.

The state-of-the-art BME680 breakout lets you measure temperature, pressure, humidity, and indoor air quality, and the best part is that it’s Arduino-compatible!

The BME680 Environmental Sensor

At the heart of the module is the next-generation environmental sensor manufactured by Bosch – BME680.

bme680 module hardware overview chip

This small sensor has temperature, humidity, barometric pressure, and VOC gas (volatile organic compounds) sensing capabilities. It contains a small MOx (metal-oxide) sensor. The heated metal oxide changes resistance based on VOCs in the air, so it can be used to detect gases & alcohols such as Ethanol, Alcohol and Carbon Monoxide and perform air quality measurements. Therefore the BME680 can work as a fully standalone environmental sensor.

Like most gas sensors, this sensor is sensitive to multiple gases and alcohols, but cannot tell which is which. It will give you one resistance value, with overall VOC content. So, it is best for measuring changes in a known gas density, not detecting which is changing.

Like the BME280, this precision sensor can measure humidity with ±3% accuracy, barometric pressure with ±1 hPa absolute accuracy, and temperature with ±1.0°C accuracy. The pressure measurements are so precise; you can even use it as an altimeter with ±1 meter accuracy.

Here are the complete specifications:

Temp-40°C to 85°C
Humidity0 to 100% RH with ±3% accuracy
Pressure300Pa to 1100 hPa with ±1 hPa absolute accuraccy
Altitude0 to 30,000 ft (9.2 km) with ±1 meter accuracy

For more details, please refer below datasheet.

What are volatile organic compounds (VOCs)?

Volatile organic compounds (VOCs) are emitted as gases from certain products we use to build and maintain our homes (such as Air fresheners, cleaning products, Cosmetics, Fuel oil, gasoline, Paint, varnishes, Carpet, and vinyl flooring etc.). VOCs include a variety of chemicals, some of which may have short- and long-term adverse health effects.

Common examples of VOCs that may be present in our daily lives are: benzene, ethylene glycol, formaldehyde, methylene chloride, tetrachloroethylene, toluene, xylene, and 1,3-butadiene.

Power Requirement

The module comes with a XC6206 3.3V precise voltage regulator and voltage level translator, so you can use it with your favorite 3.3V or 5V microcontroller without worry.

bme680 module voltage regulator translator

The BME680 consumes less than 1mA during measurements and only 0.15µA during sleep mode. This low power consumption allows the implementation in battery driven devices such as handsets, wearables or smart watches.

Digital interfaces

The sensor communicates via either I2C or SPI.

I2C Interface

The sensor uses the I2C interface for communication with the Arduino. It supports two separate I2C addresses: 0x77Hex and 0x76Hex. This allows two BME680s to be used on the same bus or to avoid address conflicts with another device on the bus.

bme680 module i2c address selection pin

The SDO pin determines the I2C address of the module. This pin has a built-in pull-up resistor. Therefore, when you leave the SDO pin unconnected, the default I2C address is 0x77Hex and when you connect it to GND, the line is pulled LOW and the I2C address becomes 0x76Hex.

SPI Interface

The sensor can also communicate via the SPI interface.

FYI:

If you have to choose between the two, SPI is generally the better tool, if you need faster transfer speeds (up to 10 MHz). I2C, on the other hand, is best if you have limited pins available on your microcontroller.

How BME680 MEMS Gas sensor works?

Unlike MQ2 or MQ3, BME680 gas sensor is fabricated with micro-electro-mechanical system (MEMS) technology. MEMS based gas detection provides dramatic size reduction, a reduction in power consumption, and the ability to increase functionality and selectivity.

A typical MEMS gas sensor consists of a metal oxide semiconductor layer (sensing material) that reacts with the target gas, at least two electrodes to monitor the electrical resistance/current variations, and a micro-hotplate to increase the operating temperature.

metal oxide gas sensor construction

When metal oxide semiconductor layer is heated at high temperature, oxygen is adsorbed on the surface. In clean air, electrons from the conduction band in metal-oxide are attracted to oxygen molecules. This form an electron depletion layer just below the surface of metal-oxide particles and forms a potential barrier. As a result, the metal-oxide layer becomes highly resistive and prevents electric current flow.

In the presence of gases, however, the surface density of adsorbed oxygen decreases as it reacts with the gases; which lowers the potential barrier. Electrons are then released into the metal-oxide layer, allowing current to flow freely through the sensor.

metal oxide gas sensor working animation

BME680 Module Pinout

Now let’s have a look at the pinout.

bme680 environmental sensor pinout

Power Pins:

VCC is the power pin. You can connect it to 5V output from your Arduino.

GND is the common ground for power and logic.

SPI Logic pins:

SCL is the SPI Clock pin, its an input to the chip

SDA is the Serial Data In (SDI/MOSI)pin, for data sent from your microcontroller to the BME680.

SDO is the Serial Data Out (SDO/MISO) pin, for data sent from the BME680 to your microcontroller.

CS is the Chip Select pin, pull it LOW to enable an SPI communication. If you want to connect multiple BME680’s to one microcontroller, have them share the SDI, SDO and SCK pins and assign each one a unique CS pin.

I2C Logic pins:

SCL is also the I2C clock pin, connect to your microcontrollers I2C clock line.

SDA is also the I2C data pin, connect to your microcontrollers I2C data line.

Preparing BME680 Module for Use

Prior to the first use of the sensor, it is recommended that you run it for 48 hours to burn-in, and then 30 minutes in the desired mode every time the sensor is in use. The reason for this is that the sensitivity level of the sensor changes during initial use and the resistance gradually increases as the MOx heats up to its baseline reading over time.

Wiring BME680 Module with Arduino

Now that we know everything about the module, we can begin hooking it up to our Arduino!

I2C Wiring

Use this wiring if you want to connect via I2C interface.

Start by connecting the VCC pin to the power supply, 3V-5V is fine. Use the same voltage that your microcontroller logic is based off of. For most Arduinos, that is 5V. For 3.3V logic devices, use 3.3V. Now connect GND to common ground.

Connect the SCL pin to the I2C clock pin and the SDA pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

The following illustration shows the wiring.

wiring connecting bme680 sensor module with arduino through i2c interface

SPI Wiring

Since it’s an SPI-capable sensor, you can use hardware or software SPI. However, it is recommended that you use hardware SPI pins as they are much faster than ‘bit-banging’ the interface code using another set of pins.

Again, each Arduino Board has different SPI pins which should be connected accordingly. For Arduino boards such as the UNO/Nano V3.0 those pins are digital 13 (SCK), 12 (MISO), 11 (MOSI) and 10 (SS).

If you are using a different Arduino board, it is advisable to check the official documentation about SPI pin locations before proceeding.

The following illustration shows the wiring.

wiring connecting bme680 sensor module with arduino through spi interface

Once your module is connected to the Arduino it’s time to write some code!

Library Installation

To begin reading sensor data, you will need to install the Adafruit_BME680 library. It is available from the Arduino library manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing ‘adafruit bme680‘. Click on that entry, and then select Install.

installing bme680 library in arduino ide

The BME280 sensor library uses the Adafruit Sensor support backend. So, search the library manager for Adafruit Unified Sensor and install that too (you may have to scroll a bit)

adafruit unified sensor library installation

BME680 Example Sketches

The Adafruit_BME680 library has a number of example sketches. You can use these example sketches as a basis for developing your own code.

To access the example sketches, navigate to the File > Examples > Adafruit BME680 Library You will see a selection of example sketches.

adafruit bme680 library example sketches

Arduino Code – Reading gas, pressure, humidity, temperature, and altitude

Load the bme680test sketch from the example sketches into your Arduino IDE. This is a basic Arduino sketch. Go ahead and upload it to your Arduino. You should see temperature, pressure, humidity, gas, and approx. altitude on the serial monitor.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"

// Uncomment if you want to use SPI
//#define BME_SCK 13
//#define BME_MISO 12
//#define BME_MOSI 11
//#define BME_CS 10

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME680 bme;
//Adafruit_BME680 bme(BME_CS); // Uncomment if you want to use SPI
//Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO,  BME_SCK);

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

  if (!bme.begin()) {
    Serial.println("Could not find a valid BME680 sensor, check wiring!");
    while (1);
  }

  // Set up oversampling and filter initialization
  bme.setTemperatureOversampling(BME680_OS_8X);
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setPressureOversampling(BME680_OS_4X);
  bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
  bme.setGasHeater(320, 150); // 320*C for 150 ms
}

void loop() {
  if (! bme.performReading()) {
    Serial.println("Failed to perform reading :(");
    return;
  }
  Serial.print("Temperature = ");
  Serial.print(bme.temperature);
  Serial.println(" *C");

  Serial.print("Pressure = ");
  Serial.print(bme.pressure / 100.0);
  Serial.println(" hPa");

  Serial.print("Humidity = ");
  Serial.print(bme.humidity);
  Serial.println(" %");

  Serial.print("Gas = ");
  Serial.print(bme.gas_resistance / 1000.0);
  Serial.println(" KOhms");

  Serial.print("Approx. Altitude = ");
  Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(" m");

  Serial.println();
  delay(2000);
}

Note that you must set your serial monitor to a speed of 115200 baud to try out the sketch.

You will see myriad of data displaying temperature, pressure, humidity, gas, and approx. altitude values. Try moving your sensor around and notice how the data changes.

bme680 sensor arduino code output

Code Explanation:

The sketch starts with including four necessary libraries viz. Wire.h (for I2C), SPI.h (for SPI), Adafruit_Sensor.h and Adafruit_BME680.h.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"

This example uses I2C communication protocol to communicate with the sensor. However, the code is prepared if you want to use SPI. You just need to uncomment the following lines of code that define the SPI pins.

//#define BME_SCK 13
//#define BME_MISO 12
//#define BME_MOSI 11
//#define BME_CS 10

Next, a variable called SEALEVELPRESSURE_HPA is defined. This variable saves the sea level pressure in millibar and is used to estimate the altitude for a given pressure by comparing it with the sea level pressure. This sketch uses the default value, but for accurate results, replace the value with the current sea level pressure at your location.

#define SEALEVELPRESSURE_HPA (1013.25)

This sketch uses I2C communication protocol by default. The following line creates an Adafruit_BME680 object called bme on the Arduino I2C pins.

Adafruit_BME680 bme;

If you want to use SPI, you need to comment previous line and uncomment the following lines.

//Adafruit_BME680 bme(BME_CS);
//Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO,  BME_SCK);

In the setup, we initialize the serial communication with PC and call the begin() function. The begin() function initializes I2C interface and checks if the chip ID is correct. It then resets the chip using soft-reset & waits for the sensor for calibration after wake-up.

Serial.begin(9600);
while (!Serial);

if (!bme.begin()) {
	Serial.println("Could not find a valid BME680 sensor, check wiring!");
	while (1);
}

Next, we set up some parameters for the sensor.

bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150); // 320*C for 150 ms

Let’s look at each function one by one.

setTemperatureOversampling(uint8_t os)

setPressureOversampling(uint8_t os)

setHumidityOversampling(uint8_t os)

To increase the effective resolution of measurements and reduce noise, the BME680 supports oversampling (taking multiple samples and averaging them together). There are three functions called setTemperatureOversampling(), setHumidityOversampling() and setPressureOversampling() to set oversampling for temperature, humidity, and pressure measurements, respectively. These functions accept one of the following parameters:

  • BME680_OS_NONE (turns off reading)
  • BME680_OS_1X
  • BME680_OS_2X
  • BME680_OS_4X
  • BME680_OS_8X
  • BME680_OS_16X

setIIRFilterSize(fs)

The BME680 also features an internal IIR filter to reduce short-term changes in sensor output values caused by external disturbances (such as slamming a door or blowing air into the sensor). The setIIRFilterSize() function sets the IIR filter. The IIR filter can be configured to different filter coefficients by choosing one of the following parameters:

  • BME680_FILTER_SIZE_0 (no filtering)
  • BME680_FILTER_SIZE_1
  • BME680_FILTER_SIZE_3
  • BME680_FILTER_SIZE_7
  • BME680_FILTER_SIZE_15
  • BME680_FILTER_SIZE_31
  • BME680_FILTER_SIZE_63
  • BME680_FILTER_SIZE_127

setGasHeater(heaterTemp, heaterTime)

In addition, the gas sensor hot plate can be configured to be heated before the gas measurement is performed. The setGasHeater() function sets the heater profile and accepts two arguments

  • heaterTemp – The heater temperature (in °C)
  • heaterTime – The time the heater should be on (in milliseconds)

For example, bme.setGasHeater(320, 150) will heat the gas sensor hot plate at 320°C for 150 milliseconds.

In the loop, we call the bme.performReading() function to perform a reading in blocking mode. Once this is done we can access bme’s instance variables using the dot (.) operator.

bme.temperature returns temperature reading.

bme.pressure returns barometric pressure reading.

bme.humidity returns relative humidity reading.

bme.gas_resistance returns gas resistance.

bme.readAltitude(SEALEVELPRESSURE_HPA) function calculates the altitude (in meters) from the specified atmospheric pressure (in hPa), and sea-level pressure (in hPa).

if (! bme.performReading()) {
	Serial.println("Failed to perform reading :(");
	return;
}
Serial.print("Temperature = ");
Serial.print(bme.temperature);
Serial.println(" *C");

Serial.print("Pressure = ");
Serial.print(bme.pressure / 100.0);
Serial.println(" hPa");

Serial.print("Humidity = ");
Serial.print(bme.humidity);
Serial.println(" %");

Serial.print("Gas = ");
Serial.print(bme.gas_resistance / 1000.0);
Serial.println(" KOhms");

Serial.print("Approx. Altitude = ");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");

A lot of people are flying drones these days, and knowing the altitude your drone reached is a nice thing to know. A blazingly fast, temperature stable, pinpoint-accurate barometric pressure sensor like Bosch’s BMP388 could be the perfect choice for altitude tracking in your next drone build.

Hardware Overview

At the heart of the module is the next-generation digital pressure and temperature sensor, from Bosch – BMP388.

The BMP388 Pressure Sensor

The BMP388 is a very small, blazingly fast, precise, low power, low noise absolute barometric pressure sensor. It enables accurate altitude tracking and is perfectly suited for drone applications or any project that wants to track height-above-sea-level.

bmp388 module hardware overview chip

The BMP388 can measure barometric pressure from 300 hPa to 1250 hPa and has a relative accuracy of 8 Pascals, which translates to about ± 0.5 meter of altitude.

The BMP388 has an on-chip temperature sensor that can be used to compensate for the changes in the environment and to calibrate the measurements. This is a reasonably precise temperature sensor that measures the ‘die temperature’ in the range of -40˚C to +85˚C with an accuracy of ±1˚C.

Power Requirement

The module comes with a MIC5219 3.3V precise voltage regulator and voltage level translator, so you can use it with your favorite 3.3V or 5V microcontroller without worry.

bmp388 module voltage regulator translator

The BMP388 consumes less than 0.7mA during measurements and only 2µA during sleep mode. This low power consumption allow the implementation in battery driven devices such as handsets, wearables or smart watches.

Digital interfaces

The sensor communicates via either I2C or SPI.

I2C Interface

The sensor uses the I2C interface for communication with the Arduino. It supports two separate I2C addresses: 0x77Hex and 0x76Hex. This allows two BMP388 modules to be used on the same bus or to avoid address conflicts with another device on the bus.

bmp388 module i2c address selection pin

The SDO pin determines the I2C address of the module. This pin has a built-in pull-up resistor. Therefore, when you leave the SDO pin unconnected, the default I2C address is 0x77Hex and when you connect it to GND, the line is pulled LOW and the I2C address becomes 0x76Hex.

SPI Interface

The sensor can also communicate via the SPI interface.

FYI:

If you have to choose between the two, SPI is generally the better tool, if you need faster transfer speeds (up to 10 MHz). I2C, on the other hand, is best if you have limited pins available on your microcontroller.

Modes of Operation

The BMP388 has 3 modes of operation:

  • SLEEP_MODE: puts the device into an inactive standby state (no measurements are performed).
  • NORMAL_MODE: performs continuous conversions, separated by the standby time.
  • FORCED_MODE: performs a single conversion, returning to SLEEP_MODE upon completion.

FIFO Buffer

The BMP388 embeds a 512 bytes FIFO (first-in-first-out) buffer for storing up to 72 pressure and/or temperature measurement results. The FIFO buffer can offload the microcontroller from reading each new data sample from the sensor, thereby saving system power.

Interrupts

The BMP388 has an interrupt (INT) output pin that enables measurements to be interrupt driven instead of using polling, allowing the host microcontroller to perform other tasks while the data is collected by the sensor. The interrupt can be enabled for 3 different sources:

  • Data Ready: triggers after a pressure and temperature measurement ends and conversion results are stored to the data registers and to the FIFO.
  • FIFO Watermark: triggers when fill level of the FIFO has reached a pre-set limit.
  • FIFO Full: triggers when the FIFO becomes full and future data is about to be lost.
bmp388 module interrupt output pin

The INT pin output drive can be: PUSH_PULL or OPEN_COLLECTOR, the level of the INT pin can be: ACTIVE_LOW or ACTIVE_HIGH and interrupt itself can be: UNLATCHED or LATCHED. In UNLATCHED mode the interrupt signal automatically clears after 2.5ms, while in LATCHED mode the interrupt signal remains active until the data is read.

Technical Specifications

Here are the complete specifications:

Power supply3.3V to 5.5V
Current draw~0.7mA (during measurements)
~2µA (during standby mode)
Pressure Measurement Range300Pa to 1250 hPa
Pressure Absolute Accuracy±0.5 hPa
Temperature Range-40˚C to +85˚C
Temperature Accuracy±1˚C

For more details, please refer below datasheet.

BMP388 Module Pinout

Now let’s have a look at the pinout.

bmp388 module pinout

Power Pins:

VCC is the power pin. You can connect it to 5V output from your Arduino.

3Vo is the 3.3V output from the voltage regulator. You can grab up to 100mA from this, if you need.

GND is the common ground for power and logic.

SPI Logic pins:

SCK is the SPI Clock pin, it’s an input to the chip.

SDO is the Serial Data Out (MISO) pin, for data sent from the BMP388 to your microcontroller.

SDI is the Serial Data In (MOSI) pin, for data sent from your microcontroller to the BMP388.

CS is the Chip Select pin, pull it LOW to enable an SPI communication. If you want to connect multiple BMP388’s to one microcontroller, have them share the SDI, SDO and SCK pins and assign each one a unique CS pin.

I2C Logic pins:

SCK is also the I2C clock pin, connect to your microcontrollers I2C clock line.

SDI is also the I2C data pin, connect to your microcontrollers I2C data line.

SDO pin determines the I2C address of the module. When you leave the SDO pin unconnected, the default I2C address is 0x77Hex and when you connect it to GND, the line is pulled LOW and the I2C address becomes 0x76Hex.

Interrupt Pin:

INT is the Interrupt pin. The BMP388 can be programmed to generate interrupts on certain events. Please refer Interrupts section.

Wiring up a BMP388 Module to an Arduino

Now that we know everything about the module, we can begin hooking it up to our Arduino!

I2C Wiring

Use this wiring if you want to connect via I2C interface.

Start by connecting the VCC pin to the power supply, 3V-5V is fine. Use the same voltage that your microcontroller logic is based off of. For most Arduinos, that is 5V. For 3.3V logic devices, use 3.3V. Now connect GND to common ground.

Connect the SCK pin to the I2C clock pin and the SDI pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

The following illustration shows the wiring.

wiring bmp388 module with arduino through i2c interface

SPI Wiring

Since it’s an SPI-capable sensor, you can use hardware or software SPI. However, it is recommended that you use hardware SPI pins as they are much faster than ‘bit-banging’ the interface code using another set of pins.

Again, each Arduino Board has different SPI pins which should be connected accordingly. For Arduino boards such as the UNO/Nano V3.0 those pins are digital 13 (SCK), 12 (MISO), 11 (MOSI) and 10 (SS).

If you are using a different Arduino board, it is advisable to check the official documentation about SPI pin locations before proceeding.

The following illustration shows the wiring.

wiring bmp388 module with arduino through spi interface

Once your module is connected to the Arduino it’s time to write some code!

Library Installation

To begin reading sensor data, you will need to install the Adafruit BMP3XX library. It is available from the Arduino library manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing ‘adafruit bmp3xx‘ and install the library.

adafruit bmp3xx library installation

The BMP3XX sensor library uses the Adafruit Sensor support backend. So, search the library manager for Adafruit Unified Sensor and install that too (you may have to scroll a bit)

adafruit unified sensor library installation

Arduino Code – Reading Pressure, Altitude and Temperature

Below is a basic Arduino sketch. Go ahead and upload it to your Arduino. You should see pressure, temperature and approx. altitude on the serial monitor.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"

//Uncomment if you want to use SPI
//#define BMP_SCK 13
//#define BMP_MISO 12
//#define BMP_MOSI 11
//#define BMP_CS 10

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BMP3XX bmp;

void setup() {
  Serial.begin(115200);
  while (!Serial);

  if (!bmp.begin_I2C()) {   // hardware I2C mode, can pass in address & alt Wire
  //if (! bmp.begin_SPI(BMP_CS)) {  // hardware SPI mode  
  //if (! bmp.begin_SPI(BMP_CS, BMP_SCK, BMP_MISO, BMP_MOSI)) {  // software SPI mode
    Serial.println("Could not find a valid BMP3 sensor, check wiring!");
    while (1);
  }

  // Set up oversampling and filter initialization
  bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
  bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
  bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
  bmp.setOutputDataRate(BMP3_ODR_50_HZ);
}

void loop() {
  if (! bmp.performReading()) {
    Serial.println("Failed to perform reading :(");
    return;
  }
  Serial.print("Temperature = ");
  Serial.print(bmp.temperature);
  Serial.println(" *C");

  Serial.print("Pressure = ");
  Serial.print(bmp.pressure / 100.0);
  Serial.println(" hPa");

  Serial.print("Approx. Altitude = ");
  Serial.print(bmp.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(" m");

  Serial.println();
  delay(2000);
}

Note that you must set your serial monitor to a speed of 115200 baud to try out the sketch.

You will see a lot of data displaying pressure, temperature and approx. altitude values. Try moving your sensor around and notice how the data changes.

bmp388 sensor arduino output

Code Explanation:

The sketch starts with including four necessary libraries viz. Wire.h (for I2C), SPI.h (for SPI), Adafruit_Sensor.h and Adafruit_BMP3XX.h.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"

This sketch uses I2C communication protocol to communicate with the sensor. However, the sketch is prepared if you want to use SPI. You just need to uncomment the following lines of code that define the SPI pins.

//#define BMP_SCK 13
//#define BMP_MISO 12
//#define BMP_MOSI 11
//#define BMP_CS 10

Next, a variable called SEALEVELPRESSURE_HPA is defined. This variable saves the sea level pressure in milibar and is used to estimate the altitude for a given pressure by comparing it with the sea level pressure. This sketch uses the default value, but for accurate results, replace the value with the current sea level pressure at your location.

#define SEALEVELPRESSURE_HPA (1013.25)

The following line creates an Adafruit_BMP3XX object called bmp.

Adafruit_BMP3XX bmp;

In the setup, we initialize the serial communication with PC and call the begin() function.

The bmp.begin_I2C() function initializes I2C interface and checks if the chip ID is correct. It then resets the chip using soft-reset & waits for the sensor for calibration after wake-up. Likewise the bmp.begin_SPI() function initializes SPI interface. If you want to use SPI, you need to uncomment corresponding if.

Serial.begin(115200);
while (!Serial);
Serial.println("Adafruit BMP388 / BMP390 test");

if (!bmp.begin_I2C()) {   // hardware I2C mode, can pass in address & alt Wire
	//if (! bmp.begin_SPI(BMP_CS)) {  // hardware SPI mode  
	//if (! bmp.begin_SPI(BMP_CS, BMP_SCK, BMP_MISO, BMP_MOSI)) {  // software SPI mode
	Serial.println("Could not find a valid BMP3 sensor, check wiring!");
	while (1);
}

Next, we set up some parameters for the sensor.

bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
bmp.setOutputDataRate(BMP3_ODR_50_HZ);

Let’s look at each function one by one.

setTemperatureOversampling(uint8_t os)

setPressureOversampling(uint8_t os)

To increase the effective resolution of measurements and reduce noise, the BMP388 supports oversampling (taking multiple samples and averaging them together). There are two functions called setTemperatureOversampling() and setPressureOversampling() to set oversampling for temperature and pressure measurements, respectively. These functions can accepts one of the following parameters:

  • BMP3_NO_OVERSAMPLING (turns off reading)
  • BMP3_OVERSAMPLING_2X
  • BMP3_OVERSAMPLING_4X
  • BMP3_OVERSAMPLING_8X
  • BMP3_OVERSAMPLING_16X
  • BMP3_OVERSAMPLING_32X

setIIRFilterCoeff(uint8_t fs)

The BMP388 also features an internal IIR filter to reduce short-term pressure changes in sensor output values caused by external disturbances (such as slamming a door or window, or blowing air into the sensor). The setIIRFilterSize() function sets the IIR filter. The IIR filter can be configured to different filter coefficients by choosing one of the following parameters:

  • BMP3_IIR_FILTER_DISABLE (no filtering)
  • BMP3_IIR_FILTER_COEFF_1
  • BMP3_IIR_FILTER_COEFF_3
  • BMP3_IIR_FILTER_COEFF_7
  • BMP3_IIR_FILTER_COEFF_15
  • BMP3_IIR_FILTER_COEFF_31
  • BMP3_IIR_FILTER_COEFF_63
  • BMP3_IIR_FILTER_COEFF_127

Note that higher IIR filter coefficients provide stable measurements at the cost of slow response time.

setOutputDataRate(uint8_t odr)

The setOutputDataRate() function sets the output data rate i.e. the maximum you can read the device for the given settings. This function accepts one of the following parameters:

  • BMP3_ODR_200_HZ
  • BMP3_ODR_100_HZ
  • BMP3_ODR_50_HZ
  • BMP3_ODR_25_HZ
  • BMP3_ODR_12_5_HZ
  • BMP3_ODR_6_25_HZ
  • BMP3_ODR_3_1_HZ
  • BMP3_ODR_1_5_HZ
  • BMP3_ODR_0_78_HZ
  • BMP3_ODR_0_39_HZ
  • BMP3_ODR_0_2_HZ
  • BMP3_ODR_0_1_HZ
  • BMP3_ODR_0_05_HZ
  • BMP3_ODR_0_02_HZ
  • BMP3_ODR_0_01_HZ
  • BMP3_ODR_0_006_HZ
  • BMP3_ODR_0_003_HZ
  • BMP3_ODR_0_001_HZ

In the loop, we call the bmp.performReading() function to perform a reading. Once this is done we can access object’s (bmp) instance variables using the dot operator.

bmp.temperature returns temperature reading.

bmp.pressure returns barometric pressure reading.

bmp.readAltitude(SEALEVELPRESSURE_HPA) function calculates the altitude (in meters) from the specified atmospheric pressure (in hPa), and sea-level pressure (in hPa).

void loop() {
  if (! bmp.performReading()) {
    Serial.println("Failed to perform reading :(");
    return;
  }
  Serial.print("Temperature = ");
  Serial.print(bmp.temperature);
  Serial.println(" *C");

  Serial.print("Pressure = ");
  Serial.print(bmp.pressure / 100.0);
  Serial.println(" hPa");

  Serial.print("Approx. Altitude = ");
  Serial.print(bmp.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(" m");

  Serial.println();
  delay(2000);
}

Whether you want to know what altitude your RC aircraft reached, the elevation of the mountain pass through which you’re driving, or how high you and your hiking buddies got on your last trek; the inexpensive MS5611 barometric pressure sensor/altimeter may meet your needs.

Hardware Overview

At the heart of the module is a very small, blazingly fast, precise, low power, low noise barometric pressure sensor, from MEAS Switzerland – MS5611.

ms5611 module hardware overview chip

The MS5611 can measure barometric pressure from 10 mbar to 1200 mbar with absolute accuracy of ±1.5 mbar over the pressure range of 450 to 1100 mbar. Outside that pressure range, the guaranteed accuracy is ± 2.5 mbar.

This barometric pressure sensor is optimized for altimeters and variometers with an altitude resolution of 0.012 mbar, which translates to about 10cm (4″) of altitude.

The MS5611 has an on-chip temperature sensor that can be used to compensate for the changes in the environment and to calibrate the measurements. This is a reasonably precise temperature sensor that measures the ‘die temperature’ in the range of -40˚C to +85˚C with an accuracy of ±0.8˚C.

Power Requirement

The module comes with a MIC5205 3.3V precise voltage regulator and voltage level translator, so you can use it with your favorite 3.3V or 5V microcontroller without worry.

ms5611 module voltage regulator translator

The MS5611 consumes less than 1.4mA during measurements and less than 0.15µA during standby mode. This low power consumption allow the implementation in battery driven devices such as handsets, wearables or smart watches.

Digital interfaces

The MS5611 communicates via either I2C or SPI. The PS (Protocol Select) pin determines which interface is operational.

ms5611 module i2c spi protocol selection pin

Pulling the PS pin to LOW selects the SPI interface, pulling PS to HIGH selects the I2C interface. A 1K pull-up resistor on the module pulls this pin HIGH, so I2C interface is selected by default.

I2C Interface

The sensor uses the I2C interface for communication with the Arduino. It supports two separate I2C addresses: 0x77Hex and 0x76Hex. This allows two MS5611 modules to be used on the same bus or to avoid address conflicts with another device on the bus.

ms5611 module i2c address selection pin

The CSB pin determines the I2C address of the module. This pin has a built-in 2.2K pull-down resistor. Therefore, when you leave the CSB pin unconnected, the default I2C address is 0x77Hex and when you connect it to VCC, the line is pulled HIGH and the I2C address becomes 0x76Hex.

SPI Interface

The sensor is capable of communicating over SPI as well! To enable the SPI interface, connect PS (Protocol Select) pin to ground.

Technical Specifications

Here are the complete specifications:

Power supply3.3V to 5.5V
Current draw~1.4mA (during measurements)
~0.15µA (during standby mode)
Pressure Measurement Range10 to 1200 mbar
Pressure Absolute Accuracy±1.5 mbar
ADC Resolution24 bit
Temperature Range-40˚C to +85˚C
Temperature Accuracy±0.8˚C

For more details, please refer below datasheet.

MS5611 Module Pinout

Now let’s have a look at the pinout.

ms5611 module pinout

Power Pins:

VCC is the power pin. You can connect it to 5V output from your Arduino.

GND is the common ground for power and logic.

SPI Logic pins:

SCL is the SPI Clock pin, its an input to the chip.

SDA is the Serial Data In (MOSI) pin, for data sent from your microcontroller to the MS5611.

CSB is the Chip Select pin. If you want to connect multiple MS5611’s to one microcontroller, have them share the SDA, SDO and SCL pins and assign each one a unique CSB pin.

SDO is the Serial Data Out (MISO) pin, for data sent from the MS5611 to your microcontroller.

PS is Protocol Select pin. Pull it LOW to enable an SPI communication.

I2C Logic pins:

SCL is also the I2C clock pin, connect to your microcontrollers I2C clock line.

SDA is also the I2C data pin, connect to your microcontrollers I2C data line.

CSB pin determines the I2C address of the module. When you leave the CSB pin unconnected, the default I2C address is 0x77Hex and when you connect it to VCC, the line is pulled HIGH and the I2C address becomes 0x76Hex.

PS is Protocol Select pin. Leave it unconnected to enable an I2C communication.

Wiring up a MS5611 Module to an Arduino

Now that we know everything about the module, we can begin hooking it up to our Arduino!

Start by connecting the VCC pin to the power supply, 3V-5V is fine. Use the same voltage that your microcontroller logic is based off of. For most Arduinos, that is 5V. For 3.3V logic devices, use 3.3V. Now connect GND to common ground.

Connect the SCL pin to the I2C clock pin and the SDA pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

The following illustration shows the wiring.

wiring ms5611 module with arduino

Once your module is connected to the Arduino it’s time to write some code!

Library Installation

To begin reading sensor data, you will need to install the MS5611 library. It is available from the Arduino library manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing ‘ms5611‘ and install the library.

ms5611 library installation

WARNING EXPERIMENTAL:

Rob Tillart also wrote MS5611_SPI (an experimental SPI version of the library) to communicate with the MS5611 via the SPI interface. But unfortunately he found that somehow selecting SPI as the protocol causes internal heating, leading to incorrect temperature readings. That’s why we will only cover the I2C interface in our tutorial.

Arduino Code – Reading Pressure and Temperature

Below is a basic Arduino sketch. Go ahead and upload it to your Arduino. You should see pressure and temperature on the serial monitor.

#include "MS5611.h"

MS5611 MS5611(0x77);

void setup() {
  Serial.begin(115200);
  while(!Serial);

  if (!MS5611.begin()) {
    Serial.println("MS5611 not found, check wiring!");
    while (1);
  }
}

void loop() {
  MS5611.read();
  Serial.print("Temperature: ");
  Serial.print(MS5611.getTemperature(), 2);
  Serial.print("\tPressure: ");
  Serial.print(MS5611.getPressure(), 2);
  Serial.println();
  delay(1000);
}

Note that you must set your serial monitor to a speed of 115200 baud to try out the sketch.

You will see a lot of data displaying pressure and temperature values. Try moving your sensor around and notice how the data changes.

ms5611 sensor arduino output

Code Explanation:

The code is quite straightforward. At the beginning, MS5611.h library is included and an object is created in the global space MS5611 MS5611(0x77). Note that the object is constructed with the I2C address as a parameter.

#include <Wire.h>

MS5611 MS5611(0x77);

In the setup, we initialize the serial communication with PC and call the begin() function.

The MS5611.begin() function initializes I2C interface and checks if the chip ID is correct. It then resets the chip using soft-reset & waits for the sensor for calibration after wake-up.

void setup() {
  Serial.begin(115200);
  while(!Serial);

  if (!MS5611.begin()) {
    Serial.println("MS5611 not found, check wiring!");
    while (1);
  }
}

In the loop, we call the MS5611.read() function to perform a reading. Once this is done we can access object’s (MS5611) methods using the dot operator.

MS5611.getTemperature() returns temperature reading.

MS5611.getPressure() returns barometric pressure reading.

void loop() {
  MS5611.read();
  Serial.print("Temperature: ");
  Serial.print(MS5611.getTemperature(), 2);
  Serial.print("\tPressure: ");
  Serial.print(MS5611.getPressure(), 2);
  Serial.println();
  delay(1000);
}

One of the easiest and inexpensive ways to add temperature sensing in your Arduino project is to use TMP36 Temperature Sensor. These sensors are fairly precise and needs no external components to work. So, with just a few connections and some Arduino code you’ll be sensing temperature in no time!

TMP36 Temperature Sensor

The TMP36 is a low voltage, precision centigrade temperature sensor manufactured by Analog Devices. It is a chip that provides a voltage output that is linearly proportional to the temperature in °C and is, therefore, very easy to use with an Arduino.

tmp36 temperature sensor

The TMP36 temperature sensor is fairly precise, never wears out, works under many environmental conditions and requires no external components to work. In addition, the TMP36 sensor does not require calibration and provides a typical accuracy of ±1°C at +25°C and ±2°C over the −40°C to +125°C temperature range.

The sensor can be powered with a 2.7V to 5.5V power supply and consumes only 50µA during active temperature conversions, providing very low self-heating (less than 0.1°C in still air). In addition, a shutdown function is provided to reduce the supply current to less than 0.5µA.

Here are the complete specifications:

Power supply2.7V to 5.5V
Current draw50µA
Temperature range-40°C to 125°C
Accuracy±2°C
Output scale factor10mV/°C
Output range0.1V (-40°C) to 1.75V (125°C)
Output at 25°C750mV

For more information, please refer below datasheet.

Working Principle

The TMP36 uses a solid-state technique to measure the temperature. It makes use of the fact that the voltage drop between the base and emitter (forward voltage – Vbe) of the Diode-connected transistor decreases at a known rate as the temperature increases. By precisely amplifying this voltage change, it is easy to generate an analog signal that is directly proportional to temperature.

relationship between forward voltage and temperature

This linear relationship between forward voltage and temperature is the reason why diode-connected transistors are used as temperature measurement devices. Essentially this is how temperature is measured, although there have been some improvements in this technique over the years. More information about this technique can be found here.

The good news is that all these complex calculations are done inside the TMP36. It just outputs a voltage that is linearly proportional to temperature.

How to Measure Temperature

The TMP36 is easy to use, just connect the left pin to power (2.7-5.5V) and the right pin to ground (assuming the flat side of the sensor is facing you). Then the middle pin will have an analog voltage that is directly proportional (linear) to the temperature in °C. This can be easily seen in the output voltage vs temperature characteristic. Note that the analog output voltage is independent of the power supply.

tmp36 temperature sensor output relationship curve

To convert the voltage to temperature, simply use the basic formula:

Temp (°C) = (Vout – 0.5) * 100

So for example, if the voltage out is 1V that means that the temperature is (1 – 0.5) * 100 = 50 °C

Want to know how this formula is derived?

Before calculating the temperature reading, we subtract 0.5V from the output voltage because the TMP36 has a 500mV offset. This offset is what gives the sensor the ability to read negative temperatures.

Now to convert this voltage in to temperature, we simply multiply it by 100 because the TMP36 has a scaling factor of 10mV/°C. As easy as ABC.

Testing the TMP36 Sensor

Testing the TMP36 is pretty easy, just connect the left pin to 2.7-5.5V power supply (Two AA batteries work great) and the right pin to ground (assuming the flat side of the sensor is facing you). Now connect your multimeter in DC voltage mode to ground and the middle pin. At the room temperature (25°C), the voltage should be about 0.75V.

Try squeezing the plastic case of the sensor gently to see a rise in temperature.

try squeezing tmp36 to see rise in temperature

Or try touching the sensor with an ice cube (in a plastic bag so your circuit doesn’t come into contact with water) and watch the temperature drop.

try touching tmp36 with ice to watch temperature drop

TMP36 Sensor Pinout

The TMP36 comes in three different form factors, but the most common type is the 3-pin TO-92 package, which looks just like a transistor. Let’s take a look at its pinout.

tmp36 temperature sensor pinout

+Vs is the power supply for the sensor which can be anywhere between 2.7V to 5.5V.

Vout pin produces an analog voltage that is directly proportional (linear) to the temperature. It should be connected to an Analog (ADC) input.

GND is a ground pin.

Connecting the TMP36 Temperature Sensor to an Arduino

Hooking up the TMP36 to an Arduino is super simple. You only need to connect three pins: two for power and one for reading the sensor value.

The sensor can be powered from 3.3 or 5V output. The positive voltage connects to ‘+Vs’ and ground connects to ‘GND‘. The middle pin ‘Vout’ is the analog signal output from the sensor and connects to the A0 analog input of an Arduino.

Below is the hookup for the experiments with the TMP36:

wiring tmp36 temperature sensor to arduino

To measure air temperature leave the sensor in the open air or attach it to an object you want to measure the temperature of, such as a hit sink.

Reading the Analog Temperature Data

As you can see in the wiring diagram above, the output of the TMP36 is connected to one of the analog inputs of the Arduino. The value of this analog input can be read with the analogRead() function.

However, the analogRead() function does not actually return the output voltage of the sensor. Instead it maps the input voltage between 0 and the ADC reference voltage (technically it is the operating voltage i.e. 5V or 3.3V unless you change it) to 10-bit integer values ​​ranging from 0 to 1023. To convert this value back to the sensor’s output voltage, use this formula:

Vout = (reading from ADC) * (5 / 1024)

This formula converts the number 0-1023 from the ADC into 0-5V

If you’re using a 3.3V Arduino, you’ll want to use this:

Vout = (reading from ADC) * (3.3 / 1024)

This formula converts the number 0-1023 from the ADC into 0-3.3V

Then, to convert volts into temperature, use this formula:

Temperature (°C) = (Vout – 0.5) * 100

Arduino Code – Simple Thermometer

The following sketch shows a quick way to read a TMP36 temperature sensor and can serve as the basis for more practical experiments and projects. It simply reads the value from the TMP36 using analog port A0 and prints the current temperature (in both °C and °F) on the serial monitor. Go ahead and upload it to your Arduino.

// Define the analog pin, the TMP36's Vout pin is connected to
#define sensorPin A0

void setup() {
  // Begin serial communication at 9600 baud rate
  Serial.begin(9600);
}

void loop() {
  // Get the voltage reading from the TMP36
  int reading = analogRead(sensorPin);

  // Convert that reading into voltage
  // Replace 5.0 with 3.3, if you are using a 3.3V Arduino
  float voltage = reading * (5.0 / 1024.0);

  // Convert the voltage into the temperature in Celsius
  float temperatureC = (voltage - 0.5) * 100;

  // Print the temperature in Celsius
  Serial.print("Temperature: ");
  Serial.print(temperatureC);
  Serial.print("\xC2\xB0"); // shows degree symbol
  Serial.print("C  |  ");
  
  // Print the temperature in Fahrenheit
  float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
  Serial.print(temperatureF);
  Serial.print("\xC2\xB0"); // shows degree symbol
  Serial.println("F");

  delay(1000); // wait a second between readings
}

You should see the following output in the serial monitor.

tmp36 arduino output

Code Explanation:

The sketch starts by defining the Arduino pin to which the sensor’s Vout pin is connected.

#define sensorPin A0

In the setup, we initialize the serial connection with the computer.

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

In the loop, we first read in the analog signal from the TMP36 using the analogRead() function.

int reading = analogRead(sensorPin);

Next, we will use the formulas we discussed earlier in the article to convert the analog reading into voltage and then into temperature.

float voltage = reading * (5.0 / 1024.0);

float temperatureC = (voltage - 0.5) * 100;

Next, the results are printed on the Serial Monitor.

Serial.print("Temperature: ");
Serial.print(temperatureC);
Serial.print("\xC2\xB0"); // shows degree symbol
Serial.print("C  |  ");

The temperature value we get is in Celsius (°C). It is converted in to Fahrenheit (°F) using a simple formula and printed on the Serial Monitor.

T(°F) = T(°C) × 9/5 + 32

float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
Serial.print(temperatureF);
Serial.print("\xC2\xB0"); // shows degree symbol
Serial.println("F");

Improving the Accuracy of the TMP36 Sensor

Because we didn’t configure the reference voltage (ARef) used for analog input (the default analog reference on 5V Arduino boards is 5 volts), the maximum resolution we get from the ADC is 5/1024 = 4.88 mV or 0.49°C.

For better results, using the 3.3v reference voltage as ARef instead of the 5V will be more precise and less noisy. With 3.3V as the reference voltage, we get a resolution of 3.3/1024 = 3.22 mV or 0.32°C.

To use the 3.3v pin as your analog reference, connect it to the AREF (Analog Reference) input like this.

arduino wiring for improving accuracy of tmp36 sensor

Also you need to make some changes to the code. I have highlighted the lines you need to add/change in the code below:

// Define the analog pin, the TMP36's Vout pin is connected to
#define sensorPin A0

// Tie ARef to 3.3V
#define aref_voltage 3.3
void setup() {
  // Begin serial communication at 9600 baud rate
  Serial.begin(9600);

  // If you want to set the aref to something other than 5v
  analogReference(EXTERNAL);}

void loop() {
  // Get the voltage reading from the TMP36
  int reading = analogRead(sensorPin);

  // Convert that reading into voltage
  float voltage = reading * (aref_voltage / 1024.0);
  // Convert the voltage into the temperature in Celsius
  float temperatureC = (voltage - 0.5) * 100;

  // Print the temperature in Celsius
  Serial.print("Temperature: ");
  Serial.print(temperatureC);
  Serial.print("\xC2\xB0"); // shows degree symbol
  Serial.print("C  |  ");
  
  // Print the temperature in Fahrenheit
  float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
  Serial.print(temperatureF);
  Serial.print("\xC2\xB0"); // shows degree symbol
  Serial.println("F");

  delay(1000); // wait a second between readings
}

You should see the following output in the serial monitor.

tmp36 arduino improved accuracy output

You can see that the accuracy can be improved a bit, but for most projects, this will not be enough.

An alternative to the TMP36 is to use a digital temperature sensor like the DS18B20 which comes in the same package. Digital temperature sensors have better noise immunity which is useful when the sensor is placed at a distance or in an electrically noisy environment.

Arduino Project – Standalone Thermometer with TMP36 and an I2C LCD

Sometimes you come up with an idea where you want to display the temperature readings in real time and show an alert when the temperature is outside the specified range. Then you’ll probably need a 16×2 character LCD instead of a serial monitor.

In this example, we’ll hook the I2C LCD up to the Arduino along with the TMP36.

Connecting the I2C LCD is quite easy as you can see in the wiring diagram below. If you’re not familiar with 16×2 character I2C LCDs, consider reading (at least skimming) below tutorial.

The following diagram shows you how to wire everything.

wiring tmp36 temperature sensor to arduino and i2c lcd

The following sketch will print the temperature values on the 16×2 character I2C LCD. The code is similar to the first example, except that the values are printed on the I2C LCD.

// Include the LiquidCrystal_I2C library
#include <LiquidCrystal_I2C.h>

// Create a new instance of the LiquidCrystal_I2C class
LiquidCrystal_I2C lcd(0x3F, 16, 2);

// Define a custom degree character
byte Degree[] = {
  B00111,
  B00101,
  B00111,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};

// Define the analog pin, the TMP36's Vout pin is connected to
#define sensorPin A0

void setup() {
  // Start the LCD and turn on the backlight
  lcd.init();
  lcd.backlight();

  // Create a custom character
  lcd.createChar(0, Degree);
}

void loop() {
  // Get the voltage reading from the TMP36
  int reading = analogRead(sensorPin);

  // Convert that reading into voltage
  // Replace 5.0 with 3.3, if you are using a 3.3V Arduino
  float voltage = reading * (5.0 / 1024.0);

  // Convert the voltage into the temperature in Celsius
  float temperatureC = (voltage - 0.5) * 100;

  // Print the temperature on the LCD;
  lcd.setCursor(0, 0);
  lcd.print("Temperature:");
  lcd.setCursor(0, 1);
  lcd.print(temperatureC, 1);
  lcd.write(0); // print the custom degree character
  lcd.print("C ");
  
  // Print the temperature in Fahrenheit
  float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
  lcd.print(temperatureF, 1);
  lcd.write(0); // print the custom degree character
  lcd.print("F ");

  delay(1000); // wait a second between readings
}

You should see the following output on the LCD:

tmp36 sensor output on i2c lcd

Since the emergence of COVID-19, non-contact infrared temperature scanners have been popping up everywhere around the world, from airports to restaurants. Maybe you are curious about these temperature scanners, or maybe you’re interested in building one. Well, in that case, the Melexis MLX90614 Module might be the best inexpensive option out there.

MLX90614 Module Hardware Overview

At the heart of the module is a high precision non-contact infrared temperature sensor from melexis – MLX90614. Unlike most temperature sensors, this sensor measures temperature without being physically touched. This can be very useful for monitoring the temperature of something moving like a spinning motor shaft or objects on a conveyor belt for example. Simply point the sensor at what you want to measure and it will detect the temperature by absorbing the emitted IR waves.

mlx90614 module hardware overview

Capabilities

The MLX90614 generates two temperature measurements: an object temperature and an ambient temperature. The object temperature is the non-contact measurement ‘observed’ from the sensor, while the ambient temperature measures the temperature on the die of the sensor. Ambient temperature can be used to calibrate the data, but what we really need comes from object temperature measurements.

Because it does not have to touch the object being measured, it can sense a wider range of temperatures than most digital sensors: object temperature measurements range from -70 to 382.2°C, while ambient temperature measurements range from -40 to 125°C. Both the ambient temperature and the object temperature have a resolution of 0.02°C with a standard accuracy of 0.5°C around room temperatures.

Built-In Optical Filter

The MLX90614 has a built-in optical filter that cuts off visible and near-infrared light, reducing their effect on measurements. It also provides immunity against ambient light and sunlight.

mlx90614 module optical filter

Power Requirement

The module comes with a 662K 3.3V precision voltage regulator and voltage level translator, so you can use it with your favorite 3.3V or 5V microcontroller without any worries.

The MLX90614 consumes less than 2mA during measurement. This low power consumption allows implementation in battery powered devices such as handheld thermal scanners.

mlx90614 module voltage regulator

Here are the complete specifications:

Object temperature-70°C to 382.2°C
Ambient temperature-40°C to 85°C
Accuracy±0.5°C (around room temperatures)
Resolution±0.2°C
Field of view90°
Supply voltage3.3 to 5.5V
Operating Current2mA

For more details, please refer below datasheet.

How Do Infrared Thermometers Work?

If you’ve ever used or seen someone use an infrared thermometer before, you may have found yourself wondering, “How is this style of measurement even possible?”

Well, Infrared thermometers like MLX90614 take advantage of the fact that any object, including humans, above absolute zero (0°K or -273°C) temperature, emits (not visible to the human eye) light in the infrared spectrum that is directly proportional to its temperature. Refer to the Stefan–Boltzmann law.

Internally, the MLX90614 is a pair of two devices: an infrared thermopile detector and an ASSP (Signal-Conditioning Application Processor). Here is the internal block diagram of the MLX90614 showing both the thermopile and the ASSP.

mlx90614 infrared thermometer working block diagram

The IR radiation emitted by an object or human is first focused by a converging (convex) lens onto a special infrared detector called a Thermopile. The thermopile senses how much infrared energy is being emitted by objects in its field-of-view (FOV), and generates an electrical signal proportional to that.

The voltage produced by the thermopile is picked up by the ASSP’s 17-bit ADC and then processed before passing to the microcontroller.

And the best part is that this whole process is achieved in a fraction of a second.

Field of View (FOV)

An IR thermometer’s field-of-view (FOV) is one of the most important metrics to be aware of.

It is determined by the angle in which the sensor is sensitive to thermal radiation. This means that the sensor will detect all objects in the field-of-view and return the average temperature of all objects in it.

mlx90614 infrared thermometer field of view

It is important that the measured object completely fills the field-of-view. Otherwise, the sensor may detect objects that are not supposed to be measured, resulting in inaccurate measurements.

mlx90614 infrared thermometer focused field of view

The field-of-view also determines the relationship between the distance from an object and the sensing area. If the sensor is near the object, its sensing area is very narrow, but gets increasingly wider as it moves farther away.

The field-of-view of the MLX90614 is cone-shaped and relatively wide: 90°. This means that for every 1cm you move away from an object, the sensing area increases by 2cm. If you are one foot 30cm (approx. 1 foot) away from an object, the sensing area will be 60cm (approx. 2 feet).

mlx90614 ir sensor fov distance relation

MLX90614 Output Interfaces

The MLX90614 supports two interfaces; though you will need one to access the other. The 2-wire SMBus interface is the primary means of communicating with the MLX90614. Once the SMBus interface is set up, you can later configure the MLX90614 to produce a PWM (pulse-width-modulated) signal representing the measured temperature.

SMBus Interface

The primary interface to the MLX90614 is the 2-wire SMBus interface which is basically the same as I2C (a slightly non-standard type of I2C called “repeated-start”) and uses the same two signals – SDA and SCL – to carry data and clock signals respectively. A master device controls the clock signal, while the data signal is controlled bi-directionally.

Every MLX90614 has a default I2C address of 0x5A. However, it can be programmed to have one of 127 I2C addresses so that you can add up to 127 devices to the same bus to get a larger temperature map.

PWM Interface

The MLX90614’s data can also be read through the PWM interface. Note that in order to use the PWM interface, the MLX90614 must first be configured over the SMBus.

Once configured, the MLX90614 outputs a continuous 10-bit PWM signal on the SDA pin that represents the measured object temperature. By default the PWM signal covers the range of -20°C to 120°C with an output resolution of 0.14°C, but this can also be adjusted via SMBus.

Thermal Relay/Thermal Switch

By configuring this range (setting minimum and maximum temperature values) the PWM output can be turned into a “Thermal Relay/Thermal Switch” signal.

So when the temperature exceeds the set threshold, the PWM pin is triggered which can be used as an interrupt source or can be used to directly control a relay. Note that the output drive capability is 25mA only.

MLX90614 Module Pinout

The MLX90614 module brings out the following connections.

mlx90614 module pinout

VCC is the power pin. You can connect it to 3.3V or 5V output from your Arduino.

GND is the ground.

SCL is the I2C clock pin, connect to your Arduino’s I2C clock line.

SDA is the I2C data pin, connect to your Arduino’s I2C data line.

Wiring up a MLX90614 Module to an Arduino

Now that we know everything about the module, we can begin hooking it up to our Arduino!

Start by connecting the VCC pin to the power supply, 5V is fine. Use the same voltage that your microcontroller logic is based off of. For most Arduinos, that is 5V. For 3.3V logic devices, use 3.3V. Now connect GND to common ground.

Connect the SCL pin to the I2C clock pin and the SDA pin to the I2C data pin on your Arduino. Note that each Arduino Board has different I2C pins which should be connected accordingly. On the Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also known as A5 (SCL) and A4 (SDA).

The following illustration shows the wiring.

wiring mlx90614 infrared thermometer module with arduino

Library Installation

There are several libraries available for the MLX90614 sensor. However in our example, we are using the Adafruit library which is very easy to use, but it only supports basic temperature measurement and not the advance features of the sensor. The library can be downloaded from within the Arduino IDE Library Manager.

To install the library navigate to the Sketch > Include Library > Manage Libraries… Wait for Library Manager to download libraries index and update list of installed libraries.

manage libraries

Filter your search by typing ‘adafruit mlx90614‘. Click on the entry, and then select Install.

mlx90614 adafruit library

Arduino Code

Below is a basic Arduino sketch that allows you to quickly test the functionality of the MLX90614. Go ahead and upload it to your Arduino. You should see the ambient and object temperature printed on the serial interface.

#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

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

	if (!mlx.begin()) {
		Serial.println("Error connecting to MLX sensor. Check wiring.");
		while (1);
	};
}

void loop() {
	Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempC());
	Serial.print("*C\tObject = "); Serial.print(mlx.readObjectTempC()); Serial.println("*C");
	Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempF());
	Serial.print("*F\tObject = "); Serial.print(mlx.readObjectTempF()); Serial.println("*F");

	Serial.println();
	delay(500);
}

Once the sketch is uploaded, open your serial monitor, setting the baud rate to 9600 bps. You should see both the ambient temperature and the object temperature begin to stream by.

mlx90614 sensor arduino code output

Try pointing the sensor at objects lying around you or pointing it at your forehead to make sure you don’t have a fever!

Note:

Electromagnetic interference can give incorrect results. So while using an infrared thermometer, make sure that your phone, microwave, WiFi router, TV or any electrical device is completely away.

Code Explanation:

The sketch starts with including the Adafruit_MLX90614 library. In that same global area, an Adafruit_MLX90614 object called mlx is defined.

#include <Adafruit_MLX90614.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

In the setup, we initialize the serial communication with PC and call the begin() function.

The begin() function initializes I2C interface. This function optionally takes a parameter (the 7-bit address of your sensor) but if left empty it assumes the address is set to the default (0x5A).

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

	if (!mlx.begin()) {
		Serial.println("Error connecting to MLX sensor. Check wiring.");
		while (1);
	};
}

In the loop, we simply print the current ambient and object temperatures using the readAmbientTempC()/mlx.readAmbientTempF() and readObjectTempC()/readObjectTempF() functions.

void loop() {
	Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempC());
	Serial.print("*C\tObject = "); Serial.print(mlx.readObjectTempC()); Serial.println("*C");
	Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempF());
	Serial.print("*F\tObject = "); Serial.print(mlx.readObjectTempF()); Serial.println("*F");

	Serial.println();
	delay(500);
}

Gone are the days when people waited in long checkout lines at the grocery store, thanks to RFID technology. With an RFID based walk-through automatic checkout solution, you can fill up your cart and walk right out the door. You no longer need to wait for someone to ring each item in your cart one by one; Now with the help of RFID tags attached to the items, every item in the cart will be detected and ringed almost instantly.

For most of our RFID based Arduino projects, the RC522 RFID reader/writer module is a great choice. It is low power, low cost, very rugged, easy to interface and extremely popular among hobbyists.

What is RFID technology and how does it work?

An RFID or radio frequency identification system consists of two main components, a tag attached to the object to be identified, and a reader that reads the tag.

A reader consists of a radio frequency module and an antenna that generates a high frequency electromagnetic field. Whereas the tag is usually a passive device (it does not have a battery). It consists of a microchip that stores and processes information, and an antenna for receiving and transmitting a signal.

How RFID Technology Works Reader Writer System Tag Communication

When the tag is brought close to the reader, the reader generates an electromagnetic field. This causes electrons to move through the tag’s antenna and subsequently powers the chip.

The chip then responds by sending its stored information back to the reader in the form of another radio signal. This is called a backscatter. The reader detects and interprets this backscatter and sends the data to a computer or microcontroller.

Hardware Overview

The RC522 RFID module based on the MFRC522 IC from NXP is one of the cheapest RFID options you can get online for less than four dollars. It usually comes with an RFID card tag and a key fob tag with 1KB of memory. And the best part is that it can write a tag that means you can store any message in it.

RC522 RFID Reader Writer Module with Tag Card and FOB Key Tag

The RC522 RFID reader module is designed to create a 13.56MHz electromagnetic field and communicate with RFID tags (ISO 14443A standard tags).

The reader can communicate with a microcontroller over a 4-pin SPI with a maximum data rate of 10 Mbps. It also supports communication over I2C and UART protocols.

The RC522 RFID module can be programmed to generate an interrupt, allowing the module to alert us when a tag approaches it, instead of constantly asking the module “Is there a card nearby?”.

The module’s operating voltage ranges from 2.5 to 3.3V, but the good news is that the logic pins are 5-volt tolerant, so we can easily connect it to an Arduino or any 5V logic microcontroller without using a logic level converter.

Technical Specifications

Here are the specifications:

Frequency Range13.56 MHz ISM Band
Host InterfaceSPI / I2C / UART
Operating Supply Voltage2.5 V to 3.3 V
Max. Operating Current13-26mA
Min. Current(Power down)10µA
Logic Inputs5V Tolerant
Read Range5 cm

For more details, please refer below datasheet.

RC522 RFID Module Pinout

The RC522 module has a total of 8 pins that connect it to the outside world. The connections are as follows:

RC522 RFID Reader Writer Module Pinout

VCC supplies power to the module. This can be anywhere from 2.5 to 3.3 volts. You can connect it to the 3.3V output from your Arduino. But remember that connecting it to the 5V pin will probably destroy your module!

RST is an input for reset and power-down. When this pin goes low the module enters power-down mode. In which the oscillator is turned off and the input pins are disconnected from the outside world. Whereas the module is reset on the rising edge of the signal.

GND is the ground pin and needs to be connected to the GND pin on the Arduino.

IRQ is an interrupt pin that alerts the microcontroller when an RFID tag is in the vicinity.

MISO / SCL / Tx pin acts as master-in-slave-out when SPI interface is enabled, as serial clock when I2C interface is enabled and as serial data output when the UART interface is enabled.

MOSI (Master Out Slave In) is the SPI input to the RC522 module.

SCK (Serial Clock) accepts the clock pulses provided by the SPI bus master i.e. Arduino.

SS / SDA / Rx pin acts as a signal input when the SPI interface is enabled, as serial data when the I2C interface is enabled and as a serial data input when the UART interface is enabled. This pin is usually marked by encasing the pin in a square so that it can be used as a reference to identify other pins.

Wiring an RC522 RFID Module to an Arduino

Now that we know everything about the module, let’s start connecting it to our Arduino!

First connect the VCC pin on the module to 3.3V and the GND pin to ground on the Arduino. Pin RST can be connected to any digital pin on the Arduino. In our case, it is connected to digital pin #5. The IRQ pin is left unconnected because the Arduino library we are going to use does not support it.

Now we are left with the pins that are used for SPI communication. Since RC522 modules require a lot of data transfer, they will give the best performance when connected to the hardware SPI pins on the microcontroller. For Arduino boards such as the UNO/Nano V3.0, these pins are digital 13 (SCK), 12 (MISO), 11 (MOSI) and 10 (SS).

If you are using a different Arduino than the boards mentioned above, please check the Arduino’s official documentation before proceeding.

The following table lists the pin connections:

RC522 ModuleArduino
VCC3.3V
GNDGND
RST5
MISO / SCL / Tx12
MOSI11
SCK13
SS / SDA / Rx10

The image below shows how to connect the RC522 module to the Arduino.

Arduino Wiring Fritzing Connections with RC522 RFID Reader Writer Module

Once you have connected everything you are ready to go!

Library Installation

Communicating with an RC522 RFID module is a lot of work, but luckily for us there is a library called the MFRC522 library that makes reading and writing RFID tags simple.

This library is not included in the Arduino IDE, so you will need to install it first.

To install the library navigate to Sketch > Include Libraries > Manage Libraries… Wait for Library Manager to download the library index and update the list of installed libraries.

manage libraries

Filter your search by typing ‘mfrc522‘. Look for the library by GithubCommunity. Click on that entry, and then select Install.

mfrc522 library installation

Arduino Code – Reading an RFID Tag

Once you have installed the library, open the Examples submenu and choose MFRC522 > DumpInfo example sketch.

MFRC522 Library Sketch DumpInfo

This sketch just reads the tag and displays the information stored in it. This sketch can be very handy before trying out any new tags!

Go to the beginning of the sketch and make sure RST_PIN is initialized correctly, in our case we are using digital pin #5 so change it to 5.

MFRC522 Library Sketch DumpInfo RST pin

Now upload the sketch and open Serial Monitor. As you bring the tag closer to the module, you’ll get something like the following. Do not move the tag until all the information is displayed.

MFRC522 Library DumpInfo Output

It displays all the useful information about the tag including the tag’s Unique ID (UID), memory size, and the entire 1K memory.

MIFARE Classic 1K Memory Layout

The tag’s 1K memory is organized into 16 sectors (from 0 to 15). Each sector is further divided into 4 blocks (blocks 0 to 3). And each block can store 16 bytes of data (from 0 to 15).

This of course tells us that we have :

16 sectors x 4 blocks x 16 bytes of data = 1024 bytes = 1K memory

The entire 1K of memory, along with sectors, blocks, and data, is highlighted below.

MFRC522 Library DumpInfo Memory Layout

Here is the 3D representation of the MIFARE Classic 1K memory map layout.

3D Representation MIFARE Classic 1K Memory Map Layout
3D Representation of MIFARE Classic 1K Memory Map Layout

The last block of each sector is called a Sector Trailer. It contains information called Access Bits that provide read and write access to the remaining blocks in the sector. This means that only 3 blocks of each sector (Blocks #0, #1 and #2) are actually writable, in other words only 48 bytes per sector are available for use.

Also Block #0 of Sector #0 is called Manufacturer Block which contains IC Manufacturer data and Unique Identifier (UID). The manufacturer block is highlighted in red.

MFRC522 Library DumpInfo Manufacturer Block

Warning:

Remember that overwriting the manufacturer block is very risky and can permanently lock the card.

Arduino Code – Writing an RFID Tag

Assuming that you have successfully read an RFID tag, we will move on to our next experiment. The following sketch demonstrates writing custom data to an RFID tag. Before we start a detailed analysis of it, give Sketch a try.

#include <SPI.h>      //include the SPI bus library
#include <MFRC522.h>  //include the RFID reader library

#define SS_PIN 10  //slave select pin
#define RST_PIN 5  //reset pin

MFRC522 mfrc522(SS_PIN, RST_PIN);  // instatiate a MFRC522 reader object.
MFRC522::MIFARE_Key key;          //create a MIFARE_Key struct named 'key', which will hold the card information

//this is the block number we will write into and then read.
int block=2;  

byte blockcontent[16] = {"Last-Minute-Engg"};  //an array with 16 bytes to be written into one of the 64 card blocks is defined
//byte blockcontent[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  //all zeros. This can be used to delete a block.

//This array is used for reading out a block.
byte readbackblock[18];

void setup() 
{
    Serial.begin(9600);        // Initialize serial communications with the PC
    SPI.begin();               // Init SPI bus
    mfrc522.PCD_Init();        // Init MFRC522 card (in case you wonder what PCD means: proximity coupling device)
    Serial.println("Scan a MIFARE Classic card");
  
  // Prepare the security key for the read and write functions.
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;  //keyByte is defined in the "MIFARE_Key" 'struct' definition in the .h file of the library
  }
}

void loop()
{  
  // Look for new cards
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  
  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) 
  {
    return;
  }
    Serial.println("card selected");
         
   //the blockcontent array is written into the card block
   writeBlock(block, blockcontent);
   
   //read the block back
   readBlock(block, readbackblock);
   //uncomment below line if you want to see the entire 1k memory with the block written into it.
   //mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
   
   //print the block contents
   Serial.print("read block: ");
   for (int j=0 ; j<16 ; j++)
   {
     Serial.write (readbackblock[j]);
   }
   Serial.println("");
}



//Write specific block    
int writeBlock(int blockNumber, byte arrayAddress[]) 
{
  //this makes sure that we only write into data blocks. Every 4th block is a trailer block for the access/security info.
  int largestModulo4Number=blockNumber/4*4;
  int trailerBlock=largestModulo4Number+3;//determine trailer block for the sector
  if (blockNumber > 2 && (blockNumber+1)%4 == 0){Serial.print(blockNumber);Serial.println(" is a trailer block:");return 2;}
  Serial.print(blockNumber);
  Serial.println(" is a data block:");
  
  //authentication of the desired block for access
  byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
         Serial.print("PCD_Authenticate() failed: ");
         Serial.println(mfrc522.GetStatusCodeName(status));
         return 3;//return "3" as error message
  }
  
  //writing the block 
  status = mfrc522.MIFARE_Write(blockNumber, arrayAddress, 16);
  //status = mfrc522.MIFARE_Write(9, value1Block, 16);
  if (status != MFRC522::STATUS_OK) {
           Serial.print("MIFARE_Write() failed: ");
           Serial.println(mfrc522.GetStatusCodeName(status));
           return 4;//return "4" as error message
  }
  Serial.println("block was written");
}



//Read specific block
int readBlock(int blockNumber, byte arrayAddress[]) 
{
  int largestModulo4Number=blockNumber/4*4;
  int trailerBlock=largestModulo4Number+3;//determine trailer block for the sector

  //authentication of the desired block for access
  byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));

  if (status != MFRC522::STATUS_OK) {
         Serial.print("PCD_Authenticate() failed (read): ");
         Serial.println(mfrc522.GetStatusCodeName(status));
         return 3;//return "3" as error message
  }

//reading a block
byte buffersize = 18;//we need to define a variable with the read buffer size, since the MIFARE_Read method below needs a pointer to the variable that contains the size... 
status = mfrc522.MIFARE_Read(blockNumber, arrayAddress, &buffersize);//&buffersize is a pointer to the buffersize variable; MIFARE_Read requires a pointer instead of just a number
  if (status != MFRC522::STATUS_OK) {
          Serial.print("MIFARE_read() failed: ");
          Serial.println(mfrc522.GetStatusCodeName(status));
          return 4;//return "4" as error message
  }
  Serial.println("block was read");
}

The output on the serial monitor will look something like this.

RC522 RFID Writing Tag Code Sketch Output

Code Explanation:

The sketch begins by including the MFRC522 and SPI libraries, defining the Arduino pins to which the RC522 is connected, and instantiating the MFRC522 reader object.

#include <SPI.h>//include the SPI bus library
#include <MFRC522.h>//include the RFID reader library

#define SS_PIN 10  //slave select pin
#define RST_PIN 5  //reset pin
MFRC522 mfrc522(SS_PIN, RST_PIN);        // instatiate a MFRC522 reader object.
MFRC522::MIFARE_Key key;//create a MIFARE_Key struct named 'key', which will hold the card information

After this we define a block in which we are going to store our data. Here Sector #0 Block #2 is selected. Remember to never select block #3 of any sector. Writing in a ‘Sector Trailer’ block can make the block unusable.

//this is the block number we will write into and then read.
int block=2;

Next we define an array of 16 bytes called blockcontent, which holds the message we want to write to the block. When you want to delete a block you can set blockcontent to 0.

byte blockcontent[16] = {"Last-Minute-Engg"};  //an array with 16 bytes to be written into one of the 64 card blocks is defined
//byte blockcontent[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  //all zeros. This can be used to delete a block.

Next we define an array of 18 bytes called readbackblock. We will use it to read back written content. Wait… 18 bytes? Shouldn’t it be 16 bytes? The answer is No. The MIFARE_Read method in the MFRC522 library requires a buffer at least 18 bytes long that holds 16 bytes of a block.

//This array is used for reading out a block.
byte readbackblock[18];

In setup, we initialize serial communication, the SPI library, and the MFRC522 object. We also prepare a security key for read and write operations. Here all six bytes of key are set to 0xFF.

Remember that newer cards have all six bytes of the key set to 0xFF. If you have a card that has been programmed by someone else, you will need to know the key in order to access the card. Store that key in a key variable then.

Serial.begin(9600);        // Initialize serial communications with the PC
SPI.begin();               // Init SPI bus
mfrc522.PCD_Init();        // Init MFRC522 card (in case you wonder what PCD means: proximity coupling device)
Serial.println("Scan a MIFARE Classic card");
  
// Prepare the security key for the read and write functions.
for (byte i = 0; i < 6; i++) {
   key.keyByte[i] = 0xFF;  //keyByte is defined in the "MIFARE_Key" 'struct' definition in the .h file of the library
}

In the loop, we look for the new card and select it for writing and reading purposes.

// Look for new cards
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  
  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) 
  {
    return;
  }
  Serial.println("card selected");

Writing blocks is much easier now. We just call a custom function called writeBlock() which takes two parameters – a block number to which we are interested in writing the data and the data itself.

//the blockcontent array is written into the card block
writeBlock(block, blockcontent);

To check whether the write operation was successful, we read back the block contents. For this we use a custom function called readBlock() which again takes two parameters – the block number and the array to store the block content. If you want to see entire 1k memory you can call PICC_DumpToSerial() function.

//read the block back
readBlock(block, readbackblock);
//uncomment below line if you want to see the entire 1k memory with the block written into it.
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid));

Finally we print the contents of the readbackblock array on the serial monitor using a for loop.

//print the block contents
 Serial.print("read block: ");
 for (int j=0 ; j<16 ; j++)
 {
   Serial.write (readbackblock[j]);
 }
 Serial.println("");

Arduino Project – RFID Based Door Access Control System

Let’s quickly create an Arduino project to demonstrate how a simple RC522 RFID reader module can be used to build a door access control system.

The program below scans the unique ID of each RFID tag. If the tag’s UID matches a predefined value (master tag) that is stored in the Arduino memory, access is granted. For unknown tags, access is denied. Awesome! Right?

This is how the output looks.

RC522 RFID Reader Writer Door Lock Access Control Arduino Project
Door Lock Access Control Arduino Project Output

Of course this project can be expanded to open doors, switch relays, light LEDs, or anything else you can think of.

If you’re not familiar with 16×2 character LCDs, consider reading the tutorial below.

Wiring

Before we upload the code and start scanning the tags, let’s look at the wiring for the project.

Arduino Wiring Fritzing Connections with RC522 RFID Reader & LCD
Wiring RC522 RFID Reader Writer Module with Arduino UNO & 16×2 Character LCD

Let’s try the sketch below.

#include <SPI.h>
#include <MFRC522.h>
#include <LiquidCrystal.h>

#define RST_PIN 9
#define SS_PIN 10

byte readCard[4];
String MasterTag = "20C3935E";	// REPLACE this Tag ID with your Tag ID!!!
String tagID = "";

// Create instances
MFRC522 mfrc522(SS_PIN, RST_PIN);
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); //Parameters: (rs, enable, d4, d5, d6, d7) 

void setup() 
{
  // Initiating
  SPI.begin(); // SPI bus
  mfrc522.PCD_Init(); // MFRC522
  lcd.begin(16, 2); // LCD screen

  lcd.clear();
  lcd.print(" Access Control ");
  lcd.setCursor(0, 1);
  lcd.print("Scan Your Card>>");
}

void loop() 
{
  
  //Wait until new tag is available
  while (getID()) 
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    
    if (tagID == MasterTag) 
    {
      
      lcd.print(" Access Granted!");
      // You can write any code here like opening doors, switching on a relay, lighting up an LED, or anything else you can think of.
    }
    else
    {
      lcd.print(" Access Denied!");
    }
    
      lcd.setCursor(0, 1);
      lcd.print(" ID : ");
      lcd.print(tagID);
      
    delay(2000);

    lcd.clear();
    lcd.print(" Access Control ");
    lcd.setCursor(0, 1);
    lcd.print("Scan Your Card>>");
  }
}

//Read new tag if available
boolean getID() 
{
  // Getting ready for Reading PICCs
  if ( ! mfrc522.PICC_IsNewCardPresent()) { //If a new PICC placed to RFID reader continue
  return false;
  }
  if ( ! mfrc522.PICC_ReadCardSerial()) { //Since a PICC placed get Serial and continue
  return false;
  }
  tagID = "";
  for ( uint8_t i = 0; i < 4; i++) { // The MIFARE PICCs that we use have 4 byte UID
  //readCard[i] = mfrc522.uid.uidByte[i];
  tagID.concat(String(mfrc522.uid.uidByte[i], HEX)); // Adds the 4 bytes in a single String variable
  }
  tagID.toUpperCase();
  mfrc522.PICC_HaltA(); // Stop reading
  return true;
}

Code Explanation:

The program is quite simple. In the beginning we include the required libraries, define the Arduino pins, create instances of LCD and MFRC522 objects and define the master tag.

#include <SPI.h>
#include <MFRC522.h>
#include <LiquidCrystal.h>

#define RST_PIN 9
#define SS_PIN 10

byte readCard[4];
String MasterTag = "20C3935E";	// REPLACE this Tag ID with your Tag ID!!!
String tagID = "";

// Create instances
MFRC522 mfrc522(SS_PIN, RST_PIN);
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); //Parameters: (rs, enable, d4, d5, d6, d7)

In the setup, we initialize the SPI interface, the MFRC522 object, and the LCD. After that we print the welcome message on the LCD.

void setup() 
{
  // Initiating
  SPI.begin(); // SPI bus
  mfrc522.PCD_Init(); // MFRC522
  lcd.begin(16, 2); // LCD screen

  lcd.clear();
  lcd.print(" Access Control ");
  lcd.setCursor(0, 1);
  lcd.print("Scan Your Card>>");
}

In the loop, we wait until the new tag is scanned. After scanning we compare the unknown tag ID with the master ID. If both match, access is granted else access is denied.

void loop() 
{
  
  //Wait until new tag is available
  while (getID()) 
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    
    if (tagID == MasterTag) 
    {
      
      lcd.print(" Access Granted!");
      // You can write any code here like opening doors, switching on a relay, lighting up an LED, or anything else you can think of.
    }
    else
    {
      lcd.print(" Access Denied!");
    }
    
      lcd.setCursor(0, 1);
      lcd.print(" ID : ");
      lcd.print(tagID);
      
    delay(2000);

    lcd.clear();
    lcd.print(" Access Control ");
    lcd.setCursor(0, 1);
    lcd.print("Scan Your Card>>");
  }
}

The most important code here is the getID() custom function. Once it scans the new card, inside the for loop it converts the 4 bytes of the uid into a string and concatenates to form a single string.

boolean getID() 
{
  // Getting ready for Reading PICCs
  if ( ! mfrc522.PICC_IsNewCardPresent()) { //If a new PICC placed to RFID reader continue
  return false;
  }
  if ( ! mfrc522.PICC_ReadCardSerial()) { //Since a PICC placed get Serial and continue
  return false;
  }
  tagID = "";
  for ( uint8_t i = 0; i < 4; i++) { // The MIFARE PICCs that we use have 4 byte UID
  //readCard[i] = mfrc522.uid.uidByte[i];
  tagID.concat(String(mfrc522.uid.uidByte[i], HEX)); // Adds the 4 bytes in a single String variable
  }
  tagID.toUpperCase();
  mfrc522.PICC_HaltA(); // Stop reading
  return true;
}

Color sensors provide more reliable solutions to complex automation challenges. They are used in various industries including the food and beverage, automotive and manufacturing industries for purposes such as detecting material, detecting color marks on parts, verifying steps in the manufacturing process and so on.

While expensive color sensors are used in industrial applications, inexpensive sensors such as TCS230 color sensor can be used for less stringent applications.

The TCS230 color sensor (also branded as the TCS3200) is quite popular, inexpensive and easy to use. Before we use this color sensor in our Arduino project, it would be good to see how a color sensor actually works.

How Color Sensors Work

White light is made up of three primary colors (Red, green and blue), which have different wavelengths. These colors combine with each other to form different shades of colors.

When white light falls on any surface, some wavelengths of light are absorbed and some are reflected, depending on the properties of the surface material. The color we see is a result of which wavelengths are reflected back into our eyes.

how we see colors

Now coming back to the sensor, a typical color sensor includes a high-intensity white LED that projects a modulated light onto the object. To detect the color of reflected light, almost all the color sensors consists of a grid of color-sensitive filter, also known as ‘Bayer Filter‘ and an array of photodiodes underneath, as shown in the picture below.

color sensor bayer filter and photodiodes arrangement

A single pixel is made up of 4 filters, one red, one blue, one green and one clear filter (no filter). This pattern is also known as the ‘Bayer Pattern‘. Each filter passes light of just a single color to the photodiode beneath, while the clear filter passes light as it is, as shown below. This extra light passed through the clear filter is a major advantage in low light conditions.

color sensor bayer filter working light passing through filters

The processing chip then addresses each photodiode (one color at a time), and measures the intensity of the light. As there is an array of photodiodes, the results are first averaged and then sent out for processing. By measuring the relative level of red, green and blue light, the color of the object is determined.

TCS230 Color Sensor Module

At the heart of the module is an inexpensive RGB sensor chip from Texas Advanced Optoelectronic Solutions – TCS230. The TCS230 Color Sensor is a complete color detector that can detect and measure an almost infinite range of visible colors.

tcs230 tcs3200 module hardware overview

The sensor itself can be seen at the center of the module, surrounded by the four white LEDs. The LEDs light up when the module is powered up and are used to illuminate the object being sensed. Thanks to these LEDs, the sensor can also work in complete darkness to determine the color or brightness of the object.

The TCS230 operates on a supply voltage of 2.7 to 5.5 volts and provides TTL logic-level outputs.

TCS230 Operation

The TCS230 detects color with the help of an 8 x 8 array of photodiodes, of which sixteen photodiodes have red filters, 16 photodiodes have green filters, 16 photodiodes have blue filters, and remaining 16 photodiodes are clear with no filters.

If you look closely at the sensor, you can actually see these filters.

tcs230 tcs3200 color sensor close up

Each 16 photodiodes are connected in parallel, so using two control pins S2 and S3 you can choose which of them to read. So for example, if you want to detect only red color, you can select 16 red-filtered photodiodes by setting the two pins to LOW according to the table.

Similarly, you can choose different types of photodiodes by different combinations of S2 and S3.

S2S3Photodiode type
LOWLOWRed
LOWHIGHBlue
HIGHLOWClear (No filter)
HIGHHIGHGreen

An internal current-to-frequency converter converts readings from photodiodes into a square wave whose frequency is proportional to the intensity of the chosen color. The range of the typical output frequency is 2HZ~500KHZ.

The sensor has two more control pins, S0 and S1, which are used for scaling the output frequency. The frequency can be scaled to three different preset values of 2%, 20% or 100%. This frequency-scaling function allows the sensor to be used with a variety of microcontrollers and other devices.

S0S1Output frequency scaling
LOWLOWPower down
LOWHIGH2%
HIGHLOW20%
HIGHHIGH100%

You can get different scaling factor by different combinations of S0 and S1. For the Arduino most applications use the 20% scaling.

TCS230 Color Sensor Module Pinout

The following diagram shows the pinout of a common TCS230 module.

tcs230 tcs3200 color sensor module pinout

GND is a ground pin.

OE is the Output Enable pin. This pin is rarely used and on most modules is permanently enabled. If not already enabled then pull it LOW.

S0 & S1 pins are used to select the frequency scaling.

S2 & S3 pins are used to select the color array.

OUT pin is a TTL level square wave.

VCC pin supplies power to the module. Connect it to the 2.7V to 5.5V power supply.

Wiring TCS230 Color Sensor to Arduino UNO

Hooking up the TCS 230 to an Arduino is very simple. Every pin is used except the Output Enable pin, and the module is powered safely from the 5-volt output of the Arduino.

Below is the hookup for the experiments with the TCS230:

wiring tcs230 tcs3200 color sensor module with arduino

None of the pins used on the Arduino are critical because the module does not require any pin-specific features, so if you want to use different pins you can do so safely. Just be sure to change the pin numbers in the code to reflect any changes to the wiring.

Once your sensor is connected to the Arduino it’s time to write some code!

Calibrating the Sensor

We will actually use two sketches to work with the TCS230 color sensor.

  1. The first sketch (calibration sketch) will help us to obtain the raw data from the sensor.
  2. The second sketch (main Arduino sketch) will use the raw data previously received to display RGB values for the color being sensed.

Note that both sketches will use the same hardware hookup.

Following is the calibration sketch. This sketch addresses the TCS230 sensor color-by-color and reads the pulse width of the output pin. The output is then displayed on the serial monitor.

Load the sketch to your Arduino and mount the sensor so that it is facing the objects. Start by finding a reference object for white and black color. These reference objects will produce readings at both maximum and minimum values for all three colors.

// Define color sensor pins
#define S0 4
#define S1 5
#define S2 6
#define S3 7
#define sensorOut 8

// Variables for Color Pulse Width Measurements
int redPW = 0;
int greenPW = 0;
int bluePW = 0;

void setup() {
	// Set S0 - S3 as outputs
	pinMode(S0, OUTPUT);
	pinMode(S1, OUTPUT);
	pinMode(S2, OUTPUT);
	pinMode(S3, OUTPUT);

	// Set Pulse Width scaling to 20%
	digitalWrite(S0,HIGH);
	digitalWrite(S1,LOW);

	// Set Sensor output as input
	pinMode(sensorOut, INPUT);

	// Setup Serial Monitor
	Serial.begin(9600);
}

void loop() {
	// Read Red Pulse Width
	redPW = getRedPW();
	// Delay to stabilize sensor
	delay(200);

	// Read Green Pulse Width
	greenPW = getGreenPW();
	// Delay to stabilize sensor
	delay(200);

	// Read Blue Pulse Width
	bluePW = getBluePW();
	// Delay to stabilize sensor
	delay(200);

	// Print output to Serial Monitor
	Serial.print("Red PW = ");
	Serial.print(redPW);
	Serial.print(" - Green PW = ");
	Serial.print(greenPW);
	Serial.print(" - Blue PW = ");
	Serial.println(bluePW);
}


// Function to read Red Pulse Widths
int getRedPW() {
	// Set sensor to read Red only
	digitalWrite(S2,LOW);
	digitalWrite(S3,LOW);
	// Define integer to represent Pulse Width
	int PW;
	// Read the output Pulse Width
	PW = pulseIn(sensorOut, LOW);
	// Return the value
	return PW;
}

// Function to read Green Pulse Widths
int getGreenPW() {
	// Set sensor to read Green only
	digitalWrite(S2,HIGH);
	digitalWrite(S3,HIGH);
	// Define integer to represent Pulse Width
	int PW;
	// Read the output Pulse Width
	PW = pulseIn(sensorOut, LOW);
	// Return the value
	return PW;
}

// Function to read Blue Pulse Widths
int getBluePW() {
	// Set sensor to read Blue only
	digitalWrite(S2,LOW);
	digitalWrite(S3,HIGH);
	// Define integer to represent Pulse Width
	int PW;
	// Read the output Pulse Width
	PW = pulseIn(sensorOut, LOW);
	// Return the value
	return PW;
}

Once you upload the sketch you will get such readings. Record the readings you get at both extremes.

tcs230 color sensor calibration output

Code Explanation:

The sketch begins with defining the pins used to connect the TCS230. Some variables are also defined to represent the pulse widths of the red, green and blue color array.

#define S0 4
#define S1 5
#define S2 6
#define S3 7
#define sensorOut 8

int redPW = 0;
int greenPW = 0;
int bluePW = 0;

In the setup, we define the S0-S3 pins as outputs. These pins will be used to select the frequency scaling and the color we wish to address. The S0 and S1 pins are used to set the frequency scaling to 20%, which is a common value when using this color sensor with an Arduino. Next, The sensors Output pin is defined as an input to the Arduino, this is where we will receive the square wave. Finally, we set up the serial monitor.

void setup() {
	// Set S0 - S3 as outputs
	pinMode(S0, OUTPUT);
	pinMode(S1, OUTPUT);
	pinMode(S2, OUTPUT);
	pinMode(S3, OUTPUT);

	// Set Pulse Width scaling to 20%
	digitalWrite(S0,HIGH);
	digitalWrite(S1,LOW);

	// Set Sensor output as input
	pinMode(sensorOut, INPUT);

	// Setup Serial Monitor
	Serial.begin(9600);
}

In the loop section, we call three functions getRedPW(), getGreenPW() and getBluePW() to obtain the pulse width. Let’s examine getRedPW() as an example.

The getRedPW() function gets the red pulse width. It starts by setting the S2 and S3 pins to select the red filter. This is the only step where this function differs from its green and blue counterparts.

Next, an integer is defined to store the pulse width. The pulse width is then determined using the Arduino pulseIn() function. This function measures the pulse width, note that we have configured it to measure the width of the LOW part of the pulse. The result is time in milliseconds. This value is then returned and the function terminates.

int getRedPW() {
	// Set sensor to read Red only
	digitalWrite(S2,LOW);
	digitalWrite(S3,LOW);
	// Define integer to represent Pulse Width
	int PW;
	// Read the output Pulse Width
	PW = pulseIn(sensorOut, LOW);
	// Return the value
	return PW;
}

Back in the loop, we call three functions to read the color pulse widths, adding a delay of 200ms between them to allow the sensor to stabilize. We then print the values on the serial monitor and repeat the loop.

void loop() {
	// Read Red Pulse Width
	redPW = getRedPW();
	// Delay to stabilize sensor
	delay(200);

	// Read Green Pulse Width
	greenPW = getGreenPW();
	// Delay to stabilize sensor
	delay(200);

	// Read Blue Pulse Width
	bluePW = getBluePW();
	// Delay to stabilize sensor
	delay(200);

	// Print output to Serial Monitor
	Serial.print("Red PW = ");
	Serial.print(redPW);
	Serial.print(" - Green PW = ");
	Serial.print(greenPW);
	Serial.print(" - Blue PW = ");
	Serial.println(bluePW);
}

Arduino Code – Reading RGB Values from the TCS230

Once you have taken your readings you can upload the next sketch where we will read RGB values from the TCS230 color sensor.

Before uploading the sketch, enter the six calibration values you obtained from the calibration sketch in the top of the sketch. replace the “0” with your actual values.

// Define color sensor pins
#define S0 4
#define S1 5
#define S2 6
#define S3 7
#define sensorOut 8

// Calibration Values
// *Get these from Calibration Sketch
int redMin = 0; // Red minimum value
int redMax = 0; // Red maximum value
int greenMin = 0; // Green minimum value
int greenMax = 0; // Green maximum value
int blueMin = 0; // Blue minimum value
int blueMax = 0; // Blue maximum value

// Variables for Color Pulse Width Measurements
int redPW = 0;
int greenPW = 0;
int bluePW = 0;

// Variables for final Color values
int redValue;
int greenValue;
int blueValue;

void setup() {
	// Set S0 - S3 as outputs
	pinMode(S0, OUTPUT);
	pinMode(S1, OUTPUT);
	pinMode(S2, OUTPUT);
	pinMode(S3, OUTPUT);

	// Set Sensor output as input
	pinMode(sensorOut, INPUT);

	// Set Frequency scaling to 20%
	digitalWrite(S0,HIGH);
	digitalWrite(S1,LOW);

	// Setup Serial Monitor
	Serial.begin(9600);
}

void loop() {
	// Read Red value
	redPW = getRedPW();
	// Map to value from 0-255
	redValue = map(redPW, redMin,redMax,255,0);
	// Delay to stabilize sensor
	delay(200);

	// Read Green value
	greenPW = getGreenPW();
	// Map to value from 0-255
	greenValue = map(greenPW, greenMin,greenMax,255,0);
	// Delay to stabilize sensor
	delay(200);

	// Read Blue value
	bluePW = getBluePW();
	// Map to value from 0-255
	blueValue = map(bluePW, blueMin,blueMax,255,0);
	// Delay to stabilize sensor
	delay(200);

	// Print output to Serial Monitor
	Serial.print("Red = ");
	Serial.print(redValue);
	Serial.print(" - Green = ");
	Serial.print(greenValue);
	Serial.print(" - Blue = ");
	Serial.println(blueValue);
}


// Function to read Red Pulse Widths
int getRedPW() {
	// Set sensor to read Red only
	digitalWrite(S2,LOW);
	digitalWrite(S3,LOW);
	// Define integer to represent Pulse Width
	int PW;
	// Read the output Pulse Width
	PW = pulseIn(sensorOut, LOW);
	// Return the value
	return PW;
}

// Function to read Green Pulse Widths
int getGreenPW() {
	// Set sensor to read Green only
	digitalWrite(S2,HIGH);
	digitalWrite(S3,HIGH);
	// Define integer to represent Pulse Width
	int PW;
	// Read the output Pulse Width
	PW = pulseIn(sensorOut, LOW);
	// Return the value
	return PW;
}

// Function to read Blue Pulse Widths
int getBluePW() {
	// Set sensor to read Blue only
	digitalWrite(S2,LOW);
	digitalWrite(S3,HIGH);
	// Define integer to represent Pulse Width
	int PW;
	// Read the output Pulse Width
	PW = pulseIn(sensorOut, LOW);
	// Return the value
	return PW;
}

Load the sketch and observe the results with samples of different colors. You can make minor adjustments to calibration values if necessary.

Code Explanation

You will notice that the majority of this sketch is exactly the same as the previous sketch, except:

The six calibration values you obtained from the calibration sketch are entered in the top of the sketch.

// Calibration Values
int redMin = 0; // Red minimum value
int redMax = 0; // Red maximum value
int greenMin = 0; // Green minimum value
int greenMax = 0; // Green maximum value
int blueMin = 0; // Blue minimum value
int blueMax = 0; // Blue maximum value

Three new variables are defined for the RGB values we want to output.

int redValue;
int greenValue;
int blueValue;

In the loop section, we read each of the values ​​using the same function used in the previous sketch. Then we use the Arduino map() function to convert these values ​​into RGB values, ​​using our calibration values ​​as a reference.

Note that we have reversed the range (Min value is mapped to 255 amd Max value is mapped to 0) because our functions return pulse width, not frequency.

// Read Red value
redPW = getRedPW();
// Map to value from 0-255
redValue = map(redPW, redMin,redMax,255,0);
// Delay to stabilize sensor
delay(200);

Finally, we output the values on the serial monitor. These final readings will correspond to the RGB values of the item being scanned.

Have you ever wondered how your smartphone knows which way is up? It’s one of the most innovative features of today’s smartphones. They all have a tiny device called an accelerometer built into the circuitry that detects when you tilt the device from side to side. That’s how your smartphone knows when to switch from portrait to landscape mode.

Accelerometers are widely used in low-power, low-cost motion and tilt sensing applications such as mobile devices, gaming systems, disk drive protection, image stabilization, and sports and health devices.

Let’s go over what they are, what they do, and how they work.

How Does an Accelerometer Work?

To understand how accelerometers work, imagine a ball inside a 3D cube.

Accelerometer Working Illustration - Weightless State

Assuming that the cube is in outer space, where everything is weightless, the ball will simply float in the center of the cube.

Now assume that each wall represents a specific axis.

If we suddenly move the box to the left with acceleration 1g (a single G-force 1g is equivalent to gravitational acceleration 9.8 m/s2), the ball will undoubtedly hit the wall X. If we measure the force the ball exerts on wall X, we can obtain an output value of 1g along the X axis.

Accelerometer Working Illustration - Sudden Movement

Let’s see what happens when we place that cube on Earth. The ball will simply fall on the wall Z, exerting a force of 1g as shown in the diagram below:

Accelerometer Working Illustration - Gravitation Force

In this case, the box isn’t moving, but we still get a 1g reading on the Z axis. This is because gravity (which is actually a form of acceleration) is pulling the ball downward with a force of 1g.

While this model does not exactly represent how a real-world accelerometer sensor is built, it is often useful in understanding why an accelerometer’s output signal is typically specified in ±g, or why an accelerometer reads 1g in the z-axis at rest, or what accelerometer readings you can expect at different orientations.

In the real world, accelerometers are based on Micro-Electro-Mechanical Systems (MEMS fabrication technology). So, let’s find out how a MEMS accelerometer works.

How Does a MEMS Accelerometer Work?

A MEMS (Micro-Electro-Mechanical System) accelerometer is a micro-machined structure built on top of a silicon wafer.

mems accelerometer working
accelerometer animation labels

This structure is suspended by polysilicon springs. It allows the structure to deflect when accelerated along the X, Y, and/or Z axes.

As a result of deflection, the capacitance between fixed plates and plates attached to the suspended structure changes. This change in capacitance is proportional to the acceleration along that axis.

The sensor processes this change in capacitance and converts it into an analog output voltage.

ADXL335 Module Hardware Overview

At the core of the module is a small, low-power, low-noise triple axis MEMS accelerometer from Analog Devices – ADXL335. It can measure not only static acceleration caused by gravity, but also dynamic acceleration caused by motion, shock, or vibration.

ADXL335 Accelerometer Module Hardware Overview

This breadboard-friendly module breaks out every pin of the ADXL335 to a 6-pin, 0.1′′ pitch header, including 3 analog outputs for X, Y, and Z axis measurements, 2 supply pins, and a self-test pin.

Power

The ADXL335 operates on 1.8V to 3.6VDC (typically 3.3V). However, the on-board 3.3V regulator makes it ideal for interfacing with 5V microcontrollers like the Arduino.

The sensor consumes only 350μA of current during normal operation.

Measurement Range

The ADXL335 has a full sensing range of ±3g. Meaning the maximum amount of acceleration that the ADXL335 can accurately measure and represent as an output is ±3g. If it is accelerated at 4g, for example, the accelerometer will not break, but the output may rail.

The absolute maximum acceleration of the ADXL335 is 10,000g. When subjected to accelerations greater than 10,000g, the ADXL335 may fail.

Ratiometric Output

The ADXL335 output is ratiometric, therefore, the output voltage increases linearly with acceleration over the range. This means that the 0g measurement output is always at half of the 3.3V supply voltage (1.65V), -3g is at 0v, and +3g is at 3.3V, with full scaling in between.

Technical Specifications

Here are the specifications.

Operating Voltage1.8V – 3.6V
Operating Current350μA (typical)
Sensing Range±3g (Full Scale)
Temperature Range−40 to +85°C
Sensing axis3 axis
Sensitivity270 to 330mV/g (Ratiometric)
Shock ResistanceUp to 10,000g
Dimension4mm x 4mm x 1.45mm

For more information, please refer to the datasheet below.

ADXL335 Accelerometer Pinout

Before we get into the hookup and example code, let’s take a look at its Pinout.

ADXL335 Accelerometer Module Pinout

VCC supplies power to the module. Connect it to the 5V output of your Arduino.

X-Out outputs an analog voltage proportional to acceleration along the X axis.

Y-Out outputs an analog voltage proportional to acceleration along the Y axis.

Z-Out outputs analog voltage proportional to acceleration along the Z axis.

GND is the ground pin.

ST(Self-Test) pin controls the self-test feature that allows you to test the sensor’s functionality in the final application. This feature is discussed in depth at the end of the tutorial.

Wiring an ADXL335 Accelerometer to an Arduino

Now that we know how the ADXL335 accelerometer works, we can move on to hooking it up to our Arduino.

Connections are pretty simple. Begin by mounting the accelerometer on the breadboard. Connect the VCC pin to the Arduino’s 5V pin and the GND pin to the Arduino’s Ground pin. Connect the X, Y, and Z outputs to Arduino’s analog pins A0, A1, and A2.

To get accurate results, we need to change the analog reference (AREF) voltage on the Arduino. This is accomplished by connecting the Arduino’s 3.3V pin to the AREF pin.

The following image shows the wiring.

Wiring ADXL335 Accelerometer Module to Arduino UNO

Arduino Example Code – Reading the ADXL335 accelerometer

The sketch is quite simple. It simply displays the calibrated sensor output on each axis on the serial interface. Try out the sketch before we get into the details.

const int xInput = A0;
const int yInput = A1;
const int zInput = A2;

// initialize minimum and maximum Raw Ranges for each axis
int RawMin = 0;
int RawMax = 1023;

// Take multiple samples to reduce noise
const int sampleSize = 10;

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

void loop() 
{
	//Read raw values
	int xRaw = ReadAxis(xInput);
	int yRaw = ReadAxis(yInput);
	int zRaw = ReadAxis(zInput);

	// Convert raw values to 'milli-Gs"
	long xScaled = map(xRaw, RawMin, RawMax, -3000, 3000);
	long yScaled = map(yRaw, RawMin, RawMax, -3000, 3000);
	long zScaled = map(zRaw, RawMin, RawMax, -3000, 3000);

	// re-scale to fractional Gs
	float xAccel = xScaled / 1000.0;
	float yAccel = yScaled / 1000.0;
	float zAccel = zScaled / 1000.0;

	Serial.print("X, Y, Z  :: ");
	Serial.print(xRaw);
	Serial.print(", ");
	Serial.print(yRaw);
	Serial.print(", ");
	Serial.print(zRaw);
	Serial.print(" :: ");
	Serial.print(xAccel,0);
	Serial.print("G, ");
	Serial.print(yAccel,0);
	Serial.print("G, ");
	Serial.print(zAccel,0);
	Serial.println("G");

	delay(200);
}

// Take samples and return the average
int ReadAxis(int axisPin)
{
	long reading = 0;
	analogRead(axisPin);
	delay(1);
	for (int i = 0; i < sampleSize; i++)
	{
	reading += analogRead(axisPin);
	}
	return reading/sampleSize;
}

The images below show the accelerometer readings at various orientations.

ADXL335 Accelerometer Output on +X axis
ADXL335 Accelerometer Output on -Y axis
ADXL335 Accelerometer Output on -X axis
ADXL335 Accelerometer Output on +Y axis
ADXL335 Accelerometer Output on -Z axis
ADXL335 Accelerometer Output on +Z axis

Code Explanation:

The sketch begins by declaring Arduino’s analog input pins to which the sensor’s X, Y, and Z output pins are connected.

const int xInput = A0;
const int yInput = A1;
const int zInput = A2;

Next, two variables, RawMin and RawMax, are defined. Because the Arduino has a 10-bit ADC (210 = 1024), it will map the output voltages of the ADXL335, which range from 0 to 3.3 volts, into integer values between 0 and 1023. That is why RawMin is set to 0 and RawMax is set to 1023.

// initialize minimum and maximum Raw Ranges for each axis
int RawMin = 0;
int RawMax = 1023;

The sampleSize variable specifies the number of samples that should be taken by the Arduino during each conversion. In our case, we set sampleSize to 10 to achieve more accurate results.

// Take multiple samples to reduce noise
const int sampleSize = 10;

In the setup section, we first set the analog reference to EXTERNAL by calling analogReference(EXTERNAL). We then initiate serial communications with the PC.

Warning:

This is required since we have connected 3.3V to the AREF pin of the Arduino. If you fail to call analogReference(EXTERNAL), you will short together the internally generated active reference voltage and the AREF pin, potentially causing damage to your Arduino.

You can learn more about the analogReference() function on Arduino’s official website.

analogReference(EXTERNAL);
Serial.begin(9600);

In the loop section, we read analog outputs from the sensor every 200ms. Note that we are calling the ReadAxis() custom function instead of the analogRead() function. This function simply takes ten ADC conversion samples and returns the average.

//Read raw values
int xRaw = ReadAxis(xInput);
int yRaw = ReadAxis(yInput);
int zRaw = ReadAxis(zInput);

Converting ADXL335 Output to Acceleration(g)

The following code snippet is the most important part of the program. It maps and converts the analog output voltages from the sensor to gravitational acceleration(G).

The mapping is handled by the IDE’s built-in map() function. When we call map(xRaw, RawMin, RawMax, -3000, 3000), the value of RawMin is mapped to -3000, the value of RawMax is mapped to 3000, and values in-between are mapped to values in-between.

The numbers -3000 and 3000 are not random. They actually represent the gravitational acceleration measured by the sensor in milli-g i.e. ±3g (-3000 milli-g to 3000 milli-g).

For example,

  • If the sensor outputs 0 volts on x-axis, that is xRaw=0, the map() function will return -3000, which corresponds to -3g.
  • If the sensor outputs 1.65 volts on x-axis, that is xRaw=511, the map() function will return 0, which corresponds to 0g.
  • If the sensor outputs 3.3 volts on x-axis, that is xRaw=1023, the map() function will return 3000, which corresponds to +3g.

The term Ratiometric makes more sense now that the output voltage increases linearly with acceleration across the range.

// Convert raw values to 'milli-Gs"
long xScaled = map(xRaw, RawMin, RawMax, -3000, 3000);
long yScaled = map(yRaw, RawMin, RawMax, -3000, 3000);
long zScaled = map(zRaw, RawMin, RawMax, -3000, 3000);

Finally, the sensor’s output is scaled down to fractional Gs by dividing it by 1000 and displayed on the serial monitor.

// re-scale to fractional Gs
float xAccel = xScaled / 1000.0;
float yAccel = yScaled / 1000.0;
float zAccel = zScaled / 1000.0;

Serial.print("X, Y, Z  :: ");
Serial.print(xRaw);
Serial.print(", ");
Serial.print(yRaw);
Serial.print(", ");
Serial.print(zRaw);
Serial.print(" :: ");
Serial.print(xAccel,0);
Serial.print("G, ");
Serial.print(yAccel,0);
Serial.print("G, ");
Serial.print(zAccel,0);
Serial.println("G");

ADXL335 Self Test Feature

The ADXL335 Accelerometer has a self-test feature that allows you to test the sensor’s functionality in the final application. ST(self-test) pin on the module controls this feature.

ADXL335 Module SelfTest Pin

When the ST pin is connected to 3.3V, an electrostatic force is exerted on the accelerometer beam internally. The resulting movement of the beam allows the user to determine whether or not the accelerometer is functional.

The expected change in output is

  • −1.08 g (−325 mV) on the X-axis
  • +1.08 g (+325 mV) on the Y-axis
  • +1.83 g (+550 mV) on the Z-axis

The ST pin may be left open or connected to GND during normal operation.

Warning:

Exposing the ST pin to voltages higher than 3.6V may permanently damage the accelerometer.

As kids, the gyroscopes at the science fair never failed to amaze us because they moved in strange ways and even seemed to defy gravity. Their unique properties make them crucial in everything from small RC helicopters to the advanced navigation system on the space shuttle.

In recent years, ingenious engineers have successfully developed micromachined gyroscopes. These MEMS (microelectromechanical system) gyroscopes have paved the way for an entirely new set of innovative applications, including gesture recognition, enhanced gaming, augmented reality, panoramic photo capture, vehicle navigation, and fitness monitoring, to name a few.

Without a doubt, the gyroscope and accelerometer are each exceptional in their own way. However, when we put them together, we can obtain incredibly precise data about an object’s orientation. This is where the MPU6050 enters the picture. The MPU6050 includes a gyroscope and an accelerometer, allowing us to measure rotation along all three axes, static acceleration due to gravity, and dynamic acceleration due to motion.

Before we use the MPU6050 in our Arduino project, it’s a good idea to understand how accelerometers and gyroscopes work.

Did you know?

The Russian Mir space station relied on 11 gyroscopes to keep its orientation to the sun. The Hubble Space Telescope is equipped with six navigational gyros that ensure accurate pointing during observations.

How Does an Accelerometer Work?

To understand how accelerometers work, imagine a ball inside a 3D cube.

Accelerometer Working Illustration - Weightless State

Assuming that the cube is in outer space, where everything is weightless, the ball will simply float in the center of the cube.

Now, assume that each wall represents a specific axis.

If we suddenly move the box to the left with acceleration 1g (a single G-force 1g is equivalent to gravitational acceleration 9.8 m/s2), the ball will undoubtedly hit the wall X. If we measure the force the ball exerts on wall X, we can obtain an output value of 1g along the X axis.

Accelerometer Working Illustration - Sudden Movement

Let’s see what happens when we place that cube on Earth. The ball will simply fall on the wall Z, exerting a force of 1g as shown in the diagram below:

Accelerometer Working Illustration - Gravitation Force

In this case, the box isn’t moving, but we still get a 1g reading on the Z axis. This is because gravity (which is actually a form of acceleration) is pulling the ball downward with a force of 1g.

While this model does not exactly represent how a real-world accelerometer sensor is built, it is often useful in understanding why an accelerometer’s output signal is typically specified in ±g, or why an accelerometer reads 1g in the z-axis at rest, or what accelerometer readings you can expect at different orientations.

In the real world, accelerometers are based on Micro-Electro-Mechanical Systems (MEMS fabrication technology). So, let’s find out how a MEMS accelerometer works.

How Does a MEMS Accelerometer Work?

A MEMS (Micro-Electro-Mechanical System) accelerometer is a micro-machined structure built on top of a silicon wafer.

mems accelerometer working
accelerometer animation labels

This structure is suspended by polysilicon springs. It allows the structure to deflect when accelerated along the X, Y, and/or Z axes.

As a result of deflection, the capacitance between fixed plates and plates attached to the suspended structure changes. This change in capacitance is proportional to the acceleration along that axis.

The sensor processes this change in capacitance and converts it into an analog output voltage.

How Does a Gyroscope Work?

While accelerometers measure linear acceleration, gyroscopes measure angular rotation. To accomplish this, they measure the force generated by the Coriolis Effect.

Coriolis Effect

The Coriolis Effect states that when a mass (m) moves in a specific direction with a velocity (v) and an external angular rate (Ω) is applied (Red arrow), the Coriolis Effect generates a force (Yellow arrow) that causes the mass to move perpendicularly. The value of this displacement is directly related to the angular rate applied.

coriolis force on a mass

Consider two masses oscillating in opposite directions at a constant frequency. When an angular rate is applied, the Coriolis effect produced by each mass is in opposite directions, resulting in a proportional change in capacitance between the masses. By measuring this change in capacitance, the angular rate can be calculated.

coriolis force on two masses

How Does a MEMS Gyroscope Work?

The MEMS sensor consists of a proof mass (consisting of four parts M1, M2, M3, and M4) that is maintained in a continuous oscillating movement so that it can respond to the coriolis effect. They simultaneously move inward and outward in the horizontal plane.

mpu6050 accel gyro working drive mode output

When we begin to rotate the structure, the Coriolis force acting on the moving proof mass causes the vibration to change from horizontal to vertical.

There are three modes depending on the axis along which the angular rotation is applied.

Roll Mode:

When an angular rate is applied along the X-axis, M1 and M3 will move up and down out of the plane due to the coriolis effect. This causes a change in the roll angle, hence the name Roll Mode.

mpu6050 accel gyro working roll mode output

Pitch Mode:

When an angular rate is applied along the Y-axis, M2 and M4 will move up and down out of the plane. This causes a change in the pitch angle, hence the name Pitch Mode.

mpu6050 accel gyro working pitch mode output

Yaw Mode:

When an angular rate is applied along the Z-axis, M2 and M4 will move horizontally in opposite directions. This causes a change in the yaw angle, hence the name Yaw Mode.

mpu6050 accel gyro working yaw mode output

Whenever the coriolis effect is detected, the constant motion of the driving mass will cause a change in capacitance (∆C) that is detected by the sensing structure and converted into a voltage signal.

For your information, this is what the MEMS structure die of a 3-axis digital gyroscope looks like. Thanks to Adam McCombs for sharing this image of a decaped L3GD20HTR MEMS gyroscope.

decaped l3gd20htr mems gyroscope
Credit: Adam McCombs

MPU6050 Module Hardware Overview

At the core of the module is a low-power, low-cost 6-axis MotionTracking chip – MPU6050 – that integrates a 3-axis gyroscope, 3-axis accelerometer, and a Digital Motion Processor (DMP) into a tiny 4mm x 4mm package.

It can measure angular momentum or rotation along all three axes, static acceleration caused by gravity, and dynamic acceleration caused by motion, shock, or vibration.

mpu6050 module hardware overview

The module includes an on-board LD3985 3.3V regulator, so you can safely use it with a 5V logic microcontroller like Arduino.

The MPU6050 consumes less than 3.6mA during measurements and only 5μA when idle. Because of its low power consumption, it can be used in battery-powered devices.

Additionally, the module has a Power LED that illuminates when the module is powered on.

Measuring Acceleration

The MPU6050 has an on-chip accelerometer that can measure acceleration over four programmable full scale ranges of ±2g, ±4g, ±8g, and ±16g.

mpu6050 module accelerometer axis

The MPU6050 is equipped with three 16-bit analog-to-digital converters that simultaneously sample the three axes of movement (along the X, Y, and Z axes).

Measuring Rotation

The MPU6050 has an on-chip gyroscope that can measure angular rotation over four programmable full scale ranges of ±250°/s, ±500°/s, ±1000°/s, and ±2000°/s.

mpu6050 module gyroscope axis

The MPU6050 is equipped with three more 16-bit analog-to-digital converters that simultaneously sample the three axes of rotation (along the X, Y, and Z axes). The sampling rate can be adjusted from 3.9 to 8000 samples per second.

Measuring Temperature

The MPU6050 includes an embedded temperature sensor that can measure temperatures from -40 to 85°C with a ±1°C accuracy.

Note that this temperature measurement is of the silicon die itself, not the ambient temperature. These measurements are typically used to compensate for accelerometer and gyroscope calibration or to detect temperature changes rather than measuring absolute temperatures.

The I2C Interface

The module communicates with the Arduino via the I2C interface. It supports two different I2C addresses: 0x68HEX and 0x69HEX. This allows two MPU6050s to be used on the same bus or to avoid address conflicts with other devices on the bus.

mpu6050 module i2c address selection pin

The ADO pin determines the I2C address of the module. This pin is pulled down with a 4.7K resistor. Therefore, when you leave the ADO pin unconnected, the default I2C address is 0x68HEX; when you connect it to 3.3V, the line is pulled HIGH, and the I2C address becomes 0x69HEX.

Adding External Sensors

You can improve the accuracy of the MPU6050 module even further by connecting external sensors to it. These external sensors can be connected to the MPU6050 via a second, completely independent I2C bus (XDA and XCL).

mpu6050 module external i2c pins

This external connection is usually used to attach a magnetometer, which can measure magnetic fields along three axes. The MPU6050 has six Degrees of Freedom (DOF), three for the accelerometer and three for the gyroscope combined. The addition of a magnetometer increases the sensor’s degree of freedom from 6 to 9 DOF.

Technical Specifications

Here are the specifications:

Operating Voltage5V (typical)
Accelerometer Range±2g, ±4g, ±8g, ±16g
Gyroscope Range±250°/s, ±500°/s, ±1000°/s, ±2000°/s
Temperature Range-40 to +85°C
Absolute Maximum AccelerationUp to 10,000g

For more information, please refer to the datasheet below.

MPU6050 Module Pinout

The MPU6050 module’s pinout is as follows:

mpu6050 3 axis accelerometer gyroscope module pinout

VCC supplies power to the module.

GND is the ground pin.

SCL is a serial clock pin for the I2C interface.

SDA is a serial data pin for the I2C interface.

XDA is the external I2C data line. The external I2C bus is for connecting external sensors, such as a magnetometer.

XCL is the external I2C clock line.

AD0 allows you to change the I2C address of the MPU6050 module. It can be used to avoid conflicts between the module and other I2C devices or to connect two MPU6050s to the same I2C bus. When you leave the ADO pin unconnected, the default I2C address is 0x68HEX; when you connect it to 3.3V, the I2C address changes to 0x69HEX.

INT is the Interrupt Output pin. The MPU6050 can be programmed to generate an interrupt upon detection of gestures, panning, zooming, scrolling, tap detection, and shake detection.

Wiring an MPU6050 Module to an Arduino

Let’s hook the MPU6050 module to the Arduino.

Connections are straightforward. Begin by connecting the VCC pin to the Arduino’s 5V output and the GND pin to ground.

Now we are left with the pins that are used for I2C communication. Note that each Arduino board has different I2C pins that must be connected correctly.  On Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also referred to as A5 (SCL) and A4 (SDA).

Check out the table below for quick reference.

SCLSDA
Arduino UnoA5A4
Arduino NanoA5A4
Arduino Mega2120
Leonardo/Micro32

The diagram below shows how to connect everything.

wiring mpu6050 accel gyro module with arduino

Library Installation

Setting up the MPU6050 module to begin capturing the device’s raw data output is fairly simple. Manipulating the data into something meaningful, on the other hand, is more difficult, but there are some libraries at our disposal.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the library index and update the list of installed libraries.

manage libraries

Filter your search by entering ‘mpu6050’. Look for the Adafruit MPU6050 Library by Adafruit. Click on that entry and then choose Install.

adafruit mpu6050 library installation

The Adafruit MPU6050 library makes use of the Adafruit Unified Sensor Driver and Adafruit Bus IO Library internally. So, search the library manager for Adafruit Unified Sensor and BusIO and install them as well.

adafruit unified sensor library installation
adafruit busio library installation

Arduino Example Code

Here is a simple program that reads the linear acceleration, angular rotation, and temperature from the MPU6050 module and prints them on the serial monitor.

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;

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

	// Try to initialize!
	if (!mpu.begin()) {
		Serial.println("Failed to find MPU6050 chip");
		while (1) {
		  delay(10);
		}
	}
	Serial.println("MPU6050 Found!");

	// set accelerometer range to +-8G
	mpu.setAccelerometerRange(MPU6050_RANGE_8_G);

	// set gyro range to +- 500 deg/s
	mpu.setGyroRange(MPU6050_RANGE_500_DEG);

	// set filter bandwidth to 21 Hz
	mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

	delay(100);
}

void loop() {
	/* Get new sensor events with the readings */
	sensors_event_t a, g, temp;
	mpu.getEvent(&a, &g, &temp);

	/* Print out the values */
	Serial.print("Acceleration X: ");
	Serial.print(a.acceleration.x);
	Serial.print(", Y: ");
	Serial.print(a.acceleration.y);
	Serial.print(", Z: ");
	Serial.print(a.acceleration.z);
	Serial.println(" m/s^2");

	Serial.print("Rotation X: ");
	Serial.print(g.gyro.x);
	Serial.print(", Y: ");
	Serial.print(g.gyro.y);
	Serial.print(", Z: ");
	Serial.print(g.gyro.z);
	Serial.println(" rad/s");

	Serial.print("Temperature: ");
	Serial.print(temp.temperature);
	Serial.println(" degC");

	Serial.println("");
	delay(500);
}

Make sure you set the baud rate to “115200” in the serial port monitor. Because the MPU6050 returns an excessive amount of data, this higher speed is required to display it.

There will be a lot of information displayed, such as linear acceleration, angular rotation, and temperature. Move your sensor around and observe how the data changes.

mpu6050 arduino code acceleration gyro temperature output

Code Explanation:

At the beginning of the sketch, all the necessary libraries are included. As previously stated, the Adafruit_MPU6050 library implements the hardware functions of the MPU6050, while the Adafruit_Sensor library implements the unified sensor abstraction layer. Wire.h, which allows us to communicate with I2C devices, is also included.

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Next, an instance of the Adafruit_MPU6050 class is created in order to access its associated methods.

Adafruit_MPU6050 mpu;

In the setup section of the code, we first initialize the serial communication with the PC and call the begin() function. The begin() function initializes the I2C interface and verifies that the chip ID is correct. It then soft-resets the chip and waits for the sensor to calibrate after wake-up.

Serial.begin(115200);

// Try to initialize!
if (!mpu.begin()) {
	Serial.println("Failed to find MPU6050 chip");
	while (1) {
	  delay(10);
	}
}

The following three functions are then used to configure the measurement range of the MPU6050.

setAccelerometerRange(mpu6050_accel_range_t)

The setAccelerometerRange() function sets the accelerometer measurement range. This function accepts the following values:

  • MPU6050_RANGE_2_G – for ±2g range (default)
  • MPU6050_RANGE_4_G – for ±4g range
  • MPU6050_RANGE_8_G – for ±8g range
  • MPU6050_RANGE_16_G – for ±16g range

Keep in mind that the smaller the range, the more sensitive the accelerometer readings will be.

setGyroRange(mpu6050_gyro_range_t)

The setGyroRange() function sets the gyroscope measurement range. This function accepts the following values:

  • MPU6050_RANGE_250_DEG – for 250 degrees-per-second range (default)
  • MPU6050_RANGE_500_DEG – for 500 degrees-per-second range
  • MPU6050_RANGE_1000_DEG – for 1000 degrees-per-second range
  • MPU6050_RANGE_2000_DEG – for 2000 degrees-per-second range

Remember that a smaller degrees-per-second range leads to a more sensitive output.

setFilterBandwidth(mpu6050_bandwidth_t)

The setFilterBandwidth() function sets the bandwidth of the Digital Low-Pass Filter. This function accepts the following values:

  • MPU6050_BAND_260_HZ – for 260 Hz bandwidth (According to the documentation, this disables the filter)
  • MPU6050_BAND_184_HZ – for 184 Hz bandwidth
  • MPU6050_BAND_94_HZ – for 94 Hz bandwidth
  • MPU6050_BAND_44_HZ – for 44 Hz bandwidth
  • MPU6050_BAND_21_HZ – for 21 Hz bandwidth
  • MPU6050_BAND_10_HZ – for 10 Hz bandwidth
  • MPU6050_BAND_5_HZ – for 5 Hz bandwidth

The bandwidth selection allows you to alter the low-pass filter’s cutoff frequency, allowing you to smooth out the signal by removing high-frequency noise.

In this example, we set the accelerometer range to ±8G, the gyro range to ±500°/s, and the filter bandwidth to 21 Hz.

mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

The measurement range is the maximum acceleration or angular velocity that your MPU6050 can read. Think about what you are measuring and set limits based on that. Do you need to measure the rotational speed of a record player (which is extremely slow) or a spinning wheel (which can be extremely fast)?

In the loop section of the code, we create three objects of type sensors_event_t to hold our results. sensors_event_t is simply a user-defined datatype (structures in C) that stores a variety of sensor data such as acceleration, gyro, temperature, light, pressure, and many others. More information is available on github.

sensors_event_t a, g, temp;

The function getEvent() is then called. This function reads a new set of values from the sensor (a sensor “event”), converts them to the correct SI units and scale, and then assigns the results to our mpu object.

mpu.getEvent(&a, &g, &temp);

Finally, the values are displayed on the serial monitor.

Serial.print("Acceleration X: ");
Serial.print(a.acceleration.x);
Serial.print(", Y: ");
Serial.print(a.acceleration.y);
Serial.print(", Z: ");
Serial.print(a.acceleration.z);
Serial.println(" m/s^2");

Serial.print("Rotation X: ");
Serial.print(g.gyro.x);
Serial.print(", Y: ");
Serial.print(g.gyro.y);
Serial.print(", Z: ");
Serial.print(g.gyro.z);
Serial.println(" rad/s");

Serial.print("Temperature: ");
Serial.print(temp.temperature);
Serial.println(" degC");

Arduino Example Code – Plotting MPU6050 data

Simply looking at the raw data from the MPU6050 will not help. Use a Serial Plotter if you really want to see how your MPU6050 reacts when you move it around.

The Arduino IDE includes a useful tool called the serial plotter. It can provide real-time visualizations of variables. This is extremely useful for visualizing data, debugging code, and visualizing variables as waveforms.

Let’s give it a shot with the updated code below. Compile and upload the program below, then navigate to Tools > Serial Plotter (Ctrl+Shift+L). The code uses a baud rate of 115200; ensure that the serial plotter is also set to 115200.

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;

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

	// Try to initialize!
	if (!mpu.begin()) {
		Serial.println("Failed to find MPU6050 chip");
		while (1) {
		  delay(10);
		}
	}

	// set accelerometer range to +-8G
	mpu.setAccelerometerRange(MPU6050_RANGE_8_G);

	// set gyro range to +- 500 deg/s
	mpu.setGyroRange(MPU6050_RANGE_500_DEG);

	// set filter bandwidth to 21 Hz
	mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

	delay(100);
}

void loop() {
	/* Get new sensor events with the readings */
	sensors_event_t a, g, temp;
	mpu.getEvent(&a, &g, &temp);

	/* Print out the values */
	Serial.print(a.acceleration.x);
	Serial.print(",");
	Serial.print(a.acceleration.y);
	Serial.print(",");
	Serial.print(a.acceleration.z);
	Serial.print(", ");
	Serial.print(g.gyro.x);
	Serial.print(",");
	Serial.print(g.gyro.y);
	Serial.print(",");
	Serial.print(g.gyro.z);
	Serial.println("");

	delay(10);
}

When you move the module up and down the Z axis, you should see something like this.

mpu6050 arduino plotter output

Code Explanation:

You’ll notice that the majority of this sketch is identical to the previous one, with the exception of:

  • Temperature readings are not printed.
  • All other readings are printed in such a way that they form a comma-separated list of values.
  • The readings are taken every 10 milliseconds.

The self-driving car is technology’s biggest gift to civilization since the birth of the Internet. It’s only a matter of time before these cars take over the roads.

In order for a self-driving car to successfully navigate a road, it must be aware of the physical objects around it. Thanks to the spinning LiDAR sensor mounted on the roof, which helps generate a three-dimensional view of the road around the vehicle.

LiDAR isn’t new and has been around for a while; in fact, it was conceived shortly after the development of the laser. Early work on LiDAR was documented in 1963. The high cost of laser equipment limited the use of LiDAR to only government and military agencies.

However, recent price drops have made LiDAR accessible to DIYers like us, allowing us to incorporate it into our projects. One of the widely used low-cost yet accurate LiDAR sensors is the TFMini-S.

This tutorial will show you how to interface the TFMini-S module with an Arduino to perform high-accuracy distance measurements, but first, a brief introduction to LiDAR.

What is LiDAR and How does it Work?

LiDAR is a combination of the words “Light” and “RADAR” or, if you prefer, a backronym for “Light Detection And Ranging”. LiDAR is like RADAR, except that it uses light instead of radio waves.

At its core, LiDAR works by shooting a laser at an object. The laser bounces off of the object and returns to the sensor. By measuring the time it takes for that light to return to the sensor, the distance to the object can be estimated. The distance measured may vary depending on the environment and the reflectivity of the object.

lidar scanning working

By sweeping or spinning a LiDAR sensor, a 3D map of the area can be quickly constructed. Typically, this is presented as a “point cloud” to better understand what the LiDAR is picking up.

lidar 3d point cloud
Courtesy: Dana Peters – Arduino-based LiDAR Scanner – http://youtu.be/xkut3yRL61U

TFMini-S Hardware Overview

The TFMini-S is a high-accuracy, single-point ToF (Time of Flight) LiDAR sensor from Benewake (Beijing) Co. Ltd. It’s perfect for incorporating high-accuracy laser-based ranging into any robotics or interactive project.

tfmini s

The size of a USB stick, the TFMini-S allows you to integrate LiDAR into projects that were previously reserved for smaller sensors like the SHARP GP-series infrared rangefinders.

The TFMini-S can measure the distance to an object as close as 10 centimeters and as far as 12 meters.

In addition to its low cost, small size, and long range, the TFMini-S has a higher distance measurement accuracy of ±6cm up to 6m and ±1% thereafter.

It should be noted that this sensor does not use laser light for ranging. Instead, it uses a focused 850nm infrared LED and optics. That is why the device is relatively inexpensive.

Effective Detection Range

As with all LiDAR sensors, the effective detection range depends on lighting conditions, weather, and the reflectivity of your target object.

The graph below shows the operating range of the TFMini-S under various conditions.

lidar effective detection range
  • 0-10cm is TFMini-S’s blind zone; within this range, data is unreliable.
  • Under extreme conditions, the operating range of the TFMini-S is 0.1-3m. Extreme condition refers to the outdoor glare (of which illumination intensity is around 100klux outdoors at noon in summer) and detection of black target (with reflectivity of 10%).
  • Under normal sunlight conditions (with illumination intensity of around 70klux), the operating range of the TFMini-S is 0.1-7m.
  • In an indoor environment or with low ambient light, the operating range of the TFMini-S is 0.1-12m.

Communication Interfaces

The TFMini-S communicates over the UART interface by default, with commonly used UART RX and TX pins operating at 115200bps.

You can also configure the sensor to communicate over I2C by sending the appropriate commands.

Detection Frequency

According to the datasheet, the TFMini-S can perform at up to 1000 measurements per second (default is 100). This frequency can be changed by sending the appropriate commands.

It should be noted that increasing the output frequency reduces accuracy. Therefore, depending on how accurate you want the measurements to be, you should adjust the output frequency.

Input Power

According to the datasheet, the TFMini-S operates on 5V and draws about 140 mA during an acquisition. The maximum current it could draw is around 200 mA.

During testing, however, the sensor was drawing about 70mA on its own. So, if you use a 5V Arduino, a logic level converter, and the sensor, you can expect to consume around 100mA. Therefore, for basic tests, the sensor can be powered by the USB port (5V/500mA) without issue.

Please keep in mind that the TFMini-S has no overvoltage protection, so keep the power supply voltage fluctuations within 0.1V.

Logic Levels

While the TFMini-S can be powered at 5V, the I/O pins are only 3.3V logic. Therefore, it is recommended to use a logic level converter when using the sensor with a 5V microcontroller.

However, if you only want to read the TFMini-S (in UART mode), you don’t need a logic level converter because 3.3V devices output logic levels that are compatible with 5V devices.

Technical Specifications

Here are the specifications:

Detection range10cm – 12m
Resolution1cm
Ranging Accuracy±6cm up to 6m and ±1% thereafter
Input Voltage5V
UART TTL Voltage3.3V
Current Consumption140mA (typ.), 800mA (peak)
Detection Frequency1 to 1000 scans per second (adjustable)
Light Wavelength850nm
Field of view2.3°
Communication interfacesUART and I2C
Baud Rate115200

For more information about the TFMini-S LiDAR sensor, please refer to the datasheet below.

TFMini-S Pinout

Now let’s have a look at the pinout. The TFMini-S has four pins.

tfmini s pinout

GND is the Ground connection.

VCC is the power input pin. Connect it to 5V power supply only.

RXD/SDA is the pin that you can use to send data to the sensor (when communicating via UART) or send/receive data (when communicating via I2C). It is 3.3V logic level.

TXD/SCL is the pin that either transmits data from the sensor to your microcontroller (when communicating via UART) or functions as a clock (when communicating via I2C). Note that it is also 3.3V logic level.

Wiring a TFMini-S Sensor to an Arduino

Connecting a TFMini-S sensor to an arduino is a breeze. You only need to connect four wires.

Begin by connecting the Red wire (VCC) of the TFMini-S sensor to the Arduino’s 5V output pin and the Black wire (GND) to the Arduino’s GND pin.

Now connect the White wire (RXD/SDA) of the TFMini-S sensor to the Arduino’s digital pin 3 and Green wire (TXD/SCL) to the Arduino’s digital pin 2, as we will be implementing a software UART.

The image below shows how to build the circuit.

wiring tfmini s to arduino

Arduino Example Code

Now that we have everything hooked up, let’s run a simple sketch to demonstrate the capabilities of the TFMini-S sensor.

#include <SoftwareSerial.h>   //header file of software serial port
SoftwareSerial Serial1(2, 3); //define software serial port name as Serial1 and define pin2 as RX & pin3 as TX
 
int dist;                     //actual distance measurements of LiDAR
int strength;                 //signal strength of LiDAR
int check;                    //save check value
int i;
int uart[9];                   //save data measured by LiDAR
const int HEADER = 0x59;      //frame header of data package
 
 
void setup()
{
  Serial.begin(9600);         //set bit rate of serial port connecting Arduino with computer
  Serial1.begin(115200);      //set bit rate of serial port connecting LiDAR with Arduino
}
 
 
void loop() {
  if (Serial1.available())                //check if serial port has data input
  {
    if (Serial1.read() == HEADER)        //assess data package frame header 0x59
    {
      uart[0] = HEADER;
      if (Serial1.read() == HEADER)      //assess data package frame header 0x59
      {
        uart[1] = HEADER;
        for (i = 2; i < 9; i++)         //save data in array
        {
          uart[i] = Serial1.read();
        }
        check = uart[0] + uart[1] + uart[2] + uart[3] + uart[4] + uart[5] + uart[6] + uart[7];
        if (uart[8] == (check & 0xff))        //verify the received data as per protocol
        {
          dist = uart[2] + uart[3] * 256;     //calculate distance value
          strength = uart[4] + uart[5] * 256; //calculate signal strength value
          Serial.print("distance = ");
          Serial.print(dist);                 //output measure distance value of LiDAR
          Serial.print('\t');
          Serial.print("strength = ");
          Serial.print(strength);             //output signal strength value
          Serial.print('\n');
        }
      }
    }
  }
}

Once the sketch is uploaded, open your serial monitor, setting the baud rate to 9600 bps.

Try pointing the sensor at objects lying around you. You should see the measured distance begin to stream by.

tfmini s arduino output

If there is no information displayed, make sure the TFmini-S is properly connected; when turned on a red light should be visible inside the transmitting lens when viewed from in front.

TFMini Software

The TFMini software is a powerful tool for testing TFMini sensors. It is a free tool, but it can only be used on the Windows platform.

You can download this program from the official Benewake website.

Connecting TFMini to Software

To use the TFMini software, connect your TFMini-S to your PC using a USB to TTL converter. Just make sure that you are providing 5V for VCC.

wiring tfmini s to usb to ttl converter

Using TFMini Software

The software itself comes as a “Portable” RAR package. Download it and extract to a folder of your choice. Launch the WINCC_TF.exe.

The program will start up as shown below.

tfmini software

Find the Settings section and choose TFMiniS for the product type. Next, select the COM port to which the TFMini-S is connected. Finally, press the Connect button.

tfmini software settings

Once the device is connected, the program begins to display a distance-over-time waveform in the “Time Line Chart” section. Down below, the “Real Time Data” section displays the current distance (Dist), the number of effective data points per second (Effective Points), and the signal strength (Strength).

tfmini software waveform

We are all aware that most MCUs used in our projects are time-agnostic; that is, they are unaware of the time around them. It’s fine for most of our projects, but every now and then, when you come across an idea where keeping time is critical, the DS1307 precision RTC module comes in handy. It is suitable for a wide range of projects, including clocks, timers, and alarms, as well as data logging and time stamping.

Hardware Overview

This module is built around the highly capable DS1307 RTC chip and the AT24C32 EEPROM, both of which have been around for a while and have good library support.

DS1307 RTC Chip

At the heart of the module is a low-cost, extremely accurate RTC chip from Maxim — the DS1307. It handles all timekeeping functions and communicates with the microcontroller over I2C.

The DS1307 can keep track of seconds, minutes, hours, days, dates, months, and years. It can work in either a 12-hour or 24-hour format and has an AM/PM indicator. For months with fewer than 31 days, it automatically adjusts the date at the end of the month, including leap year corrections (valid up to 2100).

Another interesting feature of the DS1307 is the SQW pin, which can be programmed to output one of four square-wave frequencies: 1Hz, 4kHz, 8kHz, or 32kHz.

The DS1307 requires an external 32kHz crystal for timekeeping, the frequency of which is easily affected by external temperature. Even though this change in frequency is negligible, it does add up.

It might seem like a problem, but it’s not. It actually causes the clock to be off by about two minutes per month.

Battery Backup

The DS1307 IC incorporates a battery input for maintaining accurate timekeeping even when the device’s main power is interrupted.

The built-in power-sense circuit continuously monitors the status of VCC to detect power failures and automatically switches to the backup supply. This means that even in the event of a power outage, the IC can still keep time.

CR2032 Battery Holder on DS1307 Module

For backup power, the bottom of the board houses a battery holder for a 20mm 3V lithium coin cell. Any CR2032 battery will fit nicely here.

Assuming you use a fully charged 47mAh coin cell battery and keep the chip current draw to a minimum of 300nA, the battery should be able to power the RTC for at least 17 years without the need for an external power supply.

47mAh/300nA = 156666.67 hours = 6527.78 days = 17.87 years

Onboard 24C32 EEPROM

The DS1307 RTC module also includes a 32-byte (4K x 8-bits) AT24C32 EEPROM (non-volatile) chip with 1,000,000 write cycles. This chip doesn’t really have anything to do with the RTC, but it can be useful for things like data logging or storing any other data that you want to be non-volatile.

24C32 EEPROM Chip on DS1307 Module

The 24C32 EEPROM communicates via I2C and shares the same I2C bus as the DS1307.

The onboard 24C32 EEPROM has a fixed I2C address of 0x50.

The AT24C32 EEPROM is rated for 1,000,000 write cycles, so it won’t wear out during normal data logging applications as long as you’re not writing data every second.

Module’s Hidden Feature – DS18B20

The DS1307 RTC module has a provision for installing a DS18B20 temperature sensor that often goes unnoticed.

The DS18B20 can be installed using the three mounting holes in the upper right corner, close to the battery holder (marked U1).

Provision for DS18B20 on DS1307 Module

After installing the DS18B20, you will be able to obtain temperature readings from the DS pin. These readings can be used to compensate for temperature-based time drift.

Solder the DS18B20 according to the polarity shown on the silk screen. You may also require a 4.7K resistor between VCC and DS.

I2C Interface

The module has a simple I2C interface that takes up two addresses. The DS1307 RTC chip’s fixed I2C address is 0x68, and the 24C32 EEPROM’s fixed I2C address is 0x50.

The SDA and SCL signals, as well as power and ground, are also broken out to the other side of the module to allow these signals to be looped out to another module.

ds1307 module i2c pins for daisy chaining

To enable communication, both the SDA and SCL lines have 4.7K pull-up resistors.

Technical Specifications

Here are the specifications:

Operating Voltage4.5 to 5.5V (5V typical)
Current Consumption< 1.5mA (typ.)
Accuracy (0-40°C)Depends on crystal (± 20ppm typ.)
BatteryCR2032 (3V Coin)

For more information on the DS1307 RTC and the 24C32 EEPROM chip, please refer to the datasheets listed below.

DS1307 RTC Module Pinout

The DS1307 RTC module has 7 pins in total. The pinout is as follows:

DS1307 RTC Module Pinout

SQW pin outputs one of four square-wave frequencies: 1Hz, 4kHz, 8kHz, or 32kHz.

DS pin should provide temperature readings if a DS18B20 temperature sensor is installed using the three mounting holes in the upper right corner, close to the battery holder (marked U1).

SCL is a serial clock pin for the I2C interface.

SDA is a serial data pin for the I2C interface.

VCC provides power to the module. You can connect it to a 5 volt power supply.

GND is the ground pin.

BAT is a backup supply input for any standard 3V lithium cell or other energy source to keep accurate time when the device’s main power is interrupted.

Wiring a DS1307 RTC Module to an Arduino

Let’s connect the RTC to the Arduino.

Connections are straightforward. Begin by connecting the VCC pin to the Arduino’s 5V output and the GND pin to ground.

Now we are left with the pins that are used for I2C communication. Note that each Arduino board has different I2C pins that must be connected correctly.  On Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also referred to as A5 (SCL) and A4 (SDA).

Check out the table below for quick reference.

SCLSDA
Arduino UnoA5A4
Arduino NanoA5A4
Arduino Mega2120
Leonardo/Micro32

The diagram below shows how to connect everything.

Wiring DS1307 RTC module with Arduino

Installing uRTCLib Library

It takes a lot of effort to communicate with an RTC module. Fortunately, the uRTCLib library was created to hide all of the complexities, allowing us to issue simple commands to read the RTC data.

It is a simple yet powerful library that also supports programming the SQW output, which is not supported by many RTC libraries.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the library index and update the list of installed libraries.

Arduino Library Installation - Selecting Manage Libraries in Arduino IDE

Filter your search by entering ‘urtclib’. Look for uRTCLib by Naguissa. Click on that entry and then choose Install.

urtclib library installation

At the end of the tutorial, we’ve also included code for reading and writing the onboard 24C32 EEPROM. If you’re interested, you’ll need to install the uEEPROMLib library. Look for ‘ueepromlib‘ and install it as well.

ueepromlib library installation

Arduino Code – Reading Date and Time

This is a simple sketch for setting/reading the date and time from the DS1307 RTC module.

#include "Arduino.h"
#include "uRTCLib.h"

// uRTCLib rtc;
uRTCLib rtc(0x68);

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup() {
  Serial.begin(9600);
  delay(3000); // wait for console opening

  URTCLIB_WIRE.begin();

  // Comment out below line once you set the date & time.
  // Following line sets the RTC with an explicit date & time
  // for example to set January 13 2022 at 12:56 you would call:
   rtc.set(0, 56, 12, 5, 13, 1, 22);
  // rtc.set(second, minute, hour, dayOfWeek, dayOfMonth, month, year)
  // set day of week (1=Sunday, 7=Saturday)
}

void loop() {
  rtc.refresh();

  Serial.print("Current Date & Time: ");
  Serial.print(rtc.year());
  Serial.print('/');
  Serial.print(rtc.month());
  Serial.print('/');
  Serial.print(rtc.day());

  Serial.print(" (");
  Serial.print(daysOfTheWeek[rtc.dayOfWeek()-1]);
   Serial.print(") ");

  Serial.print(rtc.hour());
  Serial.print(':');
  Serial.print(rtc.minute());
  Serial.print(':');
  Serial.println(rtc.second());
  
  delay(1000);
}

Here’s what the output looks like:

ds1307 rtc arduino output

Code Explanation:

The sketch begins by including the Arduino.h and uRTCLib.h libraries for communicating with the module. We then create an uRTCLib library object and define the daysOfTheWeek 2D character array to store the days information.

To interact with the RTC module, we use the following functions in the setup and loop sections of the code:

begin() function ensures that the RTC module is properly initialized.

set(ss, mm, hh, day, dd, mm, yy) function sets the RTC to an explicit date and time. For example, to set January 13, 2022 at 12:56, you would call: rtc.set(0, 56, 12, 5, 13, 1, 22);

refresh() function refreshes data from the HW RTC.

year() function returns the current year.

month() function returns the current month.

day() function returns the current day.

dayOfWeek() function returns the current day of the week (1 to 7). This function is typically used as an index for a 2D character array that stores information about the days.

hour() function returns the current hour.

minute() function returns the current minute.

second() function returns the current seconds.

Arduino Code – Read/Write the 24C32 EEPROM

As an added bonus, the DS1307 RTC module includes 32 bytes of Electrically Erasable ROM. Its contents will not be erased even if the device’s main power is interrupted.

This is a simple sketch that attempts to write an integer, float, character, and string to the 24C32 EEPROM and then reads them back. This sketch can be extended to save settings, passwords, or just about anything.

#include "Arduino.h"
#include "Wire.h"
#include "uEEPROMLib.h"

// uEEPROMLib eeprom;
uEEPROMLib eeprom(0x50);

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

  Wire.begin();

  int inttmp = 32123;
  float floattmp = 3.1416;
  char chartmp = 'A';
  char c_string[] = "lastminuteengineers.com"; //23
  int string_length = strlen(c_string);

  Serial.println("Writing into memory...");
  
  // Write single char at address 
  if (!eeprom.eeprom_write(8, chartmp)) {
  Serial.println("Failed to store char.");
  } else {
  Serial.println("char was stored correctly.");
  }

  // Write a long string of chars FROM position 33 which isn't aligned to the 32 byte pages of the EEPROM
  if (!eeprom.eeprom_write(33, (byte *) c_string, strlen(c_string))) {
  Serial.println("Failed to store string.");
  } else {
  Serial.println("string was stored correctly.");
  }

  // Write an int
  if (!eeprom.eeprom_write(0, inttmp)) {
    Serial.println("Failed to store int.");
  } else {
    Serial.println("int was stored correctly.");
  }

  // write a float
  if (!eeprom.eeprom_write(4, floattmp)) {
    Serial.println("Failed to store float.");
  } else {
    Serial.println("float was stored correctly.");
  }

  Serial.println("");
  Serial.println("Reading memory...");
  
  Serial.print("int: ");
  eeprom.eeprom_read(0, &inttmp);
  Serial.println(inttmp);

  Serial.print("float: ");
  eeprom.eeprom_read(4, &floattmp);
  Serial.println((float) floattmp);

  Serial.print("char: ");
  eeprom.eeprom_read(8, &chartmp);
  Serial.println(chartmp);

  Serial.print("string: ");
  eeprom.eeprom_read(33, (byte *) c_string, string_length);
  Serial.println(c_string);

  Serial.println();
}

void loop() {
}

Here’s what the output looks like:

24c32 eeprom arduino output

When reading/writing the EEPROM, keep in mind that different data types take up different amounts of memory. For example, a character is one byte, an integer is two bytes, and a float is four bytes.

In other words, if you’re storing a character, it can be written to each memory location (0, 1, 2, 3, 4, 5….).

However, if you are storing an integer, you must reserve two memory locations for each value, so you will want to store data in every other memory location, such as (0,2,4,6….).

We are all aware that most MCUs used in our projects are time-agnostic; that is, they are unaware of the time around them. It’s fine for most of our projects, but every now and then, when you come across an idea where keeping time is critical, the DS3231 Precision RTC module comes in handy. It is suitable for a wide range of projects, including clocks, timers, and alarms, as well as data logging and time stamping.

Hardware Overview

This module is built around the highly capable DS3231S RTC chip and the AT24C32 EEPROM, both of which have been around for a while and have good library support.

DS3231 RTC Chip

At the heart of the module is a low-cost, extremely accurate RTC chip from Maxim — the DS3231. It handles all timekeeping functions and communicates with the microcontroller over I2C.

DS3231 RTC Module Chip

The DS3231 can keep track of seconds, minutes, hours, days, dates, months, and years. For months with fewer than 31 days, it automatically adjusts the date at the end of the month, including leap year corrections (valid up to 2100).

It can work in either a 12-hour or 24-hour format and has an AM/PM indicator. It also has two time-of-day alarms that can be programmed.

The INT/SQW pin on the DS3231 provides either an interrupt signal (due to alarm conditions) or a nice square wave at 1Hz, 4kHz, 8kHz, or 32kHz.

Additionally, the DS3231 outputs a stable (temperature compensated) and accurate reference clock on the 32K pin. This clock output may be useful in some applications for measuring clock timing accuracy or clocking other circuits.

Temperature Compensated Crystal Oscillator(TCXO)

Most RTC modules, such as the DS1307, require an external 32kHz crystal oscillator for timekeeping. The issue with these crystals is that their oscillation frequency is affected by external temperature. This change in frequency is negligible, but it adds up.

To avoid such minor crystal drifts, the DS3231 is powered by a 32kHz temperature compensated crystal oscillator (TCXO), which is highly resistant to external temperature changes.

TCXO Crystal Oscillator Compensation

The TCXO is actually composed of an integrated temperature sensor, a 32kHz crystal oscillator, and control logic. The integrated temperature sensor compensates for frequency changes by adding or removing clock ticks, ensuring that timekeeping remains accurate.

That is why the TCXO provides the most stable and accurate reference clock and keeps the RTC accurate to within ±2 minutes per year.

DS3231 Vs DS1307

The primary difference between the DS3231 and the DS1370 is the accuracy of timekeeping.

The DS1307 requires an external 32kHz crystal for timekeeping, the frequency of which is easily affected by external temperature. As a result, the clock is usually off by about five minutes per month.

The DS3231, on the other hand, is much more accurate because it has an internal Temperature Compensated Crystal Oscillator (TCXO) that isn’t affected by temperature, making it accurate to a few minutes per year at most.

This doesn’t mean that the DS1307 isn’t accurate; it’s still a great RTC that will serve you well, but if your project needs more accurate timekeeping, the DS3231 is a better choice.

Battery Backup

The DS3231 IC incorporates a battery input for maintaining accurate timekeeping even when the device’s main power is interrupted.

The built-in power-sense circuit continuously monitors the status of VCC to detect power failures and automatically switches to the backup supply. This means that even in the event of a power outage, the IC can still keep time.

CR2032 Battery Holder on DS3231 RTC Module

For backup power, the bottom of the board houses a battery holder for a 20mm 3V lithium coin cell.

Assuming you use a fully charged 220mAh coin cell battery and keep the chip current draw to a minimum of 3µA, the battery should be able to power the RTC for at least 8 years without the need for an external power supply.

220mAh/3µA = 73333.34 hours = 3055.56 days = 8.37 years

Onboard 24C32 EEPROM

The DS3231 RTC module also includes a 32-byte (4K x 8-bits) AT24C32 EEPROM (non-volatile) chip with 1,000,000 write cycles. This chip doesn’t really have anything to do with the RTC, but it can be useful for things like data logging or storing any other data that you want to be non-volatile.

24C32 EEPROM & I2C Address Selection Jumpers on DS3231 RTC Module

The 24C32 EEPROM communicates via I2C and shares the same I2C bus as the DS3231.

If you are using multiple devices on the same I2C bus, you may need to set a different I2C address for EEPROM so that it does not conflict with another I2C device.

To accomplish this, the module has three solder jumpers (A0, A1 and A2) on the back. Shorting a jumper with a blob of solder sets the address.

I2C Address selection jumpers on DS3231 module

According to the datasheet for the 24C32, these three bits are placed at the end of the seven-bit I2C address, just before the Read/Write bit.

24C32 EEPROM I2C Address Register

Because there are three address inputs that can be either HIGH or LOW, we can create eight (23) different addresses.

By default, all three address inputs are pulled HIGH using onboard pullups, giving the 24C32 a default I2C address of 1010111Binary or 0x57Hex.

By shorting out the solder jumpers, the address inputs are pulled LOW. It allows you to set the I2C address according to the table below.

24C32 EEPROM I2CAddress Selection Jumpers on DS3231 RTC module

The AT24C32 EEPROM is rated for 1,000,000 write cycles, so it won’t wear out during normal data logging applications as long as you’re not writing data every second.

I2C Interface

The module has a simple I2C interface that takes up two addresses. The DS3231S RTC chip’s fixed I2C address is 0x68, and the EEPROM’s default I2C address is 0x57 (though the address range is 0x50 to 0x57).

The I2C SDA and SCL signals, as well as power and ground, are broken out to one side of the module to allow these signals to be looped out to another module.

ds3231 module i2c pins for daisy chaining

To enable communication, both the SDA and SCL lines have 4.7K pull-up resistors.

Technical Specifications

Here are the specifications:

Operating Voltage2.3 to 5.5V (3.3 or 5V typical)
Current Consumption< 300µA (typ.)
Accuracy (0-40°C)± 2ppm
BatteryCR2032 (3V Coin)

For more information on the DS3231 RTC and the 24C32 EEPROM chip, please refer to the datasheets listed below.

Warning

These modules normally come with a 200Ω resistor soldered next to the 1N4148 diode, as you can see in the image below.

ds3231 module problem fix

The resistor and diode form a simple charging circuit designed for use with LIR2032 rechargeable batteries.

Be aware that some DS3231 modules come with a non-rechargeable CR2032 battery. If this is the case, you must remove the resistor because attempting to charge a non-rechargeable battery can cause serious damage.

DS3231 RTC Module Pinout

The DS3231 RTC module has 6 pins in total. The pinout is as follows:

DS3231 RTC Module Pinout

32K pin outputs a stable (temperature compensated) and accurate reference clock.

INT/SQW pin provides either an interrupt signal (due to alarm conditions) or a square-wave output at either 1Hz, 4kHz, 8kHz, or 32kHz.

SCL is a serial clock pin for the I2C interface.

SDA is a serial data pin for the I2C interface.

VCC provides power to the module. You can connect it to a 3.3 to 5 volt power supply.

GND is the ground pin.

Wiring a DS3231 RTC module to an Arduino

Let’s connect the RTC to the Arduino.

Connections are straightforward. Begin by connecting the VCC pin to the Arduino’s 5V output and the GND pin to ground.

Now we are left with the pins that are used for I2C communication. Note that each Arduino board has different I2C pins that must be connected correctly.  On Arduino boards with the R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF pin. They are also referred to as A5 (SCL) and A4 (SDA).

The following table lists the pin connections:

DS3231 ModuleArduino
VCC5V
GNDGND
SCLSCL or A5
SDASDA or A4

The diagram below shows how to connect everything.

Wiring DS3231 RTC module with Arduino

Installing uRTCLib Library

It takes a lot of effort to communicate with an RTC module. Fortunately, the uRTCLib library was created to hide all of the complexities, allowing us to issue simple commands to read the RTC data.

It is a simple yet powerful library that also supports time-of-day alarms and programming the SQW output, which is not supported by many RTC libraries.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the library index and update the list of installed libraries.

Arduino Library Installation - Selecting Manage Libraries in Arduino IDE

Filter your search by entering ‘urtclib’. Look for uRTCLib by Naguissa. Click on that entry and then choose Install.

urtclib library installation

At the end of the tutorial, we’ve also included code for reading and writing the onboard 24C32 EEPROM. If you’re interested, you’ll need to install the uEEPROMLib library. Look for ‘ueepromlib‘ and install it as well.

ueepromlib library installation

Arduino Code – Reading Date, Time and Temperature

This is a simple sketch for setting/reading the date, time, and temperature from the DS3231 RTC module.

#include "Arduino.h"
#include "uRTCLib.h"

// uRTCLib rtc;
uRTCLib rtc(0x68);

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup() {
  Serial.begin(9600);
  delay(3000); // wait for console opening

  URTCLIB_WIRE.begin();

  // Comment out below line once you set the date & time.
  // Following line sets the RTC with an explicit date & time
  // for example to set January 13 2022 at 12:56 you would call:
   rtc.set(0, 56, 12, 5, 13, 1, 22);
  // rtc.set(second, minute, hour, dayOfWeek, dayOfMonth, month, year)
  // set day of week (1=Sunday, 7=Saturday)
}

void loop() {
  rtc.refresh();

  Serial.print("Current Date & Time: ");
  Serial.print(rtc.year());
  Serial.print('/');
  Serial.print(rtc.month());
  Serial.print('/');
  Serial.print(rtc.day());

  Serial.print(" (");
  Serial.print(daysOfTheWeek[rtc.dayOfWeek()-1]);
  Serial.print(") ");

  Serial.print(rtc.hour());
  Serial.print(':');
  Serial.print(rtc.minute());
  Serial.print(':');
  Serial.println(rtc.second());

  Serial.print("Temperature: ");
  Serial.print(rtc.temp()  / 100);
  Serial.print("\xC2\xB0");   //shows degrees character
  Serial.println("C");

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

Here’s what the output looks like:

ds3231 rtc arduino output

Code Explanation:

The sketch begins by including the Arduino.h and uRTCLib.h libraries for communicating with the module. We then create an uRTCLib library object and define the daysOfTheWeek 2D character array to store the days information.

To interact with the RTC module, we use the following functions in the setup and loop sections of the code:

begin() function ensures that the RTC module is properly initialized.

set(ss, mm, hh, day, dd, mm, yy) function sets the RTC to an explicit date and time. For example, to set January 13, 2022 at 12:56, you would call: rtc.set(0, 56, 12, 5, 13, 1, 22);

refresh() function refreshes data from the HW RTC.

year() function returns the current year.

month() function returns the current month.

day() function returns the current day.

dayOfWeek() function returns the current day of the week (1 to 7). This function is typically used as an index for a 2D character array that stores information about the days.

hour() function returns the current hour.

minute() function returns the current minute.

second() function returns the current seconds.

temp() function returns the current temperature of the ‘die’.

Arduino Code – Read/Write the 24C32 EEPROM

As an added bonus, the DS3231 RTC module includes 32 bytes of Electrically Erasable ROM. Its contents will not be erased even if the device’s main power is interrupted.

This is a simple sketch that attempts to write an integer, float, character, and string to the 24C32 EEPROM and then reads them back. This sketch can be extended to save settings, passwords, or just about anything.

#include "Arduino.h"
#include "Wire.h"
#include "uEEPROMLib.h"

// uEEPROMLib eeprom;
uEEPROMLib eeprom(0x57);

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

  Wire.begin();

  int inttmp = 32123;
  float floattmp = 3.1416;
  char chartmp = 'A';
  char c_string[] = "lastminuteengineers.com"; //23
  int string_length = strlen(c_string);

  Serial.println("Writing into memory...");
  
  // Write single char at address 
  if (!eeprom.eeprom_write(8, chartmp)) {
  Serial.println("Failed to store char.");
  } else {
  Serial.println("char was stored correctly.");
  }

  // Write a long string of chars FROM position 33 which isn't aligned to the 32 byte pages of the EEPROM
  if (!eeprom.eeprom_write(33, (byte *) c_string, strlen(c_string))) {
  Serial.println("Failed to store string.");
  } else {
  Serial.println("string was stored correctly.");
  }

  // Write an int
  if (!eeprom.eeprom_write(0, inttmp)) {
    Serial.println("Failed to store int.");
  } else {
    Serial.println("int was stored correctly.");
  }

  // write a float
  if (!eeprom.eeprom_write(4, floattmp)) {
    Serial.println("Failed to store float.");
  } else {
    Serial.println("float was stored correctly.");
  }

  Serial.println("");
  Serial.println("Reading memory...");
  
  Serial.print("int: ");
  eeprom.eeprom_read(0, &inttmp);
  Serial.println(inttmp);

  Serial.print("float: ");
  eeprom.eeprom_read(4, &floattmp);
  Serial.println((float) floattmp);

  Serial.print("char: ");
  eeprom.eeprom_read(8, &chartmp);
  Serial.println(chartmp);

  Serial.print("string: ");
  eeprom.eeprom_read(33, (byte *) c_string, string_length);
  Serial.println(c_string);

  Serial.println();
}

void loop() {
}

Here’s what the output looks like:

24c32 eeprom arduino output

When reading/writing the EEPROM, keep in mind that different data types take up different amounts of memory. For example, a character is one byte, an integer is two bytes, and a float is four bytes.

In other words, if you’re storing a character, it can be written to each memory location (0, 1, 2, 3, 4, 5….).

However, if you are storing an integer, you must reserve two memory locations for each value, so you will want to store data in every other memory location, such as (0,2,4,6….).

In your upcoming project, you might want to use your Arduino to control a high-voltage device, like a lamp, fan, or other household appliance. However, because the Arduino runs on 5 volts, it cannot directly control these high-voltage appliances.

This is where relay modules come into play. These well-contained modules are inexpensive, simple to connect, and ideal for home-brew projects that require switching modest amounts of AC or DC power. The only downside is that, because these are electro-mechanical devices, they are more prone to wear and tear over time.

This tutorial will walk you through setting up the relay module to turn on a lamp or other device, but first, a quick primer on relays.

How Do Relays Work?

At the core of a relay is an electromagnet (a wire coil that becomes a temporary magnet when electricity is passed through it). A relay can be thought of as an electric lever; you turn it on with a relatively small current, and it turns on another device with a much larger current.

Relay Basics

Here’s a small animation showing how a relay links two circuits together.

relay working animation.gif

To illustrate, think about two simple circuits: one with an electromagnet and a switch or sensor, and the other with a magnetic switch and a light bulb.

Initially, both circuits are open, with no current flowing through them.

When a small current flows through the first circuit, the electromagnet is energized, creating a magnetic field around it. The energized electromagnet attracts the second circuit’s contact, closing the switch and allowing a large current to flow.

When the current in the first circuit stops flowing, the contact returns to its original position, reopening the second circuit.

Relay Operation

A relay typically has five pins, three of which are high voltage terminals (NC, COM, and NO) that connect to the device being controlled.

relay pinout

The device is connected between the COM (common) terminal and either the NC (normally closed) or NO (normally open) terminal, depending on whether the device should remain normally on or off.

Between the remaining two pins (coil1 and coil2) is a coil that acts as an electromagnet.

relay working animation2.gif

Normally (initial position), the COM terminal is connected to the NC terminal and the NO terminal is open.

When current flows through the coil, the electromagnet becomes energized, causing the switch’s internal contact to move. The COM then connects to the NO terminal, disconnecting from the NC terminal.

When the current stops flowing through the coil, the internal contact is returned to its initial position, re-connecting the NC terminal to the COM and re-opening the NO terminal.

To put it another way, the relay functions as a single-pole-double-throw switch (SPDT).

One-Channel Relay Module Hardware Overview

The one-channel relay module is designed to allow your Arduino to control a single high-powered device. It has a relay with a maximum current rating of 10A at 250VAC or 30VDC.

one channel relay module

Modules with two, four, and eight channels are also available. You can choose the one that best meets your needs.

LEDs

The relay module contains two LEDs.

one channel relay module led

The power LED will light up when the module is powered on. The status LED will light up when the relay is activated.

Output Terminal Block

The high voltage terminals (NC, COM, and NO) of the relay are broken out to a screw terminal. The device you want to control can be connected across them.

one channel relay module output terminal

Control Pin

On the other side of the module, there is an input pin IN for controlling the relay. This pin is 5V logic compatible, so if you have a microcontroller like an Arduino, you can drive a relay with any digital output pin.

one channel relay module control pins

The input pin is active low, which means that a logic LOW activates the relay and a logic HIGH deactivates it.

Module Power

The module operates on 5 volts and draws approximately 70 mA when the relay is activated.

The module also includes a flyback diode that is connected in parallel with the relay coil to safely shunt current when the relay coil is de-energized.

one channel relay module flyback diode

One-Channel Relay Module Pinout

Let’s take a look at the pinout.

one channel relay module pinout

Power Pins:

GND is the common ground pin.

VCC pin provides power to the module.

Control Pin:

IN pin is used to control the relay. This is an active low pin, which means that pulling it LOW activates the relay and pulling it HIGH deactivates it.

Output Terminals:

COM terminal connects to the device you intend to control.

NC terminal is normally connected to the COM terminal, unless you activate the relay, which breaks the connection.

NO terminal is normally open, unless you activate the relay that connects it to the COM terminal.

Wiring a One-Channel Relay Module to an Arduino

Now that we know everything about the relay module, it’s time to put it to use! Let’s wire up our relay module to operate a lamp.

Warning:
This board interacts with HIGH AC voltage. Improper or incorrect use could result in serious injury or death. Therefore, it is intended for people who are familiar with and knowledgeable about HIGH AC voltage.

Begin by connecting the module’s VCC pin to the Arduino’s 5V pin and the GND pin to ground. Connect digital pin #6 to the IN input pin.

You’ll also need to connect the relay module to the AC-powered device you want to control, in this case, a lamp. You’ll need to cut your live AC line and connect one end of the cut wire (coming from the wall) to COM and the other to NC or NO, depending on what you want your device’s initial state to be.

If you want to keep your device off most of the time and turn it on occasionally, connect the other end of the wire to NO. Otherwise, connect it to NC.

For this project, we want our lamp to be off at first and then turn on when we activate the relay, so we will connect one end of the wire to COM and the other to NO.

The following illustration shows the wiring.

wiring one channel relay module with arduino

Arduino Example Code

Controlling a relay module with the Arduino is as easy as controlling an LED. Here’s a simple code that will activate the relay for 3 seconds and then deactivate it for 3 seconds.

int RelayPin = 6;

void setup() {
	// Set RelayPin as an output pin
	pinMode(RelayPin, OUTPUT);
}

void loop() {
	// Let's turn on the relay...
	digitalWrite(RelayPin, LOW);
	delay(3000);
	
	// Let's turn off the relay...
	digitalWrite(RelayPin, HIGH);
	delay(3000);
}

Code Explanation:

The sketch begins by declaring the pin to which the relay module’s input pin is connected.

int RelayPin = 6;

In the setup function, we configure the input pin to behave as an output.

pinMode(RelayPin, OUTPUT);

In the loop function, we turn the device ON/OFF by pulling the relay pin LOW/HIGH.

digitalWrite(RelayPin, LOW) pulls the pin LOW, whereas digitalWrite(RelayPin, HIGH) pulls the pin HIGH.

digitalWrite(RelayPin, LOW);
delay(3000);

digitalWrite(RelayPin, HIGH);
delay(3000);

In your upcoming project, you might want to use your Arduino to control a high-voltage device, like a lamp, fan, or other household appliance. However, because the Arduino runs on 5 volts, it cannot directly control these high-voltage appliances.

This is where relay modules come into play. These well-contained modules are inexpensive, simple to connect, and ideal for home-brew projects that require switching modest amounts of AC or DC power. The only downside is that, because these are electro-mechanical devices, they are more prone to wear and tear over time.

This tutorial will walk you through setting up the relay module to turn on a lamp or other device, but first, a quick primer on relays.

How Do Relays Work?

At the core of a relay is an electromagnet (a wire coil that becomes a temporary magnet when electricity is passed through it). A relay can be thought of as an electric lever; you turn it on with a relatively small current, and it turns on another device with a much larger current.

Relay Basics

Here’s a small animation showing how a relay links two circuits together.

relay working animation.gif

To illustrate, think about two simple circuits: one with an electromagnet and a switch or sensor, and the other with a magnetic switch and a light bulb.

Initially, both circuits are open, with no current flowing through them.

When a small current flows through the first circuit, the electromagnet is energized, creating a magnetic field around it. The energized electromagnet attracts the second circuit’s contact, closing the switch and allowing a large current to flow.

When the current in the first circuit stops flowing, the contact returns to its original position, reopening the second circuit.

Relay Operation

A relay typically has five pins, three of which are high voltage terminals (NC, COM, and NO) that connect to the device being controlled.

relay pinout

The device is connected between the COM (common) terminal and either the NC (normally closed) or NO (normally open) terminal, depending on whether the device should remain normally on or off.

Between the remaining two pins (coil1 and coil2) is a coil that acts as an electromagnet.

relay working animation2.gif

Normally (initial position), the COM terminal is connected to the NC terminal and the NO terminal is open.

When current flows through the coil, the electromagnet becomes energized, causing the switch’s internal contact to move. The COM then connects to the NO terminal, disconnecting from the NC terminal.

When the current stops flowing through the coil, the internal contact is returned to its initial position, re-connecting the NC terminal to the COM and re-opening the NO terminal.

To put it another way, the relay functions as a single-pole-double-throw switch (SPDT).

Two-Channel Relay Module Hardware Overview

The two-channel relay module is designed to allow your Arduino to control two high-powered devices. It has two relays, each with a maximum current rating of 10A at 250VAC or 30VDC.

two channel relay module relays

Modules with one, four, and eight channels are also available. You can choose the one that best meets your needs.

Output Terminal Blocks

The high voltage terminals (NC, COM, and NO) of each relay are broken out to two screw terminals. The device you want to control can be connected across them.

two channel relay module output terminal blocks

Module Control

On the other side of the module, there are two input pins, IN1 and IN2, for controlling the relay. These pins are 5V logic compatible, so if you have a microcontroller like an Arduino, you can drive a relay with any digital output pin.

two channel relay module control pins

The input pins are active low, which means that a logic LOW activates the relay and a logic HIGH deactivates it.

The relay module has two LEDs that indicate the status of the relay. When a relay is activated, the corresponding LED lights up.

Built-in Optocouplers

One of the best features of these modules is the inclusion of two optocouplers on the logic inputs.

two channel relay module optocouplers

Optocouplers offer complete electrical isolation between the logic control input and the relay power as an extra layer of protection in the event of a major failure on the relay’s AC load, such as a lightning strike.

Power Supply Selection Jumper

The module has a jumper between the header pins JD-VCC and VCC (the relay power is connected to the logic power). This jumper determines whether or not the relay module is physically connected to the Arduino.

two channel relay module power supply selection jumper

When the jumper is in place, the Arduino powers the relay’s electromagnet directly. In this case, the relay module and the Arduino are not physically isolated from each other, but this makes the device easier to use because it only needs one power supply.

relay module power selection jumper setting

When you remove the jumper, the relay module is physically isolated from the Arduino. However, in this case, you must provide a separate 5V power supply voltage to the JD-VCC and GND.

Module Power

The module operates on 5 volts and draws approximately 140mA when both relays are activated (70mA each).

The module also includes flyback diodes that are connected in parallel with the relay coils to safely shunt current when the relay coil is de-energized.

two channel relay module flyback diodes

Remember that if optocouplers are enabled, two separate 5V power sources are required.

Two-Channel Relay Module Pinout

Let’s take a look at the pinout.

relay module pinout

Control Pins:

VCC pin provides power to the built-in optocouplers and, optionally, the relay’s electromagnet (if you keep the jumper in place). Connect it to the 5V pin on the Arduino.

GND is the common ground pin.

IN1 & IN2 pins control the relay. These are active low pins, which means that pulling them LOW activates the relay and pulling them HIGH deactivates it.

Power Supply Selection Pins:

JD-VCC provides power to the relay’s electromagnet. When the jumper is in place, JD-VCC is shorted to VCC, allowing the electromagnets to be powered by the Arduino’s 5V line. Without the jumper cap, you’d have to connect it to a separate 5V power source.

VCC pin is shorted to the JD-VCC pin with the jumper cap on. Keep this pin disconnected if you remove the jumper.

GND is the common ground pin.

Output Terminals:

COM terminal connects to the device you intend to control.

NC terminal is normally connected to the COM terminal, unless you activate the relay, which breaks the connection.

NO terminal is normally open, unless you activate the relay that connects it to the COM terminal.

Wiring a Two-Channel Relay Module to an Arduino

Now that we know everything about the relay module, it’s time to put it to use! Let’s wire up our relay module to operate a lamp.

Warning:
This board interacts with HIGH AC voltage. Improper or incorrect use could result in serious injury or death. Therefore, it is intended for people who are familiar with and knowledgeable about HIGH AC voltage.

Begin by connecting the module’s VCC pin to the Arduino’s 5V pin and the GND pin to ground. We will only be using one relay for our experiment, so connect digital pin #6 to the IN1 input pin.

You’ll also need to connect the relay module to the AC-powered device you want to control, in this case, a lamp. You’ll need to cut your live AC line and connect one end of the cut wire (coming from the wall) to COM and the other to NC or NO, depending on what you want your device’s initial state to be.

If you want to keep your device off most of the time and turn it on occasionally, connect the other end of the wire to NO. Otherwise, connect it to NC.

For this project, we want our lamp to be off at first and then turn on when we activate the relay, so we will connect one end of the wire to COM and the other to NO.

The following illustration shows the wiring.

wiring relay module with arduino

We left the jumper in place in the above wiring diagram, so the relay’s electromagnet will be driven directly from the Arduino. In this case, the relay module and the Arduino will not be physically isolated.

If you want to keep them isolated, you must provide a separate 5V power supply voltage to the JD-VCC and GND. The wiring below shows how to accomplish this.

wiring relay module with arduino and external supply

Arduino Example Code

Controlling a relay module with the Arduino is as easy as controlling an LED. Here’s a simple code that will activate the relay for 3 seconds and then deactivate it for 3 seconds.

int RelayPin = 6;

void setup() {
	// Set RelayPin as an output pin
	pinMode(RelayPin, OUTPUT);
}

void loop() {
	// Let's turn on the relay...
	digitalWrite(RelayPin, LOW);
	delay(3000);
	
	// Let's turn off the relay...
	digitalWrite(RelayPin, HIGH);
	delay(3000);
}

Code Explanation:

The sketch begins by declaring the pin to which the relay module’s input pin is connected.

int RelayPin = 6;

In the setup function, we configure the input pin to behave as an output.

pinMode(RelayPin, OUTPUT);

In the loop function, we turn the device ON/OFF by pulling the relay pin LOW/HIGH.

digitalWrite(RelayPin, LOW) pulls the pin LOW, whereas digitalWrite(RelayPin, HIGH) pulls the pin HIGH.

digitalWrite(RelayPin, LOW);
delay(3000);

digitalWrite(RelayPin, HIGH);
delay(3000);

Membrane keypads are an excellent starting point for adding key input to a project because they are inexpensive, long-lasting, and resistant to water. And knowing how to interface them with an Arduino is extremely useful for building a variety of projects that require user input for menu selection, password entry, or robot operation.

Membrane keypads come in a variety of sizes, the most common of which are the 4×3 keypad (12 keys) and the 4×4 keypad (16 keys). They have a layout similar to that of a standard telephone keypad, making them easy to use for anyone.

4x3 & 4x4 Keypads

No matter what size they are, they all function identically. Let’s learn more about Membrane keypads.

Membrane Keypad Construction

Membrane keypads are made of a thin, flexible membrane material and typically have six layers:

membrane keypad construction

Graphic Overlay – Graphic overlays are typically made of polyester because it has better flex life than polycarbonate.

Metal Domes – This layer houses metal domes or polydomes that provide tactile feedback.

Top Circuit Layer – This is typically a polyester printed layer with silver-filled electrically conductive inks. This layer terminates as a flexible tail that connects to the outside world.

Spacer – This layer separates the top and bottom circuits, allowing the switch to remain normally open until the keypad is pressed.

Bottom Circuit Layer – This is typically a polyester printed layer with silver-filled electrically conductive inks. This layer also terminates as a flexible tail.

Rear Adhesive Layer – This layer sticks the keypad to almost anything, which is convenient.

If you peel the paper backing off the keypad, you can see how it is made.

Internal Conductive Traces of 4x3 Membrane Keypad On Back Side

In order to reduce the number of I/O connections, as you can see, all rows and columns are wired together. If this were not the case, interfacing 16 individual pushbuttons, for example, would require 17 I/O pins, one for each pushbutton and one for a common ground. By connecting rows and columns, only 8 pins are required to control the entire 4×4 keypad. This technique of controlling a large number of inputs using fewer pins is known as Multiplexing.

How Does the Matrix Keypad Work?

The matrix keypad consists of pushbutton contacts that are connected to the row and column lines. There is one pin for each column and one pin for each row. So the 4×4 keypad has 4 + 4 = 8 pins, while the 4×3 keypad has 4 + 3 = 7 pins.

This illustration of a basic 4×3 keypad arrangement demonstrates how the internal conductors connect the rows and columns.

4x3 keypad arrangement

When the button is pressed, one of the rows is connected to one of the columns, allowing current to flow between them. When the key ‘4’ is pressed, for instance, column 1 and row 2 are connected.

4x3 Membrane Keypad Working

By identifying which column and row are connected, we can determine which button has been pressed.

Keypad Scanning

Here is how a microcontroller scans rows and columns to identify which button has been pressed.

  1. Each row is connected to an input pin, and each column is connected to an output pin.
  2. Input pins are pulled HIGH by enabling internal pull-up resistors.
  3. The microcontroller then sequentially sets the pin for each column LOW and then checks to see if any of the row pins are LOW. Because pull-up resistors are used, the rows will be high unless a button is pressed.
  4. If a row pin is LOW, it indicates that the button for that row and column is pressed.
  5. The microcontroller then waits for the switch to be released. It then searches the keymap array for the character that corresponds to that button.

4×3 and 4×4 Membrane Keypad Pinout

The keypad has a female Dupont connector. When looking at the front of the keypad, the row pins are on the left, and they usually have a dark strip near the connector to help identify them. The pinouts are as follows:

4x3 Membrane Keypad Pinout
4x4 Mambrane Keypad Pinout

Connecting a 4×3 and a 4×4 Membrane Keypad to an Arduino

Now that we know everything about the membrane keypad, we can start connecting it to Arduino.

The connection is quite straightforward, as the Arduino connections are made in the same order as the keypad connector. Begin by connecting keypad pin 1 to Arduino digital pin 9. And continue doing the same with the subsequent pins (2 to 8, 3 to 7, and so on).

The most convenient way to connect everything is to use an 8-pin male-to-male Dupont ribbon cable.

Wiring 4x3 Membrane Keypad with Arduino
Wiring 4×3 Membrane Keypad with Arduino
Wiring 4x4 Membrane Keypad with Arduino
Wiring 4×4 Membrane Keypad with Arduino

Installing Keypad Library

To determine which key was pressed, we must continuously scan rows and columns. Fortunately, Keypad.h was written to abstract away this unnecessary complexity.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the libraries index and update the list of installed libraries.

Arduino Library Installation - Selecting Manage Libraries in Arduino IDE

Filter your search by entering ‘keypad’. Look for Keypad by Mark Stanley, Alexander Brevig. Click on that entry and then choose Install.

Arduino Keypad Library Installation

Arduino Example Code

The basic sketch below will print key presses to the Serial Monitor.

Code for 4×3 Keypad

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns

char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3}; //connect to the column pinouts of the keypad

//Create an object of keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
  Serial.begin(9600);
}
  
void loop(){
  char key = keypad.getKey();// Read the key
  
  // Print if key pressed
  if (key){
    Serial.print("Key Pressed : ");
    Serial.println(key);
  }
}

Code for 4×4 Keypad

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad

//Create an object of keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
  Serial.begin(9600);
}
  
void loop(){
  char key = keypad.getKey();// Read the key
  
  // Print if key pressed
  if (key){
    Serial.print("Key Pressed : ");
    Serial.println(key);
  }
}

After loading the sketch, open your serial monitor at 9600 baud. Now, press some keys on the keypad; the serial monitor should display the key values.

4x3 & 4x4 Keypad Arduino interfacing Output On Serial Monitor

Code Explanation

The sketch begins by including the Keypad.h library and defining constants for the number of rows and columns on the keypad. If you’re using a different keypad, modify these constants accordingly.

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

Following that, we define a 2-dimensional keymap array keys[ROWS][COLS] that contains characters to be printed when a specific keypad button is pressed. In our sketch, the characters are laid out exactly as they appear on the keypad.

char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

However, you can define these to be anything you want. For example, if you intend to create a calculator project, simply change the array definition to this:

char keys[ROWS][COLS] = {
  {'1','2','3','4'},
  {'5','6','7','8'},
  {'9','0','+','-'},
  {'.','*','/','='}
};

Two more arrays are defined. These arrays specify the Arduino connections to the keypad’s row and column pins.

byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad

Next, we create a keypad library object. The constructor accepts five arguments.

  • makeKeymap(keys) initializes the internal keymap to be equal to the user defined keymap.
  • rowPins and colPins specify the Arduino connections to the keypad’s row and column pins.
  • ROWS and COLS represent the keypad’s rows and columns.
//Create an object of keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

In the Setup, we initialize the serial port.

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

In the Loop, we use the getKey() method to get a key value when a keypress is detected. Then we print it to the serial monitor.

void loop(){
  char key = keypad.getKey();// Read the key
  
  // Print if key pressed
  if (key){
    Serial.print("Key Pressed : ");
    Serial.println(key);
  }
}

Some useful functions of the Keypad library

There are many useful functions you can use with the Keypad object. Some of them are listed below:

  • char waitForKey() waits forever until someone presses a key. Warning – It blocks all other code until a key is pressed. That means no blinking LED’s, no LCD screen updates, no nothing with the exception of interrupt routines.
  • KeyState getState() returns the current state of any of the keys. The four states are IDLE, PRESSED, RELEASED and HOLD.
  • boolean keyStateChanged() let’s you know when the key has changed from one state to another. For example, instead of just testing for a valid key you can test for when a key was pressed.
  • setHoldTime(unsigned int time) sets the amount of milliseconds the user will have to hold a button until the HOLD state is triggered.
  • setDebounceTime(unsigned int time) sets the amount of milliseconds the keypad will wait until it accepts a new keypress/keyEvent.
  • addEventListener(keypadEvent) triggers an event if the keypad is used.

We are surrounded by rotary encoders without even realizing it, as they are used in so many everyday items, from printers and cameras to CNC machines and robots. The most common application of a rotary encoder is the volume knob on a car radio.

A rotary encoder is a type of position sensor that converts the angular position (rotation) of a knob into an output signal that can be used to determine which direction the knob is turned.

Rotary encoders are classified into two types: absolute and incremental. The absolute encoder reports the exact position of the knob in degrees, whereas the incremental encoder reports the number of increments the shaft has moved.

The rotary encoder used in this tutorial is of the incremental type.

Rotary Encoders Vs Potentiometers

Rotary encoders are the modern digital equivalent of potentiometers and are more versatile.

Rotary encoders can rotate 360° without stopping, whereas potentiometers can only rotate 3/4 of the circle.

Potentiometers are used in situations where you need to know the exact position of the knob. Rotary encoders, on the other hand, are used in situations where you need to know the change in position rather than the exact position.

How Rotary Encoders Work?

Inside the encoder is a slotted disc that is connected to pin C, the common ground. It also has two contact pins A and B, as shown below.

rotary encoder internal structure

When you turn the knob, A and B make contact with the common ground pin C in a specific order depending on which direction you turn the knob.

When they make contact with common ground, two signals are generated. These signals are 90° out of phase because one pin makes contact with common ground before the other. It is referred to as quadrature encoding.

rotary encoder working animation

When the knob is turned clockwise, the A pin connects to ground before the B pin. When the knob is turned anticlockwise, the B pin connects to ground before the A pin.

By monitoring when each pin connects or disconnects from ground, we can determine the direction in which the knob is being rotated. This can be accomplished by simply observing the state of B when A’s state changes.

When A changes state:

  • if B != A, then the knob is turned clockwise.
    rotary encoder output pulses in clockwise rotation
  • if B = A, the knob is turned counterclockwise.
    rotary encoder output pulses in anticlockwise rotation

Rotary Encoder Pinout

The pinout of the rotary encoder module is as follows:

rotary encoder module pinout

GND is the ground connection.

VCC is the positive supply voltage, which is typically between 3.3 and 5 volts.

SW is the output of the push button switch (active low). When the knob is depressed, the voltage goes LOW.

DT (Output B) is similar to CLK output, but it lags behind CLK by a 90° phase shift. This output is used to determine the direction of rotation.

CLK (Output A) is the primary output pulse used to determine the amount of rotation. Each time the knob is turned in either direction by just one detent (click), the ‘CLK’ output goes through one cycle of going HIGH and then LOW.

Wiring a Rotary Encoder to an Arduino

Now that we understand how the rotary encoder works, it’s time to put it to use!

Let’s hook up the rotary encoder to the Arduino. The connections are quite simple. Begin by connecting the module’s +V pin to the Arduino’s 5V output and the GND pin to ground.

Now connect the CLK and DT pins to digital pins #2 and #3, respectively. Finally, connect the SW pin to digital pin #4.

The following image shows the wiring.

wiring rotary encoder with arduino uno

Arduino Example Code 1 – Reading Rotary Encoders

Our first example is very straightforward; it simply detects the rotation direction of the encoder and when the button is pressed.

First, try out the sketch, and then we’ll go over it in more detail.

// Rotary Encoder Inputs
#define CLK 2
#define DT 3
#define SW 4

int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
unsigned long lastButtonPress = 0;

void setup() {
        
	// Set encoder pins as inputs
	pinMode(CLK,INPUT);
	pinMode(DT,INPUT);
	pinMode(SW, INPUT_PULLUP);

	// Setup Serial Monitor
	Serial.begin(9600);

	// Read the initial state of CLK
	lastStateCLK = digitalRead(CLK);
}

void loop() {
        
	// Read the current state of CLK
	currentStateCLK = digitalRead(CLK);

	// If last and current state of CLK are different, then pulse occurred
	// React to only 1 state change to avoid double count
	if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){

		// If the DT state is different than the CLK state then
		// the encoder is rotating CCW so decrement
		if (digitalRead(DT) != currentStateCLK) {
			counter --;
			currentDir ="CCW";
		} else {
			// Encoder is rotating CW so increment
			counter ++;
			currentDir ="CW";
		}

		Serial.print("Direction: ");
		Serial.print(currentDir);
		Serial.print(" | Counter: ");
		Serial.println(counter);
	}

	// Remember last CLK state
	lastStateCLK = currentStateCLK;

	// Read the button state
	int btnState = digitalRead(SW);

	//If we detect LOW signal, button is pressed
	if (btnState == LOW) {
		//if 50ms have passed since last LOW pulse, it means that the
		//button has been pressed, released and pressed again
		if (millis() - lastButtonPress > 50) {
			Serial.println("Button pressed!");
		}

		// Remember last button press event
		lastButtonPress = millis();
	}

	// Put in a slight delay to help debounce the reading
	delay(1);
}

You should see similar output in the serial monitor.

rotary encoder output on serial monitor

If the reported rotation is the opposite of what you expect, try swapping the CLK (Output A) and DT (Output B) pins.

Code Explanation:

The sketch begins by declaring the Arduino pins to which the encoder’s CLK, DT, and SW pins are connected.

#define CLK 2
#define DT 3
#define SW 4

Following that, some variables are defined.

  • The counter variable will increment each time the knob is rotated one detent (click).
  • The variables currentStateCLK and lastStateCLK store the state of the CLK output and are used to calculate the amount of rotation.
  • A string named currentDir will be used to display the current rotational direction on the serial monitor.
  • The variable lastButtonPress is used to debounce a switch.
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
unsigned long lastButtonPress = 0;

In the setup section, we first configure the rotary encoder connections as inputs, and then we enable the input pull-up on the SW pin. We also set up the serial monitor.

Finally, we read the current value of the CLK pin and store it in the variable lastStateCLK.

pinMode(CLK,INPUT);
pinMode(DT,INPUT);
pinMode(SW, INPUT_PULLUP);

Serial.begin(9600);

lastStateCLK = digitalRead(CLK);

In the loop section, we check the CLK state again and compare it to the lastStateCLK value. If they differ, it indicates that the knob has been turned. We also check to see if currentStateCLK is 1 in order to react to only one state change and avoid double counting.

currentStateCLK = digitalRead(CLK);

if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){

Within the if statement, the direction of rotation is determined. To accomplish this, we simply read the DT pin and compare it to the current state of the CLK pin.

  • If these two values differ, it indicates that the knob is turned anticlockwise. The counter is then decremented, and the currentDir is set to “CCW”.
  • If these two values are identical, it indicates that the knob is turned clockwise. The counter is then incremented, and the currentDir is set to “CW”.
if (digitalRead(DT) != currentStateCLK) {
    counter --;
    currentDir ="CCW";
} else {
    counter ++;
    currentDir ="CW";
}

The results are then printed to the serial monitor.

Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);

Following the if statement, we update lastStateCLK with the current state of CLK.

lastStateCLK = currentStateCLK;

The next step involves reading and debouncing the push button switch. We first read the current button state, and when it changes to LOW, we wait 50 ms for the push button to debounce.

If the button remains LOW for more than 50ms, it indicates that it has really been pressed. As a result, we print “Button pressed!” to the serial monitor.

int btnState = digitalRead(SW);

if (btnState == LOW) {
    if (millis() - lastButtonPress > 50) {
        Serial.println("Button pressed!");
    }
    lastButtonPress = millis();
}

Then we repeat the process.

Arduino Example Code 2 – Using Interrupts

To read the rotary encoder, we must constantly monitor changes in the DT and CLK signals.

One way to detect these changes is to poll them continuously, as we did in our previous sketch. It is, however, not the best solution for the following reasons.

  • We must perform frequent checks to see if the value has changed. If the signal level does not change, there will be a waste of cycles.
  • There is a possibility of latency between the time the event occurs and the time we check. If we need to react quickly, we will be delayed due to this latency.
  • If the duration of the change is short, it is possible to completely miss the signal change.

One way to deal with this is to use interrupts.

With interrupts, there is no need to continuously poll for a specific event. This frees up the Arduino to perform other tasks without missing an event.

Wiring

Because most Arduino boards (including the Arduino UNO) only have two external interrupts, we can only monitor changes in the DT and CLK signals. Therefore, we will remove the SW pin connection.

Some boards (such as the Arduino Mega 2560) have more external interrupts than others. If you have one of these, you can keep the SW pin connection and modify the sketch below to include the push button code.

The updated layout of the wiring is as follows:

control rotary encoder using interrupts with arduino uno

Arduino Code

Here’s an example of how to read a rotary encoder with interrupts.

// Rotary Encoder Inputs
#define CLK 2
#define DT 3

int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";

void setup() {

	// Set encoder pins as inputs
	pinMode(CLK,INPUT);
	pinMode(DT,INPUT);

	// Setup Serial Monitor
	Serial.begin(9600);

	// Read the initial state of CLK
	lastStateCLK = digitalRead(CLK);
	
	// Call updateEncoder() when any high/low changed seen
	// on interrupt 0 (pin 2), or interrupt 1 (pin 3)
	attachInterrupt(0, updateEncoder, CHANGE);
	attachInterrupt(1, updateEncoder, CHANGE);
}

void loop() {
    //Do some useful stuff here
}

void updateEncoder(){
	// Read the current state of CLK
	currentStateCLK = digitalRead(CLK);

	// If last and current state of CLK are different, then pulse occurred
	// React to only 1 state change to avoid double count
	if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){

		// If the DT state is different than the CLK state then
		// the encoder is rotating CCW so decrement
		if (digitalRead(DT) != currentStateCLK) {
			counter --;
			currentDir ="CCW";
		} else {
			// Encoder is rotating CW so increment
			counter ++;
			currentDir ="CW";
		}

		Serial.print("Direction: ");
		Serial.print(currentDir);
		Serial.print(" | Counter: ");
		Serial.println(counter);
	}

	// Remember last CLK state
	lastStateCLK = currentStateCLK;
}

Observe that the main loop of this program is left empty, so Arduino will be busy doing nothing.

Now, as you turn the knob, you should see similar output on the serial monitor.

rotary encoder interrupt output on serial monitor

Code Explanation:

This sketch simply monitors digital pins 2 (corresponding to interrupt 0) and 3 (corresponding to interrupt 1) for signal changes. In other words, it detects when the voltage changes from HIGH to LOW or LOW to HIGH as a result of turning the knob.

When a change occurs, the Arduino immediately detects it, saves its execution state, executes the function updateEncoder() (also known as the Interrupt Service Routine or simply ISR), and then returns to whatever it was doing before.

The following two lines configure the interrupts. The attachInterrupt() function instructs the Arduino which pin to monitor, which ISR to execute when the interrupt is triggered, and what type of trigger to look for.

attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);

Arduino Example Code 3 – Controlling Servo Motor with Rotary Encoder

In the following example, we will use a rotary encoder to control the position of a servo motor.

This project can be very useful in a variety of situations. For example, if you want to operate a robotic arm, it can help you position the arm and its grip accurately.

If you are unfamiliar with servo motors, please read the following tutorial.

Wiring

Let’s include a servo motor in our project. Connect the red wire of the servo motor to the external 5V supply, the black/brown wire to ground, and the orange/yellow wire to PWM enabled digital pin 9.

You can, of course, use the Arduino’s 5V output, but keep in mind that the servo may induce electrical noise on the 5V supply line, which could damage your Arduino. Therefore, it is advised that you use an external power supply.

wiring for controlling servo motor with rotary encoder

Arduino Code

Here is the code for using the rotary encoder to precisely control the servo motor. Each time the knob is rotated one detent (click), the position of the servo arm changes by one degree.

// Include the Servo Library
#include <Servo.h>

// Rotary Encoder Inputs
#define CLK 2
#define DT 3

Servo servo;
int counter = 0;
int currentStateCLK;
int lastStateCLK;

void setup() {

	// Set encoder pins as inputs
	pinMode(CLK,INPUT);
	pinMode(DT,INPUT);
	
	// Setup Serial Monitor
	Serial.begin(9600);
	
	// Attach servo on pin 9 to the servo object
	servo.attach(9);
	servo.write(counter);
	
	// Read the initial state of CLK
	lastStateCLK = digitalRead(CLK);
}

void loop() {
        
	// Read the current state of CLK
	currentStateCLK = digitalRead(CLK);
	
	// If last and current state of CLK are different, then pulse occurred
	// React to only 1 state change to avoid double count
	if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){
		
		// If the DT state is different than the CLK state then
		// the encoder is rotating CCW so decrement
		if (digitalRead(DT) != currentStateCLK) {
			counter --;
			if (counter<0)
				counter=0;
		} else {
			// Encoder is rotating CW so increment
			counter ++;
			if (counter>179)
				counter=179;
		}
		// Move the servo
		servo.write(counter);
		Serial.print("Position: ");
		Serial.println(counter);
	}
	
	// Remember last CLK state
	lastStateCLK = currentStateCLK;
}

Code Explanation:

If you compare this sketch to our very first sketch, you will notice that, with a few exceptions, they are quite similar.

In the beginning, we include the built-in Arduino Servo library and create a servo object to represent our servo motor.

#include <Servo.h>

Servo servo;

In the setup, we attach the servo object to pin 9 (to which the control pin of the servo motor is connected).

servo.attach(9);

In the loop, we limit the counter to the range 0 to 179 because servo motors only accept values within this range.

if (digitalRead(DT) != currentStateCLK) {
    counter --;
    if (counter<0)
        counter=0;
} else {
    counter ++;
    if (counter>179)
        counter=179;
}

Finally, the counter value is used to position the servo motor.

servo.write(counter);

One of the nice things about the Arduino is that it has a fair amount of I/O pins to work with. You can wire up a few buttons, sensors, servos, and so on, but as the list grows, you will quickly run out of pins.

The solution is to use a ‘shift register,’ which allows you to add more I/O pins to the Arduino (or any microcontroller). By far the most widely used shift register is the 74HC595, also known as just “595”.

The 74HC595 controls eight different output pins with only three input pins. If you need more than 8 I/O pins, you can daisychain as many shift registers as necessary to generate a large number of I/O pins.

The 74HC595 achieves this through a technique known as bit-shifting. If you are interested in learning more about bit-shifting, you will find this Wikipedia article extremely useful.

When Should You Use a Shift Register?

Shift registers are often used to increase the number of I/O pins on a microcontroller.

For example, If your project needs to control 16 individual LEDs, you will, of course, require 16 pins of an Arduino, which is not possible. This is where the shift register comes in handy.

With two shift registers connected in series, you can control 16 LEDs with only three I/O pins. That is quite a difference, and the more shift registers you chain together, the more pins you’ll add. In theory, by daisy-chaining shift registers, an infinite number of I/O pins can be added.

The Original Nintendo Controller, released in 1985, is a practical application of a shift register. The main microcontroller of the Nintendo Entertainment System, used shift registers to gather button presses from the controller.

Nintendo Entertainment System NES Controller FL
Photo credit: Wikipedia

SIPO vs PISO Shift Registers

There are two types of shift registers, SIPO (serial-in-parallel-out) and PISO (parallel-in-serial-out).

SIPO is useful for controlling a large number of outputs, such as LEDs. While PISO is useful for gathering a large number of inputs, such as buttons, similar to the original Nintendo controller discussed above.

The most popular SIPO chip is 74HC595, and the most popular PISO chip is 74HC165.

How does the 74HC595 Shift Register work?

The 74HC595 has two 8-bit registers (which can be thought of as “memory containers”). The first is referred to as the Shift Register, and the second as the Storage/Latch Register.

Every time the 74HC595 receives a clock pulse, two things happen:

  • The bits contained in the shift register are shifted to the left by one position. Bit 0’s value is pushed into bit 1, while bit 1’s value is pushed into bit 2, and so on.
  • Bit 0 in the shift register accepts the current value on the DATA pin. On the rising edge of the clock pulse, if the DATA pin is high, 1 is pushed into the shift register, otherwise 0.

This process will continue as long as 74HC595 is clocked.

When the latch pin is enabled, the contents of the shift register are copied to the storage/latch register. Each bit of the storage register is linked to one of the IC’s output pins QA-QH. As a result, whenever the value in the storage register changes, the output changes.

The animation below will help you understand it better.

74HC595 Shift Register Working

74HC595 Shift Register Pinout

It is important to note that several companies manufacture the same 74HC595 chip. In this article, we will discuss the ubiquitous SN74HC595N from Texas Instruments. If you happen to have a different one, carefully read the datasheet and make note of any differences.

Let’s take a look at its pinout.

Pinout 74HC595 Shift Register

GND is the ground pin.

VCC is the power supply for the 74HC595 shift register, which must be connected to 5V.

SER (Serial Input) pin is used to send data into the shift register one bit at a time.

SRCLK (Shift Register Clock) is the clock for the shift-register and is positive-edge triggered. This means that the bits are pushed in on the rising edge of the clock.

RCLK (Register Clock / Latch) is a very important pin. When this pin is pulled HIGH, the contents of the Shift Register are copied into the Storage/Latch Register, which eventually appears at the output. So, the latch pin can be seen as the last step before we see our results at the output.

SRCLR (Shift Register Clear) pin allows us to reset the entire Shift Register, setting all the bits to zero. Because this is an active-low pin, we must pull the SRCLR pin LOW to perform the reset.

OE (Output Enable) is also an active-low pin: when pulled HIGH, the output pins are disabled (set to high impedance state). When it is pulled LOW, the output pins function normally.

QA–QH (Output Enable) are the output pins.

QH’ pin outputs bit 7 of the shift register. This allows you to daisy-chain the 74HC595s. If you connect this pin to the SER pin of another 74HC595, and feed both ICs the same clock signal, they will behave as if they were a single IC with 16 outputs. Of course, with this technique, you can daisy-chain as many ICs as you want.

Wiring a 74HC595 Shift Register to an Arduino

Now that we know the basics of how 74HC595 works, we can start connecting it to our Arduino!

Start by putting the shift register on your breadboard. Make sure that each side of the IC is on a different side of the breadboard. With the little U-shaped notch facing up, pin 1 of the chip is to the left of this notch.

Connect pins 16 (VCC) and 10 (SRCLR) to the Arduino’s 5V output, and pins 8 (GND) and 13 (OE) to the ground. This should keep the IC in the normal working mode.

Next, connect the three pins that will be used to control the shift register. Connect pin 11 (SRCLK), pin 12 (RCLK), and pin 14 (SER) of the shift register to the Arduino’s pins 6, 5, and 4, respectively.

All that remains is to connect the LEDs to the output pins. Connect the cathode (short pin) of each LED to a common ground, and the anode (long pin) of each LED to its respective shift register output pin.

Don’t forget to add a 220Ω resistor in series to protect the LEDs from being overloaded.

When connecting the LEDs, ensure that QA is connected to the first LED and QH is connected to the last; otherwise, the LEDs will not light up in the correct order!

The following diagram shows you how to wire everything up.

Arduino Wiring Fritzing Connections with 74HC595 Shift Register

Using the shift register to supply power in this manner is called sourcing current. Some shift registers can’t source current, they can only do what is called sinking current. If you have one of those, you will need to reverse the direction of the LEDs, connecting the anodes directly to power and the cathodes to the shift register outputs.

Arduino Example Code

Here is an example sketch that turns on each LED in turn until they are all on, then turns them all off and the cycle repeats.

First, try out the sketch, and then we’ll go over it in detail.

int latchPin = 5;      // Latch pin of 74HC595 is connected to Digital pin 5
int clockPin = 6;      // Clock pin of 74HC595 is connected to Digital pin 6
int dataPin = 4;       // Data pin of 74HC595 is connected to Digital pin 4

byte leds = 0;         // Variable to hold the pattern of which LEDs are currently turned on or off

/*
 * setup() - this function runs once when you turn your Arduino on
 * We initialize the serial connection with the computer
 */
void setup() 
{
  // Set all the pins of 74HC595 as OUTPUT
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
}

/*
 * loop() - this function runs over and over again
 */
void loop() 
{
  leds = 0;        // Initially turns all the LEDs off, by giving the variable 'leds' the value 0
  updateShiftRegister();
  delay(500);
  for (int i = 0; i < 8; i++)        // Turn all the LEDs ON one by one.
  {
    bitSet(leds, i);                // Set the bit that controls that LED in the variable 'leds'
    updateShiftRegister();
    delay(500);
  }
}

/*
 * updateShiftRegister() - This function sets the latchPin to low, then calls the Arduino function 'shiftOut' to shift out contents of variable 'leds' in the shift register before putting the 'latchPin' high again.
 */
void updateShiftRegister()
{
   digitalWrite(latchPin, LOW);
   shiftOut(dataPin, clockPin, LSBFIRST, leds);
   digitalWrite(latchPin, HIGH);
}

After uploading the code to the Arduino, you should see the following output:

74HC595 Shift Register Sketch Output

Code Explanation:

The first step is to define the three control pins of the 74HC595, namely the latch, clock, and data pin, which are connected to the Arduino’s digital pins #5, #6, and #4, respectively.

int latchPin = 5;
int clockPin = 6;
int dataPin = 4;

Next, a variable called leds of datatype byte is defined. The byte datatype can store eight bits, which is ideal for tracking the on/off status of eight LEDs.

// Variable to hold the pattern of which LEDs are currently turned on or off
byte leds = 0;

In the ‘setup’ function, we simply configure the three pins to be digital output.

void setup() 
{
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
}

The loop function first turns off all the LEDs by setting all the bits of the variable leds to 0 and calling a custom function updateShiftRegister(). We’ll talk about how updateShiftRegister() works later.

The program pauses for half a second before counting from 0 to 7 using the for loop and the variable i.

During each iteration, the built-in function bitSet() is called to set the bit in the variable leds that controls a particular LED, and the function updateShiftRegister() is called to change the status of LEDs. Then there is a half-second delay before incrementing i and turning on the next LED.

void loop() 
{
  leds = 0;
  updateShiftRegister();
  delay(500);
  for (int i = 0; i < 8; i++)
  {
    bitSet(leds, i);
    updateShiftRegister();
    delay(500);
  }
}

The function updateShiftRegister() first sets the latch pin to LOW and then calls the built-in function shiftOut() before setting the latch pin to HIGH again.

The Arduino IDE includes a shiftOut() function that is designed specifically for shift registers. This function allows us to shift out a byte of data one bit at a time.

The shiftOut() function accepts four inputs, the first two of which are Data and Clock pins. The third parameter specifies whether the bits should be shifted out in MSBFIRST or LSBFIRST order (Most Significant Bit First, or, Least Significant Bit First). The final parameter is the actual data to be shifted out, in this case leds.

Setting the latch pin to HIGH copies the contents of the Shift Register into the Storage/Latch Register. Notice that we set the latch pin LOW before shifting bits because, if we don’t, the wrong LEDs would flicker while data is being loaded into the shift register.

void updateShiftRegister()
{
   digitalWrite(latchPin, LOW);
   shiftOut(dataPin, clockPin, LSBFIRST, leds);
   digitalWrite(latchPin, HIGH);
}

Arduino Example 2: Controlling Brightness Using PWM

Here’s another project based on the same setup but with a slight variation: we manipulate the OE pin on the IC to control the brightness of the output LEDs!

We already know that the OE (Output Enable) pin functions as a switch. When this pin is set to HIGH, the output pins are disabled (remember, it is active-low). When OE is set to LOW, the output pins function normally.

In our previous example, we connected OE to ground, allowing the output to be active at all times. If we connect this pin to any of the Arduino’s digital pins and program it to toggle its state, we can get the following result.

74HC595 Shift Register Output Enable Toggle Output

However, by connecting OE to any of the Arduino’s PWM-enabled pins, we can control the brightness of the LEDs, as shown below.

74HC595 Shift Register Output Enable PWM Output

Wiring

All you have to do is change the connection to 74HC595’s pin 13 (OE). Instead of connecting OE to ground, connect it to Arduino pin 3.

Arduino PWM Brightness Control Wiring Fritzing Connections with 74HC595 Shift Register

Example Code

This sketch works similarly to the previous one, with the exception that once all of the LEDs are lit, they will gradually fade back to off.

int latchPin = 5;           // Latch pin of 74HC595 is connected to Digital pin 5
int clockPin = 6;           // Clock pin of 74HC595 is connected to Digital pin 6
int dataPin = 4;            // Data pin of 74HC595 is connected to Digital pin 4
int outputEnablePin = 3;    // OE pin of 74HC595 is connected to PWM pin 3

byte leds = 0;              // Variable to hold the pattern of which LEDs are currently turned on or off

/*
 * setup() - this function runs once when you turn your Arduino on
 * We initialize the serial connection with the computer
 */
void setup() 
{
  // Set all the pins of 74HC595 as OUTPUT
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
  pinMode(outputEnablePin, OUTPUT); 
}

/*
 * loop() - this function runs over and over again
 */
void loop() 
{
  setBrightness(255);
  leds = 0;                // Initially turns all the LEDs off, by giving the variable 'leds' the value 0
  updateShiftRegister();
  delay(500);
  for (int i = 0; i < 8; i++)        // Turn all the LEDs ON one by one.
  {
    bitSet(leds, i);                // Set the bit that controls that LED in the variable 'leds'
    updateShiftRegister();
    delay(500);
  }
  for (byte b = 255; b > 0; b--)        // Gradually fade all the LEDs back to off
  {
    setBrightness(b);
    delay(50);
  }
}

/*
 * updateShiftRegister() - This function sets the latchPin to low, then calls the Arduino function 'shiftOut' to shift out contents of variable 'leds' in the shift register before putting the 'latchPin' high again.
 */
void updateShiftRegister()
{
   digitalWrite(latchPin, LOW);
   shiftOut(dataPin, clockPin, LSBFIRST, leds);
   digitalWrite(latchPin, HIGH);
}

/*
 * setBrightness() - Generates PWM signal and writes it to OE pin.
 */
void setBrightness(byte brightness) // 0 to 255
{
  analogWrite(outputEnablePin, 255-brightness);
}

When you upload the code to the Arduino, you should see the following output:

74HC595 Shift Register PWM Brightness Control Sketch Output

When you hear the word thumb joystick, the first thing that comes to mind is a game controller. Although they are most commonly used for gaming, you can do a lot of cool things with them if you’re into DIY electronics, such as controlling a robot or rover or, controlling camera movement.

Let’s take a deeper dive into the joystick module and see how it works.

Hardware Overview

Those who are familiar with the PS2 (PlayStation 2) controller will notice that this joystick is strikingly similar to the one used in that device. It is a self-centering spring-loaded joystick, which means that when you release it, it will center itself. It also has a comfortable cup-type knob/cap that feels like a thumb-stick.

Potentiometers

The basic idea behind a joystick is to convert the stick’s position on two axes — the X-axis (left to right) and the Y-axis (up and down) — into an electrical signal that a microcontroller can process. This is accomplished by incorporating two 5K potentiometers (one for each axis) connected with a gymbal mechanism that separates “horizontal” and “vertical” movements.

PS2 Joystick Module 2 Potentiometers Internal Structure

The two gray boxes on either side of the joystick are the potentiometers. If you move the joystick while keeping an eye on the potentiometer, you’ll notice that each potentiometer only detects movement in one direction. We’ll go over how they work in more detail later.

Momentary Pushbutton switch

PS2 Joystick Module Push Button Switch Internal Structure

This joystick also has a momentary pushbutton switch (a small black box on one side of the joystick) that is actuated when you press the joystick knob. When the knob is pressed down, you’ll notice a lever pressing down on the switch’s head. It is designed in such a way that the lever operates regardless of the position of the joystick.

How does the Thumb Joystick Module Work?

It is truly remarkable how a joystick can translate every tiny motion of your fingertips. This is made possible by the joystick’s design, which consists of two potentiometers and a gimbal mechanism.

Gimbal Mechanism

When you move the joystick, a thin rod that sits between two rotatable-slotted-shafts (Gimbal) moves. One of the shafts allows movement along the X-axis (left and right), while the other allows movement along the Y-axis (up and down).

analog joystick internal gimbal structure

When you move the joystick back and forth, the Y-axis shaft pivots. When you move it left to right, the X-axis shaft pivots. And when you move it diagonally, both shafts pivot.

2-Axis Joystick Working Gimbal Mechanism

Each shaft is connected to a potentiometer so that moving the shaft rotates the corresponding potentiometer’s wiper. In other words, pushing the knob all the way forward will cause the potentiometer wiper to move to one end of the resistive track, and pulling it back will cause it to move to the opposite end.

Potentiometer Working In Joystick Module

By reading these potentiometers, the knob position can be determined.

Reading analog values from Joystick

The joystick outputs an analog signal whose voltage varies between 0 and 5V. When you move the joystick along the X axis from one extreme to the other, the X output changes from 0 to 5V, and the same thing happens when you move it along the Y axis. And, when the joystick is centered (rest position), the output voltage is approximately half of VCC, or 2.5V.

This output voltage can be fed to an ADC on a microcontroller to determine the physical position of the joystick.

Because the Arduino board has a 10-bit ADC resolution, the values on each analog channel (axis) can range from 0 to 1023. Therefore, when the joystick is moved from one extreme to the other, it will read a value between 0 and 1023 for the corresponding channel. When the joystick is centered, the vertical and horizontal channels will both read 512.

The figure below depicts the X and Y axes as well as how the outputs will respond when the joystick is moved in different directions.

PS2 Joystick Module Movement Analog Values on Arduino

Thumb Joystick Module Pinout

Let’s take a look at the pinout of the Thumb Joystick module.

Pinout PS2 Joystick Module

GND is the ground pin.

VCC provides power to the module. Connect this to your positive supply (usually 5V or 3.3V depending on your logic levels).

VRx is the horizontal output voltage. Moving the joystick from left to right causes the output voltage to change from 0 to VCC. The joystick will read approximately half of VCC when it is centered (rest position).

VRy is the vertical output voltage. Moving the joystick up and down causes the output voltage to change from 0 to VCC. The joystick will read approximately half of VCC when it is centered (rest position).

SW is the output from the pushbutton switch. By default, the switch output is floating. To read the switch, a pull-up resistor is required so that when the joystick knob is pressed, the switch output becomes LOW, otherwise it remains HIGH. Keep in mind that the input pin to which the switch is connected must have the internal pull-up enabled, or an external pull-up resistor must be connected.

Wiring a Thumb Joystick Module to an Arduino

Now that we know everything about the joystick module, let’s hook it up to the Arduino.

To begin, connect VRx to Arduino’s analog pin A0 and VRy to analog pin A1. To detect whether the joystick is pressed, we connect the joystick’s SW pin to Arduino digital pin D8.

Finally, connect the VCC pin to the 5V terminal and the GND pin to the Arduino’s ground terminal.

The following table lists the pin connections:

Joystick ModuleArduino
GNDGND
VCC5V
VRxA0
VRyA1
SW8

The figure below shows how to connect the thumb joystick module to the Arduino.

Arduino Wiring Fritzing Connections with PS2 2-axis Joystick Module

That’s it. It’s time to put your joystick skills to the test.

Arduino Example 1 – Reading the Joystick

Here’s a simple Arduino sketch that configures the microcontroller to read the inputs and then continuously print the values to the serial monitor.

// Arduino pin numbers
const int SW_pin = 8; // digital pin connected to switch output
const int X_pin = 0; // analog pin connected to X output
const int Y_pin = 1; // analog pin connected to Y output

void setup() {
  pinMode(SW_pin, INPUT);
  digitalWrite(SW_pin, HIGH);
  Serial.begin(9600);
}

void loop() {
  Serial.print("Switch:  ");
  Serial.print(digitalRead(SW_pin));
  Serial.print(" | ");
  Serial.print("X-axis: ");
  Serial.print(analogRead(X_pin));
  Serial.print(" | ");
  Serial.print("Y-axis: ");
  Serial.print(analogRead(Y_pin));
  Serial.println(" | ");
  delay(200);
}

After uploading the sketch, you should see the following output on the serial monitor.

PS2 Joystick Module Arduino Sketch Output on serial window

Code Explanation:

As the first step of the sketch, the joystick module’s connections are declared.

// Arduino pin numbers
const int SW_pin = 8; // digital pin connected to switch output
const int X_pin = 0; // analog pin connected to X output
const int Y_pin = 1; // analog pin connected to Y output

In the setup section, we configure the SW pin to behave as an INPUT and pull it HIGH. We also begin serial communication with the PC.

  pinMode(SW_pin, INPUT);
  digitalWrite(SW_pin, HIGH);
  Serial.begin(9600);

In the loop section, we simply use the digitalRead() function to read the button’s state and the analogRead() function to read the VRx and VRy pins. The values are then printed to the serial monitor.

  Serial.print("Switch:  ");
  Serial.print(digitalRead(SW_pin));
  Serial.print(" | ");
  Serial.print("X-axis: ");
  Serial.print(analogRead(X_pin));
  Serial.print(" | ");
  Serial.print("Y-axis: ");
  Serial.print(analogRead(Y_pin));
  Serial.println(" | ");
  delay(200);

Arduino Example 2 – Animating Joystick Movements In Processing IDE

Let’s create an Arduino project to demonstrate how a joystick module can be used to control animations in the Processing IDE.

This is what the output looks like.

Arduino Project - Animating Joystick in Precessing IDE
Joystick movement animation in Precessing IDE

Of course, this project can be expanded to animate characters, conduct surveillance, or pilot unmanned aircraft.

Arduino Code

This example sends the state of the pushbutton and two analog outputs serially to the computer. The associated Processing sketch reads the serial data to animate the joystick position.

The sketch is identical to the one above, except that the values printed on the serial monitor are separated by commas. The reason for separating values with commas is to make data transfer easier. The concept here is to send the values as a comma-separated string that we can parse in the Processing IDE to retrieve the values.

Upload the sketch below to your Arduino.

int xValue = 0 ; // read value of the X axis	
int yValue = 0 ; // read value of the Y axis	
int bValue = 0 ; // value of the button reading	

void setup()	
{	
	Serial.begin(9600) ; // Open the serial port
	pinMode(8,INPUT) ; // Configure Pin 2 as input
	digitalWrite(8,HIGH);	
}	

void loop()	
{	
	// Read analog port values A0 and A1	
	xValue = analogRead(A0);	
	yValue = analogRead(A1);	

	// Read the logic value on pin 2	
	bValue = digitalRead(8);	

	// We display our data separated by a comma	
	Serial.print(xValue,DEC);
	Serial.print(",");
	Serial.print(yValue,DEC);
	Serial.print(",");
	Serial.print(!bValue);

	// We end with a newline character to facilitate subsequent analysis	
	Serial.print("\n");

	// Small delay before the next measurement	
	delay(10);	
}

Processing Code

After uploading the program to Arduino, we can begin animating the joystick position in the Processing IDE. Keep your Arduino plugged in and run the Processing code below.

import processing.serial.*; //import the Serial library
Serial myPort;

int x; // variable holding the value from A0
int y; // variable holding the value from A1
int b; // variable holding the value from digital pin 2
PFont f; // define the font variable
String portName;
String val;

void setup()
{
  size ( 512 , 512 ) ; // window size
  
  // we are opening the port
   myPort = new Serial(this, Serial.list()[0], 9600);
  myPort.bufferUntil('\n'); 
  
  // choose the font and size
  f = createFont("Arial", 16, true); // Arial, 16px, anti-aliasing
  textFont ( f, 16 ) ; // size 16px
}

// drawing loop
void draw()
{
  fill(0) ; // set the fill color to black
  clear() ; // clean the screen
  
  fill(255) ; // set the fill color to white
  
  if (b == 1) // check if the button is pressed
  {
    // draw a larger circle with specified coordinates
    ellipse(x/2,y/2, 50, 50);
  } 
  else
  {
    // we draw a circle with a certain coordinates
    ellipse(x/2,y/2, 25, 25);
  }
  
  // we display data
  text("AnalogX="+(1023-x)+" AnalogY="+(1023-y),10,20);
}


// data support from the serial port
void serialEvent( Serial myPort) 
{
  // read the data until the newline n appears
  val = myPort.readStringUntil('\n');
  
  if (val != null)
  {
        val = trim(val);
        
    // break up the decimal and new line reading
    int[] vals = int(splitTokens(val, ","));
    
    // we assign to variables
    x = vals[0];
    y = vals[1] ;
    b = vals[2];

  }
}

Code Explanation:

Let’s quickly analyze the code. First, we import the serial library in order to read values from the serial port.

import processing.serial.*; //import the Serial library
Serial myPort;

The variables that will hold the x-axis, y-axis, and button state values are then declared.

int x; // variable holding the value from A0
int y; // variable holding the value from A1
int b; // variable holding the value from digital pin 2
PFont f; // define the font variable
String portName;
String val;

In the setup function, we first set the window size to 512×512. Next, we use Serial.list()[0] to open the first available serial port. If this does not work, select the port to which Arduino is connected. We also select a font for displaying our values on the window.

  size ( 512 , 512 ) ; // window size
  
  // we are opening the port
   myPort = new Serial(this, Serial.list()[0], 9600);
  myPort.bufferUntil('\n'); 
  
  // choose the font and size
  f = createFont("Arial", 16, true); // Arial, 16px, anti-aliasing
  textFont ( f, 16 ) ; // size 16px

At the start of the draw function, the window’s background color is set to black. Then we choose a white color to draw a small circle to represent the position of the joystick. To show that the pushbutton has been pressed, we make the circle bigger.

fill(0) ; // set the fill color to black
  clear() ; // clean the screen
  
  fill(255) ; // set the fill color to white
  
  if (b == 1) // check if the button is pressed
  {
    // draw a larger circle with specified coordinates
    ellipse(x/2,y/2, 50, 50);
  } 
  else
  {
    // we draw a circle with a certain coordinates
    ellipse(x/2,y/2, 25, 25);
  }

The x- and y-axis values are then printed in the upper left corner of the window.

  // we display data
  text("AnalogX="+(1023-x)+" AnalogY="+(1023-y),10,20);

The custom function serialEvent(Serial myPort) helps us parse a comma-separated list of values.

void serialEvent( Serial myPort) 
{
  // read the data until the newline n appears
  val = myPort.readStringUntil('\n');
  
  if (val != null)
  {
        val = trim(val);
        
    // break up the decimal and new line reading
    int[] vals = int(splitTokens(val, ","));
    
    // we assign to variables
    x = vals[0];
    y = vals[1] ;
    b = vals[2];

  }
}


Login
ADS CODE