Friday, August 26, 2011

Hacking an LED POV Fan



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.

24 comments:

  1. I just got one of these fans at a convention =) thanks for your experimenting on this!

    ReplyDelete
  2. FYI for anyone else using the latest Arduino IDE 1.0.1 I had to find/replace the Wire.send with Wire.write.

    ReplyDelete
  3. I'd really like to try this myself. My work just received a bunch and a few of us were wondering on how we can 're-program' since it is a 'Programmable' fan.

    My question is, will the Arduino Uno - R3 be a good selection? I'm in no way a programmer, but I understand what you've done here. I just need to know what is a good board to use, and install the required files on a Windows 7 computer.

    Thanks and look forward to hearing back from you soon!

    ReplyDelete
    Replies
    1. @Matt J -- Sure, any Arduino will work just fine. I used a Duemilanove with the Arduino 0022 version of the IDE because that's what I had at the time. I haven't tried anything newer, but if you see Colyn1337's comment above, it looks like you'll need to make some tweaks to get it to work with the newer IDE. It should be pretty straight forward--let me know if you have any issues.

      Delete
  4. Greetings,

    I found your post here after Googling how to hack this awesome little fan! I am NOT a programmer and have little working knowledge of coding, etc. but I'm an ace at following instructions. I have Raspberry Pi but not Arduino. Could I use that instead? Like I said, I'm admitting I'm a noob in this. If I can use Raspberry Pi that would be awesome. I don't want to buy a board for a free fan.

    ReplyDelete
    Replies
    1. Anything with an I2C interface could be used to reprogram the fan. All I'm doing is sending bytes over I2C to the EEPROM chip on the fan. I don't have a Rasberry Pi, but the links below would be where I would start if I were to replicate this on it:

      http://www.bootc.net/archives/2012/05/19/i2c-and-the-raspberry-pi/

      http://learn.adafruit.com/adafruit-raspberry-pi-educational-linux-distro/overview

      Delete
  5. Hey, thanks for this! Just tried it out and I can get up to 1 character to display. Adding any more and the thing goes haywire. Has that ever happened on your end ?

    =======================
    Wire.beginTransmission(0x50);
    Wire.write(0);
    Wire.write(1);
    Wire.write(1);
    Wire.write(0x00);//M
    Wire.write(0xFD);//M
    Wire.write(0xE3);//M
    Wire.write(0xFD);//M
    Wire.write(0x00);//M
    Wire.endTransmission();
    =======================
    The abobe works but adding another character and incrementing Wire.write(1); to (2), and i get a whole lot of gibberish.

    Any ideas?

    ReplyDelete
  6. Ok, it took me a little while to get the required files, but hopefully this post will help someone.

    The octave version needed is 3.0.2, newer versions error out on the script.
    For Arduino, version 22 or 23 seem to work fine.

    Octave is available here.

    http://sourceforge.net/projects/octave/files/Octave%20Windows%20binaries/

    Arduino is available here.

    http://arduino.cc/en/Main/Software

    Good luck!

    ReplyDelete
  7. Hello, I have a couple of these fans as well and have found there are some differences between models. Mine Has Blue LEDs and the PCB layout is different with the EPROM on the "West" side of the PCB as opposed to being just above the header connector. Also my header pinout is different from Left to right:
    Pin 1 - SCL
    Pin 2 - SDA
    Pin 3 - B1(??)
    Pin 4 - GND
    Pin 5 - VCC

    My guess on the mysterious B1 pin which appears to route to the glop drop chip and is logic high when powered up is that it holds the other bus master (The uC under the glop drop_ in Reset when pulled low to prevent contention on the I2C bus. All I know is that the Eprom is not responding to the address reads with just SCL and SDA connected. I have a MSO2024 with I2C analyzer option connected and see activity at power on, which is from the Fan's uC. That also gave away the address. I then noticed that my Bus Pirate is fudging 8-bit addresses the wrong way. For example the 24C02N(only 2Kbit on my fan) is set to address 1010000(R/W) which shows up as 0x50 when the onboard uC is accessing. If I use address 0x50 on the Bus Pirate, the analyzer shows 0x28. So I had to shift the address left and use 0xA0 to get the analyzer to show accesses to 0x50. Still no Ack though. I have a delicate array of probe pins which are a bit small for the Bus Pirate headers plus the scope probes with one clipped on and the other held by hand, so the whole thing is kind of a mess. I need to prepare a header with a breakout board so I can continue to experiment hands free. It also helps to pop the hub off of the fan as the circuit stays with the Propellor, this keeps the thing from trying to spin when you power it up externally. Once I get single byte access working, I'll have to learn how to script up EEPROM Dumps and Loads as I just bought the Bus Pirate and am learning how to use it on this project.

    ReplyDelete
  8. OK I figured out how to program the Blue ones. The pinout is from left to right with the connector facing south on the propellor hub: SCL, SDA, B1, GND, VCC. B1 needs to be grounded to get control of the EEPROM from the uC. I made a cool programming fixture and it works with my Bus Pirate.

    The programming of the blue ones seems simpler - ASCII encoded, not BitMapped like the red one above. You start right off with the "Screens" no need to count screens. Each word has a prefix byte - the upper nibble is the length in characters starting at 0=1 and going through F=16(16 is the max and fills the circular "Screen"). The lower Nibble is the display Style with the following encodings:

    0= Don't Display at all(Not very useful)
    1-3 = Display Once for 6 seconds or so.
    4-6 = Flash Three Times
    7 = Draw clockwise, then pinwheel entire word, then erase counterclockwise.
    8 = same as 7 with faster pinwheel
    9 = same as 7 with fastest pinwheel
    A = Grow word from center, then pinwheel, then shrink to center.
    B = Same as A with faster pinwheel
    C = Same as A with Fastest pinwheel
    D = Flash 3 times, then pinwheel, then grow, pinwheel, shrink(Combo of everything)
    E = Same as D with faster pinwheel
    F = Same as E with fastest pinwheel

    At the end of the original program was a bunch of zeros then a couple of stray bytes. Those bytes may or may not be the command to repeat the whole operation from the first screen. I need to do some more investigating on properly terminating the "program"

    Note the ASCII character set has some custom characters. I found a Spade Symbol and a Face by accident. Lowercase chars exist, but are hard to read. I'll do a sweep of the char set when I have more time. For now, I made a birthday message for my wife to surprise her with as she was the original recipient of these fans from a job fair and they had a company name programmed into them originally.

    This was alot of fun! I Also found out my second fan is like the original poster's Red with the same PCB layout, so now I get to try something different! I'm assuming I can do bitmapped graphics on this one?

    ReplyDelete
    Replies
    1. Hi MacDoogie. I have one of these blue ones and I am trying to get it to work. Could you possibly post a sample command string that you used to program yours along with any special character ids that you found?

      Thanks!

      Delete
  9. Got My Red one reprogrammed now! That one works just like the OPs pictured above. It is bitmapped characters and there doesn't appear to be a way to program in the different display styles like the blue one. Maybe there's something encoded in the MSB or in the bytes following the last screen? I play around more when I have the time and report back if I find anything interesting. Now I'm wishing my wife would have picked up more of these at her career fair :D

    ReplyDelete
  10. Hey I need some help, I have some of these fans and when I took them apart, I am not seeing an EEPROM or any chip for that matter. It also does not have a connector port either...
    Any suggestions? I will try to post a picture of the board, the led strip was tore off and I removed the black blob to see if it was under there... it wasn't. =(
    I am going to post a picture at my website bc I don't know how to post it here.

    My Website is www.designdrs.com
    you can respond on here or send me an email to:
    matt@designdrs.com

    PLEASE HELP!!!!
    Thanks,
    Matt

    ReplyDelete
  11. Ok, Here are the links to my pictures of the circuit board so everyone can see it and hopefully figure it out.

    http://i1324.photobucket.com/albums/u620/matt121188/board1_zpsead95d42.jpg

    http://i1324.photobucket.com/albums/u620/matt121188/board2_zps1dcb39a1.jpg

    Thanks!
    Matt W

    ReplyDelete
    Replies
    1. Yours is a completely different type of fan, and it doesn't look like there is a separate EEPROM to store the data. The microcontroller is under that black blob. I don't know much about how that works. I would expect there is some way to flash new firmware to it by something that presses up against all of those open pads, but maybe not. It wouldn't be simple, but it might be doable.

      Delete
    2. see that is what I thought too since I didn't see a eeprom anywhere.

      are you up for a challenge? Would you want to give it a shot if I send you one?

      Delete
    3. Sure. Not making any promises, but I'd be willing to try. I'll send you an email.

      Delete
  12. Not sure if this thread is still being monitored but I am trying to reprogram a POV T808 which has the place for the 4 pin plug. I have found several places that seem to walk you through the writing the code issues but I have yet to find a place to buy the actual device to connect it to my computer. Please advise, I am not technical enough to build what I am seeing in the instructions. Thank You

    ReplyDelete
    Replies
    1. I'm not sure if the fans are the same, but with mine if you position the hub so that the header is on the right side (with the fan pointed at you), the pins are (top->bottom) DATA,Vcc,CLK,GND,PA0. I used jumper wires to connect to a microcontroller development board called Arduino. You can get a newer version of that board here:

      https://www.sparkfun.com/products/11021

      Here are the connections between the fan and the Arduino:
      Fan Arduino
      ----------------
      DATA | Analog 4
      Vcc | 3.3v
      CLK | Analog 5
      GND | GND

      From reading through other comments here, it looks like the software I used has been updated and no longer works directly with the files I have posted. If what the other commenters have said doesn't make sense, let me know and I'll try to post an update that will work the the current versions of Arduino and Octave.

      Delete
  13. I have created a little arduino program that re-programs the fans with a built-in character generator.

    https://github.com/mikeleib/povfan

    ReplyDelete
  14. Zach: can you share the Arduino sketch that dumps the EEPROM? I have a fan with Blue LEDs, but the circuit board is labelled same as yours (Data/Vdd/Clk/Gnd/PA0), want to verify which ROM I have?

    ReplyDelete
    Replies
    1. It was based on the example here: http://arduino.cc/en/Tutorial/MasterReader. If your fan has a bigger EEPROM you'll want to increase the number of loops. Have the serial monitor open so you can see the data when you run it. This was for an older version of Arduino, so I'm not sure if it will work right away, but it shouldn't be too hard to adjust. It won't let me put the less than/greater than signs around Wire.h in the comment box, so make sure you add those.

      #include Wire.h
      int j;

      void setup()
      {
      Wire.begin(); // join i2c bus (address optional for master)
      Serial.begin(9600); // start serial for output
      Wire.beginTransmission(0x50);
      Wire.send(0);
      Wire.endTransmission();
      Serial.println("Reading Memory From Beginning");
      j = 0;
      }

      void loop()
      {
      for (int i= 0; i<32; i++){
      Wire.requestFrom(0x50, 16); // request 6 bytes from slave device #2
      while(Wire.available()) // slave may send less than requested
      {
      char c = Wire.receive(); // receive a byte as character
      Serial.println(c,HEX); // print the character
      }
      delay(500);
      j++;
      }
      Serial.println("Done");
      Serial.end();
      }

      Delete
  15. Hi all,

    I just got the fan 45 series.

    What hardware & software are required to re-program message?

    Thanks.

    ReplyDelete