Attiny13 flashlight controller debugging

Hello, I was modding this code that was working before (so was the circuit), but after adding some calculations to compensate for the battery voltage variation it stopped working but it compiles with no error, so I would like somebody to check it out and see if you find something since the attiny13 has no Serial its a little harder to debug.

This is the first one that works but has no compensation for voltage variation…

//Flashlight controller aTTiny13a
//Low battery indicator, fast PWM, 10% - 35% - 100%, Strobe.

#define WhitePin 0
#define ButtonPin 1
#define BatRead A1
#define RedPin 3
#define GreenPin 4
byte LedPower = 25;
byte Mode = 0;
unsigned int BatVolt;
unsigned long i;

void setup() {
  analogReference(INTERNAL);
  TCCR0A |= _BV(WGM01) | _BV(WGM00); // set timer mode to FAST PWM
  TCCR0A |= _BV(COM0A1); // connect PWM signal to pin (AC0A => PB0)
  TCCR0B = (TCCR0B & ~((1 << CS02) | (1 << CS01) | (1 << CS00))) | (_BV(CS00));
  DDRB |= (1 << DDB0); //WhitePin as output
  OCR0A = LedPower;
  DDRB |= (0 << DDB1); //ButtonPin as input
  DDRB |= (1 << DDB3); //RedPin as output
  DDRB |= (1 << DDB4); //GreenPin as output
}

void loop() {
  BatVolt = analogRead(BatRead);      //3,75 at 65% e 3,6v at35%
  if (BatVolt > 957) {
    digitalWrite(RedPin, LOW);
    digitalWrite(GreenPin, HIGH);
  }
  else if (BatVolt < 958 && BatVolt > 920) {
    digitalWrite(RedPin, HIGH);
    digitalWrite(GreenPin, HIGH);
  }
  else if (BatVolt < 921) {
    digitalWrite(RedPin, HIGH);
    digitalWrite(GreenPin, LOW);
  }

  if (digitalRead(ButtonPin) == LOW ) {
    delay(130);
    if (Mode < 4) {
      Mode++;
    }
    else if (Mode >= 4) {
      Mode = 0;
    }

    if (Mode == 0) {
      LedPower = 24;
    }
    else if (Mode == 1) {
      LedPower = 90;
    }
    else if (Mode == 2) {
      LedPower = 255;
    }
    OCR0A = LedPower;
  }
  if (Mode == 3) {
    delay(90);
    if (LedPower == 24) {
      LedPower = 255;
    }
    else {
      LedPower = 24;
    }
    OCR0A = LedPower;
  }
}

And this is the one not working after the modifications:

//Flashlight controller aTTiny13a
//Low battery indicator, fast PWM, 10% - 35% - 100%, Strobe.

#define WhitePin 0
#define ButtonPin 1
#define BatPin A1
#define RedPin 3
byte LedPower = 25;
byte Mode = 0;
byte MaxP;
byte MedP;
byte MinP;
unsigned int BatReading;

void setup() {
  analogReference(INTERNAL);
  TCCR0A |= _BV(WGM01) | _BV(WGM00); // set timer mode to FAST PWM
  TCCR0A |= _BV(COM0A1); // connect PWM signal to pin (AC0A => PB0)
  TCCR0B = (TCCR0B & ~((1 << CS02) | (1 << CS01) | (1 << CS00))) | (_BV(CS00));
  DDRB |= (1 << DDB0); //WhitePin as output
  OCR0A = LedPower;
  DDRB |= (0 << DDB1); //ButtonPin as input
  DDRB |= (1 << DDB3); //RedPin as output
}

void loop() {

  BatReading = analogRead(BatPin);
  if (BatReading > 698) {                //power compensation MaxP 174/255 at 4.2v
    MaxP = 255 - ((BatReading - 698) / 4);
    MedP = (MaxP / 255) * 90;
    MinP = (MaxP / 255) * 24;
  }
  else if (BatReading <= 698) {
    MaxP = 255;
    MedP = 90;
    MinP = 24;
  }
  if (BatReading > 872) {
    DDRB |= (0 << PB3);
  }
  else if (BatReading <= 872 && BatReading >= 837) {//Between 3.75v and 3.6v (65%-35%)
    DDRB |= (1 << PB3);
    DDRB |= (0 << PB3);
  }
  else if (BatReading < 837) {  //Less than 3.6v
    DDRB |= (1 << PB3);
  }

  if (digitalRead(ButtonPin) == LOW ) {
    delay(130);
    if (Mode < 4) {
      Mode++;
    }
    else if (Mode >= 4) {
      Mode = 0;
    }
    if (Mode == 0) {
      LedPower = MinP;
    }
    else if (Mode == 1) {
      LedPower = MedP;
    }
    else if (Mode == 2) {
      LedPower = MaxP;
    }
    OCR0A = LedPower;
  }
  if (Mode == 3) {
    delay(90);
    if (LedPower < 110) {
      LedPower = MaxP;
    }
    else {
      LedPower = MinP;
    }
    OCR0A = LedPower;
  }
}

DDB4 has gone as output and this looks odd to me:

else if (BatReading <= 872 && BatReading >= 837) {//Between 3.75v and 3.6v (65%-35%)
    DDRB |= (1 << PB3);
    DDRB |= (0 << PB3);

PB3 high and then immediately low again

as hmeijdam suggests, why change the direction bits based on measured voltage

Or did you mean to replace your digitalWrite by port/pin manipulation?
Then look at below example, as you need something different then

   PORTB |= (1 << PB3);			//replaces digitalWrite(PB3, HIGH);
   PORTB &= ~(1 << PB3);		//replaces digitalWrite(PB3, LOW);

Borrowed from this example sketch

#include <avr/io.h>
void setup(){
DDRB |= (1 << PB3);			//replaces pinMode(PB3, OUTPUT);
DDRB |= (1 << PB4);  			//replaces pinMode(PB4, OUTPUT);
}
void loop()
{
 delay(random(600000, 900000));
 byte state = random(0, 2);
 switch(state)
  {
   case 0:
   PORTB |= (1 << PB3);			//replaces digitalWrite(PB3, HIGH);
   delay(20);
   PORTB &= ~(1 << PB3);		//replaces digitalWrite(PB3, LOW);
   break;
   case 1:
   PORTB |= (1 << PB4);			//replaces digitalWrite(PB4, HIGH);
   delay(20);
   PORTB &= ~(1 << PB4);		//replaces digitalWrite(PB4, LOW);
   break;
  }
}

Or did you mean to replace your digitalWrite by port/pin manipulation?

Yes, thanks, simple mistake that I’d take a while to find out.

Here’s the corrected code, now it works, but it seems to have one last little issue, when pressing the button (I can’t use millis() because of FastPwm being used, so I used delay for debouncing…) it seems like when going from mode 3 (strobe) to mode 0 (low intensity flashlight) quite often the LED gets strange, either blinks strangely or lights up very weak… Any ideas why?

//Flashlight controller aTTiny13a
//Low battery indicator, fast PWM, 10% - 35% - 100%, Strobe.

#define WhitePin 0
#define ButtonPin 1
#define BatPin A1
#define RedPin 3
byte LedPower = 25;
byte Mode = 0;
byte MaxP;
byte MedP;
byte MinP;
unsigned int BatReading;

void setup() {
  analogReference(INTERNAL);
  TCCR0A |= _BV(WGM01) | _BV(WGM00); // set timer mode to FAST PWM
  TCCR0A |= _BV(COM0A1); // connect PWM signal to pin (AC0A => PB0)
  TCCR0B = (TCCR0B & ~((1 << CS02) | (1 << CS01) | (1 << CS00))) | (_BV(CS00));
  DDRB |= (1 << DDB3); //RedPin as output
  DDRB |= (1 << DDB0); //WhitePin as output
  OCR0A = LedPower;
  DDRB |= (0 << DDB1); //ButtonPin as input
}

void loop() {

  BatReading = analogRead(BatPin);
  if (BatReading > 698) {                //power compensation MaxP 174/255 at 4.2v
    MaxP = 255 - ((BatReading - 698) / 4);
    MedP = (MaxP / 255) * 90;
    MinP = (MaxP / 255) * 24;
  }
  else if (BatReading <= 698) {
    MaxP = 255;
    MedP = 90;
    MinP = 24;
  }
  if (BatReading > 872) {
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading <= 872 && BatReading >= 837) {//Between 3.75v and 3.6v (65%-35%)
    PORTB |= (1 << PB3);
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading < 837) {  //Less than 3.6v
    PORTB |= (1 << PB3);
  }

  if (digitalRead(ButtonPin) == LOW ) {
    delay(130);
    if (Mode < 4) {
      Mode++;
    }
    else if (Mode >= 4) {
      Mode = 0;
    }
    if (Mode == 0) {
      LedPower = MinP;
    }
    else if (Mode == 1) {
      LedPower = MedP;
    }
    else if (Mode == 2) {
      LedPower = MaxP;
    }
    OCR0A = LedPower;
  }
  if (Mode == 3) {
    delay(90);
    if (LedPower < 110) {
      LedPower = MaxP;
    }
    else {
      LedPower = MinP;
    }
    OCR0A = LedPower;
  }
}

If mode is three when the button is pressed, mode becomes four and there is nothing specific for that mode for controlling the LED. What it does will depend on how mode three code left LedPower.

What it does will depend on how mode three code left LedPower.

I was aware of that, but even changing it doesn’t fix the problem, I suspect it may be something to do with the voltage compensation, but honestly I don’t know.

//Flashlight controller aTTiny13a
//Low battery indicator, fast PWM, 10% - 35% - 100%, Strobe.

#define WhitePin 0
#define ButtonPin 1
#define BatPin A1
#define RedPin 3
byte LedPower = 25;
byte Mode = 0;
byte MaxP;
byte MedP;
byte MinP;
unsigned int BatReading;

void setup() {
  analogReference(INTERNAL);
  TCCR0A |= _BV(WGM01) | _BV(WGM00); // set timer mode to FAST PWM
  TCCR0A |= _BV(COM0A1); // connect PWM signal to pin (AC0A => PB0)
  TCCR0B = (TCCR0B & ~((1 << CS02) | (1 << CS01) | (1 << CS00))) | (_BV(CS00));
  DDRB |= (1 << DDB3); //RedPin as output
  DDRB |= (1 << DDB0); //WhitePin as output
  OCR0A = LedPower;
  DDRB |= (0 << DDB1); //ButtonPin as input
}

void loop() {

  BatReading = analogRead(BatPin);
  if (BatReading > 698) {                //power compensation MaxP 174/255 at 4.2v
    MaxP = 255 - ((BatReading - 698) / 4);
    MedP = (MaxP / 255) * 90;
    MinP = (MaxP / 255) * 24;
  }
  else if (BatReading <= 698) {
    MaxP = 255;
    MedP = 90;
    MinP = 24;
  }
  if (BatReading > 872) {
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading <= 872 && BatReading >= 837) {//Between 3.75v and 3.6v (65%-35%)
    PORTB |= (1 << PB3);
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading < 837) {  //Less than 3.6v
    PORTB |= (1 << PB3);
  }

  if (digitalRead(ButtonPin) == LOW ) {
    delay(130);
    if (Mode < 3) {
      Mode++;
    }
    else if (Mode >= 3) {
      Mode = 0;
    }
    if (Mode == 0) {
      LedPower = MinP;
    }
    else if (Mode == 1) {
      LedPower = MedP;
    }
    else if (Mode == 2) {
      LedPower = MaxP;
    }
    OCR0A = LedPower;
  }
  if (Mode == 3) {
    delay(90);
    if (LedPower < 110) {
      LedPower = MaxP;
    }
    else {
      LedPower = MinP;
    }
    OCR0A = LedPower;
  }
}

This does not set a one bit to zero (ORing with zero leaves the value unchanged).

  DDRB |= (0 << DDB1); //ButtonPin as input

As already mentioned above, to set a bit to zero, use bitwise AND

  DDRB &= ~(1 << DDB1); //ButtonPin as input

I changed the bottom part into a switch/case as I found all the ifs difficult to read and incorporated jremington’s find.

//Flashlight controller aTTiny13a
//Low battery indicator, fast PWM, 10% - 35% - 100%, Strobe.

#define WhitePin 0
#define ButtonPin 1
#define BatPin A1
#define RedPin 3
byte LedPower = 25;
byte Mode = 0;
byte MaxP;
byte MedP;
byte MinP;
unsigned int BatReading;

void setup() {
  analogReference(INTERNAL);
  TCCR0A |= _BV(WGM01) | _BV(WGM00); // set timer mode to FAST PWM
  TCCR0A |= _BV(COM0A1); // connect PWM signal to pin (AC0A => PB0)
  TCCR0B = (TCCR0B & ~((1 << CS02) | (1 << CS01) | (1 << CS00))) | (_BV(CS00));
  DDRB |= (1 << DDB3); //RedPin as output
  DDRB |= (1 << DDB0); //WhitePin as output
  OCR0A = LedPower;
  DDRB &= ~(1 << DDB1); //ButtonPin as input
}

void loop() {

  BatReading = analogRead(BatPin);
  if (BatReading > 698) {                //power compensation MaxP 174/255 at 4.2v
    MaxP = 255 - ((BatReading - 698) / 4);
    MedP = (MaxP / 255) * 90;
    MinP = (MaxP / 255) * 24;
  }
  else if (BatReading <= 698) {
    MaxP = 255;
    MedP = 90;
    MinP = 24;
  }
  if (BatReading > 872) {
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading <= 872 && BatReading >= 837) {//Between 3.75v and 3.6v (65%-35%)
    PORTB |= (1 << PB3);
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading < 837) {  //Less than 3.6v
    PORTB |= (1 << PB3);
  }

  if (digitalRead(ButtonPin) == LOW ) {
    delay(130);
    Mode++;
    switch (Mode) {
      case 1:
        LedPower = MedP;
        break;
      case 2:
        LedPower = MaxP;
        break;
      case 3:
        // do nothing, LedPower taken care of separately
        break;
      default: //anything else
        LedPower = MinP;
        Mode = 0;
        break;
    }
  }
  if (Mode == 3) {
    delay(90);
    if (LedPower < 110) {
      LedPower = MaxP;
    }
    else {
      LedPower = MinP;
    }
  }
  OCR0A = LedPower;
}

compiled ok but not tested “live”

This does not set a one bit to zero (ORing with zero leaves the value unchanged).

I fixed that but still, after going from mode 3 (strobe) to MinP it seems like mode 0 and 1 don’t work, the led gets very weak, only mode 2 and 3 work.

//Flashlight controller aTTiny13a
//Low battery indicator, fast PWM, 10% - 35% - 100%, Strobe.

#define WhitePin 0
#define ButtonPin 1
#define BatPin A1
#define RedPin 3
byte LedPower = 25;
byte Mode = 0;
byte MaxP;
byte MedP;
byte MinP;
unsigned int BatReading;

void setup() {
  analogReference(INTERNAL);
  TCCR0A |= _BV(WGM01) | _BV(WGM00); // set timer mode to FAST PWM
  TCCR0A |= _BV(COM0A1); // connect PWM signal to pin (AC0A => PB0)
  TCCR0B = (TCCR0B & ~((1 << CS02) | (1 << CS01) | (1 << CS00))) | (_BV(CS00));
  DDRB |= (1 << DDB3); //RedPin as output
  DDRB |= (1 << DDB0); //WhitePin as output
  OCR0A = LedPower;
  DDRB &= ~(1 << DDB1); //ButtonPin as input
}

void loop() {

  BatReading = analogRead(BatPin);
  if (BatReading > 698) {                //power compensation MaxP 174/255 at 4.2v
    MaxP = 255 - ((BatReading - 698) / 4);
    MedP = (MaxP / 255) * 90;
    MinP = (MaxP / 255) * 24;
  }
  else if (BatReading <= 698) {
    MaxP = 255;
    MedP = 90;
    MinP = 24;
  }
  if (BatReading > 872) {
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading <= 872 && BatReading >= 837) {//Between 3.75v and 3.6v (65%-35%)
    PORTB |= (1 << PB3);
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading < 837) {  //Less than 3.6v
    PORTB |= (1 << PB3);
  }

  if (digitalRead(ButtonPin) == LOW ) {
    delay(130);
    if (Mode < 3) {
      Mode++;
    }
    else if (Mode >= 3) {
      Mode = 0;
    }
    if (Mode == 0) {
      LedPower = MinP;
    }
    else if (Mode == 1) {
      LedPower = MedP;
    }
    else if (Mode == 2) {
      LedPower = MaxP;
    }
    OCR0A = LedPower;
  }
  if (Mode == 3) {
    delay(90);
    if (LedPower < 110) {
      LedPower = MaxP;
    }
    else {
      LedPower = MinP;
    }
    OCR0A = LedPower;
  }
}

I changed the bottom part into a switch/case as I found all the ifs difficult to read and incorporated jremington's find.

Your code didn't work properly either... Only mode 3 is working this way.

caiopoit: Your code didn't work properly either... Only mode 3 is working this way.

hmm...maybe time to wire up my breadboard and learn something. Which T13 core do you use?

I use this core:

I tested something and the error appears to be at the voltage correction calculation since this code “works”, I fixed the value as you can see the commented out part:

//Flashlight controller aTTiny13a
//Low battery indicator, fast PWM, 10% - 35% - 100%, Strobe.

#define WhitePin 0
#define ButtonPin 1
#define BatPin A1
#define RedPin 3
byte LedPower = 25;
byte Mode = 0;
byte MaxP;
byte MedP;
byte MinP;
unsigned int BatReading;

void setup() {
  analogReference(INTERNAL);
  TCCR0A |= _BV(WGM01) | _BV(WGM00); // set timer mode to FAST PWM
  TCCR0A |= _BV(COM0A1); // connect PWM signal to pin (AC0A => PB0)
  TCCR0B = (TCCR0B & ~((1 << CS02) | (1 << CS01) | (1 << CS00))) | (_BV(CS00));
  DDRB |= (1 << DDB3); //RedPin as output
  DDRB |= (1 << DDB0); //WhitePin as output
  OCR0A = LedPower;
  DDRB &= ~(1 << DDB1); //ButtonPin as input
}

void loop() {

  BatReading = analogRead(BatPin);
  if (BatReading > 698) {                //power compensation MaxP 174/255 at 4.2v
    MaxP = 255 - ((BatReading - 698) / 4);
    MedP = 90;//(MaxP / 255) * 90;
    MinP = 24;//(MaxP / 255) * 24;
  }
  else if (BatReading <= 698) {
    MaxP = 255;
    MedP = 90;
    MinP = 24;
  }
  if (BatReading > 872) {
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading <= 872 && BatReading >= 837) {//Between 3.75v and 3.6v (65%-35%)
    PORTB |= (1 << PB3);
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading < 837) {  //Less than 3.6v
    PORTB |= (1 << PB3);
  }

  if (digitalRead(ButtonPin) == LOW ) {
    delay(130);
    if (Mode < 3) {
      Mode++;
    }
    else if (Mode >= 3) {
      Mode = 0;
    }
    if (Mode == 0) {
      LedPower = MinP;
      analogWrite(WhitePin, MinP);
    }
    else if (Mode == 1) {
      LedPower = MedP;
      analogWrite(WhitePin, MedP);
    }
    else if (Mode == 2) {
      LedPower = MaxP;
      analogWrite(WhitePin, MaxP);
    }
  }
  if (Mode == 3) {
    delay(90);
    if (LedPower < 110) {
      LedPower = MaxP;
      analogWrite(WhitePin, LedPower);
    }
    else {
      LedPower = MinP;
      analogWrite(WhitePin, LedPower);
    }
  }
}

For handling a button push, this is a bit simpler:

  if (digitalRead(ButtonPin) == LOW ) {
      delay(130);
      Mode++;
      if (Mode >= 4) Mode = 0;
    }

All those else ifs are confusing. I avoid them.

Now it seems to be working fine… I just need to check with an oscilloscope if the compensation is working as intended.

//Flashlight controller aTTiny13a
//Low battery indicator, fast PWM, 10% - 35% - 100%, Strobe.

#define WhitePin 0
#define ButtonPin 1
#define BatPin A1
#define RedPin 3
byte LedPower = 25;
byte Mode = 0;
byte MaxP;
byte MedP;
byte MinP;
unsigned int BatReading;

void setup() {
  analogReference(INTERNAL);
  TCCR0A |= _BV(WGM01) | _BV(WGM00); // set timer mode to FAST PWM
  TCCR0A |= _BV(COM0A1); // connect PWM signal to pin (AC0A => PB0)
  TCCR0B = (TCCR0B & ~((1 << CS02) | (1 << CS01) | (1 << CS00))) | (_BV(CS00));
  DDRB |= (1 << DDB3); //RedPin as output
  DDRB |= (1 << DDB0); //WhitePin as output
  OCR0A = LedPower;
  DDRB &= ~(1 << DDB1); //ButtonPin as input
}

void loop() {

  BatReading = analogRead(BatPin);
  if (BatReading > 698) {                //power compensation MaxP 174/255 at 4.2v
    MaxP = 255 - ((BatReading - 698) / 4);
    MedP = ((((MaxP * 100) / 255) * 90) / 100);
    MinP = ((((MaxP * 100) / 255) * 24) / 100);
  }
  else if (BatReading <= 698) {
    MaxP = 255;
    MedP = 90;
    MinP = 24;
  }
  if (BatReading > 872) {
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading <= 872 && BatReading >= 837) {//Between 3.75v and 3.6v (65%-35%)
    PORTB |= (1 << PB3);
    PORTB &= ~(1 << PB3);
  }
  else if (BatReading < 837) {  //Less than 3.6v
    PORTB |= (1 << PB3);
  }

  if (digitalRead(ButtonPin) == LOW ) {
    delay(130);
    Mode++;
    if (Mode >= 4) Mode = 0;
  }
  if (Mode == 0) {
    LedPower = MinP;
    analogWrite(WhitePin, MinP);
  }
  else if (Mode == 1) {
    LedPower = MedP;
    analogWrite(WhitePin, MedP);
  }
  else if (Mode == 2) {
    LedPower = MaxP;
    analogWrite(WhitePin, MaxP);
  }
  if (Mode == 3) {
    delay(90);
    if (LedPower < 110) {
      LedPower = MaxP;
      analogWrite(WhitePin, LedPower);
    }
    else {
      LedPower = MinP;
      analogWrite(WhitePin, LedPower);
    }
  }
}

That will also depend on the voltage divider you use to bring the 1S lipo (or li-ion) voltage range from 3 to 4.2 volt in the range of 0-1.1 volt, as anything over 1.1 volt is now measured as 1.1 volt.
At least that is what I observed on my breadboard. I had never used the internal reference voltage as basis for the analogRead, so was curious how that works out.

I did add one line just before “void setup()”

 PORTB |= (1 << PB1); // activate internal pull-up resistor on input pin

or I had to add an external pullup resistor to avoid the floating in-pin. Your flashlight must have an external pullup resistor.

That will also depend on the voltage divider you use to bring the 1S lipo (or li-ion) voltage range from 3 to 4.2 volt in the range of 0-1.1 volt, as anything over 1.1 volt is now measured as 1.1 volt.

I always use a trimpot! Best solution for a small and convenient voltage divider... And, about the pull-up for some reason I thought that the attiny13 had no internal resistor available. Thanks for the shout out!