wight catchup

It’s probably worth mentioning that OTSM has not gotten any widespread use yet. It’s only implemented on a couple drivers, and is only supported by one firmware. Part of this, I think, was due to the overall difficulty of making it work right. Part was probably due to the relevant code being tricky. And a lot of it was probably due to a general shift toward e-switch lights around the same time. Not much need to measure offtime when power is only disconnected to change batteries.

Lately, when there is new hardware needing firmware, instead of writing entirely new firmware to fit the hardware, I mostly just add a few bits of code to FSM to support the new hardware. And then the FSM-based interfaces mostly “just work” on the new hardware. For an idea of how FSM changes or simplifies the UI code, here’s an example of implementing an Olight Baton interface using STAR as a base versus using FSM as a base.

Hey wight, nice to have you back.
I joined after you started your break, but I have seen the drivers you created. Hope you have a nice time here on BLF :+1:

Good to see you wight.

wight catch-up. Now that bought a smile to the dial. Welcome back. For those that don’t know wight also built a light or two that defied the laws of magic. Must go find the threads for a refresher.

Wight is so old ...

he wrote the original low/high sos/strobe next mode memory driver

I remember back when I joined, people were talking about the missing “wight”. :slight_smile:

Here’s my catch-up. Maybe I am wrong. I think the hobby has changed. As I understand, years ago the average person expected Alkaline/light-bulb performance, so showing them a lithium/LED flashlight was a mind-blow. Everyone had fun.
Now there are lithium/LED flashlights on every retail shelf. And while it’s will always be possible to out-perform a mass-produced light, it takes more money and effort than ever to ‘wow’ the average person. The “Giggles” light for example is great for wow-ing people, but had to be built so absurdly big it’s hard to even think of an excuse to take it off the shelf.

Welcome back wight :slight_smile: :+1:

Before my time but the legend lived on. Welcome back!

Good to see you back Wight, while our times here did not cross your work was what heavily influenced the TA driver series (with the work of PD68 and DEL adding there share in the mean time of course).

I look forward to seeing what you cook up now.

Getting the OTSM working with some viable firmware would be nice for the time when clicky lights are required for example. Right now the only firmware that supports it is a bit too buggy to make widespread use possible and dealing with the OTC is a royal pain, having to deal with this right now actually and think I might just revert to a non-OTC firmware in order to solve the problem.

Welcome back!

Schoki has made a boost driver that will be very interesting once Lexel gets it running and on sale… :partying_face:

That is putting it very politely.

Flintrock disappeared quite a while ago, but whilst he was here, he had, lets say, a high opinion of his abilities, and derision for almost every one else who questioned some things.

And his code was, let’s just say, unusual.

I think I fixed the OTC design some years ago and made it consistent, defined, analysable. Instead of a capacitor just dangling in the breeze.

But it seemed nobody at the time was interested. Or perhaps felt threatened.

I could discuss, but only in PM.

Hmmm… I wonder if Flintrock ever thought about coming back under a different name and trying to build a different reputation for himself…

:disguised_face:

I’ve heard it can be done. :wink:

The code supporting OTSM was tricky mostly because it relied on bare interrupts running raw assembly code. This made it extremely hardware-specific, and it also appears to have made the code buggy in some pretty unpredictable ways. I think perhaps the bare interrupts were less safe than Flintrock had thought, and may have had subtle unintended effects, but I haven’t tested to verify that. I just know that, whenever I tried BistroHD on real hardware, it exhibited a pretty wide variety of issues which were not easy to reproduce on purpose.

I’ve been trying to push code in the opposite direction instead — making it less hardware-specific and more portable, so people can mix and match whatever hardware they want with whatever UI they want. And for that, I think it’s about time to support more than one MCU architecture. I’m not sure which one to add next, but I’m thinking maybe tiny84 or tiny841 or tiny1634. New driver designs frequently seem to need more pins, and I have a bad habit of filling space, so it wouldn’t hurt to have more ROM too.

I agree, a new MCU is needed, something with both more pins and rom space.

I am also very grateful for the move to more universal firmware setup, now that I have started to play with Anduril I am seeing the flexability, I just need to figure out what the limits are. I tend to push past the bounds unless I know exactly where they are.

OTSM is absolutely reliable, pretty simple to handle, no "raw assembly code" required! This is the proof-of-concept code I sent to Mike C on request almost 2 years ago. I would have given it to anyone here who had asked but sadly there was no interest.

Try it!

/* Simple sample firmware for otc less design (single cell).

Half press less than about 300 ms: next mode
between 300 and 900 ms: previous mode
longer: blink out voltage reading
(0 to 255) in 3 decimals

Suggested capacity of buffer cap (C2): >= 47 uF.
Vbatt is connected to PB3 (Pin2).
Voltage divider still connected to Pin7.
Timing specified for Attiny25 at 10 Mhz.
Don't fuse BOD.
Created and built with Atmel Studio.
Fuses for Attiny25: lfuse: 0xD2, hfuse: 0xDF.
*/

#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

#define PIN_7135 PB0
#define PIN_FET PB1
#define PIN_POWER_DETECT PB3
#define INT_POWER_DETECT PCINT3
#define VLT_PIN PB2
#define VLT_CHANNEL 0x01
#define VLT_DIDR ADC1D
#define ADC_VREF ((1 << REFS1) | (1 << REFS2)) // 2.56V
#define ADC_PRESCL 0x06

#define PWM_PHASE 0xA1
#define PWM_7135 OCR0A
#define PWM_FET OCR0B

#define MODE_COUNT 6
#define PWM_LEVELS_7135 30, 80, 255, 255, 255, 0
#define PWM_LEVELS_FET 0, 0, 0, 30, 80, 255

// forward declarations
uint8_t adc_init_and_read();
void blink_once();
void blink_once_long();
void blink_value(uint8_t value);
void delay_250_micro_sec(uint8_t n);
void delay_50_milli_sec(uint8_t n);
void delay_1_sec();
void led_off();
void led_on();

volatile uint8_t s_clicked = 0;
const uint8_t pwm_levels_7135[] = { PWM_LEVELS_7135 };
const uint8_t pwm_levels_fet[] = { PWM_LEVELS_FET };
uint8_t mode_index = 0;

////////////////////////////////////////////////////////////////////////////////

int main(void)
{
uint8_t blink_voltage = 0;
uint8_t voltage;

// set LED ports as output
DDRB = (1 &lt;&lt; PIN_7135) | (1 &lt;&lt; PIN_FET);

// initialize PWM
TCCR0A = PWM_PHASE;
TCCR0B = 0x01;

// enable pin change interrupts
PCMSK = (1 &lt;&lt; INT_POWER_DETECT);
GIMSK |= (1 &lt;&lt; PCIE);
sei();

// start with first mode
PWM_7135 = pwm_levels_7135[mode_index];
PWM_FET = pwm_levels_fet[mode_index];

while ( 1 )
{
    if ( s_clicked )
    {
        if ( s_clicked &lt;= 10 ) // &lt; about 300 ms
        {
            blink_voltage = 0;
            mode_index++;
            if ( mode_index &gt;= MODE_COUNT )
                mode_index = 0;
        }
        else if ( s_clicked &lt;= 30 ) // &lt; about 900 ms
        {
            blink_voltage = 0;
            mode_index--;
            if  ( mode_index == 255 )
                mode_index = MODE_COUNT - 1;
        }
        else // &gt; about 900 ms
        {
            mode_index = 0;
            blink_voltage = 1;
        }
        
        s_clicked = 0;
        PWM_7135 = pwm_levels_7135[mode_index];
        PWM_FET  = pwm_levels_fet[mode_index];
    }
            
    voltage = adc_init_and_read();
    
    if ( blink_voltage )
        blink_value(voltage);
    
    delay_1_sec();
}

}

////////////////////////////////////////////////////////////////////////////////

ISR(PCINT0_vect)
{
// disable output
PORTB = 0;

// turn off PWM generation (might not be necessary)
TCCR0A = 0;
TCCR0B = 0;

// ADC off
ADCSRA &amp;= ~(1 &lt;&lt; ADEN);

// disable pin change interrupt
GIMSK &amp;= ~(1 &lt;&lt; PCIE);
PCMSK = 0;

s_clicked = 0;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);

do
{
    s_clicked++;
    
    wdt_reset();
    WDTCR = (1&lt;&lt;WDIE) | WDTO_30MS;

    sei();
    sleep_mode();
    cli();
    
} while ( (PINB &amp; (1 &lt;&lt; PIN_POWER_DETECT)) == 0 );

wdt_disable();

// debounce
delay_250_micro_sec(10); 

// activate pwm generation
TCCR0A = PWM_PHASE;
TCCR0B = 0x01;

// enable pin change interrupts
PCMSK = (1 &lt;&lt; INT_POWER_DETECT);
GIMSK |= (1 &lt;&lt; PCIE);

}

////////////////////////////////////////////////////////////////////////////////

void delay_250_micro_sec(uint8_t n)
{
while ( n-- )
{
int k = 180;
while ( k-- )
{
if ( s_clicked )
return;
}
}
}

////////////////////////////////////////////////////////////////////////////////

void delay_50_milli_sec(uint8_t n)
{
do
{
delay_250_micro_sec(200);
if ( s_clicked )
return;
} while ( --n );
}

////////////////////////////////////////////////////////////////////////////////

void delay_1_sec()
{
delay_50_milli_sec(20);
}

////////////////////////////////////////////////////////////////////////////////

void led_on()
{
PWM_7135 = pwm_levels_7135[mode_index];
PWM_FET = pwm_levels_fet[mode_index];
}

////////////////////////////////////////////////////////////////////////////////

void led_off()
{
PWM_7135 = 0;
PWM_FET = 0;
}

////////////////////////////////////////////////////////////////////////////////

void blink_once()
{
led_on();
delay_50_milli_sec(5);
led_off();
delay_50_milli_sec(5);
}

////////////////////////////////////////////////////////////////////////////////

void blink_once_long()
{
led_on();
delay_1_sec();
led_off();
delay_1_sec();
}

////////////////////////////////////////////////////////////////////////////////

void blink_once_short()
{
led_on();
delay_50_milli_sec(1);
led_off();
delay_50_milli_sec(5);
}

////////////////////////////////////////////////////////////////////////////////

void blink_value(uint8_t value)
{
// 100er
if ( value < 100 )
blink_once_short();

while ( value &gt;= 100 )
{
    value -= 100;
    blink_once();
}
delay_1_sec();

// 10er
if ( value &lt; 10 )
    blink_once_short();

while ( value &gt;= 10 )
{
    value -= 10;
    blink_once();
}
delay_1_sec();

// 1er
if ( value == 0 )
    blink_once_short();

while ( value &gt; 0 )
{
    value--;
    blink_once();
}
delay_1_sec();

}

////////////////////////////////////////////////////////////////////////////////

uint8_t adc_read()
{
uint8_t value;

while (ADCSRA &amp; (1 &lt;&lt; ADSC))
{
    if ( s_clicked )
        return 0;
}
value = ADCH;
ADCSRA |= (1 &lt;&lt; ADSC);
return value;

}

////////////////////////////////////////////////////////////////////////////////

uint8_t adc_init_and_read()
{
DIDR0 |= (1 << VLT_DIDR);
ADMUX = ADC_VREF | (1 << ADLAR) | VLT_CHANNEL;
ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRESCL;
adc_read(); // first run not reliable (see spec)
return adc_read();
}

////////////////////////////////////////////////////////////////////////////////
// empty interrupt function required otherwise mcu will reset

ISR(WDT_vect)
{
};

////////////////////////////////////////////////////////////////////////////////

Oh, nice. I didn’t know anyone except Flintrock had done it. This is much more useful, and demonstrates a pretty clean way to do it. It would not have occurred to me to go to sleep inside an interrupt handler.

Could I add this to the repository?

A legend returns! Welcome back wight. Even though I joined after the start of your “sabbatical”, it seems like I’ve read (and re-read) so many of your posts. Thanks for your contributions thus far.

Also a welcome back. Heard quite a bit about you. I keep following along.

Good to have you back wight.