Strange behavior with setRGBled

A full fix will certainly only be made by flipping the device. And that is not going to happen for the vast majority of devices. But all the posts here make clear that even the backwards device can be at least partly on in some fashion, some of the time.

I think perhaps I was not clear. I don’t expect to get any control of color back with a simplified call. All you get is “there is something lit” or “there is nothing lit”. It might be red, blue, or something else. The goal was not to fix the LED, but provide a meaningful communication mechanism for developers, via the API, so that you get a consistent “on” and “off”. Because at least that much is possible for both types of Arduboys.

You don’t need to add a new function to the library for this. In a sketch, just only control the red and/or blue LED and always leave the green one off. For an incorrectly installed RGB LED, blue will be red and red will be blue, but as long as you leave green fully off, something will light up.

I was thinking…
As I’ve discussed above, it’s likely not possible to test if the RGB LED has been reversed, using software. However, we could add a flag in the library’s reserved system EEPROM area which indicates whether the unit has the LED installed correctly or not. Users that knew their LED was wrong could load a sketch which would set this flag. The same sketch could also allow resetting the flag for a unit that was repaired or if the flag was wrong for some other reason. This sketch would only have to be run once (or again any time the flag was wrong) because setting EEPROM is non-volatile. This sketch could be included as one of the examples in the library, so it would be easy for users to load and run.

A function named something like rgbLEDincorrect() could be added to the library, which would read the flag and return true or false. A sketch could use the result of calling this function to control the RGB LED according to what it was capable of, like so:

#ifdef AB_DEVKIT
  // running on a Dev Kit, so only control the blue LED (which is all it has)
#else
if (arduboy.rgbLEDincorrect()) {
  // code that uses blue as red and red as blue and always leaves green off
}
else {
  // code that uses the full capabilities of the RGB LED
}
#endif

So, is this something that’s worth doing? Would enough sketches make use of the feature, or would adding it be a waste of time and a waste of a byte in EEPROM? Would owners be bothered to run the sketch required to set the flag properly, if necessary?

2 Likes

I’ve actually been working on a utility sketch and I think I could add that as an option. It would ask a question like “what color is the rgb led” and you just pick red or blue, setting the correct flag in the process.

So the question remains, if we do this is anyone going to take advantage of the feature, or will it just sit there unused while wasting some EEPROM space?

Well - my five pack finally arrived!

And … they all have the backward LED.

I’ve written a test utility to both run up the skills and also check out what works and what does not. One of the things I worked out is exactly what the backwards LED can show.

If you want full compatibility, there are only two settings: BLACK #000000 and MAGENTA #ff0ff … these two should display the same on every production Arduboy. That would be setRGBled(0,0,0) and setRGBled(255,0,255).

For reasons I don’t quite follow, varying the individual red and blue lines does not influence the blue and red (respectively) color intensity. It appears that R=#00 gives no blue, but all of R=#01 through R=#FF give “bright blue”. This is also the case for varying the blue line to influence the red LED. It might be because of the values of the resistors and the odd configuration of them that results when the LED is reversed.

This does mean that there are only four “interesting” red/blue values … #00/#00 #00/#ff #ff/#00 and #ff/#ff. These correspond to Black (off), and Red, Blue, and Magenta colors (on).

For any “on” value, it appears that the intensity can be controlled using the Green line. A green value of #00 gives full intensity, while #ff gives “off”, no matter what state the R/B lines are in. The intensity drop is not linear across the range, but it is noticable for most changes of the G line.

For the value of the EEPROM byte, it would be nice if the byte could be used with a simple converter function that would let you avoid code constructs like the one shown above. Is it possible to use a macro (included from a *.h) which overrides the setRGBled method on your arduboy instance as it exists, replacing it with one that knows about the setting byte and how to map colors? That way, in a nice future with very few funkyLED Arduboys, the code could be dropped just by removing the include that brought it in.

When I get a little time to work out using standard code sharing, I’ll post my “Button & LED Tester”.

1 Like

Yes, I think it would be useful. I’m writing a fun thing that uses the serial port and being able to reliably set the led to red or blue for the two different connection modes would be neat.

Strange. I can’t see why that would be. At the very least, you should be able to vary red or blue individually, while the other one is set to off (along with green off).

So are you saying that, for you, the blue LED just goes from on to off, instead of fading down, when the ARDUBOY logo scrolls down at the start of a sketch that uses begin() with the standard Arduboy library?

Instead of a macro, it would probably be better to add a new function to the library, like setRGBledCorrected(), which would do the mapping you propose and then call setRGBled(). A sketch that wanted the LEDs to work the same on all Arduboys would call setRGBledCorrected(). A sketch that wanted to have full control of a correctly installed LED, or map it themselves, and possibly save some code space, would call setRGBled(). Since for an incorrect LED, green can never be turned on, it would probably be best if setRGBledCorrect() only accepted values for red and blue (so maybe just call it setRBled()?).

We would still need a flag in EEPROM, so the routine would know how to properly map the values.

Read on for a technical explanation of why this is so:

To simplify things, I’ll mostly ignore the resistors in the circuit. The resistors just limit the current, to determine the maximum brightness possible, and prevent the LEDs from burning out.

The RGB LED contains an individual red, green and blue LED in a package that has 4 leads. An LED is a two leaded device. To get 3 LEDs (6 leads total) into a 4 lead package, one lead of each LED is tied to a single common lead in the package. This single lead provides power to all three LEDs. (Technically, this is known as a “common anode” RGB LED.) The other lead of each LED is connected to its own I/O pin (through a resistor), providing the capability to individually control each LED in software.

One thing to understand is that an LED will only work when voltage is applied to it in one direction. One lead (the anode) has to be positive and the other (the cathode) negative. If you reverse the voltage, it won’t light.

Another thing to understand is that the actual logic for the LEDs is reversed. The common lead is tied to a positive voltage, so the I/O pin must be set low to turn it on. The setRGBled() routine compliments the values passed to it, so this “reverse logic” is transparent to the user.

With the RGB LED package installed reversed, the green LED ends up being reversed across the power and its I/O pin. Being reversed, the green LED can never light.

Instead of being attached to power, the common lead is now attached to the green LEDs I/O pin, meaning the red and blue LEDs now get their power from green LEDs I/O pin. Because of the “reverse logic” described above, to turn off an LED the I/O pin is set high. So, when the green LED is off, the high on its I/O pin provides the power to the red and blue LEDs. Setting the green LEDs I/O pin low, to turn it on, provides no power to the red and blue LEDs, which is why they won’t light when green is on.

Now, if the intensity of the green LED is varied between full on and off, it varies the power being supplied to the red and blue LEDs, thus controlling their intensity. Because of the resistors in the circuit, there will be some interaction between the LEDs and, as observed, control may not be linear.

(All “settings” are setRGBled() parameters)

Setting 0,0,0 gives “black” (off)

Setting 1,0,0 gives blue.
Setting 0,0,1 gives red
Setting 1,0,1 gives “magenta”

Setting 255,0,0 gives blue.
Setting 0,0,255 gives red
Setting 255,0,255 gives “magenta”.

The colors in the second bunch are just as bright as the first bunch.

So I can certainly control the red and blue individually. But the control (using only the single dedicated color line) is a minimal level of control at best.

For example - 0,0,1 is essentially indistinguishable from 0,0,255. In my utility, those two settings are only two quick button pushes apart. It seems pretty clear that there is no brightness distinguishing these two.

Now, during the initial scroll down of “ARDUBOY”, the blue led does taper off in color.

I can reproduce that behaviour, but only by manipulating the green line.

1,0,0 is bright blue.
1,128,0 is only slightly dimmed
1,160,0 is more dimmed
1,240,0 is well dimmed, but each step up to
1,254,0 is still dimmer until
1,255,0 is black

One of my button options is a random LED setting. Given the lack of variation in red and blue control, and that for most of the green line range, the red and blue are still quite bright, most pushes of the random button give you “bright magenta”.

Well, here’s the code that scrolls the logo and dims the LED. As you can see, it always leaves green at 0 and only controls red.

void Arduboy::bootLogo()
{
  // setRGBled(10,0,0);
  for(int8_t y = -18; y<=24; y++) {
    setRGBled(24-y, 0, 0);

    clear();
    drawBitmap(20,y, arduboy_logo, 88, 16, WHITE);
    display();
    delay(27);
    // longer delay post boot, we put it inside the loop to
    // save the flash calling clear/delay again outside the loop
    if (y==-16) {
      delay(250);
    }
  }

  delay(750);
  setRGBled(0,0,0);
}

OK, I think the problem is related to this issue. And because begin() always initialises the tunes functions, thus affecting timer 1, it causes the results of setRGBled() to misbehave.

The reason the LED fades with the ARDUBOY logo is that it’s run before tunes is initialised.

Actually, after thinking about it for a bit longer, I don’t think that it’s important enough to be part of a global setting. Also it’s likely that this setting will be wrong because most people are unlikely to ever run that utility sketch. I’ll just put the configuration question in the program itself. Unless anyone else here has some plans for the led?

To summarize …

The timer is used in PWM mode to control duty cycle (with comparators) to enable variable brightness on the LEDs. This works during boot up.

Once tunes is initialized, it takes over the timer, and prevents it being used for this purpose. As a result, the only capabilities left are pure on and off - and even that may be accidental.

If I have followed this correctly, this would cause issues even on a correctly wired LED.

I note that there is a suggestion to move tunes out of the core library, which would likely solve this issue.

The much bigger and longer term solution might be to dedicate one timer to internal functions, so that any library function can run off the same timer. (I am under no illusions that this is an easy thing to say, and extraordinarily difficult to do.)

Correct. I tried it.

Likely making cultural mistakes in how I do this, but here’s the utility …

https://github.com/smith725/ButtonLedTester-Arduboy

I would appreciate some feedback on:

  1. how this behaves with a proper LED, so that I know that works

  2. how this behaves if someone has patched their library to avoid clobbering setRGBled

You generally get very bright close to white colors. Red and blue are always full on for any value except 0, which will be full off. Green works properly.

Red and blue are probably using timer 1 for PWM, so they don’t work properly because tunes is also using timer 1. Green is probably using timer 0, so it works properly because there’s no timer 0 conflict.

Everything works fine.

You should replace deprecated function clearDisplay() with its sanctioned name clear(), and replace deprecated function getInput() with its sanctioned name buttonsState().

I will definitely update the deprecated calls. Of note, though, I think they are that way in the packaged examples.

As for the LED?

I’m feeling pretty draconian on this one. Pull the PWM capability out of the library - because a timer is a limited and valuable resource. If a project wants to dedicate a timer to the LED control, so be it, but that should not be permanently setup that way. Save the space and resources from the library and return it to project discretion.

Let setRGBled take on/off for each bit, using a color number (make it just setLed(); ). I know that gives you only eight colours and one of those is off. Use g-b-r to assign bits to the numbering so that 0,1,2,3 work everywhere, but 4,5,6,7 only work if you have a proper LED. The LED control byte in EEPROM is nothing more than a marker that says ‘switch the r/b bits’ on the way out to the port.

Push all the specialized stuff - PWM in particular (both for LED and sound) back into selectable libraries. That way, if a project wants the timers for a different purpose, there is a standard way to not attach them to LEDs and sounds.

I realize this is not the somewhat spiffy LED that was expected. But this is no longer a theoretical or design discussion. 80% of the Arduboys cannot light their green LED – and they are going to stay that way. Time to make the best of the hardware that is actually deployed.

I’m going to ramble for a moment.

If I correctly sketched out the “right” and “wrong” versions, I’m wondering if there might still be a way to test this.

Consider the blue LED line. Wired correctly, it starts at Vcc, goes through the blue LED, through a resistor, and to the control pin. If the pin is a low voltage sink, the LED lights. If the pin is a high voltage source, the LED is dark. If the pin is a high impedance input, then I would expect the pin to float somewhat high - possibly to Vcc less the voltage drop across the blue LED.

Now consider the blue LED line. Wired IN-correctly, it starts at the green control line, goes through a resister, through the red LED, through a resistor, and to the control pin. If the pin is a low voltage sink, the red LED lights - but only if the green pin is a high voltage source. If the pin is a high voltage source, the LED is dark. If the pin is a high impedance input, then I would expect the pin to float again - but this time to the value of the green pin less the voltage drop across the red LED.

As I see it, in the correct case, swinging the green LED line up and down as an output cannot influence the blue LED line readings as an ADC. In the incorrect case, those readings can be influenced. This suggests that configuring green as an output and blue as an ADC input, and then running a series of up and down swings on the green line while monitoring the blue line would result in different readings for the correct and incorrect cases.

Am I missing something? Perhaps my understanding of the wiring is wrong, and I have been mislead by my diagrams?

Take a look at the polarity of the red LED in this case. There will be no consistent voltage drop across the red LED because if you pull the green pin low, the red LED will be reverse biased.

If the blue LED pin is a high impedance input which is being read with the ADC, you have two cases:

  1. Green LED pin output high: The red LED will be very high impedance until the blue pin is lower than the green pin by the value of the red LEDs forward voltage drop when lit (about 1.8V). Since the blue pin is an input, there’s nothing to actually pull the blue pin down. Stray noise from any EMI could be picked by the blue pin, resulting in an ADC reading anywhere between Vcc and Vcc-1.8V.

  2. Green LED pin output low: Again, for the red LED to not be high impedance the blue pin must drop below the green pin by at least 1.8V. Since the green pin is now at ground, this can never happen. Stray noise from any EMI could be picked by the blue pin, resulting in an ADC reading anywhere between Vcc and ground.

So, if external random noise causes the ADC to occasionally read something below Vcc-1.8V when the green pin is low, there’s a good chance that the RGB LED is incorrect. However, if the ADC never reads below Vcc-1.8V that doesn’t mean the RGB LED is correct. There just might not be any random noise pulling the blue pin down.

However, the above cases are evaluating DC characteristics. An LED has some internal capacitance and therefore will conduct an AC signal to some extent. It’s possible that if you were to toggle the green LED pin at a high enough frequency, it would be coupled through the capacitance of the red LED and could be sensed by an ADC reading on the blue pin.

I considered this possibility previously, but didn’t feel like spending the effort to experiment. It would require a fair amount of code and I didn’t feel it would be worth it for the value that it provides. Besides, with me not having an actual Arduboy, I’m not sure any results that I get from my home made system would apply to the real thing. (The RGB LED I’m using isn’t the same one that the Arduboy uses.)

Just forcing the owner to set a flag in EEPROM one time is a far simpler solution, if even doing that is worth it.

You must have a very old version of the Arduboy library installed.

I admit we are now deep in the parts of the datasheet that nobody usually reads.

Although the input impedance of the ADC is high, it is not infinite. And although the reverse current through a reverse biased diode is extremely low, it is not zero.

The input impedance might be megaohms, and the reverse current might be microamps.

In this case, however, both of these things work in our favor. Nothing is dragging the blue pin high, and both non-infinite input impedance and reverse current will leak any small potential out of the reading. I would think that the result would be a relatively high assurance that the ADC reading will not stay even above 1/2 Vcc. That would give a very high probability that the two circumstances can be reliably separated.

In such circumstances, I don’t think you even need to go into AC territory. Simply setting up the second configuration and holding it for a tenth of a second would likely be enough to let the reading stabilize. Alternatively, rather than a timer, just repeatedly read the ADC until it stabilizes (with a very high timeout cap on the number of readings) and then use that result.

All that said - this needs to be done with the real parts, and on more than one device. Variability in reverse bias current could be quite high (although always absolutely low).

However …

You have notably more experience at setting up the test scenarios. Is the code overly complex? If it could be embedded in a callable subroutine (which likely needs to override some of the normal Arduboy setup) I can provide test feedback from the five units in my fun pack.