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

Strings finally in Flash! #1623

Merged
merged 12 commits into from
Oct 8, 2021
Merged

Strings finally in Flash! #1623

merged 12 commits into from
Oct 8, 2021

Conversation

crankyoldgit
Copy link
Owner

@crankyoldgit crankyoldgit commented Oct 4, 2021

Finally convince the compiler to store the text strings into flash.

Saves approx 2k of Global ram, for a trade-off of between ~0-0.5k of extra flash space used.

e.g. (updated)

  • IRMQTTServer example code:

    • Before:
      RAM: [===== ] 54.1% (used 44344 bytes from 81920 bytes)
      Flash: [===== ] 54.2% (used 566209 bytes from 1044464 bytes)
      Bin file size = 570368
    • After:
      RAM: [===== ] 51.3% (used 41992 bytes from 81920 bytes)
      Flash: [===== ] 54.2% (used 566201 bytes from 1044464 bytes)
      Bin file size = 570352
  • IRrecvDumpV2 example code:

    • Before:
      RAM: [==== ] 37.9% (used 31044 bytes from 81920 bytes)
      Flash: [==== ] 35.6% (used 372025 bytes from 1044464 bytes)
      Bin file size = 376176
    • After:
      RAM: [==== ] 35.5% (used 29072 bytes from 81920 bytes)
      Flash: [==== ] 35.7% (used 372525 bytes from 1044464 bytes)
      Bin file size = 376672

Fixes #1614
Fixes #1493

Co-authored with @mcspr

Finally convince the compiler to store the text strings into flash.

Saves approx 1k of Global ram, and 1-1.5k of Flash space.
Plus an overall reduction of ~1k on the resulting bin file size.

e.g.
* IRMQTTServer example code:
  - Before:
    RAM:   [=====     ]  53.2% (used 43560 bytes from 81920 bytes)
    Flash: [=====     ]  53.1% (used 554500 bytes from 1044464 bytes)
    Bin file size = 558656
  - After:
    RAM:   [=====     ]  52.0% (used 42572 bytes from 81920 bytes)
    Flash: [=====     ]  53.0% (used 553448 bytes from 1044464 bytes)
    Bin file size = 557600
* IRrecvDumpV2 example code:
  - Before:
    RAM:   [====      ]  37.0% (used 30348 bytes from 81920 bytes)
    Flash: [====      ]  35.4% (used 369644 bytes from 1044464 bytes)
    Bin file size = 373792
  - After:
    RAM:   [====      ]  35.9% (used 29372 bytes from 81920 bytes)
    Flash: [====      ]  35.2% (used 368036 bytes from 1044464 bytes)
    Bin file size = 372192

Fixes #1614
Fixes #1493
@crankyoldgit crankyoldgit added enhancement Hacktoberfest Hacktoberfest participation Pending Confirmation Waiting for confirmation from user Hardware hacktoberfest-accepted Marking PRs as accepted for Hacktoberfest. labels Oct 4, 2021
@crankyoldgit crankyoldgit self-assigned this Oct 4, 2021
@crankyoldgit
Copy link
Owner Author

FYI @Pecacheu & @mcspr

Copy link
Collaborator

@NiKiZe NiKiZe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is almost a "replace" of PROGMEM char* x with PROGMEM char x[]
and then same type change of the extern declarations.

@crankyoldgit
Copy link
Owner Author

crankyoldgit commented Oct 4, 2021

This is almost a "replace" of PROGMEM char* x with PROGMEM char x[] and then same type change of the extern declarations.

That is pretty much correct. Outside of that, it adds #include "IRtext.h" to IRtext.cpp and some notes about why it is the way it is, for future generations of frustrated Arduino ESP8266 programmers. Le Sigh!

@crankyoldgit
Copy link
Owner Author

I mean seriously ... char *array and char array[] are equivilent AFAIK, but clearly not in Arduino-land.

@mcspr
Copy link
Contributor

mcspr commented Oct 4, 2021

at least one thing will break here - bulk of IRtext strings is used in the IRac::strTo* with strcasecmp, which will crash. will try to find the other calls with '<string>' funcs...

specific to IRac, these should be strcasecmp_P instead and the test framework updated with something like these:
https://github.com/esp8266/Arduino/blob/9d024d17fd18dffa569fa646b5326b5e8f58b0d5/tests/host/sys/pgmspace.h#L57

Make sure any strlen() & strcasecmp()'s are replaced with strlen_P() & strcasecmp_P()'s on the ESP8266 platform.

AFAIUI, the _P variants will handle non-flash located strs as well.

Add a macro structure to allow overrides and allow minimal code changes.

Note: Entire code base was checked for any `_P` possible replacements.
* Example code was excluded (and it doesn't handle the flash stored strings anyway) i.e. Its safe.
* `memcpy()`s exist but sources are not stored in flash at present.

Convert some strings to Flash based via the `F()` macro.
Ref: #1623 (comment)
@crankyoldgit
Copy link
Owner Author

at least one thing will break here - bulk of IRtext strings is used in the IRac::strTo* with strcasecmp, which will crash. will try to find the other calls with '' funcs...

specific to IRac, these should be strcasecmp_P instead and the test framework updated with something like these: https://github.com/esp8266/Arduino/blob/9d024d17fd18dffa569fa646b5326b5e8f58b0d5/tests/host/sys/pgmspace.h#L57

Latest commit to the branch should address that now. Let me know if it doesn't.

I couldn't find any other likely occurrences anywhere else in the code base, so fingers crossed, I think I may have them all.

src/IRac.cpp Show resolved Hide resolved
@crankyoldgit
Copy link
Owner Author

FYI. This utterly breaks IRrecvDumpV2 etc. So don't use this. Depending on the fix, I may go with plan B

@mcspr
Copy link
Contributor

mcspr commented Oct 5, 2021

FYI. This utterly breaks IRrecvDumpV2 etc. So don't use this. Depending on the fix, I may go with plan B

Ok... This is embarrassing, because only part of the WString deals flashmem & ram string handling and it will fail with operator+= / concat. The first crash is at here:

0x40203c9b: String& String::operator+=<__FlashStringHelper const*>(__FlashStringHelper const* const&) at C:\Users\max\.platformio\packages\framework-arduinoespressif8266\cores\esp8266/WString.h:134
  \-> inlined by: resultToHumanReadableBasic(decode_results const*) at C:\Users\max\Documents\PlatformIO\Projects\IRremoteESP8266\src/IRutils.cpp:384

b/c of the strlen call trying to access flash address (see String::concat), but I was looking at the different operator and it got lost in the linked issue 🤦

FPSTR(kProtocolStr) will do reinterpret_cast<const __FlashStringHelper*>(kProtocolStr) and things will work though. But, since this is another new code, let me think on alternative a bit more.
(and maybe patch the esp8266 Core as well)

@mcspr
Copy link
Contributor

mcspr commented Oct 5, 2021

Just as a quick example
https://github.com/mcspr/IRremoteESP8266/tree/strings_in_flash_flashstringtype
(however... strange that looks :)

But it is slightly larger, will try actually looking at the resulting binary tomorrow

(penv) ~\D\P\P\I\e\IRrecvDumpV2> pio run -e nodemcuv2 -t checkprogsize
...
RAM:   [====      ]  35.5% (used 29068 bytes from 81920 bytes)
Flash: [====      ]  35.7% (used 372553 bytes from 1044464 bytes)

(...probably b/c of some implicit string conversions...)

@mcspr
Copy link
Contributor

mcspr commented Oct 5, 2021

(...probably b/c of some implicit string conversions...)

And one more thing

diff --git a/src/IRutils.cpp b/src/IRutils.cpp
index 1d8e4b5..0c18209 100644
--- a/src/IRutils.cpp
+++ b/src/IRutils.cpp
@@ -535,6 +535,11 @@ namespace irutils {
     return addLabeledString((value ? kOnStr : kOffStr), label, precomma);
   }

+  String addBoolToString(const bool value, const __FlashStringHelper* label,
+                         const bool precomma) {
+    return addBoolToString(value, String(label), precomma);
+  }
+
   /// Create a String with a colon separated labeled Integer suitable for
   /// Humans.
   /// e.g. "Foo: 23"
diff --git a/src/IRutils.h b/src/IRutils.h
index fdda6d7..795e25b 100644
--- a/src/IRutils.h
+++ b/src/IRutils.h
@@ -48,6 +48,8 @@ float fahrenheitToCelsius(const float deg);
 namespace irutils {
   String addBoolToString(const bool value, const String label,
                          const bool precomma = true);
+  String addBoolToString(const bool value, const __FlashStringHelper* label,
+                         const bool precomma = true);
   String addIntToString(const uint16_t value, const String label,
                         const bool precomma = true);
   String addSignedIntToString(const int16_t value, const String label,

Size does go down to Flash: [==== ] 35.3% (used 368389 bytes from 1044464 bytes), but it feels like there's a bug somewhere in the String itself

@crankyoldgit
Copy link
Owner Author

b/c of the strlen call trying to access flash address (see String::concat), but I was looking at the different operator and it got lost in the linked issue 🤦

FPSTR(kProtocolStr) will do reinterpret_cast<const __FlashStringHelper*>(kProtocolStr) and things will work though. But, since this is another new code, let me think on alternative a bit more. (and maybe patch the esp8266 Core as well)

Yeah, I found that out already. Where I use strlen(well, strlen_P) via a pointer to an arbitary spot in a PSTR, it crashes the ESP8266, thus I had to replace/implement it via using the pgm_blah functions to get one byte at a time.
I've gotten it to progress a lot further by wrapping most kBlahStrs with FPSTR(kBlahStr)s or PSTR(kBlahStr) but this is getting ugly fast.
i.e. I'm happy to do it inside the library, but if some existing ESP8266 user has relied on accessing kBlahStr it's going to cause them no end of grief working out why.

If I am going to cause that much pain, I might as well hide the strings behind an access function so we are sure we are getting a correct/safe type to a user.

/me shakes fist at the ESP8266 architecture!

@crankyoldgit
Copy link
Owner Author

Just as a quick example
https://github.com/mcspr/IRremoteESP8266/tree/strings_in_flash_flashstringtype
(however... strange that looks :)

Okay. You've convinced me to stick with/go with that approach for now. That's less messy than my experimental code was turning into.

src/IRutils.cpp Show resolved Hide resolved
crankyoldgit and others added 2 commits October 8, 2021 11:11
* fix esp32 build flashstringhelper casts

* try to revert the original type<->string, use correct types

Authored-by: Maxim Prokhorov <[email protected]>
Fix:
```
src/IRtext.cpp:29:  Lines should be <= 80 characters long  [whitespace/line_length] [2]
src/IRtext.cpp:33:  Lines should be <= 80 characters long  [whitespace/line_length] [2]

```
@crankyoldgit
Copy link
Owner Author

Okay. I've tested IRrecvDumpV2 & IRMQTTServer on a Nodemcu & a d1_mini. All seem to work.

@crankyoldgit
Copy link
Owner Author

crankyoldgit commented Oct 8, 2021

FYI, this now saves ~2k of global ram. We lost a bit on the flash space, but I think I have some ideas on how to claw that back.

Updated figures:

  • IRMQTTServer example code:

    • Before:
      RAM: [===== ] 54.1% (used 44344 bytes from 81920 bytes)
      Flash: [===== ] 54.2% (used 566209 bytes from 1044464 bytes)
      Bin file size = 570368
    • After:
      RAM: [===== ] 51.3% (used 41992 bytes from 81920 bytes)
      Flash: [===== ] 54.2% (used 566201 bytes from 1044464 bytes)
      Bin file size = 570352
  • IRrecvDumpV2 example code:

    • Before:
      RAM: [==== ] 37.9% (used 31044 bytes from 81920 bytes)
      Flash: [==== ] 35.6% (used 372025 bytes from 1044464 bytes)
      Bin file size = 376176
    • After:
      RAM: [==== ] 35.5% (used 29072 bytes from 81920 bytes)
      Flash: [==== ] 35.7% (used 372525 bytes from 1044464 bytes)
      Bin file size = 376672

@NiKiZe Time for another re-review of the code I'm afraid. It's changed a lot.
FWIW, I'm fairly happy with it, but I'm not going to admit it's a) easy to understand & b) that I understand it fully either.

@mcspr Endless thanks mate! I'm not sure I'd have gotten this worked out in any reasonable timeframe without your help.

@crankyoldgit crankyoldgit removed the Pending Confirmation Waiting for confirmation from user label Oct 8, 2021
Copy link
Collaborator

@NiKiZe NiKiZe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can not find anything against this, but not tested in any way.

is there anything that we need to update in .py grabber scripts or similar?

src/IRac.cpp Show resolved Hide resolved
src/IRac.cpp Show resolved Hide resolved
@crankyoldgit
Copy link
Owner Author

I can not find anything against this, but not tested in any way.

is there anything that we need to update in .py grabber scripts or similar?

I updated the IRtext.h builder script. I can't immediately think of any of the python scripts that need adjusting.

Possibly the keywords.txt generator (which probably needs a complete overhaul or removal anyway)

@crankyoldgit crankyoldgit reopened this Oct 8, 2021
* Handle new constant structure for strings.
* Fix some other niggling issues.
@crankyoldgit crankyoldgit merged commit 1beea0c into master Oct 8, 2021
@mcspr
Copy link
Contributor

mcspr commented Oct 8, 2021

btw, also re.

/me shakes fist at the ESP8266 architecture!

another issue with not using ..._P aligned-access helpers is the the alternative is pretty slow - we could setup a hardware software trap for the exception, and redirect the read to the correct value. but, it does not know the thing / pattern to access the thing we want, so the result is hit and miss most of the time.
esp8266 Core has un-documented handler for 'generic' flash & iram access, since Core 3.0.0+ (esp8266/Arduino#7060)
(something like this is happening with esp32 builds, but b/c of a slightly different memory composition placing strings into data rom vs. the instruction one (ref. 1.1). and sw variant will possibly will be enabled with esp8266 rtos ports)

edit: typo, clarify esp32 difference

@crankyoldgit crankyoldgit deleted the strings_in_flash branch October 12, 2021 08:39
crankyoldgit added a commit that referenced this pull request Nov 19, 2021
_v2.8.0 (20211119)_

**[Bug Fixes]**
- Fix compilation issue when using old 8266 Arduino Frameworks. (#1639 #1640)
- Fix potential security issue with `scrape_supported_devices.py` (#1616 #1619)

**[Features]**
- SAMSUNG_AC
  - Change `clean` setting to a toggle. (#1676 #1677)
  - Highest fan speed is available without Powerful setting. (#1675 #1678)
  - Change `beep` setting to a toggle. (#1669 #1671)
  - Fix Beep for AR12TXEAAWKNEU (#1668 #1669)
  - Add support for Horizontal Swing & Econo (#1277 #1667)
  - Add support for On, Off, & Sleep Timers (#1277 #1662)
  - Fix power control. Clean-up code & bitmaps from Checksum changes. (#1277 #1648 #1650)
- HAIER_AC176/HAIER_AC_YRW02
  - Add support A/B unit setting (#1672)
  - Add support degree Fahrenheit (#1659)
  - Add support `Lock` function (#1652)
  - Implement horizontal swing feature (#1641)
  - Implement Quiet setting. (#1634 #1635)
- Basic support for Airton Protocol (#1670 #1681)
- HAIER_AC176: Add Turbo and Quiet settings (#1634)
- Gree: Add `SwingH` & `Econo` control. (#1587 #1653)
- MIRAGE
  - Add experimental detailed support. (#1573 #1615)
  - Experimental detailed support for KKG29A-C1 remote. (#1573 #1660)
- ELECTRA_AC: Add support for "IFeel" & Sensor settings. (#1644 #1645)
- Add Russian translation (#1649)
- Add Swedish translation (#1627)
- Reduce flash space used. (#1633)
- Strings finally in Flash! (#1493 #1614 #1623)
- Add support for Rhoss Idrowall MPCV 20-30-35-40 A/C protocol (#1630)
- Make `IRAc::opmodeToString()` output nicer for humans. (#1613)
- TCL112AC/TEKNOPOINT: Add support for `GZ055BE1` model (#1486 #1602)
- Support for Arris protocol. (#1598)
- SharpAc: Allow position control of SwingV (#1590 #1594)

**[Misc]**
- HAIER_AC176/HAIER_AC_YRW02
  - Replace some magic numbers with constants (#1679)
  - Small fix `Quiet` and `Turbo` test (#1674)
  - Fix `IRHaierAC176::getTemp()` return value description (#1663)
- Security Policy creation and changes. (#1616 #1617 #1618 #1621 #1680)
- IRrecvDumpV2/3: Update PlatformIO envs for missing languages (#1661)
- IRMQTTServer
  - Use the correct string for Fan mode in Home Assistant. (#1610 #1657)
  - Move a lot of the strings/text to flash. (#1638)
- Minor code style improvements. (#1656)
- Update Supported Devices
  - HAIER_AC176 (#1673)
  - LG A/C (#1651 #1655)
  - Symphony (#1603 #1605)
  - Epson (#1574 #1601)
  - GREE (#1587 #1588)
  - SharpAc (#1590 #1591)
- Add extra tests for LG2 protocol (#1654)
- Fix parameter expansion in several macros.
- Move some strings to `IRtext.cpp` & `locale/default.h` (#1637)
- RHOSS: Move include and defines to their correct places (#1636)
- Make makefile only build required files when running `run-%` target (#1632)
- Update Portuguese translation (#1628)
- Add possibility to run specific test case (#1625)
- Change `googletest` library ignore (#1626)
- Re-work "Fan Only" strings & matching. (#1610)
- Address `C0209` pylint warnings. (#1608)
@crankyoldgit crankyoldgit mentioned this pull request Nov 19, 2021
crankyoldgit added a commit that referenced this pull request Nov 19, 2021
## _v2.8.0 (20211119)_

**[Bug Fixes]**
- Fix compilation issue when using old 8266 Arduino Frameworks. (#1639 #1640)
- Fix potential security issue with `scrape_supported_devices.py` (#1616 #1619)

**[Features]**
- SAMSUNG_AC
  - Change `clean` setting to a toggle. (#1676 #1677)
  - Highest fan speed is available without Powerful setting. (#1675 #1678)
  - Change `beep` setting to a toggle. (#1669 #1671)
  - Fix Beep for AR12TXEAAWKNEU (#1668 #1669)
  - Add support for Horizontal Swing & Econo (#1277 #1667)
  - Add support for On, Off, & Sleep Timers (#1277 #1662)
  - Fix power control. Clean-up code & bitmaps from Checksum changes. (#1277 #1648 #1650)
- HAIER_AC176/HAIER_AC_YRW02
  - Add support A/B unit setting (#1672)
  - Add support degree Fahrenheit (#1659)
  - Add support `Lock` function (#1652)
  - Implement horizontal swing feature (#1641)
  - Implement Quiet setting. (#1634 #1635)
- Basic support for Airton Protocol (#1670 #1681)
- HAIER_AC176: Add Turbo and Quiet settings (#1634)
- Gree: Add `SwingH` & `Econo` control. (#1587 #1653)
- MIRAGE
  - Add experimental detailed support. (#1573 #1615)
  - Experimental detailed support for KKG29A-C1 remote. (#1573 #1660)
- ELECTRA_AC: Add support for "IFeel" & Sensor settings. (#1644 #1645)
- Add Russian translation (#1649)
- Add Swedish translation (#1627)
- Reduce flash space used. (#1633)
- Strings finally in Flash! (#1493 #1614 #1623)
- Add support for Rhoss Idrowall MPCV 20-30-35-40 A/C protocol (#1630)
- Make `IRAc::opmodeToString()` output nicer for humans. (#1613)
- TCL112AC/TEKNOPOINT: Add support for `GZ055BE1` model (#1486 #1602)
- Support for Arris protocol. (#1598)
- SharpAc: Allow position control of SwingV (#1590 #1594)

**[Misc]**
- HAIER_AC176/HAIER_AC_YRW02
  - Replace some magic numbers with constants (#1679)
  - Small fix `Quiet` and `Turbo` test (#1674)
  - Fix `IRHaierAC176::getTemp()` return value description (#1663)
- Security Policy creation and changes. (#1616 #1617 #1618 #1621 #1680)
- IRrecvDumpV2/3: Update PlatformIO envs for missing languages (#1661)
- IRMQTTServer
  - Use the correct string for Fan mode in Home Assistant. (#1610 #1657)
  - Move a lot of the strings/text to flash. (#1638)
- Minor code style improvements. (#1656)
- Update Supported Devices
  - HAIER_AC176 (#1673)
  - LG A/C (#1651 #1655)
  - Symphony (#1603 #1605)
  - Epson (#1574 #1601)
  - GREE (#1587 #1588)
  - SharpAc (#1590 #1591)
- Add extra tests for LG2 protocol (#1654)
- Fix parameter expansion in several macros.
- Move some strings to `IRtext.cpp` & `locale/default.h` (#1637)
- RHOSS: Move include and defines to their correct places (#1636)
- Make makefile only build required files when running `run-%` target (#1632)
- Update Portuguese translation (#1628)
- Add possibility to run specific test case (#1625)
- Change `googletest` library ignore (#1626)
- Re-work "Fan Only" strings & matching. (#1610)
- Address `C0209` pylint warnings. (#1608)
@crankyoldgit
Copy link
Owner Author

FYI, the changes mentioned above have now been included in the new v2.8.0 release of the library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Hacktoberfest Hacktoberfest participation hacktoberfest-accepted Marking PRs as accepted for Hacktoberfest. Hardware
Projects
None yet
Development

Successfully merging this pull request may close these issues.

IRtext.{h,cpp} strings are not in PROGMEM Suggestion for Lower Memory Usage
3 participants