Adafruit's 32x32 RGB LED matrix panel

Posted on
  • Hi,

    Has anyone has attempted to use one of this with Espruino, they have libraries built for arduino so I think it should be possible to do it with an Espruino, as an output for projects or to show bmp and animated GIFs, please let me know if anyone has some advance on this.

    Cheers

    Arturo

  • Isn't that just a 32x32 array of WS2812's, just like the RGB123 ones? (only with different spacing and pricing)

    Try talking to them like they were an RGB123 array - note that you wont be able to light up very many of the LEDs at full brightness if you're powering it off of USB; you'll need a real power supply.
    http://www.espruino.com/RGB123

  • You mean these kinds of things? http://learn.adafruit.com/32x16-32x32-rgb-led-matrix/overview

    If it is those, they don't have any built-in controller so you're going to have to write code to scan each pixel out individually. With a bit of work you might be able to get it working, but personally I don't think the result is going to be that great (Espruino isn't designed for bit-bashing data out, so the scan rate won't be that fast and you're unlikely to be able to get more than 1 bit per channel = 7 colours).

    They also seem to be 'end of life' units - I don't think they're being made any more?

    If you haven't already bought one of Adafruit's modules, I'd strongly recommend that you use RGB123 or one of the chinese versions - you'll get full 24 bit colour and they're much easier to wire up and use.

  • Yes, those are the ones I meant, they mentioned on the page that with a 16 MHz Arduino are able to pull 12-bit color, so I think it is doable, I got one, the size was the reason to get it, I wanted something at least 32x32 so I'll try to make it work.

  • The difference is that the 16Mhz Arduino was programmed using inline assembler (if you look at the library source) rather than in JavaScript :)

    Having said that, you could:

    • Compile Espruino with your own C function that would clock out the 6 bits of colour required.
    • Write some inline assembler for Espruino to handle it in Espruino, using the nativeFunction call that'll be appearing in 1v60.
  • Wow. I'm stunned that they're not WS2812's, which would be a cinch to control... At that price, i'd really expect them to be.

  • I would like to play around a little bit with this. RGB123 is too expensive having in mind to play only. What is the "chinese version" of that ?
    I only found this: http://www.banggood.com/MAX7219-Dot-Matrix-MCU-LED-Display-Control-Module-Kit-For-Arduino-p-915478.html for 2.54€ with red LEDs only.

  • The MAX7219 is really neat and easy to chain. There's a thread on it somewhere (although it's gone a bit quiet now).

  • @JumJum I'm playing around with the Adafruit bi-color 8x8 matrix which is pretty cool. About £16 here in the UK, so not overly expensive. I've written a library for it for the Espruino on my other thread, so should be pretty easy to get going.

  • Using the attached as a test image with the following code, I ve got times of 1.43 seconds to fill a screen, however disabling the pin outputs the time falls down to 0.96 seconds, so I am sure more can be done to be optimized probably using assembler as the logic is actually quite simple, I expect this to be useful to someone with more skills that can improve it or changed to make it usable.

    //pin B2 as B1
    //pin B3 as G1
    //pin B4 as R1
    //pin B5 as B2
    //pin B6 as G2
    //pin B7 as G3
    //pin B8 as clock
    //pin b9 as Latch
    //pin b13 as OE
    var TONES=1;
    console.log(getTime());
     var image = require("fs").readFile("GIF/wizardcity00001.bmp", "binary");
    image = image.substr(54,image.length);
    var bytes = new Uint8Array(4096);
    for (var j=0;j<4096;j++){ 
      bytes[j]=Math.round(image[j].charCodeAt()*TONES/256);
    }
    console.log(getTime());
    console.log(process.memory());
    image='';
    console.log(process.memory());
    
    
    function rcolor(x){
      return (TONES*x/256);
    }
    
    function clock(){
      digitalWrite(B8,1);
      digitalWrite(B8,0);
    }
    
    function latch(){
      digitalWrite(B9,1);
      digitalWrite(B9,0);
    }
    
    //function linea(y){
    //  return (y-1)*128;
    //}
    function linea(y){
      if (y==1) {return 0;}
      if (y==2) {return 128;}
      if (y==3) {return 256;}
      if (y==4) {return 384;}
      if (y==5) {return 512;}
      if (y==6) {return 640;}
      if (y==7) {return 768;}
      if (y==8) {return 896;}
      if (y==9) {return 1024;}
      if (y==10) {return 1152;}
      if (y==11) {return 1280;}
      if (y==12) {return 1408;}
      if (y==13) {return 1536;}
      if (y==14) {return 1664;}
      if (y==15) {return 1792;}
      if (y==16) {return 1920;}
      if (y==17) {return 2048;}
      if (y==18) {return 2176;}
      if (y==19) {return 2304;}
      if (y==20) {return 2432;}
      if (y==21) {return 2560;}
      if (y==22) {return 2688;}
      if (y==23) {return 2816;}
      if (y==24) {return 2944;}
      if (y==25) {return 3072;}
      if (y==26) {return 3200;}
      if (y==27) {return 3328;}
      if (y==28) {return 3456;}
      if (y==29) {return 3584;}
      if (y==30) {return 3712;}
      if (y==31) {return 3840;}
      if (y==32) {return 3968;}
    }
    
    function toled(){
      var c=0;
      var v=0;
      var abcd = 0;
      for (var f=0;f<TONES;f++){
        for (var i=32;i>24;i--){
          digitalWrite(B13,1);
          abcd=32-i+1;
          y=linea(i);
          z=y+127;
          w=linea(i-8);
          x=w+127;
          for (var k=y;k<z+1;k=k+4){
            //if(rcolor(bytes[k])>c){
            if(bytes[k]>c){
              digitalWrite(B2,1);
            } else {
              digitalWrite(B2,0);
            }
            //if(rcolor(bytes[k+1])>c){
            if(bytes[k+1]>c){
              digitalWrite(B3,1);
            } else {
              digitalWrite(B3,0);
            }
            //if(rcolor(bytes[k+2])>c){
            if(bytes[k+2]>c){
              digitalWrite(B4,1);
            } else {
              digitalWrite(B4,0);
            }
            clock();
            w+=4;
            c+=1;
          }
        latch();
        }
        c=0;
        for (var i=16;i>8;i--){
          digitalWrite(B13,1);
          abcd=32-i+1;
          y=linea(i);
          z=y+127;
          w=linea(i-8);
          x=w+127;
          for (var k=y;k<z+1;k=k+4){
            //if(rcolor(bytes[k])>c){
            if(bytes[k]>c){
              digitalWrite(B5,1);
            } else {
              digitalWrite(B5,0);
            }
            //if(rcolor(bytes[k+1])>c){
            if(bytes[k+1]>c){
              digitalWrite(B6,1);
            } else {
              digitalWrite(B6,0);
            }
            //if(rcolor(bytes[k+2])>c){
            if(bytes[k+2]>c){
              digitalWrite(B7,1);
            } else {
              digitalWrite(B7,0);
            }
            clock();
            w+=4;
            c+=1;
          }
        latch();
        }
      }
    }
    
    
    
    function onInit(){
      while (digitalRead(BTN1) == 0){
      var t =getTime();
      toled();
      console.log(' out ', t - getTime());
      }
      console.log(process.memory());
    }  
    

    1 Attachment

  • Thanks! So does the LED matrix that you have actually hold its contents - or does it have to be scanned out repeatedly?

    You could do something like this to write all 3 colours and the clock in one instruction (instead of your for (var k loop):

    var words = new Uint32Array(bytes.buffer,y/*byte offset*/,(z-y)/4 /*length*/);
    [].forEach.call(words, function(col) {
      digitalWrite([B8,B8,B5,B6,B7], 8 | ((((col&0x808080)*0b100000010000001)>>21)&7));
    });
    

    It should be a lot faster as you're effectively getting Espruino to iterate over the array.

    I don't have a display to test with, but it should work (although the offset/length for Uint32Array could be wrong).

    The craziness with multiply+shift is because I'm trying to extract 3 bit colour from the 24 bit image. I guess ideally you'd use the built-in graphics library which will do 4 bit colour, so you could use 3 bits of those 4 bits and make your life a lot easier.

  • It would hold the last bit you send, it doesn't have internal pwm so basically you do the pwm by sending the screen over and over, so the source comes with 256 tones for each color, but on the first lines I make the conversion to the amount of tones I would use, in this case I chose 2 to get the times I posted above.

    Thnx for your response but I do not really understood the code you share wonder if you could explain a little more what it does, I am just beginning with Espruino and Javascript.

  • Ahh, thanks... I think getting multiple tones (or even something non-flickery) is going to be properly hard work without using assembler I'm afraid :(

    In the code I posted:

    • new Uint32Array(.. creates a 'view' of the other array (so it doesn't copy it but just lets you see it 24 bits at a time)
    • [].forEach.call(words, function is a bit grotty (it'll be easier soon), but basically it creates an empty array, 'steals' forEach and calls it as if it was actually a function on Uint32Array. forEach will then call the function defined after it for each of the 32 bit words in the array (but it'll do it quite quickly).
    • And digitalWrite writes data out to the pins defined in the array, but does so one at a time. That means that you can write the 3 colours out, then write a 1 to B8, and then a 0 to B8.
    • The crazy multiply/and/shift stuff is to take the top bit of the 8 bit colour in each of 3 bytes, put them together, and move them down to the bottom 3 bits.

    Anyway, that and some other tweaks should make it a lot faster, but I think you're still going to have quite a bit of trouble with it...

  • Hi Gordon, thanks, I 've been really trying to understand the logic on the bitwise operations so I could adjust something similar to get alike results in terms of time, but I don't really get it, according to my understanding the (*) and the shift both operations are equivalent to divide within 127 so why not just divide, perhaps I don't really get it at all, also on the digitalWrite for multiple pins on which order gets executed, from right to left?

    I really appreciate your feedback.

    Thnx

  • digitalWrite executes right to left (least-significant bit first)

    You're right about the /127, but it's not quite 127, it's 127.000060558 - and I believe that makes a difference. You could try it and see I guess...

    There was a discussion elsewhere on here, but if you break the multiply down, you get:

    x*0b100000010000001 =
      x*0b100000000000000 +
      x*0b000000010000000 +
      x*0b000000000000001 =
      x << 14 +
      x << 7 +
      x
    

    If you assume that the 3 bits you want are as follows:

    x = R0000000G0000000B0000000
    

    Then when you do that multiply you get:

    R0000000G0000000B000000000000000000000 +
    0000000R0000000G0000000B00000000000000 +
    00000000000000R0000000G0000000B0000000
    
    = R000000RG00000RGB00000GB000000B0000000
    

    And then when you shift it down and & with 7 you get:

    = R000000RG00000RGB & 7
    = RGB
    

    Anyway, it's a bit of a nasty hack. Precalculating so as to avoid having to do that would be much better :)

  • Hi,

    I am using this:

    digitalWrite([B8,B8,B5,B6,B7],8|(((((col&0x80808000)>>15)*0x4081)>>14)&7));
    

    The data from the file comes as

    BB,GG,RR,AA
    Blue Green Red Gamma
    

    The issue I am facing is that it works as long as I have as blue >128 other wise something funny happens, I don't really know what, here is the full code and some sample files.

    var TONES=1;
    console.log(getTime());
     var image = require("fs").readFile("GIF/blue.bmp", "binary");
    image = image.substr(54,image.length);
    if (image.length==4096){
      var bytes = new Uint8Array(4096);
      for (var j=0;j<4096;j++){ 
        //bytes[j]=Math.round(image[j].charCodeAt()*TONES/256);
        bytes[j]=image[j].charCodeAt();
        //console.log(image[j].charCodeAt());
      }
    }
    if (image.length==3072){
      var c = 0;
      var bytes = new Uint8Array(4096);
      for (var j=0;j<3072;j++){
        if ((c+1)%4==0){
          bytes[c]=128;
          c+=1;
        } else {
          //bytes[j]=Math.round(image[j].charCodeAt()*TONES/256);
          bytes[c]=image[j].charCodeAt();
          
          //console.log(image[j].charCodeAt());
        }
        c+=1;
      }
    }
    console.log(getTime());
    console.log(process.memory());
    image='';
    console.log(process.memory());
    console.log(bytes);
    
    
    function rcolor(x){
      return (TONES*x/256);
    }
    
    function clock(){
      digitalWrite(B8,1);
      digitalWrite(B8,0);
    }
    
    function latch(){
      digitalWrite([B9,B9],1);
    }
    
    //function linea(y){
    //  return (y-1)*128;
    //}
    function linea(y){
      if (y==1) {return 0;}
      if (y==2) {return 128;}
      if (y==3) {return 256;}
      if (y==4) {return 384;}
      if (y==5) {return 512;}
      if (y==6) {return 640;}
      if (y==7) {return 768;}
      if (y==8) {return 896;}
      if (y==9) {return 1024;}
      if (y==10) {return 1152;}
      if (y==11) {return 1280;}
      if (y==12) {return 1408;}
      if (y==13) {return 1536;}
      if (y==14) {return 1664;}
      if (y==15) {return 1792;}
      if (y==16) {return 1920;}
      if (y==17) {return 2048;}
      if (y==18) {return 2176;}
      if (y==19) {return 2304;}
      if (y==20) {return 2432;}
      if (y==21) {return 2560;}
      if (y==22) {return 2688;}
      if (y==23) {return 2816;}
      if (y==24) {return 2944;}
      if (y==25) {return 3072;}
      if (y==26) {return 3200;}
      if (y==27) {return 3328;}
      if (y==28) {return 3456;}
      if (y==29) {return 3584;}
      if (y==30) {return 3712;}
      if (y==31) {return 3840;}
      if (y==32) {return 3968;}
    }
    
    function toled(){
      var c=0;
      var v=0;
      var abcd = 0;
      for (var f=0;f<TONES;f++){
        for (var i=32;i>24;i--){
          digitalWrite(B13,1);
          abcd=32-i+1;
          y=linea(i);
          z=y+127;
          w=linea(i-8);
          x=w+127;
          /*for (var k=y;k<z+1;k=k+4){
            //if(rcolor(bytes[k])>c){
            if(bytes[k]>c){
              digitalWrite(LED3,1);
            } else {
              digitalWrite(LED3,0);
            }
            //if(rcolor(bytes[k+1])>c){
            if(bytes[k+1]>c){
              digitalWrite(LED2,1);
            } else {
              digitalWrite(LED2,0);
            }
            //if(rcolor(bytes[k+2])>c){
            if(bytes[k+2]>c){
              digitalWrite(LED1,1);
            } else {
              digitalWrite(LED1,0);
            }
            clock();
            w+=4;
            c+=1;
          }*/
        var words = new Uint32Array(bytes.buffer,y,(z-y)/4);
          //console.log(words);
          [].forEach.call(words,function(col){
            digitalWrite([B8,B8,B5,B6,B7],8|(((((col&0x80808000)>>15)*0x4081)>>14)&7));
            console.log(col.toString(16),' - ',(col&0x80808000).toString(16));
          });
        latch();
    
        }
        c=0;
        for (var i=16;i>8;i--){
          digitalWrite(B13,1);
          abcd=32-i+1;
          y=linea(i);
          z=y+127;
          w=linea(i-8);
          x=w+127;
          /*for (var k=y;k<z+1;k=k+4){
            //if(rcolor(bytes[k])>c){
            if(bytes[k]>c){
              digitalWrite(B5,1);
            } else {
              digitalWrite(B5,0);
            }
            //if(rcolor(bytes[k+1])>c){
            if(bytes[k+1]>c){
              digitalWrite(B6,1);
            } else {
              digitalWrite(B6,0);
            }
            //if(rcolor(bytes[k+2])>c){
            if(bytes[k+2]>c){
              digitalWrite(B7,1);
            } else {
              digitalWrite(B7,0);
            }
            clock();
            w+=4;
            c+=1;
          }*/
        var words = new Uint32Array(bytes.buffer,y,(z-y)/4);
          //console.log(words);
          [].forEach.call(words,function(col){
            digitalWrite([B8,B8,LED3,LED2,LED1],8|(((((col&0x80808000)>>15)*0x4081)>>14)&7));
          console.log(col.toString(16),' - ',(col&0x80808000).toString(16));
          });
        latch();
    
        }
      }
    }
    
    
    
    function onInit(){
      while (digitalRead(BTN1) == 0){
      var t =getTime();
      toled();
      console.log(' sale ', t - getTime());
      }
      console.log(process.memory());
    }
    

    4 Attachments

  • It seems that what is empty on the left is filled with Ones, and consider the number negative, how do I avoid this?

  • Try replacing:

    digitalWrite([B8,B8,B5,B6,B7],8|(((((col&0x80808000)>>15)*0x4081)>>14)&7));
    

    with:

    digitalWrite([B8,B8,B5,B6,B7],8|(((((col&0x80808000)>>>15)*0x4081)>>14)&7));
    

    The >>> operator is an unsigned shift, whereas >> is a signed shift.

    The issue is that when the top bit of a number gets set, signed shift shifts in extra 1s as you shift right (so -8>>1 == -4 like you'd expect) - unsigned shift always shifts in 0.

    Having said that, there appears to be a bug in Espruino that causes Uint32Array to return negative numbers (which is why this is a problem). I've had it down as something to fix - however it works when compiled on the PC, but not when running on the device!

  • Same result.

  • Just unrelated tip, but will reduce size of emptiness in your code :)
    Replace linea function with this:

    function linea(y) {
      return (y - 1) * 128;
    }
    
  • Hi Moka,

    Yes, I had like that, this was just a desperate attempt to make it faster , right now, I am able to get 1 screen every 0.59 seconds which is barely usable.

  • Seems to work for me?

    >col=0x80000000;(8|(((((col&0x80808000)>>>15)*0x4081)>>14)&7)).toString(2)  
    ="1100"
    >col=0x00800000;(8|(((((col&0x80808000)>>>15)*0x4081)>>14)&7)).toString(2)  
    ="1010"
    >col=0x00008000;(8|(((((col&0x80808000)>>>15)*0x4081)>>14)&7)).toString(2)  
    ="1001"
    >col=0xFFFFFFFF;(8|(((((col&0x80808000)>>>15)*0x4081)>>14)&7)).toString(2)         
    ="1111"
    

    If you're serious about getting the scanning speed up, I'd try and precalculate a Uint8Array of bytes where the 4th bit is 1, and the 1st, 2nd and 3rd are R,G and B.

    Then you can do something like:

    var p = [B8,B8,LED3,LED2,LED1];     
    var d = digitalWrite;                          
    words.forEach(function(w) {d(p,w)});
    

    (new versions of Espruino allow you to use forEach directly, rather than the whole [].forEach thing that was done before)

    Having said that, for the 1024 bytes (32x32) it still seems to take 247ms (and that's without the latching), which is too slow for you really. To get the speed up you might still be looking at writing some inline assembler.

    A quick test shows that you can get down to around 30ms:

    var sender = E.asm("void(int)", 
      "bx  lr");
    
    words = new Uint8Array(1024);
    
    a=getTime();
    words.forEach(sender);
    print(getTime()-a);
    

    although that's not doing anything in the assembler code itself - you'd have to write some code to set the relevant pins to the right values.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Adafruit's 32x32 RGB LED matrix panel

Posted by Avatar for user6837 @user6837

Actions