Wire Library in ATtiny and Uno/Raspberry Pi

Hello everyone, I am using an ATtiny85 as an I2C slave sending data to an Uno that requests data. I am using ATtiny core by Spence Kode.
The settings are of the chip are: LTO enabled, Timer CPU, BOD disabled, chip ATtiny85, Clock 8MHz internal, EEPROM retained, millis enabled, programmer Arduino as ISP.

I can program the ATtiny85 with no issues and if I run a I2C scanner on the UNO, I can see the address of the ATtiny85 which means that the I2C protocol has been setup fine. I am also using 4.7K resistors as pullup.

Here is the code of the Arduino receiver:


uint8_t a;
#include <Wire.h>

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Wire.setClock(100000);  //set speed of 1KHz
  Serial.begin(115200);  // start serial for output
  
  Serial.println("Program started");         // print the character
  delay (2000);
}

void loop() {
  Wire.requestFrom(8, 1);    // request 1 bytes from slave device #8

  while (Wire.available()) { // slave may send less than requested
    a = Wire.read(); // receive a byte as character
    Serial.println(a);         // print the character
  }

  delay(500);
}

Here is the code for the ATtiny85 sender:

uint8_t a = 10;

#include <Wire.h>

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.setClock(100000);  //set speed of 1KHz

  Wire.onRequest(requestEvent); // register event
}

void loop() {
 // delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Wire.write(a); // respond with message 
  // as expected by master
}

The data received by the Arduino Uno should be "10", instead I am receiving "255".
When I replaced the ATtiny85 with another Arduino Uno as the sender, the first Arduino Uno used as the receiver started getting the correct data which is "10".

Possible issue might be incompatibility with libraries or other hardware differences between the Uno and Attiny85.

Anyone has suggestions on how to solve this issue? @DrAzzy

I have played a lot to debugg your both Master and Slave sketches using the fantastic debug friendly Digispark ATtiny85 Dev Board (Fig-1). I have been able to read 10 from the Slave with the following sketches; but, I am unable to draw any conclusion as to why your skeches are not working.

(Anyway, the Slave hangs with my sketches after a while (within 5 to 10 transactions.)

I think that the ATtiny85 has been designed to acquire sensor signls and NOT to work with UNO as a Slave (an academic exercise). In that case, TinyWireM.h is just the right Library for ATtiny85 considering its 8 Kbyte flash.
DigisparkAttiny85Board
Figure-1:

Sketch that reads 10 from Slave:
Master Sketch:

uint8_t a;
#include <Wire.h>

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  //Wire.setClock(100000);  //set speed of 1KHz
  Serial.begin(115200);  // start serial for output

  Serial.println("Program started");         // print the character
  delay (2000);
  Wire.beginTransmission(8);
  byte busStatus = Wire.endTransmission();
  if (busStatus != 0)
  {
    Serial.println("ATtiny85 is not fond!");
    while (true); //wait for ever
  }
  Serial.println("ATtiny is found.");
}

void loop()
{
  Wire.beginTransmission(8);
  Wire.write(5);
  Wire.endTransmission();

  Wire.requestFrom(8, 1);    // request 1 bytes from slave device #8
  a = Wire.read(); // receive a byte as character
  Serial.println(a);         // print the character

  delay(1000);
}

Slave Sketch:

#include <Wire.h>
#define LED 1
uint8_t a = 10;
volatile bool flag = false;
byte y;

void setup()
{
  pinMode(LED, OUTPUT);
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
  Wire.onReceive(receiveEvent);
}

void loop()
{
  if (flag == true)
  {
    for (int i = 0; i < y; i++)
    {
      digitalWrite(LED, HIGH); //debugging
      delay(100);
      digitalWrite(LED, LOW);
      delay(100);
    }
    flag = false;
  }
}

void requestEvent()
{
  digitalWrite(LED, HIGH); //debugging
  delay(100);
  digitalWrite(LED, LOW);
  delay(100);

  Wire.write(a);
}

void receiveEvent(int howMany)
{
  y = Wire.read();
  flag = true;
}

Output:

Program started
ATtiny is found.
10
10
10
10
1 Like

I was able to reproduce your 255 result with your ATtiny sketch.

I was then able to get it working.

The culprit was the commented out delay(100); in the slave's loop() function.

With that back in the code, the slave responded correctly to the master's data request.

Exactly why that should make a difference is not immediately obvious to me, but there it is.

That's not the first time I've see someone with an I2C slave sketch and a commented out delay in the loop function. May I ask, did you do that on your own, or are people copying and modifying an existing sketch from someplace that has that call commented out? It's not commented out in the slave examples in the ATTinyCore Wire library.

Also, I don't think setClock has any meaning in a slave context. As I understand it, the master chooses the clock frequency and generates the clock. If I'm wrong about this, I look forward to learning something new.

I've modified the slave sketch to increment the value sent each time and modified the master sketch to display the results on an OLED display so you can see that the value does increment, and it doesn't hang.

Slave

uint8_t a = 10;

#include <Wire.h>

void setup() {
  Wire.begin(8);                // join i2c bus with address #8

  Wire.onRequest(requestEvent); // register event
}

void loop() {
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Wire.write(a); // respond with message 
  // as expected by master
  ++a;
}

2 Likes

Hi everyone. I have solved the issue by taking apart the circuit and rewiring everything from scratch and putting in the loop function just an LED that turns ON and OFF.

As stated by @van_der_decken , there must be something in the loop, you cannot leave it empty. I lowered the delay to 1 ms and the code was still working. I tried to go even lower with delayMicroseconds(unsigned int us) but that broke the code again, there is an issue with the function itslef, because even at 1000 ms = 1 ms the code still doesn't work. I am not going to investigate this matter further.

I used the Wire library instead of the Tiny version because this is what is written in the documentation of the ATtinyCore. @GolamMostafa you are right about using a simplified version of the I2C protocol if I am just sending data, I will do it later on if needed to save extra memory, otherwise I am not going to fall into the trap of optimising everything you can.

I reduced the clock frequency to the minimum on purpose to avoid any noise issues and corruption caused by the higher frequencies. (I have all messy wires on breadboard).

Thanks again for taking your precious time to respond, I really appreciate it.
I might post the project I am doing once completed.

1 Like

In the I2C-UNO-Master and I2C-NANO-Slave setup, the Slave's loop() function does not need any delay() code to respond to Mater's requestFrom() method. The exception is UNO-ATtiny85 setup which needs further investigation.

Master Sketch:

#include<Wire.h>

void setup() 
{
  Serial.begin(9600);
  Wire.begin();
}

void loop() 
{
  Wire.requestFrom(8, 1);
  Serial.println(Wire.read(), DEC);
  delay(1000);
}

Slave Sketch:

#include<Wire.h>
uint8_t a = 10;

void setup() 
{
  Serial.begin(9600);
  Wire.begin(8);
  Wire.onRequest(sendEvent);
}

void loop() 
{
  
}

void sendEvent()
{
  Wire.write(a);
}

Master's Serial Monitor:

10
10
10
10

Curiosity piqued.

This works:

void loop() {
   delay(10);
}

This doesn't work:

void loop() {
   delayMicroseconds(10);
}

This doesn't work:

uint16_t d = 10;
void loop() {
   delayMicroseconds(d);
}

This does work:

volatile uint16_t d = 10;
void loop() {
   delayMicroseconds(d);
}

And at this point I was getting the "something is being optimized away that shouldn't be" vibes.

This doesn't work:

void loop() {
}

UNLESS you disable LTO in the build options. Then it does work.

1 Like

Something is weird. I have this code on my ATtiny85 as slave:
DOB disabled | Cock 8 MHZ internal | EEPROM retained | LTO disabled | millis Enabled | Timer1 clock CPU.

uint8_t a = 101;   //message

#include <Wire.h>

void setup() {
  // Set PB3 as an output
  DDRB |= (1 << DDB3);

  Wire.begin(10);                // join i2c bus with address #
  //Wire.setClock(100000);  //set speed of 1KHz

  Wire.onRequest(requestEvent); // register event
}

void loop() {
  //PORTB ^= (1 << PB3);  //toggle LED
  delay(1);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {

  Wire.write(a); // respond with message 
  // as expected by master
  
}

As a master I am using a raspberry pi 4. I can always see the address of the Attiny slave at any time, BUT I can read the data out of the Attiny ONLY 15 times, after which I receive an error message on the raspebrry : [ IOError: [Errno 121] Remote I/O error]. Bear in mind I can still detect the Attiny adress by scanning the devices available on the bus. If I reboot the Attiny I can see the message again for 15 times.
15 is a very sus number, seems like something is overflowing, I have no idea what it could be, would be nice if you can point me to a direction.

I have tried the exact same setup but replacing the Raspberry pi 4 with an Arduino Uno.
In this scenario, I was able to read the slave ATtiny85 indefenitely and without any issue.
This means that the problem is in the raspberry pi side.
I might not investigate further and instead start using the CAN bus instead of the I2C protocol.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.