The company where I work has recently been doing a big push for safety, and last week they passed out small POV fans with sayings like, “Thx 4 Being Safe”, “Being Safe is Cool”, “I'm a Safety Fan”, etc. The fan has clear, flexible plastic blades, and one of the blades has a strip of 7 LEDs in it which are used to display the messages. The fan itself is pretty cool....but I wanted to see if I could make it say something else. The box it came in says "Programmable Message Fan: Model 45 Series". I searched online and found similar fans that were re programmable, but those had 4 buttons for programming in messages, and mine only has an on/off button. This was a very simple project, but it was fun to figure out how something worked and then make it do something else.
There is a removable cap that reveals a 5 pin port, but there was no documentation with the fan indicating what the port was or how to reprogram it. Removing the entire front of the fan reveals a circular PCB.
Whatever microcontroller is being used is under a black blob, but the SOIC-8 IC with a marking on it was a CAT24C04, which is a 4kbit (512 byte) I2C EEPROM. On the back side of the PCB, the 5 pin port was labeled: [DATA, VCC, CLK, GND, PA0], and I confirmed that these pins were connected to the I2C SDA and SCL pins on the EEPROM. The PA0 pin threw me for a little bit, but it is connected to what would be the A0 address pin on the EEPROM for smaller chips in the same line. I'm assuming they use the same fan PCB with the smaller EEPROMs and use the other address bit for some reason. The 512 byte version leaves the A0 pin unconnected and that bit in the EEPROM slave address is reserved for the high bit of the memory address. The smaller EEPROMs can address their entire memory in a single byte, but the 4kbit (and larger) versions require part of the device address for the higher values. The table below (from the EEPROM datasheet) shows how to figure out the I2C slave address. The first 4 bits are fixed, and then A2 and A1 are determined by the state of the corresponding pins. Those pins were connected to ground, so both values were zero. For the CAT24C04, the a8 bit is 0 for bytes 0-255 of the EEPROM, and 1 for bytes 256-511.
The first thing I tried to do was read the data stored on the EEPROM. I have used the Bus Pirate from Dangerous Prototypes in the past for initial I2C prototyping with new chips, but I didn't have any luck with this one....I may have done something bad to my Bus Pirate. After wasting a couple hours on that dead end, I pulled out my Arduino and was immediately able to read the EEPROM. I wrote a short sketch that read out each page of memory and sent it over the serial port to my PC, and then copied all of the data to Excel as a single column. I converted the values to decimal numbers so I could make more sense of them and then tried to figure out the data format. I wrote out each of the messages displayed on the screen and counted their characters. The very first byte of memory contained the number of screens, and the second byte contained the number of characters in that screen. From another post about a similar fan I'd seen that the data was probably stored with 5 bytes per character, and I confirmed that this was the case. So, the first screen initially read “Thx 4 being safe”, which contains 16 characters. The second byte of memory contained the number 16 (0x10), and the following 5x16=80 bytes contained the character codes to display. The very next byte contained the length of the second screen, and that pattern continued to the end. Below you can see the beginning of the data dump.
Once I had read and stored all the data (so I could presumably restore it if I totally messed up the fan), I started experimenting with writing data to the fan. The first thing I did was change the first byte of memory to 1 instead of 6, which caused the fan to only display the first screen. This was what I expected to see. Rather than try to pull the alphabet out of all the character codes on the fan (which would have yeilded an incomplete set anyways), I decided to just figure out how the characters are stored and then create my own set. I changed the second byte of the fan to 1 so that the first screen only displayed a single character, and then experimented with different values until I figured out how they mapped to the LEDs. My basic process was to write all 1's or 0's to different rows/columns, record what happened, and then write new values. After 2 or 3 tests I figured out what was going on. What I found was that each character is 5 LEDs wide and 7 LEDs tall. Each byte of the 5 character bytes represents a single column. In each byte, the LSB is the bottom of the screen, and the MSB is the top. The 8th bit is unused, so I set it to 1 in all of my codes. In each byte, a 1 means the LED is off, and a 0 means an LED is on. The screen also prints from right to left, which means that everything has to be entered in the reverse order to display correctly. To create the characters I drew each letter backward in OOCalc using 1's and 0's, and then converted to the corresponding hex values. I wrote a single test screen to the Arduino by copying each byte by hand into my sketch and sending the appropriate start/stop conditions, and it worked, but it was very tedious and I would not want to enter multiple screens worth of data that way. The example below shows how I created the letter Z. 0's represent an ON LED, and the MSB is ignored. The bottom row automatically converts to the hex value, so all I had to do was enter the 1's and 0's, and I ended up with a table of the alphabet. Once I had the full alphabet, I started trying to write new phrases to the fan.
I probably could have put the character map into my Arduino sketch and had someway to enter words and convert them there, but I thought it would be easier to do the character conversion in some kind of script on my computer first. The result is ugly (and unneccesarily long) Arduino code, but it makes it much faster and easier to input new words. I use Matlab frequently at work for quick data processing scripts, so that was the first thing my mind jumped to. I don't have Matlab at home, but Octave is an open source clone that has most of the major functionality and I decided to give it a try. Basically, all my Arduino sketch does is load/enable the I2C library, send the data, and then blink an LED forever to say that it is done. I cut the sketch into three parts: header, body, and footer. The header and footer are always the same, and the body of the sketch (which sends the data) comes from Octave. The Octave script has a user input section to enter the screen information, and it converts the words to the 5 byte character codes, reverses the order, and then writes them a page at a time to the EEPROM, with the correct # of screen and # of characters bytes as well. I was running on Linux, and the Octave script uses the Linux cat command to combine the header, Octave output, and footer into a single file. If you want to run this on Windows, you'll need to manually combine the files.
Running the Octave script creates a finalPDE.pde file which is what I load onto the Arduino. Only 4 wires are needed (PA0 is unused) for programming: Vcc to 3.3v, GND to GND, DATA to Analog 4, and CLK to Analog 5. The amount of data you can display is limited to 6 screens maximum with 20 characters (including spaces) per screen. The screens wipe across the display in different patterns, and as far as I can tell there is no way to control these from the EEPROM....it's dictated by the MCU. Here's a video of the fan in action (before and after):
I've posted the Octave script, OOCalc file with character codes, and a sample Arduino sketch at https://sourceforge.net/projects/povfan/ if anyone wants to try this for themselves.