Adafruit Alphanum display with attiny85

I'm trying to use the following code on an attiny85.
It works without any problems with an arduino uno but not with the attiny85.
I'm pretty sure the problem is the Wire.h library because the attiny can't use i2c as simple as the uno can. I tried using the tinyWireM.h instead which should help using i2c and even the
attiny core to compile the code and should have a build in transformation of wire.h for attiny. Both options didn't work, probably because the
Adafruit led backpack library, which I need to control the alphanum-display, isn't working with tinyWireM.h or something.

This is my code:

// include libraries
#include <Wire.h>
#include "Adafruit_LEDBackpack.h"
#include <ClickEncoder.h>
#include <TimerOne.h>
#include <avr/sleep.h>
#include <avr/power.h>

// Set up the LED display
Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();
char displayBuffer[4];
uint8_t dimensionLetter='C';

// Set up the click encoder
ClickEncoder *encoder;
int16_t last, value;
#define encoderPinA          A1
#define encoderPinB          A0
#define encoderButtonPin     A2

// Steps per notch can be 1, 4, or 8. If your encoder is counting
// to fast or too slow, change this!
#define stepsPerNotch        8

// Comment this line to make the encoder increment in the opposite direction
#define reverseEncoderWheel


// FX Board output delay (ms)
const int msDelay = 500;

// Set up the LED
#define LEDpin           9

// Set up interrupt pin for wake-up
#define NAV0_PIN A2

//Let us know if woke up from sleep
volatile bool justWokeUp;


void timerIsr() {
  encoder->service();
}

void setup() {
  enablePinInterupt(NAV0_PIN);
  
  //Set up pin modes
  pinMode(LEDpin, OUTPUT);
  
  digitalWrite(LEDpin, HIGH);
  
  encoderSetup();
  alpha4.begin(0x70);  // pass in the address for the LED display
  
  justWokeUp = false;
}

void loop() {
  // Woke up
  if (justWokeUp) {
    digitalWrite(LEDpin, HIGH);
    justWokeUp = false;
  }  

  
  ClickEncoder::Button b = encoder->getButton();
  switch (b) {
    case ClickEncoder::Held:
      // Holding the button will put your trinket to sleep.
      // The trinket will wake on the next button press
      alpha4.clear();
      alpha4.writeDigitAscii(0, 'R');
      alpha4.writeDigitAscii(1, 'I');
      alpha4.writeDigitAscii(2, 'C');
      alpha4.writeDigitAscii(3, 'K');
      digitalWrite(LEDpin, LOW);
      alpha4.writeDisplay();
      delay(5000);
      alpha4.clear();
      alpha4.writeDisplay();
      delay(5000);
      justWokeUp = true;
      goToSleep();
    break;
    case ClickEncoder::Clicked:
      // When the encoder wheel is single clicked
   
    break;
    case ClickEncoder::DoubleClicked:
      //If you double click the button, it sets the dimension to C137
      dimensionLetter = 'C';
      value = 137;
    break;
    case ClickEncoder::Open:
      // The dimension will increment from 0-999, then roll over to the next
      // letter. (A999 -> B000)
      updateDimension();
    break;
  }
}


void encoderSetup(){
    // set up encoder
    encoder = new ClickEncoder(encoderPinA, encoderPinB, encoderButtonPin, stepsPerNotch);
    encoder->setAccelerationEnabled(true);
  
    Timer1.initialize(1000);
    Timer1.attachInterrupt(timerIsr); 
    last = -1;
    value = 137;
}


void updateDimension(){
  #ifdef reverseEncoderWheel
  value -= encoder->getValue();
  #endif
  
  #ifndef reverseEncoderWheel
  value += encoder->getValue();
  #endif
  
  if (value != last) {
    if (value > 999){
      value = 0;
      if (dimensionLetter == 'Z') {
        dimensionLetter = 'A';
      } else {
        dimensionLetter ++;        
      }
    } else if ( value < 0 ) {
      value = 999;
      if (dimensionLetter == 'A') {
        dimensionLetter = 'Z';
      } else {
        dimensionLetter --;
      }
    }
    last = value;
  }
  
  sprintf(displayBuffer, "%03i", value);
  alpha4.clear();
  alpha4.writeDigitAscii(0, dimensionLetter);
  alpha4.writeDigitAscii(1, displayBuffer[0]);
  alpha4.writeDigitAscii(2, displayBuffer[1]);
  alpha4.writeDigitAscii(3, displayBuffer[2]);
  alpha4.writeDisplay();
}





/*
============== Sleep/Wake Methods ==================
====================================================
*/

// Most of this code comes from seanahrens on the adafruit forums
// http://forums.adafruit.com/viewtopic.php?f=25&t=59392#p329418


void enablePinInterupt(byte pin)
{
    *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
    PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
    PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}

void goToSleep()   
{
// The ATmega328 has five different sleep states.
// See the ATmega 328 datasheet for more information.
// SLEEP_MODE_IDLE -the least power savings 
// SLEEP_MODE_ADC
// SLEEP_MODE_PWR_SAVE
// SLEEP_MODE_STANDBY
// SLEEP_MODE_PWR_DOWN -the most power savings
// I am using the deepest sleep mode from which a
// watchdog timer interrupt can wake the ATMega328

 


set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode.
sleep_enable(); // Enable sleep mode.
sleep_mode(); // Enter sleep mode.
// After waking the code continues
// to execute from this point.

sleep_disable(); // Disable sleep mode after waking.                   
}

ISR (PCINT0_vect) // handle pin change interrupt for D8 to D13 here
{    
  // if I wired up D8-D13 then I'd need some code here
} 

ISR (PCINT1_vect) // handle pin change interrupt for A0 to A5 here // NAV0
{
    /* This will bring us back from sleep. */
  
  /* We detach the interrupt to stop it from 
   * continuously firing while the interrupt pin
   * is low.
   */
  
  detachInterrupt(0);

}

ISR (PCINT2_vect) // handle pin change interrupt for D0 to D7 here // NAV1, NAV2
{
  // Check it was NAV1 or NAV2 and nothing else
}

It reads a rotary encoder and writes something on the display accordingly and also has a sleep mode.

The error message I receive with the attiny ist the following:

C:\Users\Anton\Documents\Arduino\libraries\Adafruit_LED_Backpack-master\Adafruit_LEDBackpack.cpp:695:6: error: prototype for 'void Adafruit_7segment::writeDigitNum(uint8_t, uint8_t, boolean)' does not match any in class 'Adafruit_7segment'
 void Adafruit_7segment::writeDigitNum(uint8_t d, uint8_t num, boolean dot) {
      ^~~~~~~~~~~~~~~~~
In file included from C:\Users\Anton\Documents\Arduino\libraries\Adafruit_LED_Backpack-master\Adafruit_LEDBackpack.cpp:38:0:
C:\Users\Anton\Documents\Arduino\libraries\Adafruit_LED_Backpack-master\Adafruit_LEDBackpack.h:376:8: error: candidate is: void Adafruit_7segment::writeDigitNum(uint8_t, uint8_t, bool)
   void writeDigitNum(uint8_t x, uint8_t num, bool dot = false);
        ^~~~~~~~~~~~~
C:\Users\Anton\Documents\Arduino\libraries\Adafruit_LED_Backpack-master\Adafruit_LEDBackpack.cpp:726:6: error: prototype for 'void Adafruit_7segment::writeDigitAscii(uint8_t, uint8_t, boolean)' does not match any in class 'Adafruit_7segment'
 void Adafruit_7segment::writeDigitAscii(uint8_t d, uint8_t c, boolean dot) {
      ^~~~~~~~~~~~~~~~~
In file included from C:\Users\Anton\Documents\Arduino\libraries\Adafruit_LED_Backpack-master\Adafruit_LEDBackpack.cpp:38:0:
C:\Users\Anton\Documents\Arduino\libraries\Adafruit_LED_Backpack-master\Adafruit_LEDBackpack.h:384:8: error: candidate is: void Adafruit_7segment::writeDigitAscii(uint8_t, uint8_t, bool)
   void writeDigitAscii(uint8_t x, uint8_t c, bool dot = false);
        ^~~~~~~~~~~~~~~
exit status 1
Fehler beim Kompilieren für das Board ATtiny25/45/85 (No bootloader).

I'm happy about any help.
Thank you!

Have you burned a bootloader onto the ATTiny?
Are you using a programmer?

No I'm using the attiny without bootloader and use the uno as isp to program it.

Whether or not the ATTiny has a bootloader is irrelevant - this is a compile error, not an upload error.

Might be an error in the library, as pointed out in reply #3 from this post https://forum.arduino.cc/t/ardruio-using-adafruit-7-segment-display/920812/2 the header file uses bool in the function prototype, but the cpp file uses boolean for the function.

Ouch! That hurt! Good catch David. I was obviously too superficial and now my ego hurts.

It's weird that bool vs. boolean causes an error though. The Arduino reference says that they are identical. Like many, I previously used boolean and now use bool and have never encountered an error though I often copy/paste old code into new projects and thus have the situation that the original function expects boolean but the new program passes bool.

@Anton3006

I believe you can do this fix in the files on your own, but if you want to try, I have a HT16K33 library which implements the print.h to make printing of numbers and characters easier.
https://werner.rothschopf.net/201909_arduino_ht16k33.htm
Just did a test and at at least it compiles for the ATTiny45/85 Optiboot.

by the way, this forum has also a very active German speaking section:
https://forum.arduino.cc/c/international/deutsch/47

The ATTinyCore is not maintained by the group that does the Arduino core, so the Arduino reference is not always applicable. Not really sure why there would be a difference between bool and boolean, but it does seem odd that the Adafruit library is not consistent in its usage.

also these guys (and ladies) are just humans and no robots, bugs can happen.

True. Its possibly one person wrote the header file and then different people wrote the actual functions.

Adafruit has updated the library to be consistent in the usage of bool. Check to see that you have version 1.3.2, they were very responsive, I reported the issue on github yesterday after commenting on this post.

Don't you need AdafruitGFX library installed?

Yes, the Adafruit_LED_Backpack library requires Adafruit_GFX and Adafruit_I2CDevice, but if the OP were missing those the compilation for an UNO would also fail, and it's unlikely the compiler would have gotten far enough to flag the errors shown.

Yes, the thought crossed my mind. Too bad the sketch is not a minimal one, I have the Adafruit libraries installed but not all the other ones, so I can't test it.

Strange thing, the methods that are producing errors, don't even belong to the Alphanum class.

@david_2018
I was not the original poster. Thanks for your assistance to him though. And, yes, I am aware of the german site, actually I sometimes go there as well. Actually, I was born in the USA but have lived in Germany for 40 years and am fluent both languages. No, I didn't take offense, just thought it may be worth mentioning.:slight_smile:

Wow, this really did the trick.
Thanky you!

Now I have a lot of errors because of the interrupts which are working perfectly on the uno too, but one step closer.

Maybe someone knows a solution for this:

//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1   D-SCL 
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1 
//            GND  4|    |5  PB0 (D 0) pwm0   D-SDA
//                  +----+


#include <Wire.h>                                                    // HT16K33 uses I2C, include the Wire Library
#include "NoiascaHt16k33.h"                                          // include the noiasca HT16K33 library
#include <ClickEncoder.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <TimerOne.h>

Noiasca_ht16k33_hw_14_4 display = Noiasca_ht16k33_hw_14_4();         // object for 14 segments - 4 digits

char displayBuffer[4];
uint8_t dimensionLetter='C';

// Set up the click encoder
ClickEncoder *encoder;
int16_t last, value;
#define encoderPinA          A1
#define encoderPinB          A2
#define encoderButtonPin     A0

// Steps per notch can be 1, 4, or 8. If your encoder is counting
// to fast or too slow, change this!
#define stepsPerNotch        8

// Comment this line to make the encoder increment in the opposite direction
#define reverseEncoderWheel

// Set up the Green LEDs
#define LEDpin           3

// Set up what we need to sleep/wake the Trinket
// Define the pins you'll use for interrupts - CHANGE THESE to match the input pins
// you are using in your project
#define NAV0_PIN A2

//Let us know if our Trinket woke up from sleep
volatile bool justWokeUp;


void timerIsr() {
  encoder->service();
}

void setup() 
{
  enablePinInterupt(NAV0_PIN);
  
  //Set up pin modes
  pinMode(LEDpin, OUTPUT);
  
  digitalWrite(LEDpin, HIGH);
  
  encoderSetup();

  Wire.begin();                                   // start the I2C interface
  Wire.setClock(400000);                         // optional: activate I2C fast mode. If it is to fast for other I2C devices. deactivate this row.
 
  display.begin(0x70);                 // I2C address of first display, optional total number of devices

  display.clear();
  display.setBrightness(15);            // set brightness from 0 to 15

  justWokeUp = false;
  
}


void loop() 
{
  if (justWokeUp) {
    digitalWrite(LEDpin, HIGH);
    justWokeUp = false;
  }  
  
  ClickEncoder::Button b = encoder->getButton();
  switch (b) {
    case ClickEncoder::Held:
      // Holding the button will put your trinket to sleep.
      // The trinket will wake on the next button press
      display.clear();
      display.print("RICK");
      digitalWrite(LEDpin, LOW);
      delay(3000);
      display.clear();
      justWokeUp = true;
      goToSleep();
    break;
    case ClickEncoder::Clicked:
      // When the encoder wheel is single clicked
   
    break;
    case ClickEncoder::DoubleClicked:
      //If you double click the button, it sets the dimension to C137
      dimensionLetter = 'C';
      value = 137;
    break;
    case ClickEncoder::Open:
      // The dimension will increment from 0-999, then roll over to the next
      // letter. (A999 -> B000)
      updateDimension();
    break;
  }
}

void encoderSetup(){
    // set up encoder
    encoder = new ClickEncoder(encoderPinA, encoderPinB, encoderButtonPin, stepsPerNotch);
    encoder->setAccelerationEnabled(true);

    Timer1.initialize(1000);
    Timer1.attachInterrupt(timerIsr); 
    last = -1;
    value = 137;
}

void updateDimension(){
  #ifdef reverseEncoderWheel
  value -= encoder->getValue();
  #endif
  
  #ifndef reverseEncoderWheel
  value += encoder->getValue();
  #endif
  
  if (value != last) {
    if (value > 999){
      value = 0;
      if (dimensionLetter == 'Z') {
        dimensionLetter = 'A';
      } else {
        dimensionLetter ++;        
      }
    } else if ( value < 0 ) {
      value = 999;
      if (dimensionLetter == 'A') {
        dimensionLetter = 'Z';
      } else {
        dimensionLetter --;
      }
    }
    last = value;
  }
  
  sprintf(displayBuffer, "%03i", value);
  display.clear();
  display.write(dimensionLetter);
  display.print(displayBuffer[0]);
  display.print(displayBuffer[1]);
  display.print(displayBuffer[2]);
}



/*
============== Sleep/Wake Methods ==================
====================================================
*/

// Most of this code comes from seanahrens on the adafruit forums
// http://forums.adafruit.com/viewtopic.php?f=25&t=59392#p329418


void enablePinInterupt(byte pin)
{
  *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
    PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt   //PCIFR
    PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group    //PCICR
}

void goToSleep()   
{
  // The ATmega328 has five different sleep states.
  // See the ATmega 328 datasheet for more information.
  // SLEEP_MODE_IDLE -the least power savings 
  // SLEEP_MODE_ADC
  // SLEEP_MODE_PWR_SAVE
  // SLEEP_MODE_STANDBY
  // SLEEP_MODE_PWR_DOWN -the most power savings
  // I am using the deepest sleep mode from which a
  // watchdog timer interrupt can wake the ATMega328
    
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode.
  sleep_enable(); // Enable sleep mode.
  sleep_mode(); // Enter sleep mode.
  // After waking the code continues
  // to execute from this point.
  
  sleep_disable(); // Disable sleep mode after waking.                   
}

ISR (PCINT0_vect) // handle pin change interrupt for D8 to D13 here
{    
  // if I wired up D8-D13 then I'd need some code here
} 

ISR (PCINT1_vect) // handle pin change interrupt for A0 to A5 here // NAV0
{
    /* This will bring us back from sleep. */
  
  /* We detach the interrupt to stop it from 
   * continuously firing while the interrupt pin
   * is low.
   */
  
  detachInterrupt(0);
}

ISR (PCINT2_vect) // handle pin change interrupt for D0 to D7 here // NAV1, NAV2
{
  // Check it was NAV1 or NAV2 and nothing else
}

And this is the error:

C:\Users\Anton\Desktop\Portal_Gun_AT_new\Portal_Gun_AT_new.ino: In function 'void enablePinInterupt(byte)':
Portal_Gun_AT_new:174:5: error: 'PCIFR' was not declared in this scope
     PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt   //PCIFR
     ^~~~~
C:\Users\Anton\Desktop\Portal_Gun_AT_new\Portal_Gun_AT_new.ino:174:5: note: suggested alternative: 'PCIF'
     PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt   //PCIFR
     ^~~~~
     PCIF
Portal_Gun_AT_new:175:5: error: 'PCICR' was not declared in this scope
     PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group    //PCICR
     ^~~~~
C:\Users\Anton\Desktop\Portal_Gun_AT_new\Portal_Gun_AT_new.ino:175:5: note: suggested alternative: 'MCUCR'
     PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group    //PCICR
     ^~~~~
     MCUCR
exit status 1
'PCIFR' was not declared in this scope

Changing "PCIFR" to "PCIF" and "PCICR" to "MCUCR" causes a lot of other erros.

Not related to your bugs, but I'm very confident that my lib doesn't have any dependency to

#include "Adafruit_GFX.h"

and this

 display.write("RICK");

should be a

 display.print("RICK");
1 Like