Alternative Firmware for GL.iNet GL-S20 Thread Border Router

By using non-vendor-managed firmware for the GL.iNet GL-S20 Thread Border Router, the Thread network is no longer limited to 3 or 4 devices and a few of the more minor annoyances with the standard firmware have been addressed:

  • The new Web UI no longer constantly requires the Admin password to be entered. Now it doesn’t ever require any password – which could be considered a security risk, although that can be addressed by restricting access to the TCP/IP network it’s connected to.
  • The Web UI now includes a Thread network Topology viewer which shows how the End Devices are connected to the Leader and any other Router nodes.

As a bonus, the TBR now supports Thread version 1.4 and it didn’t require the Thread network to be re-created following the installation of the new firmware; the previous details must have been saved in Non-Volatile Storage.

A very minor issue is that the wired network interface is using a very different MAC address and so is no longer matching the DHCP lease reservation assigned to the old one. Also, it was probably unreasonable to expect the Factory Rest button press to continue to restore the original factory firmware, so reverting to the GL.iNet-managed firmware would be more involved, perhaps most easily accomplished using the Windows firmware installation tool.

The source code for this alternative firmware comes from https://github.com/EPinci/ep-s20-otbr – where the ‘heavy lifting’ is done by links to the standard Espressif IoT Development Framework (IDF) and Thread Border Router (Thread-BR) SDK GitHub repositories. There’s then a fairly ‘thin’ add-on layer (based on some code previously released by GL.iNet) which is specific to the GL-S20 hardware.

The IDF runs perfectly happily on a Linux (Fedora) desktop which had no issues connecting over a USB cable to the USB-C port on the GL-S20. (The first installation of the alternative firmware must be done via a USB cable, although subsequent updates can be done over the network.)

The main issue I hit was a difference in the sizing of the Non-Volatile Storage (nvs) partition on the GL-S20: on my device this was previously sized at 0x10000 (ten thousand) bytes and by default the new firmware was mapping it as 0x6000 (six thousand) bytes, which triggered an error check for ‘no free pages’ (and a constant reboot cycle). Fortunately I’d saved a dump of the boot log when running the GL.iNet 2.0.1-B1 firmware which included the original partition table sizing, which I was then able to match with a small edit to partitions.csv. I added much more detail on this in Sizing of ‘nvs’ partition; ESP_ERR_NVS_NO_FREE_PAGES error (fixed) #1 in the Issues log on the GitHub repo.

The Thread Topology viewer is a feature that is almost essential for managing a Thread network with more than a very few nodes.

A screenshot from the Web UI for the Espressif Thread Border Router. The main body of the image shows a set of coloured circles connected by pale grey solid and dashed lines.
The largest circle is coloured Blue and the Key shows this is the 'Leader' of the Thread network. Two small Green circles are linked to the Blue circle by dashed lines, showing these are 'children' of the Leader.
A mid-sized Cyan circle represents a Router, linked to the Blue circle (Leader) by a solid line.
Three small Green circles represent Child nodes, connected to the Router by dashed lines.
Example of the Thread Network Topology view

The example Topology view above shows the Thread network Leader (the GL-S20) in Blue, with one Router (an IKEA GRILLPLATS smart plug) in Cyan. The ‘Child’ (i.e. non-Router) devices are in Green.

This Topology view introduces a set of node identifiers I had not come across before; IDs like 0xb402 are Thread network Routing Locator (RLOC) identifiers – strictly speaking RLOC16 identifiers (since these are just the last 16 bits of the full RLOCs) which identify the placement of nodes within the Topology graph. It is implicit in the naming that Node 0xb402 is a Child of Router 0xb400. Clearly these identities must change as the Topology changes – which is why alternative identifiers are also in play.

As yet I’ve not tried Commissioning a new Matter-over-Thread device with the TBR running this alternative firmware, but given that all the Thread credentials appear to have been preserved there’s no reason why that shouldn’t work the same as before.

Matter-over-Thread: Low Limit on Number of Devices when using GL-S20 TBR?

After successfully adding three IKEA TIMMERFLOTTE Temperature & Humidity sensors to the Matter-over-Thread network, which is connected to Home Assistant, I bought two further TIMMERFLOTTE sensors and one GRILLPLATS ‘smart plug’ (primarily for its power monitoring function, rather than the ability to control the outlet – and potentially also to act as a mains-powered ‘range extender’ for the Thread radio network).1

For some reason, these new devices are all consistently failing to Commission as Matter devices in Home Assistant, despite using the exact same procedure as worked successfully for the older devices. There’s no problem with the first stage of commissioning (“Connecting”) but the second stage (“Setting Up”) always times out. Since the first three devices are still working perfectly, there can’t be any issues with the basic set-up of Thread or Matter.

There are some other reports of this same issue when using the GL.iNet GL-S20 Thread Border Router, on both the Home Assistant and GL.iNet community forums:

None of those discussion threads has any definitive resolution identified (apart from perhaps the last one, which isn’t GL-S20-specific). Other Home Assistant users have much greater number of devices working successfully using different TBR solutions, so it looks like it might be an isolated issue with the GL-S20 firmware.

While there is no newer ‘Stable’ release of the GL-S20 firmware available (as of May 2026), I tried the latest ‘Beta’ (2.0.1-B1 dated 2025-07-02) but that didn’t improve matters. (This version enables the OTBR API integration with Home Assistant, but GL.iNet do not seem to publish Release Notes for firmware updates so it is not clear what other changes might be included.)

Interestingly, after only a few months (I bought mine in November 2025) the GL-S20 seems to be no longer available for purchase and there also doesn’t appear to be much recent activity from GL.iNet in releasing new firmware for it. In particular, there doesn’t seem to be much prospect of them providing support for Thread version 1.4, which was released a few months ago.

Under the covers the GL-S20 uses standard Espressif chips closely following the reference design (as documented here). GL.iNet provide some guidance on compiling a very slightly adapted version of the standard Espressif ‘example’ firmware here. GL.iNet have also released an SDK for a variant of this firmware here.

More interestingly, Edoardo Pinci has released the source code for a fork of the firmware for the GL-S20 on GitHub at https://github.com/EPinci/ep-s20-otbr which has been updated to support Thread 1.4 and provide better integration with Home Assistant (via the OTBR API). It also appears to disable the WiFi interface – which is fine, because that’s not required in my use case (and avoids the risk of interference with the Thread network which runs on the same 2.4GHz frequency). Running firmware for which the source code is readily available is very appealing, to help identify where error messages might be coming from and to address the limited logging available by default with the standard GL-S20 firmware. I have enough experience of cross-compiling software for embedded platforms for this not to be particularly scary prospect.

While doing some preparation for testing this alternative firmware, including checking for connectivity via the USB console (which happened to require the GL-S20 to be relocated into the House, near a Linux desktop machine) it became apparent that the ‘new’ Thread devices could all be Commissioned successfully (since with the GL-S20 in this temporary location, the existing TIMMERFLOTTE sensors were out-of-range). That confirms the issue is nothing to do with the device hardware and probably not directly an issue with Home Assistant but instead appears to be a limit on the number of ‘active’ Thread devices, imposed by the GL-S20.

With the three extra devices commissioned successfully, moving the GL-S20 back to its original location resulted in two of the ‘old’ devices coming back online, for a total of four – and a couple of SrpServer “Failed to add service” error messages in the GL-S20 log file.

My current hypothesis is there’s a hard-coded limit on the number of ‘services’ that can be managed using SRP, the Service Registration Protocol. SRP is used within the Thread network, by Thread devices which register with the SRP server hosted on the Thread Border Router (which then broadcasts those same registrations on the wired network using mDNS). While it’s no surprise that such a limit exists, it is a surprise that only a very few devices can be active concurrently.

In some ways I’d prefer to continue using the vendor-supplied firmware, since that works with their Smartphone App (to enable extraction of the Thread network credentials) and I presume it will be necessary to re-create the Thread network (using the same details?) when running alternative firmware – but if there is a hard-coded limit in the firmware (and no prospect of GL.iNet releasing new firmware to increase that) there don’t seem to be many other options for continuing to use the GL-S20 hardware.

So while I’m slightly disappointed GL.iNet are not doing more to actively support a device which is only a few months old, their decision to closely follow the Espressif reference design such that the standard Espressif firmware cross-compilation and firmware installation tools can be used means the hardware can fairly easily run non-GL.iNet firmware – which ensures the hardware will be usable for many years to come.

  1. The Node Roles and Types page in the OpenThread documentation explains the different types and sub-types of Thread nodes. The TIMMERFLOTTE devices are battery-powered and show up in Home Assistant as Thread device type “Sleepy end device” (which is a sub-type of “Minimal End Device”) – so these are definitely not able to act as a ‘Range Extender’ for the Thread wireless network. The GRILLPLATS devices are mains-powered and so more likely to be candidates for acting as a ‘Range Extender’. Home Assistant initially showed these as Thread device type “End device” without clarifying if this was a Full Thread Device or a Minimal Thread Device. The former would leave open the possibility of being a “Router Eligible End Device” (REED) which could be promoted to being a Router within the Thread wireless network (as distinct from a Thread Border Router). Some lists of Matter devices indicate these are FTDs which will act as Routers. Upon later inspection, the GRILLPLATS changed to showing as a “Routing end device” within Home Assistant which confirms it is routing. It’s not clear if this was in response to the placement of other devices nearby which wanted to use it as a router; the OpenThread documentation says that if there are fewer than 16 Routers in a Thread network, any REED joining the network will automatically become a Router. ↩︎