Resurrected LED panels


 

I just finished building a frame for some resurrected LED panels from a decommissioned super computer. The computer was a CM-5 by Thinking Machines. It has been used at the College of Oceanography and Atmospheric Science at Oregon State for a fairly long while. A few weeks ago, its time came, and we surplussed it. I was able to get the light panels and built a frame for them at my house. This post describes a little about the process and includes some trivia about the CM-5 and the panels themselves.

 

The CM-5 in name alone probably doesn’t resonate with many people, but hopefully you can recognize it in the background of this photo from Jurassic Park:

Now, I don’t want to slide my glasses up my nose an snort, but the way they’re setup in this image is not at all like how would they be setup in real life. The installation engineer that setup ours had to leave half-way through to setup the Jurassic Park set. These are simply the empty chassises with the light panels. The CM-5 was also 3rd in this list of Top Ten Coolest and Most Powerful Supercomputers. The previous link has an image that shows how it would actually be setup. On top of the machine there are huge bundles of wires.

Anyway, I’m not writing this post to discuss the history of the CM-5, at least not that much, so I’ll get on with the LED panel build. It’s a really simple idea; I laid them out on the floor and measured the dimensions of their perimeter. Using these measurements, I built a simple wooden frame out of 1×2″ maple. The width of the panel is considerably less than the width between studs in my wall, so I had to secure it to a single stud on the top and bottom of the frame.

Bottom of frame and backside of one module

The location I chose for the frame covered an outlet; This not only made it easier to route the cord (I didn’t have to make a cutout), but it also looks much cleaner. Of course, I would need a way to turn it on and off. To do this, I chose an X10 transciever/switch. I covered the antenna with shrink-wrap to avoid shorting anything out. Also, I had to turn the outlet in the wall upside down because the X10 module has the plug coming out on the bottom.

X10 module installed in a reversed outlet

The next challenge was securing the power supplies into the frame. The frame was just thick enough to accommodate the supplies, but it left me little room to attach it.

Power supply against frame

There were mounting holes and a small recess in the heat sink, but the holes were far too large to thread to the hardware I was using. I tried to drill and tap new holes for 4-40 hardware.

It broke my tap!

Unfortunately, the heat sink is made of some bizarre metal that really doesn’t like to be tapped (turns out it’s aluminum). It felt very gummy, if that makes any sense. When I tried to unscrew the tap it broke right off. I tried a few different things, including sharpening the other, broken, end of the tap into a new tip. Really none of these things worked. I was practicing on a bad supply, and I decided to just take it apart and see if there was anything else I could do. When I did, I discovered that they used some strange self-tapping 4-40 screws.

Self-tapping 4-40 screws

These screws mostly did the trick.

Mounted power supplies

Once the power supplies were mounted, I attached the frame to the wall, and began wiring. Notice, in the image above, that the output ends of both supplies are near each other. This is because I wanted to use the factory wiring harnesses from the CM-5.

Low voltage DC wiring

Everything on that machine was overbuilt. Each of those supplies can source 30 Amps at 5 Volts. Each panel requires in the neighborhood 5-7 Amps, so there is almost 3x over provisioning. Not only are the supplies overbuilt, but the cabling is also a little over-the-top. It’s really a testament to the scale of the whole machine. While talking about it to those that used it, I often heard “when something costs $20 million, you expect a certain level of quality.”

Testing

Here I am testing the final wiring for a single LED module. The bottom rows are dark, not because they aren’t on, but because they’re painted black. When it’s installed in the computer, you can’t see these rows because they’re covered up, so for some reason they just painted them black. If you look close, you can see though the paint a bit at and see the lights.

Narrow margins

Here, you can see just how tight the fit of everything in the frame really is. There is barely enough room for the power connectors and cables, let alone the power supplies and X10 module.

installing modules

The modules install simply and cover up all the wiring and electronics.

All finished!

Finally, we’re all done!! The whole system looks amazing. One factoid that I find pretty interesting is that the “random and pleasing mode #7” produces exactly the same “random” pattern on every module! Next on the docket: reverse-engineering them to display messages and designs! Also, make sure to see my gallery of the process of surplussing the computer.

Update:

Someone asked about the details of the silkscreen on the LED panel.  Hopefully this image clearly displays how it was made.

20130719-210028.jpg

 

Update 2:

I found some video at work of the CM5 (really, a CM500, but that’s another story) being installed.  I cut it down a bit, it was about an hour long.  I think that it was installed in 1995, but I’m not sure of the date more precisely than that.

At 4:29, you can see the edges of the CPU board.  They each have 8 banks of 4 LEDs.  I took a picture of the edge of the board for those that are curious.

Edge of the CM5 CPU board

Edge of the CM5 CPU board

  1. #1 by Mark on April 12, 2016 - 4:22 pm

    iskunk,

    in the mean time i tried if the cm5 panel can be done with the mentioned 2 LED panels and the smallest/cheapest microcontroller, the arduino nano, or the even cheaper china clones for about 1-2$. Also i added a DS3231 Real Time Clock Module ( ebay 2$) witch is temperature compensated so it is very precise and as a side effect you get a temperature reading from it.I can tell you that this works very well with a fair refresh rate. So you can get a small replica panel for 2×10$ + 2$ +2$ = 24$ with also can display time, date and temperature 🙂

  2. #2 by iskunk on April 12, 2016 - 4:55 pm

    What refresh rate do you get, for a 16×128 LED array?

    (Using a less-expensive controller would be great, but my overriding concern would be no human-perceptible flicker at all. So the refresh rate would need to be in the vicinity of 120Hz or so)

  3. #3 by Mark on April 12, 2016 - 5:10 pm

    dear iskunk,

    on the nano i used this code for the timer interrupt.
    I haven´t measured that the code comments are correct but if it is correct it does 160Hz scan rate. I can confirm that it looks visualy ok, i can see no flickering, and the screen refreshes also look ok and are fast enough. also i can confirm that the nano runs at a maximum of 16 MHz.

    cli(); // clear interrupts
    TCCR2A = 0; TCCR2B = 0; TCNT2 = 0;
    TCCR2B |= (1 << CS12) | (1 << CS10); // Set 1024 prescaler
    // 160Hz scan rate = 10 frames/second = 16 (pairs of) row(s)/second
    OCR2A = 97; // 97 = (16,000,000 / (1024*160)) – 1
    TCCR2A |= (1 << WGM21); TIMSK2 |= (1 << OCIE2A);

  4. #4 by iskunk on April 12, 2016 - 5:31 pm

    Wait, so a 160Hz scan rate, but only 10 frames per second? Do the LEDs stay on for a certain amount of time after they’ve been activated?

    With old-style CRT monitors, the 120Hz refresh rate was needed because the phosphor dots on the screen glow only when the electron beam hits them. You have to scan very quickly, and rely on the human eye’s persistence of vision. (And if you film the monitor with a video camera, you often get flickering, because the refresh and the video scanning get out of sync.

    These LEDs might behave similarly, or they might stay lit for a short period of time to “bridge the gap” to the next refresh. If there’s no such persistence, however, then you’re going to get flicker.

    Even if it’s not noticeable flicker, your eyes still catch it, and it can still bother you. That was the problem back in the day with using 60Hz-refresh monitors all day long; it was murder on the eyes. And even with LEDs—if you trill your tongue while looking at a cheap LED-display alarm clock, you’ll see the digits distort in shape, which occurs because they are flickering at 60Hz.

    If the Teensy can push out ~120 whole frames per second, I’d recommend that. It’s a subtle difference, but it’s worth the extra trouble.

  5. #5 by Matt B on September 1, 2016 - 8:04 am

    Wow amazing work done here! I’m going to build a replica as well now using the Canton F3.75 displays.

    I actually think when its working I might add more features like actual CPU performance, network traffic, etc. But of course Mode 7 will always be my favorite.

  6. #6 by Mark on November 20, 2016 - 4:09 pm

    took some time since i´m a 3d noobie but here is my attempt.

    https://youtu.be/6Ko4qBkEcBM

  7. #7 by hpux735 on November 20, 2016 - 4:13 pm

    Dude! That’s amazing. If you want to add a final touch, on the left side of the first cabinet (looking at the LEDs) Add lights through the panel. That panel was actually smoked plastic and the HDD LEDs shone through. I don’t have a photo of that, unfortunately, though.

  8. #8 by Mark on November 20, 2016 - 4:40 pm

    tnx hpux735, i´m limited by the few pictures i have found online and have never seen the actual machine for myself – if anyone has more material that shows unseen views or obvious errors in the rendering , please let me know – i would like to make it more realistic. The few public relation materials of Thinking Machines are more confusing than helping, sometimes seams are visible and sometimes not – the backside of the machine is barely or never visible at all so i had to build that from some visitors photos from the “frostburg” exibition from a computing museum. Without better reference photos, this is really hard for me to make it better.

    kind regards,
    mark

  9. #9 by hpux735 on November 20, 2016 - 8:02 pm

    Totally. It’s really impressive as it is, I have to say!

  10. #10 by psergiu on March 2, 2018 - 12:22 am

    @hpux735 – can you please measure the spacing between the LEDs and post-it here ? I’d like to build a close replica – but i cannot find the measurements anywhere.
    Thanks a lot !

  11. #11 by NetAgent on June 2, 2018 - 6:56 am

    @Mark

    Any chance we could get a copy of your complete code to run a Arduino with two of the 2 Canton LED Display 16×64 panels?

  12. #12 by Mark on June 2, 2018 - 7:25 am

    @NetAgent sure thing. I have to slim it down a litte since mine is running a lot of more patterns.

    Btw, it does not depent how many 16×64 panals you add, they all show the same thing (daisy chained).

  13. #13 by Mark on June 2, 2018 - 7:49 am

    this should do it on a teensy 3.1 with canton displays. The Interrupt timer is different to Arduino. I am sure i have also Arduino Code but not with Canton Display. The function it self works on both completly the same.
    Let me know if something is not working in your environment.

  14. #14 by Mark on June 2, 2018 - 7:50 am

    #include <math.h>

    #define COLUMNS 32
    #define HEIGHT 64

    byte ar[3][66][66];

    // Connections to board

    const byte data_R2=11;

    const byte latchPin=8;
    const byte clockPin=12;
    const byte data_R1=10;
    const byte en_74138=2;
    const byte la_74138=3;
    const byte lb_74138=4;
    const byte lc_74138=5;
    const byte ld_74138=6;

    byte ScanRow = 0;
    unsigned long counter;

    byte buffer[128] = { // Display buffer (which is scanned by the interrupt timer) of 8×32 bytes or 16×16 bytes
    0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

    };

    // set a single pixel on or off
    void setP(byte x, byte y, byte colour) {

    bitWrite(buffer[(y*COLUMNS)+(x>>3)],7-(x&7),colour);

    };

    void shiftOut(byte row) { // fast routine to shove out 8 columns into two rows via board’s shift registers
    unsigned int rtimesc = row*COLUMNS;
    for(byte column=0;column<COLUMNS;column++) {
    unsigned int index = column + rtimesc;
    for(byte i=0;i<8;i++) {
    PORTB &=~(3<<(data_R1-8)); // data_R2 is LOW; data_R1 is LOW;
    PORTB &=~(1<<(clockPin-8)); // digitalWrite(clockPin,LOW);
    PORTB |= !((buffer[index]>>(7-i)) & 0x01) << (data_R1-8); // top set of rows
    // if (column >= 8) PORTB |= !((buffer[index+128]>>(7-i)) & 0x01) << (data_R2-8); // bottom set of rows
    PORTB |= 1<<(clockPin-8); // digitalWrite(clockPin,HIGH);
    };
    };
    };

    // Scan a pair of rows on to the display from “buffer” via the interrupt
    void pnt(){
    cli();
    // digitalWrite(en_74138, HIGH); // Turn off display

    shiftOut(ScanRow); // Shift out 8 (or 16) columns
    digitalWrite(latchPin, LOW);
    digitalWrite(latchPin, HIGH);
    PORTD = (ScanRow << 3); // Highlight row: pins 3 4 5 6 (la_74138 lb_74138 lc_74138 ld_74138)

    // digitalWrite(en_74138, LOW); // Turn on display
    ScanRow++; // Do the next pair of rows next time this routine is called
    ScanRow &= 0x0F;
    // if (ScanRow==16)ScanRow=0;
    sei();
    // digitalWrite(en_74138, HIGH);

    };

    void setup() {

    pinMode(latchPin,OUTPUT); pinMode(clockPin,OUTPUT);
    pinMode(data_R1,OUTPUT);
    //pinMode(data_R2,OUTPUT);

    pinMode(en_74138,OUTPUT);
    pinMode(la_74138,OUTPUT); pinMode(lb_74138,OUTPUT);
    pinMode(lc_74138,OUTPUT); pinMode(ld_74138,OUTPUT);

    digitalWrite(en_74138, LOW);
    digitalWrite(data_R1, HIGH);
    //digitalWrite(data_R2, HIGH);

    //setTime(1421491560);
    // Teensy3Clock.set(1454166313); // set the RTC
    // setTime(15,02,19,9,2014); // h:m:s:day:month:year

    setSyncProvider(getTeensy3Time);
    setSyncInterval(9000);
    myTimer.begin(pnt, 800);//1200 old //200 optimum //400

    };

    /*******************************************************************************/
    // main loop
    /*******************************************************************************/
    void loop(){

    clearArrays();

    cm5mode5(16);

    }

    byte getPixel(byte x, byte y, byte whichArray){

    unsigned long long temp = (1ULL << y);
    if(array[whichArray][x] & temp){
    return 1; //the yth bit was set, so return 1.
    }
    else {
    return 0; //the yth bit was clear, so return 0;
    }
    }

    void setPixel(byte x, byte y, byte whichArray, byte pixel){

    if (x>=0 && x<=WIDTH && y>=0 && y<HEIGHT){
    unsigned long long temp = (1ULL << y);
    if (pixel){
    array[whichArray][x] |= temp;
    }
    else {
    array[whichArray][x] &= ~temp;
    }
    }
    }

    #define RNUM_SEED 0xBAD
    static uint16_t rnum = RNUM_SEED;

    static uint16_t get_random_bit(void)
    {
    #define X rnum

    uint16_t lfsr_bit = ((X >> 0) ^ (X >> 1) ^ (X >> 3) ^ (X >> 12)) & 1;

    uint16_t rand_bit = (X | (X >> 2)) & 1;

    #undef X

    rnum = (lfsr_bit << 15) | (rnum >> 1);

    return rand_bit;

    }

    void paintCM(){
    int i, j;bool pixel;

    for(j=0; j<HEIGHT; j++) { for(i=0; i<WIDTH; i++ ) {
    if (getPixel(i,j,0)==0) setP(i,j,0);else setP(i,j,1);
    }}
    }

    void cm5mode5(int w){//cm5 mode 7 variable width

    byte pixel; int i,j;

    uint16_t bit;

    int count=0;
    clearArrays();

    while (count<150){
    clearArrays();
    for (i = 0; i < 16; i++){
    for (j = 0; j < 16; j++){
    uint16_t bit_lower = get_random_bit();
    uint16_t bit_upper = get_random_bit();
    if (bit_upper==0){ setPixel(i,j,0,1);setPixel(i,j+32,0,1); setPixel(i+48,j,0,1);setPixel(i+48,j+32,0,1); }
    if (bit_lower==0){ setPixel(i,j+16,0,1);setPixel(i,j+48,0,1); setPixel(i+48,j+16,0,1);setPixel(i+48,j+48,0,1); }
    }
    }
    if (count>w){ paintCM(); delay(200);}
    count++;

    }
    }

  15. #15 by NetAgent on June 2, 2018 - 8:35 am

    Thanks for the quick reply.

    When I try to compile your code in the Arduino IDE with Teensyduino added I get a few errors:

    “file.ino:42: error: too many initializers for ‘byte [128] {aka unsigned char [128]}”

    If I change “buffer[128]” to “buffer[512]” it continues on to erroring with “not declared in this scope” errors for getTeensy3Time, setSyncProvider, setSyncInterval, myTimer, clearArrays, array, & WIDTH.

    I am brand new to programming with the Arduino IDE so I am not sure if I am missing something. Fresh Arduino IDE install and Teensyduino software added with no other files or modifications done.

    Thanks.

  16. #16 by Mark on June 2, 2018 - 8:46 am

    Do you have a teensy ? or an arduino only ?

    If you have only an arduino i will check for a complete working code, without the display. when this works, we continue on the display-part…

    How much RAM has your microcontroller ?

  17. #17 by Mark on June 2, 2018 - 9:04 am

    this should work for both, it compiles fine now.

    let me know if it compiles on your system.

    If it works, we have to talk about the display, do you have a working library for it that can set pixels on and off?

    #include <TimerOne.h>

    #define HEIGHT 16
    #define WIDTH 64

    #define HEIGHTR 64
    #define WIDTHR 16
    byte row=0;

    long long array[2][16];

    byte getPixel(byte x, byte y, byte whichArray){

    unsigned long long temp = (1ULL << y);
    if(array[whichArray][x] & temp){
    return 1; //the yth bit was set, so return 1.
    }
    else {
    return 0; //the yth bit was clear, so return 0;
    }
    }

    void setPixel(byte x, byte y, byte whichArray, byte pixel){

    unsigned long long temp = (1ULL << y);
    if (pixel){
    array[whichArray][x] |= temp;
    }
    else {
    array[whichArray][x] &= ~temp;
    }
    }

    //=== S E T U P ===

    void setup() {

    }

    //=== L O O P ===

    void loop() {

    cm5();

    }

    void HardwarePlot(int x, int y, bool State)
    {

    //Display specific Code goes here:

    // SetPixel x/y to on/off by boolean State
    }

    void clearArray(byte no) {
    byte i, j; for(j=0; j<HEIGHT; j++) { for(i=0; i<WIDTH; i++ ) { setPixel(i, j, no, 0); } }}

    #define RNUM_SEED 0xBAD
    static uint16_t rnum = RNUM_SEED;

    void paintCM5(){
    int i, j;bool pixel;

    for(i=0; i<16; i++ ) { for(j=0; j<32; j++) {

    pixel=getPixel(i,j,0);

    if (j & 4) {
    if (pixel==1 ) {HardwarePlot(j,i,0); }
    else {HardwarePlot(j,i,1); }
    }
    else if (pixel==1 ) {HardwarePlot(j,15-i,0);

    }
    else { HardwarePlot(j,15-i,1);

    }

    }
    }
    }

    static uint16_t get_random_bit(void)
    {
    #define X rnum

    uint16_t lfsr_bit = ((X >> 0) ^ (X >> 1) ^ (X >> 3) ^ (X >> 12)) & 1;

    uint16_t rand_bit = (X | (X >> 2)) & 1;

    #undef X

    rnum = (lfsr_bit << 15) | (rnum >> 1);

    return rand_bit;

    }

    void cm5() {

    int pixel,i,j;
    int count=0;
    clearArray(0);
    //cm5 mode 7
    uint16_t gen_bit;

    uint16_t rnum = 1;

    while (count<3080){

    for(j=31; j>=0; j-- ){

    uint16_t bit = get_random_bit();

    if (bit==0) { setPixel(16,j,0,1);
    }
    }

    for(j=0; j<32; j++) { for(i=0; i<16+1;i++){
    pixel=getPixel(i+1,j,0); if(pixel==1)setPixel(i,j,0,1);else setPixel(i,j,0,0); } }

    count++;

    if (count>0){
    paintCM5();delay(131);}
    }
    }

  18. #18 by NetAgent on June 2, 2018 - 4:01 pm

    I currently have the a Arduino Uno R3 and a Teensy 3.2 on order. The LED panels are still being shipped from China.

    The code complies and uploads fine after installing the TimerOne library. I am not sure exactly which LED library I will need to set pixels on and off, there are several different ones online.

  19. #19 by Mark on June 2, 2018 - 11:40 pm

    ok great, when you have the panels, start working with only one of them and continue when everything is working right.

    These cheap panels are not intelligent and multiplexed, so you can´t simply turn individual LEDs on and off, you have to refresh the panal continuely from an memory buffer.

    The Uno has only 2 K Ram if i see correctly. This is the reason why in the code is this array trickery with long long array[2][16].

    This way it can be done, also on the Arduino Nano, which i was running it with.

    If you go with the Teensy3, it has plenty of memory, you can delete these set/get Pixel functions completly and directly write to the buffer of the used library for the display.

    You might have to search on the internet/manufacturer to find a working democode for you panel, there a minor differences.

    Let me know when your shipment arrives, i will help you to get it running.

  20. #20 by NetAgent on June 11, 2018 - 11:11 pm

    Okay I finally received my Canton LED display panels.

    I put together your code with some other code I found for the display. The display appears to be working however there is a lot of flicker on some of the corner lights, not sure why.

    Video:
    https://youtu.be/ez2HgJFPICY

    Code:
    https://pastebin.com/RCdyZKNC

  21. #21 by NetAgent on June 11, 2018 - 11:16 pm

    Forgot to say, the code will compile fine for Arduino but will not compile with the Teensy 3.2.

    I put put together some of your other code and go it to compile but there is severe flickering. Code here: https://pastebin.com/VS3BrPS0

  22. #22 by NetAgent on June 11, 2018 - 11:21 pm

    Looks like my first post didn’t get sent correctly:

    Okay I finally received my Canton LED display panels.

    I put together your code with some other code I found for the display. It compiled under Arduino but would not compile for Teensy 3.2. The display appears to be working however there is a lot of flicker on some of the corner lights, not sure why.

    Video:
    https://youtu.be/ez2HgJFPICY

    Code:
    https://pastebin.com/RCdyZKNC

    I used some other code and got it to compile for Teensy 3.1 it looks with be working, but the flicker is very severe. Code here: https://pastebin.com/VS3BrPS0

  23. #23 by Mark on June 12, 2018 - 2:13 am

    AFAIK for Teensy you have to use a different Timer Library.

    When you get the flickering – if you comment out the CM5 Code, are you able to Control single LEDs with the Library ? Or do you get flickering all the time?

  24. #24 by NetAgent on June 12, 2018 - 5:06 am

    Commenting out the CM5(); still causes random LEDs to flicker.

    When received my Canton LED display panels I put together your code with some other code I found for the display. It compiled under Arduino but would not compile for Teensy 3.2 (I used a different code from my last post). The display appears to be working however there is a lot of flicker on some of the corner lights, not sure why. See video link in the top of the code.

    https://pastebin.com/RCdyZKNC

  25. #25 by Mark on June 12, 2018 - 6:09 am

    You are already very close!

    I’m pretty sure i know what is going wrong: every cycle, a new random value is added to earch row, as the 17th element and should be invisible – you only see led 1-16 of the row.

    then, after the new element is added, the whole row is shifted to the left, so that the 1st element is lost and the new element appears in the 16th Position.
    The Problem is that you Code/Display wants to paint in the element, while it should not be visible, yet. That is most certain the main Problem.
    That also was a reason, to use an own framebuffer since the framebuffer for the real Display only has space for exactly the real LEDs and no additional space to shift Pixels in.

    I will look into your complete Code later today.

  26. #26 by Mark on June 12, 2018 - 2:09 pm

    lets concentrate on the pattern 16×32 since this is all, only repeated for all panels.

    please try this:

    void setPixelunsafe(byte x, byte y, byte whichArray, byte pixel){

    unsigned long long temp = (1ULL << y);
    if (pixel){
    array[whichArray][x] |= temp;
    }
    else {
    array[whichArray][x] &= ~temp;
    }
    }

    void cm5() {

    int pixel,i,j;
    int count=0;
    clearArray(0);
    //cm5 mode 7
    uint16_t gen_bit;

    uint16_t rnum = 1;

    while (count<3080){

    for(j=31; j>=0; j-- ){

    uint16_t bit = get_random_bit();

    if (bit==0) { setPixelunsafe(16,j,0,1);
    }
    }

    for(j=0; j<32; j++) { for(i=0; i<16+1;i++){
    pixel=getPixel(i+1,j,0); if(pixel==1)setPixelunsafe(i,j,0,1);else setPixelunsafe(i,j,0,0); } }

    count++;

    if (count>0){
    paintCM5();delay(131);}
    }
    }

  27. #27 by Mark on June 12, 2018 - 2:14 pm

    change plot to your hardware drawing…x/y might be reversed.

    void paintCM5(){
    int i, j;bool pixel;

    for(i=0; i<16; i++ ) { for(j=0; j<32; j++) {

    pixel=getPixel(i,j,0);

    if (j & 4) {
    if (pixel==1 ) {Plot(j,i,0); Plot(j+32,i,0); }
    else {Plot(j,i,1); Plot(j+32,i,1);}
    }
    else if (pixel==1 ) {Plot(j,15-i,0);
    Plot(j+32,15-i,0);

    }
    else { Plot(j,15-i,1);
    Plot(j+32,15-i,1);
    }

    }
    }
    }

  28. #28 by NetAgent on June 12, 2018 - 8:57 pm

    Ok, I updated the code from your instructions.

    On Arduino the pattern is appears cloned after half the panel. I still see the same flickering on the left and right side of the panel has shown on the video. https://youtu.be/ez2HgJFPICY

    On Teensy the severe flicking remains. https://youtu.be/9cinW4gzyfM I suspect it may be a buffer issue or setting problem with refreshing. If I reverse the pattern so more LEDs are on, the flickering goes away for the most part.

    I’d like to focus first on the Arduino code first, I think it may be a panel buffer/refresh issue, I’m not sure. Unfortunately I do not have enough experience with the code and hardware to tell.

    On the video you previously linked with two Canton panels working, did you use a Arduino or Teensy? Either way we seem to have the same hardware so testing your complete and original code you used during that might help as for the moment I’m just trying to duplicate your video. I’m hoping it’s not a problem with the panel itself. I tested both panels and both have the same flickering on the sides. All 9 pins are connected correctly.

  29. #29 by Mark on June 13, 2018 - 6:21 am

    I am sure it is a Buffer or logic problem in the Code. I will try what happens if i load your Code on an Arduino Nano v3 with Canton Display. Maybe i get the same error, then it will be easy to fix.

    It is supposed that the pattern is repeated every 64 rows. That is exactly what the original Machine does. You chain as many Panels as you like, you adress only one Panel in the Code, all other Panels will also Show the same Content.
    If you want to adress the Panels individualy, for displaying other things as CM5 Patterns, i strongly suggest to use the Teensy for the higher Memory and Speed requirements.

  30. #30 by Mark on June 13, 2018 - 7:40 am

    Sorry, i made a mistake, the pattern is repeated every 32 rows. not 64 rows.

  31. #31 by Mark on June 13, 2018 - 7:49 am

    I have 2 pins connected differently, can you quickly check this ?

    i will try to recreate your environment and see if i get the same wrong result.

    my connection is:

    // Connections to board
    const byte latchPin=8;
    const byte clockPin=12;
    const byte data_R1=10;
    const byte data_R2=11;
    const byte en_74138=2;
    const byte la_74138=3;
    const byte lb_74138=4;
    const byte lc_74138=5;
    const byte ld_74138=6;

    yours is // Connections to board
    const byte latchPin = 8;
    const byte clockPin = 12;
    const byte data_R1 = 11;
    const byte data_R2 = 10;
    const byte en_74138 = 2;
    const byte la_74138 = 3;
    const byte lb_74138 = 4;
    const byte lc_74138 = 5;
    const byte ld_74138 = 6;

  32. #32 by Mark on June 13, 2018 - 8:43 am

    ok! This is way overcomplicated but i am sure it will work for you now !! the array mess is because the arduino nano has only 2k ram. you can simplify this code very much if you have more memory!

    if nothing happens, please check that

    const byte data_R1=10;
    const byte data_R2=11;

    pins are connected this way, otherway reverse the 2 pins.

    https://pastebin.com/9zCiRNLc

  33. #33 by NetAgent on June 13, 2018 - 6:50 pm

    Okay, I think I figured out what the issue was:

    These two lines in ISR(TIMER2_COMPA_vect:
    digitalWrite(en_74138, HIGH); // Turn off display
    digitalWrite(en_74138, LOW); // Turn on display

    Should be commented out. Now the display no longer has flickering. I updated the code here https://pastebin.com/cV4aifsz Video here https://youtu.be/slS0B7DZA44

    The panels look good now. There is a few slightly lit LEDs on the panel next to some of the fully lit ones, but it’s not really an issue for me.

    I’ll keep working on the Teensy code to see if I can address the flicking.

    Thank you so much for your hard work making this possible for me to put together!

  34. #34 by Mark on June 14, 2018 - 10:35 am

    YES! :_)

    ..there is always some LED “bleeding”, at least in these cheap panels. You can play with the refresh rate, sometimes this makes it better , sometimes worse.

    The patterns look correct to me.
    To get the visuals closer to the original, there is same space between the 16×32 blocks, so i shifted it in the paint function.. also the lowest block only shows 11 or 12 lines, the other LEDs were invisible because they are completly inside the case.
    …All these things are optional, do it how you like it :_

    And as said, if you have the memory, you can discard the in-between Array/Buffer Tricks and write to a “normal” integer Array, just be sure to add an extra element for shifting in the new pixels. You can then delete the get/set functions and write directly to the simplified buffer and let this paint by the hardware-paint buffer.
    it will make no noticable change, just cleaner code.

    kind regards,
    mark

(will not be published)

Please complete this capcha. I get almost 1000 spam comments a day! * Time limit is exhausted. Please reload CAPTCHA.