Arduino - Allerlei sensoren
In dit project draait alles om waarnemingen met behulp van eenvoudige sensoren. Bij deze sensoren wordt de eigenschap die wordt gemeten omgezet in een spanning en wordt de spanning vervolgens gemeten via een analogRead(). Zo kun je temperatuur bijvoorbeeld meten met behulp van een NTC. Dat is een weerstand waarvan de weerstandswaarde afneemt als de temperatuur toeneemt. Onderstaand schema geeft een werkbaar voorbeeld.
|
| Een voorbeeld van een simpele schakeling voor licht detectie |
Contents
De LDR
De letters LDR staan voor Light Dependent Resistor: een weerstand die lichtafhankelijk is. In volkomen donker is de weerstand van een LDR heel hoog, meer dan 40MΩ (40 miljoen Ohm) terwijl die vlakbij een felle lamp lager kan zijn dan 10Ω, maar onder normale lichtomstandigheden moet je denken aan enkele honderden Ohms oplopend tot wel enkele tienduizenden in de schaduw. De weerstand van een LDR varieert dus sterk met de hoeveelheid licht die erop valt. Als we een LDR in serie zetten met een weerstand van 5kΩ zal de tussenspanning (die op A0) oplopen met de lichtval. De spanning op A0 zal heel laag worden als de LDR in volkomen duisternis zit en kan dichtbij 5V komen bij zeer sterk licht. Net als in het vorige project kunnen we die spanning meten en op een LCD display laten zien.
De schakeling
Extra benodigdheden (voor een LDR lichtdetector):
- 1 weerstand van 4.7k
- 1 LDR weerstand
|
| Verbindingen in het breadboard met een bijbehorende schema tekening |
In bovenstaande schakeling laten we alleen het meet-gedeelte zien. Het programma gaat ervan uit dat je daarnaast een LCD display hebt aangesloten zoals in Het 1602 LCD Display. Als je geen LCD display hebt (of geen zin hebt om hem aan te sluiten) kun je ook de Serial Monitor gebruiken. Kijk dan even goed naar het programma: regels voor het gebruik van de Serial Monitor staan er wel maar als commentaar.
Hierboven is de LDR schakeling op het breadboard weergegeven. Voor dit project beperken we ons tot dit voorbeeld; voor andere sensoren geven we alleen een beschrijving aan van wat je op het breadboard moet doen. Steeds geldt dan dat A0 is verbonden met de A0-pin van de Arduino - de plek waar de meting binnenkomt.
Het programma
De programma's voor dit type metingen lijken allemaal sterk op elkaar. Steeds wordt de spanning op A0 gemeten (zoals in het vorige project), waarna die spanning wordt omgerekend naar de gemeten waarde (bijvoorbeeld de temperatuur). Dat omrekenen is steeds anders omdat elke sensor zijn eigen karakteristieken heeft. Om een sensor goed te kunne gebruiken moet je die karakteristieken wel kennen en soms moet je er eerst wat aan rekenen. Het is bijna altijd een goed idee om eerst een paar berekeningen met de hand te doen, bijvoorbeeld voor twee verschillende temperaturen, voordat je het programmaregels maakt voor de omrekening. Omdat we voor de hoeveelheid licht geen duidelijke eenheid hebben, doen we in dit eerste sensor project nog geen echte berekeningen. Het resultaat is een getal tussen 0 en 1000, waarbij 0 wijst op volkomen duisternis en 1000 op zeer fel licht.
#include <LiquidCrystal.h> // dit is de LCD bibliotheek
int sensor_pin = 0; // A0 is the analog input pin for the sensor signal
// maak het lcd object en geef aan welke hoe verbindingen lopen
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
void setup() {
// start het LCD object
lcd.begin(16, 2);
lcd.print("Lichtmeting");
// als je geen LCD hebt kun je ook de Serial Monitor gebruiken
// Serial.begin(9600);
}
float LCDtoLight(float volt) // deze functie is voor elke sensor anders
{
return volt*200; // even simpel: 0 Volt = 0; 5 Volt = 1000
}
void ShowResult(float value)
{
lcd.setCursor(0, 1);
lcd.print("helderheid: ");
lcd.print(value,0); // ",0" => geen decimalen
lcd.print(" "); // extra spaties om delen van oude getallen weg te halen
// zonder LCD:
// Serial.print("helderheid: ");
// Serial.println(value,0);
}
void loop() {
float volt = 0.00488758 * analogRead(sensor_pin); // lees de waarde uit en zet meteen om naar Volt
float value = LCDtoLight(volt);
ShowResult(value);
delay(500); // beperk response tot 2x per seconde
}
Uitleg van het programma
De loop() functie leest de waarde van A0 en zet die meteen om in Volt. Die spanning wordt in de functie LCDtoLight() omgerekend naar een hoeveelheid licht. Alle sensor- en schakelingkenmerken moeten dus verwerkt worden in deze functie. De functie ShowResult() zet het resultaat op de LCD (of de Serial Monitor). Als je een andere sensor wilt gebruiken moet je een omrekenfunctie maken met de karakteristieken van die sensor. Ook moet je de weergave functie aanpassen zodat het resultaat in de juiste eenheid en nauwkeurigheid wordt weergegeven.
Een IR vlam sensor - detecteren van vuur
Een IR flame sensor is eigenlijk een infrarood sensor. Infrarood licht is onzichtbare warmtestraling en vuur geeft natuurlijk veel warmtestraling. Vandaar dat dit element een vuur-sensor genoemd kan worden. Het element is alleen gevoelig voor infrarood licht door de plastic omhulling die alleen infrarood licht doorlaat. Het laat dus geen zichtbaar licht door en lijkt daarom zwart plastic. De sensor is in feite een diode die in de sperrichting (de richting waarin normaal geen stroom kan lopen) toch stroom doorlaat als er infrarood licht op valt. Naarmate er meer licht op valt wordt er ook meer stroom doorgelaten. Het is maar en goedkope sensor en de gevoeligheid voor het infrarode licht van vlammen tot zo'n 500˚C is eigenlijk vrij laag. Vandaar dat je een kaars relatief dichtbij zult moeten houden voor een goede response.
Extra benodigdheden voor de schakeling van de vlam sensor:
- 1 weerstand van 10k
- 1 IR flame sensor diode
De schakeling is precies gelijk aan die van de LDR, waarbij een wat grotere weerstand gebruikt kan worden en de photodiode natuurlijk de plaats inneemt van de LDR. Let wel op dat het korte pootje van de photodiode aansluit op de plus en het lange pootje aan de weerstand en pin A0 van de Arduino. Op deze manier staat de photodiode in sperrichting en loopt de spanning op A0 op naarmate er meer infrarood licht op de sensor valt.
Bij hetzelfde programma als hierboven kreeg ik in mijn opstelling een signaal van tussen de 725 en 750, in een goed verlichte kamer op een bewolkte, zelfs regenachtige dag. Met een kaars op 5 cm werd het signaal duidelijk sterker en kwam dan boven de 850, maar om boven de 900 te komen moest ik het kaarsje echt dichtbij houden. Op een hete soldeerbout reageerde de sensor nauwelijks. Voor de beveiliging van je huis kan ik deze vlam-sensor dan ook niet aanbevelen.
De NTC
Een NTC is een weerstand waarvan de weerstandswaarde lager wordt met de temperatuur. Bij hogere temperatuur is de weerstand van een NTC dus lager, en omgekeerd. Als we in de eerdere schakeling de LDR vervangen door een NTC zal de spanning op A0 dus toenemen met de temperatuur en afnemen als de temperatuur lager wordt. Hoewel de schakeling zeer eenvoudig is, is het verband tussen temperatuur en spanning is niet rechtlijnig wat een complicerende factor is. De relatie tussen weerstand en temperatuur is voor een NTC alsvolgt:
R_NTC = A * exp(B/(T+273.15))
Of omgekeerd:
T = B / ln(R_NTC/A) - 273
Hierbij zijn A en B constanten en is T de temperatuur (in ˚C).
In onze schakeling gebruiken we een NTC waar op staat "33k". Dit getal wijst erop dat deze NTC een weerstand zou moeten hebben van 33kΩ bij 25˚C. Bij meting blijkt hij een weerstand te hebben van 46,3kΩ bij 19,2˚C (kraanwater) en van 5,61kΩ bij 70.2˚C (hete thee). Na enige tijd is de temperatuur van de thee gezakt naar 64,5˚C en meten we 6,95kΩ. Een stuk later bij 42.7˚C blijkt de weerstand 15.69kΩ. Let op dat bij dit soort metingen het metaal van de NTC het water niet raakt, want dan loopt er ook stroom door de vloeistof en meet je dus niet zuiver de NTC. Dit is gemakkelijk te voorkomen door de NTC steeds in een plastic zakje te houden. Met deze gegevens kunnen we A en B in principe bepalen, al kost dat misschien enige moeite. We vinden dan:
A=0.0295
B=4159
Deze waarden zijn vrij gevoelig voor meetfouten en je kunt dan ook gemakkelijk wat andere waarden vinden. Mede hierdoor is een temperatuurmeting met een NTC meestal niet zo nauwkeurig.
De waarden voor A en B zijn de belangrijkste karakteristieken van de NTC en noodzakelijk om de temperatuur te kunnen bepalen. Dat doen we via een bepaling van de weerstand en die bepalen we via de spanning op A0. De spanning over de NTC is gelijk aan: 5-A0. De stroom door de NTC is gelijk aan de stroom door de serieweerstand en die is: A0/R0. De weerstand van de NTC is gelijk aan de spanning over de NTC gedeeld door de stroom door de NTC en dus: (5-A0)/(A0/R0).
R_NTC = R0*(5/A0-1)
Dus:
T = B / ln((R0*(5/A0-1))/A) - 273
Met deze functie en de eerder gevonden parameterwaarden kunnen we nu een functie maken die de spanning omzet naar een temperatuur:
#include <math.h> // dit is nodig omdat we in de formule de ln() functie gebruiken
float NTCtoTemp(float volt)
{
// de karaktersitieken van de schakeling en de NTC
float A = 0.0295;
float B = 4159;
float R0 = 4700;
// de omrekenformule
return B / log((R0*(5/volt-1))/A) - 273; // ln() heet log() in de <math.h> library
}
Ook de ShorResult() functie moet natuurlijk worden aangepast en dan wil je natuurlijk de eenhied netjes weergeven als °C. Daarvoor blijkt een speciale code nodig te zijn:
void ShowResult(float value)
{
lcd.setCursor(0, 1);
lcd.print("temp.: ");
lcd.print(value,1); // ",1" => aantal decimalen
lcd.print(" \337C "); // "\337" staat voor het graden ° teken
}
Na al deze moeite hebben we een bruikbare maar nog altijd niet erg nauwkeurige temperatuurmeting. De NTC wordt dan ook voornamelijk gebruikt bij temperatuur indicatie. In dergelijke gavllen hoef je niet precies te weten hoe warm het is; het is genoeg als je weet dat het behoorlijk warm aan het worden is. De koeling moet misschien aan of harder als het gevaarlijk warm dreigt te worden. Dan is de NTC een prima oplossing. De LM35 is een eveneens goedkope temperatuur sensor die wèl bedoeld is voor echte temperatuurmetingen.
Temperatuur meten met de LM35
De LM35 is een goedkope maar zeer bruikbare temperatuur sensor die heel gemakkelijk is om te gebruiken. Het signaal is een spanning die 0 Volt is bij 0 ˚C en oploopt met 0.01 volt per graad. De temperatuur is dus heel eenvoudig uit te rekenen door de spanning te vermenigvuldigen met 100. Het element heeft geen ijking nodig en de nauwkeurigheid is rond de halve graad (beste bij 25 ˚C). Bij gebruik van een LM35 is geen serie-weerstand nodig en het resultaat is niet afhankelijk van de exacte voedingsspanning die moet liggen tussen de 4 en 30 Volt. De opgegeven nauwkeurigheid gaat er wel van uit dat de spanning van het signaal foutloos gemeten kan worden. De manier waarop we dat tot nu toe doen is zeker niet foutloos. Een meetfout van 0.1 Volt leidt al tot een afwijking van 10 graden. Dit betekent dat je voor een goed resultaat vooral de kwaliteit van de spanningsmeting goed moet bekijken.
De LM35 wordt alsvolgt aangesloten:
|
| Verbindingen in het breadboard met een bijbehorende schema tekening |
De functie om de temperatuur uit de spanning te berekenen is dan alsvolgt:
float LM35toTemp(float volt)
{
// de LM35 heeft een rechtlijnig signaal: 10 mV per °C
// dus: T = volt * 100
return volt*100;
}
De LM35 is gemakkelijker, betrouwbaarder en nauwkeuriger in gebruik dan de NTC en de schakeling heeft geen extra weerstand nodig.
Naast de LM35, die vooral handig is als je met graden Celsius werkt, is er een LM34 die bedoeld is om met graden Fahrenheit te werken. Ook zijn er andere sensoren die wat de werking en bruikbaarheid zeer vergelijkbaar zijn met de LM35/LM34, zoals de TMP36 familie (omvat ook TMP35 en TMP37).
Na het lezen van het bovenstaande zul je die LM35 wel waarderen. Het wordt alleen wat lastiger als de temperatuur onder het vriespunt komt. De LM35 zou dan een negatieve spanning moeten geven, maar omdat dat buiten het bereik valt, lukt dat niet. We kunnen negatieve spanningen overigens ook niet zomaar meten met de Arduino. Wat we wel kunnen doen is de LM35 voor de gek houden door de nul-pin aan te sluiten op een iets hoger voltage. Dat kan prima worden gedaan met wee diodes. Een diode is een elementje dat de stroom maar in een richting doorlaat (de richting van het pijltje), maar een detail is dat over de diode een constante spanning blijft staan van 0.45 Volt. Twee diodes maken dan 0.9 Volt. De nul van de LM35 wordt daarmee feitelijk 0.9 Volt terwijl het ding denkt dat het 0 is. De uitgang van het signaal moet vervolgens naar de echte nul worden getrokken via een weerstand van 18k. Het signaal is dan wel 0.9 Volt hoger dan normaal en dus moet dat eerst worden afgetrokken voor we de temperatuur berekenen:
T = (volt-0.9) * 100
De schakeling ziet er dan alsvolgt uit:
![]()
|
| De schakeling voor het meten bij vorst |
Toch weer iets ingewikkelder, maar het is dus alleen nodig als je ook bij vorst wilt kunnen meten.
Temperatuur meten met een Pt100
Een Pt100 is een zeer betrouwbare sensor die industrieel heel veel wordt gebruikt voor het nauwkeurig meten van temperatuur. Het bestaat in principe uit een heel dunne platina draad die een weerstand heeft van 100Ω bij 0°C. Dit verklaard ook de naam van de sensor omdat de scheikundige aanduiding van platina Pt is. Platina is een zeer edel metaal, roest niet en wordt zelfs niet aangetast door sterke zuren en andere chemische invloeden (maar pas op: de rest van de sensor, zoals behuizing en verbindingsdraden, kunnen wel worden aangetast). Platina heeft bovendien een hoog smeltpunt (boven de 2000°C) waardoor een Pt100 in principe gebruikt kan worden over een zeer brede temperatuurrange. Van platina is goed bekend hoe de elektrische weerstand toeneemt met de temperatuur. Omdat we de weerstand bij T=0°C kennen, kunnen we de weerstand op elke temperatuur uitrekenen zonder verdere informatie en is er geen ijking nodig. Er is zelfs een Europese norm voor de Pt100:
R_Pt100 = 100 + 0.385055 * T
R is dan de weerstand van de Pt100 in Ω T is de temperatuur in °C
Het komt erop neer dat de weerstand van een zuivere Pt100 toeneemt met 0.385055 Ohm per graad Celsius. Goedkope Pt100 sensoren willen echter wel eens afwijken en hebben dan bijvoorbeeld een weerstand van 104Ω bij 0°C. Als je hier geen rekening mee houdt leidt dat zomaar tot een meetfout van meer dan 10 graden! Je kunt je Pt100 controleren door de weerstand van je sensor te meten bij 0°C. Dat maak je door koud water in een thermoskan te doen en daar een ruime hoeveelheid ijsblokjes aan toe te voegen. Sluit de thermoskan goed af en wacht een paar uur. Eventueel schud je de kan een paar keer. Controleer af en toe of er nog voldoende ijs in het water drijft; als dat dreigt op te raken moet je meer ijs toevoegen. Als de boel in evenwicht is gekomen, is de temperatuur van het water gelijk aan die van smeltend ijs en dus 0°C. Nu kun je je sensor erin hangen en de weerstand meten. Ook nu moet je wachten tot de sensor van binnen helemaal is afgekoeld en de weerstand niet meer veranderd. Te kort wachten geeft een slechte meting, maar langer wachten kan geen kwaad. Je kunt dan onderstaande, aangepaste formule gebruiken:
R_Pt100 = R_0C * (1+0.00385055*T)
ofwel:
T = 259.703*(R_Pt100/R_0C - 1)
Hierin is R_0C de gemeten weerstand bij 0 °C
Nu kunnen we met Arduino geen weerstanden meten, alleen spanningen. Als we de schakeling van de LDR bekijken zien we dat de gemeten spanning wel degelijk maatgevend is voor de weerstand van de LDR. Als die weerstand afneemt wordt de spanning hoger en vice versa. Voor de Pt100 plaatsen we echter liever de sensor aan de nul en de serieweerstand aan de plus. De schakeling is dan alsvolgt:
|
| De schakeling voor de Pt100 |
We kunnen de weerstand van de Pt100 uitrekenen met de volgende formule:
R_Pt100 = R1*U/(Vcc-U)
Waarbij Vcc de voedingsspanning is (5V), R1 de serieweerstand en U de gemeten spanning op A0. Dus:
T = 259.703 * ( (R1/R_0C) * U/(Vcc-U) - 1)
Het getal 259.703 is gelijk aan 1/0.00385055. Arduino's kunnen een stuk sneller vermenigvuldigen, vandaar dat deze vorm de voorkeur heeft.
We kiezen R1=1000Ω en gaan even uit van een R_0C=100Ω en Vcc=5V. Bij -20 °C zal de spanning op A0 0.4225 Volt zijn, terwijl die spanning bij 100°C 0.6083 Volt zou moeten zijn. Over de meest interessante temperatuur-range, is het spanningsverschil slechts 0.18 Volt. Als we die spanning met A0 meten zal de analogRead() een waarde geven van 86 (bij -20°C) tot 124 (bij 100°C): slechts 38 punten voor een range van 120 graden. Het is duidelijk dat de resolutie nogal slecht is omdat elk puntje voor ruim 3 graden staat. Gelukking kunnen we dit zonder verdere componenten nog wat verbeteren door een andere refentiespanning te gebruiken. Daarover gaat de volgende paragraaf.
ARef
Het resultaat van een analogRead() opdracht is:
A0 = 1023 * U / ARef
Hierbij is U de spanning op pin A0 en ARef de referentiespanning. Normaal gebruikt Arduino Vcc als referentiespanning. Vcc is dan 5 Volt waardoor elk punt in A0 gelijk staat aan 5/1023 = 5 milli Volt. Hier kleven echter een paar problemen aan. Het eertse is dat Vcc meestal niet precies 5 Volt is, maar vaker iets in de buurt van 4.9 Volt, zelfs als de USB keurig 5 Volt afgeeft. Nu kun je daar rekening mee houden, maar als je vervolgens van voedingsbron wisselt en bijvoorbeeld overgaat op batterijvoeding, kan Vcc plots een andere waarde krijgen. Dit blijft problematisch zolang spanningen worden gemeten ten opzichte van Vcc. Een ander probleem is dat de range 0-5 Volt voor sommige toepassingen te groot is. Als je alleen spanningen wilt meten die tussen 0 en 1 Volt liggen, blijft een groot deel van je schaal onbenut. Je resolutie (de stapgrootte) is dan minder goed dan je wellicht zou willen. Dit zien we ook bij de Pt100 waarbij de interessante range loopt van ongeveer 0.4 tot 0.6 Volt.
Als je goed naar je Arduino kijkt zie je een pin met de naam ARef. Deze zit in de buurt van pin 13. Op deze pin kun je een spanning aanbieden en die als referentie laten dienen voor alle analogRead() calls. Alle metingen van analogRead() worden dan gerefereerd aan deze spanning. Als je ARef bijvoorbeeld precies 1 Volt is, krijgt je analogRead() 1023 stapjes tussen 0 en 1 Volt, in plaats van 1023 stapjes tussen 0 en 5 Volt. De precisie van de meting is dan 5 keer beter. Nu moet je wel opletten. Als je op ARef een externe referentiespanning zet moet je ervoor zorgen dat Arduino die ook gebruikt. Dat doe je door analogReference(EXTERNAL) aan te roepen in je setup(). Als je dit niet doet is er grote kans dat je Arduino doorbrandt bij de eerste aanroep van analogRead(). Dit komt doordat de Arduino dan intern Vcc op ARef zal zetten, terwijl daar al een andere spanning op staat. Probleem! Zorg er dus altijd voor dat analogReference() wordt aangeroepen vóór de eerste analogRead()! Zorg er ook voor dat je externe referentiespanning niet meer is dan 5 Volt.
Naast een extern referentiesignaal kun je ook een interne referentie gebruiken, afwijkend van Vcc. Als je analogReference(INTERNAL) aanroept wordt je referentiespanning 1,1 Volt. Je hebt dan niks anders nodig en hoeft ook geen aansluiting te maken naar ARef. Dit geldt voor de meeste Arduino's, maar op een Arduino met een Atmega8 chip is het 2.56 Volt. Een Arduino Mega heeft beide mogelijkheden: 1.1 Volt en 2.56 Volt. De eerste wordt dan aangezet met analogReference(INTERNAL1V1) en de tweede met analogReference(INTERNAL2V56). Zoals gezegd werkt dit alleen op Atmega's. Overigens is die 1,1 Volt als het goed is gewoon 1,1 Volt, ook als Vcc wat afwijkt van 5 Volt.
Het gebruik van een referentie anders dan Vcc levert in het algemeen nauwkeurigere metingen en biedt de vrijheid om een geschikter meetbereik te kiezen.
Voor de Pt100 met bovengenoemde schakeling zou je dan de volgende regel kunnen toevoegen aan setup():
analogReference(INTERNAL);
De functie om de temperatuur te bepalen ziet er dan alsvolgt uit:
float GetTemperature()
{
float R1 = 1000;
float R_0C = 100; // standaard Pt100
float ARef = 1.1;
float U = analogRead(sensor_pin) * ARef / 1023;
float T = 259.703 * ( (R1/R_0C) * U/(ARef-U) - 1);
return T;
}
De constanten R1, R_0C en ARef kunnen ook buiten de functie worden gedefinieerd. Als je voor efficiency gaat kun je beter nog een constante gebruiken voor ARef/1023 en een voor R1/R_0C. De functie is dan nog sneller klaar.
ARef
Normaal gebruikt Arduino Vcc als referentiespanning. De meetwaarde op een analoge ingang is dan gelijk aan:
A0 = 1023 * U / Vcc
waarbij U de aangeboden spanning op de ingang is. In ons geval varieert U tussen 0.4225 en 0.6083 Volt. Als de spanning En dan ARef=1.1V.
Andere sensoren
- Gas sensor
- Luchtdruk sensor
- Water flow sensor
- Luchtvochtigheidssensor (en temperatuur sensor)
Beetje spelen
Met alle hier behandelde sensoren en de diverse opties daarbij hebben we al genoeg gespeeld.





