STAR Firmware by JonnyC - Source Code and Explanation

You know, I never got around to looking at vex_zg’s code. Oops. It looks like we wrote almost exactly the same code though. Both offer 3 time frames — short, medium, long. The default STAR off-time uses only two timeframes — short and long.

I still want to tweak the thresholds more… sometimes I have trouble getting a medium press. Otherwise though, it seems to work pretty well. If JonnyC finds it worthwhile he might include it in the default STAR firmware. However, it does complicate the UI and STAR generally tries to “keep it simple, stupid” so it might remain a custom patch.

Yes, I think so. I wanted the blinky modes to actually turn off between blinks, so I made it use phase-correct PWM by default (and only use fast PWM for the always-on modes).

BTW, I tried the idea of changing the ceiling value according to voltage. I had to rearrange a bunch of other code to make room, but I think it’ll work… ish. I’ve only tried a very coarse correction table so far, and due to the variability in voltage measurements, it tends to make moon mode blink brighter or dimmer every few seconds.

This could be fixed by using a bigger table and/or taking an average of several voltage readings, but those both take more space. I’ll probably at least try a slightly bigger table though.

The problem right now is that I’m too sleepy for any complicated math. I’ve got a non-linear curve with an unknown equation (the power to the emitter rising during each pulse) which changes shape according to another non-linear curve (voltage drop), and the only way I can correct it involves another non-linear curve (PFM response curve, roughly 1/x shape). Given these three, I’m trying to make a fourth curve which takes the first three as input and returns a result which is as close to flat as possible. And then quantize that fourth curve into a relatively small table.

That’s too much math for me to do this morning.

I would really love it if this driver had a single 7135 chip available for handling moon mode. (like RMM’s moonlight special, only on a FET driver)

Edit: So, I tried a simpler approach… added a lowpass filter instead. It seems to work pretty well. The PFM level will simply ramp up/down by 1 any time the voltage reading says it’s not right. That means the flicker is very small, and when it does change it’s pretty smooth. If the voltage flips rapidly above and below a threshold, the PFM level will just hang out somewhere in the middle. Plus, it looks cool smoothly ramping up on boot.

OTOH, I’m seriously scraping the bottom of the barrel now for spare bytes. It’s at exactly 1024 and I don’t see anything else I can reduce in size without dropping features.

Oh, yes, probably. But I didn’t know that was a usable symbol in avr-gcc so I used the hex value instead. I think it’s probably fine either way as long as it’s documented / commented.

OK so this totally didn’t compile. Not sure what was going on, sometimes AVR Studio can get confused and compile a nice little nothing instead of what I pasted in there. Also it was pretty late at night. :wink:

This version compiles v004 but does not seem to work correctly. I get around 50% output and no low battery flashes. Does anyone see an obvious reason why?

I think I found a small typo in “MTN_momentary_temp.c”.

// MUX 01 corresponds with PB2, 02 for PB3. Will switch back and forth

0x02 is actually PB4, right?

Is there an off-time version with strobe?

OK, several small changes and this is where I’m at now: v009.c

It’s still definitely not right.

  • I’m able to adjust the output PWM with the pot, but I don’t think I’m getting 100% duty cycle even though the voltage on the input pin is the same as Vcc. (I did not check duty cycle with a scope though).
  • LVP is definitely not working correctly, but so far it has never turned the light off in any version. The best I ever got was around 10 flashes and then 100% duty cycle and unresponsive to the brightness pot. Now….
  • Right now I’ve got some constant pulsing going on, but that wasn’t happening until the latest version… I think.

There are a lot of things I want to tweak, but none of them should be causing these issues… in intermediate versions it felt almost like PB2 was floating, but of course it was not - I checked multiple times with a DMM.

I’m starting to feel like I’m going backwards. I understand better than ever what all this stuff is supposed to be doing, but less than ever why it’s actually doing what it’s doing. It’s all hacked up copy-paste stuff, so maybe significant re-writes would help. In the context of a flashlight where the brightness is controlled by a potentiometer none of the original battery monitoring code makes a ton of sense. I think removing it and starting with a clean slate may be the smart move now.

I guess I’d better disable voltage monitoring again and test to see whether I get full duty cycle with that disabled. Either way the answer is still probably to remove all voltage monitoring code, troubleshoot any remaining problems, then add voltage monitoring back in.

The only one I’ve seen is the one I made in the last couple days… but it’s kind of, um, deviated from its STAR roots. I wrote it to use on my Cypreus, and it’s a tad bit heavy on the blinkies. I haven’t made any which are more appropriate for general purpose use.

I’d suggest copying the strobe bits back into STAR, but the strobe functions are pretty trivial and the code is pretty far from where it started. I suspect my version would only be useful for ideas, not for actual code.

It’s an analog-to-digital signal, converted on cheap hardware, and it’s pretty much guaranteed to be noisy and +/- 10% off spec. It’ll be even noisier if the MCU is doing anything during the conversion. So, it’s safe to assume that you’ll need to scale the values and run a lowpass over them to get any reasonably-sane output.

As for LVP, I think you’re correct that you need to just turn it off until the other functions are working.

Also, it might be helpful to declare all your global variables as volatile. Since the source file isn’t used as a module by other files, declaring globals as static has no effect. Volatile, however, tells the compiler to assume the variable is being changed by other threads or interrupts, and should never be trusted or cached.

I see something else which could be causing issues too…

while(1) {
    if (ADCSRA & (1 << ADIF)) {
        ...
    }
    // Start conversion for next time through
    ADCSRA |= (1 << ADSC);
}

It looks like you’re re-starting the conversion again before it has a chance to finish. The re-start should probably be inside the if, not after it.

You could also just busy-wait on the result, since the code isn’t doing anything else at the time. For example, this is how I block until I get a voltage reading:

uint8_t get_voltage() {
    // Start conversion
    ADCSRA |= (1 << ADSC);
    // Wait for completion
    while (ADCSRA & (1 << ADSC));
    // Return the raw value, caller can decide what to do with it
    return ADCH;
}

I’m not sure if the ADC is as noisy as you think. It’s definitely noisy, but my small experience with MCUs and ADC’s tells me that ADC noise is not the core of any of the actual problems. The driver should behave with no smoothing, it should just be a little rough. I think, heh. I definitely plan to come back and do something about the noise.

Thanks for the poitners on declaring as volatile. I’ve had that explained to me before, but lost track of it. I’ll go through and clean it up.

That final point of yours about the structure of the loop is right on the money. That is clearly a bad setup, I see it now that you have pointed it out. Either blocking for it or moving it inside the if conditional seem equally good to me for this setup. For now I’ve moved it inside the if conditional.

Most recent results:

# With VOLTAGE_MON undefined in v009 the pulsing goes away AND I’m able to turn the output all the way up with my pot. And all the way down.

# No change after moving:

ADCSRA |= (1 << ADSC);

It was still a good point though.

  1. v011 has batt_adc_ticks declared as volatile. It’s the only variable which should be able to change unbeknownst do the main loop. No change.
  2. I went ahead and commented out the entire mechanism that does anything to the output based on voltage monitoring, but left VOLTAGE_MON defined so that the ADC would toggle back and forth. Sure enough, the pulsing is gone - time will tell whether that was ADC noise or a screwup(s) in the LVP section I commented out. In the meantime I noticed that with this setup I still have upper and lower limits on what I can do with the pot. It won’t turn the output all the way down OR all the way up. I’m thinking that I need to do two readings each time I change Vref and discard the first reading.

We’ll come back to that thought though, it’s time to hang up the hat for the day. Thanks for your help ToyKeeper.

To mostly eliminate the noise, it works well to do something like…

  if (ADCH > PWM_LVL) { PWM_LVL ++; }
  else if (ADCH < PWM_LVL) { PWM_LVL --; }

It works pretty well as a lowpass filter, and adds a nice ramp effect when the value changes really quickly.

Or you could, you know, resolve the cause of the noise. But sheesh, who wants to do things properly? :slight_smile:

Yup, thanks! I'll change that comment.

I never added strobe to any of mine. I believe RMM has a strobe option in his versions which I never included back in mine, and of course the other guys here have added their own strobes.

This is what RMM did in the "int main" routine, changed this...

[quote] set_output(modes[mode_idx]); [/quote]

to...

[quote] if (modes[mode_idx] == 254) {

// Strobe. The program will stop here, you will lose the voltage monitoring feature

while (1) {

set_output(255);

_delay_ms(20);

set_output(0);

_delay_ms(60);

}

} else {

set_output(modes[mode_idx]);

} [/quote]

A mode of "254" will become a strobe then. I can add this to STAR and STAR_off_time if it works well enough.

As for strobe, what is the most effective one? Does the 20 ms on and 60 ms off work effectively? Does anyone know if this can be done via PWM by dividing the clock (really slowing down the PWM)?

It’s not necessary to lose voltage monitoring just because the light is in a blinky mode. In mine, I just light up, wait, turn off, wait, then proceed with the main loop (which allows voltage monitoring to happen), then it happens again next time the loop comes around. Instead of using modes[mode_idx] as a code to enter strobe mode, I use mode_idx itself (with #defines to decide how to map indexes to behaviors). The modes[mode_idx] value represents the strobe speed. This way, you can have strobes of different speeds without using mode space in the ROM.

Here is what I’m using, an off-time firmware with strobe modes:

http://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/files/head:/ToyKeeper/cypreus/

That’s the one which has deviated the furthest from STAR though. Its predecessor, ToyKeeper/s7/s7.c, is closer (but is on-time based). Also, they both use party strobes (freezes motion) instead of tactical strobes (for stunning people), so you’d need to change the duty cycle.

From what I’ve heard/read, the most effective tactical strobe is 10Hz with a 50% duty cycle. So, something like this should work:

#define OWN_DELAY
#define SOLID_MODES 4
#define TACTICAL_STROBE_MODES 2+SOLID_MODES
...
const uint8_t modes[] = {
    MODE_LOW, MODE_MED, MODE_HIGH, MODE_TURBO,  // solid modes
    50, 25,  // 10Hz strobe, 20Hz strobe  (1000 / 2 / X == strobe speed)
};
...
int main(void)
{
    ...
    while(1) {
        if(mode_idx < SOLID_MODES) { // Just stay on at a given brightness
            PWM_LVL = modes[mode_idx];
            sleep_mode();  // the WDT will wake us up for voltage checks
        } else if (mode_idx < TACTICAL_STROBE_MODES) { // strobe mode, fixed-speed
            PWM_LVL = 255;
            _delay_ms(modes[mode_idx]);  // requires OWN_DELAY
            PWM_LVL = 0;
            _delay_ms(modes[mode_idx]);
        }
        #ifdef VOLTAGE_MON
        if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
            ...
        }
        #endif
    }
}

As for using PWM to do strobes… it should be possible down to 2Hz (I wrote a script to do the math a while back), but it would require setting the CPU to its absolute slowest clock speed. I haven’t looked up if that can be done from inside the program or if it needs fuses set. So, it might interfere with the PWM on regular modes, and the only real benefit is that it would use slightly less power.

Oops, I just realized/remembered that STAR doesn’t know at compile time how many modes there will be. That certainly makes it harder to use the mode index to decide what kind of behavior to use.

Hi, I really like a feature, rmm’s tk61 super driver has, the momentary turbo from off mode. It works until you leave the long press. Richard gave me some tips on how I can add this to star firmware but my skills dont let me to accomplise that.
Can anyone assist me on that?

Adding momentary turbo from off shouldn’t be terribly difficult, but it has a cost that you lose the ability to map long-press-from-off to something else.

I added it to my todo list, but I’m not sure when it’ll happen.

For now, there are a couple of similar options:

  • Long press from off goes to turbo, release and short-press to turn it off. (in one variant, holding the initial press longer will ramp down) Short press from off goes to the lowest mode (repeat to increase brightness). Turn off by raising or lowering brightness past the maximum level. One version also has a battery check mode on short-then-long-from-off.
  • Long press from off goes to moon, short press goes to last-used level, double click from off goes to turbo. While on, short press turns the light off, long press and hold ramps brightness (smoothly or in steps). Extra-long press from off toggles soft lockout on one version of this UI.

Would either of those work? There’s basically STAR momentary, my version of STAR momentary, a smooth ramping UI, or a stepped ramping UI (like an Olight Baton).

Thanks TK, will I find these in your repository?

Yes. STAR itself is in the JonnyC directory, and the others are all under ToyKeeper/Ferrero_Rocher/ .

I still need to make the red/green battery indicator bits easy to turn off at compile time; these were designed for a very specific driver. They work on other attiny13-based e-switch drivers though. The most likely thing to need changing is the PWM levels, since that’s very hardware-dependent. It’s calibrated for a FET by default.

Edit: Actually, looking at the code today, I have lots of misc cleanup to do… and need to apply upgrades from some of the files to the other files. I mostly stopped after getting things working, and didn’t finish the “polish” to make it really clean for others to use.

Edit 2: I just did some significant cleanup, mostly merging code between the three Ferrero Rocher firmwares to make them more consistent. It should be significantly easier to turn off the red/green indicators now, at least. Also freed up more space on two of the three.

Hello all!
I really like the Star (On-Time Memory) program. It is simple and functional. However, I would like to see one additional feature: the ability to reduce the mods in the normal and reverse cycle. I mean the following:
Short press (from off) (ML), Low, Med, Off
Long press (from off) Turbo, High, (Med), Off
In this way I do not need to go through unnecessary mods.
Is this addition difficult to accomplish?

I think this is impossible. Long/short press functionality while on has been implemented by a member here in STAR off-time, but I believe it is impossible from an off state.