Skip to content

Commit 372c0ad

Browse files
committed
(2.4.6) Reduce memory usage by 1.6kB
Lower heap fragmentation
1 parent 2166a0f commit 372c0ad

File tree

4 files changed

+75
-73
lines changed

4 files changed

+75
-73
lines changed

library.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=Espalexa
2-
version=2.4.5
2+
version=2.4.6
33
author=Christian Schwinne
44
maintainer=Christian Schwinne
55
sentence=Library to control an ESP module with the Alexa voice assistant

readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Here is an overview of the devices (light types) Espalexa can emulate:
9696
| EspalexaDeviceType::whitespectrum | Color temperature adjustment not working on Dot |
9797
| EspalexaDeviceType::color | Works as intended (dimming + color) |
9898
| EspalexaDeviceType::extendedcolor | Color temperature adjustment not working on Dot |
99-
| EspalexaDeviceType::onoff (experimental) | Works, but is treated as dimmable by Alexa |
99+
| EspalexaDeviceType::onoff (experimental) | Deprecated. Treated as dimmable. |
100100
101101
See the example `EspalexaFullyFeatured` to learn how to define each device type and use the new EspalexaDevice pointer callback function type!
102102

src/Espalexa.h

+72-71
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111
/*
1212
* @title Espalexa library
13-
* @version 2.4.5
13+
* @version 2.4.6
1414
* @author Christian Schwinne
1515
* @license MIT
1616
* @contributors d-999
@@ -49,7 +49,7 @@
4949
#include <WiFiUdp.h>
5050

5151
#ifdef ESPALEXA_DEBUG
52-
#pragma message "Espalexa 2.4.5 debug mode"
52+
#pragma message "Espalexa 2.4.6 debug mode"
5353
#define EA_DEBUG(x) Serial.print (x)
5454
#define EA_DEBUGLN(x) Serial.println (x)
5555
#else
@@ -81,35 +81,29 @@ class Espalexa {
8181
WiFiUDP espalexaUdp;
8282
IPAddress ipMulti;
8383
bool udpConnected = false;
84-
char packetBuffer[255]; //buffer to hold incoming udp packet
8584
String escapedMac=""; //lowercase mac address
8685

8786
//private member functions
88-
String boolString(bool st)
89-
{
90-
return(st)?"true":"false";
91-
}
92-
93-
String modeString(EspalexaColorMode m)
87+
const char* modeString(EspalexaColorMode m)
9488
{
9589
if (m == EspalexaColorMode::xy) return "xy";
9690
if (m == EspalexaColorMode::hs) return "hs";
9791
return "ct";
9892
}
9993

100-
String typeString(EspalexaDeviceType t)
94+
const char* typeString(EspalexaDeviceType t)
10195
{
10296
switch (t)
10397
{
104-
case EspalexaDeviceType::dimmable: return "Dimmable light";
105-
case EspalexaDeviceType::whitespectrum: return "Color temperature light";
106-
case EspalexaDeviceType::color: return "Color light";
107-
case EspalexaDeviceType::extendedcolor: return "Extended color light";
98+
case EspalexaDeviceType::dimmable: return PSTR("Dimmable light");
99+
case EspalexaDeviceType::whitespectrum: return PSTR("Color temperature light");
100+
case EspalexaDeviceType::color: return PSTR("Color light");
101+
case EspalexaDeviceType::extendedcolor: return PSTR("Extended color light");
108102
}
109-
return "Light";
103+
return "";
110104
}
111105

112-
String modelidString(EspalexaDeviceType t)
106+
const char* modelidString(EspalexaDeviceType t)
113107
{
114108
switch (t)
115109
{
@@ -118,7 +112,7 @@ class Espalexa {
118112
case EspalexaDeviceType::color: return "LST001";
119113
case EspalexaDeviceType::extendedcolor: return "LCT015";
120114
}
121-
return "Plug";
115+
return "";
122116
}
123117

124118
//Workaround functions courtesy of Sonoff-Tasmota
@@ -135,38 +129,38 @@ class Espalexa {
135129
}
136130

137131
//device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001)
138-
String deviceJsonString(uint8_t deviceId)
132+
void deviceJsonString(uint8_t deviceId, char* buf)
139133
{
140134
deviceId--;
141-
if (deviceId >= currentDeviceCount) return "{}"; //error
135+
if (deviceId >= currentDeviceCount) {strcpy(buf,"{}"); return;} //error
142136
EspalexaDevice* dev = devices[deviceId];
143-
144-
String json = "{\"state\":{\"on\":";
145-
json += boolString(dev->getValue());
146-
if (dev->getType() != EspalexaDeviceType::onoff) //bri support
147-
{
148-
json += ",\"bri\":" + String(dev->getLastValue()-1);
149-
if (static_cast<uint8_t>(dev->getType()) > 2) //color support
150-
{
151-
json += ",\"hue\":" + String(dev->getHue()) + ",\"sat\":" + String(dev->getSat());
152-
json += ",\"effect\":\"none\",\"xy\":[" + String(dev->getX()) + "," + String(dev->getY()) + "]";
153-
}
154-
if (static_cast<uint8_t>(dev->getType()) > 1 && dev->getType() != EspalexaDeviceType::color) //white spectrum support
155-
{
156-
json += ",\"ct\":" + String(dev->getCt());
157-
}
158-
}
159-
json += ",\"alert\":\"none";
160-
if (static_cast<uint8_t>(dev->getType()) > 1) json += "\",\"colormode\":\"" + modeString(dev->getColorMode());
161-
json += "\",\"mode\":\"homeautomation\",\"reachable\":true},";
162-
json += "\"type\":\"" + typeString(dev->getType());
163-
json += "\",\"name\":\"" + dev->getName();
164-
json += "\",\"modelid\":\"" + modelidString(dev->getType());
165-
json += "\",\"manufacturername\":\"Philips\",\"productname\":\"E" + String(static_cast<uint8_t>(dev->getType()));
166-
json += "\",\"uniqueid\":\"" + String(encodeLightId(deviceId+1));
167-
json += "\",\"swversion\":\"espalexa-2.4.5\"}";
168137

169-
return json;
138+
//char buf_bri[12] = "";
139+
//brightness support, add "bri" to JSON
140+
//if (dev->getType() != EspalexaDeviceType::onoff)
141+
// sprintf(buf_bri,",\"bri\":%u", dev->getLastValue()-1);
142+
143+
char buf_col[80] = "";
144+
//color support
145+
if (static_cast<uint8_t>(dev->getType()) > 2)
146+
sprintf_P(buf_col,PSTR(",\"hue\":%u,\"sat\":%u,\"effect\":\"none\",\"xy\":[%f,%f]")
147+
,dev->getHue(), dev->getSat(), dev->getX(), dev->getY());
148+
149+
char buf_ct[16] = "";
150+
//white spectrum support
151+
if (static_cast<uint8_t>(dev->getType()) > 1 && dev->getType() != EspalexaDeviceType::color)
152+
sprintf(buf_ct, ",\"ct\":%u", dev->getCt());
153+
154+
char buf_cm[20] = "";
155+
if (static_cast<uint8_t>(dev->getType()) > 1)
156+
sprintf(buf_cm,PSTR("\",\"colormode\":\"%s"), modeString(dev->getColorMode()));
157+
158+
sprintf_P(buf, PSTR("{\"state\":{\"on\":%s,\"bri\":%u%s%s,\"alert\":\"none%s\",\"mode\":\"homeautomation\",\"reachable\":true},"
159+
"\"type\":\"%s\",\"name\":\"%s\",\"modelid\":\"%s\",\"manufacturername\":\"Philips\",\"productname\":\"E%u"
160+
"\",\"uniqueid\":\"%u\",\"swversion\":\"espalexa-2.4.6\"}")
161+
162+
, (dev->getValue())?"true":"false", dev->getLastValue()-1, buf_col, buf_ct, buf_cm, typeString(dev->getType()),
163+
dev->getName().c_str(), modelidString(dev->getType()), static_cast<uint8_t>(dev->getType()), encodeLightId(deviceId+1));
170164
}
171165

172166
//Espalexa status page /espalexa
@@ -181,14 +175,14 @@ class Espalexa {
181175
res += "Value of device " + String(i+1) + " (" + dev->getName() + "): " + String(dev->getValue()) + " (" + typeString(dev->getType());
182176
if (static_cast<uint8_t>(dev->getType()) > 1) //color support
183177
{
184-
res += ", colormode=" + modeString(dev->getColorMode()) + ", r=" + String(dev->getR()) + ", g=" + String(dev->getG()) + ", b=" + String(dev->getB());
178+
res += ", colormode=" + String(modeString(dev->getColorMode())) + ", r=" + String(dev->getR()) + ", g=" + String(dev->getG()) + ", b=" + String(dev->getB());
185179
res +=", ct=" + String(dev->getCt()) + ", hue=" + String(dev->getHue()) + ", sat=" + String(dev->getSat()) + ", x=" + String(dev->getX()) + ", y=" + String(dev->getY());
186180
}
187181
res += ")\r\n";
188182
}
189183
res += "\r\nFree Heap: " + (String)ESP.getFreeHeap();
190184
res += "\r\nUptime: " + (String)millis();
191-
res += "\r\n\r\nEspalexa library v2.4.5 by Christian Schwinne 2020";
185+
res += "\r\n\r\nEspalexa library v2.4.6 by Christian Schwinne 2020";
192186
server->send(200, "text/plain", res);
193187
}
194188
#endif
@@ -206,7 +200,7 @@ class Espalexa {
206200
EA_DEBUGLN("Body: " + body);
207201
if(!handleAlexaApiCall(server))
208202
#endif
209-
server->send(404, "text/plain", "Not Found (espalexa-internal)");
203+
server->send(404, "text/plain", "Not Found (espalexa)");
210204
}
211205

212206
//send description.xml device property page
@@ -216,30 +210,31 @@ class Espalexa {
216210
IPAddress localIP = WiFi.localIP();
217211
char s[16];
218212
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
219-
220-
String setup_xml = "<?xml version=\"1.0\" ?>"
213+
char buf[1024];
214+
215+
sprintf_P(buf,PSTR("<?xml version=\"1.0\" ?>"
221216
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
222217
"<specVersion><major>1</major><minor>0</minor></specVersion>"
223-
"<URLBase>http://"+ String(s) +":80/</URLBase>"
218+
"<URLBase>http://%s:80/</URLBase>"
224219
"<device>"
225220
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
226-
"<friendlyName>Espalexa ("+ String(s) +")</friendlyName>"
221+
"<friendlyName>Espalexa (%s)</friendlyName>"
227222
"<manufacturer>Royal Philips Electronics</manufacturer>"
228223
"<manufacturerURL>http://www.philips.com</manufacturerURL>"
229224
"<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>"
230225
"<modelName>Philips hue bridge 2012</modelName>"
231226
"<modelNumber>929000226503</modelNumber>"
232227
"<modelURL>http://www.meethue.com</modelURL>"
233-
"<serialNumber>"+ escapedMac +"</serialNumber>"
234-
"<UDN>uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"</UDN>"
228+
"<serialNumber>%s</serialNumber>"
229+
"<UDN>uuid:2f402f80-da50-11e1-9b23-%s</UDN>"
235230
"<presentationURL>index.html</presentationURL>"
236231
"</device>"
237-
"</root>";
232+
"</root>"),s,s,escapedMac.c_str(),escapedMac.c_str());
238233

239-
server->send(200, "text/xml", setup_xml.c_str());
234+
server->send(200, "text/xml", buf);
240235

241-
EA_DEBUG("Sending :");
242-
EA_DEBUGLN(setup_xml);
236+
EA_DEBUG("Send setup.xml");
237+
//EA_DEBUGLN(setup_xml);
243238
}
244239

245240
//init the server
@@ -290,22 +285,23 @@ class Espalexa {
290285
char s[16];
291286
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
292287

293-
String response =
294-
"HTTP/1.1 200 OK\r\n"
288+
char buf[1024];
289+
290+
sprintf_P(buf,PSTR("HTTP/1.1 200 OK\r\n"
295291
"EXT:\r\n"
296292
"CACHE-CONTROL: max-age=100\r\n" // SSDP_INTERVAL
297-
"LOCATION: http://"+ String(s) +":80/description.xml\r\n"
293+
"LOCATION: http://%s:80/description.xml\r\n"
298294
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber
299-
"hue-bridgeid: "+ escapedMac +"\r\n"
295+
"hue-bridgeid: %s\r\n"
300296
"ST: urn:schemas-upnp-org:device:basic:1\r\n" // _deviceType
301-
"USN: uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"::ssdp:all\r\n" // _uuid::_deviceType
302-
"\r\n";
297+
"USN: uuid:2f402f80-da50-11e1-9b23-%s::ssdp:all\r\n" // _uuid::_deviceType
298+
"\r\n"),s,escapedMac.c_str(),escapedMac.c_str());
303299

304300
espalexaUdp.beginPacket(espalexaUdp.remoteIP(), espalexaUdp.remotePort());
305301
#ifdef ARDUINO_ARCH_ESP32
306-
espalexaUdp.write((uint8_t*)response.c_str(), response.length());
302+
espalexaUdp.write((uint8_t*)buf, response.length());
307303
#else
308-
espalexaUdp.write(response.c_str());
304+
espalexaUdp.write(buf);
309305
#endif
310306
espalexaUdp.endPacket();
311307
}
@@ -362,7 +358,8 @@ class Espalexa {
362358
if (!packetSize) return; //no new udp packet
363359

364360
EA_DEBUGLN("Got UDP!");
365-
int len = espalexaUdp.read(packetBuffer, 254);
361+
char packetBuffer[255]; //buffer to hold incoming udp packet
362+
uint16_t len = espalexaUdp.read(packetBuffer, 254);
366363
if (len > 0) {
367364
packetBuffer[len] = 0;
368365
}
@@ -447,13 +444,13 @@ class Espalexa {
447444
{
448445
EA_DEBUGLN("devType");
449446
body = "";
450-
server->send(200, "application/json", "[{\"success\":{\"username\":\"2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr\"}}]");
447+
server->send(200, "application/json", F("[{\"success\":{\"username\":\"2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr\"}}]"));
451448
return true;
452449
}
453450

454451
if (req.indexOf("state") > 0) //client wants to control light
455452
{
456-
server->send(200, "application/json", "[{\"success\":{\"/lights/1/state/\": true}}]");
453+
server->send(200, "application/json", F("[{\"success\":{\"/lights/1/state/\": true}}]"));
457454

458455
uint32_t devId = req.substring(req.indexOf("lights")+7).toInt();
459456
EA_DEBUG("ls"); EA_DEBUGLN(devId);
@@ -530,7 +527,9 @@ class Espalexa {
530527
for (int i = 0; i<currentDeviceCount; i++)
531528
{
532529
jsonTemp += "\"" + String(encodeLightId(i+1)) + "\":";
533-
jsonTemp += deviceJsonString(i+1);
530+
char buf[512];
531+
deviceJsonString(i+1, buf);
532+
jsonTemp += buf;
534533
if (i < currentDeviceCount-1) jsonTemp += ",";
535534
}
536535
jsonTemp += "}";
@@ -543,7 +542,9 @@ class Espalexa {
543542
{
544543
server->send(200, "application/json", "{}");
545544
} else {
546-
server->send(200, "application/json", deviceJsonString(devId));
545+
char buf[512];
546+
deviceJsonString(devId, buf);
547+
server->send(200, "application/json", buf);
547548
}
548549
}
549550

src/EspalexaDevice.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ EspalexaDevice::EspalexaDevice(String deviceName, DeviceCallbackFunction gnCallb
2828
_callbackDev = gnCallback;
2929
_type = t;
3030
if (t == EspalexaDeviceType::onoff) _type = EspalexaDeviceType::dimmable; //on/off is broken, so make dimmable device instead
31+
if (t == EspalexaDeviceType::whitespectrum) _mode = EspalexaColorMode::ct;
3132
_val = initialValue;
3233
_val_last = _val;
3334
}

0 commit comments

Comments
 (0)