Touch climate display on an Arduino Uno
An AHT20 and a 16x2 I2C LCD on the same two wires, and a TTP223 touch pad that flips the reading between Celsius and Fahrenheit. The whole build, an Arduino Uno and 3 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 25 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $33. On Mokxi they're free.
What you'll learn
- Sharing one I2C bus between devices
- Integer math without floating point
- Edge detection for a touch pad
- Updating a display on a timer
- Mixing 3.3 V and 5 V parts
- Difficulty: Intermediate
- Time: About 25 minutes
- Board: Arduino Uno R3
- Parts: 4
How it works
An AHT20 sensor and a 16x2 LCD share the same two I2C wires. Each answers only to its own address, so they can sit on one bus. The sketch reads temperature and humidity every half second and shows them on the LCD.
A TTP223 touch pad on pin 2 switches the temperature between Celsius and Fahrenheit. The sketch flips the units on the rising edge, so holding a finger on the pad flips them once. Nothing is sent to the Serial Monitor.
Parts list
4 parts, plus the jumper wires. They retail for about $33; on Mokxi they're free.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Character LCD with I2C backpack pin SCL | Arduino Uno R3 pin SCL |
| Character LCD with I2C backpack pin SDA | Arduino Uno R3 pin SDA |
| Character LCD with I2C backpack pin GND | Arduino Uno R3 pin GND |
| Touch sensor, TTP223 pin SIG | Arduino Uno R3 pin 2 |
| Character LCD with I2C backpack pin VCC | Arduino Uno R3 pin IOREF |
| Touch sensor, TTP223 pin VCC | Arduino Uno R3 pin 3V3 |
| Humidity sensor, AHT20 pin VCC | Arduino Uno R3 pin 5V |
| Humidity sensor, AHT20 pin GND | Arduino Uno R3 pin GND |
| Touch sensor, TTP223 pin GND | Arduino Uno R3 pin GND |
| Humidity sensor, AHT20 pin SDA | Arduino Uno R3 pin A4 |
| Humidity sensor, AHT20 pin SCL | Arduino Uno R3 pin A5 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Touch climate display: an AHT20 temperature and humidity sensor and a 16x2
// LCD on the same two I2C wires, and a TTP223 touch pad that switches the
// temperature between Celsius and Fahrenheit.
//
// A4, A5 SDA and SCL, to the AHT20 (0x38); the LCD backpack (0x27) is on
// the header's other SDA and SCL, which are the same two wires
// pin 2 the TTP223's SIG: high while a finger is on the pad
// power the AHT20 on 5V, the LCD on IOREF (also 5 V), the TTP223 on 3V3:
// its 3.3 V high is still a HIGH to a 5 V Uno
//
// Two I2C devices on one pair of wires is what the bus is for: each answers
// only its own address. The AHT20 module and the backpack both carry
// pull-ups, which is normal and harmless.
//
// # The sensor
//
// Adafruit_AHTX0.h asks the AHT20 for a reading and waits the 80 ms the chip
// takes to make one. The sketch then takes the numbers in hundredths
// (getTemperatureCentis and getHumidityCentis), because Fahrenheit is a sum
// and there is no floating point on this board:
//
// F x 100 = C x 100 x 9 / 5 + 3200
//
// # The touch pad
//
// The TTP223 needs no pull-up and does not bounce: SIG is a real output,
// high while touched. It takes up to 60 ms to decide, and it ignores the pad
// for the first half second after power comes up. The sketch flips the units
// on the rising edge, so holding a finger there flips them once.
//
// Everything is on the display and nothing goes to the serial monitor: the
// sensor library, two I2C drivers and the LCD font fill most of the 8 KB an
// example is allowed.
#include <Adafruit_AHTX0.h>
#include "mokxi_lcd1602_i2c.h"
const int TOUCH = 2;
Adafruit_AHTX0 aht;
Lcd1602I2c lcd(A5, A4);
bool fahrenheit = false;
bool wasTouched = false;
/** Hundredths to tenths, rounded half away from zero. */
int tenthsOf(long centis) {
return (int)((centis >= 0 ? centis + 5 : centis - 5) / 10);
}
void show() {
sensors_event_t humidity, temp;
if (!aht.getEvent(&humidity, &temp)) {
lcd.setCursor(0, 0);
lcd.print("sensor?");
lcd.padLine();
return;
}
long c = aht.getTemperatureCentis();
int shown = tenthsOf(fahrenheit ? c * 9 / 5 + 3200 : c);
int rh = (int)((aht.getHumidityCentis() + 50) / 100);
lcd.setCursor(0, 0);
lcd.print("Temp ");
lcd.printFixed(shown, 1);
lcd.print(fahrenheit ? " F" : " C");
lcd.padLine();
lcd.setCursor(0, 1);
lcd.print("Humid ");
lcd.print(rh);
lcd.print(" %");
lcd.padLine();
}
void setup() {
pinMode(TOUCH, INPUT);
lcd.begin();
lcd.print("Mokxi climate");
if (!aht.begin()) {
lcd.setCursor(0, 1);
lcd.print("no AHT20");
while (true) {
delay(100);
}
}
delay(400);
lcd.clear();
}
void loop() {
bool touched = digitalRead(TOUCH) == HIGH;
bool flipped = touched && !wasTouched;
wasTouched = touched;
if (flipped) {
fahrenheit = !fahrenheit;
}
static unsigned long last = 0;
if (flipped || millis() - last >= 500) {
last = millis();
show();
}
delay(20);
}
Code walkthrough
The key functions of the sketch, and what each one does.
tenthsOf sketch.ino
Turns hundredths of a degree into tenths, rounding half away from zero. That lets the sketch work in whole numbers, since the Uno has no floating point.
/** Hundredths to tenths, rounded half away from zero. */
int tenthsOf(long centis) {
return (int)((centis >= 0 ? centis + 5 : centis - 5) / 10);
}show sketch.ino
Reads the sensor. If that fails it prints sensor? on the LCD. Otherwise it converts to Fahrenheit if needed with C times 9 over 5 plus 3200, and prints the temperature and the humidity.
void show() {
sensors_event_t humidity, temp;
if (!aht.getEvent(&humidity, &temp)) {
lcd.setCursor(0, 0);
lcd.print("sensor?");
lcd.padLine();
return;
}
long c = aht.getTemperatureCentis();
int shown = tenthsOf(fahrenheit ? c * 9 / 5 + 3200 : c);
int rh = (int)((aht.getHumidityCentis() + 50) / 100);
lcd.setCursor(0, 0);
lcd.print("Temp ");
lcd.printFixed(shown, 1);
lcd.print(fahrenheit ? " F" : " C");
lcd.padLine();
lcd.setCursor(0, 1);
lcd.print("Humid ");
lcd.print(rh);
lcd.print(" %");
lcd.padLine();
}loop sketch.ino
Watches the touch pad and flips fahrenheit on the moment of touch. It redraws when the units flip or every 500 ms.
void loop() {
bool touched = digitalRead(TOUCH) == HIGH;
bool flipped = touched && !wasTouched;
wasTouched = touched;
if (flipped) {
fahrenheit = !fahrenheit;
}
static unsigned long last = 0;
if (flipped || millis() - last >= 500) {
last = millis();
show();
}
delay(20);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The LCD shows no AHT20 This project
Check the sensor's SDA on A4 and SCL on A5, and power on 5V. - Touching does nothing at first This project
The TTP223 ignores the pad for the first half second after power comes up, and takes up to 60 ms to decide. - Readings are zero, stale, or the first value is wrong. Humidity sensor
After power on, wait about 40 ms. Send the measure command, then wait about 80 ms before reading the six bytes. Check that the busy bit in the status is clear first. - The sensor is not found on the bus. Humidity sensor
The address is fixed at 0x38, so two AHT20s cannot share a bus without a multiplexer. Check SDA and SCL, power, and pull-up resistors (about 4.7 k) on both lines. - The screen stays blank or shows only a row of blocks. Character LCD with I2C backpack
The I2C address is usually 0x27 or 0x3F. Run an I2C scanner to find yours. Then turn the contrast trimmer on the backpack slowly until text appears. - The backlight is on but there is no text, or text is garbled. Character LCD with I2C backpack
Wire SDA and SCL to the right pins (A4 and A5 on an Uno), power it from 5 V, and call lcd.init() and the backlight call in setup. - It never triggers, or triggers by itself. Touch sensor
Power it with 2.0 V to 5.5 V and share ground. Keep your hand and the wires away from the pad while it powers up, since it sets its baseline at startup. Avoid long wires. - The output is HIGH when you expect LOW. Touch sensor
Pads on the board are jumpers for mode. The default is momentary and active high. Soldering the jumpers can change it to toggle or active low. Check your board and its mode.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.