logo elektroda
logo elektroda
X
logo elektroda

How I reduced my IoT config page size 11 times by using Javascript instead of static HTML

p.kaczmarek2 3582 40

TL;DR

  • OpenBeken's GPIO/pins configuration page was redesigned to shrink a repetitive dropdown-heavy HTML interface for IoT device pin roles.
  • The page now uses Javascript to generate one shared role list, populate each select by ID, and skip PWM roles on non-PWM pins.
  • The pins page dropped from about 90kB down to only 8kB, cutting the generated page by more than one order of magnitude.
  • The smaller procedural page loads faster, but it still needs testing on Firefox and across multiple OBK platforms.
Generated by the language model.
ADVERTISEMENT
📢 Listen (AI):
  • #31 21249224
    max4elektroda
    Level 24  
    Posts: 745
    Help: 47
    Rate: 183
    max4elektroda wrote:
    So my idea is to put this all together in one place - maybe an array of a struct containing all information in one place.
    That will be quite some work to implement and change all functions using this.
    But it would mean there is only one place to maintain.


    It really took a very long time to finally find an approach to maintain the actual (good) approach to store a pins role as char, using "enum ioRole_e" to define them.
    So you always have to define a "parallel" implementation (regarding the index) e.g. to hold the names used in HTML code.

    I finally found a way to generate the enum from a file containing the other information/properties, too.

    So in PR#1378 is a first implementation:

    The enum is not directly present as code, but generated by a macro
    All information present is generated from the original sources, so it should be consistent.

    My "test" was to compare the resulting web-page (var r = ...).
    This var is generated by use of "enum", the HTML-name and th number of channels and is equal (exept the one fix I made that "BL0937SEL_n" also needs "0" channels (it was defined for 1 before).

    So, I'm quite sure the code works as expected now, but would be glad to get some feedback. Maybe you find the approach only stupid, think this can be made much smarter...

    And there is on thing I don't know how to check:

    The documentation is generated by "getcommands.js" - I tried change this, too, but how to check?!?
  • ADVERTISEMENT
  • ADVERTISEMENT
  • #33 21877107
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14408
    Help: 650
    Rate: 12345
    That's not really related to HTML, but still interesting. What's this GCC? @insmod How is it custom?
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #34 21877209
    insmod
    Level 31  
    Posts: 1353
    Help: 160
    Rate: 425
    >>21877107
    Self-built GCC 15.2 via crosstool-ng tuned specifically to arm968e-s (and armv8-m.main for RTL87X0C), plus i selected some newlib optimizations.
    I've built them because ARM GCC increased binary size (because libstdc++ is verbose), and pulling from APT can hang in workflows.

    .rbl on 7238 is ~10kb lighter (i also enabled LTO for c++ code), RTL87X0C is about 30kb lighter.
  • #35 21877210
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14408
    Help: 650
    Rate: 12345
    That's huge improvement, but what about BK7231? And BL602 potentially?

    Is it stable?
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #36 21877219
    insmod
    Level 31  
    Posts: 1353
    Help: 160
    Rate: 425
    >>21877210
    Seems to be, at least all test commands on both 7238 and RTL are successful.
    But, on LN8825 with custom GCC (that i haven't included), setChannel command failed here: https://github.com/openshwprojects/OpenBK7231T_App/blob/master/src/new_pins.c#L1705
    Index was always 331, regardless of input value.

    All platforms using new beken sdk will use custom GCC.
    BL602 use Xuantie GCC, it can't be built via crosstool-ng.
    BK7231N/T use 4.9 GCC, moving to a newer one will result in an unknown behaviour in flash vars (time_t will become 64bits)
  • #37 21877226
    divadiow
    Level 38  
    Posts: 4850
    Help: 421
    Rate: 854
    regarding "All test commands passed successfully on BK7238 and RTL8720C." in DM message, does that mean all self-tests passed or do you run other/separate?
  • #38 21877233
    insmod
    Level 31  
    Posts: 1353
    Help: 160
    Rate: 425
    >>21877226
    All commands in drv_test.c, using "backlog startDriver Test; StartTest 100;"
  • #39 21877236
    divadiow
    Level 38  
    Posts: 4850
    Help: 421
    Rate: 854
    insmod wrote:
    All commands in drv_test.c, using "backlog startDriver Test; StartTest 100;"

    ahh :)
    nice
  • #40 21877362
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14408
    Help: 650
    Rate: 12345
    This 331 seems so random and mysterious.

    Wait, maybe I can add some more tests to OBK runtime tests? I would just need to think what to test. Compiler functionality tests? Variable scopes? Types?
    Helpful post? Buy me a coffee.
  • #41 21877471
    insmod
    Level 31  
    Posts: 1353
    Help: 160
    Rate: 425
    Just merge it and wait if someone reports a random error that wasn't there before?
    I think it should work just fine, i've tried starting/stopping IR driver (didn't check recv/send), bt proxy works same as before.

    Additionally, merge BL616 pr? Like i said earlier, it's disabled in workflows until someone requests it or a real device using it shows up. BL602_ALT is disabled too, very unstable, but somewhat works.
📢 Listen (AI):

Topic summary

✨ The discussion centers on optimizing the configuration page of an IoT device, specifically reducing its size from approximately 90kB to 8kB by utilizing JavaScript instead of static HTML. The OpenBeken project serves as a primary example, demonstrating how dynamic content generation can enhance loading speeds across various platforms. Participants share their experiences with different modules, such as the LN882H and issues encountered with the BK7231, particularly in Firefox. Suggestions for further optimization include reducing whitespace, reusing code for creating elements, and improving the visibility of channel fields based on user selections. The conversation also touches on the implications of optimization flags in Makefiles and the potential for future enhancements in the code structure.
Generated by the language model.

FAQ

TL;DR: OpenBeken cut one GPIO config page from about 90kB to 8kB, and one tester said it "resolved that issue for me" when a module used to freeze. This FAQ is for embedded and IoT developers who generate web UIs from C and need faster, smaller config pages on constrained devices by moving repeated form markup into JavaScript. [#21078388]

Why it matters: On small IoT devices, removing repeated HTML can dramatically improve page responsiveness without changing the underlying GPIO-role workflow.

Approach Approx. page size Main behavior Trade-off
Static HTML from C >90,000 bytes Repeats full <option> list for every GPIO Simple generation, large payload
JavaScript-generated form ~8,000 bytes Reuses one global roles array and builds controls in browser Slightly more JS logic
Intermediate optimized version ~9× smaller than before Moves repeated options to JS Still leaves some repeated wrapper HTML

Key insight: The biggest win was not micro-minification. The biggest win was eliminating duplicated dropdown markup and generating repeated UI elements from one shared data structure in JavaScript. [#21078388]

Quick Facts

  • The OpenBeken pins page dropped from above 90,000 bytes to about 8kB, an improvement of roughly 11× on the transmitted page. [#21078388]
  • A tester reported that one N module that would completely freeze on the old config page worked after the change. [#21078389]
  • The first JavaScript refactor cut the page about , and generating the surrounding div structure procedurally reduced it further to 8kB. [#21078388]
  • Adding -ffunction-sections, -fdata-sections, --gc-sections, and -flto cut one final firmware binary by about 19kB. [#21085184]
  • A self-built GCC 15.2 toolchain reportedly made .rbl images about 10kB lighter on BK7238 and about 30kB lighter on RTL87X0C. [#21877209]

How can I reduce an OpenBeken IoT config page from about 90kB to 8kB by generating the GPIO form with JavaScript instead of static HTML from C?

Move repeated GPIO form markup out of C and build it once in JavaScript. 1. Store all role names in one global JS array. 2. For each pin, call one function that creates the div, select, and optional channel inputs. 3. Pass the pin ID, selected role index, and PWM capability so the function can build the right UI. That change cut the OpenBeken page from above 90,000 bytes to about 8kB. [#21078388]

Why does repeating the same

It slows the page because every GPIO repeats the full role list in the HTML payload. On a sample platform with about 30 IO pins, the same <option> block gets emitted roughly 30 times. That pushes the page above 90,000 bytes, so the browser must download, parse, and render much more markup on limited devices. OpenBeken users also reported freezes on some modules and Firefox-specific failures, which the smaller page helped address. [#21078388]

What is OpenBeken (OBK), and how does its pins configuration page work for assigning GPIO roles?

"OpenBeken" is IoT firmware that exposes a web interface for device setup, GPIO mapping, and role-based pin control, with support for multiple embedded platforms. Its pins page shows GPIO entries with dropdowns that assign each pin a role, plus one or two text inputs when that role needs channel numbers. For example, a button role can use two linked channels for single and double click actions. [#21078783]

JavaScript-generated HTML vs static HTML from C for IoT config pages — which approach is better for page size and load speed?

JavaScript-generated HTML is better when the page repeats the same data many times. In this case, static HTML from C exceeded 90,000 bytes because each GPIO repeated the full option list, while the JS-driven page dropped to about 8kB by reusing one roles array and one builder function. Static HTML stays simpler to emit, but the thread shows the JS approach gave the biggest speed and size win. [#21078388]

What is Link Time Optimization (LTO), and how do -flto, -ffunction-sections, -fdata-sections, and --gc-sections affect embedded firmware size?

"Link Time Optimization" is a compiler-linker optimization stage that analyzes whole-program code across translation units, enabling code removal and tighter optimization during linking. In the thread, adding -ffunction-sections, -fdata-sections, -Wl,--gc-sections, and -flto cut one final binary by about 19kB. The reported downside was not functional breakage there, but the discussion highlighted the need to test carefully after enabling these flags. [#21085184]

How do I preserve the selected GPIO role when dynamically building a