2
* GXB172 Rev A - Single-Cell ~50W 17mm Boost LED Flashlight Driver
3
* Copyright (C) 2018 Gao Guangyan
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18
* Firmware version 1.0 (27 Mar 2018) for ATtiny841
19
* - For more information: www.loneoceans.com/labs/gxb172/
20
* - by Gao Guangyan, contact loneoceans@gmail.com
22
* NOTE - THIS FIRMWARE IS A CONTINUAL WORK IN PROGRESS!
23
* NOTE - Values used in this example work for a Convoy S2+ Host
25
* Code written in Arduino IDE for ease of use among hobbyists
27
* - Complied with Arduino 1.8.5 with ATtinyCore Board Library V1.1.4
28
* (http://highlowtech.org/?p=1695)
29
* - Select Tools>Board: ATTiny441/ATTiny841 (no bootloader) with 8MHz internal crystal
30
* - Board reference https://github.com/SpenceKonde/ATTinyCore
31
* (Currently using V1.1.4 of ATTinyCore)
32
* Settings: Board Attiny441/841 no bootloader, BOD LTO Disabled, ATtiny841, Clk 8MHz internal
33
* - Credit for Felias-fogg's SoftI2CMaster
34
* - Credit for PID Library V1.2.1 by Brett Beauregard
35
* PID files need to be in the same directory during compile
36
* Some parts not required removed from code to reduce compile size
38
* Complie using Arduino IDE (https://forum.arduino.cc/index.php?topic=131655.0)
39
* Flash 841 using AVR ISP2 or Atmel ICE or similar (e.g. via Atmel Studio 7).
41
* - Set fuses to Internal 8MHz Oscillator, Low.CKDIV8 unchecked.
42
* - Fuses are: 0xFF,DF,C2 (EXT, HI, LOW)
44
* "Sketch uses 6852 bytes (83%) of program storage space.
45
* Global variables use 128 bytes (25%) of dynamic memory, leaving 384 bytes for local variables."
46
* To complie, enable Verbose Output, click Verify in Arduino IDE. Get .hex from displayed dir.
51
#define I2C_TIMEOUT 1000
52
#define SDA_PORT PORTA
54
#define SCL_PORT PORTA
57
#include <SoftI2CMaster.h>
61
// #######################################
62
// ## SET DEFINITIONS ##
63
// #######################################
65
// Pin Definitions (Based on Arduino IDE)
67
const uint8_t Pin_PWM = 3; // PA7 PP15 OC2B
68
const uint8_t Pin_LedDbg = 7; // PA3 PP2
69
const uint8_t Pin_Enable = 10; // PA0 PP5
70
const uint8_t Pin_Batt = 1; // PA1 ADC1 PP4
71
const uint8_t Pin_OTCA = 2; // PA2 ADC2 PP3
72
const uint8_t Pin_OTCD = 8; // PA2 ADC2 PP3
73
const uint8_t Pin_PB1 = 1; // PB1 PP12
74
const uint8_t Pin_PB0 = 0; // PB0 PP11
76
uint8_t bVreadings = 0;
77
uint8_t runningPID = 0;
78
int currentBrightness = 0; // the current LED brightness out of 1023
80
// Flashlight brightness States
81
int brightnessValues[5] = {0,-1,45,182,852}; // Default - Modify if desired
83
uint8_t NUMSTATES = 5; // Modify if desired
84
uint8_t currentState = 0;
85
uint8_t olderState = 128; // Init with a big number at first, will be updated when program is run
86
uint8_t memory = 1; // If memory mode is on or not. 1 = ON by default
87
uint8_t runningCandlelight;
88
// runningCandlelight is set to 0 during state changes so we can get out of candlelightmode.
90
// Variables for Mode Memory and OTC
92
volatile int eepos = 0;
95
#define EEPLEN 255 // Modify this depending on micro used, some only have 128 bytes
96
#define CAP_SHORT 250 // Short Press (modify depending on OTC cap), ADC val out of 1024
97
#define CAP_LONG 100 // Medium Press (modify depending on OTC cap), functionality not used yet..
99
// Define Low Voltage Values
100
// Battery is read across 1kR low across 5.7kR network at VCC=2.5V.
102
#define LOW_BATTERY 210 // ~2.93V
103
#define CRITICAL_BATTERY 193 // ~2.69V
105
// Define TMP103 Values
108
#define TEMPERATURE_REGISTER 0x00
109
#define MAX_TEMPERATURE 128
110
#define MIN_TEMPERATURE -55
112
double SET_TEMPERATURE = 60; // PID will regulate at this temperature
113
int CRITICAL_TEMPERATURE = 90; // If temp exceeds this, shut down flashlight
116
// Define PID Variables
118
double Setpoint, Input, Output, OldOutput;
119
double Kp=1, Ki=1, Kd=1;
120
PID tempPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
121
#define PID_LOW_LIMIT 60 // Adjust to prevent too much undershoot
123
// #######################################
124
// ## METHODS FOR EEPROM MEMORY ##
125
// #######################################
127
// Wear leveling adapted from JCapSolutions STAR_off_time.c
128
// Note - be sure to clear EEPROM before using
138
EEPROM.write(eepos,eep);
139
EEPROM.write(oldpos,0xff);
142
void restore_state(){
143
// Find the saved state from the EEPROM and restore
145
for (int p=0; p<EEPLEN; p++){
146
eep = EEPROM.read(p);
153
if (currentState >= NUMSTATES){
161
if (currentState >= NUMSTATES){
176
// #######################################
177
// ## METHODS FOR TMP103 READ ##
178
// #######################################
180
uint8_t read_register(uint8_t reg){
181
uint8_t returned_data;
182
i2c_start_wait((TMP103G<<1)|I2C_WRITE);
184
i2c_rep_start((TMP103G<<1)|I2C_READ);
185
returned_data = i2c_read(true);
187
return returned_data;
190
int getTemperature(){
191
// Reads TMP103 and returns temperature in celsius
192
int temp = read_register(TEMPERATURE_REGISTER);
193
temp = constrain(temp, MIN_TEMPERATURE, MAX_TEMPERATURE);
197
// #######################################
198
// ## GENERAL METHODS ##
199
// #######################################
201
int doubleAnalogRead(uint8_t pin){
202
int adcValue = analogRead(pin);
203
adcValue = analogRead(pin);
207
void runCandlelight(){
208
// Candlelight mode - needs a lot of work.., currently not good!
209
runningCandlelight = 1;
210
while (runningCandlelight == 1){
212
checkCriticalTemperature();
213
pwmPinWrite((4+random(2)-random(2)));
214
delay(random(70)+random(50)+random(20));
220
checkBattery() reads the battery voltage a few times:
221
If it's low for a few times (to smooth readings
222
e.g. bVreadings times in a row) it then drops state.
223
If the state is 0, it checks for critical battery instead.
224
If it's below critical battery, turn light off.
227
int bV = doubleAnalogRead(Pin_Batt);
228
if (currentState == 0){
229
// Lowest state already, monitor for critical battery
230
if (bV < CRITICAL_BATTERY){
236
if (bVreadings >= 10){
237
// Battery is Critically low
238
ErrorFlashBatteryCritical();
242
// Current state is > 0, check for low battery
243
if (bV < LOW_BATTERY){
250
// Battery is low, drop state by 1 and flash error message
251
runningCandlelight = 0;
252
ErrorFlashBatteryLow();
258
// #######################################
259
// ## ERROR HANDLING ##
260
// #######################################
262
void checkCriticalTemperature(){
263
// Check if temperature is Critical. If it is, turn off.
264
int temp = getTemperature();
265
if (temp>=CRITICAL_TEMPERATURE){
266
ErrorFlashCriticalTemperature();
270
// Method for flashing certain number of times
271
void flash(uint8_t flashes){
273
rampFromValToOff(currentBrightness,2);
274
digitalWrite(Pin_Enable,LOW);
279
digitalWrite(Pin_Enable,HIGH);
281
digitalWrite(Pin_Enable,LOW);
287
void ErrorFlashBatteryLow(){
291
void ErrorFlashBatteryCritical(){
293
// Turns off LED completely, requires hard turn-off and on for safety.
302
void ErrorFlashCriticalTemperature(){
303
// Critical Temperature
304
// Turns off LED completely, requires hard turn-off and on for safety.
313
// #########################################
314
// ## METHODS FOR CHANGING LED BRIGHTNESS ##
315
// #########################################
317
void pwmPinWrite(uint16_t val){
318
// 10 bit brightness value
320
digitalWrite(Pin_PWM,LOW);
323
digitalWrite(Pin_PWM,HIGH);
325
TCCR2A &= ~(1<<COM2B0);
326
TCCR2A |= (1<<COM2B1);
328
currentBrightness = val;
331
void rampFromValToVal(int fval, int tval, int speed){
332
// speed = 1 = fastest
351
void rampToVal(int pwm, int speed){
352
// speed = 1 = fastest
355
pwm = 0; // handles cases like candlelight mode etc
357
digitalWrite(Pin_Enable,HIGH);
366
void rampFromValToOff(int pwm, int speed){
367
// speed = 1 = fastest
374
digitalWrite(Pin_Enable,LOW);
379
int newBrightness = brightnessValues[s];
381
// Initialize with lowest brightness
383
digitalWrite(Pin_Enable,HIGH);
385
if (newBrightness == -1){
389
// if current is greater than ~500mA, enable PID
390
if (newBrightness > 70){
391
tempPID.SetOutputLimits(PID_LOW_LIMIT,(double)newBrightness);
392
Output = (double)newBrightness;
393
tempPID.Initialize();
397
// If battery voltage is below 3.7V, do not run mode above ~3 Amps (514/1023)
398
// Flash error message and drop one state to alert user
400
int bV = doubleAnalogRead(Pin_Batt);
401
if (bV < 265 && newBrightness > 514){
403
ErrorFlashBatteryLow();
408
rampToVal(newBrightness,8);
413
void handleModeGroups(){
415
// Allows us to set some mode groups with X1 and X2 jumpers
416
// Default unbriged = HIGH (input pullups)
419
X1 X2 Modes (current in mA) Set Temp Crit Temp (deg C)
420
1 1 Low Candle 250 1000 5000 60C 90C (default mode)
421
1 0 Low 50 250 1500 60C 90C
422
0 1 Low 50 250 1000 4200 60C 90C
423
0 0 Low 50 250 1000 5500 60C 90C
424
Increase the current at your own risk ;)...
427
int x1 = digitalRead(Pin_PB0);
428
int x2 = digitalRead(Pin_PB1);
430
if(x1 == HIGH && x2 == LOW){
432
brightnessValues[1] = 8;
433
//brightnessValues[2] = 45;
434
brightnessValues[3] = 275;
435
if (currentState>=4){
439
else if(x1 == LOW && x2 == HIGH){
440
brightnessValues[1] = 8;
441
brightnessValues[4] = 726;
443
else if(x1 == LOW && x2 == LOW){
444
brightnessValues[1] = 8;
445
brightnessValues[4] = 938;
449
// #######################################
450
// ## INITILIZATION ##
451
// #######################################
454
analogReference(DEFAULT);
455
pinMode(Pin_LedDbg,OUTPUT);
456
pinMode(Pin_OTCD,INPUT);
458
// First up let's read ADC Value on OTC
459
int otc_val = doubleAnalogRead(Pin_OTCA);
461
// Next restore state and take action based on OTC reading
462
// Long Press not implemented yet.. TODO
464
if(otc_val>CAP_SHORT){
468
// Long press, or reset to the first mode
475
pinMode(Pin_PWM,OUTPUT);
476
pinMode(Pin_Enable,OUTPUT);
477
pinMode(Pin_OTCD, OUTPUT);
478
pinMode(Pin_PB0,INPUT_PULLUP);
479
pinMode(Pin_PB1,INPUT_PULLUP);
480
pinMode(Pin_Batt,INPUT);
482
digitalWrite(Pin_OTCD,HIGH);
483
digitalWrite(Pin_Enable,LOW);
484
digitalWrite(Pin_PWM,LOW);
490
// 7.8kHz 10bit PWM setup
491
TCCR2B = (TCCR2B & 0b11111000) | 0x01;
492
TCCR2B &= ~(1 << WGM23); // Timer B clear bit
493
TCCR2B |= (1 << WGM22); // set bit
494
TCCR2A |= (1 << WGM21); // Timer A set bit
495
TCCR2A |= (1 << WGM20); // set bit
497
// If I2C does not initialize, flash error message
498
if (!i2c_init()) ErrorFlashCriticalTemperature();
501
Input = getTemperature(); // Initialize starting value to be starting Temperature instead of 0
502
Setpoint = SET_TEMPERATURE; // Set our PID to be the desired Set Temperature
503
tempPID.SetMode(AUTOMATIC);
507
// #######################################
508
// ## MAIN APPLICATION LOOP ##
509
// #######################################
513
// Run LED depending on state
514
if (currentState!=olderState){
515
olderState = currentState;
516
runLED(currentState);
519
// Now compute PID or run regular error handling
521
if (runningPID == 1){
522
checkCriticalTemperature();
525
Input = getTemperature();
531
// not running PID; light is on lower power levels
532
checkCriticalTemperature();
535
// Delay a little since we don't need to keep doing readings