En LCD 16x2 på Arduino er den enkleste veien til et prosjekt som viser noe uten PC: fire ledninger, et bibliotek, og tallene står på skjermen. Du starter med en hilsen og ender med en klimamåler som viser temperatur, fuktighet, min/maks og en pil for om det blir varmere eller kaldere i rommet.
hd44780, som selv finner adressen. Står det ingenting på skjermen, drei på kontrastskruen før du endrer noe i koden.- Nivå
- Nybegynner til viderekommen
- Tid
- 2 til 3 timer i alt
- Lodding
- Ja, 16 punkter
- Du lærer
- I²C, egne tegn, sensorer og knapper uten delay
Tre nivåer, tre skjermer
Hvert nivå ender med et program som virker, og du kan stoppe der du vil. Nivå 1 beviser at skjermen og loddingen er i orden. Nivå 2 gir en nyttig måler på samme skjerm. Nivå 3 bytter til fire linjer og lar programmet huske og sammenligne målinger.
Delene du trenger
LCD 2x16 med blått baklys1 stk, nivå 1 og 2
I²C-adapter for HD447801 stk per skjerm, gjør 16 pinner om til 4
Arduino UNO R4 WiFi1 stk, pluss en USB-C-kabel som overfører data
DHT22 AM2302-modul1 stk, nivå 2 og 3
Koblingsbrett med 400 hull1 stk, nivå 2 og 3, fordeler 5V og GND
Sett med 140 koblingsledningerHann-hunn til adapteren, hann-hann til koblingsbrettet
LCD 4x20 med grønt baklys1 stk, nivå 3, pluss en adapter til
Trykknapp 12 x 12 mm1 stk, nivå 3, nullstiller min/maks
Du trenger også loddebolt og loddetinn. Har du ikke valgt loddeutstyr ennå, hjelper guiden om loddebolt og varmluft. Andre varianter av brett og ledninger finner du blant koblingsbrett og koblingsledninger.


Lodd adapteren riktig vei første gang
Adapteren har 16 lange pinner som skal gjennom hullene i LCD-en. Loddingen er vanskelig å gjøre om, så bruk ett minutt på retningen før du varmer opp bolten.
- Trekk ut USB og legg LCD-en med baksiden opp. Finn merkingen 1 og 16 ved hullraden.
- Sett adapteren inn fra baksiden, med de fire tilkoblingspinnene på samme side som pinne 1.
- Snu alt og hold adapteren på plass med litt tape. Lodd først ett hjørne og kontroller at adapteren står rett.
- Lodd de andre 15 pinnene på skjermsiden. Hold bolten på pinnen og flaten samtidig, og pass på den svarte rammen.
- Se etter loddebroer mellom nabopinner og matte, klumpete loddepunkter. Sjekk at adapteren ikke ligger inntil komponenter på baksiden av LCD-en.
Nivå 1: få skjermen til å si hei
LCD med Arduino og I2C: hvilke fire ledninger trenger du?
GND, VCC, SDA og SCL er hele koblingen. I²C-adapteren på baksiden tar seg av de seksten pinnene på skjermen, og koden under trenger bare ett bibliotek.
| Adapter | UNO R4 WiFi | Funksjon |
|---|---|---|
| GND | GND | Felles jord |
| VCC | 5V | Strøm til skjerm og baklys |
| SDA | SDA (samme signal som A4) | Data |
| SCL | SCL (samme signal som A5) | Klokkesignal |
- Åpne Arduino IDE, velg kortet «Arduino UNO R4 WiFi» og riktig port.
- Åpne Library Manager, søk etter
hd44780og installer biblioteket fra Bill Perry. - Lim inn programmet under og last det opp.
- Drei den blå kontrastskruen langsomt med en liten skrutrekker til tegnene står skarpt.
// Nivå 1: tekst på LCD 16x2 via PCF8574-adapter (I2C).
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
hd44780_I2Cexp lcd; // Ingen adresse: biblioteket finner adapteren selv
void setup() {
int status = lcd.begin(16, 2);
if (status) {
// Fant ingen skjerm: den innebygde LED-en blinker feilkoden.
hd44780::fatalError(status);
}
lcd.setCursor(0, 0); // Kolonne 0, rad 0
lcd.print("Hei fra MRSMART");
lcd.setCursor(0, 1); // Kolonne 0, rad 1
lcd.print("LCD 16 x 2");
}
void loop() {
// Teksten står på skjermen til strømmen går. Her er ingenting å gjøre.
}
hd44780_I2Cexp lcd; står uten adresse. Biblioteket leter selv på bussen og finner både adressen og hvordan adapteren er koblet til LCD-en. lcd.begin(16, 2) gir 0 når alt er i orden. Andre tall betyr feil, og da blinker fatalError den innebygde LED-en i stedet for å fortsette i blinde.
Hva ser du på skjermen, og hva betyr det?
En tekstskjerm gir bedre feilsøking enn du skulle tro, fordi hvert feilbilde peker mot sin egen årsak.
begin().Prøv gjerne ut en egen tekst før du laster opp. Feltet under viser hvor mye som får plass, og lager koden du kan lime inn i stedet for de to lcd.print-linjene. Eksemplet inneholder en felle:
Slik styrer to ledninger en hel skjerm
I²C er en felles buss: alle enhetene henger på de samme to ledningene. SCL er takten, SDA er dataene. Arduino begynner hver melding med en adresse, og bare den enheten som har adressen, svarer. Derfor kan du senere koble flere sensorer på de samme to pinnene.
LCD-en selv kan ikke I²C. Den får bare halve tegn om gangen på fire ledninger, med en puls på E for hver halvdel. Hvert tegn blir derfor til flere meldinger på bussen, og det er grunnen til at programmene på nivå 2 og 3 ikke tegner hele skjermen på nytt hele tiden.
Se hvem som svarer på bussen
Dette lille programmet spør alle adresser om de svarer. Det er nyttig når skjermen ikke svarer, og senere når du kobler til flere enheter. Åpne Serial Monitor og velg 115200 baud.
// Spør alle adresser på I2C-bussen og skriver ut hvem som svarer.
#include <Wire.h>
void setup() {
Serial.begin(115200);
Wire.begin();
}
void loop() {
Serial.println("Skanner I2C-bussen ...");
int antall = 0;
for (uint8_t adresse = 1; adresse < 127; adresse++) {
Wire.beginTransmission(adresse);
if (Wire.endTransmission() == 0) { // 0 betyr at enheten svarte
Serial.print("Fant enhet på 0x");
if (adresse < 16) {
Serial.print("0");
}
Serial.println(adresse, HEX);
antall++;
}
}
Serial.print("Antall enheter: ");
Serial.println(antall);
Serial.println();
delay(5000);
}
Skanner I2C-bussen ... Fant enhet på 0x27 Antall enheter: 1
Adressen kan være en annen. Databladet til PCF8574 oppgir området 0x20 til 0x27 avhengig av loddeflatene A0, A1 og A2, og varianten PCF8574A bruker 0x38 til 0x3F. Står det «Antall enheter: 0», er SDA og SCL byttet, eller så mangler 5V, GND eller en lodding.
Skjermen kan ikke norsk, men du kan lære den det
Tegnene ligger fast i en ROM i kontrolleren. Den vanligste varianten, A00, har japanske tegn og enkelte greske bokstaver, men ikke æ, ø og å. Skriver du «ø» i koden, kommer det noe helt annet på skjermen, fordi UTF-8-tegnet er to byte som viser to tilfeldige ROM-tegn.
Løsningen er CGRAM: et lite minne med plass til åtte egne tegn på 5 x 8 punkter. Du tegner hver rad som et tall, der 1 er et punkt som lyser.
// Æ, Ø og Å som egne tegn på LCD 16x2.
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
hd44780_I2Cexp lcd;
// Hver linje er én rad med 5 punkter: 1 er på, 0 er av.
uint8_t storAE[8] = {0b01111, 0b10100, 0b10100, 0b11110, 0b10100, 0b10100, 0b10111, 0b00000};
uint8_t storOE[8] = {0b01110, 0b10011, 0b10101, 0b10101, 0b10101, 0b11001, 0b01110, 0b00000};
uint8_t storAA[8] = {0b00100, 0b01010, 0b00100, 0b01110, 0b10001, 0b11111, 0b10001, 0b10001};
uint8_t litenAE[8] = {0b00000, 0b00000, 0b01010, 0b00101, 0b01111, 0b10100, 0b01011, 0b00000};
uint8_t litenOE[8] = {0b00000, 0b00000, 0b01110, 0b10011, 0b10101, 0b11001, 0b01110, 0b00000};
uint8_t litenAA[8] = {0b00100, 0b01010, 0b00100, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111};
void setup() {
int status = lcd.begin(16, 2);
if (status) {
hd44780::fatalError(status);
}
// Plass 1 til 6. Plass 0 finnes også, men kan ikke stå inne i en tekst.
lcd.createChar(1, storAE);
lcd.createChar(2, storOE);
lcd.createChar(3, storAA);
lcd.createChar(4, litenAE);
lcd.createChar(5, litenOE);
lcd.createChar(6, litenAA);
lcd.setCursor(0, 0);
lcd.print("Bl\006b\004rsyltet\005y");
lcd.setCursor(0, 1);
lcd.print("\001 \002 \003 \004 \005 \006");
}
void loop() {
}
Se på "Bl\006b\004rsyltet\005y". \006 er tegnet med nummer 6 skrevet inne i teksten. Tegn nummer 0 kan ikke brukes slik, fordi 0 betyr slutten på en tekst i C++. Derfor starter vi på 1.
Vil du se hva akkurat din skjerm har i ROM, kjør bibliotekets eksempel LCDcharset under hd44780examples. Det er en god måte å finne ut hvilken variant du har fått.
Nivå 2: en klimamåler med egne ikoner
UNO-kortet har bare én 5V-pinne, men nå er det to enheter som vil ha strøm. Derfor bruker vi koblingsbrettets strømskinner. Sensorens tre pinner står i hver sin kolonne, så du kan sette modulen rett i brettet.
| Fra | Til | Ledning |
|---|---|---|
| UNO 5V | Plusskinne | Hann-hann |
| UNO GND | Minusskinne | Hann-hann |
| Adapter VCC | Plusskinne | Hunn-hann |
| Adapter GND | Minusskinne | Hunn-hann |
| Adapter SDA og SCL | UNO SDA og SCL | Hunn-hann, som før |
| DHT22 + (kolonne 11) | Plusskinne | Hann-hann |
| DHT22 - (kolonne 13) | Minusskinne | Hann-hann |
| DHT22 out (kolonne 12) | UNO D7 | Hann-hann |
Modulen har allerede de passive komponentene sensoren trenger, så du skal ikke legge til noen ekstra motstand. Kablene som følger med, har hunnkontakter i begge ender på produktbildet, så i dette oppsettet bruker du dem ikke.
Installer «DHT sensor library» fra Adafruit i Library Manager, og godta at «Adafruit Unified Sensor» kommer med. Last så opp programmet.
// Nivå 2: temperatur og fuktighet fra DHT22 på LCD 16x2.
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
#include <DHT.h>
const uint8_t DHT_PIN = 7; // out på DHT22 går til D7
const unsigned long MALEINTERVALL_MS = 2500; // Databladet krever minst 2 s
// Numrene på de egne tegnene
const uint8_t TEGN_TERMOMETER = 1;
const uint8_t TEGN_DRAPE = 2;
const uint8_t TEGN_GRAD = 3;
hd44780_I2Cexp lcd;
DHT dht(DHT_PIN, DHT22);
unsigned long sisteMaling = 0;
uint8_t termometer[8] = {0b00100, 0b01010, 0b01010, 0b01010, 0b01110, 0b11111, 0b11111, 0b01110};
uint8_t drape[8] = {0b00100, 0b00100, 0b01010, 0b01010, 0b10001, 0b10001, 0b10001, 0b01110};
uint8_t grad[8] = {0b00110, 0b01001, 0b01001, 0b00110, 0b00000, 0b00000, 0b00000, 0b00000};
// Lager tekst med én desimal og norsk komma, for eksempel "-3,5".
void medKomma(float verdi, char *tekst, size_t plass) {
long tideler = lround(verdi * 10.0f);
long positiv = labs(tideler);
snprintf(tekst, plass, "%s%ld,%ld", tideler < 0 ? "-" : "", positiv / 10, positiv % 10);
}
void visMaling(float temperatur, float fuktighet) {
char tall[8];
char felt[8];
medKomma(temperatur, tall, sizeof(tall));
snprintf(felt, sizeof(felt), "%6s", tall); // Høyrejustert, så kommaet står stille
lcd.setCursor(0, 0);
lcd.write(TEGN_TERMOMETER);
lcd.print(" Temp ");
lcd.print(felt);
lcd.write(TEGN_GRAD);
lcd.print("C");
medKomma(fuktighet, tall, sizeof(tall));
snprintf(felt, sizeof(felt), "%6s", tall);
lcd.setCursor(0, 1);
lcd.write(TEGN_DRAPE);
lcd.print(" Fukt ");
lcd.print(felt);
lcd.print(" %");
}
void setup() {
Serial.begin(115200);
int status = lcd.begin(16, 2);
if (status) {
hd44780::fatalError(status);
}
lcd.createChar(TEGN_TERMOMETER, termometer);
lcd.createChar(TEGN_DRAPE, drape);
lcd.createChar(TEGN_GRAD, grad);
lcd.setCursor(0, 0);
lcd.print("Starter sensor");
dht.begin();
sisteMaling = millis(); // Første måling kommer etter 2,5 s
}
void loop() {
if (millis() - sisteMaling < MALEINTERVALL_MS) {
return;
}
sisteMaling = millis();
float fuktighet = dht.readHumidity();
float temperatur = dht.readTemperature();
if (isnan(fuktighet) || isnan(temperatur)) {
lcd.setCursor(0, 0);
lcd.print("Sensorfeil ");
lcd.setCursor(0, 1);
lcd.print("Sjekk D7,5V,GND ");
Serial.println("Sensorfeil");
return;
}
visMaling(temperatur, fuktighet);
// Punktum her: dette formatet kan Serial Plotter tegne som graf.
Serial.print("Temp:");
Serial.print(temperatur, 1);
Serial.print(",Fukt:");
Serial.println(fuktighet, 1);
}
Det som ikke er selvsagt i koden
- Ingen
delay().loop()sjekker om 2,5 sekunder er gått, og går ellers rett videre. Det gir ingen forskjell her, men på nivå 3 må programmet følge med på en knapp samtidig. medKomma.lcd.print(23.4)gir punktum. Vi regner om til tideler medlroundog setter inn kommaet selv, og slipper samtidig små avrundingsfeil i flyttallet."%6s". Tallet høyrejusteres i seks tegn. Da overskriver 9,5 den gamle verdien 10,0 uten å la et siffer stå igjen, og kommaet står på samme sted.isnan(). DHT-biblioteket gir NaN, ikke et tall, når lesingen feiler. Uten kontrollen ville skjermen vist tull.
Temp:23.4,Fukt:41.2 Temp:23.4,Fukt:41.3 Temp:23.5,Fukt:41.3
DHT22 pinout: hvilke pinner har sensoren?
Modulen i denne guiden har tre pinner, merket + out -. Sensoren alene har fire bein: pluss (VDD), data, ikke tilkoblet og GND.
Hvor mye kan du stole på en DHT22?
Skjermen viser én desimal, men det betyr ikke at målingen er så nøyaktig. Databladet skiller mellom oppløsning, nøyaktighet og gjentakbarhet, og forskjellen er verdt å kjenne:
| Egenskap | Temperatur | Fuktighet | Hva det betyr |
|---|---|---|---|
| Område | -40 til 80 °C | 0 til 100 %RH | Dekker inneklima og det meste utendørs |
| Oppløsning | 0,1 °C | 0,1 %RH | Minste steg på skjermen |
| Nøyaktighet | ±0,5 °C | ±2 %RH, høyst ±5 %RH | Hvor langt fra sann verdi den kan vise |
| Gjentakbarhet | ±0,2 °C | ±1 %RH | Spredning mellom målinger under like forhold |
| Langtidsstabilitet | ±0,5 %RH per år | Fuktmålingen kan drive med tiden |
Står det 23,4 °C, kan altså rommet være alt fra 22,9 til 23,9 °C. En endring fra 23,4 til 23,5 sier lite, mens en jevn stigning over ti minutter er reell. Derfor reagerer trendpilen på nivå 3 først ved 0,3 °C. Databladet krever også minst 2 sekunder mellom målingene og 1 sekund ro etter påslag. Begge deler er tatt hensyn til i programmene.
Plasser sensoren unna USB-kontakten og UNO-kortets spenningsregulator, direkte sol, vifter og pust. Se den som et godt verktøy for å følge trender i et rom, ikke som et kalibrert måleinstrument.
Nivå 3: 20x4 med min/maks, trendpil og knapp
Den første adapteren er loddet fast, så du lodder en ny på den store skjermen på samme måte. Skjermen har samme 16 pinner, og produktbildet viser merkingen 16 til venstre og 1 til høyre på baksiden. Den leveres uten kontakter. På baksiden finnes et tomt felt med merkene U7, SCL, SDA, VDD og GND, men I²C-brikken er ikke montert der, så bruk adapteren.
Knappen er koblet fra pinne D2 til GND. INPUT_PULLUP holder D2 høy så lenge knappen er sluppet, og trykket drar den ned. Beinavstanden er ulik fra knapp til knapp, så flytt den gjerne en rad om den ikke passer i rad d og g.
// Nivå 3: klimamåler på LCD 20x4 med min/maks, trendpil og knapp for nullstilling.
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
#include <DHT.h>
const uint8_t DHT_PIN = 7; // out på DHT22 går til D7
const uint8_t KNAPP_PIN = 2; // Knappen går fra D2 til GND
const unsigned long MALEINTERVALL_MS = 2500; // Databladet krever minst 2 s
const unsigned long TRENDINTERVALL_MS = 60000; // Én trendprøve hvert minutt
const unsigned long PRELL_MS = 50; // Ventetid mot kontaktprell
const unsigned long MELDING_MS = 2000; // Så lenge meldingen vises
const uint8_t TREND_PROVER = 10; // Sammenligner med målingen for ca. 10 minutter siden
const float TREND_GRENSE = 0.3f; // Større enn sensorens gjentakbarhet på 0,2 grader
const uint8_t MAKS_FEIL = 3; // Så mange feil på rad før feilmelding
const uint8_t TEGN_GRAD = 1;
const uint8_t TEGN_OPP = 2;
const uint8_t TEGN_NED = 3;
const uint8_t TEGN_FLAT = 4;
uint8_t grad[8] = {0b00110, 0b01001, 0b01001, 0b00110, 0b00000, 0b00000, 0b00000, 0b00000};
uint8_t pilOpp[8] = {0b00100, 0b01110, 0b10101, 0b00100, 0b00100, 0b00100, 0b00100, 0b00000};
uint8_t pilNed[8] = {0b00100, 0b00100, 0b00100, 0b00100, 0b10101, 0b01110, 0b00100, 0b00000};
uint8_t pilFlat[8] = {0b00000, 0b00100, 0b00010, 0b11111, 0b00010, 0b00100, 0b00000, 0b00000};
hd44780_I2Cexp lcd;
DHT dht(DHT_PIN, DHT22);
unsigned long sisteMaling = 0;
unsigned long sisteTrendprove = 0;
unsigned long periodeStart = 0;
unsigned long meldingSlutt = 0;
unsigned long sistVistSekund = 0;
float minTemp = 0;
float maksTemp = 0;
bool harMinMaks = false;
bool harMaling = false;
bool viserMelding = false;
uint8_t feilPaRad = 0;
float trendprover[TREND_PROVER];
uint8_t antallProver = 0;
uint8_t nesteProve = 0;
// Lager tekst med én desimal og norsk komma, for eksempel "-3,5".
void medKomma(float verdi, char *tekst, size_t plass) {
long tideler = lround(verdi * 10.0f);
long positiv = labs(tideler);
snprintf(tekst, plass, "%s%ld,%ld", tideler < 0 ? "-" : "", positiv / 10, positiv % 10);
}
// Skriver en hel rad, fylt ut med mellomrom, så gammel tekst forsvinner.
void skrivRad(uint8_t rad, const char *tekst) {
char linje[21];
snprintf(linje, sizeof(linje), "%-20s", tekst);
lcd.setCursor(0, rad);
lcd.print(linje);
}
void lagreTrendprove(float temperatur) {
trendprover[nesteProve] = temperatur;
nesteProve = (nesteProve + 1) % TREND_PROVER;
if (antallProver < TREND_PROVER) {
antallProver++;
}
}
// Rad 0, kolonne 12 til 19: pil og endring, eller hvor lenge det er til trenden er klar.
void visTrend(float temperatur) {
char tekst[9];
lcd.setCursor(12, 0);
if (antallProver < TREND_PROVER) {
snprintf(tekst, sizeof(tekst), "om %d min", TREND_PROVER - antallProver);
lcd.print(tekst);
return;
}
// nesteProve peker nå på den eldste prøven i ringbufferen.
float endring = temperatur - trendprover[nesteProve];
uint8_t pil = TEGN_FLAT;
if (endring >= TREND_GRENSE) {
pil = TEGN_OPP;
} else if (endring <= -TREND_GRENSE) {
pil = TEGN_NED;
}
char tall[8];
medKomma(fabs(endring), tall, sizeof(tall));
snprintf(tekst, sizeof(tekst), " %c%-5s", endring < 0 ? '-' : '+', tall);
lcd.write(pil);
lcd.print(tekst);
}
void visMaling(float temperatur, float fuktighet) {
char tall[8];
char felt[8];
medKomma(temperatur, tall, sizeof(tall));
snprintf(felt, sizeof(felt), "%5s", tall);
lcd.setCursor(0, 0);
lcd.print("Temp");
lcd.print(felt);
lcd.write(TEGN_GRAD);
lcd.print("C");
visTrend(temperatur);
medKomma(fuktighet, tall, sizeof(tall));
char linje[21];
snprintf(linje, sizeof(linje), "Fukt%5s %%", tall);
skrivRad(1, linje);
}
void visMinMaks() {
char lav[8] = "--,-";
char hoy[8] = "--,-";
if (harMinMaks) {
medKomma(minTemp, lav, sizeof(lav));
medKomma(maksTemp, hoy, sizeof(hoy));
}
char linje[21];
snprintf(linje, sizeof(linje), "Min %5s Maks %5s", lav, hoy);
skrivRad(2, linje);
}
// Rad 3: hvor lenge min/maks har samlet målinger, hentet fra millis().
void visPeriode() {
unsigned long sekunder = (millis() - periodeStart) / 1000;
char tid[13];
char linje[21];
snprintf(tid, sizeof(tid), "%lu:%02lu:%02lu", sekunder / 3600, (sekunder / 60) % 60, sekunder % 60);
snprintf(linje, sizeof(linje), "Periode %12s", tid);
skrivRad(3, linje);
}
// Gir true én gang per trykk, først når signalet har vært stabilt i PRELL_MS.
bool knappTrykket() {
static int stabil = HIGH;
static int forrige = HIGH;
static unsigned long endret = 0;
int naa = digitalRead(KNAPP_PIN);
if (naa != forrige) {
forrige = naa;
endret = millis();
}
if (millis() - endret >= PRELL_MS && naa != stabil) {
stabil = naa;
return stabil == LOW;
}
return false;
}
void nullstillMinMaks() {
harMinMaks = false;
periodeStart = millis();
visMinMaks();
skrivRad(3, "Min/maks nullstilt");
viserMelding = true;
meldingSlutt = millis() + MELDING_MS;
}
void gjorMaling() {
float fuktighet = dht.readHumidity();
float temperatur = dht.readTemperature();
if (isnan(fuktighet) || isnan(temperatur)) {
if (feilPaRad < MAKS_FEIL) {
feilPaRad++;
}
if (feilPaRad == MAKS_FEIL) {
skrivRad(0, "Sensorfeil: sjekk");
skrivRad(1, "D7, 5V og GND");
Serial.println("Sensorfeil");
}
return;
}
feilPaRad = 0;
harMaling = true;
// Min og maks kommer bare fra gyldige målinger.
if (!harMinMaks) {
minTemp = temperatur;
maksTemp = temperatur;
harMinMaks = true;
} else {
minTemp = min(minTemp, temperatur);
maksTemp = max(maksTemp, temperatur);
}
// Første prøve lagres med en gang, deretter én hvert minutt.
if (antallProver == 0 || millis() - sisteTrendprove >= TRENDINTERVALL_MS) {
lagreTrendprove(temperatur);
sisteTrendprove = millis();
}
visMaling(temperatur, fuktighet);
visMinMaks();
// Punktum her: dette formatet kan Serial Plotter tegne som graf.
Serial.print("Temp:");
Serial.print(temperatur, 1);
Serial.print(",Fukt:");
Serial.print(fuktighet, 1);
Serial.print(",Min:");
Serial.print(minTemp, 1);
Serial.print(",Maks:");
Serial.println(maksTemp, 1);
}
void setup() {
Serial.begin(115200);
pinMode(KNAPP_PIN, INPUT_PULLUP);
int status = lcd.begin(20, 4);
if (status) {
hd44780::fatalError(status);
}
lcd.createChar(TEGN_GRAD, grad);
lcd.createChar(TEGN_OPP, pilOpp);
lcd.createChar(TEGN_NED, pilNed);
lcd.createChar(TEGN_FLAT, pilFlat);
skrivRad(0, "Inneklima 20x4");
skrivRad(1, "Starter sensor");
dht.begin();
sisteMaling = millis(); // Første måling kommer etter 2,5 s
periodeStart = millis();
}
void loop() {
if (knappTrykket()) {
nullstillMinMaks();
}
if (viserMelding && (long)(millis() - meldingSlutt) >= 0) {
viserMelding = false;
}
if (millis() - sisteMaling >= MALEINTERVALL_MS) {
sisteMaling = millis();
gjorMaling();
}
// Oppdaterer tiden én gang i sekundet når det ikke vises en melding.
unsigned long sekund = millis() / 1000;
if (harMaling && !viserMelding && sekund != sistVistSekund) {
sistVistSekund = sekund;
visPeriode();
}
}
Slik virker de nye delene
- Bare ekte målinger. Min og maks får verdi først ved den første gyldige målingen. Før det viser skjermen --,-, ikke en oppdiktet startverdi.
- Trenden.
trendproverer en ringbuffer med ti plasser. Hvert minutt legges det inn en ny verdi over den eldste. Først når den er full, sammenligner vi, og frem til da står det for eksempel «om 7 min». - Tre feil på rad. DHT22 kan av og til gi en mislykket lesing. Programmet venter derfor til tre lesinger har feilet før det melder fra.
- Prell. En knapp spretter noen millisekunder når den trykkes inn.
knappTrykket()godtar endringen først når signalet har ligget stille i 50 ms, slik at ett trykk blir ett trykk. - Tid uten klokke. «Periode» kommer fra
millis()og viser tiden siden start eller siste nullstilling. Programmet later ikke som det vet klokkeslettet. - Rad for rad.
skrivRadfyller opp til 20 tegn, slik at gammel tekst ikke blir stående.
Fra koblingsbrett til boks
Når alt virker, kan du bygge det inn. Produktsiden til LCD 2x16 har en STL-fil med stativ til 3D-skriver under Dokumenter og linker. Lager du boksen selv, har 20x4-skjermen mål 98 x 60 mm og 93 x 55 mm mellom festehullene.
La DHT22 sitte utenfor boksen eller i et eget rom med lufthull. Her kan de medfølgende kablene brukes, om du setter hannpinner mellom kabelen og det du kobler til.
Merk av hvor skruen sitter, eller lag et lite hull til en skrutrekker. Kontrasten kan måtte justeres igjen når skjermen er kommet på plass.
Fest USB-kabelen med strekkavlastning, slik at et rykk ikke tar med seg kortet og ledningene.
Prøv selv
Hver utfordring bygger på programmet fra nivå 3, og alle bruker noe du allerede har på bordet.
- Sparemodus. Slå av baklyset med
lcd.noBacklight()etter 30 sekunder uten knappetrykk, og slå det på igjen medlcd.backlight(). Tenk på at det første trykket ikke bør nullstille noe. - Fuktighet også. Legg til min og maks for fuktighet, og la et langt trykk på knappen bytte mellom temperatur og fuktighet på rad 2.
- Varsel om fukt. La rad 1 blinke «LUFT UT» når fuktigheten er over 60 %RH. Velg en grense som passer rommet.
- Animasjon. Bruk et ledig CGRAM-tegn som en liten vifte, og skriv det med
createCharpå nytt hvert halve sekund. - LED-matrisen. UNO R4 WiFi har 12 x 8 lysdioder. Vis trendpilen der også med biblioteket
Arduino_LED_Matrix, så ser du den fra andre siden av rommet. - Ekte klokke. Biblioteket
RTCfølger med kortet. Bytt ut «Periode» med klokkeslett, og tenk gjennom hva som skjer når strømmen går. Kortet har en VRTC-pinne til reservebatteri for klokken.
Hvis det ikke virker
| Det du ser | Mest sannsynlig | Gjør dette |
|---|---|---|
| Ingen baklys | 5V, GND eller LED-jumperen | Sjekk at jumperen sitter på adapteren og at VCC går til 5V |
| Baklys, ingen tegn | Kontrast | Drei skruen langsomt gjennom hele området |
| Rad med blokker | Ikke initialisert | Kjør skanneren, deretter I2CexpDiag, og se over de 16 loddepunktene |
| Antall enheter: 0 | SDA og SCL byttet, eller brudd | Bytt om de to ledningene og sjekk 5V og GND |
| Tilfeldige tegn | Dårlig kontakt | Etterlodd og trykk ledningene godt inn, ta USB ut og inn |
| Rare tegn i stedet for æ, ø, å | Tegnene finnes ikke i ROM | Bruk createChar |
| «Sensorfeil» | out ikke på D7, eller + og - feil | Følg merkingen på modulen og kontroller kolonnene |
| Knappen nullstiller av seg selv | Ikke diagonale bein, eller feil pinne | Mål at D2 bare går til GND når du trykker |
| Rester av gamle tegn | Kortere tekst skrives uten å fylle ut resten av raden | Fyll med mellomrom, slik skrivRad gjør |
Skjermen virket, men sluttet etter at du koblet til DHT22
Ta bort DHT22 og kjør nivå 1 igjen. Virker skjermen da, er det kortslutning eller feil rekkefølge rundt sensoren. Se særlig etter at + og - ikke er byttet på koblingsbrettet.
Programmet laster opp, men LED-en på kortet blinker
Da har lcd.begin() gitt en feil, og fatalError blinker feilkoden. Kjør skanneren og se om adapteren svarer. Svarer den, kjører du I2CexpDiag.
Test deg selv
Test deg selv: hvorfor er 0x27 ikke oppgitt i koden?
hd44780_I2Cexp uten adresse leter på bussen og finner adapteren selv. Da virker koden også på en PCF8574A på 0x3F, uten at du endrer noe.
Test deg selv: skjermen viser 23,4 °C. Hva er temperaturen i rommet?
Et sted mellom 22,9 og 23,9 °C, med nøyaktigheten på ±0,5 °C i databladet. Desimalen sier mest om endringer over tid.
Test deg selv: hvorfor kan du ikke skrive æ rett inn i lcd.print?
Bokstaven finnes ikke i tegnsettet A00, og i kildekoden er den to byte i UTF-8. Skjermen viser da to tegn som ikke har noe med æ å gjøre.
Test deg selv: hva skjer hvis du leser DHT22 hvert halve sekund?
Adafruit-biblioteket hopper over lesinger nærmere enn 2 sekunder og gir deg forrige resultat, så du får ikke flere målinger. Databladet krever minst 2 sekunder mellom hver.
Test deg selv: hvorfor leser D2 LOW når du trykker på knappen?
INPUT_PULLUP holder pinnen høy gjennom en intern motstand, og knappen kobler den til GND. Trykket drar derfor signalet lavt.
Test deg selv: hvorfor skriver programmet 2:15:33 og ikke klokka?
UNO-kortet vet ikke hva klokka er når det starter. millis() teller tiden siden start, og da er det ærlig å vise nettopp det.
Vanlige spørsmål
Må jeg vite at adressen er 0x27?
Nei. Biblioteket i denne guiden finner adressen selv, og skanneren viser den hvis du vil vite det. Vil du bruke et annet bibliotek, må du oppgi adressen selv.
Kan jeg koble LCD-en til Qwiic-kontakten på UNO R4?
Ikke i dette oppsettet. Qwiic-kontakten er bare 3,3 V og ligger på en annen buss (Wire1), mens adapteren skal ha 5 V og koden bruker SDA og SCL på kortet.
Kan jeg bruke samme skjerm på en ESP32?
Biblioteket støtter mange kort, men adapteren har motstander opp til forsyningen på SDA og SCL. ESP32 tåler 3,3 V på pinnene, så med adapteren på 5 V trenger du nivåtilpasning.
Kan jeg slå av baklyset når det er mørkt?
Ja. lcd.noBacklight() slår av baklyset fra programmet. Du kan også fjerne jumperen på adapteren, men da må du sette den på igjen for å bruke baklyset.
Hvorfor viser DHT22 for høy temperatur?
Som oftest er det noe som varmer den, for eksempel baklyset, kortet, en regulator eller sol. Flytt sensoren bort fra elektronikken, helst utenfor boksen, og sammenlign med et termometer. Databladet oppgir ±0,5 °C, så et lite avvik er normalt.
Kan jeg bruke DHT22 med ESPHome og Home Assistant?
Ja, men da på en ESP32, ikke på UNO R4 WiFi. ESPHome har en egen komponent for DHT22 og AM2302, og den leser sensoren hvert minutt som standard. ESP32-guiden viser hvordan du kommer i gang med kortet.
Hvor lang kan kabelen til DHT22 være?
Databladet oppgir opptil 100 meter under gode forhold, men start med korte ledninger. Lange ledninger er følsomme for støy, og feilene kommer som «Sensorfeil».
Slik bygger du videre
Nå har du et program som måler, husker og viser. Neste naturlige skritt er å la det gjøre noe: guiden om relémodul med Arduino viser hvordan et lavspent utgangssignal kan styre en vifte eller lampe. Vil du måle ute, bruker værstasjonen med ESP32 og LoRa samme sensor. Inneklima handler også om mer enn fuktighet, og guiden om CO₂, VOC og PM2,5 forklarer hva de andre målingene betyr. Er selve programmeringen ny for deg, begynn med Arduino-programmering for nybegynnere.
Trenger du en annen skjerm til Arduino, finner du LCD, OLED og større fargeskjermer som IPS på 2,4 tommer under skjerm og display. Flere kort ligger under Arduino.
Kilder: pinout og datablad for UNO R4 WiFi, hd44780-biblioteket, Adafruits DHT-bibliotek, datablad for AM2302, datablad for PCF8574 og NIST om fuktighet over mettede saltløsninger.




