Userspace Raspberry Pi library for controlling WS281X LEDs.

Raspberry PI specific forum
User avatar
mk-soft
Always Here
Always Here
Posts: 5334
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by mk-soft »

I have found a simple library for led strings. Since my case has 4 led's, I could also try to control them with PB.
Do not forget. Hardware needs root rights. PB so start with sudo

Update v1.01.6

WS2811.pbi

Code: Select all

;TOP

; Comment : Userspace Raspberry Pi library for controlling WS281X LEDs.
; Author  : mk-soft
; Version : v1.01.6
; Create  : 21.12.2021
; Update  : 28.12.2021

; Lisence : MIT and see github project

; Library : https://github.com/jgarff/rpi_ws281x


; Compile library: (See github projekt)
; -------------------------------------
;
; Open terminal:
;
; first install cmake:
;
;   sudo apt-get install cmake
; 
; also if git is not installed, install it:
;
;   sudo apt-get install git
; 
; now get the repository from the web:
;
;   git clone https://github.com/jgarff/rpi_ws281x.git
; 
; then go to the new directory:
;
;   cd rpi_ws281x
;
; follow:

;   mkdir build
;   cd build
;   cmake -D BUILD_SHARED=ON -D BUILD_TEST=OFF .. <--- don't forget double dots ;)
;   cmake --build . <--- don't forget dot ;)
;   sudo make install

; *********

EnableExplicit
  
; Header pwm.h
#RPI_PWM_CHANNELS = 2

; Header ws2811.h

#WS2811_TARGET_FREQ                     = 800000    ; Can go As low As 400000
#WS2811_TARGET_FREQ_LOW                 = 400000

; 4 color R, G, B And W ordering
#SK6812_STRIP_RGBW                      = $18100800
#SK6812_STRIP_RBGW                      = $18100008
#SK6812_STRIP_GRBW                      = $18081000
#SK6812_STRIP_GBRW                      = $18080010
#SK6812_STRIP_BRGW                      = $18001008
#SK6812_STRIP_BGRW                      = $18000810
#SK6812_SHIFT_WMASK                     = $f0000000

; 3 color R, G And B ordering
#WS2811_STRIP_RGB                       = $00100800
#WS2811_STRIP_RBG                       = $00100008
#WS2811_STRIP_GRB                       = $00081000
#WS2811_STRIP_GBR                       = $00080010
#WS2811_STRIP_BRG                       = $00001008
#WS2811_STRIP_BGR                       = $00000810

; predefined fixed LED types
#WS2812_STRIP                           = #WS2811_STRIP_GRB
#SK6812_STRIP                           = #WS2811_STRIP_GRB
#SK6812W_STRIP                          = #SK6812_STRIP_GRBW

;struct ws2811_device;

;typedef uint32_t ws2811_led_t;                   //< 0xWWRRGGBB
Structure ws2811_led_t
  Led.l[0]
EndStructure

;typedef struct ws2811_channel_t
Structure ws2811_channel_t Align #PB_Structure_AlignC
  ;{
  gpionum.l         ; //< GPIO Pin with PWM alternate function, 0 if unused
  invert.l          ; //< Invert output signal
  count.l           ; //< Number of LEDs, 0 if channel is unused
  strip_type.l      ; //< Strip color layout -- one of WS2811_STRIP_xxx constants
  *leds.ws2811_led_t; //< LED buffers, allocated by driver based on count
  brightness.a      ; //< Brightness value between 0 and 255
  wshift.a          ; //< White shift value
  rshift.a          ; //< Red shift value
  gshift.a          ; //< Green shift value
  bshift.a          ; //< Blue shift value
  *gamma            ; //< Gamma correction table
EndStructure

;typedef struct ws2811_t
Structure ws2811_t Align #PB_Structure_AlignC
  ;{
  render_wait_time.q; //< time in µs before the next render can run
  *device           ; //< Private data for driver use
  *rpi_hw           ; //< RPI Hardware Information
  freq.l            ; //< Required output frequency
  dmanum.l          ; //< DMA number _not_ already in use
  channel.ws2811_channel_t[#RPI_PWM_CHANNELS];
EndStructure  

;#define WS2811_RETURN_STATES(X)
#WS2811_SUCCESS = 0                 ; "Success"
#WS2811_ERROR_GENERIC = -1          ; "Generic failure"
#WS2811_ERROR_OUT_OF_MEMORY = -2    ; "Out of memory"
#WS2811_ERROR_HW_NOT_SUPPORTED = -3 ; "Hardware revision is not supported"
#WS2811_ERROR_MEM_LOCK = -4         ; "Memory lock failed"
#WS2811_ERROR_MMAP = -5             ; "mmap() failed"
#WS2811_ERROR_MAP_REGISTERS = -6    ; "Unable to map registers into userspace"
#WS2811_ERROR_GPIO_INIT = -7        ; "Unable to initialize GPIO"
#WS2811_ERROR_PWM_SETUP = -8        ; "Unable to initialize PWM"
#WS2811_ERROR_MAILBOX_DEVICE = -9   ; "Failed to create mailbox device"
#WS2811_ERROR_DMA = -10             ; "DMA error"
#WS2811_ERROR_ILLEGAL_GPIO = -11    ; "Selected GPIO not possible"
#WS2811_ERROR_PCM_SETUP = -12       ; "Unable to initialize PCM"
#WS2811_ERROR_SPI_SETUP = -13       ; "Unable to initialize SPI"
#WS2811_ERROR_SPI_TRANSFER = -14    ; "SPI transfer error"

; ws2811_return_t ws2811_init(ws2811_t *ws2811);                              //< Initialize buffers/hardware
; void ws2811_fini(ws2811_t *ws2811);                                         //< Tear it all down
; ws2811_return_t ws2811_render(ws2811_t *ws2811);                            //< Send LEDs off to hardware
; ws2811_return_t ws2811_wait(ws2811_t *ws2811);                              //< Wait for DMA completion
; const char * ws2811_get_return_t_str(const ws2811_return_t state);          //< Get string representation of the given return state
; void ws2811_set_custom_gamma_factor(ws2811_t *ws2811, double gamma_factor); //< Set a custom Gamma correction array based on a gamma correction factor

PrototypeC ws2811_init(*ws2811.ws2811_t);                                     //< Initialize buffers/hardware
PrototypeC ws2811_fini(*ws2811.ws2811_t);                                     //< Tear it all down
PrototypeC ws2811_render(*ws2811.ws2811_t);                                   //< Send LEDs off to hardware
PrototypeC ws2811_wait(*ws2811.ws2811_t)  ;                                   //< Wait for DMA completion
PrototypeC ws2811_get_return_t_str(ws2811_return_t_state);                    //< Get string representation of the given return state
PrototypeC ws2811_set_custom_gamma_factor(*ws2811.ws2811_t, gamma_factor.d);  //< Set a custom Gamma correction array based on a gamma correction factor

Global libws2811

Procedure InitLib_ws2811(pathlib.s = "libws2811.so")
  libws2811 = OpenLibrary(#PB_Any, pathlib)
  If libws2811
    Global ws2811_init.ws2811_init = GetFunction(libws2811, "ws2811_init")
    Global ws2811_fini.ws2811_fini = GetFunction(libws2811, "ws2811_fini")
    Global ws2811_render.ws2811_render = GetFunction(libws2811, "ws2811_render")
    Global ws2811_wait.ws2811_wait = GetFunction(libws2811, "ws2811_wait")
    Global __ws2811_get_return_t_str.ws2811_get_return_t_str = GetFunction(libws2811, "ws2811_get_return_t_str")
    Global ws2811_set_custom_gamma_factor.ws2811_set_custom_gamma_factor = GetFunction(libws2811, "ws2811_set_custom_gamma_factor")
  EndIf
  ProcedureReturn libws2811
EndProcedure

Procedure.s ws2811_get_return_t_str(state)
  Protected *state
  *state = __ws2811_get_return_t_str(state)
  If *state
    ProcedureReturn PeekS(*state, -1, #PB_UTF8)
  EndIf
EndProcedure

Procedure CloseLib_ws2811()
  CloseLibrary(libws2811)
EndProcedure

;- Example ws2812b (4 LEDs on GPIO 18 PWM)

CompilerIf #PB_Compiler_IsMainFile
  
  EnableExplicit
  
  ;IncludeFile "WS2811.pbi"
  
  If Not InitLib_ws2811()
    Debug "Error open library ws2811"
    End
  EndIf
  
  #TARGET_FREQ = #WS2811_TARGET_FREQ  ; Default 800000
  #GPIO_PIN = 18
  #DMA = 10
  ; #STRIP_TYPE = WS2811_STRIP_RGB    ; WS2812/SK6812RGB integrated chip+leds
  #STRIP_TYPE = #WS2811_STRIP_GRB     ; WS2812/SK6812RGB integrated chip+leds
  ;#STRIP_TYPE = #SK6812_STRIP_RGBW	  ; SK6812RGBW (Not SK6812RGB)
  #LED_COUNT = 4
  
  Global ledstring.ws2811_t
  Global r1
  
  With ledstring
    \freq = #TARGET_FREQ
    \dmanum = #DMA
    \channel[0]\gpionum = #GPIO_PIN
    \channel[0]\count = #LED_COUNT
    \channel[0]\invert = 0
    \channel[0]\brightness = 255
    \channel[0]\strip_type = #STRIP_TYPE
    
    Repeat ; Do
      r1 = ws2811_init(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Init: " + ws2811_get_return_t_str(r1)
        CloseLib_ws2811()
        End
      EndIf
      
      ; LEDs 0xWWRRGGBB
      \channel[0]\leds\led[0] = $00FF0000
      \channel[0]\leds\led[1] = $0000FF00
      \channel[0]\leds\led[2] = $000000FF
      \channel[0]\leds\led[3] = $00FFFFFF
      r1 = ws2811_render(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Render: " + ws2811_get_return_t_str(r1)
        Break
      EndIf
      ws2811_wait(ledstring)
      Delay(2000)
      
      ; LEDs
      \channel[0]\leds\led[0] = $00FFFFFF
      \channel[0]\leds\led[1] = $00FF0000
      \channel[0]\leds\led[2] = $0000FF00
      \channel[0]\leds\led[3] = $000000FF
      r1 = ws2811_render(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Render: " + ws2811_get_return_t_str(r1)
        Break
      EndIf
      ws2811_wait(ledstring)
      Delay(2000)
      
      ; LEDs
      \channel[0]\leds\led[0] = $000000FF
      \channel[0]\leds\led[1] = $00FFFFFF
      \channel[0]\leds\led[2] = $00FF0000
      \channel[0]\leds\led[3] = $0000FF00
      r1 = ws2811_render(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Render: " + ws2811_get_return_t_str(r1)
        Break
      EndIf
      ws2811_wait(ledstring)
      Delay(2000)
      
      \channel[0]\leds\led[0] = $00000000
      \channel[0]\leds\led[1] = $00000000
      \channel[0]\leds\led[2] = $00000000
      \channel[0]\leds\led[3] = $00000000
      r1 = ws2811_render(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Render: " + ws2811_get_return_t_str(r1)
        Break
      EndIf
      Delay(1000)
      
      \channel[0]\leds\led[0] = $0000FF00
      \channel[0]\leds\led[1] = $0000FF00
      \channel[0]\leds\led[2] = $0000FF00
      \channel[0]\leds\led[3] = $0000FF00
      r1 = ws2811_render(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Render: " + ws2811_get_return_t_str(r1)
        Break
      EndIf
      Delay(1000)
      
      \channel[0]\leds\led[0] = $00000000
      \channel[0]\leds\led[1] = $00000000
      \channel[0]\leds\led[2] = $00000000
      \channel[0]\leds\led[3] = $00000000
      r1 = ws2811_render(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Render: " + ws2811_get_return_t_str(r1)
        Break
      EndIf
      Delay(1000)
      
    Until #True ; EndDo
  EndWith
  
  ; Clear controler - DMA, etx
  ws2811_fini(ledstring)
  ; Close library
  CloseLib_ws2811()
  
CompilerEndIf
Last edited by mk-soft on Tue Dec 28, 2021 7:35 pm, edited 4 times in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Rings
Moderator
Moderator
Posts: 1427
Joined: Sat Apr 26, 2003 1:11 am

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by Rings »

thx for sharing,
comes very handy for me.

will test during holidays and report some feedback here.
SPAMINATOR NR.1
User avatar
idle
Always Here
Always Here
Posts: 5040
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by idle »

Looks interesting thanks
User avatar
Rings
Moderator
Moderator
Posts: 1427
Joined: Sat Apr 26, 2003 1:11 am

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by Rings »

it is a bit tricky to build the library, so i put some steps here:

open a terminal console in normal user space...

first install cmake:

Code: Select all

sudo apt-get install cmake
also if git is not installed, install it:

Code: Select all

sudo apt-get install git
now get the repository from the web:

Code: Select all

git clone https://github.com/jgarff/rpi_ws281x.git
then go to the new directory:

Code: Select all

cd rpi_ws281x
follow:

Code: Select all

mkdir build
cd build
cmake -D BUILD_SHARED=ON -D BUILD_TEST=OFF ..
cmake --build . 
sudo make install
if everthing goes ok, a "libws2811.so" is created in the folder rpi_ws281x/build .
copy that to your example code folder .
SPAMINATOR NR.1
User avatar
mk-soft
Always Here
Always Here
Posts: 5334
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by mk-soft »

if everthing goes ok, a "libws2811.so" is created in the folder rpi_ws281x/build .
copy that to your example code folder .
You don't need copy the "libws2811.so".
'sudo make install' copied the library to folder /usr/local/lib and the header files to folder /usr/local/includes
So ready to open the library without path to lib

Added your Deskription ;)
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Rings
Moderator
Moderator
Posts: 1427
Joined: Sat Apr 26, 2003 1:11 am

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by Rings »

ok, so forgive me,
i'm a injured victim of
"windows dll" hell
SPAMINATOR NR.1
User avatar
idle
Always Here
Always Here
Posts: 5040
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by idle »

Rings wrote: Tue Dec 28, 2021 7:42 pm ok, so forgive me,
i'm a injured victim of
"windows dll" hell
:lol: I think most of us are.

just an aside we can also use pigpio from userspace. via importing -lpigpiod_if2
To run make sure the pigpio daemon is running
sudo pigpiod
sudo is not required to run programs linked to pigpiod_if2
For examples see x_pigpiod_if2.c within the pigpio archive file.

I would recommend using it for PWM or you'll get very noisy servo action.
User avatar
Rings
Moderator
Moderator
Posts: 1427
Joined: Sat Apr 26, 2003 1:11 am

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by Rings »

idle wrote: Tue Dec 28, 2021 10:10 pm
just an aside we can also use pigpio from userspace. via importing -lpigpiod_if2
To run make sure the pigpio daemon is running
sudo pigpiod
sudo is not required to run programs linked to pigpiod_if2
For examples see x_pigpiod_if2.c within the pigpio archive file.

I would recommend using it for PWM or you'll get very noisy servo action.
this is the way if you want more than 1 program access the gpio
or access from a remote cpu .

the functionnames are a bit different than from the libpigpio.so ,
so we have to write a new include file....
SPAMINATOR NR.1
dige
Addict
Addict
Posts: 1247
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by dige »

thx MK-Soft for the WS281X include - works well :D

The specification of the GPIO does not seem to work correctly.
I have connected a LED ring to GPPIO 12 and set #GPIO_PIN = 12. Nevertheless the case LEDs are also activated.

If I set to GPIO 18, both LEDs (housing and ring) are also on.

Seems like the include is sending the commands to all GPIOs?
"Daddy, I'll run faster, then it is not so far..."
User avatar
mk-soft
Always Here
Always Here
Posts: 5334
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by mk-soft »

This may be due to the fact that "PWM0" is already coupled to GPIO 18 by the software of the MultimediaCase. GPIO 12 uses the same "PWM0".
It might work separately if you use GPIO 13, which uses "PWM1".

However, I am very familiar with PI hardware. Therefore, please read up on it.

Edit
If I understand the C code correctly, you need to set channel[0] to GPIO 18 or 12 and set channel[1] to GPIO 13 or 19. So you can use two separate strings.

README.MD
### GPIO Usage:

The GPIOs that can be used are limited by the hardware of the Pi and will
vary based on the method used to drive them (PWM, PCM or SPI).
Beware that the GPIO numbers are not the same as the physical pin numbers
on the header.

PWM:
```
PWM0, which can be set to use GPIOs 12, 18, 40, and 52.
Only 12 (pin 32) and 18 (pin 12) are available on the B+/2B/3B

PWM1 which can be set to use GPIOs 13, 19, 41, 45 and 53.
Only 13 is available on the B+/2B/PiZero/3B, on pin 33
```

PCM:
```
PCM_DOUT, which can be set to use GPIOs 21 and 31.
Only 21 is available on the B+/2B/PiZero/3B, on pin 40.
```

SPI:
```
SPI0-MOSI is available on GPIOs 10 and 38.
Only GPIO 10 is available on all models.
See also note for RPi 3 below.
```
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
dige
Addict
Addict
Posts: 1247
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by dige »

Thanks for the quick feedback.
Unfortunately, I don't understand anything. :oops:
What do I have to change to address the LED ring via header 32 / GPIO12?

Thank you in advance :)
"Daddy, I'll run faster, then it is not so far..."
User avatar
mk-soft
Always Here
Always Here
Posts: 5334
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by mk-soft »

I'm not sure, but that's how it should work.

Code: Select all

;- Example ws2812b (4 LEDs on GPIO 18 PWM)

CompilerIf #PB_Compiler_IsMainFile
  
  EnableExplicit
  
  ;IncludeFile "WS2811.pbi"
  
  If Not InitLib_ws2811()
    Debug "Error open library ws2811"
    End
  EndIf
  
  #TARGET_FREQ = #WS2811_TARGET_FREQ  ; Default 800000
  
  Global ledstring.ws2811_t
  Global r1
  
  With ledstring
    \freq = #TARGET_FREQ
    \dmanum = 10
    \channel[0]\gpionum = 18
    \channel[0]\count = 4
    \channel[0]\invert = 0
    \channel[0]\brightness = 255
    \channel[0]\strip_type = #WS2811_STRIP_GRB
    
    \channel[1]\gpionum = 13
    \channel[1]\count = 2
    \channel[1]\invert = 0
    \channel[1]\brightness = 255
    \channel[1]\strip_type = #WS2811_STRIP_GRB
    
    Repeat ; Do
      r1 = ws2811_init(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Init: " + ws2811_get_return_t_str(r1)
        CloseLib_ws2811()
        End
      EndIf
      
      ; LEDs 0xWWRRGGBB
      \channel[0]\leds\led[0] = $00FF0000
      \channel[0]\leds\led[1] = $0000FF00
      \channel[0]\leds\led[2] = $000000FF
      \channel[0]\leds\led[3] = $00FFFFFF
      ; LEDs 0xWWRRGGBB
      \channel[1]\leds\led[0] = $00FF00FF
      \channel[1]\leds\led[1] = $0000FFFF
      
      r1 = ws2811_render(ledstring)
      If r1 <> #WS2811_SUCCESS
        Debug "Error Render: " + ws2811_get_return_t_str(r1)
        Break
      EndIf
      ws2811_wait(ledstring)
      Delay(2000)
...
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
dige
Addict
Addict
Posts: 1247
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by dige »

Thank you Mk-Soft ...unfortunately, it seems that nothing is considered under \channel[1]
"Daddy, I'll run faster, then it is not so far..."
User avatar
mk-soft
Always Here
Always Here
Posts: 5334
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Userspace Raspberry Pi library for controlling WS281X LEDs.

Post by mk-soft »

Short infomation:

The library can also be compiled as 64bit version without problems.

Runs therefore also with PureBasic for Linux Arm64 :wink:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply