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.

Searching emails with Python & IMAP

Finding forgotten accounts With keywords


Cyber hygiene is invaluable, but like many, I don’t remember every throwaway account I’ve made using an alternative email. The method outlined below uses IMAP to search email’s for specific keywords. Of course, the keywords can be anything and used in many different ways.

I was inspired by chapter 16 of Al Sweigart’s book Automating the Boring Stuff with Python: Practical Programming for Total Beginners. Unfortunately, some of the IMAPClient code has become outdated since the book was released resulting in some changes. Sweigart’s book is fantastic and I highly recommend supporting his work with Python 3+.

I’m not a Python developer by trade. If you’re familiar the style guide for Python you may notice some departures from it. I am also not responsible for any user issues.

The Python script uses IMAPClient, pyzmail, email, and a few other libraries to log into an email account, search the inbox, and make a list of all the unique addresses I’ve received emails from.

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 have not added 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.

If you’re using Gmail you may find difficulty connecting even after enabling IMAP. I recommend checking Secure Account. Google may have blocked access and listed your system as a security issue. Several hours after confirming it was me I was able to access my account with the script.

If you’re still unable to connect you may also have to change your settings to allow access from less secure devices. Proceed at your risk. It’s best to turn this back to off after you’re done. If you’re 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:



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.