Need help wit ATtiny85 (Solved)

Hi to all

I read several discussions here and other sites about programming an Attiny85.

Most issues were regarding the clock timer and the frequency to use.

A have one that I would like to use in a small project, a crane moving left to right and up and down with two servos.

I have the code working on an UNO (it is not “elegant” but it seems to work).

#include <Servo.h>

Servo servox;
Servo servoy;
int servoxpin = 2;
int servoypin = 3;
int servoxpos;
int servoypos;
int servoxcurrpos;
int servoycurrpos;
int servomove = 1;
int servoxstate;
int servoystate;
int back = 175;
int front = 5;
int middle = 90;
int up = 175;
int down = 5;

int joystickxpin = A2;
int joystickypin = A3;
int joystickxread;
int joystickyread;

int buttonpin = A1;
int buttonread;
int buttonstate = 0;
int buttonprev;

int state;
int prog;

unsigned long currtime;

unsigned long servotime;
unsigned long servodelay = 20;

unsigned long buttontime;
unsigned long buttondelay = 50;

unsigned long waittime;

unsigned long warntime;
unsigned long warndelay = 2000;

void setup() {
  Serial.begin(9600);
  servox.attach(servoxpin);
  servoy.attach(servoypin);
  pinMode(joystickxpin, INPUT);
  pinMode(joystickypin, INPUT);
  joystickxread = analogRead(joystickxpin);
  joystickyread = analogRead(joystickypin);
  servoxpos = map(joystickxread, 0, 1023, 0, 180);
  servoypos = map(joystickyread, 0, 1023, 0, 180);
  servox.write(servoxpos);
  servoy.write(servoypos);
  pinMode(buttonpin, INPUT_PULLUP);
  buttonprev = HIGH;
}

void loop() {
  currtime = millis();
  buttonread = digitalRead(buttonpin);
  if (buttonread != buttonprev) {
    buttontime = millis();
  }
  if (currtime - buttontime > buttondelay) {
    if (buttonread != buttonstate) {
      buttonstate = buttonread;
      if (buttonstate == LOW) {
        state ++;
        prog = 0;
        if (state > 2) {
          state = 0;
        }
        Serial.print("o botão foi pressionado e o estado é ");
        Serial.println(state);
      }
    }
  }
  buttonprev = buttonread;

  switch (state) {
    case 0:
      if (currtime - warntime > warndelay) {
        warntime = millis();
        Serial.println("à espera de ordens ");
      }
      break;
    case 1:
      if (currtime - servotime >= servodelay) {
        servotime = millis();
        joystickxread = analogRead(joystickxpin);
        joystickyread = analogRead(joystickypin);
        servoxpos = map(joystickxread, 0, 1023, 0, 180);
        servoypos = map(joystickyread, 0, 1023, 0, 180);
        servox.write(servoxpos);
        servoy.write(servoypos);
        Serial.print("a posicao x é ");
        Serial.print(servoxpos);
        Serial.print(" e a posicao y é ");
        Serial.println(servoypos);
        Serial.print("o estado é ");
        Serial.println(state);
      }
      break;
    case 2:
      switch (prog) {
        case 0:
          f_wait(1000);
          break;
        case 1:
          f_servox(back);
          break;
        case 2:
          f_wait(1000);
          break;
        case 3:
          f_servoy(down);
          break;
        case 4:
          f_wait(3000);
          break;
        case 5:
          f_servoy(up);
          break;
        case 6:
          f_wait(1000);
          break;
        case 7:
          f_servox(front);
          break;
        case 8:
          f_wait(500);
          break;
        case 9:
          f_servoy(down);
          break;
        case 10:
          f_wait(3000);
          break;
        case 11:
          f_servoy(up);
          break;
        case 12:
          f_wait(500);
          break;
        case 13:
          f_servox(middle);
          break;
        case 14:
          f_wait(5000);
          break;
        default:

          state = 0;
          break;
      }
      break;
    default:
      state = 0;
  }
}

void f_wait(unsigned long w) {
  if (currtime - waittime >= w) {
    prog++;
  }
}

void f_servox(int x) {
  servoxcurrpos = servox.read();
  if (x == servoxcurrpos) {
    servoxstate = 0;
  }
  else if (x > servoxcurrpos) {
    servoxstate = 1;
  }
  else if (x < servoxcurrpos) {
    servoxstate = 2;
  }
  switch (servoxstate) {
    case 0:
      prog++;
      waittime = millis();
      Serial.print("next step ");
      Serial.println(prog);
      break;
    case 1: //fw
      if (currtime - servotime > servodelay && x >= servoxcurrpos) {
        servotime = millis();
        servoxpos = servoxcurrpos + servomove;
        servox.write(servoxpos);
        Serial.print("o estado é ");
        Serial.print(servoxstate);
        Serial.print(" e o programa é ");
        Serial.print(prog);
        Serial.print(" posição x ");
        Serial.print(servoxcurrpos);
        Serial.print(" ");
        Serial.println(servoxpos);
      }
      if (x < servoxpos) {
        servoxstate = 0;
        Serial.print("reached ");
        Serial.println(x);
      }
      break;
    case 2: //rv
      if (currtime - servotime > servodelay && x <= servoxcurrpos) {
        servotime = millis();
        servoxpos = servoxcurrpos - servomove;
        servox.write(servoxpos);
        Serial.print("o estado é ");
        Serial.print(servoxstate);
        Serial.print(" e o programa é ");
        Serial.print(prog);
        Serial.print(" posição x ");
        Serial.print(servoxcurrpos);
        Serial.print(" ");
        Serial.println(servoxpos);
      }
      if (x > servoxpos) {
        servoxstate = 0;
        Serial.print("reached ");
        Serial.println(x);
      }
      break;
    default:
      servoxstate = 0;
      break;
  }
}

void f_servoy(int y) {
  servoycurrpos = servoy.read();
  if (y == servoycurrpos) {
    servoystate = 0;
  }
  else if (y > servoycurrpos) {
    servoystate = 1;
  }
  else if (y < servoycurrpos) {
    servoystate = 2;
  }
  switch (servoystate) {
    case 0:
      prog++;
      waittime = millis();
      Serial.print("next step ");
      Serial.println(prog);
      break;
    case 1: //fw
      if (currtime - servotime > servodelay && y >= servoycurrpos) {
        servotime = millis();
        servoypos = servoycurrpos + servomove;
        servoy.write(servoypos);
        Serial.print("o estado é ");
        Serial.print(servoystate);
        Serial.print(" e o programa é ");
        Serial.print(prog);
        Serial.print(" posição y ");
        Serial.print(servoycurrpos);
        Serial.print(" ");
        Serial.println(servoypos);
      }
      if (y < servoypos) {
        servoystate = 0;
        Serial.print("reached ");
        Serial.println(y);
      }
      break;
    case 2: //rv
      if (currtime - servotime > servodelay && y <= servoycurrpos) {
        servotime = millis();
        servoypos = servoycurrpos - servomove;
        servoy.write(servoypos);
        Serial.print("o estado é ");
        Serial.print(servoystate);
        Serial.print(" e o programa é ");
        Serial.print(prog);
        Serial.print(" posição y ");
        Serial.print(servoycurrpos);
        Serial.print(" ");
        Serial.println(servoypos);
      }
      if (y > servoypos) {
        servoystate = 0;
        Serial.print("reached ");
        Serial.println(y);
      }
      break;
    default:
      servoystate = 0;
      break;
  }
}

Basically I have a pushbutton that if is not pressed, the controller is in a sort of “idle mode” (doing nothing). When i press it, it enters a “manual” mode, where the servos are controlled by a joystick. If I press a second time, it enters an “automatic mode”, where it executes some moves up and down and left to right. The third press returns to “idle mode”.

I have the “serial” commands just for debugging, they were removed when uploading to the Attiny85.
I used Spence Konde board configuration, as it enables the use of Servo.h and tried boatloading different MHz options.

But I could not replicate the results of the UNO. In my inexperienced opinion, I think the timings are not correct. The “manual” mode does not work and in “automatic mode” the movements are shorter.

Can this be solved? Can you help me with the code?

Thanks in advance
Filipe Almeida

I used Spence Konde board configuration, as it enables the use of Servo.h

pulsing the Servo manually is usually a better option.

in "automatic mode" the movements are shorter.

that means that the pulse is shorter than it should be, which can be either the result of the timers not being configured correctly, or the actually clock speed being higher than the clock speed used for calculating the length of the pulses

Can this be solved? Can you help me with the code?

Probably it can be solved. Please show the code that was actually uploaded (without all the Serial)

Hi again.

It is basically the same code, with the pins modified to the Attiny, but here it goes.

#include <Servo.h>

Servo servox;
Servo servoy;
int servoxpin = 0;
int servoypin = 1;
int servoxpos;
int servoypos;
int servoxcurrpos;
int servoycurrpos;
int servomove = 1;
int servoxstate;
int servoystate;
int back = 175;
int front = 5;
int middle = 90;
int up = 175;
int down = 5;

int joystickxpin = A2;
int joystickypin = A3;
int joystickxread;
int joystickyread;

int buttonpin = A1;
int buttonread;
int buttonstate = 0;
int buttonprev;

int state;
int prog;

unsigned long currtime;

unsigned long servotime;
unsigned long servodelay = 20;

unsigned long buttontime;
unsigned long buttondelay = 50;

unsigned long waittime;

unsigned long warntime;
unsigned long warndelay = 2000;

void setup() {

  servox.attach(servoxpin);
  servoy.attach(servoypin);
  pinMode(joystickxpin, INPUT);
  pinMode(joystickypin, INPUT);
  joystickxread = analogRead(joystickxpin);
  joystickyread = analogRead(joystickypin);
  servoxpos = map(joystickxread, 0, 1023, 0, 180);
  servoypos = map(joystickyread, 0, 1023, 0, 180);
  servox.write(servoxpos);
  servoy.write(servoypos);
  pinMode(buttonpin, INPUT_PULLUP);
  buttonprev = HIGH;
}

void loop() {
  currtime = millis();
  buttonread = digitalRead(buttonpin);
  if (buttonread != buttonprev) {
    buttontime = millis();
  }
  if (currtime - buttontime > buttondelay) {
    if (buttonread != buttonstate) {
      buttonstate = buttonread;
      if (buttonstate == LOW) {
        state ++;
        prog = 0;
        if (state > 2) {
          state = 0;
        }

      }
    }
  }
  buttonprev = buttonread;

  switch (state) {
    case 0:
      if (currtime - warntime > warndelay) {
        warntime = millis();

      }
      break;
    case 1:
      if (currtime - servotime >= servodelay) {
        servotime = millis();
        joystickxread = analogRead(joystickxpin);
        joystickyread = analogRead(joystickypin);
        servoxpos = map(joystickxread, 0, 1023, 0, 180);
        servoypos = map(joystickyread, 0, 1023, 0, 180);
        servox.write(servoxpos);
        servoy.write(servoypos);

      }
      break;
    case 2:
      switch (prog) {
        case 0:
          f_wait(1000);
          break;
        case 1:
          f_servox(back);
          break;
        case 2:
          f_wait(1000);
          break;
        case 3:
          f_servoy(down);
          break;
        case 4:
          f_wait(3000);
          break;
        case 5:
          f_servoy(up);
          break;
        case 6:
          f_wait(1000);
          break;
        case 7:
          f_servox(front);
          break;
        case 8:
          f_wait(500);
          break;
        case 9:
          f_servoy(down);
          break;
        case 10:
          f_wait(3000);
          break;
        case 11:
          f_servoy(up);
          break;
        case 12:
          f_wait(500);
          break;
        case 13:
          f_servox(middle);
          break;
        case 14:
          f_wait(5000);
          break;
        default:
          
          state = 0;
          break;
      }
      break;
    default:
      state = 0;
  }
}

void f_wait(unsigned long w) {
  if (currtime - waittime >= w) {
    prog++;
  }
}

void f_servox(int x) {
  servoxcurrpos = servox.read();
  if (x == servoxcurrpos) {
    servoxstate = 0;
  }
  else if (x > servoxcurrpos) {
    servoxstate = 1;
  }
  else if (x < servoxcurrpos) {
    servoxstate = 2;
  }
  switch (servoxstate) {
    case 0:
      prog++;
      waittime = millis();

      break;
    case 1: //fw
      if (currtime - servotime > servodelay && x >= servoxcurrpos) {
        servotime = millis();
        servoxpos = servoxcurrpos + servomove;
        servox.write(servoxpos);

      }
      if (x < servoxpos) {
        servoxstate = 0;

      }
      break;
    case 2: //rv
      if (currtime - servotime > servodelay && x <= servoxcurrpos) {
        servotime = millis();
        servoxpos = servoxcurrpos - servomove;
        servox.write(servoxpos);

      }
      if (x > servoxpos) {
        servoxstate = 0;

      }
      break;
    default:
      servoxstate = 0;
      break;
  }
}

void f_servoy(int y) {
  servoycurrpos = servoy.read();
  if (y == servoycurrpos) {
    servoystate = 0;
  }
  else if (y > servoycurrpos) {
    servoystate = 1;
  }
  else if (y < servoycurrpos) {
    servoystate = 2;
  }
  switch (servoystate) {
    case 0:
      prog++;
      waittime = millis();

      break;
    case 1: //fw
      if (currtime - servotime > servodelay && y >= servoycurrpos) {
        servotime = millis();
        servoypos = servoycurrpos + servomove;
        servoy.write(servoypos);

      }
      if (y < servoypos) {
        servoystate = 0;

      }
      break;
    case 2: //rv
      if (currtime - servotime > servodelay && y <= servoycurrpos) {
        servotime = millis();
        servoypos = servoycurrpos - servomove;
        servoy.write(servoypos);

      }
      if (y > servoypos) {
        servoystate = 0;

      }
      break;
    default:
      servoystate = 0;
      break;
  }
}

But this is strange, I did not change anything, and now, at least, the “manual mode” is working.

I read in a post, do not remember which one now that the pins must be configured as A# or # depending on the desired function of the pin. Maybe the error is here.

Thanks again.
Filipe Almeida

Look at this snippet from your code:

currtime = millis();
  buttonread = digitalRead(buttonpin);
  if (buttonread != buttonprev) {
    buttontime = millis();
  }
  if (currtime - buttontime > buttondelay) {

Since "currtime" and "buttontime" are both assigned almost simultaneously, the subtraction will never exceed "buttondelay", so you need to change your button handling/debouncing code.

EDIT: I made a mistake, the button code is OK! :-/

i suspect if you just do a digitalRead() it doesn't matter, but yes for analogRead() it should be defined as A#, and for output it should be #

buttonread = digitalRead(buttonpin);

Are you sure the core sets the ADC resolution correctly ?

Deva_Rishi:
Are you sure the core sets the ADC resolution correctly ?

Do you mean if the boatloader is uploaded correctly with 16 MHz?
I get a valid confirmation from the Arduino IDE. Whether it works or not, I do not know.

But I tested the "Sweeper" example for servos and the "blink without delay (using millis)" and both worked. At least to my inexperienced eye.

By the way, I am using Sparkfun Tiny AVR Programmer for uploading sketch.

Filipe Almeida

But I tested the "Sweeper" example for servos

Then the Servo is responding the way it is supposed to, in that case, is it possible that the core you are using has the ADC set to 8-bit ? In other words  servoypos = map(joystickyread, 0, 1023, 0, 180); try it with   servoypos = map(joystickyread, 0, 255, 0, 180);

So, changed a couple of things.

First, instead of Spence Konde (http://drazzy.com/package_drazzy.com_index.json) board configuration, I used Damellis (https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json) board configuration.

I configured it at 16 MHz. With this core, I have to use ATtiny SoftwareServo.h, from here (Projects from Tech: ATtiny85 Servo: SoftwareServo Library)

I updated the code but it basically does the same thing.

#include <SoftwareServo.h>

SoftwareServo servox;
int servoxpin = 0;
int servoxpos;
int servoxcurrpos;
int servoxfw = true;
int servoxhigh = 170;
int servoxlow = 10;
int servoxmid = 90;
SoftwareServo servoy;
int servoypin = 1;
int servoypos;
int servoycurrpos;
int servoyfw = true;
int servoyhigh = 170;
int servoylow = 10;
int servoymid = 90;

int joystickxpin = A2;
int joystickypin = A3;
int joystickxread;
int joystickyread;

int state;
int prog;

int buttonpin = 2;
int buttonread;
int buttonstate = LOW;
int buttonprev;

int ledpin;
int ledstate = LOW;

unsigned long ledtime;

unsigned long currtime;
unsigned long servoxtime;
unsigned long servoytime;
unsigned long servodelay = 20;

unsigned long buttontime;
unsigned long buttondelay = 50;

unsigned long waittime;

void setup () {
  servox.attach(servoxpin);
  servoy.attach(servoypin);
  pinMode(joystickxpin, INPUT);
  pinMode(joystickypin, INPUT);
  joystickxread = analogRead(joystickxpin);
  joystickyread = analogRead(joystickypin);
  servoxpos = map(joystickxread, 0, 255, 0, 180);
  servoypos = map(joystickyread, 0, 255, 0, 180);
  servox.write(servoxpos);
  servoy.write(servoypos);
  pinMode(buttonpin, INPUT_PULLUP);
  buttonprev = HIGH;
  pinMode(ledpin, OUTPUT);
}

void loop () {
  currtime = millis();

  buttonread = digitalRead(buttonpin);
  if (buttonread != buttonprev) {
    buttontime = millis();
  }
  if (currtime - buttontime > buttondelay) {
    if (buttonread != buttonstate) {
      buttonstate = buttonread;
      if (buttonstate == LOW) {
        state ++;
        prog = 0;
        if (state > 2) {
          state = 0;
        }
      }
    }
  }
  buttonprev = buttonread;

  switch (state) {
    case 0:
      break;
    case 1:
      if (currtime - servoxtime >= servodelay) {
        servoxtime = millis();
        joystickxread = analogRead(joystickxpin);
        servoxpos = map(joystickxread, 0, 1023, 0, 180);
        servox.write(servoxpos);
      }
      if (currtime - servoytime >= servodelay) {
        servoytime = millis();
        joystickyread = analogRead(joystickypin);
        servoypos = map(joystickyread, 0, 1023, 0, 180);
        servoy.write(servoypos);
      }
      break;
    case 2:
      digitalWrite(ledpin, LOW);
      switch (prog) {
        case 0:
          f_wait (1000);
          break;
        case 1:
          f_servox(servoxhigh);
          break;
        case 2:
          f_wait (1000);
          break;
        case 3:
          f_servoy(servoyhigh);
          break;
        case 4:
          f_wait (1000);
          break;
        case 5:
          f_servox(servoxlow);
          break;
        case 6:
          f_wait (1000);
          break;
        case 7:
          f_servoy(servoylow);
          break;
        case 8:
          f_wait (1000);
          break;
        case 9:
          f_servox(servoxmid);
          break;
        case 10:
          f_wait (1000);
          break;
        case 11:
          f_servoy(servoymid);
          break;
        case 12:
          f_wait (1000);
          break;
        case 13:
          f_servox(servoxlow);
          break;
        case 14:
          f_wait (1000);
          break;
        case 15:
          f_servoy(servoylow);
          break;
        default:
          prog = 0;
          break;
      }
      break;
    default:
      state = 0;
      break;
  }
}

void f_wait(unsigned long w) {
  if (currtime - waittime >= w) {
    prog++;
  }
}

void f_servox(int x) {
  if (currtime - servoxtime >= servodelay) {
    servoxcurrpos = servox.read();
    servoxtime = millis();
    if (servoxcurrpos == x) {
      prog++;
      waittime = millis();
    }
    if (servoxcurrpos < x) {
      servoxfw = true;
    }
    if (servoxcurrpos > x) {
      servoxfw = false;
    }
    if (servoxfw == true) {
      servox.write (++servoxpos);
      SoftwareServo::refresh();
    }
    if (servoxfw == false) {
      servox.write (--servoxpos);
      SoftwareServo::refresh();
    }
  }
}

void f_servoy(int y) {
  if (currtime - servoytime >= servodelay) {
    servoycurrpos = servoy.read();
    servoytime = millis();
    if (servoycurrpos == y) {
      prog++;
      waittime = millis();
    }
    if (servoycurrpos < y) {
      servoyfw = true;
    }
    if (servoycurrpos > y) {
      servoyfw = false;
    }
    if (servoyfw == true) {
      servoy.write (++servoypos);
      SoftwareServo::refresh();
    }
    if (servoyfw == false) {
      servoy.write (--servoypos);
      SoftwareServo::refresh();
    }
  }
}

Now the timings and angles of the servos in “automatic mode” (case 2 of state) work very well but the “manual mode” (case 1 of state), i.e, controlling the servos by reading the potentiometer of a joystick, does not work. The servos jitter for a second or two and then nothing happens.

I think now the issue is in the code.

Can you take a look please?

Many thanks
Filipe Almeida

SoftwareServo::refresh();I'm not familiar with the library but do you need this line in case 1 of state as well ?

Deva_Rishi:
SoftwareServo::refresh();I'm not familiar with the library but do you need this line in case 1 of state as well ?

Yep, forgot that.
And the issue is solved.

Thanks very much!!

Filipe Almeida