A Self-Filling Audio Player (Radio on Demand without Internet)
Self-Filling_Audio-Player
This Project has morphed a little bit. It was initially meant as an "MP3 Player for a Person with reduced Eyesight". https://forums.parallax.com/discussion/175150/ideas-for-a-mp3-player-for-a-person-with-reduced-eyesight#latest The discussion over there opened my view that with large SD cards today MP3 is not really inevitable anymore. I then searched, how to obtain audio material. In this process it became obvious, that in my region there is a radio station SWR2 which broadcasts programs, which are probably of interest for the user person. So it would be nice to record certain broadcasts. Actually you can have podcasts of these, if you have internet access. Focal area of usage of this project is for speech, which must be clearly understandable, so hifi quality is not necessary here.
What does it do?
This is an Audioplayer, which fills itself (Audio files on SD-card) with radio broadcasts, which are sent regularly due to a fixed weekly timetable. There are 4 categories of broadcasts defined:
News,
"Wissen", which is 30 Minutes of in depth information about a topic;
Music,
Story
For each category there are 3 soundfiles with maximum of 2 hours length available, which are overwritten in a rolling scheme. So you have available the last 3 broadcasts of each category.
For each category, there is a push button, which starts playing the last broadcast. If you press this button again, the next older one of this category is played.
There are also push buttons to stop audio and to start playing a fixed radio station directly.
There is an analog volume knob also.
A display is available, but it should be possible to use the machine without reading it.
So this is something like "Radio-on-Demand" without internet.
Implementation
Challenges:
Audio has to be sampled with an accurate clock rate.
On the other hand, while SD cards are most often very fast, they can have "write latency". The internal controller then needs very much longer to write the data as on average. See here for some examples: https://jblopen.com/sd-card-benchmarks/
Why P2?
To achieve the completely accurate adc and dac clock rate, this project uses a dedicated core for adc and dac. When recording, this core/cog writes into a circular fifo buffer. A second cog tries to keep this buffer empty and writes to SD-card. For playback the chain is reversed. Now the SD-card cog tries to keep the buffer filled and the dac cog reads from it. While such things can be done with interrupts on other controllers, it is very much more easy to use dedicated cores. Additional cores are used for display, scheduler and button user interface.
During the project I learned, that the quality of the ADC of P2 is adequate for this project. On the contrary to 16bit CD quality, FM radio broadcasts dynamic range are compressed because of reduced Signal/Noise ratio. The datasheet of the FM receiver chip states S/N=57dB.
20*Log10(2^10)=60,2dB
Frequency bandwidth of the radio chip is 100Hz…14kHz.
An enabler to use P2 for the project was the Kiss board. https://forums.parallax.com/discussion/172225/kiss-eval-board-general-discussion/p1 Many thanks here!
Why Forth (TAQOZ)?
An interactive language is very convenient during software development, as you can always try out small samples of code, whithout of the need for debugging code. Or perhaps sometimes with small debugging code. Forth is the simplest in a row of interactive languages Forth-Lua-MicroPython. Because it is simple, it is fast and it is compact. With Forth, there is no problem to have a lot of library routines, a compiler, an interpreter, an assembler, a video buffer and the application on board of P2's 512kB without external RAM. Taqoz Forth makes it even possible to have allmost full access to the interactive console in parallel to the running application.
Many thanks for Taqoz and many thanks for the Glossary, which makes it usable!!!
For convenience and better readability I like to use value type variables and local value type variables.
Display
A 1.8' ST7735 TFT display is used. In Taqoz there is a bitmap VGA driver, which is modified to use 4 colors (2 bits). It is possible to switch the print channel from serial to the vga and back. There is a character set implemented. So here we use these nice features of Taqoz. The new ST7735 driver implemented here consists only of:
- A private SPI bus with separate pins, with it's assembler routines. Their bit bang code is largely copied from routines in Taqoz.
- Some Init routine.
- A routine to enlarge the size of graphics/text for better readability. Each original pixel in the left lower corner of the vga screen is copied into 4 pixels. This is done in place in the video buffer. After the process, the lower left quarter is enlarged to fill the whole screen.
- A routine, to convert the 2-bit VGA color code to 16-bit color code of the display and to send it to the display for each pixel.
Radio and Real-Time-Clock:
As a FM-radio chip RDA5807M is used. https://datasheet.lcsc.com/szlcsc/RDA5807M_C82537.pdf
These can be bought on very tiny breakout boards.
This chip has also RDS, which can provide day of week and time including summertime. It was one of the most time consuming parts of this project to get this working. A helpful site is:
g.laroche.free.fr/english/rds/groupes/0/groupe0A.htm#C1C0
Also very helpful (German): https://mikrocontroller.net/topic/313562
Received RDS data is often incorrect. To get the RDS time, the radio is switched to the strongest station and the RDS feature is switched on. It is a common method to wait for two consecutive equal results. This method can take it's time and I modified it here: Data is fed into a 3 stage fifo buffer and taken for valid, if the new result is equal to any of the previous results in the buffer.
Update: With a shorter antenna and lower signal strength, I had a lot of bad data. Now the procedure is more critical. It stores the two most frequent results and takes the top result, if it reaches a certain probability. The station sends 3 datasets just after a new minute. 5 of these 4 have to have the same time.
With this method time reception takes 1…3 minutes at my place. Every night at 3AM the P2 clock is set from the RDS. Taqoz provides routines to use the main counter of P2 for a real time clock.
For better audio quality, the RDS feature of the chip is switched off.
Amplifier
A PAM8403 module is used as audio amplifier.
ADC and DAC
For ADC smart pin adc Sync2 mode, with gain 10, 8192clocks for 14bits resolution (S/N ratio is less!) with 24.4kHz sample frequency is used. So audio frequency bandwidth is <12.2kHz.
For DAC smart pin 16bit Dither mode is used. The adc gives the pace. A dedicated cog #6 is used for solid sample rate. Originally I planned to have a direct analog connection between the radio chip and the amplifier for maximum sound quality. Some sort of relay switch would be needed. At the moment there is no such line and even direct radio takes the route over the adc and dac.
Circular Audio data buffer and SD access
A 64kB buffer is used by a dedicated cog which uses Taqoz routines to read or write to SD. Taqoz makes it extremely simple to write to SD-card. Just open a file. To write a long into a RAM location, Forth uses the syntax:
DATA ADDRESS ! For example: 10 20 !
To write to SD, we can instead use:
DATA ADDRESS SD! For example 10 20 SD!
The address is relative to the beginning of the file. A 4kB Buffer is used to read or write chunks of data from/to SD. Taqoz takes care to always have the right buffer in RAM.
Dedicated cog #5 is used to handle SD- access.
Taqoz seems to have some limitations for the SD. I had some difficulties to use some 32GB cards. And I was not able to have more than 3*4 audio files on a 16GB card, which should have plenty of additional space.
There is a Forth word (Program) createAllAudioFiles to be used with the console to prepare the empty sound files, which use their own format.
Information to the Screen, Buttons, autoLevel, watchdog and Scheduler
Using the feature of polled multitasking these tasks are done in background by cog0, which also serves the interactive serial console. These tasks are state machine words, which are called each after another, when the console is waiting for key input. This method is suitable for less timecritical tasks and has the advantage of being cooperative. So only one of these tasks is active at one time and can use a ressource. Cycle time of these polls is normally 6…66 ms. Only RDS time decoding blocks any other activities.
AutoLevel is a slow compressor, which reduces audio output of the radio chip, if there is overflow. After some time, when only low level was detected, the volume is increased.
There is a regular timer interrupt implemented in Cog0, which is counted. If the counter is not reset, the timer-interrupt routine will cause a reboot. The regular reset is done by a polled task. So this can check, if the Taqoz polled tasks are still working. The scheduler also does reboot every night at 3am, which causes the system to get the accurate RDS time.
Writing to the display is split into 2 tasks. The fist step writes text to the vga buffer and is done by a polled task. Then cog #4 takes over, enlarges the picture then converts and sends it to the display. As enlarging and sending data takes some time, splitting helps to keep low the cycling time of the polled tasks. Handshake is done with a global variable.
For simplicity of handling for the user, settings like the broadcast frequencies/stations or the timetable must be edited in the source code and cannot be altered without PC.
Console
As all tasks of the device are done either as separate cog tasks or as background polled tasks, the Forth serial console ist still fully available. This can be used to inspect global variables and so forth. As Taqoz only allows one open file, here is necessary some caution.
=======================================================================
Source is included as is. It could be more tidy, I think.... If there are questions, just ask.
Comments, suggestions? - Does somebody know some DAB+ module for an attractive price?
I always receive much help and interesting inspiration from so many people here, many thanks!
Have fun!
Christof

Comments
An excellent, multi aspect write up.
Thank you.
The SD card quality issues are real. Do note that some cards will intermittently have extremely long access times that throw off any sufficiently short buffer. And the ones that have reliable access times are oddly enough the ones that generate more electrical noise. See: https://junkerhq.net/MDFourier/notes/note7-sdcardnoise-3.html . SanDisk is of the noisy type. I've been able to get away with a 2x256 byte buffer for audio playback (32kHz 16 bit stereo).
A trick I've found to get SD access times under control: NEVER RELEASE THE SELECT LINE EVER. On the SanDisk card I usually use, one random sector read usually takes on the order of ~400µs. If the select line was idle for more than ~10ms though, it will take significantly longer. The SD protocol doesn't rely on the chip select pin for framing, so you can just leave it permanently enabled (or until you access another device).
Hi, @Wuerfel_21, thank you for the input.
Question about that noise: Does this come into the system by variing DC current consumption of the SD card, which then leads to supply voltage sag of the controller and it's DAC? Then perhaps it would be good to have a separate power supply for the SD card? At the moment I think, the bigger noise problem in my setup comes from the display or from copying it's contents from P2 to it. At least you can hear some "cirp" every second. But at the moment there is no shielding at all in my setup, so perhaps there is some improvement possible here.
Some other idea: There is deemphasis switched on in the radio receiver chip. Perhaps it it would be an improvement, to shift that filter to the position after the DAC. As far as I understand this is a simple RC low pass filter with f=3200Hz in Europe.
Unfortunately the radio chip has an output of only 320mV (datasheet). Seems to be V.eff? 3.1*gain is needed in the adc but about one bit per sign side is lost, because 5V/3.1/2=0.8V Amplitude are never reached. So here is a path for noise to creep in.
As far as I know, the latencies for write can be very much more long than for read, because they occur, if the controller has to erase flash or did detect a bad/weared chunk of flash.
The KISS board doesn't have isolated VIO regulators like the Parallax board... But even on my P2EVAL I can hear the SD card noise, so I'm not sure. Maybe it also travels through the ground pin.
Yea, you could try that. An analog RC filter is equivalent to a digital IIR filter.
Isn't the DAC range from 0 to 3.3? I think you could just use the 10x ADC mode in that case.
Yes, write latency can be much worse due to flash management.
Good project!
The RDA5807M looks like another SDR receiver chip. Possibly a clone of or inspired by SiLabs. It seems ironic to me that the radio signal is digitized in quadrature, processed digitally, and then output as analog. Only to be digitized again by the P2.
If the radio used older super-heterodyne technology, then maybe the 10.7MHz intermediate frequency signal could be fed into the P2. That way analog noise in the circuit could be rejected by the FM demodulation process.
If you are only receiving a few stations, it may be possible to adjust the P2 clock frequency for better sound quality. The harmonics from things like the SD card clock could cause interference.
Hi,
RDA5807M was inspired by Philips TEA5767. Yes, it is kind of silly to do these analog digital conversions twice. It would be nice, if the chip enclosure had I2S Pins. As far as I understand, this would be RDA5808SP. In Germany, analog FM Radio broadcasting is due to be switched off. It was even planned to be switched off in February 2023. So I think, the way to go is DAB+, for example with Si4684. https://skyworksinc.com/-/media/Skyworks/SL/documents/public/data-shorts/Si4684-short.pdf Unfortunately I have not yet found a breakout board for a reasonable price.
Of course it would be interesting to know, if P2 itself could possibly decode DAB+? Such project would clearly be out of my range of skills. :-)
Christof
Hi,
This picture is very informative, because it not only shows the voltages but also the variing input impedance. It's by @evanh . https://forums.parallax.com/discussion/comment/1484327/#Comment_1484327
DAC range is 0...3V3. ADC with Gain 10 range is +/- 250mV, centered around 1.65V.
Update in first post:
Bugfixes.
A software compressor is used now to get the most from the adc, which has now gain 10.
Also I had to make the RDS time decoder more critical, because it let wrong results pass too easily. The radio chip detects the wrong RDS data group, if the signal strength is too small. Then repeated wrong data can get into the procedure.
Updated.
Bugfixes. Instead of just brute-force-stop the fifo-cog, this is now done more delicately. There had been hang-ups in corelation with the SD card, therefore we now have a watchdog and additionally regular reboots.
Also we now can pause and continue playback.
Still there is a problem at the location with weaker FM radio reception. I have to use an indoor antenna, which is just a wire. For sound quality the signal strength is sufficient, but the RDS data is bad quite often and the newer rejection routine still does not filter out all bad data.
My radio knowledge is "somewhat limited". At the moment I have tried to add a preselector coil. 4 windings, air D=10mm, length about 15mm. Idea is from here: https://to-st.de/content/projects/si4735lin/preselector.de.html Unfortunately the RDA5807M's input impedance seems to be too low, so you can't use the coil as transformer. I have now ordered a broadband amplifier module and hope that this will improve things.
If someone wants to point me to a powerful FM radio indoor antenna 107.5MHz solution, I would be grateful!
Christof