0

I hooked up two Arduino Nanos with I²C, using 3 wires between GND <-> GND, A4 <-> A4, A5 <-> A5Ω and 1kΩ pull-up resistors on A4 & A5.

When I use the following code to communicate between them I get a weird empty receive event.

master.cpp

#include <Arduino.h>
#include <Wire.h>

#define SERIAL_BAUD 9600

#define I2C_SLAVE_ADDRESS 8

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

void getFromState (unsigned char state) {
    Wire.beginTransmission(I2C_SLAVE_ADDRESS);
    Serial.print("s");
    Serial.print(Wire.write(state));
    Serial.print("e");
    Serial.print(Wire.endTransmission());
    delay(5000);
    Wire.requestFrom(I2C_SLAVE_ADDRESS, 2);
    int c = (unsigned char) Wire.read();
    Wire.flush();
    Serial.print("r");
    Serial.println(c);
    Wire.endTransmission();
}

void loop () {
    Serial.println("loop");
    getFromState(0);
    delay(10000);
    getFromState(1);
    delay(10000);
}

slave.cpp

#include <Arduino.h>
#include <Wire.h>

#define SERIAL_BAUD 9600

#define I2C_ADDRESS 8

unsigned char state = 0;

void requestEvent () {
    Serial.print("Request ");
    Serial.println(state);
    Wire.write(state);
}

void receiveEvent (int amount) {
    Serial.print("a");
    Serial.print(amount);
    Serial.print("r");
    state = (unsigned char) Wire.read();
    Serial.println(state);
}

void setup() {
    Serial.begin(SERIAL_BAUD);
    Wire.begin(I2C_ADDRESS);
    Wire.onRequest(requestEvent);
    Wire.onReceive(receiveEvent);
}

void loop() {
    delay(10);
}

Serial Output Master After 1.5 Loops

loop
s1e0r0
s1e0r1
loop
s1e0r0
s1e0

Serial Output Slave After 1.5 Loops

a1r0
Request 0
a0r255
a1r1
Request 0
a0r255
a1r0
Request 0
a0r255
a1r1

Question

Why is there an empty receive event a0r255 after every request? Is that normal? Am I doing something wrong? Is this some sort of hardware issue?

EDIT: I opensourced this project here, take a look if you want to!

1

The main culprit that is causing your problem is the second

Wire.endTransmission();

after your requestFrom. That causes the previous startTransmission() to be repeated with no data in it - thus giving you a second receiveEvent call with no data.

| improve this answer | |
  • That's a quite weird behavior, but sounds like it could truly be the reason this happens. Will have to check this out tomorrow moring since it's quite late in Germany already. :( – Florian Wendelborn Apr 10 '16 at 0:10
  • It's late here in the UK too, but I'm still firing on all cylinders ;) – Majenko Apr 10 '16 at 0:10
  • yeah I know that feel, I sometimes try not to stay up that long, today is one of these days where I may get a reasonable bedtime. – Florian Wendelborn Apr 10 '16 at 0:12
  • Thanks, this actually was the reason I was seeing the empty packets. Accepting this answer, since it is more tailored towards the actual problem. Doesn't mean I don't appreciate the other one a lot, but this solved & explained the "real" issue. – Florian Wendelborn Apr 10 '16 at 7:52
2

Your code has several issues:

For the master:

  • Your I2C transactions should be quick and not delayed with Serial.print()s like in getFromState()
  • You requested 2 bytes from the slave but you are reading only one from the buffer. Wire.flush() flushes the outgoing buffer, not the receive buffer. Use while (Wire.read() != -1); instead.
  • Its also advised that you use Wire.available() to iterate through the receive buffer when read()ing; its how you check if anything was received at all and how many bytes were received.
  • After using requestFrom(), you shouldn't call endTransmission(). Its self-contained.

For the slave:

  • You should never call Serial.print() or delay() in requestEvent() or receiveEvent(); both functions are called from within ISRs and ISRs are meant to execute quickly and return.
  • You are requesting 2 bytes from the slave in your master code, whereas your slave's requestEvent() writes only one byte.
  • In receiveEvent(), again its best to use either Wire.available() to iterate through the RX buffer or you use the variable amount. This way, you read everything in the buffer; you can also flush the RX buffer like I showed you before.

Finally those delays are rather long; 10ms would do, if they are even needed at all.

| improve this answer | |
  • Most of these issues are in the code for debugging purposes (serial and ultra long delays so I can actually see whats happening) The thing with 2 bytes is: as soon as I set it to one, it doesn't work anymore, it just stops. The endTransmission is an accident/leftover from code I previously used, will check if any of this changes anything tomorrow morning. I literally tried to debug this code for hours, so I tested a lot of things, one initial issue actually was that I needed the pull ups because otherwise the slave couldn't send anything to the master (except. somehow ACK bits). – Florian Wendelborn Apr 10 '16 at 0:08
  • Accepted the other answer, because it had a better explanation and was more precisely addressing the actual problem. Thanks for your input nevertheless, if possible I'd accept yours too. :( – Florian Wendelborn Apr 10 '16 at 7:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.