Different colors on WS2812 leds on same channel

Hi.

For general drone orientation while flying, I like to have a red led on the left, a green red on the right, white leds on the back and yellow leds on the front, but as far as I could see, all WS2812 leds on a channel share color. I don’t know if there have been variations on this fixed scheme.

On a Cinewhoop I placed 32 leds on its FC single channel:

  • 1-8: front right (will be green);
  • 9-16: front left (will be red)
  • 17-24: back right (will flash white)
  • 25-32: back left (will flash white

Not knowing much about C++ and ArduCopter code, I did the following modifications on v4.5.7:

libraries/AP_Notify/RGBLed.cpp:

uint32_t RGBLed::get_colour_sequence_traffic_light(void) const
{
    if (AP_Notify::flags.initialising) {
        return DEFINE_COLOUR_SEQUENCE(RED,GREEN,BLUE,RED,GREEN,BLUE,RED,GREEN,BLUE,BLACK);
    }

    if (AP_Notify::flags.armed) {
//        return DEFINE_COLOUR_SEQUENCE_SLOW(RED);
          return DEFINE_COLOUR_SEQUENCE_SLOW(WHITE);
    }
...

(when armed, instead of flashing red, flashing white (leds 17-32))

libraries/AP_Notify/SerialLED.cpp:

bool SerialLED::hw_set_rgb(uint8_t red, uint8_t green, uint8_t blue)
{
    if (enable_mask == 0) {
        // nothing is enabled, no pins set as LED output
        return true;
    }

    AP_SerialLED *led = AP_SerialLED::get_singleton();
    if (led == nullptr) {
        return false;
    }

    for (uint16_t chan = 0; chan < 16; chan++) {
        if ((1U << chan) & enable_mask) {
            const uint8_t num_leds = 32;

            for (uint16_t i = 0; i < num_leds; i++) {
                uint8_t r = red;
                uint8_t g = green;
                uint8_t b = blue;

                if (i < num_leds / 4) {
                    // Verde
                    r = 0;
                    g = 255;
                    b = 0;
                } else if (i < num_leds / 2) {
                    // Rojo
                    r = 255;
                    g = 0;
                    b = 0;
                }

                led->set_RGB(chan + 1, i, r, g, b);
            }
        }
    }

    for (uint16_t chan = 0; chan < 16; chan++) {
        if ((1U << chan) & enable_mask) {
            led->send(chan + 1);
        }
    }

    return true;
}

(Direct modification on leds 1-16. Note that class AP_SerialLED does not expose the number of leds on a channel, so this ugly code is valid for my particular case (which has a single channel, so code could be simplified)).

The result is what I wanted (shown armed):

Front right red leds and front right green leds don’t flash, but it is enough for me.

Is already there or will be a more elegant solution, avoiding the ugly code above?

This is an easy Lua script. No need for custom firmware. But if you are set on custom firmware, WS2812s can be individually controlled using existing methods (Lua scripts just call those).

Example:

1 Like

Thanks for the information but:

  • FC is Kakute H7 mini nand: no external μSD. I don’t know how to use lua scripts in this case.
  • Initially, only one WS2812 channel (LED signal). Certainly, there are unused motor outputs, but Cinewhoop dissassembly takes a long time.

The problem is on lines similar to:
serialLED:set_RGB(chan_left, -1, br_flash, br_flash, br_flash)
I think -1 (all leds) is used everywhere).

That ugly code shown is easy to modify, even with flashing (recognize BLACK), but the colors used are simple, and if the drone is far away and you see flashing you are looking at the back, and not seeing flashing you are looking at the front.

Additionally:

  • On the classes definition, there is no way (or I don’t see how) to know programatically how many leds are in a channel.
  • On the classes definition, there is no way (or I don’t see how) to know programatically how many channels are used. Here and there:
    for (uint16_t chan = 0; chan < 16; chan++)
    Flash and ram may be free, but not time.

WS2812s do not report LED count, so there is never a way to programmatically determine that.

1 Like

Configuration:
NTF_LED_LEN: Serial LED String Length

libraries/AP_Notify/AP_Notify.cpp:
AP_GROUPINFO("LED_LEN", 9, AP_Notify, _led_len, NOTIFY_LED_LEN_DEFAULT)

So being not easy to find, an easy solution is to write it directly on the ugly code.

Worse for channels number.

Yes. It has to be a parameter or magic number. There is no method to get the count by querying the WS2812s - it’s a one-way data protocol. Technically you could loop the string back so that the last LED connects to another GPIO and then do some iteration to derive LED count, but that’s extra wiring and more kind of crappy code.