Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use c-string methods in xplg_wemohue.ino to save memory #1683

Closed
wants to merge 2 commits into from

Conversation

emontnemery
Copy link
Contributor

@emontnemery emontnemery commented Jan 23, 2018

Use c-string methods in xplg_wemohue.ino (-1k flash)
Move sensor and extension jump tableto flash (-70 byte RAM)

Note: I have verified the changes xplg_wemohue.ino only by accessing the API through browser (valid JSON, seems fully aligned with before change). Let me know if this functionality can be tested with Domoticz.

@arendst
Copy link
Owner

arendst commented Jan 24, 2018

I'm trying to get the grasp of your function pointer changes... At least they save a lot of (future) space.

The rest will take time to implement as I want to investigate the consequences too.

Thnx for you many input.

arendst added a commit that referenced this pull request Jan 24, 2018
5.11.1e
 * Replaced command Ina219Mode with command Sensor13
 * Add
command Sensor15 2 to start MHZ19(B) Zero Point Calibration
 * Add
chunked webserver pages for large pages saving memory
 * Fix Non-English
JSON temperature unit attachement
 * Fix Sonoff Pow Energy Today and
Energy Total reading after restart (#1648)
 * Rewrite function pointers
to save code space and memory (#1683)
 * Add option define
HOME_ASSISTANT_DISCOVERY_ENABLE in user_config.h (#1685)
 * Fix SOnoff
Pow Energy Period roll-over (#1688)
@emontnemery
Copy link
Contributor Author

cool. let me know if I can help with the testing!

@arendst
Copy link
Owner

arendst commented Jan 28, 2018

@emontnemery What do you think (or is common knowledge) is the preferred way of concatenating a string?

I see you use a construction like:

int tmpiter = 0;
tmpiter += snprintf_P(mqtt_data+tmpiter, sizeof(mqtt_data)-tmpiter, "Start text");
tmpiter += snprintf_P(mqtt_data+tmpiter, sizeof(mqtt_data)-tmpiter, " Additional text");

as I use:

snprintf_P(mqtt_data, sizeof(mqtt_data), "Start text");
snprintf_P(mqtt_data, sizeof(mqtt_data), "%s Additional text", mqtt_data);

I see the advantage of mine in that it doesn't use an extra variable (useful as used in my string concatenation between sensors).

Just asking, not judging.

@emontnemery
Copy link
Contributor Author

I didn't give it much thought..
Your version is slightly slower, because the end of the string must be found for each operation, but at the same time much less error prone.
My version is faster, but also error prone. In this case I forget to bounds check the return value from snprintf so my version is quite broken.
Your version is much better in this case, let me know if I should update the PR.

@arendst
Copy link
Owner

arendst commented Jan 28, 2018

No need to update PR. I worked it already.

@arendst
Copy link
Owner

arendst commented Jan 29, 2018

This won't work as the char string will become too long (>893 chars) for more than one device. We'll have to stick to Strings here.

@emontnemery
Copy link
Contributor Author

emontnemery commented Jan 30, 2018

Oops! But instead of using mqtt_data, a buffer can be allocated on the stack, that's what String has to do anyway.

char tmp[1024];
snprintf_P(tmp, sizeof(tmp), "Start text");
snprintf_P(tmp, sizeof(tmp), "%s Additional text", tmp);

By the way, snprintf behaviour is undefined when output is same as output buffer, but maybe it works when you use it for concatenation like this?

arendst added a commit that referenced this pull request Feb 9, 2018
5.12.0 20180209
* Change library PubSubClient.h define MQTT_MAX_PACKET_SIZE from 512 to
1000 for Home Assistant  support
* Change relation of define MESSZ being dependent on PubSubClient.h
define MQTT_MAX_PACKET_SIZE
* Change command color parameter input checks to less strict for Home
Assistant support
* Change command Ina219Mode into command Sensor13
* Change commands HlwPCal, HlwUCal and HlwICal to PowerCal, VoltageCal
and CurrentCal to be used for both Pow and S31 calibration
* Change commands HlwPSet, HlwUSet and HlwISet to PowerSet, VoltageSet
and CurrentSet to be used for both Pow and S31 calibration
* Change uptime from hour to second resulting in a display of
123T13:45:21 where 123 is days
* Change module name Wemos D1 mini into Generic (#1220)
* Change HTML from width=100% to style=width:100% supporting HTML5
(#1358)
* Change OSWATCH_RESET_TIME (Blocked loop) from 30 to 120 seconds to
allow slow networks (#1556)
* Change WIFI_MANAGER_SEC into WIFI_CONFIG_SEC (#1616)
* Change function pointers code to save code space and memory (#1683)
* Change webserver argument processing gaining 5k code space (#1705)
* Change weblog memory usage (#1730, #1793, #1819)
* Update TasmotaSerial library to 1.1.0
* Update language files Italian (#1594), Dutch (#1723) and Spanish
(#1722)
* Fix Non-English JSON temperature unit attachement
* Fix Arilux RF induced exception by moving interrupt handler to iram on
non ESP8266/Arduino lib v2.3.0
* Fix truncated command names and wrong response for DomoticzSwitchIdx
(#1571)
* Fix %-sign issue as printf escape character in Humidity and Sonoff SC
(#1579)
* Fix DS18B20 temperature JSON decimal dot (#1561)
* Fix Energy JSON message (#1621)
* Fix IRSend parameter translation (#1636)
* Fix TSL2561 device detection (#1644, #1825)
* Fix BME680 teleperiod resistance measuring (#1647)
* Fix Energy Monitoring Energy Today and Energy Total reading after
restart (#1648)
* Fix IRReceive Data value (#1663)
* Fix Energy Monitoring Energy Period roll-over (#1688)
* Fix compiler warnings (#1774)
* Fix command PWM response if no PWM channel is configured (#1783)
* Add locale Decimal Separator to Web sensor page
* Add ColorTemperature to light status message
* Add command PowerOnState option 5 which inverts PulseTime and allows
for delayed always on after power on
* Add OtaMagic two step Web server OTA upgrade using filename-minimal
image if OTA free space is too small
* Add support for PMS5003 and PMS7003 particle concentration sensor
* Add command SetOption21 1 to allow Energy Monitoring when power is off
on Sonoff Pow and Sonoff S31 (#1420)
* Add Chinese language file (#1551)
* Add French language file (#1561)
* Add Spanish language file (#1589)
* Add HTTP Allow Cross Origin removed from ESP8266/Arduino lib v2.4.0
(#1572)
* Add Home Assistant MQTT Discovery for switch and light to be enabled
by command SetOption19 1 (#1534) or define
HOME_ASSISTANT_DISCOVERY_ENABLE in user_config.h (#1685)
* Add command State to retrieve device state information (same data as
teleperiod state and status 11 in slightly different JSON format)
* Add optional login to Webserver AP mode (#1587, #1635)
* Add command Sensor15 2 to start MHZ19(B) Zero Point Calibration
(#1643)
* Add support for Sonoff S31 Smart Socket with Power Consumption
Detection (#1626)
* Add command SetOption20 to allow update of Dimmer/Color/Ct without
turning power on (#1719, #1741)
* Add NTP sync time slot based on chip id (#1773)
* Add cursor pointer to web button (#1836)
@emontnemery
Copy link
Contributor Author

@arendst I'll rework the changes to use local variable instead of global variable. Should I do within this PR or close this and open a new one?

@arendst
Copy link
Owner

arendst commented Feb 13, 2018

You may do it here.

@emontnemery
Copy link
Contributor Author

emontnemery commented Feb 13, 2018

OK, now fixed:

  • Works with worst case response length (4 devices, each friendly name 32 characters + NUL)
  • Return value from snprintf handled, and response string is correctly truncated

Let me know if I should rebase the branch since there has been changes to sonoff/xplg_wemohue.ino

I still do:

char* wp = cresponse;
char* end = &cresponse[RESPONSE_SIZE];
if (wp < end) wp += snprintf_P(wp, end-wp, "Start text");
if (wp < end) wp += snprintf_P(wp, end-wp, " Additional text");

Instead of:

snprintf_P(cresponse, sizeof(cresponse), "Start text");
snprintf_P(cresponse, sizeof(cresponse), "%s Additional text", cresponse);

Because the second method has undefined behavior (http://pubs.opengroup.org/onlinepubs/9699919799/functions/sprintf.html):

If copying takes place between objects that overlap as a result of a call to sprintf() or snprintf(), the results are undefined.

@emontnemery
Copy link
Contributor Author

I just now realized this style of code is used throughout the project:

snprintf_P(cresponse, sizeof(cresponse), "Start text");
snprintf_P(cresponse, sizeof(cresponse), "%s Additional text", cresponse);

Maybe it's fine, as long as one is very careful to only use it for concatenation?
Should I rewrite the PR to use that style instead if the explicit concatenation with a write pointer?
A suggestion from stack overflow is to write a wrapper for snprintf to do concatenation in safe way without copies of code to keep track of a write pointer and out-of-bounds checks everywhere, that could also be an alternative.

@arendst
Copy link
Owner

arendst commented Mar 3, 2018

I prefer the current use concatenation as used in the project.

What's the overhead of the Stack Overflow wrapper?

@emontnemery
Copy link
Contributor Author

OK, I'll update the PR to use the sprintf-style concatenation instead!

This is the proposal from SO, I'll test and see if it makes any impact on code size.
(It needs to be updated to make sure bufSize - len is positive though as size_t is unsigned)

size_t 
snprintfcat(
    char* buf,
    size_t bufSize,
    char const* fmt,
    ...)
{
    size_t result;
    va_list args;
    size_t len = strnlen( buf, bufSize);

    va_start( args, fmt);
    result = vsnprintf( buf + len, bufSize - len, fmt, args);
    va_end( args);

    return result + len;
}

@arendst
Copy link
Owner

arendst commented Mar 3, 2018

This may be a problem with snprintf_P using Progmem data. I suggest to stay using the projects snprintfs

@emontnemery
Copy link
Contributor Author

There is a progmem version of vsnprint: vsnprintf_P.
But anyway, I'll revert to just using snprintf for concatenation since it seems to work fine.

@emontnemery
Copy link
Contributor Author

I just rebased this patch, and it turns out code size is now lower when using C++ string methods!
@arendst has there been some changes in the Arduino core to explain this?

@arendst
Copy link
Owner

arendst commented Mar 16, 2018

Don't know.

curzon01 pushed a commit to curzon01/Tasmota that referenced this pull request Sep 6, 2018
5.11.1e
 * Replaced command Ina219Mode with command Sensor13
 * Add
command Sensor15 2 to start MHZ19(B) Zero Point Calibration
 * Add
chunked webserver pages for large pages saving memory
 * Fix Non-English
JSON temperature unit attachement
 * Fix Sonoff Pow Energy Today and
Energy Total reading after restart (arendst#1648)
 * Rewrite function pointers
to save code space and memory (arendst#1683)
 * Add option define
HOME_ASSISTANT_DISCOVERY_ENABLE in user_config.h (arendst#1685)
 * Fix SOnoff
Pow Energy Period roll-over (arendst#1688)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants