Flashlight Firmware Repository

I got the source for battcheck.c. It uses pin #6 for LED output, so that would be the 7135 bank. It does have support for both 13A and 25/45/85, but it's done at compile time.

Here's the full source:

/*
* This firmware simply helps calibrate values for voltage readings.
* It is not intended to be used as an actual flashlight.
*
* It will read the voltage, then read out the raw value as a series of
* blinks. It will provide up to three groups of blinks, representing the
* hundreds digit, the tens digit, then the ones. So, for a raw value of 183,
* it would blink once, pause, blink eight times, pause, then blink three times.
* It will then wait longer and re-read the voltage, then repeat.
*
* NANJG 105C Diagram
* ---
* -| |- VCC
* Star 4 -| |- Voltage ADC
* Star 3 -| |- PWM
* GND -| |- Star 2
* ---
*
* CPU speed is 4.8Mhz without the 8x divider when low fuse is 0x75
*
* define F_CPU 4800000 CPU: 4.8MHz PWM: 9.4kHz ####### use low fuse: 0x75 #######
* /8 PWM: 1.176kHz ####### use low fuse: 0x65 #######
* define F_CPU 9600000 CPU: 9.6MHz PWM: 19kHz ####### use low fuse: 0x7a #######
* /8 PWM: 2.4kHz ####### use low fuse: 0x6a #######
*
* Above PWM speeds are for phase-correct PWM. This program uses Fast-PWM,
* which when the CPU is 4.8MHz will be 18.75 kHz
*
* FUSES
* I use these fuse settings
* Low: 0x75
* High: 0xff
*
* STARS (not used)
*
*/
// set some hardware-specific values...
// (while configuring this firmware, skip this section)
#if (ATTINY == 13)
#define F_CPU 4800000UL
#define EEPLEN 64
#define DELAY_TWEAK 950
#elif (ATTINY == 25)
#define F_CPU 8000000UL
#define EEPLEN 128
#define DELAY_TWEAK 2000
#else
Hey, you need to define ATTINY.
#endif


/*
* =========================================================================
* Settings to modify per driver
*/

#define OWN_DELAY // Should we use the built-in delay or our own?

#define BLINK_PWM 10

/*
* =========================================================================
*/

#ifdef OWN_DELAY
#include <util/delay_basic.h>
// Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
static void _delay_ms(uint16_t n)
{
while(n-- > 0)
_delay_loop_2(DELAY_TWEAK);
}
#else
#include <util/delay.h>
#endif

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

#define STAR2_PIN PB0
#define STAR3_PIN PB4
#define STAR4_PIN PB3
#define PWM_PIN PB1
#define VOLTAGE_PIN PB2
#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
#define ADC_PRSCL 0x06 // clk/64

#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1

inline void ADC_on() {
#if (ATTINY == 13)
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
#elif (ATTINY == 25)
ADMUX = (1 << REFS1) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
#endif
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
}

inline void ADC_off() {
ADCSRA &= ~(1<<7); //ADC off
}

uint8_t get_voltage() {
// Start conversion
ADCSRA |= (1 << ADSC);
// Wait for completion
while (ADCSRA & (1 << ADSC));
// See if voltage is lower than what we were looking for
return ADCH;
}

void noblink() {
PWM_LVL = (BLINK_PWM>>2);
_delay_ms(5);
PWM_LVL = 0;
_delay_ms(200);
}

void blink() {
PWM_LVL = BLINK_PWM;
_delay_ms(100);
PWM_LVL = 0;
_delay_ms(200);
}

int main(void)
{
// Set PWM pin to output
DDRB = (1 << PWM_PIN);

// Set timer to do PWM for correct output pin and set prescaler timing
TCCR0A = 0x21; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)

// Turn features on or off as needed
ADC_on();
ACSR |= (1<<7); //AC off

// blink once on receiving power
PWM_LVL = 255;
_delay_ms(5);
PWM_LVL = 0;

uint16_t voltage;
uint8_t i;
voltage = get_voltage();

while(1) {
PWM_LVL = 0;

// get an average of several readings
voltage = 0;
for (i=0; i<8; i++) {
voltage += get_voltage();
_delay_ms(50);
}
voltage = voltage >> 3;

// hundreds
while (voltage >= 100) {
voltage -= 100;
blink();
}
_delay_ms(1000);

// tens
if (voltage < 10) {
noblink();
}
while (voltage >= 10) {
voltage -= 10;
blink();
}
_delay_ms(1000);

// ones
if (voltage <= 0) {
noblink();
}
while (voltage > 0) {
voltage -= 1;
blink();
}
_delay_ms(1000);

// ... and wait a bit for next time
_delay_ms(3000);

}
}

Then the one I have should be compiled for the 25 as that is all I have ever used. Odd, I will try it on another driver and see what happens. I generally flash in Linux, maybe my flash command was off as I did it in windows this time, I need to make a batch file for windows flashing to keep everything straight.

Browse the code is now working!

Great, got the 25 version and now it works fine. Not a lot of difference from the 19.1k but enough to need a change.

Ok, just got the voltage calibration for a 22k R1 resistor completed on a Texas Avenger driver. Initial calibration was set with my DPS3012 power supply and I tested it with various batteries at several voltages and everything lined up in the working driver great. So here is the updated 22k R1 calibration file if anyone is interested:

tk-calibration-22k-R1.h

Also here is my latest working revision of Bistro Tripledown, not 100% I am done tweaking it yet but it is pretty close to the end goal I am personally looking for. So far I have found no bugs or issues with it.

I changed the mode groups around some to better fit with the triple channel setup, I also added a 10th mode and removed the fancy flasher in order to allow for a single 7135 and all 7135’s only mode, which I find useful in some lights where the FET is simply not needed. Not sure what other changes there are other then that, kinda forgot at this point lol.

Texas-Avenger-Bistro-Tripledown

Actually I need to edit the C file to add a line for the new 22k calibration file so people can simply comment out one to select what they are using.

Is this calibrated for the otc as well. Or just voltage. Thanks for your work!

The OTC should be the same and appears to work perfectly in my drivers. It is the same 1uf cap that the original calibration was setup for, so I saw no reason to redo it.

The quality of the cap will play a much larger role. For example I find I had to replace all the OTC caps from the A6/X6 drivers as they don’t work right once hot but work fine when replaced with X7R 10% caps.

So the overall answer is yes, it is calibrated for a 1uf OTC cap, just not by me.

Ok, I was looking at an easy way for people to select the 22k calibration by a simple “comment option” but since it appears the calibration file is used in a few places this is not appearing to be the best method.

So what is the best way to allow people to select between 19.1K and 22k? Simply move the The Avenger drivers to the 22k and rename the calibration file accordingly?

Oops, that’s rare. I wonder what happened…

*goes to read the internal chat log*

Huh. Well, that’s fascinating and kind of funny, but unfortunately I can’t really share the details. The short version is: a cascading power outage which took out a bunch of core resources and took a few hours to fix. Probably the longest outage in the company’s history.

Kinda glad it was all fixed before I woke up.

Keep a copy of “tk-calibration.h” for each different set of values you want, and … copy the relevant one over the main one before compiling. Or change the include line so it says #include “texas-ace-22k-calibration.h” or whatever. The point of putting it in its own file is to make it easy to manage calibrations and firmwares independently.

Usually I just save the calibration data under battcheck/readings/ and re-generate the header values on demand. Or for specific items, I copy the .c file to my-light.c and copy the calibration to my-light-calibration.h in the same directory, and change the include line. That way I can more easily come back a year later to tweak something small and know that nothing else has changed.

Yeah, thats how I am doing it now. I was hoping to allow an easy way for people that don’t know as much to select which R1 they are using without having to worry about 2 files of the same name floating around with different calibrations.

It is not a big deal, I just like things to be as straight forwards and stupid proof as possible for new people that have never done these kind of things before.

let’s get basic:

What are the ramping tables used for in bistro? Can I just comment out soft start? Will that make the tables unneeded?

slightly less basic:

How do I alter the mode groups to get one or two the way I like them? I’m used to blf-a6 but I’m not seeing that familiar code here.

The ramping tables are used to convert a human-friendly value (linear-scale perceptual brightness) into a set of raw PWM values. So, given a ramp 40 items long, level 10/40 would appear to be about 25% brightness, level 20/40 appears to be about 50% brightness, etc. Doesn’t matter how many power channels there are or how the ramps are shaped or how the individual power curves add together; all you need to deal with is a single number.

The ramp is used for basically everything which sets an output level, not just soft start. However, you can reduce the size of the ramp if you like, to save space.

To alter the mode groups, look for the array declared as “PROGMEM const uint8_t modegroups[]” and change its contents. Each set of 8 values is one mode group. The final one (muggle mode) can be less than 8 bytes, but all others must be padded with zeroes until they are long enough.

Blinky modes are defined near the top of the file, and are basically just numbers close to 255.

So, in bistro-tripledown:

  • 0 is off.
  • 1-40 are moon mode through turbo, on a visually-linear scale.
  • 247-254 are blinky modes.
  • Other numbers are unused.

To adjust the shape of the curve, edit the definitions of RAMP_7135, RAMP_7135s, and RAMP_FET. I recommend generating these values with level_calc.py as shown in the comment above the definitions. If you want any no-PWM medium modes, you’ll also need to manually tweak the values a little to make one or two modes align exactly with 255/0/0 and 255/255/0, then list those level numbers as “ONE7135” and “ALL7135s”. The special “RAMP” mode is there specifically to help with tweaking the ramp shape; it just goes smoothly from one end to the other and back, and should generally be removed after the levels look okay.

The ramp can be made dramatically smaller, like what I did in biscotti.c, but it also requires changing a few other parts of the code because some parts assume a long smooth ramp.

So, adjusting the ramp can be a pain… but once that’s done, all those details are abstracted away to make the rest of the code simpler.

Errr not the simple answer I was hoping for. I meed to be able to set specific combinations for use with multiple LED outputs.

So I can create custom pwm presets, or am I misunderstanding this? How do I do that? The only way I can think of this working is if I can set the specific pwm values for the 3 channels

Oh, that makes sense. If you’re controlling multiple LEDs, you’d probably want to edit the ramps directly, and turn off soft start, and edit the LVP logic so it won’t assume a lower number is a lower power level.

How many LEDs are you controlling? Are they all used as regular outputs, or are some doing their own thing in parallel to the main emitter(s) (like an indicator light)?

When I wanted to test each power channel separately, I set the first three modes up like this:

#define RAMP_7135 255,0,0,...
#define RAMP_7135s 0,255,0,...
#define RAMP_FET 0,0,255,...

You can definitely do things that way, and edit the mode table accordingly. In this case, modes 1/2/3 would be 1x7135 only, (N-1)x7135 only, and FET only. The only real complications here are the parts of the code which assume a single smooth ramp, like soft start, thermal regulation, and low-voltage step-down.

Lol so all the “complications” happen to be the same things I couldn’t get to work in the blf-a6 copy I was working with :smiley:

Basically with what I had could get the pwm levels configured perfectly, but the turbo timer and lvp seemed incapable of stepping down from the Pin3 FET turbo. Temp monitoring would have been a nice bonus, but mostly I just figured those things would work better if I just switched to bistro as my base

It’s because the code assumes only one emitter, with all the PWM settings neatly arranged in ascending order. If you want to use more than one emitter, it’ll need somewhat deeper changes.

Could you describe in detail how the hardware is set up and how you want the software to work? (or if you already did, is there a link available?)

Thanks for your attention TK. The S8 in my signature is a decent example using multiple emitters. I’ll be doing another similar one soon.

Even with a single emitter though, I tend to gravitate towards this mode set:

  1. Moon-ish (3 pwm level on single 7135)
  2. Low (roughly 50ma)
  3. Medium (100% on bank of 7135s)
  4. FET turbo.

They’re definitely not even levels, but it’s how I like it. I usually tweak mode 2 depending how I’ll be using that light. So usually when I’m using multiple mode groups, my groups will be identical except for maybe 10pwm difference on mode 2 so I can dial it in with less re-flashes.

Right now I’m using the modified blf-a6 that’s linked in the TripleDown thread OP. The problems I’m currently having are:

1. LVP will step down if I’m already in a non-turbo mode, but won’t pull it out of Turbo. Also, once stepped down from medium to low, it glitches if I try to bump it back up, usual getting stuck back in Turbo. I rarely let my cells get very low, so I didn’t even notice this till recently.

2. The turbo timer works fine on some builds, but not on others. So far I haven’t identified the pattern of why it works sometimes.

What you are trying to do is actually quite simple, just a matter of warping your brain around not having to use the PWM values when setting up the modes, took me a bit to get used to that but once you get it, it is much simpler.

I started to type out how to do it, it is really quite simple, you basically just zero out the single 7135 channel at the 27th ramp table so that the bank of 7135’s can run by themselves. Then setup the mode group.

Instead I will toss together a version that should work for you real quick.

Here are the lines you are interested in when setting up the modes in bistro.

First is the ramp table that converts the raw PWM numbers into “modes”

#define ONE7135 14
#define ALL7135s 27
#define RAMP_7135 3,4,7,11,18,27,40,57,77,103,133,169,211,255,255,255,255,255,255,255,255,255,255,255,255,255,0,255,255,255,255,255,255,255,255,255,255,255,255,0
#define RAMP_7135s 0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,22,35,49,64,82,101,122,144,169,195,224,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define 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,9,24,39,55,73,91,111,132,154,177,202,228,255

Notice that I zeroed out the one7135 channel at the 27th ramp in order to allow the bank of 7135’s to run without the single 7135 engaged. Simply return that zero to a 255 to re-enable it.

Here is where the mode groups are setup. I have this firmware set by defualt to use mode group 3 and enable moon mode. Moon mode in this firmware is 3 PWM as that is the 1st ramp table value. So that takes care of your first mode.

The code for the mode groups, the bolded group is the default one and the one that should be close to what you are looking for:

Now I am not totally sure how many mA a 7 as the first mode will draw but you can simply adjust it up or down until you get the brightness you are looking for. I am guessing it will be around 50ma since that correlates to a 40pwm in the ramp table.

You should be able to adjust from there pretty easily.

Here is the code for the above setup: PD68-Tripledown