/* AllSkyHtr433.ino 3/03/2024 Control Allsky heater with Wx data via 433 MHz * 433 MHz Wx reception code based from : Ray Wang (Rayshobby LLC) * Uses Adafruit Feather M0 WiFi, RXB6/RXB12 433 MHz receiver, MOSFET heater driver * Reads Wx data from an Acurite 592TXR Temperature/Humidity "Tower" sensor * Default PWM output of Feather M0 WiFi is at 733 Hz * Creates a web server to allow retreval of data in csv format */ #include #include #include "secrets.h" // contains WiFi network connection info // 433 reception defines #define RING_BUFFER_SIZE 256 // large enough to fit data between two successive syncs #define SYNC_LENGTH 2200 #define SYNC_HIGH 600 #define SYNC_LOW 600 #define BIT1_HIGH 400 #define BIT1_LOW 220 #define BIT0_HIGH 220 #define BIT0_LOW 400 // heater function defines #define SENSOR_ID 43 // ID of Acurite Wx sensor to monitor #define DATAPIN 5 // Connection to output of 433 MHz receiver #define PWM_PIN 10 // PWM output to heater circuit #define HTR_ON_THRES 70 // turn heater on >= humidity level #define HTR_MAX_THRES 95 // heater at 100% power >= humidity level // 433 reception global variables unsigned long timings[RING_BUFFER_SIZE]; unsigned int syncIndex1 = 0; // index of the first sync signal unsigned int syncIndex2 = 0; // index of the second sync signal bool received = false; // WiFi connection global variables char ssid[] = SECRET_SSID; // WiFi network SSID char pass[] = SECRET_PASS; // WiFi network password //IPAddress ip(HOST_IP); // static IP address for WiFi board IPAddress ip(192,168,1,215); // static IP address for WiFi board int wifiStatus = WL_IDLE_STATUS; bool wifiPresent = false; WiFiServer server(80); // Wx global variables int tempC = 0; int tempF = 0; int humidity = 0; int dewPoint = 0; // variables from selected sensor int tC = 0; int tF = 0; int hum = 0; int dP = 0; // temporary variables from 433 receiver int pwmPercent = 0; // pwm value to drive heater power with float pwmScale = 100 / (HTR_MAX_THRES - HTR_ON_THRES); // adjust for threshold changes //***** 433 MHz reception routines ***** // detect if a sync signal is present bool isSync(unsigned int idx) { // check if we've received 4 squarewaves of matching timing int i; for(i=0;i<8;i+=2) { unsigned long t1 = timings[(idx+RING_BUFFER_SIZE-i) % RING_BUFFER_SIZE]; unsigned long t0 = timings[(idx+RING_BUFFER_SIZE-i-1) % RING_BUFFER_SIZE]; if(t0<(SYNC_HIGH-100) || t0>(SYNC_HIGH+100) || t1<(SYNC_LOW-100) || t1>(SYNC_LOW+100)) { return false; } } // check if there is a long sync period prior to the 4 squarewaves unsigned long t = timings[(idx+RING_BUFFER_SIZE-i)%RING_BUFFER_SIZE]; if(t<(SYNC_LENGTH-400) || t>(SYNC_LENGTH+400) || digitalRead(DATAPIN) != HIGH) { return false; } return true; } /* Interrupt 1 handler */ void handler() { static unsigned long duration = 0; static unsigned long lastTime = 0; static unsigned int ringIndex = 0; static unsigned int syncCount = 0; // ignore if we haven't processed the previous received signal if (received == true) { return; } // calculating timing since last change long time = micros(); duration = time - lastTime; lastTime = time; // store data in ring buffer ringIndex = (ringIndex + 1) % RING_BUFFER_SIZE; timings[ringIndex] = duration; // detect sync signal if (isSync(ringIndex)) { syncCount ++; // first time sync is seen, record buffer index if (syncCount == 1) { syncIndex1 = (ringIndex+1) % RING_BUFFER_SIZE; } else if (syncCount == 2) { // second time sync is seen, start bit conversion syncCount = 0; syncIndex2 = (ringIndex+1) % RING_BUFFER_SIZE; unsigned int changeCount = (syncIndex2 < syncIndex1) ? (syncIndex2+RING_BUFFER_SIZE - syncIndex1) : (syncIndex2 - syncIndex1); // changeCount must be 122 -- 60 bits x 2 + 2 for sync if (changeCount != 122) { received = false; syncIndex1 = 0; syncIndex2 = 0; } else { received = true; } } } } //*************************************** void setup() { // Configure the Feather ATWINC1500 connections WiFi.setPins(8,7,4,2); Serial.begin(9600); delay(1000); Serial.println("AllSkyHtr433 Started."); pinMode(DATAPIN, INPUT); // put the 433 MHz data pin in input mode attachInterrupt(digitalPinToInterrupt(DATAPIN), handler, CHANGE); pinMode(PWM_PIN, OUTPUT); // put the PWM pin in output mode pinMode(13, OUTPUT); // put the red LED pin in output mode digitalWrite(13, LOW); // turn the red LED off // check for presence of onboard WiFi system: if (WiFi.status() == WL_NO_SHIELD) { wifiPresent = false; Serial.println(">> Onboard WiFi system not detected <<"); } else { // WiFi system is present to try to connect to it wifiPresent = true; WiFi.config(ip); // use this static IP address instead of DNS while (wifiStatus != WL_CONNECTED) { // this should abort if tried too many times Serial.print("Attempting to connect to "); Serial.println(ssid); wifiStatus = WiFi.begin(ssid, pass); delay(5000); // wait 5 seconds to allow for connection: } server.begin(); printWiFiStatus(); // print out wifi status after connection } } int t2b(unsigned int t0, unsigned int t1) { if (t0>(BIT1_HIGH-100) && t0<(BIT1_HIGH+100) && t1>(BIT1_LOW-100) && t1<(BIT1_LOW+100)) { return 1; } else if (t0>(BIT0_HIGH-100) && t0<(BIT0_HIGH+100) && t1>(BIT0_LOW-100) && t1<(BIT0_LOW+100)){ return 0; } return -1; // undefined } void loop() { WiFiClient client = server.available(); // listen for a web client to connect if (client) { // service the web client connection Serial.println("Web client connected"); // an http request ends with a blank line bool currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); Serial.write(c); if (c == '\n' && currentLineIsBlank) { // send a standard http response header client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); // the connection will be closed after completion of the response //client.println("Refresh: 5"); // refresh the page automatically every 5 sec client.println(); client.println(""); client.println(""); // output the web page content in csv format client.print("TempC,TempF,DewPoint,Humidity,HtrPower"); client.println("
"); client.print(tempC); client.print(","); client.print(tempF); client.print(","); client.print(dewPoint); client.print(","); client.print(humidity); client.print(","); client.print(pwmPercent); client.println("
"); client.println(""); break; } } } delay(1); // give the web browser time to receive the data client.stop(); // close the connection Serial.println("client disconnected"); } // end of web client processing if (received == true) { // process the received 433 data // disable interrupt to avoid new data corrupting the buffer detachInterrupt(digitalPinToInterrupt(DATAPIN)); unsigned int startIndex, stopIndex; // extract sensorID value unsigned int sensorID = 0; bool fail = false; bool decodeFail = false; startIndex = (syncIndex1 + (1*8+1)*2) % RING_BUFFER_SIZE; stopIndex = (syncIndex1 + (1*8+8)*2) % RING_BUFFER_SIZE; for(int i=startIndex; i!=stopIndex; i=(i+2)%RING_BUFFER_SIZE) { int bit = t2b(timings[i], timings[(i+1)%RING_BUFFER_SIZE]); sensorID = (sensorID<<1) + bit; if (bit < 0) fail = true; // fail for this value } if (fail) { Serial.print("sensorID decoding error: "); Serial.println(sensorID); // print the erroneous value decodeFail = true; // fail all values for this loop } else { Serial.print("\nSensorID: "); Serial.println(sensorID); } // extract humidity value hum = 0; fail = false; startIndex = (syncIndex1 + (3*8+1)*2) % RING_BUFFER_SIZE; stopIndex = (syncIndex1 + (3*8+8)*2) % RING_BUFFER_SIZE; for(int i=startIndex; i!=stopIndex; i=(i+2)%RING_BUFFER_SIZE) { int bit = t2b(timings[i], timings[(i+1)%RING_BUFFER_SIZE]); hum = (hum<<1) + bit; if (bit < 0) fail = true; // fail for this value } if (fail) { Serial.print("hum decoding error: "); Serial.println(hum); // print the erroneous value decodeFail = true; // fail all values for this loop } else { if (hum < 0) hum = 0; // ensure minimum limit if (hum > 100) hum = 100; // ensure maximum limit Serial.print(hum); Serial.print(","); } // extract temperature from two bytes //unsigned long temp = 0; int temp = 0; fail = false; // most significant 4 bits startIndex = (syncIndex1 + (4*8+4)*2) % RING_BUFFER_SIZE; stopIndex = (syncIndex1 + (4*8+8)*2) % RING_BUFFER_SIZE; for(int i=startIndex; i!=stopIndex; i=(i+2)%RING_BUFFER_SIZE) { int bit = t2b(timings[i], timings[(i+1)%RING_BUFFER_SIZE]); temp = (temp<<1) + bit; if (bit < 0) fail = true; // fail for this value } // least significant 7 bits startIndex = (syncIndex1 + (5*8+1)*2) % RING_BUFFER_SIZE; stopIndex = (syncIndex1 + (5*8+8)*2) % RING_BUFFER_SIZE; for(int i=startIndex; i!=stopIndex; i=(i+2)%RING_BUFFER_SIZE) { int bit = t2b(timings[i], timings[(i+1)%RING_BUFFER_SIZE]); temp = (temp<<1) + bit; if (bit < 0) fail = true; // fail for this value } if (fail) { Serial.print("temp decoding error: "); Serial.println(temp); // print the erroneous value decodeFail = true; // fail all values for this loop } else { tC = int((temp-1024)/10+1.9+0.5); // round to the nearest integer tF = int(tC*9/5+32+0.5); // round to the nearest integer if (!decodeFail) dP = int((tC-(100-hum)/5)*9/5+32+0.5); // simplistic approximation calculation Serial.print(tC); Serial.print(","); Serial.print(tF); Serial.print(","); Serial.println(dP); } // delay for 1 second to avoid repetitions delay(1000); received = false; syncIndex1 = 0; syncIndex2 = 0; // Set Wx values for web page and output PWM signal to Heater if (sensorID == SENSOR_ID && !decodeFail) { // only use selected sensor tempC = tC; tempF = tF; dewPoint = dP; humidity = hum; // save values from selected sensor pwmPercent = int((humidity - HTR_ON_THRES) * pwmScale); // using measured humidity //pwmPercent = int(random(0,100)); // using random humidity for testing if (pwmPercent < 0) pwmPercent = 0; // ensure lower limit if (pwmPercent > HTR_MAX_THRES) pwmPercent = 100; // ensure upper limit Serial.print("Set PWM to "); Serial.print(pwmPercent); Serial.print(" on pin "); Serial.println(PWM_PIN); analogWrite(PWM_PIN, int(pwmPercent*2.55)); // scale to 0-255 range needed by analogWrite } blink13(25); // blink the red LED when 433 MHz data is received // re-enable interrupt attachInterrupt(digitalPinToInterrupt(DATAPIN), handler, CHANGE); } } void printWiFiStatus() { // print the SSID of the network the board is attached to: Serial.print("Connected to SSID: "); Serial.println(WiFi.SSID()); // print the onboard WiFi system IP address: IPAddress ip = WiFi.localIP(); Serial.print("Board's 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"); } void blink13(int mS){ digitalWrite(13, HIGH); // turn LED on delay(mS); // wait time in mSec digitalWrite(13, LOW); // turn LED off }