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
|
#include <WiFi.h>
#include <HTTPClient.h>
#include <PID_v1.h>
enum {
SECOND = 1000,
PERIOD = 30*SECOND,
};
enum pins { SOLENOID_PIN = 21 };
enum tunings {
P = 2,
I = 5,
D = 1,
};
enum { SOLENOID_WINDOW = 5000 };
enum { TARGET_HUMIDITY = 35 }; // TODO: retrieve from server.
const char ssid[] = "Pixel_6504";
const char password[] = "zj3av9sjev7ed8j";
const char humidityUrl[] = "http://hvac.samanthony.xyz/humidity";
double pidInput, pidOutput, pidSetpoint;
PID pid(&pidInput, &pidOutput, &pidSetpoint, P, I, D, DIRECT);
void
setup(void) {
pinMode(SOLENOID_PIN, OUTPUT);
Serial.begin(9600);
while (!Serial) {}
pidSetpoint = TARGET_HUMIDITY;
pid.SetOutputLimits(0, SOLENOID_WINDOW);
pid.SetMode(AUTOMATIC);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println(" connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void
loop(void) {
static unsigned long lastUpdate = 0; // Last time humidity was retrived from server.
static float humidity = TARGET_HUMIDITY;
unsigned long now = millis();
if (now - lastUpdate > PERIOD) {
if (getHumidity(&humidity) == 0)
lastUpdate = now;
else
Serial.println("Failed to get humidity from server.");
}
pidInput = humidity;
pid.Compute();
writeSolenoidPin(pidOutput);
}
// Retrieve the measured humidity of the building from the server.
// Returns non-zero on error.
int
getHumidity(float *humidity) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi not connected.");
return 1;
}
// Send request to server.
HTTPClient http;
Serial.printf("GET %s...\n", humidityUrl);
http.begin(humidityUrl);
int responseCode = http.GET();
Serial.printf("HTTP response code: %d\n", responseCode);
if (responseCode != HTTP_CODE_OK)
return 1;
const char *response = http.getString().c_str();
Serial.printf("HTTP response: '%s'\n", response);
int status = parseHumidity(response, humidity);
http.end(); // Cannot be freed before parseHumidity() because response is stored in the http buffer.
return status;
}
// Returns non-zero on error.
int
parseHumidity(const char *str, float *humidity) {
if (sscanf(str, "%f", humidity) != 1) {
Serial.printf("Failed to parse humidity: '%s'\n", str);
return 1;
}
return 0;
}
void
writeSolenoidPin(double pidOutput) {
static unsigned long windowStartTime = 0;
unsigned long now = millis();
if (now - windowStartTime > SOLENOID_WINDOW) {
// Start new window.
windowStartTime = now;
Serial.printf("Duty cycle: %.2f\n", pidOutput/SOLENOID_WINDOW);
}
if (pidOutput > now - windowStartTime)
digitalWrite(SOLENOID_PIN, HIGH);
else
digitalWrite(SOLENOID_PIN, LOW);
}
|