Attiny25/45/85 FW Development Thread

Awesome. I wasn’t sure, since python3 made some pretty significant changes including syntax of common built-ins. Python 2.7 mostly supports the syntax of both (to help people make the jump), and is still in common use despite python3 being out for 8 years now.

Boy, for the ramping level calc, I used this targeting the Q8 SRK, thinking this was about right:

--> for FET+1, 124 levels 4X LED's


level_calc.py 2 124 7135 3 0.3 160 FET 1 10 5000

PWM1 values: 3,3,4,4,5,6,8,9,11,13,15,18,21,25,28,33,37,43,48,55,61,69,77,85,94,
104,115,126,138,151,164,178,193,209,226,243,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
PWM2 values: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,1,1,2,3,3,4,5,6,7,8,9,10,11,12,13,14,15,17,18,19,21,22,23,25,27,28,30,31,33
,35,37,39,41,43,45,47,49,52,54,56,59,61,64,66,69,72,75,78,80,83,87,90,93,96,100,
103,107,110,114,117,121,125,129,133,137,141,146,150,154,159,164,168,173,178,183,
188,193,198,203,209,214,220,225,231,237,243,249,255

For testing though, I'm using a M2-Z with one XPL, and it doesn't look good at all. When ramping up, it ramps quickly in the beginning, seems to pause at one level, then goes up slowly.

Of course the table was created for a 4X LED with good cells, while the M2-Z is 1 LED and running on a Pana B 3400 cell at 3.8v. But still, didn't expect this. Gotta try it in the target light (I got one modded up from a junker SRk 4X). I donn't think it will be much better based on the values in the table - too litle 7135 levels, too many FET levels - that would explain how it functions.

Anything you see here wrong, either the way I did it or the resulting values?

Nope, that looks exactly like what it should be doing… and what you described is exactly why the ramp needs to be re-calculated depending on how many lumens each channel can produce. It doesn’t work much at all like a single-channel driver where a single curve looks pretty okay no matter what the total output is.

If you want to concentrate more of the levels toward the low end, go to the bottom of the script and change the function it uses for the ramp shape (power() and invpower()). The default is cube/cube-root, but you might get better results with a fifth-root. It’ll decrease the distance between levels at the bottom, and increase distance between levels at the top. Or if you want to make the effect pretty extreme, try the e^x curve.

Or, for a test light with only one emitter, you could simply give it accurate parameters… like a max of 1300 lm instead of 5000 lm. :slight_smile:

So, if I were to calculate a FET+1 visually-linear ramp for a single XP-L, here’s how it would look using that same ramp on a quad or on a lower-powered single-emitter light. The original has linear brightness spacing, but if I put it on a quad it’ll have an “elbow” where it suddenly starts ramping up a lot faster. Or if I put it on a lower-powered light, the elbow will bend the other way and the ramp slows down.

I think the one on the right is what happened when you calculated for 5000 lm then used it with only 1300 lm.

Incidentally, the middle image shows how I expect the new E14/S41 is going to behave, since (as far as I’ve heard) it used the old BLF-A6 code unmodified.

Tom
There is ample space for code
Why not 3 ramping modes?
1 500-1500 lumens light
2 1500-4000
3 4000+
You described how it seem to be slowly ramp through certain parts of the range.
Maybe three different rampmodes would prove very cool if using a different rampmode then what the light has as output makes the two other mode show somewhat strange behaviour. It could just work out so that users like to have the ramp go slower on the low or med of higher modes and even use a ramp mode for 4000+ lights on a smaller EDC with just 650 lumens for example
Q8 is just 1 light Narsil is for :wink:

Well, what's look'n good right now on the modded M6 (4500-5000 lms) is this:

level_calc.py 2 128 7135 3 0.3 150 FET 1 1 1500

So it's set to only 1500 lumens max, so basically, the lower I went on max lumens, the better it's been looking. But I think you may be right TK - changing to a fifth root may improve this. I could see the pattern - the lower I made the max lumens, the more levels were given to the 7135.

Now this is all perception, and all my testing is indoors, so there could be some significant problems in my perception. I also should sanity check a measurement of 7135 max only. Also, the little blink at the high end went away, and I think it's because the high end is much smoother now, but that's also where it's very hard to tell it's still ramping

Ahhh - bout the BLF-A6 driver, yes, they are being bought for all sorts of applications. No one, or not many, understand these potential issues.

The Miller - yes, multiple tables could be done. For 140 levels, it's 280 bytes for one table set alone, not to mention the extra management code. I still have 2300 bytes free though.

Here's the code for the table I'm using, I reformat it out to 16 values per row:

#define RAMP_SIZE 132
#define TURBO_DROP_MIN 102
// min level in ramping the turbo timeout will engage,
// level 102 = 105 PWM, this is ~43%
#define TURBO_DROP_SET 92
// the level turbo timeout will set,
// level 92 = 71 PWM, this is ~32%


// Ramping Modes, 132 total entries, 128 generated, 4 more '3's added for 7135:
// level_calc.py 2 128 7135 3 0.3 150 FET 1 1 1500
PROGMEM const byte ramp_7135[] = {
3,3,3,3,3,3,3,4, 4,5,5,6,7,7,8,9,
11,12,13,15,17,18,20,22, 25,27,30,33,36,39,43,46,
50,54,58,63,68,73,78,84, 89,96,102,109,115,123,130,138,
146,155,163,173,182,192,202,213, 223,235,246,255,255,255,255,255, // 49-64
255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255, // 65-80
255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255, // 81-96
255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255, // 97-112
255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255, // 113-128
255,255,255,0
};

PROGMEM const byte ramp_FET[] = {
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,1,3,4,5,7,
9,10,12,14,15,17,19,21, 23,25,27,29,31,33,36,38,
41,43,45,48,51,53,56,59, 62,65,68,71,74,77,81,84, // 81-96
87,91,94,98,102,105,109,113, 117,121,125,129,134,138,143,147, // 97-112
152,156,161,166,171,176,181,186, 191,197,202,208,213,219,225,231, // 113-128
237,243,249,255
};

With many and large tables it might save program space doing the calculation in driver firmware. Doing this the user could tweak calculation parameters in configuration mode for almost unlimited variations.

True Mike, but w/floating pt. math, oh boy...

TK - just noticed the app doesn't create a max 7135 entry, no FET. That's strange, but may just be how it works out?

Also trying it in a couple more lights. Got a 50 7135 (49+1) SRK V2 MtnE driver in a 5X SRK, and it definitely has a pause or something, but that's obvious now that I look the script and data -- driving 49 7135's at PWM level 1, 2, 3, 4 (maybe 5 and 7) probably won't do anything.

Edit: It's in a total of 5 lights now: M6 fully modded, Fing 5X fully modded, 4X Securitylng w/stock MCPCB, M2-Z, and a Zy-T11 clone. Looks good in all accept the 5X, but fullt explainable there.

Wauw Tom sounds great!

Amazing work Tom. Thanks. Are you charging by the hour?

Would be nice, but the code to generate the tables… isn’t very small on an attiny. It’s all floating-point and uses functions which cost a lot of space like cube roots, while the attiny only does integer math so it’d need to either load a floating-point library into ROM or manually simulate things with fixed-point.

However, it could probably be cut almost in half by adding a restriction and an inflection point. While the 7135 channel is ramping up, the FET is all zeroes. Then while the FET is ramping up, the 7135 is all 255s. These could be combined into one table with the repetitive bits removed, with a bit of extra logic added to handle it differently. It’d save space when the ramp is large, but it’d cost space when the ramp is small.

It doesn’t attempt to alter the ramp shape to exactly hit the boundary between channels. I’m thinking of perhaps making it do that, when it can, but I’m not sure I want to mess with it. It’s usually fairly simple to wiggle its parameters a little until it’s close, then tweak a few PWM values manually to make it exact.

I’m thinking it might be good to estimate the maximum lumens on the low side, since it’ll drop with voltage anyway. Maybe it does 5000 lumens on full cells, but only 2500 at 3.6V? So maybe it’d look better with a curve calculated for 2500.

There’s also debate about what the best curve shape is. Selfbuilt insists it’s a cube root, but I find that a logarithm tends to be really nice, and there are other curves between (fifth root, seventh root, etc). Some people even prefer a square root, but I find it’s not steep enough.

The only way to tell for sure is to try it, and that’s what you’re doing. :slight_smile:
(is also why I put in some commented-out presets for different curve shapes)

avr-libc looks decent but probably too large to be useful here. A simple float multiply is a function over 200 bytes. Add pow, divide, etc and you’ll quickly use up more program space than the tables.

Fixed point could be made to work within a small footprint. I agree that LUTs are better here as long they get the job done. But if there’s some interest, I can take a stab at the fixed point math. I’ve done much worse things before :slight_smile:

I dunno - I'm pretty happy with how the current table is performing across 1400 - 5000 lumens. Only thing that has to change is for a 7135 bank vs. a FET, as far as I can see. I'll do more testing tonight though.

The only point of doing it within the firmware would be to include more options than can be done with tables due to limited space. It doesn’t seem like that will be the case.

Since there are a lot of complaints regarding "short_click/medium_click" because of temperature dependency of otc measurement I suggest a temperature compensation for next driver versions.

Quick and dirty, can be improved, works in my driver.

// experimental temperature compensation for otc value
thermal_compensation = adc_init_and_read(ADC_MODE_TEMP);
if ( thermal_compensation > 76 )
thermal_compensation = (x * (thermal_compensation - 76));
else
thermal_compensation = 0;
...
if ( otc_voltage > (OTC_SHORT_PRESS - thermal_compensation) )
...

with x something between 4 and 8. Predefined OTC border values must be increased.

I haven’t had temp issues with mine, maybe because of the component choice, not sure. For me there is much more difference if the cell is fully charged or almost depleted. Charging the OTC with 2.8V or 4.2V makes more of a difference than temperature. I couldn’t be bothered to compensate for it though, having the voltage divider and OTC on the same pin presents it’s own set of challenges.

Something like Flashy Mike’s compensation routine should be enough.

Changed my mind… Going to play around a little with voltage compensation on my drivers, just to see if I can make it work nicely. If it works out maybe I can re-use the code for temperature compensation, gotta do a little more precise testing to see how much my drivers are affected by temp changes.

It’s not nearly as much of an issue on drivers with good components. However, that said, there are other complicating factors. For one, the most-affected drivers tend to have their OTC readings right at the edge of the usable range. So, there is sometimes no room to adjust; it’s already at the boundary. It would need calibration on a per-unit basis because the behavior varies with each individual driver. The MCU’s temperature readings alone can vary by a pretty large amount, so that “76” either needs to be configurable by the user or calibrated at the factory. As Mike C said, voltage is also an important factor. Getting a reliable temp+voltage measurement can take a few tries too, so that would delay boot-up until readings are taken. The scale factor “x” needs to be quantified and might not be linear, or might require floating-point math to get it even close to accurate (floating point or even an integer multiply can take a lot of ROM space).

Overall, it should be do-able on some drivers. But it might involve undesirable tradeoffs.

Anyway, the code is all there for anyone who wants to improve it. :slight_smile:

It is a bit frustrating when people have a lot of counter arguments but don’t even try. I did try on two high current triples and it works reliably. Without compensation the lights with bistro drivers (banggood) weren’t usable at all when hot. If calibration were necessary then even more without compensation. When this driver is used in BLF lights we must use whatever banggood put on the driver, and that are not always “good components”. The “undesirable tradeoffs” are already there, just read the threads about this lights.
Of course factor “x” and other parameters have to be quantified and I know the temperature readings differ between drivers, my test drivers have quite different readings in voltage and temperature. And probably there is no linearity, I said that it was just a quick and dirty hack, my first test of compensation. And nevertheless it works better than without compensation.
I also don’t understand all this fear of floating point math. With some tricks you could do a lot in fixed point which means integer with some shifting. A 16x16 bit multiplication takes less than 40 bytes program space, a 16/16 bit division less than 50 bytes. I squeezed a complete 2-channel ramping calculation for 256 steps in ATtiny with less than 200 bytes code, configurable for probably any light by only 1 parameter which can easily done by the user.