aRGB LEDs and ATTiny

I am doing a relatively simple project (in terms of programming at least); i have two aRGBs that are to get their color and intesity from my uploaded arduino code.

I have got this to work on several different processors, last the ATTiny85, though if I try to compile the same code for the new ATTiny816, I get errors from both FastLED and NeoPixel respectively (I tried making a version of my code with each, separately) saying basically that the CPU is too slow. But the ATTiny816 isn't slower than the ATTiny85?

Or did I miss something?

(I have the megaTinyCore installed for the 0- and 1-series ATTiny)

Is there another library I should use for controlling aRGBs from the slower CPUs, or is there something else I am doing wrong?

It is ofcourse possible I did something wrong in the setup of the settings for my ATTiny816, or the setup of SpenceKonde's core, even though I did follow instructions from a website on how to set up Arduino to program the new ATTinys.

What errors are you receiving?

1 Like

in most cases i get;

exit status -1073741515
Error compiling for board ATtiny3226/3216/1626/1616/1606/826/816/806/426/416/406.

I have tried my code on other computers where I instead have got the error message I described initially, though I have to wait till tomorrow to copy the exact error message regarding the CPU speed (The most verbose error message were from NeoPixel in that regard).

However I believe the wording was "CPU SPEED NOT SUPPORTED"

EDIT:
For reference, I am using Arduino IDE 1.8.13 as recommended by SpenceKonde on the megaTinyCore GitHub

megaTinyCore includes two neopixel libraries!

tinyNeoPixel is a dropin replacement for adafruit Neopixel in every way, the only things that change are the classname and the name of the include. This thus has the same downsides as the original version:

  • It pulls in like 1.5-2k of stupid overhead for malloc and free, required for the on-the-fly configuration of the length of the neopixel string.
  • Because the buffer is allocated dynamically, the potentially large amount of memory used by that buffer is not included in the report at the end of compilation. In both tinyNeoPixel and Adafruit_NeoPixel, you can, say, on a tiny with 512b ram, ask for a 200 pixel long string. No compile error, but when you try to run it, it does everything it should except that there are no lights, because show() checks that the buffer pointer isn;t null before using it, and since malloc failed to allocate a 600 byte section of memory - which is understandable since you only had 512b*
  • It uses pinMode(), so despite the fact that you likely only set pinmode for most pins at the start of the sketch, and never use a non-constant pin number when you change the pinMode of any pin, (this is a condition frequently met by a sketch that's reasonably well written without special effort, but ofc not always), if you perform the performance and space optimization of replacing all the pinMode() calls with pinModeFast(), you don't get the final bonus that makes it worthwhile from space perspective because there are still two calls to it. (and if you're really going on a rampage out of desperation for space - I know this because that's what I was doing when I wrote the Static version below - you can go through the code and eliminate every call to digitalWrite() and digitalRead() and replace them with the fast versions to save space. But that cant happen if a library is calling pinMode.

tinyNeoPixel_Static is a more efficient modification. It is not quite a dropin replacement, though - instead of just changing two names, you also have to declare the buffer array yourself and pass as pointer to the constructor. Changing string length at runtime is prohibited, and you are expected to set the pin output (and hence can exploit the fact that it's a constant - the code cannot do this automatically - if I test if(__builtin_constant_p(pin)) {pinModeFast(pin,OUTPUT);} else {pinMode(pin,OUTPUT)} - that will always take the second branch if it's inside of a method of a class (or in proper C++ jargon, a "member function"). Class member functions, but not static member functions of a class, pretty much dynamite any hope of most optimizations. These changes have the following impacts, relative to the ones described above:

  • You save 1.5-2k of flash if you don't use malloc and free elsewhere.
  • The memory used by the frame buffer is included in the report when compiling the sketch. If you try to compile for a 200 LED string, which needs a 600b buffer on your 512k chip, you'll get a compile error at the end, because it will report that it's asking the compiler to allocate more ram than is present on the hardware. This is vastly preferable to compiling and not working. Adafruit_NeoPixel, like most Arduino libraries, also doesn't have good ways to report failures, and misses opportunities to return status codes *, and no steps have been taken to change that; they probably should be.
  • You must ensure that the pin is set output.

On both of these libraries two additional things of note (API extensions specific to tinyNeoPixel):
1, On any part with more than 4k of flash (at 4k flash and tinyNeoPixel_Static, you still care about every byte, and this isn't useful enough to justify it), there is also a show(n), which will output only the first n LEDs. This gives a way to simulate changing the length too (the actual reason I put that in, I'll admit, was selfish - I needed it for hardware test and characterization code I was writing (I needed to know how much current code that I was making.
2. There's an updateLatch() method to adjust the duration of the latch time lockout - after show() is called, if you call it a second time less than the lockout period (up to 255us supported). For compatibility we copy the adafruit latch time of 50 us by default. The reason we have this method is that despite the datasheet (which the adafruit library followed slavishly - it's as if it was written using the datasheet and a scope, without access to actual LEDs), LEDs with the datasheets claimed 50us latch timer may not actually exist. The originals had 6us. Ones with 200us definitely exist, and ones with other values likely exist, since there are dozens of near-clones of the 2812's.

* I lay the blame at Arduino's feet here. They started the tradition of having functions that could fail do so silently, rather than using the well established and widely practiced method of having such functions return status codes rather than void, so you can see when they fail and possibly why. They could have set a norm that most functions that in our reality return void, but are not assured of success (from digitalWrite() and pinMode() - you can pass in pins that don't exist). Nor have have they embraced compiletime error checking like I do (try digitalWrite(200,LOW), and seem to have been almost trying to push as much stuff that should be a compile error into runtime misbehavior instead of the opposite. But that bad practice was copied by people trying to copy arduino style and present a familiar API in their libraries.

Oh - and one last thought -
Consider moving to either the 2-series or the 16k flash versions. Neopixels like RAM:

  • 8k 1-series gets 512b ram, which has a high chance of being insufficient for applications involving neopixels. 200 pixels are out of the question, and 150 is doubtful (after accounting for other overhead)
  • 8k 2-series has 1k ram, 250 is surely possible, and 300 might be.
  • 16k 1-series gets 2k (same as 32k) and more peripherals. Now 550 pixels is a definite yes,
  • 16k 2-series gets 2k, and 32k 2-series gets 3k. 850-900 pixels possible.

You see my point. And the economics?
816 is 90 cents in microscopic QFN (3mm x 3mm), $1.11 in brick sized SOIC. 1616 is 75 cents and $1.16 respectively, 32k is brick only and $1.27. 8k 2-series would be , 90 cents and $1.13 16k 2-series $1.00 and $1.22, and finally 32k 2-series is $1.06 and $1.29....

So if you're using the QFN you could save money and quadruple your ram by using the larger flash size chip. (No joke - look: https://www.microchipdirect.com/product/ATTINY1616-MN - and yes, that price is anomalously low, and if you're not, you could double your ram it for 2 cents (though a 2-series is a general feature shakeup, you lose 2 PWM pins, but get a much better ADC), or quadruple it while staying on a 1-series for only 5 cents (and you get the bonus features). So like yeah... Might want to spend an extra nickel... 32k flash ones cost just 11 cents more than 8k ones, too... and for only 18 cents above the 816 is the 3226, with 6x the ram (and no pwm on PC0/1, but an awesome ADC)

why the libraries don't work - Adafruit neopixel only supports a few speeds. I don't know what speed you were trying to use, but even if you get past that, the results don't work right, because the results come out too fast, because the ST instruction got blessed by the AVRxt instruction timing enhancements, and is now only 1 clock instead of 2, so their loop runs too fast. (all the inline ASM is also invalid and, were it not for the fact that the compiler can't inline show and thus perform secondary optimizations on it, it would never have worked, but because it's sequestered in a function whose arguments are passed as copies, the fact that it trashes things that it says it doesn't (and doesn't trash something that it says it does) doesn't matter. That actually lets us make tinyNeoPixel work all the way down to 5 MHz, maybe even 4.... I think the main reason these were never updated by adafruit is that there's an option that has the same API distributed with megaTinyCore and DxCore. And they don't sell any modern AVR based boards I don't think.

FASTled has not been properly maintained in a while. You look at that code and it;'s just an unholy festival of creeping featurism and kitchensinkism, the code is likely only understandable to the person who wrote it. As a result, after he passed away (there were two maintaining it, now there's only one but I suspect the one who passed away was the one who better understood that tangle of code) in a boating accident a few years back, the maintenance and updating have come to a near standstill. I looked at adapting it and quickly ran away screaming, and used adafruit's library for tinyNeoPixel.

2 Likes

Thanks a lot for an extensive and insightful answer! I did get it to work eventually, though I will start using NeoPixel over FastLED from now on.

You are entirely right in that it always is best to use living (updated) code. I did not know the story regarding FastLEDs developer(s) but that is a bit of important information when deciding what libraries to focus on for a relatively fresh ATtiny-hobby enthusiast like me.

the reason I went with the QFN were size restraints. I am trying to fit this on a very small board. Actually two small boards, set up almost like a bunk bed, where the lower has an USB-C connector, and a 5v->LiPo charger, pluss a LiPo->5v regulator to go back to 5V for my electronics.

The upper board has an CH340E and the ATTIny pluss trough-hole soldering pads for connecting various implements.

This whole thing, including board and battery, should fit inside a 22x22mm box, or a circle with diameter of 23mm

So the size of the ATTiny matters a lot. Much bigger than the QFN and it won't fit on my board.

I have later reconsidered and gone with a ATtiny 841, to get serial capabilities, in the same-size package, since I had issues getting the UPDI to work propperly. Probably me not setting it up right, but after weeks of no progress, I won't pretend the frustration and impatience didn't win.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.