How my old Philips CD-i player keeps me of the streets during quarantine

Sander in 't Hout
10 min readApr 20, 2020

On a rainy day, I wanted to listen to an album from Jean-Michel Jarre. However, I did not have a CD player. At least not one I could easily hookup to my audio system.

Suddenly I remembered this Philips CD-i player (a Philips CDI 220/00) at my parent’s house. It definitely had some fun games like Tetris, Lemmings, Mystic Midway, Pinball and Dimo’s Quest, which I played pretty often as a kid. And of course: it’s a great CD player too. As my parents did not use it any more, it was stored in the basement and they wanted to get rid of it anyway.

But, it did need a little love …

When time semi-healed your broken timekeeper…

One of the reasons (besides getting myself a Nintendo GameCube) I stopped using the CD-i player was because of the broken NVRAM. When booting up the CD-i player the launch shell kept warning me that it had only 1% free space in the NVRAM, while showing an empty list of applications that makes use of this memory (and no format button either).

I remembered my dad opening up the player to see if there was a battery we could replace. But no battery found at that time. As a kid I was wondering how it actually kept my save files stored until that point in time…

Weird but true: after bringing home the player, hooking it up to my TV set and turning it on… the free space warning was gone!

Time healed my timekeeper, sort of… But it would still lose the save files and current date and time after a power cycle. Let’s fix it!

The timekeeper IC

So I got the player and of course lots of information on the internet about CD-i player repairs. The IC is a ST M48T08, a fairly common IC that incorporates some SRAM (for your save files), an RTC… and a battery not meant to be replaced. It still seems available nowadays. It could be a solution to remove the old IC from the PCB, solder a socket for easy future replacement and just replace the IC every few years.

But, as I disliked the idea of a built-in battery not meant to be replaced, I decided to take on the “screwdriver method” to make the battery replaceable. Luckily, a fellow hardware hacker from hacker and maker space RevSpace has done this before and helped me.

Closeup of the Mini MMC board’s lower right corner showing the timekeeper PC
It looks bad, but you wouldn’t be able to notice it anyway from the outside…

Yup, this fixed the issue!

A self built retro controller adapter

As the switched of my CD-i Trackball (22ER9013) seemed pretty bad (lots of double and sometimes missing clicks), I replaced the switches. After replacing them and cleaning the controller, the controller felt more nicely and worked a little better. Still it is not the ideal controller for most games.

The Philips Trackball controller
The CD-i Trackball (22ER9013), a fine controller for other kinds of software titles, but not so much for games

For a device with such low installed base, there is lots of technical documentation available, for example on Icdia. Thus I decided to look into the possibilities of making a controller adapter, so I can use a modern (still available) game controller on this retro machine.

Note:

This blog post is not a tutorial, but it might work as an intro to building this adapter. ESP32 sources and schematics for RS232 and USB host will be provided as open source soon!

Requirements and required components

I found the SNEStoCDI project and based on its code, some documentation found on Icdia (spoiler alert: it’s all just simple three byte messages over RS232) and I got one important requirement (the ability to use a modern game controller like Playstation’s, Xbox’s and Nintendo’s, wired or wirelessly).

I kicked off the project by ordering some components:

  • 1x ESP32 Devkit V1
  • 2 x Mini DIN 8 female connectors
  • 2 x Mini DIN 8 male to male cable
  • 1 x Mini USB Host Shield 2.0 ADK SLR
  • Some prototype boards and several colours of wire
  • 2 x DIP-16 socket
  • 2 x MAX3232 (DIP-16 package)
    note, the more famous MAX232 does not support 3.3V TTL!
  • 8 x 0.1 µF (= 100 nF) capacitors

Of course you can make yours with 1 or 2 male connectors instead of using female connectors and male to male cables, I just don’t like making cables, I don’t like soldering on those connectors.

The ESP32 is a microcontroller from Espressif. It is programmable in a very similar way to official Arduino boards and clones (it even supports the Arduino IDE and API), but most Arduino boards do not come with WiFi and Bluetooth (both Bluetooth ‘Classic’ and ‘Low Energy’ variants) integrated. At least not for $3 per board.

The MAX3232 mentioned is a RS232 transceiver which allows a 3.3V to 5V microcontroller (like the ESP32) to communicate to RS232 devices using proper RS232 voltage levels (which would fry your 3.3V or 5V microcontroller if connected without the transceiver). As the interface ports of the CD-i player using real RS232, you will need this.

If you want to support only one CD-i extension port, or don’t care about the CTS and TX lines from the CD-i (e.g. you only want to emulate one or two game controllers, no serial terminal, no telephony modem simulator, etc.) you will only need a single mini DIN connector, cable, DIP-16 socket, MAX3232 and only 4 capacitors instead of 8.

Difficulties

I started by soldering the connectors and the DIP-16 sockets on a prototype board, after doing that, I soldered the capacitors. As this is one of my first hardware hacking projects where I actually built something myself (instead of modding an existing piece of hardware), there is this design aspect I learned the hard way.

As I did not really think of how I would interconnect all of the components, I placed the mini DIN connectors and DIP-16 sockets next to each other. This made it extremely annoying to interconnect several of the components and makes it near impossible to make the required connections for the second port. Next time, I should make sure to have plenty of margin on all of the components, and if needed a larger prototype board.

My ugly but finished prototype boards
I need to find or make a nice enclosure for them soon, just to not have to look at them all the time

Schematics for the MAX3232 can be found in its datasheet. The pinout of the CD-i player’s mini DIN 8 can be found on Icdia. For future uses I decided to not only connect the RX and RTS pins, but also connect the TX and CTS pins. Please note that the player’s RX must be connected to the ESP32’s TX, and vice versa. The same goes for the RTS and CTS lines.

The USB Host Shield might need a small fix if yours does not contain a jumper to switch the source for the Vbus voltage. As the USB Host Shield board itself needs 3.3V, but the attached USB devices will probably need 5V to run properly.

Cut the trace as shown by below image (but be careful not to damage any other parts!), for example by a little scratching using a screw driver, and solder the blue highlighted pin to a source of 5V (e.g. the Vin pin of the ESP32), use a multimeter to verify the trace is properly cut. This fix will make sure the 5V can only loop through the attached USB device.

USB Host Shield with Vbus trace cut
Cut the trace as shown, and solder a source of 5V to the blue highlighted pin

Programming the USB input

After the hard work of soldering and coping with the design flaws I made on the prototype boards, it’s now time for programming the ESP32 to the USB input and CD-i outputs! I decided to use PlatformIO on VSCodium for this project, as it is a lot more advanced than the Arduino IDE. I created a new project selecting the ESP32 devkit v1 as board and the Arduino framework.

I do like ESP-IDF a little more, but the USB Host Shield v2.0 library needs Arduino SPI functions for interfacing with the board. Luckely one can still use most ESP-IDF functions even while selecting the Arduino framework, as the Arduino framework for ESP is build on ESP-IDF.

Unfortunately the USB Host Shield library did not work out of the box for me. Arduino SPI on ESP32 uses the VSPI bus by default. I soldered the USB Host Shield’s pins to the HSPI bus… This was easy fixable by removing the library reference from platformio.ini and just copying the library files to the lib folder of my project so I can make edits to it’s code.

After correcting the pin numbers, I loaded the PS4 example and connected my controller. It worked and I could properly read the PS4 controller’s state!

Programming the CD-i player controller output

This proved to be a lot harder than the USB input. The documentation on Icdia and SNEStoCDI project were definitely helpful, but I still ran into a lot of issues…

After testing with some code that outputs hardcoded controller data to the player, I saw the cursor moving a few times but not consistently and only once in a few seconds. I checked the player’s RX and the ESP32’s TX connections, all seemed fine. But at least seeing the cursor move already felt pretty promising.

I then decided to rewrite the serial code, which was initially based on the Arduino framework, using native ESP-IDF functions. This way I could have a little more control over the ESP32’s UART. This proved to be necessary as you will read further on.

After reading the ESP32 UART documentation over and over, I found out the ESP32 has a hardware transmission buffer you can not simply disable. This means that it is possible that the bytes you would think of being transmitted by calling the transmit bytes function, are not actually transmitted yet. Yet, your code continues running, possibly assuming all data has been transmitted. Which also means that, whenever we poll the state of the RTS line, the CD-i player has not received all bytes yet. We have a race condition.

You can, however, wait for the transmit buffer to be flushed by using uart_wait_tx_done(). This proved to be a simple but not very stable method. Calling it after transmitting a single byte, it sometimes froze the ESP32. I kept looking for another method of determining whether the UART is done transmitting. This led to a much more simple (and proved to be stable) method of waiting.

FastWait()
{
while (UART2.status.txfifo_cnt > 0)
{
// just wait
}
}

So now we wait for each byte to be really transmitted to the CD-i player before checking the state of the RTS line. Whenever it is negated we immediately stop transmitting, wait for it be asserted, then we identify ourselves as manoeuvring device.

I wrote the translation layer for the PS4 controller input data. Now I can move the cursor freely using my PS4 controller in all directions! But the movement is still rather jerky and many button presses are missed. What could I be missing here …

As the CD-i player wants to control the data flow by negating (which means: stop sending data, wait until I assert the line) and asserting (which means: identify yourself) the RTS line, I double checked that the GPIO pin is configured as input on the ESP32 and I can read it properly without errors. By writing lots of log messages to the USB serial port of the ESP32, I could see the RTS line being very quickly negated and asserted again before I could even send the id byte. Why?

After lots of tries with different UART settings and lots of small changes in the code I could get it to work a little better but still not perfect. As the ESP32 supports hardware flow control, I decided to try configuring the CD-i player’s RTS line as CTS pin in the ESP32. Perhaps I am missing some pullup or pulldown, which could lead to noise on the RTS line, which in turn leads to reading a wrong value.

uart_set_pin(uart_port_no, tx_pin, rx_pin, rts_pin, cts_pin);

But without any kind of flow control enabled. That way I can still read the pin myself and act to changes according to the specifications, as the CD-i player uses a non standard flow control mechanism.

This finally fixed the jerkiness! Additionally, a small fix was needed to make sure button presses will not get lost when the CD-i player negates the RTS line while transmitting the controller data bytes. It then simply send the id byte and retransmits the bytes it wanted to send before.

The result

Below is a picture of the result, I will upload a video soon also.

I will soon release the ESP32 code and schematics as open source on my GitHub, together with schematics so everyone can build their own. I hope it will some day evolve into a USB-to-retro adapter for much more types of retro systems (I’m looking at you, C64).

I would probably advise my future self to just solder everything to one larger prototype board. Also notice the second DIP-16 that is not populated. Due to my way too compact placing of the components, it might be a little hard to solder that one too (for a two player Philips CD-i experience!), but I might try or perhaps I will just make the second one on some other prototype board.

Top side of the two finished prototype boards
The final result!

Future plans for CD-i

As a software developer very much interested and intrigued by retro computer systems, I would of course love to make some homebrew for the Philips CD-i player. I did stumble across some zip files containing, for what it seems, the required OS-9 compiler and Balboa libraries you would need for creating software for the CD-i -platform.

Please don’t hesitate to contact me, by leaving a comment on this story, if you have any further information on this!

Luckily there is an ongoing effort in bringing a open source development kit for CD-i development to modern systems: https://www.theworldofcdi.com/open-source/cd-izi-authoring-tool/.

Other CD-i project ideas:

  • Supporting other CD-i output types (mouse, keyboard)
  • Emulating the CD-i’s telephony modem that Philips released in 1995, just because…
  • Replacing the CD-i player’s CD mechanism by a microcontroller to stream homebrew and backups

--

--

Sander in 't Hout

Software developer in The Hague, Netherlands. Interested in stuff like web technologies, retro hardware, VR games and how technology changes the world.