Fenix E12: 14500, tiny10 FET, XP-G2, 40* TIR. Finally completed!!

OK lets try this *again* (I think my phone is fighting my computer, now I'm having quotes not showing up in other threads)

/*
 * 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
  •  Star 2 = Moon if connected
    
  •    Star 3 = H-L if connected, L-H if not
    
  • Star 4 = Memory if not connected
    
  • VOLTAGE
  •  Resistor values for voltage divider (reference BLF-VLD README for more info)
    
  •  Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
    
  •       VCC
    
  •        |
    
  •       Vd (~.25 v drop from protection diode)
    
  •        |
    
  •      1912 (R1 19,100 ohms)
    
  •        |
    
  •        |---- PB2 from MCU
    
  •        |
    
  •      4701 (R2 4,700 ohms)
    
  •        |
    
  •       GND
    
  • ADC = ((V_bat - V_diode) * R2   * 255) / ((R1    + R2  ) * V_ref)
    
  • 125 = ((3.0   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
    
  • 121 = ((2.9   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
    
  •   Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
    
  •  130 and 120
    
  • To find out what value to use, plug in the target voltage (V) to this equation
    
  •        value = (V * 4700 * 255) / (23800 * 1.1)
    

*/
#define F_CPU 4800000UL

/*

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

#define VOLTAGE_MON // Comment out to disable

#define MODE_MOON 1 // Can comment out to remove mode, but should be set through soldering stars
#define MODE_1 8 // Can comment out to remove mode
#define MODE_2 75 // Can comment out to remove mode
#define MODE_TURBO 255 // Can comment out to remove mode
#define TURBO_TIMEOUT 37 // How many WTD ticks before before dropping down (.5 sec each)

#define WDT_TIMEOUT 2 // Number of WTD ticks before mode is saved (.5 sec each)

#define ADC_LOW 130 // When do we start ramping
#define ADC_CRIT 120 // When do we shut the light off

/*

  • =========================================================================
    */

#ifdef MODE_TURBO
#undef MODE_HIGH
#define MODE_HIGH MODE_HIGH_W_TURBO
#endif

//#include <avr/pgmspace.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <avr/sleep.h>
//#include <avr/power.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

/*

  • global variables
    */

// Mode storage
uint8_t eepos = 0;
uint8_t eep[32];
uint8_t memory = 0;

// Modes (gets set when the light starts up based on stars)
static uint8_t modes[10]; // Don't need 10, but keeping it high enough to handle all
volatile uint8_t mode_idx = 0;
int mode_dir = 0; // 1 or -1. Determined when checking stars. Do we increase or decrease the idx when moving up to a higher mode.
uint8_t mode_cnt = 0;

uint8_t lowbatt_cnt = 0;

void store_mode_idx(uint8_t lvl) { //central method for writing (with wear leveling)
uint8_t oldpos=eepos;
eepos=(eepos+1)&31; //wear leveling, use next cell
// Write the current mode
EEARL=eepos; EEDR=lvl; EECR=32+4; EECR=32+4+2; //WRITE //32:write only (no erase) 4:enable 2:go
while(EECR & 2); //wait for completion
// Erase the last mode
EEARL=oldpos; EECR=16+4; EECR=16+4+2; //ERASE //16:erase only (no write) 4:enable 2:go
}
inline void read_mode_idx() {
eeprom_read_block(&eep, 0, 32);
while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
else eepos=0;
}

inline void next_mode() {
if (mode_idx == 0 && mode_dir == -1) {
// Wrap around
mode_idx = mode_cnt - 1;
} else {
mode_idx += mode_dir;
if (mode_idx > (mode_cnt - 1)) {
// Wrap around
mode_idx = 0;
}
}
}

inline void WDT_on() {
// Setup watchdog timer to only interrupt, not reset, every 500ms.
cli(); // Disable interrupts
wdt_reset(); // Reset the WDT
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms
sei(); // Enable interrupts
}

inline void WDT_off()
{
cli(); // Disable interrupts
wdt_reset(); // Reset the WDT
MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
WDTCR = 0x00; // Disable WDT
sei(); // Enable interrupts
}

inline void ADC_on() {
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
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
}

#ifdef VOLTAGE_MON
uint8_t low_voltage(uint8_t voltage_val) {
// Start conversion
ADCSRA |= (1 << ADSC);
// Wait for completion
while (ADCSRA & (1 << ADSC));
// See if voltage is lower than what we were looking for
if (ADCH < voltage_val) {
// See if it's been low for a while
if (++lowbatt_cnt > 8) {
lowbatt_cnt = 0;
return 1;
}
} else {
lowbatt_cnt = 0;
}
return 0;
}
#endif

ISR(WDT_vect) {
static uint8_t ticks = 0;
if (ticks < 255) ticks++;

if (ticks == WDT_TIMEOUT) {
 if (memory) {
       store_mode_idx(mode_idx);
   } else {
        // Reset the mode to the start for next time
        store_mode_idx((mode_dir == 1) ? 0 : (mode_cnt - 1));
   }

#ifdef MODE_TURBO
//} else if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
} else if (ticks == TURBO_TIMEOUT && mode_idx == (mode_cnt - 1)) {
// Turbo mode is always at end
PWM_LVL =185;
#endif
}

}

int main(void)
{
// All ports default to input, but turn pull-up resistors on for the stars (not the ADC input! Made that mistake already)
PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN) | (1 << STAR4_PIN);

// Set PWM pin to output
DDRB = (1 &lt;&lt; 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 =&gt; 1, 2 =&gt; 8, 3 =&gt; 64...)

// Turn features on or off as needed
#ifdef VOLTAGE_MON

ADC_on();
#else
ADC_off();
#endif
ACSR |= (1<<7); //AC off

// Load up the modes based on stars

// Always load up the modes array in order of lowest to highest mode
// 0 being low for soldered, 1 for pulled-up for not soldered
// Moon (stock is 1 then 0)
#ifdef MODE_MOON
if ((PINB & (1 << STAR2_PIN))> 0) { // Stock is ==, ( > for moon default to on, soldering star will disable moon)
modes[mode_cnt++] = MODE_MOON;
}
#endif
#ifdef MODE_1
modes[mode_cnt++] = MODE_1;
#endif
#ifdef MODE_2
modes[mode_cnt++] = MODE_2;
#endif
#ifdef MODE_TURBO
modes[mode_cnt++] = MODE_TURBO;
#endif
if ((PINB & (1 << STAR3_PIN)) == 0) {
// High to Low
mode_dir = -1;
} else {
mode_dir = 1;
}
// Not soldered (1) should disable memory
memory = ((PINB & (1 << STAR4_PIN)) > 0 ) ? 0 : 1; //to disable momory without soldering star use ? 0: 1; (stock is ? 1 : 0;)

// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
// Will allow us to go idle between WDT interrupts

set_sleep_mode(SLEEP_MODE_IDLE);

// Determine what mode we should fire up
// Read the last mode that was saved
read_mode_idx();
if (mode_idx&amp;0x10) {
    // Indicates we did a short press last time, go to the next mode
    // Remove short press indicator first
   mode_idx &amp;= 0x0f;
   next_mode(); // Will handle wrap arounds
} else {
    // Didn&#39;t have a short press, keep the same mode
}

// Store mode with short press indicator
store_mode_idx(mode_idx|0x10);

WDT_on();

// Now just fire up the mode
PWM_LVL = modes[mode_idx];

uint8_t i = 0;

uint8_t hold_pwm;
while(1) {
#ifdef VOLTAGE_MON
if (low_voltage(ADC_LOW)) {
// We need to go to a lower level
if (mode_idx == 0 && PWM_LVL <= modes[mode_idx]) {
// Can't go any lower than the lowest mode
// Wait until we hit the critical level before flashing 10 times and turning off
while (!low_voltage(ADC_CRIT));
i = 0;
while (i++<10) {
PWM_LVL = 0;
_delay_ms(250);
PWM_LVL = modes[0];
_delay_ms(500);
}
// Turn off the light
PWM_LVL = 0;
// Disable WDT so it doesn't wake us up
WDT_off();
// Power down as many components as possible
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
} else {
// Flash 3 times before lowering
hold_pwm = PWM_LVL;
i = 0;
while (i++<3) {
PWM_LVL = 0;
_delay_ms(250);
PWM_LVL = hold_pwm;
_delay_ms(500);
}
// Lower the mode by half, but don't go below lowest level
if ((PWM_LVL >> 1) < modes[0]) {
PWM_LVL = modes[0];
mode_idx = 0;
} else {
PWM_LVL = (PWM_LVL >> 1);
}
// See if we should change the current mode level if we've gone under the current mode.
if (PWM_LVL < modes[mode_idx]) {
// Lower our recorded mode
mode_idx–;
}
}
// Wait 3 seconds before lowering the level again
_delay_ms(3000);
}
#endif
sleep_mode();
}

return 0; // Standard Return Code

}

I think you still didn’t get a good paste, the beginning is missing some stuff. Good enough to do a diff though, ignoring that stuff:

I’m not sure why you changed the variable names, but I don’t see a problem there.

At a glance I’m not sure what this section is for or why you changed it.

EDIT: also I should point out that I’m assuming your code does actually contain this line at the top although it did not appear in your paste:

I changed the names cause this is modified from my 5-static modes version (plus moonlight) from a different build, when I needed to have more levels than stock it was just easier for me to number them than to make a "med-high/med-low".

I'm having lots of posting issues, I think its cause I'm using the standard mobile editor from my phone and now the advanced editor here and logged in from both at the same time, I am also having issues with quotes not showing up or just showing up as empty boxes. Lets pretend I'm not computer illiterate for a minute lol.

Later today I'm going to build a second Tiny10 FET for testing and load it up with the same code, maybe try adding a gate resistor somewhere (not sure where I'll fit anything else)

You didn’t address what this change was for, do you know?

PWM_LVL = modes[—mode_idx];

I wasnt aware that was changed, I'm visually comparing it (to the first link here http://www.jcapsolutions.com/flashlights/firmware/) and thought it was the same, I didnt mean to change it, what should it be?

this is what you’ll find on the stock version (first link on that page)

PWM_LVL = modes[—mode_idx];

this is what you’ve got that’s different

PWM_LVL =185;

Are you talking about where I changed the turbo timer? I did it like this because I no longer have a definition for "MODE_HIGH_W_TURBO" so I have to physically assign the level I want instead of telling it to use a predefined definition.

#ifdef MODE_TURBO 
    //} else if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
 } else if (ticks == TURBO_TIMEOUT && mode_idx == (mode_cnt - 1)) {
      // Turbo mode is always at end
      PWM_LVL =185;
#endif
 }

}

I see now, good idea.

I don’t see any code problems in that case, assuming you’ve got F_CPU defined and it didn’t come through in your paste. To make matters a little easier on me I renamed your variables back to the stock names, there are very few changes and they all make sense: Saved diff hv94kqvv - Diff Checker

My TIR came today, the lens seems to be glued into the holder :( I'll get the dremil out later and see what can be done, hopefully I can get it cause I was really excited about the new flood beam pattern.

Alright finished up FINALLY!

The TIR came out, not just popped out like I thought but I got it (actually had to melt the holder with a soldering iron). The neutral white 40* beam is very even, no hot spot at all which is about perfect for me in the role this light will fill. I also really like the look of the TIR better than a smooth one, still I’d prefer a reflector but this is more than adequate. Also swapped the XP-E2 for a XP-G2 on a 10mm sinkpad so I could increase the turbo timer up to 60sec, step down was changed to 204 (~80%).

old vs. new

Beam shots coming tonight. The only thing else I’m considering changing is adding one additional mode but i’m not sure, I usually dont like more than 4 modes so I’ll probably just leave it as is, all in all I’m very happy with this whole build, much much better than stock.

Back on the earlier topic of memory: I have now used both on-time and off-time with “no memory” setup. Off-time with no memory is what you want, it behaves exactly as you would expect a light with no memory to act: half presses while the light is on do advance modes, but leaving the light off for a few moments causes it to start in the first mode when turned back on.

Sorry for the delay in checking this out, it ended up not happening until I had a need for it. Go figure right? 0:)

Oh nice…so no capacitor = no memory

Nice!

I might flash that and give it a whirl…thanks

Wait so you can just flash off-time without adding the extra cap?

Damn I hate my broken programmer, I want to play…ordered a set of 40 breadboard jumpers at the same time as my new programmer from the same seller, jumpers came today, programmer won’t be here till Wednesday, WTF…

What? No!

You guys are getting confused. :ghost: You can convert any nanjg based, non-eswitch driver to offtime instead of ontime by adding a capacitor to physical Pin2 and flashing the STAR offtime firmware. For no memory you comment out “#define MEMORY”. THE CAPACITOR IS REQUIRED for this firmware to operate.

Adding the capacitor is simple and easy and you’ve got tons of space for it in this build. Flash the firmware with desired settings and then add the capacitor, it will block your clip. It goes straight from pin2 (star4) to ground.

ok, now I’m confused

so you did add the capacitor, but disabled memory in the code for compiling…is that correct?

so what happens if you don’t have the capacitor?

Think of it as off-time mode switching instead of off-time “memory”.
It has off-time mode switching with no memory.

Helios- is correct.

Off the top of my head, if you leave off the capacitor then you’ll never be able to switch modes, it will stay in the first mode.

Good news, after messing with it for DAYS, I’ve got my old programmer up and running again (no clue what was going wrong but last night I decided to give the old girl one more try and it worked!).

I’ve gotta build and program two more drivers for a guy here in a bit but then I’ll be getting back to this. First goal is to lower moonlight more but then I’ll start working on off-time.

Sounds good! While it’s on my mind, you’ve got some wiggle room on the cap spec if you need it. Look in the STAR firmware discussion thread for HarleyQuin’s post on the subject, but it sounded like anything from 0.5uF to 1.0uF would be fine. A bit more probably won’t hurt anything either.

How do I turn the memory to default off (so soldering the star would turn it on) in STAR on-time?

EDIT, yeah that was simple lol, got it thanks.