Black & White CRT for Raspberry Pi

A Skippable Introduction

Every year a deluge of new electronics rolls of the assembly lines and into already cluttered homes. It’s unfortunate that the majority of them will likely find their way into a landfill even before the next batch comes along. If they have any permanence they’ll likely be made obsolete in short order.

I don’t mean to sound crotchety. There’s nothing inherently wrong with replacing the old with the new. There are many virtues in upgrading to a newer computer whether to increase productivity or enjoy the newest video-game how it was designed to be played. Then there’s the other side, like the pointlessly wasteful disposable power bank.

Many products simply become obsolete and in that process we can find spectacular examples of humanity’s ingenuity. It’s incredible to think that a little less than a century ago the black and white television was the pinnacle of technological advancement. Now, OLED TVs are becoming more affordable and newer technologies are on the way.

Despite the introduction of color televisions in the late sixties it’s black and white predecessor managed to stick around in one form or another, even into the later end of the 90s. When I was younger I remembered seeing advertisements for various portable black and white CRT televisions. I remember wanting one, but never owned one due to the price. Now, a decade and a half past it’s manufacture date I got one, for a fraction of MSRP.

The Television in Question

Specifically what I bought for ~$5.00 from a Goodwill was a Claybrooke 5 in 1 Compact TV with an incredible array of features including a radio, flashlight, and even a compass. I would really like to know if ever anyone looked at one of these and said, “man, having a compass on this thing really sold me.”

Perhaps a reason for the low price was the missing battery cover, power adapter, and busted plastic bezel. Fortunately this wasn’t an issue as far as I was concerned, as I had no intention of keeping it’s original case. Perhaps a more practical bit of fortune was that it worked.

My plan for this CRT is clear from the title but there’s more to choosing a portable TV than the price and screen size. These portable models were designed to operate off DC, often coming with a 12V car plug. With the use of a buck converter for the Raspberry Pi I can run both the Pi and the CRT off 12V, and potentially even make the package portable.

Unfortunately, Claybrooke didn’t have the decency to supply the end-user with any video or audio input. The unit does however have a TV aerial plug and in the early 2000s when this unit was manufactured RF modulators were readily available from RadioShack.

Spending two to three times more than the price of the TV for an RF modulator just to interface with a Raspberry Pi didn’t sit well with me. There had to be another way, which meant that the next step was tearing it open.

Getting Inside

WARNING: CRT screens operate with extremely high voltage and can cause serious injury and death. The information provided is intended for educational purposes only. Any actions taken based on the information provided you take at your own risk.

SAFELY DISCHARGING A CRT: Before working around a CRT it is imperative safely discharge the CRT. Information and instructions on how to safely discharge a CRT can be found online.

After removing a number of screws and safely discharging the CRT I was left to investigate the board. This TV, and many like it, are extremely similar and are based around a single TV IC.

In the Claybrooke’s case this IC was a CSC5151A. This IC is equivalent to the AN5151N and KA2915. I was able to download the datasheet for the AN5151N which contained the following block diagram and application circuit:

Pin 5 is listed as video out which would allow my to bypass the ICs video out and replace it with my own composite input. Before messing with any pins I searched online to see if anyone else had undertaken such a conversion with one of the aforementioned equivalent TV ICs. Maciej Witkowiak on Hackaday was one of several who had.

Following Witkowiak’s post I first determined that my board also used a shared ground starting with pin 21. However, where I differed was I wanted to avoid doing anything irreversible. Witkowiak cut the trace from pin 5 from the IC and then inserted their composite signal from a test point.

Since the CSC5151A was in a DIP package I was able to easily desolder pin 5 and lift it from its socket. I was then able to insert my own wire and solder it in place, thus bypassing pin 5 on the IC. This way, if I needed to use an RF modulator I could easily reinsert the pin without bodging anything. I did bodge in a wire on the bottom of the board into pin 21 for the composite input’s ground.

I connected the ground wire from pin 21 to the negative of a RCA screw terminal adapter and the wire from pin 5 to the positive. One lifted pin and two soldered wires later I had a composite input.

The Claybrooke provided an opportunity to use my Raspberry Pi B Rev 1 with it’s dedicated composite output and avoid soldering any additional headers on a newer Pi. While a modern LCD screen is in many ways preferable, there’s something uniquely cool about using a CRT.

An unavoidable downside is the risk of electrocution and death due to the CRTs high operating voltage, even here. Safely discharging a CRT isn’t difficult, but extreme caution must be taken when working around CRTs. Thankfully similar portable televisions exist that include a composite input, such as one made by GPX.

The Imperfect Workbench

I recently moved to a house and with this move came a change. I know some people prefer apartments, and that’s great for them. However, I’m certainly not one of them.

Moving from an apartment to a house wasn’t only a psychologically beneficial change of scenery (to be frank, I don’t think I could ever live in an apartment again), but also one that means I could finally get back into the business of some larger scale tinkering that simply wasn’t viable inside an apartment. Once again, I had myself a garage.

With most things it’s best to start small and build your foundation up from there. In this case the first step (well, actually second) was to build a workbench because first, I had to build something to set my miter saw (DeWalt DWS779) on, which I bought because I’d be cutting down a bunch of 2x4s. Basically, I’m buying tools to make things just so I can spend even more money on tools to do more things, that also cost money.

It may be a vicious cycle but I’m feeling okay about it.

I’m not willing to settle for just a workbench, I have some more expansive ideas in mind. The theme for this garage is flexibility. I want to be able to pull my cars out onto the driveway on a Saturday morning, take advantage of the full garage, and pull everything back in in the evening.

My plan is to make several “mobile” work stations over time and mount casters so I can roll them about as needed. I wanted the legs on my workbench to be able to sit firmly on the ground, especially since I’d be adding a pegboard. What I ended up doing is buying a set of these retractable casters from Amazon to accomplish that.

Now, I want to clearly state that this is by no means the best workbench design. I looked at a number of designs online but finally decided try my hand at designing something in Fusion 360. Fortunately, 2x4s are pretty inexpensive so even if I bungled the entire thing up I wouldn’t be out a whole lot of money, just time.


  • Miter saw
  • Jigsaw
  • Track saw
  • Drill
  • Impact driver

The impact driver was indispensable tool in this project. You certainly don’t need a miter saw but it was very helpful by turning a otherwise laborious task into half an hour or so. If you don’t use a miter saw I would be very careful because you want to make sure everything is as even and exact as possible.

The track saw is very optional, you could easily make a guide with a 2×4 and some clamps if you don’t own a edge guide for cutting the sheets

I drilled pilots for very few of the screws (maybe four when going into the end grain), however if you think you might crack the wood at some point it’s always safer to just make a pilot.

The above isn’t an extensive list, I also made use of clamps and other more common tools.


  • 14″ (4)
  • 15″ (4)
  • 24″ (10)
  • 36″ (2)
  • 57″ (5)
  • 60″ (2)
  • 74″ (2)

The above are only the lengths of 2×4 needed. I also bought a 4×8 sheet of 3/4th MDF and a sheet of pegboard, the latter of which I have plenty extra of. I used 3-1/2″ and 2-1/2″ deck screws plus various bolts outlined in the steps below.


24″ (5) & 60″ (2) 2x4s

2 – LEGS

14″ (4), 15″ (4), 36″ (2), & 74″ (2) 2×4

I placed a 2×4 where the horizontal brace for the bottom shelf would later be mounted. I wanted to make sure the legs were as even as possible and triple checked all of my measurements before finally driving in screws.


57″ (2) 2x

I attached all four legs at the same time using clamps to hold everything together while I made measurements and got out the level. After measuring everything a few times and checking repeatedly with the level I finally screwed everything together.


28″ (5) 2×4

Somewhere between mounting and legs and finishing the frame I took a trip to the hardware store and ended up picking up a LED shop light and more screws. I added some short 2×4 scraps to act as a temporary mount for the light.


57″ (3) 2×4

You can see the horizontal 2x4s for the pegboard in the photo from the previous step.

6: Bench Top

After cutting the MDF to size I used my jigsaw to cut out slots for the 2x4s that would hold up the backboard.


I drilled nine holes for 3/8th bolts for the spacers to mount the pegboard too. I also used the jigsaw here to cut the down the top edges of the pegboard due to the 2x4s that I’m using to mount my light to.


For a set of retractable casters that boasts a capacity of 800 lbs I was somewhat surprised they included screws. There wasn’t any way I was going to rely on them. I scrounged around for something and ending up drilling holes in the legs and using some 3/8th inch 5″ bolts with crush washers and some aluminum plates (I would have used steal but I aluminum was available) to mount the casters to the legs. 5″ was a little too long but it was easy enough to cut them off.

The front of the bench faces the left side of the image above. I was concerned that the placement of the casters would make the bench less stable, but I was also worried that if I had the casters sticking out front ruin my feet on them.

When I finally got my bench somewhat stocked and tested rolling my bench around I was surprised at how sturdy it all was and I had no concern that it’d topple over while moving it about.

The bench extremely sturdy on the ground, more than I expected. Even still I don’t plan to put a whole lot of weight on the pegboard. I also plan to add some sort of anti-tipping device that I can unfold just in case.

My only complaint about them isn’t really about the casters but the process of lifting them. The best way I’ve found is to simultaneously each side at once and the same applies for lowering. I plan on crafting a solution to this problem so I can engage and disengage all four at once.


The workbench isn’t completed, but it’s getting there. I have a number of things I still need to get around to but I’m pleased with it thus far.

Most pressingly I need to work out my electrical setup. Having a power strip hanging precariously from one side isn’t a permanent solution. It’s been rather chilly the last few days and once it warms up I’m planning on wiring something up.

Raiders of the Lost Accounts

Finding forgotten INTERNET accounts with Python


If I had used a password manager like KeePass from the beginning (it was released in 2003) I wouldn’t be in this predicament. One of my year-end goals was to update my password manager with not only my primary accounts but also those that have been long abandoned.

I shouldn’t blame the ease of creating online accounts for the number of accounts I’ve abandoned and even completely forgotten entirely, but I’m tempted to. If you’re using a different password for each website orphaned accounts aren’t nearly as much of a security risk as they otherwise might be. Assuming they don’t contain any significant personal information.

For me tracking down accounts from yesteryear isn’t so much about wanting to improve my internet security (though it’s a good opportunity to change passwords) but more about my own curiosity. There’s probably easier ways than doing this yourself such as the websites that will cross-reference usernames with numerous sites. That simply doesn’t interest me.

The method I’m using may not be that useful for people who delete their emails. However, if you’re like me you don’t which means there’s a good chance of finding some forgotten accounts.

One of the most laborious and dull ways would be to simply fire up each email account in turn and search for various keywords associated with the confirmation emails you get when making a new account. Or you know, I could try to code something.

Chapter 16 of Al Sweigart’s fantastic book Automating the Boring Stuff with Python: Practical Programming for Total Beginners was what gave the inspiration. Some of the code within is outdated as the IMAPClient has had updates since the book was released and I get into that below. His books are fantastic and I highly recommend supporting him!


I’m not a programmer by trade and my attempt is akin to someone stumbling through a foreign language they don’t quite grasp but have a interest in learning. If you’re someone who is familiar with the Style Guide for Python Code you may be disturbed by what comes next. Also, be aware that I am not at all responsible for anything going wrong with your email and all of that.

The Python script that I’ve cooked up utilizes IMAPClient, pyzmail, email, and a few other libraries to log into an email account, search through the inbox, and make a list of all the unique addresses I’ve received emails from. I’m using Python 3.6.

To further narrow down results you could also extract the email subjects and cross-reference them against keywords found in the typical boiler-plate language of confirmation/activation emails received when making an account. I’m not going to add that extra layer.

Most email clients allow their users to use the Internet Message Access Protocol (IMAP) to access their mail servers. However, some providers like Gmail have IMAP turned off by default while others like Yahoo allow access without updating any settings.

Before moving on you should make sure your email client is set-up to be accessed using IMAP. For Gmail go to click the settings gear > settings > Forwarding POP/IMAP > IMAP Access > Enable IMAP.

A note about Gmail: I found that even after I enabled IMAP that I was unable to log in. Under Google Account > Security there should be a hyperlink to Secure Account. You may find that Google has blocked access from your computer and may be listed as a security issue. There should be a option to choose whether it was you or not who attempted to access your account. After confirming it was me I found that I had to wait a few hours before I could finally access my Gmail account over IMAP.

If you’re unable to log in you may also have to change your settings to allow access from less secure devices. For Gmail go to Google Account > Security > Less secure app access. For Yahoo go to Account Info > Account Security > Allow apps that use less secure sign in. It’s best to turn this back to off after you’re done.

Using two-factor authentication? You should look into Application Specific Passwords to be able to access your email. Click here for Gmail and here for Yahoo.

After you’ve allowed IMAP you first need to figure out what the IMAP domain is for your email provider. I’ve listed a few common ones below:

  • Gmail:
  • and
  • Yahoo Mail:



With all that out of the way it’s time to fire up your preferred Python IDE. My oldest email is a Yahoo account so I’ll be using that for this example. First thing first we need to import the modules we’ll be using. Add the code below to your file:

import imapclient
import pyzmail
import pprint
import getpass
import imaplib
import datetime
import email

If you’re missing IMAPClient or Pyzmail you can download them using the pip module manager:

 pip install imapclient 
pip install pyzmail36

If you’re using Python 3.6 you’ll need to install the fork of pyzmail above. If you’re using a earlier version you can simply remove the “36”.

Before moving on we’re going to add a single line of code:

imaplib._MAXLINE = 10000000

The default size for searches is 10,000 bytes. For many people with too many email messages this will be too small. To avoid our script erroring as a result we’ll increase the amount 50 10,000,000 bytes.

Now that we’ve imported the necessary modules it’s time to get cracking. First, let’s simply connect to our email by adding the following code to your file:

email_address = input("Enter Email Address:")
email_pass = getpass.getpass("Enter Password:")

imap_obj = imapclient.IMAPClient('', ssl=True)

imap_obj.login(email_address, email_pass)


Let’s break it down:

email_address = input("Enter Email Address:")
email_pass = getpass.getpass("Enter Password:")

You shouldn’t insert passwords directly into your code. In this case, we’re simply asking the user to input the email and password each time. We’re using the getpass module to obfuscate the password from any potential prying eyes.

imap_obj = imapclient.IMAPClient('', ssl=True)

The imapclient.IMAPClient() function creates an IMAPClient object which connects to the IMAP server using the address parameter (Yahoo’s IMAP server in this case). Most email providers require SSL or TLS so we add ssl=True. We’ll use our newly created imap_obj with various IMAPClient methods, including the next line of code below:

imap_obj.login(email_address, email_pass) 

Next we simply pass the email and password provided by the user as strings into the login() function which will attempt to login.

Now run the script. If receive an error you may want to try the following:

  • Double-check to make sure the email and password you used are correct
  • Make sure IMAP is allowed
  • Allow access from less secure devices
  • Check “Secure Account” if you’re using Gmail

If all went well you should see something like this:


Now that we can now connect to the IMAP server let’s see what mailboxes are available by adding the code below:

email_folders = imap_obj.list_folders()

Your script should output something like this:

[((b'\Junk', b'\HasNoChildren'), b'/', 'Bulk Mail'),
((b'\Archive', b'\HasNoChildren'), b'/', 'Archive'),
((b'\Drafts', b'\HasNoChildren'), b'/', 'Draft'),
((b'\HasNoChildren',), b'/', 'Inbox'),]

More or less depending on what all folders you have in your inbox.

The list_folders() method returns a tuple collection with nested indexes. All we want is the folder’s full name so that we can later pass it to the search()method. In this case it would be the Bulk Mail, Archive, Draft, and Inbox folder names. If you’re using Gmail some folders names may be preceded by “[Gmail]/”. This is part of the full folder name.

We can use indexing to make the output more readable by replacing the pprint function with the code below:

for i in range(0, len(email_folders)):

Which should in turn output something like this:

Bulk Mail

Let’s go ahead and add the next line of code:

imap_obj.select_folder('Inbox', readonly=True)

We pass the string of the inbox we want to search to the select_folder() method. In this case let’s just search through the inbox folder. So we don’t accidentally delete or otherwise mess with anything we’ll use the readonly=True parameter. They will also not be marked as read.

Searching a Folder

Now that we’ve selected a folder let’s see look at some emails adding the code below:

email_uids =['ALL'])

Your output may look something like this:

What a messy list of numbers. Each email is represented by a Unique Identification Number (UID) that is given in ascending order and will be unique to your account. These UIDs are in effect the emails and we can pass them to various methods to fetch email data. They are returned by the search() method as a list.

In the example above the ['ALL'] IMAP search key was used with the search() method. This returns all messages in the folder selected when we used the select_folder() method.

There’s a number of IMAP search keys available for use to narrow down your results if everything is a little to broad for your tastes. For example, if we wanted to pull only emails received since May 3rd 2017 we could use the following code:

email_uids =['SINCE',, 5, 3)])

You could also use BEFORE instead of SINCE or combine them like so to get all emails between May 3rd 2017-May 3rd 2018:

email_uids =['SINCE',, 5, 3), 'BEFORE',, 5, 4)])

If you’ve looked at Chapter 16 of Automate the Boring Stuff you may notice that it uses a different date format, specifically day, month, year with hyphen delimiters. 03-MAY-2017 is an example. This date format no longer works with newer releases of IMAPClient which is why we’re using constructor.

Also, on the subject of dates while SINCE includes the day provided BEFORE does not. As a result I’ve had to use 'BEFORE',, 5, 4) to capture May 3rd.

But Wait, That’s Not All!

There’s even more search key goodness to make us of. Here’s a handy list from Automate the Boring Stuff:

Search Key Meaning
‘ALL’ Returns all messages in the folder . You may run in to imaplib size limits if you request all the messages in a large folder.
‘BEFORE date
‘ON date
‘SINCE date
These three search keys return, respectively, messages that were received by the IMAP server before, on, or after the given date .
‘SUBJECT’, ‘string
‘BODY’, ‘string
‘SINCE date
Returns messages where string is found in the subject, body, or either, respectively . If string has spaces in it, then enclose it with double quotes: ‘TEXT “search with spaces”‘ .
‘FROM’, ‘string
‘TO’, ‘string
‘CC’, ‘string
‘BCC’, ‘string
Returns all messages where string is found in the “from” emailaddress, “to” addresses, “cc” (carbon copy) addresses, or “bcc” (blind carbon copy) addresses, respectively . If there are multiple email addresses in string, then separate them with spaces and enclose them all with double quotes:  ‘CC “”‘ .
Returns all messages with and without the \Seen flag, respectively . An email obtains the \Seen flag if it has been accessed with a fetch() method call (described later) or if it is clicked when you’re checking your email in an email program or web browser . It’s more common to say the email has been “read” rather than “seen,” but they mean the same thing .
Returns all messages with and without the \Answered flag, respectively . A message obtains the \Answered flag when it is replied to .
Returns all messages with and without the \Deleted flag, respectively . Email messages deleted with the delete_messages() method are given the \Deleted flag but are not permanently deleted until the expunge() method is called (see “Deleting Emails” on page 375). Note that some email providers, such as Gmail, automatically expunge emails .
Returns all messages with and without the \Draft flag, respectively . Draft messages are usually kept in a separate Drafts folder rather than in the INBOX folder .
Returns all messages with and without the \Flagged flag, respectively . This flag is usually used to mark email messages as “Important” or “Urgent .
Returns all messages larger or smaller than N bytes, respectively .
‘NOT’, ‘search-key Returns the messages that search-key would not have returned .
‘OR’, search-key1, search-key2 Returns the messages that match either the first or second search-key

Automate the Boring Stuff With Python by Al Sweigart is licensed under CC BY 3.
Edited several rows for changes in input syntax and also clarity. See IMAPClient documentation for version history.

Let’s get into some more examples! However, please note I haven’t tested every IMAP Key with every email provider.

email_uids =['ANSWERED', 'BEFORE',, 5, 3)])

email_uids =['LARGER', 100])

email_uids =['FROM', ''])
print("You have received", len(email_uids), "email from")

email_uids =['OR', 'FROM', '', 'FROM', ""])

The Finale?

Hopefully options shown in the previous section are helpful. For now I’m going to search my entire inbox as I want to have a full list of look over. I’ll go over two ways to go about this.

Method one:

emails_from_address = []

progress_uid = 1

for i in email_uids:
    print(progress_uid, "of", len(['ALL'])), end="\r")

    duplicate_temp = False

    temp_rawmessage = imap_obj.fetch([i], ['BODY[]', 'FLAGS'])
    temp_message = pyzmail.PyzMessage.factory(temp_rawmessage[i][b'BODY[]'])
    temp_fromraw = temp_message.get_addresses('from')
    temp_from_address = temp_fromraw[0][1]
    for i2 in emails_from_address:
        if temp_from_address == i2:
                duplicate_temp = True

    if duplicate_temp == False:

    progress_uid = progress_uid + 1

for i in emails_from_address:


In short, we’re iterating through every UID from the email_uid list. We echo the script’s current progress. duplicate_temp is initially false. After parsing the message we check the temp_from_address by looping through the email_from_address list. If it’s already in the list we don’t add it to the list. If it’s unique, we add it to the list and continue increasing our progress by one email. At the end we iterate through the unique emails and logout of the IMAP server.

temp_rawmessage = imap_obj.fetch([i], ['BODY[]', 'FLAGS'])

The fetch() method does what it says on the tin and fetches the actual email’s content. The first argument [i] is the UID we’re passing from email_uid list that we created earlier. The second is the['BODY[]'] which is the body of the email in all it’s RFC 822 glory as a defaultdict. We’ll look more at this in a moment.

temp_message = pyzmail.PyzMessage.factory(temp_rawmessage[i][b'BODY[]'])

Using the pyzmail module we can make the raw email data a PyzMessage object which allows us easily pull certain data from the email body, in this case we’re pulling the sender’s email address:

temp_fromraw = temp_message.get_addresses('from')
temp_from_address = temp_fromraw[0][1]

The get_addresses('from') method returns the from address as a list containing both the sender’s name and address. In this case I only wanted the address. You could also use (‘to’), (‘cc’), and (‘bcc’).

We can also get the subject and the body of the email using the following line of code:

# Subject

#HTML body

# Text body

Getting the body of the email is evidently a little more involved. Emails can be sent as HTML, plaintext, or both. A solution could be doing something like this:

if temp_message.text_part != None:
    temp_email_body = temp_message.text_part.get_payload().decode(temp_message.text_part.charset)
    temp_email_body = temp_message.html_part.get_payload().decode(temp_message.html_part.charset)

Method Two:

We can also use the email library to parse our emails into something intelligible for us mere mortals.

email_uid = 5

raw_msg = imap_obj.fetch([email_uid], 'RFC822')
email_message = email.message_from_bytes(raw_msg[email_uid][b'RFC822'])

In the above example we add 'RFC822' as our data perimeter the parameters in the fetch() method. Then we pass the fetched message, the UID, and the b' bytes prefix to the message_from_bytes() method.

Our email_message variable is a email.message.Message class which means we can request some information from it. For example:


You could also loop through numerous emails using the following:

for uid, message_data in imap_obj.fetch(email_uids, 'RFC822').items():
    email_message = email.message_from_bytes(message_data[b'RFC822'])

When we call on the fetch() method it returns both the UID and the email data that we can conveniently pass through to the message_from_bytes().

I hope something I’ve gone over is helpful and sparked an interest in doing something with Python and emails.