The ESP32-C3 SuperMini modules are incredibly affordable, costing around €2, and are equipped with a compact PCB antenna. However, this tiny antenna significantly limits the usable WiFi range due to its design. To address this issue with minimal effort, I implemented a simple antenna modification that drastically improved performance.
#### The Antenna Modification
The modification involves attaching a 31mm piece of silver-plated 1mm wire, designed as a quarter-wavelength (lambda/4) antenna. The lower part of the wire is bent into a horizontal loop (about 5mm in diameter), and the remaining section is angled vertically.
This new antenna is soldered directly to the 50 Ohm antenna pin of the ESP32 module (= left end of the original antenna) and also to the second right end of the old antenna. This effectively bypasses the original PCB antenna. The TWO solder joints of the new antenna wire at both ends of the original PCB antenna are electrically essential. Also note the placement of the long angled end of the wire. The old aerial has been left in place as it becomes electrically ineffective in this configuration.
#### Testing with the WiFi Logger Program
To evaluate the effectiveness of this modification, I used my custom WiFi logger program written for ANNEX32 BASIC. The program compares the signal strength (RSSI) of two ESP32 modules: one unmodified and one with the new antenna. Both modules were mounted side by side on a portable power bank to ensure identical testing conditions while moving around various locations near an access point (AP). The live signal data was displayed in real-time on a tablet browser.
#### Results
The recorded signal strength curves clearly demonstrated that the modified module consistently outperformed the original. The modified antenna provided signal levels at least approximately **6dB** better, often exceeding **10dB** in certain conditions.
This improvement resulted in significantly more stable connections and extended range, as every 6db more doubles the theoretical range.
#### Observations
The original PCB antenna, due to its small size, actually being a compactified quarter-wave-wire, was inherently inefficient at transmitting and receiving RF energy. In contrast, the new non-shortened quarter-wavelength antenna produced a much stronger and more consistent field, likely due to its combination of horizontal and vertical elements, which minimized null points in its radiation pattern. Notably, removing the original PCB antenna was unnecessary as it no longer contributed to performance in this setup.
#### Conclusion
This simple yet effective modification highlights how inexpensive hardware can achieve substantial performance improvements with basic adjustments. By mounting a well-designed wire antenna, even budget ESP32-C3 modules can achieve reliable WiFi connectivity over greater distances.
#### The utilized test program:
ATTENTION! I used ANNEX32 V1.70.2 for the ESP32-C3 and not the current V1.70.5 as there are issues with WGetASYNC in the later versions.
[Local Link Removed for Guests]
EDIT:
CiccioCB ist still working to find the memory leak problem of versions 1.70.3 to 1.70.5 for the 1core ESP32-C3. He gave a hint to overcome the issue by inserting the command line r=PING(“ “) as a workaround.
I have updated the callback routine accordingly.
Code: [Local Link Removed for Guests]
'######## WIFI-GRAPH-LOGGER ####################################
' This program compares the WiFi signal strengths (RSSI) of two ESP32 modules.
' It aims to graphically display the impact of different antennas on signal quality.
'
' - Module 1 (local): Displays its own signal strength to the Access Point (AP).
' - Module 2 (remote): Sends its signal strength to Module 1.
' Operation modes:
' RX = 1: This module (local) regularly sends HTTP requests to the remote module.
' TX = 1: This module (remote) responds to HTTP requests with its signal strength.
' Author: Peter Neufeld (peter.neufeld@gmx.de, 03/2025)
' Configuration for the local module (ESP32 Module 1):
RX = 1 ' This module sends requests to the remote module.
TX = 0 ' This module does not respond to requests.
' Configuration for the remote module (ESP32 Module 2):
'RX = 0 ' This module does not send requests.
'TX = 1 ' This module responds to requests.
REMOTE_IP$ = "192.168.0.134" ' IP address of the remote module (Module 2)
X_Num = 100 ' Number of measurements to display in the graph.
WIFI_REMOTE$ = "" ' Stores the received RSSI values from the remote module.
'onhtmlreload: Triggered when the webpage is reloaded.
onhtmlreload WEBPAGE
gosub WEBPAGE
' Enables URL handler for TX mode:
IF TX = 1 onurlmessage RETURN_WIFI_STRING
' Enables asynchronous HTTP requests in RX mode:
IF RX = 1 onwgetasync RECEIVE_REMOTE_STRING
' Timer to regularly measure local signal strength:
timer0 500, LOG_MY_WIFI_CONNECTION
' Timer to regularly query remote signal strength:
IF RX = 1 timer1 1000, GET_REMOTE_WIFI_LOG_STRING
WAIT
'###############################################################
LOG_MY_WIFI_CONNECTION:
' Measures local WiFi signal strength and stores it in WIFI_LOCAL$.
w=0
for i = 1 to 50
w = wifi.rssi + w ' Accumulates RSSI values for averaging.
next i
w = W / (i-1) ' Calculates the average RSSI value.
WIFI_LOCAL$ = trim$(WIFI_LOCAL$ + " " + str$(wifi.rssi,"%2.1f"))
c = word.count(WIFI_LOCAL$, " ") ' Counts stored values.
p = instr(1, WIFI_LOCAL$, " ") ' Finds the first value in the string.
p = len(WIFI_LOCAL$) - p ' Calculates the length of remaining values.
If c > X_Num then WIFI_LOCAL$ = right$(WIFI_LOCAL$, p) ' Limits the number of values.
' Updates the graph with local values:
jscall |traceme(0,"| + WIFI_LOCAL$ + |");|
' Updates the graph with remote values if available:
if WIFI_REMOTE$ <> "" jscall |traceme(1,"| + WIFI_REMOTE$ + |");|
return
'###############################################################
RETURN_WIFI_STRING:
' Returns local RSSI values as a response to an HTTP request.
URLMSGRETURN WIFI_LOCAL$
return
'###############################################################
GET_REMOTE_WIFI_LOG_STRING:
' Sends an HTTP request to the remote module to query its RSSI values.
wgetasync ("http://" + REMOTE_IP$ + "/msg?x=1")
return
'###############################################################
RECEIVE_REMOTE_STRING:
R = PING(“ “) ‘This will overcome a memory leak issue in V1.70.3 to V1.70.5
' Receives and stores the RSSI values from the remote module.
WIFI_REMOTE$ = WGETRESULT$
return
'###############################################################
WEBPAGE:
' Creates and loads the HTML page with the graph to display RSSI values.
cls
jsexternal "/xy.min.js" ' Loads external JavaScript library for graphs.
cnt = 0
a$ = ""
a$ = a$ + |<p>WiFi Graph for two ESP32 modules:<br>|
a$ = a$ + |GREEN: With additional antenna<br>RED: With standard antenna|
a$ = a$ + |</p><canvas id="canvas1" width="800" height="400"></canvas>|
html a$
pause 500
A$ = ""
A$ = A$ + |var datasets = [|
A$ = A$ + | {|
A$ = A$ + | lineColor : 'rgba(20,100,100,1)',|
A$ = A$ + | pointColor : 'rgba(20,20,20,1)',|
A$ = A$ + | pointStrokeColor : '#fff',|
A$ = A$ + | data : []|
A$ = A$ + | },|
A$ = A$ + | {|
A$ = A$ + | lineColor : 'rgba(151,30,0,1)',|
A$ = A$ + | pointColor : 'rgba(151,80,0,1)',|
A$ = A$ + | pointStrokeColor : '#fff',|
A$ = A$ + | data : []|
A$ = A$ + | }|
A$ = A$ + |];|
A$ = A$ + |var ctx2 = document.getElementById('canvas1').getContext('2d');|
A$ = A$ + ||
A$ = A$ + |var xy = new Xy(ctx2, {rangeX:[0,|+ STR$(X_Num)+ |], rangeY:[-80,-35]|
A$ = A$ + |, smooth:0.05, pointCircleRadius:2, pointStrokeWidth:1 });|
A$ = A$ + ||
A$ = A$ + |function traceme(set, data){|
A$ = A$ + | var s = data.split(" ");|
A$ = A$ + | for (var i=0; i<s.length; i++) {|
A$ = A$ + | datasets[set].data[i] = [i, s[i]];|
A$ = A$ + | }|
A$ = A$ + | xy.draw(datasets);|
A$ = A$ + |}|
jscript A$
A$ = "" ' Frees memory.
return