*SOLVED* Code re-write Arduino <=> ATtiny: what's wrong with my code?

Hi all
This is my first post and my first Arduino project. So please forgive me if I posted in the wrong place...

I have used my Nano as a development board to test my circuit and my code. Only yesterday have I decided to put it in ISP mode to flash my ATtiny85. I've since learnt that I can't put it back in "Arduino" mode and that I'll have to order a second ATmega chip but that's not my main issue...

I have developed a fan controller that uses a potentiometer (to set the minimum speed), an NTC (modeled here using two legs of a pot. I use it to gradually increase the speed), a MOSFET (to drive the fan) and a LED (as some sort of overheating indicator, it lights up when the NTC reaches a certain temperature).

Here's my schematic:

And here is my code (I'm using the Arduino language), scroll down to download it:

// variables declaration
int SensorPin = 2;  // pin 7
int PWMPin = 0;   // pin 5
int PotPin = 4; // pin 3
int LEDPin = 1; // pin 6
int SensorVal; // "temperature" from NTC
int PWMVal;   // fan PWM output "speed"
int PotVal;   // real potentiometer value, to define the cold/normal operation fan speed.
int PotValmapped;  // lowest position shall correspond to the absolute minimum speed (see below) and not OFF, new potentiometer value
int MinSpeed=64; // absolute minimum speed, 0=0% and 255=100%, currently set to 25%, corresponds to lowest pot value

// Calibration variables
int NTCcold=512; // corresponds to ambient temperature, 10K for a 10k NTC @25°C
int NTChot=110; // corresponds to highest temperature before LED lights up, about 1.3K for a 10k NTC @80°C
int PotMin=0; // corresponds to lowest position on potentiometer
int PotMax=1023; // corresponds to highest position on potentiometer


// setup code, to run once
void setup() {
  //set PWM frequency of PB0 (Pin 5, ATTiny85, fan output) to 31,250 Hz.
  TCCR0B = TCCR0B & 0b11111000 | 0b001;

  // define pin modes ATTiny85
  pinMode(SensorPin, INPUT);
  pinMode(PotPin, INPUT);
  pinMode(PWMPin, OUTPUT);
  pinMode(LEDPin, OUTPUT);

  // Set fan to full speed for one second at startup (prevents stall)
  analogWrite(PWMPin, 255);
  
  delay(1500);
}


// main code, to run repeatedly
void loop() {

  //read NTC sensor value
  SensorVal = analogRead(SensorPin);
   if( (SensorVal > NTCcold) && (SensorVal < 1000) ){ // in case ambient temperature is lower than 25°C
    SensorVal = NTCcold;} 
   if( (SensorVal > NTCcold) && (SensorVal > 1000) ){ // NTC failure (open circuit)
    SensorVal = NTChot-1;} 
  
  //read pot value
  PotVal = analogRead(PotPin);

  // map and assign pot/pwm values. 0 to 255 corresponds to 0 to 100%
  PotValmapped = map(PotVal, PotMin, PotMax, MinSpeed, 254); // change potentiometer characteristic, lowest position corresponds to absolute minimum speed
  PWMVal = map(SensorVal, NTCcold, NTChot, PotValmapped, 255); // linear fan curve from ambient to LED lighting up. PotValmapped is the minimum fan speed set by the potentiometer

  // Overheating indicator
  if (SensorVal<NTChot){ 
    PWMVal=255; // full speed
    digitalWrite(LEDPin, HIGH); // LED ON
  }
  else {
    digitalWrite(LEDPin, LOW); // LED OFF
  }

  //write the PWM value to the pwm output pin
  analogWrite(PWMPin, PWMVal);

  delay(1000); // updates every second

}

The only differences with the Arduino code are

  • the pin names
    int SensorPin = A0;
    int PWMPin = 3;
    int PotPin = A1;
    int LEDPin = 8;
  • the set frequency line (commented out for the Arduino, removing it and refreshing the ATtiny doesn't change anything, it just buzzes louder of course)
  • the serial monitor (not shown here but irrelevant)

My problem is that while this works perfectly as intended on the Arduino, the potentiometer and NTC pins appear to be switched around on the ATTiny version (switching them back, which does not make sense, makes the NTC part work, but the potentiometer input is ignored).

I can see two possible issues, please let me know what you think:
1/ The ATtiny is not 100% compatible with the Arduino language.
2/ The calibration values (ie analogread 0-1023 and analogwrite 0-255) are not compatible with the ATtiny

What should I do to make this work?
Thumbs up if you read the whole post

Thanks!

fan.ino (2.66 KB)

The ATtiny is not an official Arduino board so the 'core' you are using was produced by a third party. What core are you using?

Good point. I'm using the attiny core by David A. Mellis v1.0.2

I just solved the problem... it was as simple as a bad pinout but boy was it a pain to diagnose.


(right click and open in new tab to make it bigger)

Pin 7 of the ATtiny is PB2, but PB2 corresponds to A1.
Pin 3 of the ATtiny is PB4, but PB4 corresponds to A2.

With this declaration, it works:
int SensorPin = A1; // pin 7
int PWMPin = PB0; // pin 5
int PotPin = A2; // pin 3
int LEDPin = PB1; // pin 6

So basically PB2 was A2 and PB4 was A4 (which does not exist)? Why did it allow me to compile the code??!
It was all thanks to this thread: *SOLVED* Simple code won't work in ATTINY85, please help. - Microcontrollers - Arduino Forum

Anyway, it works now, I just have to make a few adjustments...

Karma++ for great posting etiquette, and a useful contribution!

int SensorPin = 2;  // pin 7
int PWMPin = 0;   // pin 5
int PotPin = 4; // pin 3
int LEDPin = 1; // pin 6

bge:
So basically PB2 was A2 and PB4 was A4 (which does not exist)? Why did it allow me to compile the code??!

The compiler has no way to know that '4' is an invalid pin number for analogRead(). If you had used the name 'A4' you would have gotten a "not defined in this scope" error. That's one reason why I recommend ALWAYS using the name (A0, A1, A2...) for the analog input pins.

@johnwasser: Thanks for the explanation. Makes sense. But I remember trying to use the PB denomination for the analog inputs before I posted here and got the same results... which should not have happened? It's a real shame that this isn't more documented.

I guess the best practice for ATtiny Arduino programming is to use like you mentioned PB for the outputs and A for the analog inputs

bge:
I guess the best practice for ATtiny Arduino programming is to use like you mentioned PB for the outputs and A for the analog inputs

The only reason the PB0 through PB7 names work for Arduino pin numbers is because they are defined as the numbers 0 through 7 and happen to match the pin numbers on the ATtiny85. On other processors (ATmega328p, ATmega2560) the port bits and Arduino pin numbers don't generally match. For example Pin 10 on the UNO is PB2 but 'PB2' is 2, not 10.

ATtiny85

Arduino Name Arduino Number Port Pin Physical Pin
0 0 PB0 5
1 1 PB1 6
2 2 PB2 7
3 3 PB3 2
4 4 PB4 3
5 5 PB5 1
A0 6 PB5 1
A1 7 PB2 7
A2 8 PB4 3
A3 9 PB3 2

In the case of the ATtiny core you can, for example, use 'A0' and '5' interchangeably for digitalRead(), digitalWrite(), and analogRead(). The one place they screwed up is 'A2' and '4'. You can use them interchangeably for digitalRead(), digitalWrite(), and analogRead() but you can't use 'A2' for analogWrite()! The name 'A2' is defined as 8 and that entry in the timer lookup table is marked NOT_ON_TIMER:

const uint8_t PROGMEM digital_pin_to_timer_PGM[] = {
	TIMER0A, /* OC0A */
	TIMER0B,
	NOT_ON_TIMER,
	NOT_ON_TIMER,
	TIMER1B,
	NOT_ON_TIMER,
	NOT_ON_TIMER,
	NOT_ON_TIMER,
	NOT_ON_TIMER,
	NOT_ON_TIMER,
};

If they had put "TIMER1B" in entry 8 like in entry 4 the two designations would have been fully interchangeable. (See: attiny/variants/tiny8/pins_arduino.h at master · damellis/attiny · GitHub)

NOTE: On the UNO you can use the numbers 0 through 5 for analogRead() to reference A0 through A5. I believe this will be similar for 0 through 3 on the ATtiny... but '2' will get you A2, not A1 that shares Arduino Pin 2. And 4 won't get you anything. Certainly not A2 which shares Pin 4.