Noob with Arduino and ATtiny85

Hi ALL,

Total Arduino/ATtiny85 noob here.

I am here with hopes that someone may elude to what my problem is likely to be.

I have written the attached program so as to be able to gain the benefit of floating point arithmetic in conjunction with a PICAXE.

As can be seen within my CODE, I am using I2C communications. Basically I am sending a 16bit word in two 8bit bytes from the PICAXE, doing the math with an ATtiny and returning the result back to the PICAXE also as a 16bit word. At the PICAXE end I am simply applying a decimal point for the resultant resistance measurement. ie 12345 = 123.45 Ohms.

This program works absolutely flawlessly between Arduino UNO (ATmega328P-PU) and PICAXE using the standard WIRE include and is 100% stable in every way, however it does not function properly using the ATtiny with TinyWire and seems rather flaky at best.

I am quite experienced with electronics in general and had no problem following along to upload ArduinoISP to a UNO, wiring and uploading the Bootloader to the ATtiny85 accordingly and did an initial test with the Blink program successfully.

I have the attached program code uploaded to the ATtiny and it initially functions fine, however after what appears to be a random amount of time the communications fail and it seems the ATtiny holds the SCL line LOW. This time span may be from a few seconds to several minutes, but regardless is unreliable.

Using the exact same circuit and swapping between the UNO and the ATtiny, the UNO is 100% perfect and the ATtiny fails. The 5V supplying the ATtiny is stable and there is a 100nF cap right at the I.C. at pin 8 (VCC).

Even if I give the ATtiny and UNO different I2C addresses and independently communicate to each having both in the circuit at the same time, I get the same result whereby the ATtiny ends up holding the SCL line LOW at some random point which obviously buggers up operations.

Because of this I am thinking the problem must be my vast inexperience with Arduino/ATtiny programming and there is something inherently wrong with my coding efforts. But then... the code works perfectly with a UNO ?!?

One thing is for sure, I am getting a bald patch from pulling my hair out :wink:

ATtiny CODE:

#include <TinyWire.h>                                 // Include Arduino TinyWire library for I2C

#define SLAVE_ADDR 0x13                               //Define Slave I2C Address. PICAXE equiv = times 2 (Shift value LEFT by 1 bit)
                                                      //Arduino 0x12 = PICAXE $24

word adc_value;                                       //Received ADC Value from MASTER
float adc_Ref_Volts = 2.048;                          //ADC Reference Voltage
int VRef_Volts = 5;                                   //Voltage at top of 1K 0.01%
word adc_Max = 65535;                                 //Maximum possible ADC Value
float adc_Volts;                                      //Calculated Volts referenced from adc_value and adc_Ref_Volts
int R1 = 1000;                                        //Top resistor in voltage divider (1K 0.01%)
float R2;                                             //Final calculated unknown resistor value in Ohms xxx.xx
word R2word;                                          //R2 as WORD



void setup() {
  pinMode(4, OUTPUT);                                 //Initialise PB4 as OUTPUT for LED visual confirmation

  TinyWire.begin(SLAVE_ADDR);                         //Initialize I2C communications as Slave

  TinyWire.onReceive(receiveEvent);                   //Function to run when data received from master

  TinyWire.onRequest(requestEvent);                   //Function to run when data requested from master
}


void receiveEvent() {
  while (0 < TinyWire.available()) {
    adc_value = TinyWire.read();                      //Receive high byte
    adc_value = adc_value << 8;                       //Shift high byte to be high 8 bits of 16bit WORD
    adc_value += TinyWire.read();                     //Receive low byte as lower 8 bits
  }

  digitalWrite(4, HIGH);                              //Activate LED confirming DATA has been received

  adc_Volts = (adc_value * adc_Ref_Volts) / adc_Max;  //Calculate ADC Volts
  R2 = (adc_Volts * R1) / (VRef_Volts - adc_Volts);   //Calculate Unknown Resistor Value

  R2 = R2 * 100;                                      //Shift decimal 2 places to the right
  R2word = R2;                                        //Convert R2 Float to a 16bit WORD
}


void requestEvent() {
  TinyWire.write(R2word & 0xFF);                      //Send 16bit WORD result in two 8bit bytes via I2C to Master
  TinyWire.write(R2word >> 8);

  digitalWrite(4, LOW);                               //Deactivate LED confirming DATA has been successfully returned
}





void loop() {
}

Arduino UNO CODE:

#include <Wire.h>                                     // Include Arduino Wire library for I2C

#define SLAVE_ADDR 0x13                               //Define Slave I2C Address. PICAXE equiv = times 2 (Shift value LEFT by 1 bit)
                                                      //Arduino 0x12 = PICAXE $24

word adc_value;                                       //Received ADC Value from MASTER
float adc_Ref_Volts = 2.048;                          //ADC Reference Voltage
int VRef_Volts = 5;                                   //Voltage at top of 1K 0.01%
word adc_Max = 65535;                                 //Maximum possible ADC Value
float adc_Volts;                                      //Calculated Volts referenced from adc_value and adc_Ref_Volts
int R1 = 1000;                                        //Top resistor in voltage divider (1K 0.01%)
float R2;                                             //Final calculated unknown resistor value in Ohms xxx.xx
word R2word;                                          //R2 as WORD



void setup() {

  pinMode(LED_BUILTIN, OUTPUT);                       //Initialise PB4 as OUTPUT for LED visual confirmation

  Wire.begin(SLAVE_ADDR);                             //Initialize I2C communications as Slave

  Wire.onReceive(receiveEvent);                       //Function to run when data received from master

  Wire.onRequest(requestEvent);                       //Function to run when data requested from master

  Serial.begin(9600);                                 //Setup Serial Monitor
}


void receiveEvent() {
  while (0 < Wire.available()) {
    adc_value = Wire.read();                          //Receive high byte
    adc_value = adc_value << 8;                       //Shift high byte to be high 8 bits of 16bit WORD
    adc_value += Wire.read();                         //Receive low byte as lower 8 bits
  }

  Serial.print("Received ADC: ");
  Serial.println(adc_value);                          //Print ADC Value to terminal screen

  digitalWrite(LED_BUILTIN, HIGH);                    //Activate LED confirming DATA has been received

  adc_Volts = (adc_value * adc_Ref_Volts) / adc_Max;  //Calculate ADC Volts
  R2 = (adc_Volts * R1) / (VRef_Volts - adc_Volts);   //Calculate Unknown Resistor Value

  Serial.print("Sent Ohms: ");
  Serial.println(R2, 2);                              //Print Unknown Resistor Value to 2 decimal places to terminal screen
  Serial.println();

  R2 = R2 * 100;                                      //Shift decimal 2 places to the right
  R2word = R2;                                        //Convert R2 Float to a 16bit WORD
}


void requestEvent() {
  Wire.write(R2word & 0xFF);                          //Send 16bit WORD result in two 8bit bytes via I2C to Master
  Wire.write(R2word >> 8);

  digitalWrite(LED_BUILTIN, LOW);                     //Deactivate LED confirming DATA has been successfully returned
}





void loop() {
}

The only difference between the two coding snippets (whole program) is the omission of the serial terminal lines in the ATtiny CODE and of course the change to TinyWire from WIRE.

Additionally, not that I believe it makes any difference whatsoever, there is also a 24bit ADC I.C. on the I2C bus communicating the taken measurements to the PICAXE but I am only using the top 16bits of the returned measurement and in essence am completely eradicating any inherent INL or OFFSET errors.

Also to note, thinking that perhaps it is a timing issue, I have the ATtiny running at 16MHz with the internal clock and switching to a 16MHz external crystal makes absolutely no difference.

Putting a 10K pullup on the ATtiny reset pin also doesn't change anything.

And finally, are my comments regarding the BYTE's correctly commented (HIGH byte, LOW byte) ?

I'd appreciate any clues that could be offered.

Thanks in advance.

Regards,
Mort.

If you are doing anything with the ATTiny85 I very highly recommend using that ATTinyCore implementation.

By far the best thing I ever did in regards to using these devices.

It makes porting code a lot easier as a lot of the standard Arduino functionality is supported... like Wire.

... it even has an implementation of Serial which makes debugging a lot easier.

What frequency did you set your ATTiny85 for? Do you have external crystal? What ATTiny85 chip do you have?

EDIT: just noticed 16Mhz. I suggest to go to 1 reburn bottloader and try with lower clock speed to eliminate that instability possibility

Frequency as noted post #1 for ATtiny85 is 16MHz and I have tried both internal RC and external crystal.

I have also tried 8MHz, but there is no difference in operations/failures.

Actual chip is ATtiny85-20SF.

Cheers.

P.S. I did also try 1MHz, still no change.

Thanks, I’ll look into the ATtinyCore implementation.

FYI.. your UNO code compiles as is when using the ATTinyCore, for an ATTiny85.

2 warnings...

/var/folders/4z/3g9th3gs4cng3b803dg5bhw40000gn/T/arduino_modified_sketch_62668/Blink.ino: In function 'void setup()':
/var/folders/4z/3g9th3gs4cng3b803dg5bhw40000gn/T/arduino_modified_sketch_62668/Blink.ino:23:30: warning: invalid conversion from 'void (*)()' to 'void (*)(int)' [-fpermissive]
   Wire.onReceive(receiveEvent);                       //Function to run when data received from master
                              ^
In file included from /var/folders/4z/3g9th3gs4cng3b803dg5bhw40000gn/T/arduino_modified_sketch_62668/Blink.ino:1:0:
/Users/redcar/Documents/Arduino/hardware/ATTinyCore-2.0.0-dev/avr/libraries/Wire/src/Wire.h:154:12: note:   initializing argument 1 of 'void TwoWire::onReceive(void (*)(int))'
       void onReceive( void (*)(int));
            ^~~~~~~~~

Note that I'm running the dev version (2.0.0)

Interesting. I am using Arduino IDE 2.0.0-rc9.2, but mine compiles without error.

I also just tried using the ATtinyCore as you suggested (I used TOOLS>ATTinyCore>ATTiny85 (Micronucleus / Digispark), but again, no change to my problem.

Cheers.

Sorry... I meant the ATTinyCore dev 2.0.0 version. I needed some features that weren't working in the stable release version.

Ok.. When you say it's not working what exactly is the issue?

Ah ok, sorry, yes my ATTinyCore is also 2.0.0-dev.

So the exact issue is described in post #1. Everything starts out just fine, but then in a random amount of time communications come to a halt and it appears it is the ATtiny85-20SF that holds the SCL line LOW.

When the UNO is in circuit, there is no problem.

Cheers.

No idea on the FIFO buffer regards to Wire. This was one of the things I wondered regards to my comments. Am I using the commands correctly and also commenting my code correctly?

Perhaps I am assembling backwards, however from the PICAXE end I send LOW byte first, HIGH byte second and when receiving the data back, I store it in the same order. My Arduino code does as I need and the WORD values returned are correct being that I have to be getting the bytes the right way around else the returned WORD values would not be correct.

Cheers.

Never mind, PICAXE code is missing so my point is moot.

My sincere apologies. I have told you WRONG. Previously I said I send LOW first, HIGH second.

PICAXE send:

hi2cout [$26], (b1,b0)

b1 is the HIGH byte, b0 is the LOW byte.

Arduino receive:

while (0 < Wire.available()) {
   adc_value = Wire.read();                          //Receive high byte
   adc_value = adc_value << 8;                   //Shift high byte to be high 8 bits of 16bit WORD
   adc_value += Wire.read();                       //Receive low byte as lower 8 bits

This works as I want.

So if I want to send it with LOW byte first, HIGH byte second:

PICAXE send:

hi2cout [$26], (b0,b1)

How should the Arduino code look?

Cheers.

It is probably fine as it is, I think your other device is big endian

With the Arduino code left as is and I change the PICAXE code around to be:

hi2cout [$26], (b0,b1)

I get the wrong result.

So with this being the case, what should the Arduino code look like to accommodate this change?

Cheers.

Try

  adc_value = Wire.read();
  adc_value |= (word)Wire.read() << 8;       
1 Like

That's it. That works as intended with PICAXE code:

hi2cout [$26], (b0,b1)

Thank you mate.

1 Like

I may not have used ATTinyCore correctly.

I did not put:

#include <ATTinyCore.h>

So what do I replace for example TinyWire.begin with?

Cheers.

Just use your Arduino code... that's the great thing about ATTinyCore... most standard stuff is supported.

If I use #include <ATTinyCore.h> and then change TinyWire.begin to Wire.begin it throws up a Compilation error: 'Wire' was not declared in this scope.

Sorry if I am misunderstanding, I really am a noob with this coding.

PICAXE... No worries.
Electronic circuit design and PCB design... No worries.

Arduino coding... Fish out of water.

Cheers.

Are you using ATTinyCore board manager?

Did you try the Uno code you posted in post #1?