---

Tuesday, April 15, 2025

Accessible FM Radio Case Study - Part 2 - PCB Antenna

[Part 1] [Code and Design Files]

In the first part of this series, I looked at the overall requirements for the project. A key one of these was to avoid the need for the classic 1/4 wave telescopic antenna, which is, something of a hazard.

Cars haven't had telescopic antenna for some time now. It turns out they are generally a conductive trace built into the car glass. (Not much use for this project). However, I had noticed the 'shark-fin' antenna on the top of cars. It seems, that according to this research paper. It is possible to make a compact PCB antenna suitable for receiving FM broadcast transmissions in the normal range of 88-108 MHz.

PCB antenna design

The research paper gives a detailed design for the PCB, including all dimensions. I have recreated this in KiCAD and you can find the design files and even generated CAM files for production in the github repository for this project.


The PCBs came back from JLCPCB and looked good, so I made a couple of the boards up using the surface mount components specified in the paper. You can see the values in the picture above.

I actually made up 2 boards, one with the intended SMA co-axial connector, and one using a chopped off end of a 3.5mm audio connector, to make it easy to connect the antenna to the TEA5767 board that I was using for testing.


I used a hot glue gun to stick down the audio lead to prevent the wires pulling off.



TEA5767 Module

As a first test of the feasibility of using this type of PCB coil antenna, I used the TEA5767 module shown here.


There are two 3.5mm headphone style sockets on the board. One is for stereo output, and can directly drive headphones and the other is for attaching an antenna. 4 header pins provide power and the 2 I2C bus lines.

The module has an I2C interface that I will control using an Arduino Uno. The I2C interface allows you to set the tuning and volume, but also, crucially allows you to obtain the signal strength.

I can use this to compare the performance of the antenna against other antenna options, by tuning to a known radio station and then switching antennas.

The TEA5767 module is also 5V operation so I can connect it directly to the Arduino Uno and use the Arduino to power it.

The wiring is as follows:

  • 5V on the Arduino (red) to Vcc on the radio module.
  • GND on the Arduino (blue) to GND on the radio module.
  • SCL on the Arduino (yellow) to SCL on the radio module.
  • SDA on the Arduino (orange) to SDA on the radio module.


You can find the Arduino test sketch here.

/*
 * Modified from original library and examples by big12boy 2017
 * https://github.com/big12boy/TEA5767
 * 
 * Mostly reformat and recomment and simplification.
 * 
 * Signal strength only updates if you do a reset. Use Arduino Reset button
 * 
 * Tested on Arduino Uno connected to TEA5767 module. 
 * 
 * This used to test TEA5767 basic operation and measure signal strength for various antenna.
 * 
 * See blog post here for more detail: 
 * https://www.doctormonk.com/2025/04/accessible-fm-radio-case-study-part-1.html
 * 
 */

#include <TEA5767.h>
TEA5767 radio = TEA5767();

float frequency = 93.0; // Enter your own Frequency in MHz. Look up the frequency of a station near you. 
long baud = 9600;       //Enter your own Baudrate. I always use 9600.

void setup() {
  Serial.begin(baud);
  Wire.begin();
}

void loop() {
  radio.setFrequency(frequency);
  printReady();
  printStereo();
  printSignalLevel();
  
  Serial.println();
  delay(1000); 
}

void printReady(){
  int rdy = radio.getReady();
  Serial.print("Ready: ");
  Serial.println(rdy);
}

void printStereo(){
  bool stereo = radio.isStereo(); 
  Serial.print("Stereo: ");
  Serial.println(stereo);
}

void printSignalLevel(){
  short level = radio.getSignalLevel(); 
  Serial.print("Signal (0-15): ");
  Serial.println(level);
}

Before running the program on your Arduino, change the line: 

float frequency = 93.0;

to a frequency of a station that you can receive strongly with an FM radio. In my case, this is BBC Radio 4.
When you open the serial monitor in the Arduino IDE and set the baud rate to 9600, you should see something like this:


You can, of course, plug some headphones in to the radio module to hear how much noise is accompanying the signal.

Test Results

Trying to keep all other things constant, the antenna options I measured, were:

  • No antenna, sig strength 0/15
  • 23cm telescopic antenna, sig strength 4/15
  • PCB antenna, sig strength 8/15
On listening to the audio on headphones, the quality was quite reasonable with only a little hissy noise.

This is really good news, and I'm hoping we can do without the telescopic antenna.

In Part 3 of this series, I'm going to start looking at audio amplifier and speaker options, as this will inform decisions about the power supply.

Friday, April 11, 2025

Accessible FM Radio Case Study - Part 1

In this series of blog posts, I am going to document my journey (which I am about to start) in designing a FM Radio receiver for which I have the very specific requirement of being usable by my brother, who suffered a stroke a few years ago. This has left him with cognitive impairment and use of just his left arm.

This blog will effectively form my notes, and follow my usual design process which is:
  1. Investigate technologies
  2. Create 'spike' examples to de-risk various aspects of the design
  3. Breadboard or strip-board prototype
  4. Testing
  5. Prototype PCB creation
  6. Enclosure and user testing
As I hope this will end up in a design that will help others, I am going to release all the CAD and firmware files under an open license. 


User interface

  • Volume control - rotary, a pot, therefore with absolute positioning and no turning down to silent.
  • On/Off switch - probably a stylish toggle switch, but low physical resistance and ok for one handed operation without having to hold the radio.
  • Three preset buttons. Real switches with an indicator to see which is selected. Obvious icons (probably on paper) to select the channel. Talk (BBC Radio 4), Classical (BBC Radio 3), Pop (BBC Radio 2).
  • A separate interface (behind a panel - or using a BLE phone app) to configure stations

Other requirements

  • Large capacity LiPo battery power
  • USB charge and pass through
  • Decent power amplifier and speaker
  • FM broadcast receiver (*)
  • Bluetooth audio for wireless headphones
  • No eye-pokey antenna
  • Stable enclosure (one-hand friendly)
* Why FM rather than DAB or Internet Radio. Well DAB is power hungry and offers no real advantage (except perhaps future-proofing) over FM. 

As for internet radio? Well, this was my first thought as it solves the antenna problem. However, when I have looked at this before, it's quite difficult to get stable URIs for station streams, especially for BBC stations, which is likely all my brother will listen to.

Looking at what was commercially available, I found this


Which is actually pretty close to what I was planning. And if all else fails, I could easily see myself getting one for my brother. However, although you can't see it here, it has an eye-poker antenna and is powered by D-cells. Neither of which I am keen on.


Technologies

At the moment, my initial list of technologies to experiment with includes:
  • A PCB antenna (as used in 'shark-fin' car antenna) -- this is the big risk, and subject of my next blog post.
  • The  RDA5807M radio receiver IC (I might also try TEA5767 to see which is more sensitive)
  • An ATTiny1614 microcontroller 
  • A power amp probably D-class. Ideally 10W or more
I've ordered some RDA5807M modules from Aliexpress and started designing a PCB FM broadcast antenna using the research paper I found. I will need both the radio module and the PCB antenna for my first experiments.

Wednesday, December 18, 2024

The search for the built-in LED - ESP32

A recent question for me from a reader of my ESP32 book (thanks Richard) was: 

how do I find out which pin the built-in LED (if there is one) is connected to?

You'd think this would be a simple matter of consulting the documentation, but the documentation is very hard to find because there are so many different ESP32 development boards. They are often not even identified by a consistent name.

Pinout diagrams are fairly easy to find, but they do not usually identify the pin used for a built-in LED.


To help Richard out, I've come up with this test program to help in the search for the pin. 

The code

from machine import Pin
from time import sleep

pins = [x for x in range(0, 36)] # end of range last pin no. + 1
ignore_pins = [0, 1, 6, 7, 8, 11] # exclusions

pins = list(set(pins) - set(ignore_pins))

print(pins)

for pin in pins:
    try:
        print(f"Trying Pin {pin}")
        led = Pin(pin, Pin.OUT)
        led.on()
        sleep(0.5)
        led.off()
        sleep(0.5)
    except:
        print(f"Pin {pin} not allowed")


Usage

To use the program,  hold your board up in front of Thonny (or whatever you are running the code in) so that you will be able to see when the LED lights.

WARNING: This program will attempt to set every pin to be an output, so do NOT attach any extra electronics, and reset the board once you've finished testing it.

Run the program, and when the LED blinks, make a note of the pin that was being tested.

When you try and set a pin to be an output, this will either cause an exception (which we can catch) or cause the whole board to crash, or USB communication to fail (which we can't catch). So, if a certain pin seems to be causing a crash, add it to ignore_pins and try again.

Results

I tried this on a couple of boards, with these results. 

  • ESP32 Lite - 22
  • ESP32 Dev Kit 1 - 2

If you try this out and identify the pins used on a board,  send a comment (comments moderated, so it may take a while) or @simonmonk2 on X and I'll keep updating this list.



Monday, September 23, 2024

Are the Pico 2 (RP2350) GPIO pins broken?

TLDR; A little bit, yes, but it probably doesn't matter.

The new Raspberry Pi Pico 2 mostly represents a bump in performance and memory. 

A welcome feature of the release, for authors such as myself, is that the functioning of the GPIO pins and the pinout remains the same as the original Pico 1, as does the most used microPython programming environment,  

So, it was a surprise to find various reports of a hardware bug affecting GPIO pins. My main concern was, does this affect the way GPIO pins are used in the examples for my book Programming the Pico, as well as the instructions for our Pico electronics kit.?



The Problem

You can find the formal explanation of the bug in the RP2350's datasheet - on page 1341 (yup, perhaps don't start looking for it on page 1). The detail is somewhat complex, affecting only the situation where a GPIO pin (any) is used as a digital input with the internal pull-down resistor enabled.

Digital Input (Internal Pull-UP resistor enabled)

Before we look at the problem scenario, let's try out the more common situation where a GPIO pin is sued as a digital input with the internal PULL_UP resistor enabled.


Here is the example code (from Programming Pico).

from machine import Pin

from utime import sleep

switch = Pin(10, Pin.IN, Pin.PULL_UP)

while True:

    print(switch.value())

    sleep(0.1)

In this scenario, the internal pullup resistor is enabled by the line of code:

switch = Pin(10, Pin.IN, Pin.PULL_UP)

.. this keeps the GPIO pin (10 in this example pulled up to the 3.3V supply voltage, registering as a 1 when it's value is read. This resistor, that is built into the PGIO hardware of the RP2350, has a value of a few 10s of kΩ. When the switch is pressed, this relatively weak pull-up is overpowered as the switch connect the GPIO pin to GND (0V). The input then registers as a value of 0 when read.

When you run the program, you will see something like this.

By default, the value shown in the Shell area is 1, but when the switch is pressed, it changes to 0. Here's the breadboard layout using parts from the MonkMakes Electronics Kit 1 for Pico.


So, in short, this works just fine. No problem here.


Digital Input (Internal Pull-DOWN resistor enabled)

Some microcontrollers do not even have the option of a built-in 'pull-down' as opposed to 'pull-up' resistor. Generally switches on digital inputs connect the GPIO pin to GND (0V) when pressed. This is the safest option, as generally boards only have one GND of 0V, but may have several supply voltages (3.3V and 5V -- in the case of a Pico). Connecting 5V to a 3.3V digital input can destroy that input on may GPIO designs, so it's much safer to use pull-up resistors and cope with the inverted logic of 0 meaning pressed and 1 meaning not pressed in your code.

Having said that, the feature is there, so people will make use of it. So, here is the problem situation, of digital inputs with a built-in pull-down resistor.


This is shown on breadboard, here, where the switch is now between GPIO 10 and 3.3V on the Pico. There are also 2 more leads connected to a DMM acting as a voltmeter measuring the voltage at GPIO 10.



Here is the code. All that's changed is that Pin.PULL_UP has become Pin.PULL_DOWN

from machine import Pin

from utime import sleep

switch = Pin(10, Pin.IN, Pin.PULL_DOWN)

while True:

    print(switch.value())

    sleep(0.1)

When I run the program, there is immediately a problem apparent. The Shell is reporting values at the GPIO pin of 1. This should be 0, because the switch is NOT pressed and the pull-down resistor is enabled. The voltmeter is reading 2.11V, which is above the threshold for being a high input (reading a value of 1).

If I then press the button, there is no change to the output in the Shell, it is still saying 1, but the voltage at the PGIO pin is now 3.3V as you would expect.

So, if the GPIO acting as an input, actually suddenly looks like an output at 2.11V, how much current flows through the GPIO pin when we press the switch connecting it to 3.3V?!

Connecting the multimeter to the GPIO pin to measure the current drawn, then the answer is a reassuringly tiny current of just 60µA. So, we are not going to burn anything out.

So, in short, don't try and use the built-in pull-down resistors, they don't work. 

Conclusion

The internal pull-up resistor feature of digital inputs works just fine, but the pull-down feature does not work. I can live with that, because it's better practice to just use pull-up resistors.

So, should you buy a Pico 2? Yes, unless you really ned to use internal pull-down resistors.

Note that my Pico 2, was bought in September 2024, so you might find that this has been fixed in the silicon that you have.

Thursday, September 5, 2024

Jacdac - First Impressions

Jacdac is a modular prototyping/educational electronics system and protocol. This isn't Microsoft's first foray into this world. Some of you will I'm sure remember the .Net Gadgeteer system. 

In Jacdac, Microsoft have clearly learnt from their experiences of .Net Gadgeteer and produced an easy to use and more child friendly system, that can make use of the micro:bit, as well as other 'brains' using a selection of adaptors.

The Kit I was kindly given to play with is the Jacdac Starter Kit A, which is available from Amazon.com and elsewhere.

What's in the box?


The kit includes: 

  • push buttons, 
  • a rotary encoder, 
  • a slider (pot), 
  • a Neopixel (multicolor LED), 
  • a bundle of Jacdac leads,
  • a Jacdac micro:bit interface board,
  • a Jacdac expander (if you need more connections)
Each module (even a key-switch) has a tiny microcontroller to handle communication, as well as indicator LEDs that show connection and communication is working. 

Getting Started

To get started, I followed this tutorial. Note that this kit only works with version 2 of the micro:bit (the one with the bumpy edge connector).

Follow each step carefully. I hadn't noticed that the slide switch on the micro:but adapter was in the wrong position, This needs to be set to the position labelled Jacdac.

Follow the tutorial, but in overview the steps are:
  • Create a new Makecode project
  • Install the Jacdac extension
  • Download the new Makecode program onto your micro:bit
  • Start plugging stuff in

Connectors

The Jacdac connectors are reversible (like USB-c) and great for kids -- at least compared to wrangling open the jaws of alligator clip leads. The kit helpfully supplies a selection of lengths of lead.

The leads can be connected to any of the ports on the micro:bit adapter and can be daisy-chained together, making the wiring simple and un-cluttered.

If everything is setup correctly, then when you connect one of the modules to the adaptor, the module will appear in the left-hand side of the Makecode editor window.


Here you can see that Makecode has become aware of the Slider and LED ring devices.

You don't yet have any blocks you can use, but if you click on the ADD BLOCKS button, blocks for using the devices connected will appear in the Modules category of blocks in Makecode.

The screen capture above show a program that sets the brightness of the LED ring depending on the position of the slider.

Conclusion

Overall, I really liked using this hardware. The quality is excellent and leverages the already popular micro:bit and Makecode nicely.

Pros

  • Easy to use and child friendly
  • High quality

Cons

  • There is no concept of a circuit. It's modules plugged together. In extremis, you could just leave everything in the kit plugged together all the time. You are really just choosing which modules to use and which to exclude.
  • A little on the pricey-side. But, looking at the design, I can see why it has to cost what it does. 


Who's it For

I think, that as an educational tool, this is an excellent system. Kids and educators will have a lot of fun with this.

It may also have some use during product design, as a way of producing a functional prototype very quickly, allowing usability to be assessed.

Who isn't it for

I don't see this being used by electronics hobbyists and makers. It's too expensive to be used in permanent installation of projects. Arduino and clones coupled with solderless breadboard migrated onto soldered prototyping board are a more realistic prospect.

Even though it's good for making prototypes from a user interface perspective, it of no real value in prototyping an electronic design. No electronic engineer is going to couple a microcontroller with every LED or push-button in an embedded system.  

Monday, June 10, 2024

New Book on ESP32 and MicroPython

I've had several requests, over the years, from readers of my books for me to write a book on the ESP32. Yes, there is lots of information out there on the Internet, but it's nice to have everything in one place, and also tried and tested on a variety of ESP32 boards. So, somewhat late to the party, I've finally written one.


I've based the book on MicroPython using Thonny. Thonny helps a lot with the ESP32. Not only is it a nice simple Python editor for those new to coding, but it also integrates with the esptools software, making it easy to flash your ESP32 board with the MicroPython firmware, without having to use the command line.


One of the biggest challenges in writing a book about ESP32 is the shear number of different ESP32 boards available.


This book is two-thirds Python programming, which applies to and ESP32 board and one third electronics using solderless breadboard. When it comes the electronics side of things, I provide breadboard layouts for two of the most popular ESP32 boards, the ESP32 Lite and the DevKit 1.


The paperback version of the book is printed in monochrome to keep the costs down, but a color ebook version is also available.

The book is available on Amazon.com.


In the future it will also be available from some of MonkMakes resellers and you can find details, here:

https://monkmakes.com/book_prog_esp32

All the example code is available on github here. https://github.com/simonmonk/prog_esp32

The books webpage is here: https://simonmonk.org/esp32



Monday, April 1, 2024

ESP32 Programmable Output Current - LEDs without resistors?

I'm currently in the final stages of a new book 'Programming the ESP32' and while browsing the MicroPython documentation for the ESP32, I came across a section describing setting the output current of an ESP32 GPIO pin. This implied that by including this in your code: 

p6 = Pin(32, Pin.OUT, drive=Pin.DRIVE_3

you could set the drive current of a GPIO pin to 40mA - perhaps enough to directly drive an 8Ω speaker to quite a high volume. 

Or, by doing this: 

p6 = Pin(32, Pin.OUT, drive=Pin.DRIVE_0

you could limit the current to just 5mA and drive an LED directly without the need for a current limiting resistor. 


Background

The GPIO (General Purpose Input Output) pins on a modern microcontroller are little marvels of configurability. In software, you can configure them in various ways:
  • as an input or output
  • if acting as an input enable pull-up or pull-down resistors to bias the input high or low respectively
  • route a pin to an ADC (analog to digital converter) to act as an analog input and measure voltage
  • route a pin to a capacitative touch detector
  • route pins to special bus interfaces I2C, SPI and UART
When acting as outputs, GPIO pins have what are called complimentary outputs. That is, current can flow from the pin through something (say an LED and resistor) to GND -- called 'sourcing' or, you can connect one end of the load to the +3.3V supply and 'sink' current through the GPIO pin.

It looks like the ESP32 takes this a step further and allows you to set the maximum source or sink current for a GPIO pin in your code.

Goals of the experiment

Note that there is a big difference between good professional design practice and hobbyists messing around. A proper electronics engineer would never try to 'get away with things' in the way that I am here.

So, anyway, here's what I hoped to find out, using a low-cost ESP32 Lite board. After all there's a good chance this testing will end in the destruction of the board, so it might as well be a cheap one.
  1. Do these drive current settings actually control the DC current limiting of the GPIO pins?
  2. Does this current limiting also amount to short-circuit protection for GPIO pins?
  3. How much current can I draw from an ESP32's GPIO pins before I kill it?
  4. Can I use an LED without a current limiting resistor?

Method

Rather than jump straight to potentially destructive short-circuit testing. I began with the setup below, using a 100Ω resistor load to provide current limiting to I=V/R = 3.3/100 = 33mA. 

With the DMM set to a current range, the resistance across its leads will be close enough to 0Ω to ignore in these tests. From the MicroPython documentation here are the different drive currents. Our 100Ω resistor won't allow us to go to the maximum 40mA, but we can at least get an initial view of any current limitting.

  • Pin.DRIVE_0: 5mA / 130 ohm

  • Pin.DRIVE_1: 10mA / 60 ohm

  • Pin.DRIVE_2: 20mA / 30 ohm (default strength if not configured)

  • Pin.DRIVE_3: 40mA / 15 ohm

Results

GPIO 32


100Ω resistor load




Setting in Code

Nominal current (mA)

Measured Current (mA)

Pin.DRIVE_0

5

10.5

Pin.DRIVE_1

10

18.3

Pin.DRIVE_2

20

24.4

Pin.DRIVE_3

40

28


So, the current is about twice what was expected - at this point, I thought I might have an 'out-by-1' problem with my code (see the end of this post) but the code looks ok.

Nothing got hot, so I dispensed with the resistor and jumped to effectively short circuit testing, with the DMM ammeter connected directly between the GPIO and GND as shown below




Here are the results for this short circuit test.

GPIO 32

short cct





Setting in Code

Measured Current (mA)


Pin.DRIVE_0

10.9


Pin.DRIVE_1

22.6


Pin.DRIVE_2

44.6


Pin.DRIVE_3

84.9



Wow, so nearly 85mA from a single GPIO pin shorted to ground. I checked the ESP32 chip and using the finger test scale of: ambient, warm, hot, ouch, we were definitely 'warm'. Leaving the setup at DRIVE_3 for 90 minutes, there was minimal change in current or temperature of the ESP32.

Deciding to push my luck, I next tried 2 GPIOs in parallel at DRIVE_3, and modified my code to set the drive mode for all 2. Using pins 32 and 33, the current rose to 152mA and the ESP32 chip was still only warm.

Next, I tried adding GPIO 35. This made no difference to the current, nor for that matter did GPIO 34. However when I used GPIO 27 instead the current leapt to 220mA. I left this running for 30 mins and while the temperature increased to 'hot' it didn't get anywhere near 'ouch'.

I didn't push it any further, but instead tried some LEDs using the lowest current setting DRIVE_0. 

Red 8.4mA, Green 4.6mA, Blue 4.1mA. All lit nicely.

What Does the Datasheet Say?

The datasheet for the ESP32 is pretty vague about the GPIO pins. I couldn't find anything about the programability of current limiting. Searching for 'current' I came across this implying that 40mA was fine:


and also this:


The note 1. is staggering! 1.2A of GPIO power! 

Conclusion

Q1. Do these drive current settings actually control the DC current limiting of the GPIO pins? 
A. Yes - and it seems to work pretty well, even with a GPIO short-circuited.

Q2. Does this current limiting also amount to short-circuit protection for GPIO pins?
A. Yes.

Q3. How much current can I draw from an ESP32's GPIO pins before I kill it?
A. Well, it coped with 220mA from 3 GPIO pins without a problem.

Q4. Can I connect an LED without series resistor.
A. At the lowest current setting, I don't see any problem with connecting an LED directly to GPIO pins.

Also, the current limiting features of the GPIO pins means that it's relatively safe to parallel up multiple GPIO pins. If you were designing a product, then I wouldn't do this, but I'd be really tempted for a home project.

The caveat in all this is that without something documented in the datasheet there are no guarantees about how stressing the GPIO pins hard will affect the lifespan of the ESP32.

Test Program

Here's the test program I used for a single GPIO pin.


from machine import Pin

pin = 32

currents = [
    {'drive': Pin.DRIVE_0, 'mA': 5},
    {'drive': Pin.DRIVE_1, 'mA': 10},
    {'drive': Pin.DRIVE_2, 'mA': 20},
    {'drive': Pin.DRIVE_3, 'mA': 40},
    ]

while True:
    print('(0) 5mA, (1) 10mA, (2) 20mA, (3) 40mA')
    index_str = input('enter number 0 to 3: ')
    try:
        index = int(index_str)
        p = Pin(pin, Pin.OUT, drive=currents[index]['drive'])
        p.value(1)
        print('current set to mA: ' + str(currents[index]['mA']))
        print()
    except:
        pass

And here's the code for a lit of GPIO pins in parallel

from machine import Pin

pins = [32, 33, 27]

currents = [
    {'drive': Pin.DRIVE_0, 'mA': 5},
    {'drive': Pin.DRIVE_1, 'mA': 10},
    {'drive': Pin.DRIVE_2, 'mA': 20},
    {'drive': Pin.DRIVE_3, 'mA': 40},
    ]

while True:
    print('(0) 5mA, (1) 10mA, (2) 20mA, (3) 40mA')
    index_str = input('enter number 0 to 3: ')
    try:
        index = int(index_str)
        for pin in pins:
            p = Pin(pin, Pin.OUT, drive=currents[index]['drive'])
            p.value(1)
        print('current set to mA: ' + str(currents[index]['mA']))
        print()
    except:
        pass