About two years ago, I got a Caliber Audio Technology Universel PMT801DAB-BT. It’s a combo device that features a DAB+ tuner, a Bluetooth interface, as well as an FM transmitter that can be tuned to modulate the audio signal to a frequency in the 87.6-108.0 MHz range.
It worked okay for a while, but overall I wouldn’t recommend this device for several reasons:
- Signal strength is poor, even after sticking the antenna to the windscreen. In fact, DAB+ reception was almost the same when I had temporarily put the antenna into the passenger door pocket.
- The device reboots when the engine is being started, and drops its Bluetooth connection as a result. So keep in mind to start your podcasts only when the engine’s running.
- You can take a call hands-free, but the built-in microphone captures the car’s vibrations much louder than anything else.
- The software is fairly buggy. Sometimes the FM transmission frequency setting changes randomly, and needs to be manually re-set to the desired channel. You cannot turn the device off while you’re in a (Bluetooth) call. And instead of ignoring channels with weak signals during the channel scan, they all show up in the list. Selecting one of these channels will make the device unresponsive for a few seconds.
Well, on the positive side, at least I was able to play back podcasts and listen to some DAB+ stations while driving.
The USB connector issue
The DAB+ receiver is powered by a micro USB plug which is not soldered on properly when the device leaves the factory. Thus, as soon as you plug the cable in for the fifth time (your mileage may vary, though), there’s a good chance the USB receptable will come loose from the circuit board and drop into the case. I’ve returned my first PMT801DAB-BT after less then a month because of this issue, and was thankfully given a new unit (which I handled more carefully then). Still, the USB connector of the replacement unit did not survive for more than a year and a half, during which I did not unplug the USB cable at all.
Look at this photo of the circuit board. Very clearly, the USB socket did not reflow properly, and consequently the solder joints were not strong enough to take the mechanical forces of plugging in a cable.
Opening the case
The first step towards soldering the USB connector back on is, of course, trying to get the case open. That’s easier said than done. Given the absence of any visible screw heads, I assumed that the case was just held together by plastic clips. But even though it was easy to gently pry open a small gap between the two parts of the case, something still seemed to hold the parts together. Having made no progress for the better part of an hour, and given that the unit did not work anyway, I used a serrated knife to cut my way through four studs, one in each corner of the device. It turned out the device is not only held together by plastic clips, but in fact also by four screws. To access the screw heads, however, one would have needed to remove the transparent plastic panel covering the display. As it is glued tightly to the case, there’s no way to remove it without leaving visible traces, though.
Your best bet is likely to drill holes through the transparent display cover. They’re about 8 millimeters from either side of the case, and located approximately 8 millimeters from the top edge, and 20 millimeters from the bottom. With a bit of luck, you’ll get access to the screws this way, and can remove them to fully open the case.
Did that fix it?
Now that my case was irrevocably open, I soldered the connector back on. After hooking the unit up to a 5V micro USB power supply, however, I was greeted with a mostly white screen, with a few black pixels on the left.
Well, turns out that I had unintentionally pushed the point of my knife into the display controller when trying to crack the case open. I noticed that the keys still worked, but without a usable display, the DAB tuner was beyond repair. The chances of finding a display with the very same pinout and specs are rather slim, particularly given that it didn’t even state a model number.
What’s there on the inside?
With no hope left to repair the device, let’s at least have a look at how it was constructed, and maybe scavenge some parts. I’ve disconnected the display (24pin flex connector) to get a closer look. You can still notice the leftovers of the black tape that held the display in place.
Underneath the tape, one can find the URL of the manufacturer’s website (haikesz.com), and the model number DAB008 V1.0. Not very surprisingly, I’ve never heard of them before.
On the front, there’s a bunch of ICs:
- Nuvoton M0516LDN MCU with cFeon QH16-104HIP serial Flash memory
- Nuvoton NAU88C22 stereo audio codec
- Zhuhai Jieli Technology APOY912-4A8 (next to a PCB antenna, so probably the Bluetooth device or the FM modulator)
- An IC labeled “8027 1562 7TKC” (next to the microphone, but not its preamp)
As opposed to the many components present on the top, the bottom of the PCB only contains a bunch of passives: The line input and output connectors, the antenna jack, the power button, a micro SD memory card slot, a capacitor (100u/16V), and of course the USB plug (unless it came loose). But there’s one more thing: A metal enclosure hides a stacked daughterboard to which the active DAB antenna is directly connected. Let’s take a closer look.
Four ICs are the board, which is labeled C912S DAB module, Ver. 01:
- C&A CA6817F with LiXe L24C128-TI serial Flash memory
- Silicon Labs Si4684-A10 with cFeon F80-100HCP serial Flash memory
According to its data sheet, the Si4684 is a “Single-Chip, FM/DAB/DAB+ Radio Receiver”. I was unable to locate any documentation for the C&A chip, but it looks like it’s a microcontroller that drives the Si4684. Given that the “RX” and “TX” test pads on the back of the PCB are broken out to pins on the 13-pad connector, the microcontroller is likely present to translate a serial UART communication into commands to control the DAB+ receiver.
On one edge of the PCB features, 13 pads are located at a 1.8mm pitch. On the other edge, eight more pins are present. Probing their connections with a multimeter, however, reveals that only a few pins are actually connected.
GND 1 26 GND ANT 2 GND 3 Audio 4 Audio 5 GND 6 21 GND GND 7 20 n/c n/c 8 19 n/c VDD 9 (3.3V) 18 n/c TX 10 (data to mainboard) 17 n/c RX 11 (data from mainboard) 16 GND n/c 12 GND 13 14 GND
The two pins labeled Audio are connected to the microphone input of the NAU88C22 on the mainboard, so I presume they’re the analog output signals of the DAB+ receiver.
Is the DAB+ daughterboard still good for something?
Now this sounds promising: A fully functional FM/DAB+ receiver, controllable via a serial connection, with analog output. Targeting to prove that my assumption is right, I connected the DAB+ module to the mainboard with a few short cables, and hooked up a little mic preamp I had left from a previous project to the test points I had soldered to the audio output. As soon as I had powered up the unit, music from the last station I had tuned to started playing back.
Logging some serial traffic
By tapping into the serial bus with an oscilloscope, I could sample the following handshake: A sequence made up of 0x00 bytes are being sent from the main board at 1.2kbps, until the DAB daughterboard responds, first with 0x4F, then with 0x4E. Once this has happened, the baud rate is increased to 19.2kbps.
Right away, the main board sends another 16 bytes with 0x00 value on the TX line, before moving to regular operation and polling data from the module using a two-byte sequence made up of 0x73 0x33 (ASCII “s3”).
Throughout the further operation, longer messages begin to occur on the RX line. From the looks of it, identifying information of the currently tuned station (“NDR1 NDS”) can be extracted.
Reversing the control protocol
In order to successfully control the module, I needed a list of its supported commands (and corresponding responses). So I captured some traffic (well, a lot of traffic actually), and later started pushing the buttons on the device one by one, to see what messages they resulted in.
Logging traffic from the mainboard to the DAB+ module
I was able to capture the following control sequences (all non-printable characters substituted by *) on the TX line (mainboard -> DAB module):
sequence hexadecimal representation ASCII -------- ---- ---- ---- ---- ---- ---- ---- ---- -------- boot 16 times 0x00, then 3 times 0x73 0x33 **s3s3s3 init 0x73 0x33 0x7F 0x38 0x72 0x32 0x71 0x31 s3*8r2q1 poll 0x72 0x32 0x71 0x31 r2q1 next ch 0x0D 0x41 0x01 0x72 0x32 0x71 0x31 *A*r2q1 prev ch 0x0E 0x42 0x02 0x72 0x32 0x71 0x31 *B*r2q1 preset 0x45 0x72 0x32 0x71 0x31 0x05 Er2q1* info 0x47 0x07 G* autoscan 0x48 0x08 H* menu (stop sending regular "poll" queries)
The “boot” sequence is sent right away once the baud rate has been changed to 19200 (see also the scope trace above); note that the DAB+ module does not send any reply to the “boot” sequence. Afterwards, no action takes place on the bus for a period of approximately 500ms. Then the “init” sequence is repeatedly sent every 110 milliseconds (with no direct response by the DAB+ module either) until the daughterboard finally transmits a status data frame (see below for the format). As soon as this response to the init command has been received, the device switches to polling mode and emits the “poll” command, again every 110 milliseconds, to which the DAB+ module responds with its data frame.
The commands for “next ch”, “prev ch”, “info”, “preset”, and “autoscan” are emitted when the corresponding buttons on the keypad are pressed. Neither of them is followed by an immediate response, but once “info” has been pushed, the module keeps sending more data than the pure station name. Some sort of redundancy is seemingly added by sending each command twice, first with the 0x40 bit set, and then repeated without it.
Logging the traffic from the DAB+ module to the mainboard
While I was logging data, the following two message types were exchanged.
The default response message (type 0x02 in field #0) follows the 46 bytes long structure shown in the following sample. Some values (like the checksum) of course vary across messages.
ID content hexadecimal representation --- -------- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- const 0xFF 0x23 0x10 0x02 #0 type 0x02 const 0xFE 0xFE 0xFE 0xFE 0x00 #1 scan_cnt 0x00 (updated after autoscan) const 0x02 0x01 #2 carrier 0x01 (0x00 - 0x25) const 0xFE #3 tuned 0x1D 0x01 (no) -> 0x12 0x00 (yes) const 0x18 #4 quality 0x53 (fluctuates between 0x4A and 0x5B) const 0xFE 0xFE #5 scan_chn 0x02 (updated during scan) #6 max# 0x08 (initialized on startup, updated during scan) const 0x00 0xFF #7 logic# 0x01 (0x01 up to total # of stations) const 0x3C 0x03 0x01 0xFE #8 time sec min hr day mon yr_h yr_l (init: 7x 0xFE) const 0xFE 0xFE 0xFE 0xFE 0xFE 0xFE 0xFE 0xFE cksum 0xDF
Based watching the traces for a couple of hours, the following mapping seems to make the most sense (for now, at least), even though I wouldn’t vouch for its correctness:
- Entry #6 (“max#") is the total number of stations currently stored. It is initialized when the device is powered on. This value also increments in real-time while a scan is performed, so you can track the scanning success.
- The module automatically sorts channels alphabetically by their name (it even includes some channels named “Weak Signal” at the end of the list). Entry #7 (“logic#") contains a logical ID of the currently selected station. The value starts at 0x01 and is upper-bounded by “max#”. It is incremented/decremented by one when switching to the next/previous station.
- Entry #2 (“carrier”) likely contains some index of the currently tuned DAB carrier (I didn’t work out the exact mapping between carrier IDs and the corresponding frequencies).
- Entry #3 (“tuned”) always skips to 0x1D 0x01 temporarily when I switch to another station, and returns to 0x12 0x00 shortly afterwards (mostly via the interim state 0x1D 0x00). The audio already starts playing, even when the value has not yet changed.
- When an automatic channel scanning is in progress, the currently scanned carrier is available in entry #5 (“scan_chn”), and the total number of stored stations updated in entry #6 (“max#"). Once the autoscan has completed, entry #1 (“scan_cnt”) also reflects the number of channels found in total (on boot-up it is initialized with 0x00).
- The current time is present in field #8 (“time”). With exception of the year, all fields simply contain the numerical values of the corresponding entry. The year field is composed of two bytes, and calculated as yr_h*256 + yr_l. After system start, when the time is not yet available, all seven fields contain the value 0xFE instead.
- My guess for field #4 (“quality”) is that it’s an indicator of the RX quality. It drops to 0x00 when the station cannot be tuned to, and fluctuates around values around 0x55 when reception is possible. A clear correlation between its value and the chances of the station fading out remains to be found, though.
After the status message (type 0x02, shown above), one or more messages with ASCII payload follow. They begin with the same four constant header bytes, but use a different type for each message (starting at 0x03 and incremented for each frame).
content hexadecimal representation ------- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- const 0xFF 0x23 0x10 0x02 type 0x03, 0x04, 0x05, ... (incremented for each frame) text ASCII text, 0x00 padded to 40 bytes len cksum 0x5F
When newly tuning to a station only messages of type 0x03 are being sent as long as its extended title information has not yet been received. In these messages, only the first eight bytes are used to indicate the station ID; the remainder is just padded with 0x00 values. Stations that provide more information on the current song and/or show do so in a sequence of messages. In these messages, the text is transferred in chunks of up to 40 ASCII characters each, each of which is assigned a different “type” (starting from 0x03). Frames with such additional information are automatically sent by the module (they don’t need to be solicited specifically) as soon as the station provide corresponding information.
I noticed some more points, but cannot tell if they carry any significance:
- The delay between “poll” messages and the response is rather non-deterministic. In any case, responses stop arriving altogether when the controller stops sending the “poll” messages. Given the regular rate at which “poll” is emitted, the software on the device probably simply floods the DAB+ module with requests instead of waiting from them to be served.
- When hitting the “autoscan” key and no channels are found, the frame contents vastly differ (the const between #1 and #2 is 0x00 0x01, field #2 read 0xFE, “tuned” is 0x43 0x01, #4, #6, #7 are all 0x00, and the third byte of the const between #7 and #8 is also 0x00, and the ASCII text in the follow-up frame of type 0x03 reads just “DAB”). Let’s skip this for now, given that there’s no use for a DAB+ receiver in an area without any stations.
- Information for all channels is transmitted when the “info” button is pressed. One info message is sent for each known station (starting with message type 0x03, and incremented for each channel) whose payload contains the two-digit station number, following by a period, a space, and the station’s name. Using the “next ch” and “prev ch” buttons, this list can be navigated (likely to select a new station, but keep listening to the current one meanwhile).
- The checksum calculation is simply carried out by adding all the 40 bytes of the frame payload (i.e., everything following the type field #0) and taking the two’s complement of the sum. This makes its verification easy: When summing all payload bytes and the checksum value up, the lower eight bits of the result always read zero in valid frames.
Building a DAB receiver
With the reversing knowledge gained above, let’s put the pieces back together and build a (working) DAB tuner from the scavenged parts.
Interfacing the DAB module
The 1.8mm pitch of the connectors on the DAB module make it virtually impossible to mount it on a breadboard, so I had to design a little adapter board. I transplanted the antenna connector from the orginal device, as well as a SMD fuse and the inductor using which the 5V supply is inserted into the antenna’s signal pin. It was also my first time to use KiCad’s PCB calculator tool to work out trace width and spacing in order to match the impedance on the PCB (even though I doubt the castellated edges of the DAB+ module make for a good impedance-matched connection). Simply enter all parameters of the manufacturer’s PCB stackup, and the tool will guide you to the optimum track width and clearance. I also added 10uF and 100nF caps to the 3.3V supply rail of the tuner module, and used some epoxy to glue the DAB module to the PCB for more mechanical stability.
Controlling the DAB module
The module runs on both a 5V (to supply the antenna voltage) and a 3.3V (to supply the DAB module) rail. As a result, it appeared sensible to use a USB-powered microcontroller board running on 3.3V signal level. Digging through my stash, I found a Teensy 3.2 that fit this requirement, so I hooked it up to the DAB board. All that was left was to write some software to control the module. In a nutshell, I used a timer to periodically transmit the “poll” message, and dissected incoming serial traffic according to the frame structure given above. Frames with invalid checksums were discarded (they sometimes occur because the module starts sending out a new frame before the previous one was fully transmitted).
As I didn’t have a suitable breakout board at hand to connect the rotary encoder of the original DAB+ tuner to the Teensy, I went for a row of three buttons instead. They are currently configured as follows:
- Left button: Prev channel (long press: skip back 10 channels)
- Center button: More info (long press: automatic channel scanning)
- Right button: Next channel (long press: skip ahead 10 channels)
The LEDs integrated into the buttons are currently used to give a visual feedback that the button press has been recognized (for debouncing, the buttons are only polled once every 50ms). Note that I deliberately omitted the “preset” and “info” messages, because I have no use for them.
I also found an old “Nokia 5110 LCD Module” that runs on 3.3V. So I hooked it up to the Teensy and used the u8g2 (Universal 8bit Graphics Library V2) for text and graphics display.
Amplifying the signal
The output signal comes at approximately 50mV RMS, i.e., it is too weak to be provided to an amplifier’s line-in port. I’ve found a NE5532-based microphone amplifier board I had designed ages ago (and reworked numerous times since then, as you can probably tell from all the residual flux on the board). It’s amplifying the signal by a factor of 10x as well as applying a bandpass envelope (to remove very low frequencies as well as anything inaudible). Powered by its dedicated 3.3V regulator, a good rejection of noise on the digital rails is accomplished. The output signal is provided on a 3.5mm connector.
Putting things together
Finally, I put the whole device into an enclosure, to avoid shorting out any pins and make it a little more portable. A spare 75x50x25mm box had just the right dimensions to fit the boards (after clearing out the studs in the corners). Granted, the lid didn’t fit back on, but that’s something I can well live with. After a few hours of cutting, drilling, and filing things into shape, the following photo shows the final look of my new old DAB+ tuner.