B4R Library WS2812B Code Module

Hi guys...

After accepting a challenge in this thread to create a Library to control the NeoPixel type of thingy, I ordered myself a 8x5 WS2812B based shield.

So yesterday I got my shield...
https://cdn-shop.adafruit.com/1200x900/1430-03.jpg

... and starting doing my research.

Almost 80% of results took me to the NeoPixel Library... BUT, I found a Video on YouTube from a Guy called Kevin Darrah, in which he explained his Library-less approach... and found his code...
So after about an hour of head banging and trouble shooting, I had success in controlling my shield using his C code as an inline C Block.

So I decided to Trim it to its smallest footprint, and created a Module for it.

B4X:
'Original Function Creator : Kevin Darrah
'B4R Code Module Adaptation : Cableguy

'Usage:

'I Think it could not be simpler...
'Initialize it passing the amount of LEDs in your array
'In this Module, down in the C code block, change "byte RGB[120];", changing the size of this array to be 3 times the amount of leds passed in the initializer
'Call "SetLed" passing the Led number and the RGB values to set it to

'That's it!!!

Sub Process_Globals
    Private PIN8 As Pin
    Private LED As Int
    Private RED As Byte
    Private GREEN As Byte
    Private BLUE As Byte
    Private NLEDs As Int

End Sub

Public Sub Initialize(NumberOfLEDS As Int)
    PIN8.Initialize(8, PIN8.MODE_OUTPUT)
    NLEDs = NumberOfLEDS
End Sub

Public Sub SetLed(LEDNumber As Int, R As Byte, G As Byte, B As Byte)
    RED = R
    GREEN = G
    BLUE = B
    LED = LEDNumber
    If LED = -1 Then
        Log("all lit")
        Log(NLEDs)
        For n = 0 To NLEDs-1
            LED=n
            RunNative("RGB_update", Null)
        Next
    Else
        RunNative("RGB_update", Null)
    End If
End Sub


#if C
#define numberOfLEDs b4r_ws2812b::_nleds
byte RGB[120];//take your number of LEDs and multiply by 3 ! THIS IS VERY IMPORTANT!!!
void RGB_update(B4R::Object* o) {
byte ExistingPort, WS2812pinHIGH;
  if(b4r_ws2812b::_led >=0){
  RGB[ b4r_ws2812b::_led * 3] =  b4r_ws2812b::_green;
  RGB[ b4r_ws2812b::_led * 3 + 1] = b4r_ws2812b::_red;
  RGB[ b4r_ws2812b::_led * 3 + 2] = b4r_ws2812b::_blue;
  }
  noInterrupts();
  ExistingPort = PORTB;
  WS2812pinHIGH = PORTB | 1;
  int bitStream = numberOfLEDs * 3;

  for (int i = 0; i < bitStream; i++) {

    PORTB = WS2812pinHIGH;//bit 7  first, set the pin HIGH - it always goes high regardless of a 0/1

    PORTB = ((RGB[i] & B10000000) && B10000000) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

    PORTB = WS2812pinHIGH;//bit 6
    PORTB = ((RGB[i] & B01000000) && B01000000) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

    PORTB = WS2812pinHIGH;//bit 5
    PORTB = ((RGB[i] & B00100000) && B00100000) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

    PORTB = WS2812pinHIGH;//bit 4
    PORTB = ((RGB[i] & B00010000) && B00010000) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

    PORTB = WS2812pinHIGH;//bit 3
    PORTB = ((RGB[i] & B00001000) && B00001000) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

    PORTB = WS2812pinHIGH;//bit 2
    PORTB = ((RGB[i] & B00000100) && B00000100) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

    PORTB = WS2812pinHIGH;//bit 1
    PORTB = ((RGB[i] & B00000010) && B00000010) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

    PORTB = WS2812pinHIGH;//bit 0
    __asm__("nop\n\t");
    PORTB = ((RGB[i] & B00000001) && B00000001) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
  }
  interrupts();
}

#end if

The code is as well commented as possible, Although I took out all of the unneeded C Code Comments.
Usage is very Straight-forward.
1st - Initialize the code module passing how many LEDs there are in your array.
2nd - In the code Module's C Code Block, Where you have "byte RGB[120]", you MUST replace it with the value of your Number Of Leds in the Array, multiplied by 3! THIS IS VERY IMPORTANT
3rd - Call SetLed, passing the Led to act upon, and the RGB values...

This code has Two features built in that will be very handy, I think...
1st - If -1 is passed as the Led number in SetLed, all the array will be set with the RGB values
2nd - You don't need to change the whole array to act upon a single LED!

I would like to post a small video of this in action, but my math skills are not that great...
So if anyone can create me a "simple" code where 2 or 3 coloured lines bounce around my 8x5 bounds, I'll be thankful!

[EDIT] Added a simple test project that includes the Code Module
 

Attachments

  • WS2812B.zip
    1.9 KB · Views: 624
Last edited:

Robert Gately

Member
Licensed User
Is there anything in the module code that would cause it to not work with other pins?. So far, only pin 8 works. I'm attempting to change the pin number by changing 2 lines of code as shown:

B4X:
Sub Process_Globals
    Private PIN7 As Pin
End Sub

Public Sub Initialize(NumberOfLEDS As Int)
    PIN7.Initialize(7, PIN7.MODE_OUTPUT)

Ultimately, I'll be controlling more than one set of LEDs and will need to use more pins.

Thanks for your insights,
 

Cableguy

Expert
Licensed User
Longtime User
according to the original C code creator, only pin 8 work, but I cannot see why.. I haven't tested other pins... give it a shot...
 

Cableguy

Expert
Licensed User
Longtime User
I will try to get in touch with the C code creator and keep you posted
 

Cableguy

Expert
Licensed User
Longtime User
In the mean while, here's what I have found...
Reading the original article and watching the video, he often refers to "PORTB" and its manipulation... a quick search about ports in the Arduino gives us as result the existence of 3 Ports (B,C and D)... I will try during the day to have a play with this info and the C Code implementation...

https://www.arduino.cc/en/Reference/PortManipulation
 

Robert Gately

Member
Licensed User
Where is the original article and video?

I played around with the c code and researched as much as I could, but still was not able to get the code to work with any pin, other than pin 8. That’s okay. I can work around this limitation, but in the process of tweaking the code, I was able to eliminate a few variables and reduce the amount of code. It still has the same functionality.

B4X:
#if C
    #define numberOfLEDs b4r_ws2812b::_nleds
    byte RGB[9];    //take your number of LEDs and multiply by 3 ! THIS IS VERY IMPORTANT!!!
   
    void RGB_update(B4R::Object* o) {       
        if(b4r_ws2812b::_led >=0){
            RGB[ b4r_ws2812b::_led * 3] = b4r_ws2812b::_green;
            RGB[ b4r_ws2812b::_led * 3 + 1] = b4r_ws2812b::_red;
            RGB[ b4r_ws2812b::_led * 3 + 2] = b4r_ws2812b::_blue;
        }
         
        noInterrupts();
        DDRB = DDRB | B11111111;                                 // set PORTB for output
        int bitStream = numberOfLEDs * 3;
       
        for (int i = 0; i < bitStream; i++) {
            PORTB = (1<<PB0);                                       // high
            PORTB = ((RGB[i] & B10000000) && B00000001);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB = (0<<PB0);                                      // low
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
           
            PORTB = (1<<PB0);                                       // high
            PORTB = ((RGB[i] & B01000000) && B00000001);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB = (0<<PB0);                                      // low
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
           
            PORTB = (1<<PB0);                                       // high
            PORTB = ((RGB[i] & B00100000) && B00000001);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB = (0<<PB0);                                      // low
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
           
            PORTB = (1<<PB0);                                       // high
            PORTB = ((RGB[i] & B00010000) && B00000001);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB = (0<<PB0);                                      // low
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
       
            PORTB = (1<<PB0);                                       // high
            PORTB = ((RGB[i] & B00001000) && B00000001);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB = (0<<PB0);                                      // low
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

            PORTB = (1<<PB0);                                       // high
            PORTB = ((RGB[i] & B00000100) && B00000001);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB = (0<<PB0);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

            PORTB = (1<<PB0);                                       // high
            PORTB = ((RGB[i] & B00000010) && B00000001);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB = (0<<PB0);                                      // low
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

            PORTB = (1<<PB0);                                       // high
            PORTB = ((RGB[i] & B00000001) && B00000001);
            __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB = (0<<PB0);                                      // low
        }
        interrupts();
    }
#end if
 

Cableguy

Expert
Licensed User
Longtime User
Thanks for the updated c code...
I've been exchanging mails with the original author and according to him, it might be possible to use other pins, but the data would have to be shifted accordingly... a bit over my head...
I will pass the info I got from him by PM to you and if you want you can take up on it
 

Robert Gately

Member
Licensed User
Thanks for passing on the info. Can't get better than that video. Its a bit over my head as well, and for now I'm happy to use just pin 8. But if I do continue to play around with it and have anything useful to post, I will. Thanks again for your efforts!
 

Robert Valentino

Well-Known Member
Licensed User
Longtime User
Just my 2 cents.

In your code your are doing
B4X:
PIN8.Initialize(8, PIN8.MODE_OUTPUT)

But NO where else is PIN8 being used directly. You are not passing it to the C code. I even think if you took this line out the code may still work.

Looking at the link you provided "https://www.arduino.cc/en/Reference/PortManipulation" specifically says that

B4X:
PORTB maps to Arduino digital pins 8 to 13 The two high bits (6 & 7) map to the crystal pins and are not usable

DDRB - The Port B Data Direction Register - read/write
PORTB - The Port B Data Register - read/write
PINB - The Port B Input Pins Register - read only

So using PortB will always being using pin 8. His code references PORTB for everything.

B4X:
 ExistingPort = PORTB;
  WS2812pinHIGH = PORTB | 1;
  int bitStream = numberOfLEDs * 3;

  for (int i = 0; i < bitStream; i++) {

    PORTB = WS2812pinHIGH;//bit 7  first, set tnhe pin HIGH - it always goes high regardless of a 0/1 
    
    PORTB = ((RGB[i] & B10000000) && B10000000) | ExistingPort; 
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); 
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    
    PORTB = WS2812pinHIGH;//bit 6
    PORTB = ((RGB[i] & B01000000) && B01000000) | ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
    PORTB = ExistingPort;
    __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
...

So as I see it unless you change his PORTB to point to a different PORT you can never change Pin 8 because it is part of PORTB

Just my thoughts. I am long passed my years of being the freshest cookie in the box, so chances are I am wrong.

BUT I did ordered a string of these WE2812B strip lights to play with, and was hoping to put my old c knowledge (haven't done anything with c or c++ for 13 years now) and try and use the FastLED.h to work (using native code).

OF course if I do any good I will post it for all.

BobVal
 

Cableguy

Expert
Licensed User
Longtime User
You mentioned that you have taken a look at the provided link, so, I may add that, at the time, there were no other available libs to control neopixels and this "special usage" of PORTB was, at the time, one of the very few ways of controlling a neopixel.
The Arduino platform itself has evolved since and so did the way of controlling neopixels. There are now at least 3 native libs and wrappers available.
 

Robert Valentino

Well-Known Member
Licensed User
Longtime User
I'm sorry - I didn't mean it to come across rude or anything.

I watched part of his video and he said the same thing about only working with pin-8 and I just kept wondering to myself that if the code is using PORTB then it has to and had anyone tried using PORTD or PORTC and different pins.

NOW probably shouldn't have commented at all. I just sometimes blurt out stuff thinking I can help.
 

Cableguy

Expert
Licensed User
Longtime User
I'm sorry - I didn't mean it to come across rude or anything.

I watched part of his video and he said the same thing about only working with pin-8 and I just kept wondering to myself that if the code is using PORTB then it has to and had anyone tried using PORTD or PORTC and different pins.

NOW probably shouldn't have commented at all. I just sometimes blurt out stuff thinking I can help.
No worries, i didn't want to come across rude neither, just to note that there are now more easy and suitable solutions to neopixels managing
 
Top