Flexit Ventil med Servo Åpning og PC Vifte
Kona vil ha frisk luft på soverommet, mens jeg synes det blir for kaldt i løpet av natta.
Jeg forsøkte meg med en Fakro ZWS230 for å lukke vinduet automatisk, men den bråket for mye (WAF=0).
Jeg ønsket derfor å forsøke meg på en servostyrt ventil med en vifte i som kunne blåse frisk luft inn, men lukkes automatisk dersom det blir for kaldt.
Vifta skulle være en lydsvak PC vifte med høyt volum, og hastigheten skulle kunne reguleres trinnløst med PWM.
Siden dette skulle gå på fast strøm, valgte jeg WiFi som kommunikasjon. Arduino MRK WiFi 1010 er en kompakt og grei microprosessor som hadde alt jeg trengte. Siden jeg hadde pinner til overs, slang jeg på en temperatursensor og en RGB LED for å kunne kommunisere med omverdenen. Dette er greit for debugging, men også for å kunne vise status under konfigurasjon.
Utgangspunktet er en Flexit ventil med snortrekk for å få en blank (ikke 3D printet) front.
Har du en slik eksisterende ventil, kan du altså oppgradere med denne
Jeg hadde ikke ventil på soverommet fra før og måtte derfor sette inn en slik.
Byggeveiledningen:
(Du finner "handlelisten" i bunn av denne posten)
Start med å fjerne mekanismen for snortrekket og erstatt fjæra med en svakere en.
Min er hentet fra biltema sitt fjærsett. Den bør være sterk nok til at ventilen ikke klapprer i vind, men svak nok til at servoen orker å dra den.
Etter masse prøving og feiling landet jeg på denne 3D modellen som ble modellert i Onshape og printet på en Flashforge Inventor.
Onshape er gratis for hobbyfolk, men er helt profft og veldig greit å bruke. Anbefales!
3D modellen er tilgjengelig for dere for printing eller viderearbeid
(Lag deg en gratis konto, logg inn og trykk på linken i delelista nederst)
STL filer ligger også vedlagt.
PC vifter skal PWM styres med 25KHz, og det tok meg en del tid å finne ut hvordan en endrer PWM frekvensen på Arduinoen til dette.
Her er koden som må kjøres etter andre interupt oppsett:
/******************* setup_25khz_PWM **********************/
void setup_25khz_PWM() {
// Output 25kHz PWM on digital pin D6 using timer TCC0 (10-bit resolution)
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK4
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
GCLK_GENCTRL_ID(4); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Enable the port multiplexer for the TCC0 PWM channel 2 (digital pin D6), SAMD21 pin PA20
PORT->Group[g_APinDescription[6].ulPort].PINCFG[g_APinDescription[6].ulPin].bit.PMUXEN = 1;
// Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
// F & E specify the timers: TCC0, TCC1 and TCC2
PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg |= /*PORT_PMUX_PMUXO_F |*/ PORT_PMUX_PMUXE_F;
// Feed GCLK4 to TCC0 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK4 to TCC0 and TCC1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Normal (single slope) PWM operation: timer countinuouslys count up to PER register value and then is reset to 0
REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCC0
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
// Each timer counts up to a maximum or TOP value set by the PER (period) register,
// this determines the frequency of the PWM operation:
// 1919 = 25kHz
REG_TCC0_PER = 1919; // Set the frequency of the PWM on TCC0 to 25kHz
while(TCC0->SYNCBUSY.bit.PER);
// The CCBx register value determines the duty cycle
REG_TCC0_CCB2 = 959; // TCC0 CCB2 - 50% duty cycle on D6
while(TCC0->SYNCBUSY.bit.CCB2);
// Divide the 48MHz signal by 1 giving 48MHz (20.8ns) TCC0 timer tick and enable the outputs
REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
Vifta har mulighet til å rapportere faktisk hastighet, men det fikk jeg aldri helt til og droppet derfor det.
Å styre servo og blinke med LED er jo planke med en Arduino, så det går jeg ikke gjennom.
Siden vifta skulle ha 12V og servoen og Arduinoen 5V, måtte jeg inn med en regulator. Kunne sikkert brukt noe mindre og enklere i To-220 kapsel, men jeg hadde en haug med slike liggende:
DS18B20 er en artig IC som måler temperatur og er lett å kommunisere med fra en Arduino. Det eneste den trenger er en pull-up resistor på 4,7k
Siden LEDen også trenger 3 resistorer (220ohm), tok jeg like godt et lite kretskort med faste baner og monterte alt sammen på det.
Kuttet noen baner med bor, laget kryssninger med noe koppertråd og loddet på vinklede kontakter på enden.
Det hele blir mer stabilt også.
En liten borrefeil korrigeres med en liten kabelstump (blå)
Spenningsregulatorpinnene er merket med rødt.
Ellers er koblingene laget slik at de går mest mulig rett ut på koblingspinnene.
Her er skjema tegnet med Arduinoen og kontaktene i samme linjer som på kretskortet
Fargene på ledningene er slik de er i virkeligheten.
Vifta og servoen som har ferdige kontakter kan plugges rett inn:
Siden ventilen allerede har et hull der snora satt, kan det borres opp til 4.5 - 4.8 mm og LED'en presses inn der.
Kabel lages med riktig pinout ved å lime sammen hunn-hunn koblingskabel med superlim:
Temperatur IC'en loddes også på en slik kabel:
men her tillater jeg meg å dele dem opp slik at den brune signalkabelen plugges for seg.
- det ble alt for mye styr å få dem til å gå inn ved siden av hverandre.
Pinnene skilles med litt kna-epoxy:
Da er det klart for montering.
Først inn med servoen. Her må en bruke kubbe-trekker.
Så er det på med viftefestene. Legg merke til at de skal forbi begge hakkene. (Bare dra selv om gummien blir hvit, - de tåler det)
Neste er å feste temperatursensoren med mere kna-epoxy:
Når epoxyen har herdet, kan du sette på vifta. OBS! ledningen skal gjennom utsparingen for dette i kanten.
Fjern gjerne litt av strømpa, så blir kabelen mykere å legge og fargene synlig for å kunne sette kontaktene riktig vei.
Vifta sitter på yttersiden nettop fordi det skal bli enklere å trekke gummifestene igjennom vifta.
Nå bør du plugge det hele sammen og teste på benken før du fester noe på servoen.
Her er kode for Arduinoen (ino fil er også vedlagt):
#include <Servo.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFiNINA.h>
// SERVO
Servo myservo;
int mpos = 0; // variable to store the servo position
// LED
#define RED 7
#define GREEN 8
#define BLUE 9
//TEMP
#define ONE_WIRE_BUS 10
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
//SERVO
#define SERVO 11
// WEB
char ssid[] = "XXXXX"; // your network SSID (name)
char pass[] = "YYYYY"; // your network password (use for WPA, or use as key for WEP)
IPAddress IPAdresse = IPAddress(192,168,ZZZ,ZZZ);
WiFiClient client;
int status = WL_IDLE_STATUS;
WiFiServer server(80);
//VARS
int led = LED_BUILTIN;
char c;
int iFanSpeed =1;
int valve=90;
String currentLine;
int iTemp;
/******************************************/
void setup()
{
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
pinMode(BLUE, OUTPUT);
lightLed(LOW,LOW,LOW); // AV
delay(1000);
lightLed(HIGH,LOW,LOW); //RØD
delay(1000);
lightLed(LOW,HIGH,LOW); // GRØNN
delay(1000);
lightLed(LOW,LOW,HIGH); // BLÅ
delay(1000);
// Serial.begin(9600);
// while (!Serial) ; // wait for serial port to connect. Needed for native USB port only
Serial.println("Start");
lightLed(HIGH,LOW,LOW); //RØD
init_servo();
lightLed(LOW,LOW,HIGH); // BLÅ
setup_25khz_PWM();
init_wifi();
lightLed(LOW,LOW,LOW); // AV
}
/******************************************/
void loop() {
client = server.available(); // listen for incoming clients
if (client) {
currentLine = "";
c=0;
while (client.connected() && (c != '\n') ) {
if (client.available()) {
c = client.read();
if (c!='\n' and c!='\r') {
currentLine += c;
}
}
}
if (currentLine.startsWith("GET /")) {
lightLed(LOW,HIGH,LOW); // GRØNN
delay(1000);
Serial.print("Funnet: ");
Serial.println(currentLine);
parseGet(currentLine);
read_temp();
lightLed(LOW,LOW,LOW); // AV
delay(1000);
}
if (currentLine=="") {
send2client(iFanSpeed,mpos);
client.stop();
Serial.println("client disonnected");
}
}
}
void moveto(int pos) {
mpos=pos;
myservo.write(mpos);
}
void lightLed(int red,int green, int blue) {
digitalWrite(RED, red);
digitalWrite(GREEN, green);
digitalWrite(BLUE, blue);
}
void fanSpeed(int iSpeed) {
// 0 to 1919
Serial.print("Inout Speed:");
Serial.println(iSpeed);
iFanSpeed = iSpeed * 1919;
Serial.print("Calc Speed:");
Serial.println(iFanSpeed);
iFanSpeed = iFanSpeed /100;
Serial.print("Set Speed:");
Serial.println(iFanSpeed);
REG_TCC0_CCB2 = iFanSpeed;
// REG_TCC0_CCB2 = 767;
while(TCC0->SYNCBUSY.bit.CCB2);
}
void parseGet(String s) {
int p1;
int p2;
String sVerb = "speed=";
int i;
String ss;
// Serial.println(s.indexOf("speed="));
p1=s.indexOf(sVerb);
if (p1 > -1 ) {
p2=s.indexOf("&",p1+1);
ss=s.substring(p1 + sVerb.length(),p2);
i=ss.toInt();
fanSpeed(i);
} else {
Serial.println("not Found" + sVerb);
}
sVerb = "valve=";
p1=s.indexOf(sVerb);
if (p1 > -1 ) {
p2=s.indexOf(" ",p1+1);
ss=s.substring(p1 + sVerb.length(),p2);
i=ss.toInt();
// moveto_slow(i,50);
moveto(i);
} else {
Serial.println("not Found" + sVerb);
}
}
void moveto_slow(int target_pos, int idelay) {
int i;
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("moveto slow");
if (target_pos > mpos) {
Serial.println("tell opp");
for (i = mpos; i < target_pos; i += 1) {
moveto(i);
delay(idelay);
}
} else {
Serial.println("tell ned");
for (i = mpos; i > target_pos; i -= 1) {
moveto(i);
delay(idelay);
}
}
digitalWrite(LED_BUILTIN, LOW);
}
void read_temp() {
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
Serial.print("Temperature for Device 1 is: ");
iTemp=sensors.getTempCByIndex(0);
Serial.print(iTemp); // Why "byIndex"? You can have more than one IC on the same bus. 0 refers to the first IC on the wire
}
/******************************************/
void init_temp() {
sensors.begin(); // IC Default 9 bit. If you have troubles consider upping it 12. Ups the delay giving the IC more time to process the temperature measurement
}
/******************************************/
void init_servo() {
myservo.attach(SERVO);
}
/******************* setup_25khz_PWM **********************/
void send2client(int ispeed, int valve) {
String s;
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
// s="<form> <input checked=\"checked\" name=\"sex\" type=\"radio\" value=\"male\" /> Malex <br /><input name=\"sex\" type=\"radio\" value=\"female\" /> Female <br /> ";
s="<form>";
// s="<form> <input checked=\"checked\" name=\"sex\" type=\"radio\" value=\"male\" /> Malex <br /><input name=\"sex\" type=\"radio\" value=\"female\" /> Female <br /> ";
s=s + "Temp: " + iTemp + " <br /> ";
s=s + "Speed <input name=\"speed\" type=\"\" value=\"" + ispeed + "\" /><br /> ";
s=s + "Valve <input name=\"valve\" type=\"\" value=\"" + mpos + "\" /><br><br> ";
s=s + "<button type=\"submit\" value=\"Submit\">Submit</button></form>";
client.print(s);
// The HTTP response ends with another blank line:
client.println();
}
/******************* setup_25khz_PWM **********************/
void setup_25khz_PWM() {
// Output 25kHz PWM on digital pin D6 using timer TCC0 (10-bit resolution)
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK4
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
GCLK_GENCTRL_ID(4); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Enable the port multiplexer for the TCC0 PWM channel 2 (digital pin D6), SAMD21 pin PA20
PORT->Group[g_APinDescription[6].ulPort].PINCFG[g_APinDescription[6].ulPin].bit.PMUXEN = 1;
// Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
// F & E specify the timers: TCC0, TCC1 and TCC2
PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg |= /*PORT_PMUX_PMUXO_F |*/ PORT_PMUX_PMUXE_F;
// Feed GCLK4 to TCC0 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK4 to TCC0 and TCC1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Normal (single slope) PWM operation: timer countinuouslys count up to PER register value and then is reset to 0
REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCC0
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
// Each timer counts up to a maximum or TOP value set by the PER (period) register,
// this determines the frequency of the PWM operation:
// 1919 = 25kHz
REG_TCC0_PER = 1919; // Set the frequency of the PWM on TCC0 to 25kHz
while(TCC0->SYNCBUSY.bit.PER);
// The CCBx register value determines the duty cycle
REG_TCC0_CCB2 = 959; // TCC0 CCB2 - 50% duty cycle on D6
while(TCC0->SYNCBUSY.bit.CCB2);
// Divide the 48MHz signal by 1 giving 48MHz (20.8ns) TCC0 timer tick and enable the outputs
REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
/******************************************/
void init_wifi() {
WiFi.config(IPAdresse);
// attempt to connect to Wifi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to Network named: ");
Serial.println(ssid); // print the network name (SSID);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 1 seconds for connection:
delay(1000);
}
server.begin(); // start the web server on port 80
printWifiStatus(); // you're connected now, so print out the status
}
/******************************************/
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
// print where to go in a browser:
Serial.print("To see this page in action, open a browser to http://");
Serial.println(ip);
}
Husk å endre WiFi SSID, passord og IP adresse:
Når du går til IP-adressen du anga med en browser, skal du få opp denne websiden:
Hastigheten på vifta går fra 0 til 100 og ventilen fra 0 til 180 (grader).
Nå kan du testkjøre vifta og servoen og sjekke temperaturen før du går videre.
Skru deretter stagene sammen og fest dem på en avklippet servo-arm.
Kjør servoen tilbake til "helt lukket" (0) og fest staget på servoen slik at det er en liten klaring til veggen
Test at servoen beveger seg som den skal, sett den i "helt åpen" (180) posisjon og skru deretter staget fast i luka.
Nå må du montere hele greia i veggen før du fester LED'en, setter luka på plass og fester fjæra.
Integrasjonen med Alexa og HomeSeer er foreløpig enkel
Alexa har 3 Routines ("Bedroom Fan Close", "Bedroom Fan Silent" og "Bedroom Fan Full") som setter en verdi på en virtuell device
(Tips: La kona bestemme kommandoene så husker hun dem )
Denne har eventer som trigger når den blir satt:
Som i sin tur kaller opp primitiv-eventer:
Selve kommandoen er denne:
&hs.URLAction("http://192.168.XXX.YYY/?speed=0&valve=0", "GET", "", "")
Da er det bare å teste:
Helt til slutt vil jeg si noe om koden:
Den er ikke ferdig og slik jeg vil ha den!
Problemet er at om jeg skulle vente med å publisere dette til den ble perfekt, ville det aldri bli postet noe...
Jeg gir dere derfor koden slik den er akkurat nå og så får vi heller jobbe sammen med å få på plass de tingene jeg ønsker meg:
- Når den starter, bør den hente SSID og pwd fra EEPROM
- Dersom den ikke får kontakt med WiFi, bør den gå i AP mode slik at du kan koble til den på en fast IP (10.0.0.1 feks) med telefonen og få opp en side der du kan velge SSID og oppgi passord og ønsket IP (mens dette skjer kan den f. eks. lyse blått)
- Dersom du velger en egen parameter, burde du få opp en side der du kan endre IP
- Den burde snakke på Web-socket med HomeSeer
-
HomeSeer burde ha egne devicer med slidere for speed og ventilåpning.
Og en egen device som viser temp
DEL GJERNE TILBAKE DET DERE LAGER
Deleliste:
3D Modell i OnShape (Eller bare søk etter "Flexit vent with Servo og CPU fan")
Servoen Luxorparts S3003 Standardservo
Viften er en Noctua NF-S12A PWM
Dioden er en RGB Common Cathode 4-Pin F5 5MM
Temp sensoren er en DS18B20
Spenningsregulator 5V fra 12V
Pull-up motstand til Temp sensoren: 4,7k ohm
Motstand til LED: 3 x 220 ohm
Flexit ventil 6x6 (150x150mm) art 02024 feks herfra
- 10
- 2
41 kommentarer
Anbefalte kommentarer