Introduction: 16-bit I2C Temperature Monitor Using Arduino

Picture of 16-bit I2C Temperature Monitor Using Arduino

If you find yourself needing an ADC with greater resolution than the Arduino's onboard 10-bit ADC provides, the ADS1115 16-bit ADC is a popular and simple to use chip who communicates via I2C interface. Here, I will walk you through the steps of assembling your set-up, provide some code and go through a little theory. Let's get started!

Step 1: Gather the Hardware

Picture of Gather the Hardware

These are the ingredients you will need to get started:

- Arduino Uno

- Solderless Breadboard

- 16-bit I2C ADC (ADS1115)

- 10k Ohm resistor

- 10k Ohm NTC Thermistor

- Jumper Wires

Note: The ADS1115 can be found on Amazon and eBay for a wide range of prices. I ordered 5 off of eBay from China for less than the price of one on Amazon and they arrived in about 2 weeks. Definitely do your homework to find a balance between project lead-time and price willing to pay. As far as I can tell the boards are identical.

Step 2: ADS1115 16-bit ADC+PGA

Picture of ADS1115 16-bit ADC+PGA

This whole project revolves around this little board. In short, it is an Analog-to-Digital Converter (ADC) with 16-bits of resolution with a Programmable Gain Amplifier (PGA). You may be thinking to yourself, "but doesn't the Arduino already have and ADC built-in??" and the answer is "Yes", however it has only 10-bits of resolution. So what is the big deal about this "resolution" thing I keep mentioning? Well when you want to bring information in from the outside world to a micro-controller you use a sensor. The problem is, especially in the case of temperature, the signal is in the form of a voltage who's value is between a minimum and a maximum value. In the case of an Arduino and peripherals, this range is from ground (0 Volts) to Vcc (on the order of 5 Volts). Other voltage ranges may be used with an Arduino but a voltage divider network would need to be employed to prevent the maximum input voltage from exceeding Vcc; more on that in another tutorial! As you know, micro-controllers process information in the form of digital signals who have only two states, HIGH and LOW or 1 and 0. Therefore, to integrate the two you need an ADC! This ADC converts a voltage into a digital signal by taking a voltage and mapping it to a value between a defined minimum and maximum. This is where ADC resolution becomes inportant if one needs an accurate and reliable measurement. You can see in the hand written image the general formula for ADC conversions as well as a couple examples, but the punch line is on the lower right side. The Arduino's 10-bit ADC takes steps of 4.9mV where-as the ADS1115 takes steps of 76.3 uV and as you will see later, the smaller the ADC step, the better!

Note: The ADC resolution is found by raising 2 to the power of the number of bits. For example 2^(16) = 65536.

Step 3: Assembly Time!

Picture of Assembly Time!

The circuit is quite straight forward, just ensure that the SDA and SCL wires are in their correct places. Also, although as you will see in the code, the default I2C address is 0x48, but you can set the address to one of four so that you may connect up to 4 ADS1115 boards to a single I2C setup! Check out the image above to see how to change the address.

Step 4: The Code.

First, we have to import the proper library. There are many different ways to do this but since this one was written by the fine folks at Adafruit I found the most effective way to be through: Sketch -> Include Library -> Manage Libraries then type "Adafruit_ADS1X15" and click install. Then, copy and paste this sketch borrowed from:

Which is an excellent resource for more information on the ADS1115.


#include <Wire.h>
#include <Adafruit_ADS1015.h>

Adafruit_ADS1115 ads(0x48);

float Voltage = 0.0;

void setup(void) {




void loop(void) {

int16_t adc0; // we read from the ADC, we have a sixteen bit integer as a result

adc0 = ads.readADC_SingleEnded(0);

Voltage = (adc0 * 0.1875)/1000;

Serial.print("AIN0: ");


Serial.print("\tVoltage: ");

Serial.println(Voltage, 7);





If you open the serial monitor, you should see the ADC value as well as the Voltage at analog pin A0 (on the ADS1115).

Step 5: Now Let's Modify It.

So now that we have a proof of concept working, let's use it to give us some meaningful outputs. Here is the modified code to output the actual temperature in Celcius.


#include <Wire.h>
#include <Adafruit_ADS1015.h>

Adafruit_ADS1115 ads(0x48);

float Voltage = 0.0;

int thermistor_25 = 10000;

float refCurrent = 0.0001;

void setup(void) {




void loop(void) {

int16_t adc0; // we read from the ADC, we have a sixteen bit integer as a result

adc0 = ads.readADC_SingleEnded(0); // Read ADC value from ADS1115

Voltage = adc0 * (5.0 / 65535); // Replace 5.0 with whatever the actual Vcc of your Arduino is

float resistance = (Voltage / refCurrent); // Using Ohm's Law to calculate resistance of thermistor

float ln = log(resistance / thermistor_25); // Log of the ratio of thermistor resistance and resistance at 25 deg. C

float kelvin = 1 / (0.0033540170 + (0.00025617244 * ln) + (0.0000021400943 * ln * ln) + (-0.000000072405219 * ln * ln * ln)); // Using the Steinhart-Hart Thermistor Equation to calculate temp in K

float temp = kelvin - 273.15; // Converting Kelvin to Celcuis

Serial.print("AIN0: "); // Print ADC value to Serial Monitor


Serial.print("\tTemperature: "); // Print temperature to Serial Monitor in Celcius

Serial.println(temp, 7);


delay(500); // Change how often the sketch outputs reading



This should get you within a degree or so in Celcius and adjustments may be made for your set-up to fine tune the accuracy.

Step 6: Conclusion

That should be enough to get you going. Here are a few links that I found very helpful in setting this up:


Next, we will establish communication with Matlab and create an App to collect temperature data on multiple channels!

Please comment or pm if you have any questions, suggestions, or criticisms!


AnuragM42 (author)2017-11-14

HEY that was nice i go through that and its help me lot

Could you please share me the same program implement with FSR sensor how to get the correct value from it

When i was doing its shows maximum value and again it goes to minimum but i want gradually increment of value from 0 to 65535

Mendelbrot (author)AnuragM422017-11-14

Hey, I am a little unsure about what you are trying to do. Are you trying to read a voltage but it will only output a 0 (minimum value) or 65535 (maximum value)?

fmarengo (author)2017-04-04

Good tutorial, thank you!
Do you happen to know how to configure the sampling rate?

Mendelbrot made it! (author)fmarengo2017-04-04

Also, you may be able to place the function: ADS1015_REG_CONFIG_DR_128SPS;

(switch out 128 for your desired SPS) in the setup() portion of your sketch.

fmarengo (author)Mendelbrot2017-04-05

Thank you very much, Mendelbrot! I really apreciate your help.

The method you proposed for configuring the sampling rate is clear and quite practical. However, now I realize that I need just 1 single conversion each T ms. I mean, I wait T ms, activate Arduino's pin 12 and read the analog value detected by the ADC. Then I wait for another T ms, activate Arduino's pin 13 and read the value from the ADC. To this aim, I am using TimerOne interrupt, and I've done it using the internal 10-bit ADC from Arduino. Now I need to do the same operation using the ADS1115. Is there any easy way to do this, please? If your answer is no, where can I find further help, please?
If you prefer to discuss via e-mail. mi address is fmarengorodriguez (at) gmail (dot) com.
Thank you for your time!

Mendelbrot made it! (author)fmarengo2017-04-06

Alright, so if I interpreted your problem correctly, you want to collect ADC values from a ADS1115 at regular intervals triggered by an event (program call, button press etc.). So to do this, I set up on a breadboard an ATMEGA328P (but I'll assume you are using an Arduino UNO) and an ADS1115 as seen in the picture below (I can provide a schematic if you'd like). The Arduino communicates with the ADS1115 via the I2C interface so we must make the connections between the SCK and SDA pin on both chips; these are labeled on both PCBs. Then, we provide power ground to the ADS1115 from the Arduino along with pulling the address pin (addr) on the ADS1115 to ground to give it the default address 0x48. Then, I put the wiper of a potentiometer to pin A0 on the ADS1115 and a momentary push button on DigitalPin 9 on the Arduino. Then go ahead and upload the sketch below to your Arduino and open the Serial Monitor. If all goes well, when you press the button, the Ardiuno will print out the current ADC value on ADS1115 pin A0 five times, once every second, then stop and wait for you to press the button again. Hopefully this helps point you in the direction you want to go, and let me know if you have any questions about the code/functionality/extensions of this setup!

Here is the sketch:

#include <Wire.h>
#include <math.h>
#include <Adafruit_ADS1015.h>

Adafruit_ADS1115 ads(0x48);

int buttonPin = 9; // Declare which pin the start button is attached to
int buttonState = 0; // Initial state of button
int condition; // Current state of button
int timer = 0; // Initial value for the timer
int interval = 1000; // How often you want the ADC sample (in millis)
int stopCount = 5; // How many counts you want (stopCount = interval in seconds * # of counts)

void setup(void)

// ADS1015 ADS1115
// ------- -------
ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
// ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV

ads.begin(); // Establish connection with ADS1115
pinMode(buttonPin, INPUT);


void loop() {

condition = start(); // Runs function to check if start button was pressed
if (condition == HIGH) { // Checks to see if start button was pressed. If so:
for (timer = 0; timer < stopCount; timer++) { // Start timer

// ****Ch. 0****
float ADC0 = getADC(0, 0); // Use getADC function to... get the ADC value
Serial.println("Ch. 0: ");



// ******Get ADC from ADS1115******

float getADC(int board, int channel) {

uint16_t val;

val = ads.readADC_SingleEnded(channel); // **This is the raw ADC value you are looking for
return val;

// ******Start ADC Collection******

int start() {
buttonState = digitalRead(buttonPin);
int oldButtonState = 0;
int state;
if (buttonState != oldButtonState)
oldButtonState = buttonState;
if (buttonState == HIGH)
state = !state;
condition = 0;
}else {
condition = 1;
return condition;

fmarengo (author)Mendelbrot2017-04-07

Deal Mendelbrot,

Thank you very much for your help!
I tested your code in my PC and it works fine! Unfortunately, it doesn't work when I tried to implement it for my case, which is an oxymeter. Basically, I need that 2 leds turn on alternatively (led1 on and led2 off, then led1 off and led2 on, and so on) periodically, every T [ms]. To this aim, I use the TimerOne library. Each time the interrupt triggers, the led states toggle and the photosensor sends the detected light level to the Arduino. My software works fine using the internal 10-bit ADC of the ATMega328. Since I need higher resolution, I need to adapt this software for the 16-bit ADS1115. Hence, I called the command


in my interrupt service routine. Unfortunately, it is not working. However, the ADS1115 works fine for some basical tests here. Could you please tell me how I can overcome this drawback? My feeling is that I just need one single low-delay command.

We could discuss this issue via e-mail if it is ok with you (fmarengorodriguez [at] gmail [dot] com).

Thank you so much, God bless you :)


Mendelbrot made it! (author)fmarengo2017-04-04

Much appreciated! I think this may be a down and dirty way to adjust the sampling rate, though I haven't tested it. If you navigate to your Libraries -> Adafruit_ADS1X15 and check out the .h and .cpp files, you can see in the .h file all of the options for sampling rate and then in the .cpp file, you can adjust the values accordingly. In the attached photos I highlighted the areas of interest. An important thing to note and something I eventually want to get to the bottom of is that those sampling rates listed are not the same as the sampling rates stated in the ADS1115 data sheet, however this library works quite well. In any event, let me know if this helps!

rafununu (author)2017-03-06

Thanks. It's been a while that I plan to use several I2C peripherals with a Nano. This instr. gives me usefull tips.

TheThinker (author)2017-03-05

Just what I was looking for!

About This Instructable




More by Mendelbrot:ATMega328P Rotary Encoder and 7-Segment DisplayZIF Attiny "Arduino As ISP" Programmer With External Clock16-bit I2C Temperature Monitor Using Arduino
Add instructable to: