• GeneralSolved
  • Controlling an LED with variable colours using Bela and SuperCollider

Dear developers and users,

I will control a very bright LED with Bela using SuperCollider or PD.

The LED will be hidden back of an instrument and will work as a background light for it.
A distance sensor should control the brightness of the LED.
The amplitude of synthesis should govern the colour of the LED.

Could someone recommend some RGB LED products which work on Bela without problem, if possible, with a circuit diagram?

Is there also a template for code in PD and supercollider?

Currently, my Bela or my BeagleBone Black is not functional, so that I can do nothing. However, I tested desired sound synthesis successfully and will buy more boards and related components as soon as possible. So, it would be beneficial if someone shared some information on my questions.

I appreciate any help you can provide.

Best regards,

I have a good solution to drive NeoPixel-like RGB LEDs that has the following limitations:

  • on BelaMini: it works always
  • on Bela with BBB it works only with analog I/O disabled

    giuliomoro

    Thank you for your quick answer!

    For Audio input, I am intending to use Audio In via Molex-style connector.

    I intended to use the following distance sensors simultaneously:
    - HC-SR04-P: It does not require analogue pins.
    - GP2Y0A21YK0F: this is an analog sensor.

    If I use 'Audio In' for sampling sound and HC-SR04-P for distance sensor, I can still utilise sound with LED light controlled by the interaction from distance sensor.

    Did I understand you correctly?

    Thank you!

      prko If I use 'Audio In' for sampling sound and HC-SR04-P for distance sensor, I can still utilise sound with LED light controlled by the interaction from distance sensor.

      yes, but you wouldn't be able to use the LED lights alongside the GP2Y0A21YK0F. I mean, there are ways of doing it that would not have this restriction, but they may require some more effort as I am not familiar with them (e.g.: https://github.com/Yona-Appletree/LEDscape / https://beagleboard.org/static/prucookbook/#blocks_ws2812)

      Thank you for the link. I will try it as soon as possible!

      a year later

      Hello! Total noob here. My plan/desire/mission is to use a few LED strips to visualise some randomly generated arrays on SuperCollider. My question is: how doable is that with Bela? Can I interface the digitalIO pins from SC or do i need to use some other bridge application?
      Thanks!!

      are these Neopixel-type LED strips you are talking about ?

      3 months later

      hi @giuliomoro, i was wondering if you could share the solutions you mentioned above. I couldn't find any good tips for driving a NeoPixel-like RGB LED yet. Thank you!

      Here's some example code. This assumes BelaMini is used and the neopixels are driven from pin P2.25. To enable that you'll need to run config-pin P2.25 spi after each reboot ( this can be automated later if needed).

      #include <Bela.h>
      #include <stdio.h>
      #include <libraries/Spi/Spi.h>
      #include <cmath>
      #include <string.h>
      Spi spi;
      
      const unsigned int kSpiClock = 12000000;
      const unsigned int kSpiMinTransferSize = 160; // min number of bytes to trigger DMA transfer
      const unsigned int kSpiMaxTransferSize = 4096; // max number of bytes to be sent at once
      const unsigned int kSpiWordLength = 32;
      const uint8_t kSpiMsbFirst = 1;
      const float kSpiInterWordTime = 200;
      const float kSpiPeriodNs = 1000000000.0 / kSpiClock;
      const uint8_t kBitsPerByte = 8;
      // The specs (WS2812B-2020 datasheet) say:
      // T0H 0 code, high voltage time 220ns~380ns
      // T1H 1 code, high voltage time 580ns~1µs
      // T0L 0 code, low voltage time 580ns~1µs
      // T1L 1 code, low voltage time 220ns~420ns
      // RES Frame unit, low voltage time >280µs
      // WS2812B v1.0 on the other hand says:
      // TH+TL = 1.25µs ± 600ns
      // T0H 0 code, high voltage time 0.4us ±150ns
      // T1H 1 code, high voltage time 0.8us ±150ns
      // T0L 0 code, low voltage time 0.85us ±150ns
      // T1L 1 code, low voltage time 0.45us ±150ns
      // More recent datasheets say
      
      // We pick below some conservative values for WS2812B
      typedef struct {
      	float min;
      	float max;
      } T_t;
      
      const T_t TH[2] = {
      	{.min = 200, .max = 400}, // T0H
      	{.min = 700, .max = 900}, // T1H
      };
      
      const T_t TL[2] = {
      	{.min = 750, .max = 950}, // T0L
      	{.min = 350, .max = 550}, // T1L
      };
      
      // Additionally, there is need for adding a long enough "0" output before we start clocking out data.
      // The SPI peripheral in use seems to always set the MOSI high when not clocking out data, so we have
      // to start clocking out some zeros first.
      // Skipping this will result in 1-bit offset in the bit shifting. If you see random flashes
      // in your LEDs strip and/or the first LED remaning on when set to off
      // and/or the others have the colors "slightly" wrong, this could be the reason.
      // This could be avoided if the MOSI was to be LOW when idle, but this would require
      // an external transistor to invert it.
      const double kSpiLeadingZerosNs = 10000; // determined empirically
      const unsigned int kSpiLeadingZeros = std::ceil((kSpiLeadingZerosNs / kSpiPeriodNs));
      
      bool readBitField(uint8_t* data, uint32_t offset) {
      	uint8_t position = offset % kBitsPerByte;
      	unsigned int i = offset / kBitsPerByte;
      	return data[i] & (1 << position);
      }
      
      void writeBitField(uint8_t* data, uint32_t offset, bool value) {
      	uint8_t position = offset % kBitsPerByte;
      	unsigned int i = offset / kBitsPerByte;
      	if(value)
      		data[i] |= 1 << position;
      	else
      		data[i] &= ~(1 << position);
      }
      
      ssize_t rgbToClk(uint8_t* rgb, size_t numRgb, uint8_t* out, size_t numOut)
      {
      	memset(out, 0, numOut * sizeof(out[0]));
      	// writeBitField(out, 0, 1);
      	uint32_t clk = 0;
      	// emsure we have complete RGB sets
      	numRgb = numRgb - (numRgb % 3);
      	for(uint32_t inBit = 0; inBit < numRgb * kBitsPerByte; ++inBit) {
      		// data comes in as RGB but needs to be shuffled into GRB for the WS2812B
      		uint32_t actualInBit;
      		uint8_t remainder = inBit % (3 * kBitsPerByte);
      		if(remainder < kBitsPerByte)
      			actualInBit = inBit + kBitsPerByte;
      		else if(remainder < kBitsPerByte * 2)
      			actualInBit = inBit - kBitsPerByte;
      		else
      			actualInBit = inBit;
      		bool value = readBitField(rgb, actualInBit);
      		const T_t* Ts[2] = {TH, TL};
      		for(unsigned int t = 0; t < 2; ++t) {
      			const T_t* T = Ts[t];
      			bool highLow = (0 == t) ? true : false;
      			float time = 0;
      			while(time < T[value].min) {
      				bool wordEnd = (kSpiWordLength - 1 == clk);
      				writeBitField(out, clk, highLow);
      				time += kSpiPeriodNs;
      				if(wordEnd)
      					time += kSpiInterWordTime;
      				++clk;
      				if(clk / kBitsPerByte > numOut)
      					return -1;
      			}
      			if(time > T[value].max) {
      				printf("Error: expected %f but got %f\n", T[value].max, time);
      				return 0;
      			}
      
      		}
      	}
      	ssize_t numBytes = ((clk + kSpiWordLength - 1) / kSpiWordLength) * kSpiWordLength / kBitsPerByte; // round up to the next word
      	if(numBytes > numOut) // just in case numOut was not a multiple, we now no longer fit
      		return -1;
      	return numBytes;
      }
      
      void sendSpi(uint8_t* data, size_t length)
      {
      	if(length < kSpiMinTransferSize)
      		length = kSpiMinTransferSize;
          if (spi.transfer(data, NULL, length) == 0)
              printf("SPI: Transaction Complete. Sent %d bytes\n", length);
          else
              printf("SPI: Transaction Failed\r\n");
      }
      
      #include <vector>
      
      // rgb triplets, can be resized to match number of LEDs _before_ `task()` starts.
      // after that, its content (but not size!) can be changed from anywhere
      std::vector<uint8_t> colors = {{
      	0x00, 0xC0, 0x00,
      	0xC0, 0x00, 0x00,
      	0x00, 0x00, 0xC0,
      	0x00, 0x00, 0x00,
      }};
      
      const uint8_t kBytesPerRgb = 3;
      void task(void*) {
      	std::vector<uint8_t> data(kSpiMaxTransferSize);
      	// uint8_t kNumColors = colors.size() / kBytesPerRgb;
      	uint8_t kNumLeds = 16;
      	std::vector<uint8_t> rgb(kNumLeds * kBytesPerRgb);
      	for (unsigned int n = 0; n < 100 && !Bela_stopRequested(); ++n) {
      		for(unsigned int l = 0; l < kNumLeds; ++l) {
      			printf("RGB[%d]: ", l);
      			for(unsigned int b = 0;  b < kBytesPerRgb; ++b) {
      				uint8_t val = colors[(( n + l) * kBytesPerRgb + b) % colors.size()];;
      				rgb[l * kBytesPerRgb + b] = val;
      				printf("%#02x ", val);
      			}
      			printf("\n");
      		}
      		printf("data: ");
      		for(unsigned int n = 0; n < kNumLeds * kBytesPerRgb; ++n) {
      			if(0 == (n % 3))
      				printf("\n");
      			printf("%#02x ", rgb[n]);
      		}
      		printf("\n");
      		ssize_t len = kSpiLeadingZeros + rgbToClk(rgb.data(), rgb.size(), data.data() + kSpiLeadingZeros, data.size() - kSpiLeadingZeros);
      		if(len < 0) {
      			fprintf(stderr, "Error: message too long\n");
      			break;
      		} else {
      			printf("Data is %d\n", len);
      		}
      		// print data as it will be sent out
      		unsigned int k = 0;
      		for(unsigned int n = 0; n < len; ++n) {
      			for(unsigned int c = 0; c < kBitsPerByte; ++c) {
      				printf("%d", (bool)(data[n] & (1 << c)));
      				++k;
      				if(0 == (k % kBitsPerByte))
      					printf(" ");
      				if(0 == (k % kSpiWordLength))
      					printf("| ");
      			}
      		}
      		printf("\n");
      
      		// format data
      		// SPI transmits the most significant byte first ("right justified in each word")
      		// so if we have more than 1 byte per word we need to shuffle them around so that
      		// they are output in the correct order
      		unsigned int bytesPerWord = kSpiWordLength / kBitsPerByte;
      		for(unsigned int n = 0; n < len; n += bytesPerWord) {
      			for(unsigned int b = 0; b < bytesPerWord / 2; ++b) {
      				uint8_t tmp = data[n + b];
      				data[n + b] = data[n + bytesPerWord - 1 - b];
      				data[n + bytesPerWord - 1 - b] = tmp;
      			}
      		}
      		if(kSpiMsbFirst) {
      			for(unsigned int n = 0; n < len; ++n)
      				data[n] = __builtin_bitreverse8(data[n]);
      		}
      
      		sendSpi(data.data(), data.size());
      		usleep(100000);
      	}
      }
      bool setup(BelaContext *context, void *userData)
      {
      	spi.setup({.device = "/dev/spidev2.1", // Device to open
      		.speed = kSpiClock, // Clock speed in Hz
      		.delay = 0, // Delay after last transfer before deselecting the device, won't matter
      		.numBits = kSpiWordLength, // No. of bits per transaction word,
      		.mode = Spi::MODE3 // SPI mode
      	});
      	size_t numLeds = 12; // TODO: adjust depending on num of leds in use
      	colors.resize(kBytesPerRgb * numLeds);
      	Bela_runAuxiliaryTask(task, 90);
      	return true;
      }
      
      void render(BelaContext *context, void *userData)
      {
      // TODO: set elements of `colors`
      }
      
      void cleanup(BelaContext *context, void *userData)
      {}
      • noah replied to this.

        wow, thanks a lot for the example code @giuliomoro!

        I am using the BelaMini and also uploaded your SPI_Library to the project but I am still getting some errors.

        First of all I had change #include <libraries/SPI/SPI.h> to #include <SPI.h> to use the library but I've still got the following error messages:
        unknown type name 'SPI' column: 1, line: 6
        use of undeclared identifier 'SPI' column: 3, line: 208
        use of undeclared identifier 'SPI' column: 3, line: 211

        I checked the example code in the SPI_Library and changed SPI::SS_LOW to SPI_SS_LOW and SPI::MODE3 to SPI_MODE3 so that they are recognized.

        Now I get "use of undeclared identifier" for spi.transfer and spi.setup.

        Any idea how to fix these?

        Right, the code I posted was for an older version of the Spi library. I now tested and amended it and it should work out of the box on your board.

        now it runs perfectly, thank you!

        hi, sorry for bothering again. I tried to "set elements of colors" but I don't have too much experience with C++. Is there any chance that you include an example in the code which shows how to do that? I would really appreciate it.

        I would then try and set the color of the LED from PD. To do that I can follow the OLED example to receive OSC messages from PD.

        the global variable colors contains triplets of colors that are sent to each LED. When you start the program, the variable's content looks like this:

        std::vector<uint8_t> colors = {{
        	0x00, 0xC0, 0x00,
        	0xC0, 0x00, 0x00,
        	0x00, 0x00, 0xC0,
        	0x00, 0x00, 0x00,
        }};

        So it drives four LED: green, red, blue, dark.
        If you have more than 4 LEDs, edit this:

        	size_t numLeds = 12; // TODO: adjust depending on num of leds in use

        Once you have adjusted that, from within setup() or render() or even task() you can adjust the colors of the LEDs. This code will, for example, have all RED leds with increasing brightness

        for(unsigned int n = 0; n < colors.size() / kBytesPerRgb; n++)
        {
            float relative = n / float(colors.size() / kBytesPerRgb);
            colors[n * kBytesPerRgb] = 0xff * relative;  // LED n, red
            colors[n * kBytesPerRgb + 1] = 0;  // LED n, green
            colors[n * kBytesPerRgb + 2] = 0;  // LED n, blue
        }

        wow, you have the best support. Thanks a lot!

        4 days later

        Thanks for the code. After some adjustments for the leds i have, it work fine.
        i have encapsulate them in a class for easy usage :

        /***********************************************************
        LedStripSpi  : send Strip Led (like WS2812) data 
        with the SPI MOSI output
        This assumes BelaMini is used and the neopixels are driven from pin P2.25. 
        To enable that you'll need to run 'config-pin P2.25 spi' after each reboot
        ************************************************************/
        // The specs (WS2812B-2020 datasheet) say:
        // T0H 0 code, high voltage time 220ns~380ns
        // T1H 1 code, high voltage time 580ns~1µs
        // T0L 0 code, low voltage time 580ns~1µs
        // T1L 1 code, low voltage time 220ns~420ns
        // RES Frame unit, low voltage time >280µs
        // WS2812B v1.0 on the other hand says:
        // TH+TL = 1.25µs ± 600ns
        // T0H 0 code, high voltage time 0.4us ±150ns
        // T1H 1 code, high voltage time 0.8us ±150ns
        // T0L 0 code, low voltage time 0.85us ±150ns
        // T1L 1 code, low voltage time 0.45us ±150ns
        // More recent datasheets say
        // For led YF923-F5-F8 (aliexpress) :
        // T0H 0 code, high voltage time 0.3us ±150ns
        // T1H 1 code, high voltage time 0.6us ±150ns
        // T0L 0 code, low voltage time 0.6us ±150ns
        // T1L 1 code, low voltage time 0.3us ±150ns
        // RES Frame unit, low voltage time >50µs
        
        #ifndef LEDSTRIPSPI_H
        #define LEDSTRIPSPI_H
        
        #include <Bela.h>
        #include <stdio.h>
        #include <libraries/Spi/Spi.h>
        #include <cmath>
        #include <string.h>
        
        class LedStripSpi
        {
        	public:
        		enum LedType{
        		    WS2812B = 0,
        		    YF923F5F8
        		};
        
            protected:
                const unsigned int  kSpiClock = 12000000;
                const unsigned int  kSpiMinTransferSize = 160; // min number of bytes to trigger DMA transfer
                const unsigned int  kSpiMaxTransferSize = 4096; // max number of bytes to be sent at once
                const uint8_t		kSpiWordLength = 32;
                const uint8_t       kSpiMsbFirst = 1;
                const float         kSpiPeriodNs = 1000000000.0 / kSpiClock;
                const uint8_t       kBitsPerByte = 8;
                // Additionally, there is need for adding a long enough "0" output before we start clocking out data.
                // The SPI peripheral in use seems to always set the MOSI high when not clocking out data, so we have
                // to start clocking out some zeros first.
                // Skipping this will result in 1-bit offset in the bit shifting. If you see random flashes
                // in your LEDs strip and/or the first LED remaning on when set to off
                // and/or the others have the colors "slightly" wrong, this could be the reason.
                // This could be avoided if the MOSI was to be LOW when idle, but this would require
                // an external transistor to invert it.
                const double		kSpiLeadingZerosNs = 10000; // determined empirically
                const unsigned int	kSpiLeadingZeros = std::ceil((kSpiLeadingZerosNs / kSpiPeriodNs));
                const int8_t		kBytesPerRgb = 3;
                
                
                typedef struct {
                    float min;
                    float max;
                } T_t;
                
                bool            fReverseByte;
                bool            fGRB_Mode;
                float           fSpiInterWordTime;
                T_t             fTH[2];
                T_t             fTL[2];
                
                Spi             fSpi;
                Spi::Mode       fSpiMode;
                
                unsigned int    fNbrLeds;
                uint8_t*        fLedBuffers[2];
                uint8_t*        fDataBuffer;
                uint8_t         fCurrentSendingLedBuffer;
                uint8_t         fCurrentEditingLedBuffer;
                bool			fBufferSwitched;
                
                void SetType(LedType type)
                {
                    // We pick below some conservative values for Led Type
                    switch(type) {
                        case WS2812B:
                            fTH[0].min = 200;  
                            fTH[0].max = 400; // T0H
                            fTH[1].min = 700;
                            fTH[1].max = 900; // T1H
                            fTL[0].min = 750;  
                            fTL[0].max = 950; // T0L
                            fTL[1].min = 350;
                            fTL[1].max = 550; // T1L
                            fReverseByte = false;
                            fGRB_Mode = true;
                            fSpiInterWordTime = 200;
                            fSpiMode = Spi::MODE3;
                            break;
                        case YF923F5F8:
                            fTH[0].min = 200;  
                            fTH[0].max = 450; // T0H
                            fTH[1].min = 500;
                            fTH[1].max = 750; // T1H
                            fTL[0].min = 500;  
                            fTL[0].max = 750; // T0L
                            fTL[1].min = 200;
                            fTL[1].max = 450; // T1L
                            fReverseByte = true;
                            fGRB_Mode = false;
                            fSpiInterWordTime = 0;
                            fSpiMode = Spi::MODE2;
                            break;
                        default:
                        	break;
                    };
                }
                
                bool readBitField(uint8_t* data, uint32_t offset) {
                    uint8_t position = offset % kBitsPerByte;
                    unsigned int i = offset / kBitsPerByte;
                    return data[i] & (1 << position);
                }
        
                void writeBitField(uint8_t* data, uint32_t offset, bool value) {
                    uint8_t position = offset % kBitsPerByte;
                    unsigned int i = offset / kBitsPerByte;
                    if(value)
                        data[i] |= 1 << position;
                    else
                        data[i] &= ~(1 << position);
                }
                
                ssize_t rgbToClk(uint8_t* rgb, size_t numRgb, uint8_t* out, size_t numOut)
                {
                    memset(out, 0, numOut * sizeof(out[0]));
                    // writeBitField(out, 0, 1);
                    uint32_t clk = 0;
                    // emsure we have complete RGB sets
                    numRgb = numRgb - (numRgb % 3);
                    for(uint32_t inBit = 0; inBit < numRgb * kBitsPerByte; ++inBit) {
                        
                        uint32_t actualInBit;
                        uint8_t remainder = inBit % (3 * kBitsPerByte);
                        if(fGRB_Mode) {   // data comes in as RGB but needs to be shuffled into GRB for the WS2812B
                        
                            if(remainder < kBitsPerByte)
                                actualInBit = inBit + kBitsPerByte;
                            else if(remainder < kBitsPerByte * 2)
                                actualInBit = inBit - kBitsPerByte;
                            else
                                actualInBit = inBit;
                        } else {
                            actualInBit = inBit;
                        }
                                          
                        bool value = readBitField(rgb, actualInBit);
                        const T_t* Ts[2] = {fTH, fTL};
                        for(unsigned int t = 0; t < 2; ++t) {
                            const T_t* T = Ts[t];
                            bool highLow = (0 == t) ? true : false;
                            float time = 0;
                            while(time < T[value].min) {
                                bool wordEnd = (kSpiWordLength - 1 == clk);
                                writeBitField(out, clk, highLow);
                                time += kSpiPeriodNs;
                                if(wordEnd)
                                    time += fSpiInterWordTime;
                                ++clk;
                                if(clk / kBitsPerByte > numOut)
                                    return -1;
                            }
                            if(time > T[value].max) {
                                printf("Error: expected %f but got %f\n", T[value].max, time);
                                return 0;
                            }
        
                        }
                    }
                    ssize_t numBytes = ((clk + kSpiWordLength - 1) / kSpiWordLength) * kSpiWordLength / kBitsPerByte; // round up to the next word
                    if(numBytes > numOut) // just in case numOut was not a multiple, we now no longer fit
                        return -1;
                    return numBytes;
                }
        
                void SendSpi(uint8_t* data, size_t length)
                {
                    if(length < kSpiMinTransferSize)
                        length = kSpiMinTransferSize;
                    if (fSpi.transfer(data, NULL, length) != 0)
                        printf("SPI: Transaction Failed\r\n");
                }
                
                void SendBufferToLeds(uint8_t* buffer, ssize_t buffersize, uint8_t* out, ssize_t outsize)
                {
                    ssize_t len = kSpiLeadingZeros + rgbToClk(buffer, buffersize, out + kSpiLeadingZeros, outsize - kSpiLeadingZeros);
                    if(len < 0) {
                        fprintf(stderr, "Error: message too long\n");
                        return;
                    }
                    unsigned int bytesPerWord = kSpiWordLength / kBitsPerByte;
                    for(unsigned int n = 0; n < len; n += bytesPerWord) {
                        for(unsigned int b = 0; b < bytesPerWord / 2; ++b) {
                            uint8_t tmp = out[n + b];
                            out[n + b] = out[n + bytesPerWord - 1 - b];
                            out[n + bytesPerWord - 1 - b] = tmp;
                        }
                    }
                    if(kSpiMsbFirst) {
                        for(unsigned int n = 0; n < len; ++n)
                            out[n] = __builtin_bitreverse8(out[n]);
                    }
                    SendSpi(out, outsize);
                }
                         
            public:
                        
                LedStripSpi() 
                {
                    SetType(WS2812B);
                    fNbrLeds = 0;
                    fDataBuffer = new uint8_t[kSpiMaxTransferSize];
                    fCurrentSendingLedBuffer = 1;
                    fCurrentEditingLedBuffer = 0;
                    fLedBuffers[0] = NULL;
                    fLedBuffers[1] = NULL;
                    fBufferSwitched = false;
                }
                
                bool Init(LedType ledtype, unsigned int nbrleds)
                {
                    if(fNbrLeds > 0) // we don't call init sevelal times
                        return false;
                    SetType(ledtype);
                    if(fSpi.setup({ .device = "/dev/spidev2.1", // Device to open
                                .speed = kSpiClock, // Clock speed in Hz
                                .delay = 0, // Delay after last transfer before deselecting the device, won't matter
                                .numBits = kSpiWordLength, // No. of bits per transaction word,
                                .mode = fSpiMode // SPI mode
                    }) != 0) {
                        printf("SPI: Initialisation Failed\r\n");
                        return false;
                    }
                    // Led buffers initialisation           
                    fNbrLeds = nbrleds;
                    ssize_t buffersize = fNbrLeds*kBytesPerRgb;
                    fLedBuffers[0] = new uint8_t[buffersize];
                    memset(fLedBuffers[0] ,0,buffersize);
                    fLedBuffers[1] = new uint8_t[buffersize];
                    memset(fLedBuffers[1] ,0,buffersize);
                    return true;
                }
                
                void Draw()
                {
                    if(!fDataBuffer || !fLedBuffers[fCurrentSendingLedBuffer] || !fBufferSwitched) //draw only if new buffer
                        return;
                    fBufferSwitched = false;
                    uint8_t* buffer = fLedBuffers[fCurrentSendingLedBuffer];
                    SendBufferToLeds(buffer, fNbrLeds*kBytesPerRgb, fDataBuffer, kSpiMaxTransferSize); 
                }
                
                void Begin()
                {
                    memset(fLedBuffers[fCurrentEditingLedBuffer] ,0,fNbrLeds*kBytesPerRgb);
                }
                
                void SetLedColor(int idxled, uint8_t red, uint8_t green, uint8_t blue)
                {
                   if(idxled >= fNbrLeds)
                       return;
                   uint8_t* buffer = fLedBuffers[fCurrentEditingLedBuffer];
                   buffer[idxled * kBytesPerRgb] = (fReverseByte ==  true) ? __builtin_bitreverse8(red) : red;
                   buffer[idxled * kBytesPerRgb + 1] = (fReverseByte ==  true) ? __builtin_bitreverse8(green) : green;
                   buffer[idxled * kBytesPerRgb + 2] = (fReverseByte ==  true) ? __builtin_bitreverse8(blue) : blue;
                }
                
                void SetAllLedsBlack()
                {
                   memset(fLedBuffers[fCurrentEditingLedBuffer] ,0,fNbrLeds*kBytesPerRgb); 
                }
                
                void End()
                {
                   uint8_t tmp = fCurrentEditingLedBuffer;
                   fCurrentEditingLedBuffer = fCurrentSendingLedBuffer;
                   fCurrentSendingLedBuffer = tmp;
                   fBufferSwitched = true;
                   
                }
                
                
                ~LedStripSpi()
                {
                    
                    delete fLedBuffers[0];
                    fLedBuffers[0] = NULL;
                    delete fLedBuffers[1];
                    fLedBuffers[1] = NULL;
                    delete fDataBuffer;
                    fDataBuffer = NULL;
                }
        };
        #endif

        here a sample usage with a trill bar :

        #include <Bela.h>
        #include "ledstripspi.h"
        #include <libraries/Trill/Trill.h>
        
        Trill gTouchSensor;
        LedStripSpi gLedStrip;
        
        unsigned int gNbrLeds = 12;
        
        unsigned int gRefreshRate = 50000;
        unsigned int gTrillRefreshRate = 100000;
        
        
        void LedStripTask(void*)
        {
        	while(!Bela_stopRequested()) {
        		gLedStrip.Draw();
        		usleep(gRefreshRate);
        	}
        	//Back to black
        	gLedStrip.Begin();
        	gLedStrip.End();
        	gLedStrip.Draw();
        }
        
        // set the leds colors in funtion of the position (0 to 1)
        void RenderBarGraph(float position)
        {
            position = position * gNbrLeds;
            float luminosity = 1;
            for(int i = 0; i < gNbrLeds; i++) {
            	if((position - i) < 1)
            		luminosity = position - i;
            	else if(position < i)
            		luminosity = 0 ;
            	else luminosity = 1;
            	gLedStrip.SetLedColor(	i,
            							0x20*(float) (i/ (float) gNbrLeds)*luminosity,
            							0x14*(float) ((gNbrLeds-i)/ (float) gNbrLeds)*luminosity,
            							0*luminosity);
            }
        }
        
        //Take the position of the trill bar and render the bar graph
        void TrillBarTask(void*)
        {
        	float Position;
        	while(!Bela_stopRequested()) {
                gTouchSensor.readI2C();
                int ActiveTouches = gTouchSensor.getNumTouches();
                if( ActiveTouches > 0) {
                    Position = gTouchSensor.compoundTouchLocation();
                    gLedStrip.Begin();
                    RenderBarGraph(Position);
                    gLedStrip.End();
                }
                usleep(gTrillRefreshRate);
        	}
        }
        
        
        bool setup(BelaContext *context, void *userData)
        {
        	if(!gLedStrip.Init(LedStripSpi::YF923F5F8,gNbrLeds)) {
        		fprintf(stderr, "Unable to initialise LedStrip\n");
        		return false;
        	}
            //trill
            if(gTouchSensor.setup(1, Trill::BAR) != 0) {
        		fprintf(stderr, "Unable to initialise Trill Bar\n");
        		return false;
        	} 
        	Bela_runAuxiliaryTask(LedStripTask, 90);
            Bela_runAuxiliaryTask(TrillBarTask, 90);
        	return true;
        }
        
        void render(BelaContext *context, void *userData)
        {
        
        }
        
        void cleanup(BelaContext *context, void *userData)
        {
        
        }
        • noah replied to this.
          4 days later

          Hi @PFaivre, thanks for your contribution, what part numbers does YF923F5F8 refer to?

          11 days later

          giuliomoro This assumes BelaMini is used and the neopixels are driven from pin P2.25. To enable that you'll need to run config-pin P2.25 spi after each reboot ( this can be automated later if needed).

          Hi, Could you maybe elaborate on automating this? I could not get this to work with the "user command line arguments"...