Styra en 230V-fläkt genom relä med Arduino
Kod för att hämta temperaturdata från två temperatursensorer, avgöra om dessa uppfyller vissa villkor, och baserat på detta slå av eller på ett relä.
OBS! All installation som innefattar starkström/230V skall utföras av elektriker! I detta fall handlar det om reläet.
Det finns nu en ny version av detta inlägg som är mer detaljerat och välbeskrivet!
Hårdvaran
Fjärrstyrning i all ära, men vad gör man om man vill ha en hyfsat simpel automatisk lösning för att styra exempelvis en lampa eller fläkt?
I mitt fall handlade det om min gamle far, vars verkstad är utrustad med en mindre 230v-fläkt. Denna suger ut luft i verkstaden och drar in ny luft från gästrumsvåningen ovanför. Tanken var att denna automatiskt skulle starta när temperaturen utomhus understiger den inomhus (inom rimliga gränser, såklart).
Jag bestämde mig för att en relativt billig (och lärorik) lösning vore att använda en Arduino tillsammans med två 1-Wire-temperatursensorer av modell DS18B20+. Ibland kan man vilja köra fläkten oberoende av sensorerna, så därför behövdes också en knapp med vilken man kan skifta läge mellan manuellt och automatiskt.
Sen har vi såklart reläet. Eftersom Arduino har en +5V-koppling kan vi använda HRM-S DC5V, som kan styras av denna. Dellistan så här långt:
https://www.m.nu/arduino-uno-r3-orginal-p-964.html
https://www.m.nu/temperatursensor-pa-kabel-ds18b20-p-44.html
https://www.m.nu/rela-hrms-dc5v-inklusive-sockel-p-153.html
https://www.m.nu/tactile-switch-buttons-12mm-square-6mm-tall-x-10-pack-p-702.html
Utöver dessa delar behövs också några smådelar. Till att börja med innehåller reläets base (brytaren, kan man säga) en spole, som bildar ett magnetfält när ström förs igenom den. När strömmen bryts kollapsar magnetfältet tillbaka in i spolen vilket kan bilda en ”spänningsspik” mångdubbelt större än den ursprungliga spänningen. Detta är såklart ganska tråkigt om det sker i en krets som normalt sett tycker om 5V, men då har någon fiffig ingenjör kommit fram till att man kan sätta en diod parallellt med spolen för att motverka dessa spikar och rädda kretsarna. Diod: https://www.m.nu/1n4001-diode-10-pack-p-853.html
Sen behövs också några motstånd. Till knappen behövs ett s.k. ”pull-down”-motstånd, vilket sätter knappens utdata till ”0” så länge knappen inte är intryckt. Temperatursensorerna, som vi kör i ett 1-Wire-nät, kan köras i parasitläge vilket är smidigt. Då ansluts ben 1 och 3 på temperatursensorn till Ground, medan ben 2 ansluts till +5V genom ett motstånd. Ben 2 ansluts också till en digitalport på Arduinon, som kommer samla in sensordatan därifrån. Vi säljer ett paket med alla nödvändiga motstånd (och fler därtill!): https://www.m.nu/kit-med-motstand-metallfilm-025w-1-30-varden-20-av-varje-600-motstand-totalt-p-925.html
Reläet är som tidigare nämnt anslutet till +5V. Den porten har högre Ampere än digitalportarna. Hur gör man då för att slå av och på reläet? Jo, man använder en transistor: https://www.m.nu/npn-bipolar-transistors-pn2222-10-pack-p-852.html för att öppna och sluta kretsen.
+5V som kommer från reläet ska alltså gå in i transistorns Collector, ”insamlare”, och Emitter, ”sändare”, kopplas till Arduinons Ground. Sedan styrs dess Base med en av Arduinons digitalportar. När porten är satt till LOW/0 är anslutningen bruten, och reläet är avstängt. När porten sätts till HIGH/1 aktiveras reläet och startar fläkten.
Här är ett litet fint Paint-diagram över hur allt ska vara anslutet (knappen exkluderad, ska fixa ett finare diagram):
Arduino-koden
När vi nu har alla delar och har anslutit allt rätt, måste vi programmera Arduinon för att göra det vi vill. Arduinon använder ett relativt simpelt språk som påminner om C, och det finns många färdiga kodbibliotek för olika tillämpningar. I vårt fall kommer vi använda oss av två kodbibliotek, för 1-Wire respektive DS18B20. Dessa måste installeras i Arduino IDE:n innan man kan kompilera koden.
Denna första version av koden ska egentligen modifieras, då den i nuläget har tre olika lägen (av, på, automatisk). Av-läget behövs inte, då det finns en separat brytare för fläkten. Men eftersom jag inte vet vad andra kan tänkas ha för nytta av koden bjuder jag på det läget också!
Kan kort nämna lite om hur koden är uppbyggd. Eftersom multitasking tydligen är bökigt på Arduino, tittar jag på den inbyggda millisekunds-räknarens värde för att avgöra när en viss syssla ska utföras. Sen kan man definiera intervall för de olika uppgifterna och jämföra ”senast uppgiften gjordes” minus ”nuvarande tid” >= ”intervall”. Har försökt kommentera väl och ställa upp saker så det är lätt att avläsa vad som görs var. De tre tillstånden är tydligt markerade.
OBS! Notera att jag hårdkodat 1-Wire-adresserna till de sensorer jag använde (med DeviceAddress-objekt). För att använda koden måste man ansluta två sensorer och använda denna (eller någon motsvarande) applikation för att hämta in adresserna med hjälp av Arduinon, och lägga in de nya värdena istället. Nu till koden (nedladdningsbar källkodsfil finns längst ner):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
#include <OneWire.h> #include <DallasTemperature.h> /* Fan control using 5V controlled relay * This code controls a relay (which, in turn, controls a 220v fan or whatever you wish). * It has a button to set state (Off, Always on, Temperature based). * For the temperature based state, two 1-Wire DS18B20+ temperature sensors are used * in parasitic mode to determine whether the fan should be on or not. * The LED on pin 13 is used to display which state is active (off, on, blinking). * I have used some code from the following guides: * http://www.arduino.cc/en/Tutorial/Switch * http://arduino.cc/en/Tutorial/BlinkWithoutDelay * http://www.strangeparty.com/2010/12/13/arduino-1-wire-temperature-sensors/ * * Jonas Lundblad @ www.m.nu * August 2014 * Version 1.0 */ //The 1-Wire bus is defined instead of initialized to a variable //int tempCollectorPin = 3; // the number of the pin which recieves temperature data #define ONE_WIRE_BUS 3 #define TEMPERATURE_PRECISION 9 // Setup a oneWire instance to communicate with any OneWire devices // (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); // Arrays representing the 1-Wire device addresses DeviceAddress outsideThermometer = { 0x28, 0xC6, 0x2B, 0x84, 0x05, 0x00, 0x00, 0x59 }; DeviceAddress insideThermometer = { 0x28, 0xBE, 0xA2, 0x95, 0x04, 0x00, 0x00, 0x44 }; int buttonInPin = 2; // the number of the input pin for the button int statusPin = 13; // the number of the output pin, builtin on pin 13 int relayControlPin = 5; // the number of the pin controlling the relay // controller state, default is 2 (using temp sensors) // Other states are 0 (off) and 1 (always on) int controllerState = 2; //This value is lowered to 2 if sensors are not found correctly, // thereby only allowing on or off state. int numberOfStates = 3; // These booleans are used to minimize DigitalWrites in the different states boolean on = true; // True if in controllerState 1 (on) boolean autoOn = false; boolean relayOn = false; // True if in relay mode and relay should be active int ledState = HIGH; // LED state used to toggle the LED int buttonRead; // the current reading from the input pin int prevButtonState = LOW; // the previous reading from the input pin // the follow variables are long's because the time, measured in miliseconds, // will quickly become a bigger number than can be stored in an int. long buttonTime = 0; // the last time the button was pressed long debounce = 200; // the debounce time, increase if the output flickers long prevMillisTemp = 0; // will store last time temp was updated long prevMillisBlink = 0; // last time LED state was changed //Intervals at which to update temperature and blink the LED in controller state 2 long tempInterval = 600000; //Ten minutes long lightInterval = 1000; void setup() { Serial.begin(9600); Serial.println("1-Wire DS18B20 relay controller started."); sensors.begin(); delay(5000); // locate devices on the bus (must be more than one) if (sensors.getDeviceCount() > 1) { Serial.println("Sensor count seem OK!"); if (!sensors.getAddress(outsideThermometer, 0)) Serial.println("Unable to find address for Device 0"); if (!sensors.getAddress(insideThermometer, 1)) Serial.println("Unable to find address for Device 1"); sensors.setResolution(insideThermometer, TEMPERATURE_PRECISION); sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION); } else { //If sensors not found correctly, disables sensor mode. Serial.println("Sensors not OK! Disabling sensor state..."); controllerState = 1; numberOfStates = 2; } // Setup the pins for input or output, respectively pinMode(buttonInPin, INPUT); pinMode(statusPin, OUTPUT); pinMode(relayControlPin, OUTPUT); } void loop() { //Save current millis() value (number of milliseconds since boot) unsigned long currentMillis = millis(); //Read button state (every iteration) //######################################################################### buttonRead = digitalRead(buttonInPin); // if the input just went from LOW and HIGH and we've waited long enough // to ignore any noise on the circuit, change controller state and // remember the time if (buttonRead == HIGH && prevButtonState == LOW && currentMillis - buttonTime > debounce) { //Modulo on the controller state integer, as it has only 3 states ++controllerState; controllerState = (controllerState % numberOfStates); Serial.print("Button pressed (?)! The new state is: "); Serial.println(controllerState); buttonTime = currentMillis; } prevButtonState = buttonRead; //######################################################################### // State 0, relay off, LED off // Added a "On" boolean which makes sure digitalWrite is not done when //it is unnecessary. //######################################################################### if (controllerState == 0) { if (on) { Serial.println("Stopping activity (state 0)"); digitalWrite(relayControlPin, LOW); digitalWrite(statusPin, LOW); on = false; } } //######################################################################### //State 1, relay on, LED on //######################################################################### else if (controllerState == 1) { if (!on) { Serial.println("Starting activity (state 1)"); digitalWrite(relayControlPin, HIGH); digitalWrite(statusPin, HIGH); on = true; } } //######################################################################### //State 2, relay on if temperature outside is low enough, LED blinking //######################################################################### else { //State 2, step 1 - toggle LED when applicable //###################################################### //Reset if millis() has reset (happens after ~50 days apparently) if(currentMillis < prevMillisBlink) { prevMillisBlink = 0; } if(currentMillis - prevMillisBlink > lightInterval) { // save the last time you blinked the LED prevMillisBlink = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(statusPin, ledState); } //###################################################### //State 2, step 2 - check whether relay should be on //###################################################### //Reset if millis() has reset (happens after ~50 days apparently) if (currentMillis < prevMillisTemp) { prevMillisTemp = 0; } // Also want to check when the unit has just been started if (currentMillis - prevMillisTemp > tempInterval) { //Get temperatures from sensors sensors.requestTemperatures(); //sensors.getTempC(outsideThermometer); float insideTemp, outsideTemp; int tries = 0; // Gets temperatures from specific sensors. // It is also possible to iterate over all 1-Wire // sensors, see examples in other guides insideTemp = sensors.getTempC(insideThermometer); outsideTemp = sensors.getTempC(outsideThermometer); // The below while-loop can be used if the temperature sensor returns // error codes (85.00, -127.00 and 127.49) some of the time. // It fetches temperature values again until both are valid. // Of course, it won't work if only error codes are returned //(incorrect connection of parasitic mode may cause this) //TemperatureOK(float) is defined at the bottom while (!temperatureOK(insideTemp) || !temperatureOK(outsideTemp)) { sensors.requestTemperatures(); if (!temperatureOK(insideTemp)) { insideTemp = sensors.getTempC(insideThermometer); } if (!temperatureOK(outsideTemp)) { outsideTemp = sensors.getTempC(outsideThermometer); } tries++; } Serial.print("Outside: "); Serial.print(outsideTemp); Serial.print(", Inside: "); Serial.print(insideTemp); Serial.print(", After "); Serial.print(tries); Serial.println(" retries."); //Is it colder outside? And is it warmer than 18 degrees inside? if (insideTemp+1 > outsideTemp && insideTemp > 18.00) { Serial.println("Fan should be on!"); if (!relayOn) { relayOn = true; Serial.println("Starting fan!"); digitalWrite(relayControlPin, HIGH); } } else { Serial.println("Fan shouldn't be on."); if (relayOn) { Serial.println("Stopping fan!"); relayOn = false; digitalWrite(relayControlPin, LOW); } } //digitalWrite(relayControlPin, HIGH); prevMillisTemp = currentMillis; } //###################################################### } //######################################################################### //End of states :o } //This method checks whether the provided temperature //is within the correct interval and returns a boolean. boolean temperatureOK(float temp) { if (temp > 50.00 || temp < -40.00) { return false; } else return true; } |
Att stoppa in koden här känns som ett dåligt alternativ då formateringen blev en aning kass och det är mycket kod. Kanske gör det senare när jag får det att se rätt ut.
Slutresultatet
Jag är nybörjare på att löda, och hade fel lödtenn, så ha överseende med att det ser bedrövligt ut 🙂
Ett problem uppdagades när allt var anslutet. Systemet verkade låsa sig efter en godtycklig tidsperiod, och det var svårt att felsöka. Då vi råkade ansluta en trafo med 12V växelström ut (vi visste inte ens att det fanns sådana), funderar vi på om det kan ha pajat Arduinon. Ska testa med en ny i Oktober, så uppdaterar inlägget med ny info då!
Här är hur som helst några bilder på underverket:


Och så källkodsfilen för nedladdning:
Arduino fan control (750 nedladdningar)
4 svar till Styra en 230V-fläkt genom relä med Arduino
skulle va intressant att få ett lite mer uppdaterat elschema
Vi ska titta på att göra en uppdaterad version av denna snarast! 🙂
Om du inte sett det har vi ordnat en ny version nu:
http://blog.m.nu/styra-rela-med-arduino/
Mycket nöje 🙂
[…] Ett av de första inläggen vi la upp på bloggen var ett där jag byggde en fläktkontroller som skulle ta indata från två temperatursensorer för att avgöra h…. […]