11
11
12
12
WiFiServer server (80 );
13
13
14
+ char MAC_ADDRESS[32 ];
15
+
16
+ struct Color {
17
+ Color () = default ;
18
+ Color (float r, float g, float b, float w) : r(r), g(g), b(b), w(w) {}
19
+
20
+ bool operator ==(const Color& other) const {
21
+ return r == other.r && g == other.g && b == other.b && w == other.w ;
22
+ }
23
+ bool operator !=(const Color& other) const { return !operator ==(other); }
24
+
25
+ Color operator +(const Color& other) const {
26
+ return Color (r + other.r , g + other.g , b + other.b , w + other.w );
27
+ }
28
+ Color operator -(const Color& other) const {
29
+ return Color (r - other.r , g - other.g , b - other.b , w - other.w );
30
+ }
31
+ Color operator *(const Color& other) const {
32
+ return Color (r * other.r , g * other.g , b * other.b , w * other.w );
33
+ }
34
+ Color operator /(const Color& other) const {
35
+ return Color (r / other.r , g / other.g , b / other.b , w / other.w );
36
+ }
37
+
38
+ Color operator +(float val) const {
39
+ return Color (r + val, g + val, b + val, w + val);
40
+ }
41
+ Color operator -(float val) const {
42
+ return Color (r - val, g - val, b - val, w - val);
43
+ }
44
+ Color operator *(float val) const {
45
+ return Color (r * val, g * val, b * val, w * val);
46
+ }
47
+ Color operator /(float val) const {
48
+ return Color (r / val, g / val, b / val, w / val);
49
+ }
50
+
51
+ float r = 0 , g = 0 , b = 0 , w = 0 ;
52
+
53
+ void apply () {
54
+ auto convert = [=](float val) {
55
+ return constrain (round (val * 4095 .f ), 0 , 4095 );
56
+ };
57
+ analogWrite (RED, convert (r));
58
+ analogWrite (GREEN, convert (g));
59
+ analogWrite (BLUE, convert (b));
60
+ analogWrite (WHITE, convert (w));
61
+ }
62
+ };
63
+
64
+ struct State {
65
+ bool isOn = false ;
66
+ Color target;
67
+ Color current;
68
+ unsigned long transitionTime = 0 ;
69
+ } state;
70
+
14
71
void setup () {
15
72
// check for the WiFi module:
16
73
if (WiFi.status () == WL_NO_MODULE) {
@@ -43,87 +100,121 @@ void setup() {
43
100
Serial.print (" IP Address: " );
44
101
Serial.println (ip);
45
102
103
+ byte mac[6 ];
104
+ WiFi.macAddress (mac);
105
+ sprintf (MAC_ADDRESS, " %02X:%02X:%02X:%02X:%02X:%02X" , mac[5 ], mac[4 ], mac[3 ], mac[2 ], mac[1 ], mac[0 ]);
106
+ Serial.print (" MAC: " );
107
+ Serial.println (MAC_ADDRESS);
108
+
46
109
server.begin ();
47
110
48
111
pinMode (LED_BUILTIN, OUTPUT);
49
112
digitalWrite (LED_BUILTIN, HIGH);
50
- }
51
-
52
- struct Color {
53
- uint8_t r = 0 , g = 0 , b = 0 , w = 0 ;
54
113
55
- void apply () {
56
- analogWrite (RED, r);
57
- analogWrite (GREEN, g);
58
- analogWrite (BLUE, b);
59
- analogWrite (WHITE, w);
60
- }
61
- };
62
-
63
- Color current;
114
+ analogWriteResolution (12 );
115
+ }
64
116
65
117
enum class HttpMethod {
66
118
GET,
67
119
POST,
68
120
UNKNOWN
69
121
};
70
122
123
+ const unsigned int LOOP_TIME = 10 ; // ms
124
+
125
+ const Color off (0 , 0 , 0 , 0 );
126
+
71
127
void loop () {
128
+ if (WiFi.status () != WL_CONNECTED) {
129
+ Serial.println (" WiFi lost, reseting arduino in 10 seconds" );
130
+ delay (10000 );
131
+ NVIC_SystemReset ();
132
+ }
133
+
72
134
WiFiClient client = server.available ();
73
135
74
- if (client) {
75
- Serial.println (" Client available" );
136
+ static unsigned int prev = 0 ;
137
+ const unsigned long time = millis ();
138
+ const unsigned int diff = time - prev;
76
139
140
+ if (client) {
77
141
size_t lineNo = 0 ;
78
142
HttpMethod method = HttpMethod::UNKNOWN;
143
+ char route[20 ];
79
144
80
145
// Parse Headers
81
146
while (client.available ()) {
82
147
String line = client.readStringUntil (' \r ' );
83
148
client.read (); // Discard \n
84
- Serial.println (line);
85
149
86
150
if (lineNo == 0 ) {
87
151
if (line.startsWith (" GET" )) {
88
152
method = HttpMethod::GET;
89
153
} else if (line.startsWith (" POST" )) {
90
154
method = HttpMethod::POST;
91
155
}
156
+
157
+ const char * routeBegin = strchr (line.c_str (), ' ' ) + 1 ;
158
+ size_t i = 0 ;
159
+ while (i < sizeof (route) && routeBegin[i] != ' \0 ' && routeBegin[i] != ' ' ) {
160
+ route[i] = routeBegin[i];
161
+ i++;
162
+ }
163
+ route[i] = ' \0 ' ;
92
164
}
93
165
94
166
if (line.length () == 0 ) {
95
167
// End of headers
96
168
break ;
97
169
}
170
+
171
+ lineNo++;
98
172
}
99
173
100
- if (method == HttpMethod::GET) {
101
- DynamicJsonDocument doc (1024 );
174
+ if (method == HttpMethod::POST && strcmp (route, " /command" ) == 0 ) {
175
+ StaticJsonDocument<1024 > req;
176
+ StaticJsonDocument<1024 > res;
177
+ deserializeJson (req, client);
102
178
103
- doc[" r" ] = current.r ;
104
- doc[" g" ] = current.g ;
105
- doc[" b" ] = current.b ;
106
- doc[" w" ] = current.w ;
179
+ const char * method = req[" method" ];
180
+ auto param = req[" param" ];
107
181
108
- client.println (" HTTP/1.1 200 OK" );
109
- client.println (" Content-Type: application/json" );
110
- client.println (" Connection: close" );
111
- client.println ();
112
- serializeJson (doc, client);
113
- } else if (method == HttpMethod::POST) {
114
- DynamicJsonDocument doc (1024 );
115
- deserializeJson (doc, client);
116
-
117
- current.r = doc[" r" ];
118
- current.g = doc[" g" ];
119
- current.b = doc[" b" ];
120
- current.w = doc[" w" ];
182
+ if (strcmp (method, " set_rgbw" ) == 0 ) {
183
+ if (param[" time" ]) {
184
+ state.transitionTime = param[" time" ].as <unsigned long >();
185
+ } else {
186
+ state.transitionTime = 1000 ;
187
+ }
121
188
122
- current.apply ();
189
+ state.target .r = constrain (param[" r" ], 0 .f , 1 .f );
190
+ state.target .g = constrain (param[" g" ], 0 .f , 1 .f );
191
+ state.target .b = constrain (param[" b" ], 0 .f , 1 .f );
192
+ state.target .w = constrain (param[" w" ], 0 .f , 1 .f );
193
+ } else if (strcmp (method, " set_power" ) == 0 ) {
194
+ state.isOn = param[" value" ];
195
+ if (!state.isOn || state.transitionTime < 1000 ) {
196
+ state.transitionTime = 1000 ;
197
+ }
198
+ } else if (strcmp (method, " get_status" ) == 0 ) {
199
+ res[" power" ] = state.isOn ;
200
+ res[" r" ] = (float )state.target .r ;
201
+ res[" g" ] = (float )state.target .g ;
202
+ res[" b" ] = (float )state.target .b ;
203
+ res[" w" ] = (float )state.target .w ;
204
+ } else if (strcmp (method, " get_info" ) == 0 ) {
205
+ res[" mac" ] = MAC_ADDRESS;
206
+ } else {
207
+ client.println (" HTTP/1.1 404 Not Found" );
208
+ client.println (" Connection: close" );
209
+ client.println ();
210
+ }
123
211
124
212
client.println (" HTTP/1.1 200 OK" );
125
213
client.println (" Connection: close" );
126
214
client.println ();
215
+ if (!res.isNull ()) {
216
+ serializeJson (res, client);
217
+ }
127
218
} else {
128
219
client.println (" HTTP/1.1 404 Not Found" );
129
220
client.println (" Connection: close" );
@@ -132,4 +223,23 @@ void loop() {
132
223
133
224
client.stop ();
134
225
}
226
+
227
+ const Color target = state.isOn ? state.target : off;
228
+
229
+ if (state.transitionTime > 0 ) {
230
+ float ratio = (float )LOOP_TIME / (float )state.transitionTime ;
231
+ ratio = min (max (ratio, 0 .f ), 1 .0f );
232
+ state.current = state.current * (1 .f - ratio) + target * ratio;
233
+ state.current .apply ();
234
+
235
+ if (state.transitionTime > diff) {
236
+ state.transitionTime -= diff;
237
+ } else {
238
+ state.transitionTime = 0 ;
239
+ }
240
+ }
241
+
242
+ prev = time;
243
+
244
+ delay (LOOP_TIME);
135
245
}
0 commit comments