i2c slave communication problem

I want to simulate a Smart Battery BMS Slave with SMbus communication.

I found these two topics in this forum that are quite similar, but they seem not to be solved...
http://forum.arduino.cc/index.php?topic=329264.0
Trying to simulate a SMBus from an Smart Battery accu pack with the Arduino Uno - Programming Questions - Arduino Forum.

I have an Arduino Zero Pro board. The code I am trying is very simple; the master (the PC) sends a command to the arduino by SMBus communication and this board has to send the same command to the master. But this simple code is not working...

#include <Wire.h>
#define SLAVE_ADDRESS 0x0B 
 int command=0;
 
void setup() {
Serial.begin(9600);
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveData);
Wire.onRequest(sendData);}

void loop() {
delay(100);}

void receiveData(int byteCount){
 command=Wire.read();
 while (Wire.available()){
   Wire.read();} 
}

void sendData(){
  Wire.write(command);
}

if the master sends command "a" to arduino (slave), "0" is sent back to master.
if later, master sends command "d", "140a" is sent to master.
if later, master sends command "e", "1a0d" is sent to master...

Has anyone know where could the error be??

Your 'command' variable is an 'int', but 'write()' sends a byte. Try changing 'command' from an 'int' to a 'byte' variable.
I'm not positive that's the problem, but it was the first thing that hit me.

if the master sends command "a" to arduino (slave), "0" is sent back to master.
if later, master sends command "d", "140a" is sent to master.
if later, master sends command "e", "1a0d" is sent to master...

Is the "0", "140a" and "1a0d" printed by the master, or by some debug code on the slave? The slave only sends one byte with Wire.write()and extra bytes requested by the master are garbage. What is the master asking for in Wire.requestFrom().

void sendData(){
  Wire.write(command);
}

What happens if you declare command as a volatile byte and set it to zero after each transmission?

I changed "command" variable from ¨int¨ to ¨byte¨ but the result is still the same.

I also tried to declare "command" as a ¨volatile byte¨ and set it to zero after each transition (see code below), with the same result.

#include <Wire.h>
#define SLAVE_ADDRESS 0x0B 
 volatile byte command=0x00;
 
void setup() {
Serial.begin(9600);
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveData);
Wire.onRequest(sendData);}

void loop() {}

void receiveData(int byteCount){
 command=Wire.read();
 Serial.println(command);
 while (Wire.available()>0){
   Wire.read();} 
}

void sendData(){
  Wire.write(command);
  command=0;
}

The "0", "140a" and "1a0d" is printed by the master. The master is the PC, I have a software from Texas Instruments to read the parameters of athe battery with SMBus communication. With this software I send the command I want, and this command is received OK in the slave or arduino(I see this with serial.println(command)). The problem is that the arduino is not sending the value I want to the master.

Command sent form master to slave: Command received in Slave: Value sent to Master:
A 10 (in HEX, A) 0
D 13 (in HEX, D) 140A
E 14 (in HEX, E) 1A0D

I think there must be an error in the i2c communication, but I´m not sure where is it.

You should probably make "command" volatile.

If I understood it OK, I have already tried to declare "command" variable as VOLATILE BYTE.
I posted the code in previous post. As I explained it previously, the result was the same...

I changed the code;

#include <Wire.h>
#define SLAVE_ADDRESS 0x0B 
 volatile byte command=0x00;

void setup() {
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveData);
Wire.onRequest(sendData);}

void loop() {}

void receiveData(volatile int byteCount){
 command=Wire.read();
 while (Wire.available()>0){
 Wire.read();}
}

void sendData(){  
    Wire.write(command);
    command=0;
    Wire.write(command); 
}

Now I send 2 Bytes to the master and the master receives the command I want, but NO WHEN I WANT. This is what happends:

Master to Slave command: Slaves read command: Value sent to Master:
A 10 (in HEX, A) 0
D 13 (in HEX, D) 000A
E 14 (in HEX, E) 000D
3 3 000E

It seems that the program enters to "receivedata" when the master sends a command. But after having received the command from the master, is not entering to "sendData", when it should.

Any idea to solve this?? Thank you in advance.

What is the code at the other end of this communication?

The master is a Texas Instruments SW, "Gas Gauge Evaluation Program". As far as I know, is not an open software, but I´m goling to try to ask them how do they do the communication.

I still don´t know how is the master software doing the communications but I discovered someting more...

I add to code some SERIAL.PRINT in order to know something more. I can see that when the master sends a command, the first function executed is Wire.onRequest(sendData); and after Wire.onReceive(receiveData);. Theoretically when the master sends the command, Wire.onReceive(receiveData); should be executed and then, when the master request data from slave Wire.onRequest(sendData);.

#include <Wire.h>
#define SLAVE_ADDRESS 0x0B 
 volatile byte command=0x00;

void setup() {
Serial.begin(9600);
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
}

void loop() {}

void receiveData(int numBytes){
 command=Wire.read();
 Serial.print("received data from master:");
 Serial.println(command);
 while (Wire.available()>0){
   Wire.read();}
}

void sendData(){  
    Serial.print("data sent to master:");
    Serial.println(command);
    Wire.write(command);
    command=0;
    Wire.write(command); 
}

Master sends command 09

Sorry, I posted before finishing writting...

Master sends command 09

  • data sent to master: 0
  • received data from master: 9

Master sends command 02

  • data sent to master: 9
  • received data from master: 2

Don't do serial prints inside an ISR.

Here is a version of your slave which removes the serial printing from the interrupt. It reads a one byte command and sends a one byte echo response. There is some error testing on the number of bytes received from the master.

We have asked several times for what the master side is doing. How many bytes is it sending, and how many bytes does it request back. You really need to know that information.If it is requesting more than one byte, you need to send a multi byte buffer from the slave, and not perform multiple wire.write commands of a single byte.

I've included a Master sketch which sends one byte and requests one byte back.

Both Master and Slave sketch are in sync.

SLAVE

#include <Wire.h>
#define SLAVE_ADDRESS 0x0B
//volatile byte command = 0x00;
volatile char command;

volatile boolean newCommandReceived = false;
volatile boolean newEchoSent = false;
volatile boolean errorBytesReceived = false;
byte errorBytes;

void setup() {
  Serial.begin(9600);
  Wire.begin(SLAVE_ADDRESS);
  
  Wire.onReceive(receiveData);
  Wire.onRequest(sendData);
  
}

void loop() {
  
  if (newCommandReceived == true)
  {
    Serial.print("received data from master:");
    Serial.println(command);
    newCommandReceived = false;
  }

  if (errorBytesReceived == true)
  {
    Serial.println("Unexpected number of bytes = ");
    Serial.println(errorBytes);
    errorBytesReceived = false;
  }
  
  if (newEchoSent == true)
  {
    Serial.print("data sent to master:");
    Serial.println(command);
    Serial.println();
    newEchoSent = false;
  }
}

void receiveData(int numBytes) {
  if (numBytes == 1)
  {
    command = Wire.read();
    newCommandReceived = true;
  }
  else
  {
    errorBytesReceived = true;
    errorBytes = numBytes;
  }
}

void sendData() {
  Wire.write(command);
  newEchoSent = true;  
}

MASTER

#include <Wire.h>
char command[3] = {'a', 'd', 'e'};
#define SLAVE_ADDRESS 0x0B

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

void loop() {

  for (int i = 0; i < 3; i++)
  {
    Serial.print("Send command ");
    Serial.println(command[i]);
    Wire.beginTransmission(SLAVE_ADDRESS);
    Wire.write(command[i]);
    Wire.endTransmission();
   
    Wire.requestFrom(SLAVE_ADDRESS, 1);    
      char c = Wire.read(); 
      Serial.print("Receive echo back ");
      Serial.println(c);         
      Serial.println();
      delay(1000);
  }
}

But after having received the command from the master, is not entering to "sendData", when it should.

Your code, before you put a serial print into the ISR, looked OK. We can't debug just one side of the communication. You could put a logic analyser on the line and see what is happening. My guess is that the master isn't doing what you think it is.