psieg / Lightpack

Lightpack and Prismatik open repository
GNU General Public License v3.0
1.59k stars 189 forks source link

RGBW Support for Prismatic #278

Open DerDude87 opened 5 years ago

DerDude87 commented 5 years ago

Hello @ all !

I try to use RGBW LED Strips with almost identic Chip to WS2812B, but i cant make it work. The correct Name of the Chip is SK6812. I try to use a fix in the FastLED Libary to make it worke but nothing happend ! ! ! (here ist the Link to the fix to use RGBW as RGB Stripes... beause it is the same timing on the Chips: https://www.partsnotincluded.com/programming/fastled-rgbw-neopixels-sk6812/ )

With the Adafruitlibary the Stripe workes perfectly but i want to use it as Ambylight on my Desktop with samsung C49HG90. The old Stripe was w2812b but it was damaged and the coulurs wasnt not realy good. That is why i want a real white on the LED to get better results ... but now the big surprise ... it isnt supported @ this time ... So i hope, that all the clever minds does work in the past on it... and it will be supported in next time... or one of the brilliant people conjures up a solution =) .. so im waiting for a good message and hope so strong, that i can use my hardware in short time.... and so many other people will be happy too if its happend ...

Best regards

zomfg commented 5 years ago

You can take your FastLED firmware and change it to Adafruit, it's very similar.

As for the white LED, it won't be very useful unless you have a lot of pure white on the screen all the time.

What I did in my firmware tests is to find RGB equivalent to W and added a condition to check if RGB was in the right zone to switch over to W. It works but in real usage the W is on very rarely.

oleost commented 4 years ago

Hoping for some solution here.

I have hooked up some SK6812 RGBW leds to my monitor, only to find out afterwards that they arent supported :(

zomfg commented 4 years ago

What kind of solution are you looking for? Because read above, and again the W part is overrated in practice, but you can still have it in the firmware if you wish so. This is the fastest/easiest solution imo.

oleost commented 4 years ago

I understand that maybe the W part is overrated. Just happened to be what leds I had laying around. And now they are already on my screen.

Hmm, I dont know how to "You can take your FastLED firmware and change it to Adafruit, it's very similar." Nor "What I did in my firmware tests is to find RGB equivalent to W and added a condition to check if RGB was in the right zone to switch over to W. It works but in real usage the W is on very rarely."

zomfg commented 4 years ago
  1. get Arduino IDE
  2. install Adafruit_Neopixel library
  3. in Tools, select your Board and Port
  4. make new sketch, paste this
  5. change lines 13-14 to reflect your setup
  6. the W:
    • if you want it: change float values on line 92 until the transition RGB/W is seamless (I suggest the colorpicker of the static mood lamp mode in Prismatik for testing)
    • if you only want RGB: remove lines 92-97
  7. click Upload (it'll ask you to save it somewhere if you want)
  8. repeat steps 6-7 until you are happy with your W situation

little disclaimer: I removed some of my custom unrelated stuff from the firmware without testing, but it compiles, so fingers crossed...

oleost commented 4 years ago

Thanks for taking your time trying to help me.

Look like I run into another limitation, I have 110 leds, and the Arduino give me a overflow warning.

image

Any solution to this?

zomfg commented 4 years ago

change line 30 to this

oleost commented 4 years ago

I did that change, compiles succesfully. But still having issue.

If I set both number of pixels to 50 in firmware and in Prismatik it "works". If I changed both to 110 it fails..

zomfg commented 4 years ago

define ""works"" and "fails.."

oleost commented 4 years ago

When it fails, the arudino boots, lights up the first pixel 4 times, first green, then red,then blue and lastly white. And then there is no light at all. Only power light is steady on the arduino itself. It also does that light sequence when I go through the configuration wizard in Prismatik.

When it "works". Example I set it to 55, half of my pixel lights up. And they show correct colour.

EDIT: Also I have verified that all my leds work if I use RGBWstrandtest and set it to 110. EDIT2: Also seems like settings firmware to 110, and Prismatik to example 55 "works" also. (Only lighting up half of the pixels.) ( Seems to stop working around 60+ leds) EDIT3: https://youtu.be/dmKtWCbzAI4 EDIT4: Just to be sure, I have now tried with a different Arduino aswell. (Arduino UNO and a Nano). And I injected power in the other end aswell

zomfg commented 4 years ago

my bad, change lines 20-21

oleost commented 4 years ago

Thank a lot for your time and effort. That did the trick.

oleost commented 4 years ago

Still having some issues, unsure if its unrelated or not.

Colour and screengrab doesnt seems to match.

Example when using the "appearance of grab widget" arent the leds supposed to change colour to match? image

But at the same time when using "mood lamp" the leds change colour correctly. image https://youtu.be/ouyK5LITxk0

Also, if I run this Ambilight test video, when the entire screen is lit with one colour, every is okey. but when there are multiple colours on screen it doesnt seem to work? https://youtu.be/lU2K7R_HJ70

Tried using both "WinAPI" and "Win8 Dekstop Duplicatino"

Profile.zip

zomfg commented 4 years ago

You have "Average color on all LEDs" checked

oleost commented 4 years ago

Thanks again, I think that was the only option which I have not tried changing. Now it looks great. Not sure how I didn't see that one.

DerDude87 commented 4 years ago

I get this Errorcode if i try to push it on the Arduino ... Can u help me with it pleas oleost ?

Arduino: 1.8.10 (Windows 10), Board: "Arduino Nano, ATmega328P (Old Bootloader)"

RGBW_Amby_code:116:2: error: expected unqualified-id before '-' token

}-void setup() {

^

C:\Users\DerDude\Documents\Arduino\RGBW_Amby_code\RGBW_Amby_code.ino: In function 'void loop()':

RGBW_Amby_code:121:6: error: redefinition of 'void loop()'

void loop() {

  ^~~~

C:\Users\DerDude\Documents\Arduino\RGBW_Amby_code\RGBW_Amby_code.ino:53:6: note: 'void loop()' previously defined here

void loop() {

  ^~~~

Mehrere Bibliotheken wurden für "Adafruit_NeoPixel.h" gefunden Benutzt: C:\Users\DerDude\Documents\Arduino\libraries\Adafruit_NeoPixel exit status 1 expected unqualified-id before '-' token

Dieser Bericht wäre detaillierter, wenn die Option "Ausführliche Ausgabe während der Kompilierung" in Datei -> Voreinstellungen aktiviert wäre.

Kraechtshammer commented 4 years ago

First of all, thank you @zomfg for the code, helped me and my mate (both of us use SK6812 RGBW) a lot! In the search for a better way to calculate rgbw values on slower boards or on high refresh rates, he changed the rgb to rgbw function a bit and we think this might be a better way as it always uses the white channel while not using much resources:

//defining a type makes passing the color easier
struct colorRgbw {
  uint8_t   red;
  uint8_t   green;
  uint8_t   blue;
  uint8_t   white;
};

//converts rgb to rgbw values
colorRgbw rgb2Rgbw(uint8_t red, uint8_t green, uint8_t blue) {
  uint8_t minVal = min(red, min(green, blue));
  uint8_t r = red - minVal;
  uint8_t g = green - minVal;
  uint8_t b = blue - minVal;

  //divisor must be above 3 (rgb(255,255,255) results in rgbw(0,0,0,255) then)
  //can be customized as the white LED tends to be brighter than the other channels, 4 is a sweet spot for me.
  uint8_t w = ((red - r) + (green - g) + (blue - b)) / 4;

  //most SK6812 RGBW strips aren't RGBNW (natural white), so a bit of compensation should be done.
  //this can either be done by using psieg's color temperature slider or on the microcontroller.
  //In device setup (when using psieg's fork), do not use any color correction (set r,g,b to 100) as that only really works for rgb LEDs.

/*
  //a theory (not tested but pretty straightforward): to compensate cold or warm white (make it neutral), one could use the following (choose one line):
  //cold white:
  r += w / 10; //whatever divisor works best
  //warm white:
  b += w / 10; //whatever divisor works best
  //additionally, white should probably be turnt down a bit to not overbrighten the scene
  w -= w / 10; //use same divisor
*/

  colorRgbw rgbw = {r, g, b, w};
  return rgbw;
}

colorRgbw rgbw; //global initialization as this doesn't have to be reinitialized for every loop

if you wanna use this, paste the above function above the void setup() and replace lines 93-102 with the following:

rgbw = rgb2Rgbw(r, g, b);

pixels[i + R_OFFSET] = rgbw.red;
pixels[i + G_OFFSET] = rgbw.green;
pixels[i + B_OFFSET] = rgbw.blue;
pixels[i + W_OFFSET] = rgbw.white;
Joeri87 commented 3 years ago

Dear people. After changing to a now monitor and and wanting to do a RGBW SK6812 build this is the only place I could find some answers. I'm using an Arduino Nano Every, SK6812 LED strip with RGBW. The whit is perfect 6500K matching the white on the screen. I use the latest Arduino IDE software, installed the Adafrut Neopixel 1.7.0 library and after some tests I found out that Pin 6 is actually pin D6. The RGBW test script works perfect. Now I did copy the above text and uploaded. Did all the steps here https://youtu.be/nzDnr76fZCc?t=463 . I see the Rx orange light flicker on the arduino when I move my mouse or when a video is playing. But the ledstrip only gives light after switching power on/reset and flash RGBW to say its working. Nothing else happens except the Rx light fliker on the arduino itself. I cannot find out what to do...

Eventually I used this:

/*
 * Arduino interface for the use of WS2812 strip LEDs
 * Uses Adalight protocol and is compatible with Boblight, Prismatik etc...
 * "Magic Word" for synchronisation is 'Ada' followed by LED High, Low and Checksum
 * @author: Wifsimster <wifsimster@gmail.com> 
 * @date: 11/22/2015
 */

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
const uint16_t NUMPIXELS = 60;
#define PIN 6

const long serialRate = 500000L;

const uint8_t PIXEL_TYPE = NEO_GRBW;

uint8_t prefix[] = {'A', 'd', 'a'}, hi, lo, chk;
uint16_t i = 0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, PIXEL_TYPE + NEO_KHZ800);
uint8_t *pixels = strip.getPixels();
const byte R_OFFSET = 1;
const byte G_OFFSET = 0;
const byte B_OFFSET = 2;
const byte W_OFFSET = 3;

const uint8_t pixelSize = PIXEL_TYPE == NEO_GRBW ? 4 : 3;
const uint16_t sizeofStrip = NUMPIXELS * pixelSize;

//defining a type makes passing the color easier
struct colorRgbw {
  uint8_t   red;
  uint8_t   green;
  uint8_t   blue;
  uint8_t   white;
};

//converts rgb to rgbw values
colorRgbw rgb2Rgbw(uint8_t red, uint8_t green, uint8_t blue) {
  uint8_t minVal = min(red, min(green, blue));
  uint8_t r = red - minVal;
  uint8_t g = green - minVal;
  uint8_t b = blue - minVal;

  //divisor must be above 3 (rgb(255,255,255) results in rgbw(0,0,0,255) then)
  //can be customized as the white LED tends to be brighter than the other channels, 4 is a sweet spot for me.
  uint8_t w = ((red - r) + (green - g) + (blue - b)) / 4;

  //most SK6812 RGBW strips aren't RGBNW (natural white), so a bit of compensation should be done.
  //this can either be done by using psieg's color temperature slider or on the microcontroller.
  //In device setup (when using psieg's fork), do not use any color correction (set r,g,b to 100) as that only really works for rgb LEDs.

/*
  //a theory (not tested but pretty straightforward): to compensate cold or warm white (make it neutral), one could use the following (choose one line):
  //cold white:
  r += w / 10; //whatever divisor works best
  //warm white:
  b += w / 10; //whatever divisor works best
  //additionally, white should probably be turnt down a bit to not overbrighten the scene
  w -= w / 10; //use same divisor
*/

  colorRgbw rgbw = {r, g, b, w};
  return rgbw;
}

colorRgbw rgbw; //global initialization as this doesn't have to be reinitialized for every loop

void setup() {
  strip.begin();

  // Initial GRB(W) flash
  for (byte offset = 0; offset < pixelSize; offset++) {
    if (offset > 0)
      pixels[0 + offset - 1] = 0;
    else
      pixels[0 + 3] = 0;
    pixels[0 + offset] = 255;
    strip.show();
    delay(500);
  }
  strip.clear();
  strip.show();

  Serial.begin(serialRate);
  // Send "Magic Word" string to host
  Serial.print("Ada\n");
}

void loop() {
  // Wait for first byte of Magic Word
  for(i = 0; i < sizeof prefix; ++i) {
    waitLoop: while (!Serial.available()) {
    }

    // Check next byte in Magic Word
    if(prefix[i] == Serial.read()) continue;
    // otherwise, start over
    i = 0;
    goto waitLoop;
  }
  // Hi, Lo, Checksum  
  while (!Serial.available()) ;;
  hi=Serial.read();
  while (!Serial.available()) ;;
  lo=Serial.read();
  while (!Serial.available()) ;;
  chk=Serial.read();

  // If checksum does not match go back to wait
  if (chk != (hi ^ lo ^ 0x55)) {
    i=0;
    goto waitLoop;
  }

  const uint16_t prismatikLedCount = (((hi << 8) | lo) + 1) * pixelSize;
  const uint16_t stripLimit = min(prismatikLedCount, sizeofStrip);
  //memset(leds, 0, NUM_LEDS * sizeof(struct CRGB));
  // Read the transmission data and set LED values
  for (i = 0; i < stripLimit; i += pixelSize) {
    byte r, g, b, w = 0;
    while(!Serial.available());
    r = Serial.read();
    while(!Serial.available());
    g = Serial.read();
    while(!Serial.available());
    b = Serial.read();

rgbw = rgb2Rgbw(r, g, b);

pixels[i + R_OFFSET] = rgbw.red;
pixels[i + G_OFFSET] = rgbw.green;
pixels[i + B_OFFSET] = rgbw.blue;
pixels[i + W_OFFSET] = rgbw.white;

  }
  for (; i < prismatikLedCount; i += pixelSize)
  {
    while(!Serial.available());
    Serial.read();
    while(!Serial.available());
    Serial.read();
    while(!Serial.available());
    Serial.read();
  }

  // Shows new values
  strip.show();
}

I hope anybody can help me? Thanks a lot in advance!!!

Kraechtshammer commented 3 years ago

Dear people. After changing to a now monitor and and wanting to do a RGBW SK6812 build this is the only place I could find some answers. I'm using an Arduino Nano Every, SK6812 LED strip with RGBW. The whit is perfect 6500K matching the white on the screen. I use the latest Arduino IDE software, installed the Adafrut Neopixel 1.7.0 library and after some tests I found out that Pin 6 is actually pin D6. The RGBW test script works perfect. Now I did copy the above text and uploaded. Did all the steps here https://youtu.be/nzDnr76fZCc?t=463 . I see the Rx orange light flicker on the arduino when I move my mouse or when a video is playing. But the ledstrip only gives light after switching power on/reset and flash RGBW to say its working. Nothing else happens except the Rx light fliker on the arduino itself. I cannot find out what to do...

@Joeri87 That video you mentioned tells you to set baud rate to 115200, though in your code it is set to 500000. This is the bitrate at which the devices communicate, so it must be set to the same. You can either change it in code (Way at the top, 'serialRate') or go into your Prismatik config file and manually set it to 500000 as 115200 is the limit to which you can set it in the official UI. Another option would be to download this repositories fork of the official app, enables you to do a few other cool things, can recommend. Then you'd also be able to set baud rate to 500000. Do tell if your problem is something different.

Joeri87 commented 3 years ago

Thanks for mentioning the baud rate :). I did notice that in the code and used it. Also lightpack gave an FPS warning about the baud rate if I lowered it. But sadly enough. Only the RX led flickers when the screen changes and only at boot the first led of the led strip goes RGBW and off. So it should be connected properly. What could I have done wrong? In the RGBW strandtest script I used only NEO_RGBW, but changing that in this script did not make any difference. The test was beautifull. The LED strip is a BTF-Lightning SK6812 5volt

Kraechtshammer commented 3 years ago

Alright I've compared your code to mine and I can't find any differences, really. This means the problem lies somewhere else. I'd like to see your physical setup... Do note that the data line on the LED strip does have a specific direction marked with little arrows, but I assume you got that right, seeing that you already had a successful test of the strip? What did you use for the upload? Also I seem to remember having to replug the Arduino after every update and restart Prismatik. Maybe you just haven'T tried that yet? Are you sure you have the correct COM Port selected in Prismatik?

Joeri87 commented 3 years ago

Alright I've compared your code to mine and I can't find any differences, really. This means the problem lies somewhere else. I'd like to see your physical setup... Do note that the data line on the LED strip does have a specific direction marked with little arrows, but I assume you got that right, seeing that you already had a successful test of the strip? What did you use for the upload? Also I seem to remember having to replug the Arduino after every update and restart Prismatik. Maybe you just haven'T tried that yet? Are you sure you have the correct COM Port selected in Prismatik?

Thanks for thinking and working with me!!

The strip is still laying loose. I wanted to make sure I could get the SK6812 strip to work before mounting since info on RGBW is rare...

Materials I use are:

So what I did:

When switch the script from the above to the RGBW test script and of couce kill Prismatik, disconnect, reconnect, upload, disconnect, wait, reconnect then all the LED's do a very vibrant demo.

This is the test script that works perfect

// NeoPixel test program showing use of the WHITE channel for RGBW
// pixels only (won't look correct on regular RGB NeoPixel strips).

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN     6

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT  60

// NeoPixel brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 255 // Set BRIGHTNESS to about 1/5 (max = 255)

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGBW + NEO_KHZ800);

etc, etc, etc...

When the Baud was 11500, the Lightpack gives low FPS warning. At 500.000 there is a framerate mentioned and there is no more red cross in the traybar and Prismatik says its sending frames to the arduino.

Could you or any body tell me the steps and versions they use to make it work successful Prismatik with RGBW LED strip?

I'm just a "script kid". I can not program, but have quite a bit experience in windows, hardware (modding and tuning) and networking and I tinker with Raspberry Pi's for some time. This is my first time with an Arduino.

Does this have something to do with it? https://github.com/psieg/Lightpack/issues/425 I also run windows 10 20H2

Joeri87 commented 3 years ago

Okay, so I used the regular Arduino Nano this time and now all works perfect!

BlackRec commented 3 years ago

@Joeri87 Did you find a way to make it work with an Arduino nano every ? I'm struggling... My strip is a sk6812 rgbw, the code from @zomfg is working on my side (the first led of my strip is lighting up red green blue white and then turn off). Then on prismatik during the wizard process I'm choosing adalight, setting the serial port to com5, the baud rate to 500000, hit next, do my led set up and then my strip never light up. My Arduino is flashing orange, so something is processing but no led are on :/

Am I doing something wrong or is the code from @zomfg is outdated ?
Thanks for you help guys !

Joeri87 commented 3 years ago

Hey Quentin, Getting a REAL Arduino Nano solved it. The Every and or copy's of the Arduino Nano didn't work. As soon as I used the official one all was perfect! Just throw that other thing in the trash or make some other project happy with it.... Just not this one...

Just used the Adafruit Neopixel script I posted earlier and proper Arduino Nano Driver CDM v2.12.28 WHQL Certified driver. I have a color calibrated screen and the colors match really nice already!

BlackRec commented 3 years ago

Thanks for your reply ! Well haha I bought the nano every for this project thinking it was a better version than the original nano haha, I will keep it for another project with RGBW led. I will buy the nano from the arduino store to be sure so ! Thank you for your help

Joeri87 commented 3 years ago

Indeed, I made the exact same mistake thinking exact the same! I then was like, the only thing I can do is buy the regular official one an hope for the best. And it did.... I hope changing it worked like it did with me :)

polykoma commented 1 year ago
  1. get Arduino IDE
  2. install Adafruit_Neopixel library
  3. in Tools, select your Board and Port
  4. make new sketch, paste this
  5. change lines 13-14 to reflect your setup
  6. the W:

    • if you want it: change float values on line 92 until the transition RGB/W is seamless (I suggest the colorpicker of the static mood lamp mode in Prismatik for testing)
    • if you only want RGB: remove lines 92-97
  7. click Upload (it'll ask you to save it somewhere if you want)
  8. repeat steps 6-7 until you are happy with your W situation

little disclaimer: I removed some of my custom unrelated stuff from the firmware without testing, but it compiles, so fingers crossed...

Hey there zomfg. Im just trying your code and I couldnt really wrap my head around point 6. and wanted to ask if you may could elaborate this a bit more for me.

I got the SK6812 RGB Neutral White version. (What stripe is to be preferred? RGB WW / NW or CW ?!)

I Noticed, compared to some other rgbw code i ran before, your code seems to output warmwhite when i have a cold white screen. so white always seems to mean warm white. Is there a way to adjust the whiteness? or is that maybe a feature?

Thanks so much for your time <3

zomfg commented 1 year ago

I got the SK6812 RGB Neutral White version. (What stripe is to be preferred? RGB WW / NW or CW ?!)

whichever is closest to the white on your monitor, by default I'd say NW

I Noticed, compared to some other rgbw code i ran before, your code seems to output warmwhite when i have a cold white screen. so white always seems to mean warm white. Is there a way to adjust the whiteness? or is that maybe a feature?

well, that's what the 6. is about basically lines 96-101 could be whatever you want In ideal world, the simplest case would be

    if (r == g && r == b)
    {
      w = g;
      r = g = b = 0;
    }

so on white screen your RGB values would be something like 255,255,255 (bright white), or something lower all the way down to black (0,0,0), and you set your W to either of channel values so it mimics the intensity one problem with this is that 255,255,254 is also white enough to your eyes, but the code above would still use RGB, so you have to build in some kind of margin into your condition other problem is the one you are talking about, Warm White is not 255,255,255 anymore, it'll be way more red like 255,128,42, so r will never == g in the condition above

so that's what I'm trying to solve with

 if (abs((r * 0.45f) - g) < 2 && abs((r * 0.085f) - b) < 2)

the 0.45 and 0.085 are basically G/R and B/R ratios for MY warm white that fits my led strip and screen and < 2 is the margin of what is considered white (2 is an arbitrary pick) and then I use G value for the white intensity as it's in between R (very bright) and B (almost nothing)

you could start with

 if (abs((r * 1.0f) - g) < 2 && abs((r * 1.0f) - b) < 2)

which would work for 254,255,254 for ex and adjust from there to your temperature

or disable W, and run color picker in moodlamp until RGB LEDs match your white screen, then take RGB values from color picker and calculate the G/R, B/R ratios and use those

Tomtim1 commented 1 month ago

@Kraechtshammer As I'm using Arduino Nano (so I guess little power for a lot of demand) to operate my 100 SK6812 LEDs, I've decided to use your edit of @zomfg's code. Is there a way now to also make the calibration of white how I could did with the @zomfg code?

Basically what I'd like to do is to not use W channel while I'm watching movies/gaming and use ONLY W channel when I set pure white (255,255,255) as a static mood color in Prismatic, so I want it to trigger W LED only when pure white. So up until 254,254,254 only mixing RGB, and when 255,255,255, only my warm white LED. I like the warm tone of that white to lit my desk but it appears yellow obviously when gaming with pale blue sky for example, so when there's no perfect white on screen I'd just like to operate on RGB only and use one Prismatic profile, and then turn to second profile (static white color) when needed.

This is my current state of this code:

/*
 * Arduino interface for the use of WS2812 strip LEDs
 * Uses Adalight protocol and is compatible with Boblight, Prismatik etc...
 * "Magic Word" for synchronisation is 'Ada' followed by LED High, Low and Checksum
 * @author: Wifsimster <wifsimster@gmail.com> 
 * @date: 11/22/2015
 */

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
const uint16_t NUMPIXELS = 100;
#define PIN 3

const long serialRate = 1000000L;

const uint8_t PIXEL_TYPE = NEO_GRBW;

uint8_t prefix[] = {'A', 'd', 'a'}, hi, lo, chk;
uint16_t i = 0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, PIXEL_TYPE + NEO_KHZ800);
uint8_t *pixels = strip.getPixels();
const byte R_OFFSET = 1;
const byte G_OFFSET = 0;
const byte B_OFFSET = 2;
const byte W_OFFSET = 3;

const uint8_t pixelSize = PIXEL_TYPE == NEO_GRBW ? 4 : 3;
const uint16_t sizeofStrip = NUMPIXELS * pixelSize;
//defining a type makes passing the color easier
struct colorRgbw {
  uint8_t   red;
  uint8_t   green;
  uint8_t   blue;
  uint8_t   white;
};

//converts rgb to rgbw values
colorRgbw rgb2Rgbw(uint8_t red, uint8_t green, uint8_t blue) {
  uint8_t minVal = min(red, min(green, blue));
  uint8_t r = red - minVal;
  uint8_t g = green - minVal;
  uint8_t b = blue - minVal;

  //divisor must be above 3 (rgb(255,255,255) results in rgbw(0,0,0,255) then)
  //can be customized as the white LED tends to be brighter than the other channels, 4 is a sweet spot for me.
  uint8_t w = ((red - r) + (green - g) + (blue - b)) / 4;

  //most SK6812 RGBW strips aren't RGBNW (natural white), so a bit of compensation should be done.
  //this can either be done by using psieg's color temperature slider or on the microcontroller.
  //In device setup (when using psieg's fork), do not use any color correction (set r,g,b to 100) as that only really works for rgb LEDs.

/*
  //a theory (not tested but pretty straightforward): to compensate cold or warm white (make it neutral), one could use the following (choose one line):
  //cold white:
  r += w / 10; //whatever divisor works best
  //warm white:
  b += w / 10; //whatever divisor works best
  //additionally, white should probably be turnt down a bit to not overbrighten the scene
  w -= w / 10; //use same divisor
*/

  colorRgbw rgbw = {r, g, b, w};
  return rgbw;
}

colorRgbw rgbw; //global initialization as this doesn't have to be reinitialized for every loop
void setup() {
  strip.begin();

  // Initial GRB(W) flash
  for (byte offset = 0; offset < pixelSize; offset++) {
    if (offset > 0)
      pixels[0 + offset - 1] = 0;
    else
      pixels[0 + 3] = 0;
    pixels[0 + offset] = 255;
    strip.show();
    delay(500);
  }
  strip.clear();
  strip.show();

  Serial.begin(serialRate);
  // Send "Magic Word" string to host
  Serial.print("Ada\n");
}

void loop() {
  // Wait for first byte of Magic Word
  for(i = 0; i < sizeof prefix; ++i) {
    waitLoop: while (!Serial.available()) {
    }

    // Check next byte in Magic Word
    if(prefix[i] == Serial.read()) continue;
    // otherwise, start over
    i = 0;
    goto waitLoop;
  }
  // Hi, Lo, Checksum  
  while (!Serial.available()) ;;
  hi=Serial.read();
  while (!Serial.available()) ;;
  lo=Serial.read();
  while (!Serial.available()) ;;
  chk=Serial.read();

  // If checksum does not match go back to wait
  if (chk != (hi ^ lo ^ 0x55)) {
    i=0;
    goto waitLoop;
  }

  const uint16_t prismatikLedCount = (((hi << 8) | lo) + 1) * pixelSize;
  const uint16_t stripLimit = min(prismatikLedCount, sizeofStrip);
  const float green_red_ratio = 0.45f;
  const float blue_red_ratio = 0.085f;
  const uint8_t margin = 2;
  //memset(leds, 0, NUM_LEDS * sizeof(struct CRGB));
  // Read the transmission data and set LED values
  for (i = 0; i < stripLimit; i += pixelSize) {
    byte r, g, b, w = 0;
    while(!Serial.available());
    r = Serial.read();
    while(!Serial.available());
    g = Serial.read();
    while(!Serial.available());
    b = Serial.read();

rgbw = rgb2Rgbw(r, g, b);

pixels[i + R_OFFSET] = rgbw.red;
pixels[i + G_OFFSET] = rgbw.green;
pixels[i + B_OFFSET] = rgbw.blue;
pixels[i + W_OFFSET] = rgbw.white;
  }
  for (; i < prismatikLedCount; i += pixelSize)
  {
    while(!Serial.available());
    Serial.read();
    while(!Serial.available());
    Serial.read();
    while(!Serial.available());
    Serial.read();
  }

  // Shows new values
  strip.show();
}

Thanks!

EDIT: Well, GPT just did what I asked basically. Sadly it works only on 100% brightness level because as I assume, lowering brightness means just values going lower towards black so that's understandable.

/*
 * Arduino interface for the use of WS2812 strip LEDs
 * Uses Adalight protocol and is compatible with Boblight, Prismatik etc...
 * "Magic Word" for synchronisation is 'Ada' followed by LED High, Low and Checksum
 * @author: Wifsimster <wifsimster@gmail.com> 
 * @date: 11/22/2015
 */

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
const uint16_t NUMPIXELS = 100;
#define PIN 3

const long serialRate = 1000000L;

const uint8_t PIXEL_TYPE = NEO_GRBW;

uint8_t prefix[] = {'A', 'd', 'a'}, hi, lo, chk;
uint16_t i = 0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, PIXEL_TYPE + NEO_KHZ800);
uint8_t *pixels = strip.getPixels();
const byte R_OFFSET = 1;
const byte G_OFFSET = 0;
const byte B_OFFSET = 2;
const byte W_OFFSET = 3;

const uint8_t pixelSize = PIXEL_TYPE == NEO_GRBW ? 4 : 3;
const uint16_t sizeofStrip = NUMPIXELS * pixelSize;
//defining a type makes passing the color easier
struct colorRgbw {
  uint8_t   red;
  uint8_t   green;
  uint8_t   blue;
  uint8_t   white;
};

// converts rgb to rgbw values, only using W channel when input is pure white
colorRgbw rgb2Rgbw(uint8_t red, uint8_t green, uint8_t blue) {
  colorRgbw rgbw;

  // If color is exactly pure white (255,255,255), use only the white LED
  if (red == 255 && green == 255 && blue == 255) {
    rgbw.red = 0;
    rgbw.green = 0;
    rgbw.blue = 0;
    rgbw.white = 255; // Max W for pure white
  } else {
    // Otherwise, use RGB values and ignore the W channel
    rgbw.red = red;
    rgbw.green = green;
    rgbw.blue = blue;
    rgbw.white = 0; // Turn off W channel for non-pure white
  }

  return rgbw;
}

colorRgbw rgbw; //global initialization as this doesn't have to be reinitialized for every loop

void setup() {
  strip.begin();

  // Initial GRB(W) flash
  for (byte offset = 0; offset < pixelSize; offset++) {
    if (offset > 0)
      pixels[0 + offset - 1] = 0;
    else
      pixels[0 + 3] = 0;
    pixels[0 + offset] = 255;
    strip.show();
    delay(500);
  }
  strip.clear();
  strip.show();

  Serial.begin(serialRate);
  // Send "Magic Word" string to host
  Serial.print("Ada\n");
}

void loop() {
  // Wait for first byte of Magic Word
  for(i = 0; i < sizeof prefix; ++i) {
    waitLoop: while (!Serial.available()) {
    }

    // Check next byte in Magic Word
    if(prefix[i] == Serial.read()) continue;
    // otherwise, start over
    i = 0;
    goto waitLoop;
  }
  // Hi, Lo, Checksum  
  while (!Serial.available()) ;;
  hi = Serial.read();
  while (!Serial.available()) ;;
  lo = Serial.read();
  while (!Serial.available()) ;;
  chk = Serial.read();

  // If checksum does not match go back to wait
  if (chk != (hi ^ lo ^ 0x55)) {
    i = 0;
    goto waitLoop;
  }

  const uint16_t prismatikLedCount = (((hi << 8) | lo) + 1) * pixelSize;
  const uint16_t stripLimit = min(prismatikLedCount, sizeofStrip);

  // Read the transmission data and set LED values
  for (i = 0; i < stripLimit; i += pixelSize) {
    byte r, g, b, w = 0;
    while(!Serial.available());
    r = Serial.read();
    while(!Serial.available());
    g = Serial.read();
    while(!Serial.available());
    b = Serial.read();

    // Convert RGB to RGBW, only using W for pure white
    rgbw = rgb2Rgbw(r, g, b);

    pixels[i + R_OFFSET] = rgbw.red;
    pixels[i + G_OFFSET] = rgbw.green;
    pixels[i + B_OFFSET] = rgbw.blue;
    pixels[i + W_OFFSET] = rgbw.white;
  }

  for (; i < prismatikLedCount; i += pixelSize) {
    while(!Serial.available());
    Serial.read();
    while(!Serial.available());
    Serial.read();
    while(!Serial.available());
    Serial.read();
  }

  // Shows new values
  strip.show();
}

Here's another approach trying to fix that, but it means that anytime when white channel hits even value (for example R86,G86,B86) it will trigger the W channel and disable RGB. I'll try to use this code for some time as I think there'll be little to no scenarios when these LEDs will hit such even values in games/movies, but we will see.

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

const uint16_t NUMPIXELS = 100;
#define PIN 3

const long serialRate = 1000000L;
const uint8_t PIXEL_TYPE = NEO_GRBW;

uint8_t prefix[] = {'A', 'd', 'a'}, hi, lo, chk;
uint16_t i = 0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, PIXEL_TYPE + NEO_KHZ800);
uint8_t *pixels = strip.getPixels();
const byte R_OFFSET = 1;
const byte G_OFFSET = 0;
const byte B_OFFSET = 2;
const byte W_OFFSET = 3;

const uint8_t pixelSize = 4;
const uint16_t sizeofStrip = NUMPIXELS * pixelSize;

struct colorRgbw {
  uint8_t red;
  uint8_t green;
  uint8_t blue;
  uint8_t white;
};

colorRgbw rgb2Rgbw(uint8_t red, uint8_t green, uint8_t blue) {
  colorRgbw rgbw;

  // Check if all values are equal (any shade of pure white)
  if (red == green && green == blue) {
    // Use only white channel, scaled to input brightness
    rgbw = {0, 0, 0, red};
  } else {
    // For any other color, use RGB only
    rgbw = {red, green, blue, 0};
  }

  return rgbw;
}

colorRgbw rgbw;

void setup() {
  strip.begin();

  // Initial GRB(W) flash
  for (byte offset = 0; offset < pixelSize; offset++) {
    if (offset > 0)
      pixels[0 + offset - 1] = 0;
    else
      pixels[0 + 3] = 0;
    pixels[0 + offset] = 255;
    strip.show();
    delay(500);
  }
  strip.clear();
  strip.show();

  Serial.begin(serialRate);
  Serial.print("Ada\n");
}

void loop() {
  // Wait for first byte of Magic Word
  for(i = 0; i < sizeof prefix; ++i) {
    waitLoop: while (!Serial.available()) {}
    if(prefix[i] == Serial.read()) continue;
    i = 0;
    goto waitLoop;
  }

  while (!Serial.available()) ;;
  hi=Serial.read();
  while (!Serial.available()) ;;
  lo=Serial.read();
  while (!Serial.available()) ;;
  chk=Serial.read();

  if (chk != (hi ^ lo ^ 0x55)) {
    i=0;
    goto waitLoop;
  }

  const uint16_t prismatikLedCount = (((hi << 8) | lo) + 1) * pixelSize;
  const uint16_t stripLimit = min(prismatikLedCount, sizeofStrip);

  for (i = 0; i < stripLimit; i += pixelSize) {
    byte r, g, b;
    while(!Serial.available());
    r = Serial.read();
    while(!Serial.available());
    g = Serial.read();
    while(!Serial.available());
    b = Serial.read();

    rgbw = rgb2Rgbw(r, g, b);

    pixels[i + R_OFFSET] = rgbw.red;
    pixels[i + G_OFFSET] = rgbw.green;
    pixels[i + B_OFFSET] = rgbw.blue;
    pixels[i + W_OFFSET] = rgbw.white;
  }

  for (; i < prismatikLedCount; i += pixelSize) {
    while(!Serial.available());
    Serial.read();
    while(!Serial.available());
    Serial.read();
    while(!Serial.available());
    Serial.read();
  }

  strip.show();
}