Introduction: Flashing Custom Firmware to a BLF A6 Flashlight
I recently got a BLF A6. It's very nice, but I don't like either of the default mode groups, so I modified the firmware to use my preferred brightnesses. The information was hard to find, so I'm putting everything I learned here for myself and others.
BLF A6 (it'll probably work with other ATtiny based flashlights)
Tweezers / thin pliers / small scissors / something to undo the driver board retaining ring
Computer to flash with, preferably running a Linux distribution
USB ASP programmer / Arduino / something that can do AVR programming (apparently the USB ASP programmer is recommended, but I used an Arduino)
SOIC8 clip (it's possible to do without, but it's very fiddly and not at all recommended)
(optional) Breadboard and/or jumper wires to make connecting easier
Step 1: Download the Firmware
bzr branch lp:~toykeeper/flashlight-firmware/blf-a6-final
in a terminal. (you might have to install bzr)
Note: in a previous edit of this Instructable I used "bzr branch lp:flashlight-firmware" instead. I've since learned that this downloads an outdated version with wrong values for the off-time capacitor, making the button presses uncomfortably long. (thanks to this thread on Reddit)
The folder you want is blf-a6-final/ToyKeeper/blf-a6. It contains a compiled .hex file ready to flash (blf-a6.hex) and the C code that you can modify. (blf-a6.c) If you want to flash the stock firmware you can skip the next step and just use blf-a6.hex. Some of the other firmware in that repository will probably work too.
Step 2: Modify the Firmware
Open blf-a6.c in your preferred text editor or IDE. The most interesting lines are the mode groups between lines 116 and 131. They look like this:
// Mode group 1
#define NUM_MODES1 7 // PWM levels for the big circuit (FET or Nx7135) #define MODESNx1 0,0,0,7,56,137,255 // PWM levels for the small circuit (1x7135) #define MODES1x1 2,20,110,255,255,255,0 // My sample: 6=0..6, 7=2..11, 8=8..21(15..32) // Krono sample: 6=5..21, 7=17..32, 8=33..96(50..78) // Manker2: 2=21, 3=39, 4=47, ... 6?=68 // PWM speed for each mode #define MODES_PWM1 PHASE,FAST,FAST,FAST,FAST,FAST,PHASE // Mode group 2 #define NUM_MODES2 4 #define MODESNx2 0,0,90,255 #define MODES1x2 20,230,255,0 #define MODES_PWM2 FAST,FAST,FAST,PHASE
For each group, MODESN is the PWM value used for the FET, and MODES1 is the PWM value used for the 7135 in each mode. The number is between 0 and 255, and corresponds to the brightness of the light. More information here. (scroll down to "Mode regulation:") I'm not sure what PWM speed is exactly. If anyone knows, tell me in the comments. The FET can produce more light than the 7135, but the 7135 keeps the light level more or less the same through the life of the battery, while the FET gets darker as it runs out of battery.
Here you can adjust the PWM values to produce modes to your liking. You can probably change the number of modes too, but I haven't tried it as I wanted four modes, which happens to be the number in the second group. I wanted a darker moonlight mode, so I set the first one to 0/1, and I find turbo mode to be a bit pointless, so I replaced it with 137/255, the equivalent of mode six in the seven mode group. You can probably modify the rest of the code if need be, but I haven't tried it.
When you've got the code you want, you have to compile it to a .hex file. At the very least, you need gcc-avr and avr-libc. If you have problems, look at the other dependencies in the firmware readme. The repository includes a build script, but I couldn't get it to work. Instead, I downloaded the old version with
bzr branch lp:flashlight-firmware
and copied the old build script (that I could get to work) over the new one. Then I ran:
../../bin/build.sh 13 blf-a6
in the blf-a6 folder. (there should be a better way to do that) The ../../bin/build.sh calls the script, the 13 specifies that it's building for an ATtiny13 and the blf-a6 specifies that it's for the BLF A6. (duh) It should tell you what commands it's running and give you the output. Mine looks like this:
avr-gcc -Wall -g -Os -mmcu=attiny13 -c -std=gnu99 -fgnu89-inline -DATTINY=13 -I.. -I../.. -I../../.. -fshort-enums -o blf-a6.o -c blf-a6.c<br>avr-gcc -Wall -g -Os -mmcu=attiny13 -fgnu89-inline -o blf-a6.elf blf-a6.o avr-objcopy --set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0 --no-change-warnings -O ihex blf-a6.elf blf-a6.hex Program: 1022 bytes (99.8% Full) Data: 13 bytes (20.3% Full)
The commands are already optimised for size, so if it says it's more than 100% full, try commenting out
on line 147 to use the smaller minimal biking strobe. If it still doesn't fit, you'll probably have to remove more code somewhere. When it's finished compiling, there should be a file called blf-a6.hex in the folder. This is your compiled code, ready to flash.
Step 3: Disassemble the Flashlight
Unscrew the bulb end anti-clockwise. There are two screw joints here. The one closer to the bulb end of the flashlight opens up the reflector and LED, and the one closer to the middle opens up the driver board. You want the one closer to the middle.
Inside, you should see the battery spring and a retaining ring with two holes in it. Insert your tweezers / thin pliers / scissors into the holes and rotate them anti-clockwise. It's quite stiff, and using two separate objects probably won't give you enough leverage. I used the scissors on a Swiss Army Knife.
Once you've got the ring out, free the driver board. It's still attached with two wires, so be careful. They're twisted together, so rotate the board one way or another until the wires loosen. When you've got enough leeway, flip the board. You want it so that the chip with "TINY13A" on it is higher up and more accessible. If it's on the wrong side, flip it the other way. Tuck the spring under the side. This will temporarily keep it in place and make it easier to get to the chip. If you have trouble with this you can probably unscrew the other join and desolder the two wires from the other side so you can remove the board completely, but I haven't tried it.
Step 4: Connect Flashing Hardware
Now you use the SOIC8 clip to connect the ATtiny13 chip and your programmer. With my SOIC8 clip, if I have the red wire on the left of both ends, the row of pins closer to me on the clip end corresponds to the row of pins closer to me on the connector end, when the connector is facing down. (see my super artistic diagram) This guide recommends you use a USB ASP V2.0 programmer. If you do, connect it like this:
- Pin 1 on the ATtiny13 to pin 5 on the USB ASP (reset)
- Pin 4 on the ATtiny13 to pin 10 on the USB ASP (ground)
- Pin 5 on the ATtiny13 to pin 1 on the USB ASP (MOSI)
- Pin 6 on the ATtiny13 to pin 9 on the USB ASP (MISO)
- Pin 7 on the ATtiny13 to pin 7 on the USB ASP (SCK)
- Pin 8 on the ATtiny13 to pin 2 on the USB ASP (VCC)
If, like me, you're using an Arduino, you have to do a bit more preparation. Follow steps zero and two of this guide:
Open the Arduino IDE and make sure your Arduino is connected to your computer. Find the ISP sketch in File>Examples>11.ArduinoISP>ArduinoISP and upload it to your Arduino. Then connect the ATtiny13 to it like this:
- Pin 1 on the ATtiny13 to pin 10 on the Arduino (reset)
- Pin 4 on the ATtiny13 to GND on the Arduino (ground)
- Pin 5 on the ATtiny13 to pin 11 on the Arduino (MOSI)
- Pin 6 on the ATtiny13 to pin 12 on the Arduino (MISO)
- Pin 7 on the ATtiny13 to pin 13 on the Arduino (SCK)
- Pin 8 on the ATtiny13 to VCC / 5V / 3.3V on the Arduino (any should work, but 5V is more reliable)(VCC)
I installed the hardware package too, but it probably wasn't necessary. If in doubt, try it. It won't do any harm. But don't burn the bootloader because it'll probably brick your flashlight.
Step 5: Flash It
In order to flash the firmware, you need to install AVRDUDE. To check it works with my Arduino, I run:
avrdude -v -p attiny13 -c stk500v1 -P /dev/ttyUSB0 -b 19200 -n
If it does work, I move to an empty folder somewhere and run:
avrdude -v -p attiny13 -c stk500v1 -P /dev/ttyUSB0 -b 19200 -u -Uflash:r:flash-dump.hex:i -Ueeprom:r:eeprom-dump.hex:i -Ulfuse:r:lfuse-dump.hex:i -Uhfuse:r:hfuse-dump.hex:i
To make a backup of the existing firmware. And to flash it, from the folder with the modified blf-a6.hex I run:
avrdude -v -p attiny13 -c stk500v1 -P /dev/ttyUSB0 -b 19200 -u -Uflash:w:blf-a6.hex -Ulfuse:w:0x75:m -Uhfuse:w:0xFF:m
For some reason, I have to specify stk500v1 as the programmer, and it didn't work unless I specified the port and baud rate. If you're using an Arduino and in doubt, try disconnecting your ATtiny13 from the Arduino and uploading a sketch in the Arduino IDE using the settings here. It'll fail, but it should say what command it's using in the console window. You can copy the attributes into your AVRDUDE command.
If you're using a USB ASP programmer, instead run:
avrdude -v -p attiny13 -c usbasp -n
To see if it works and:
avrdude -v -p attiny13 -c usbasp -u -Uflash:r:flash-dump.hex:i -Ueeprom:r:eeprom-dump.hex:i -Ulfuse:r:lfuse-dump.hex:i -Uhfuse:r:hfuse-dump.hex:i
To make a backup and:
avrdude -v -p attiny13 -c usbasp -u -Uflash:w:blf-a6.hex -Ulfuse:w:0x75:m -Uhfuse:w:0xFF:m
To flash it.
-Uflash:w:blf-a6.hex refers to the file it's flashing. Replace blf-a6.hex with your file name if it's different.
-Ulfuse:w:0x75:m and -Uhfuse:w:0xFF:m are the fuses. Yours might be different, so double check the values in flashlight-firmware/bin/flash-tiny13-fuses.sh.
If it gives an out of range error, it means the image is too big to fit on the chip and you have to remove some of the code. If it successfully flashes, it should display some progress bars, then say "avrdude done. Thank you."
If it says "invalid device signature" and the jumper on your programmer is set to 3.3v, try setting it to 5v.
After you've flashed it, reassemble your flashlight and see if it works. Mine did, b̶u̶t̶ ̶t̶h̶e̶ ̶t̶i̶m̶i̶n̶g̶ ̶i̶s̶ ̶o̶f̶f̶ ̶f̶o̶r̶ ̶s̶o̶m̶e̶ ̶r̶e̶a̶s̶o̶n̶.̶ ̶T̶h̶e̶ ̶l̶o̶n̶g̶ ̶p̶r̶e̶s̶s̶e̶s̶ ̶n̶e̶e̶d̶ ̶t̶o̶ ̶b̶e̶ ̶a̶b̶o̶u̶t̶ ̶t̶h̶r̶e̶e̶ ̶s̶e̶c̶o̶n̶d̶s̶ ̶i̶n̶s̶t̶e̶a̶d̶ ̶o̶f̶ ̶1̶.̶5̶.̶ ̶I̶ ̶d̶o̶n̶'̶t̶ ̶k̶n̶o̶w̶ ̶i̶f̶ ̶i̶t̶'̶s̶ ̶s̶o̶m̶e̶t̶h̶i̶n̶g̶ ̶t̶o̶ ̶d̶o̶ ̶w̶i̶t̶h̶ ̶t̶h̶e̶ ̶A̶r̶d̶u̶i̶n̶o̶ ̶o̶r̶ ̶b̶e̶c̶a̶u̶s̶e̶ ̶I̶'̶v̶e̶ ̶u̶s̶e̶d̶ ̶t̶h̶e̶ ̶w̶r̶o̶n̶g̶ ̶s̶e̶t̶t̶i̶n̶g̶s̶ ̶s̶o̶m̶e̶w̶h̶e̶r̶e̶.̶ ̶I̶f̶ ̶y̶o̶u̶ ̶h̶a̶v̶e̶ ̶a̶n̶y̶ ̶i̶d̶e̶a̶,̶ ̶l̶e̶t̶ ̶m̶e̶ ̶k̶n̶o̶w̶ ̶i̶n̶ ̶t̶h̶e̶ ̶c̶o̶m̶m̶e̶n̶t̶s̶.̶
Edit: I fixed it. (see step 1)