I2C Arduino slave sending multiple bytes to non-arduino master failed!

I cannot get the master(non Arduino) to receive the correct response from my slave (Arduino Nano) (BLE Nano v3 – Tesla Electronics). I have no control over the master as it comes from a wheelchair that I bought.

If the master(non Arduino) asks for a single byte it works. But when it asks for multiple ones then oscilloscope shows weird signals:

In the above screenshot, the correct response after the request 0x50 2C should be 0x00, 0x08, 0x03 but only the first byte is correctly displayed.

However, when we look at the previous request 0x80, the slave returned 1 byte 0x00 which is correctly displayed.

More info:
Master project (in the test system) + overall project description: GitHub - johnnyhoichuen/wheelchair-master
And slave project (in both test & target system): wheelchair-slave/main.cpp at main · johnnyhoichuen/wheelchair-slave · GitHub

Slave code:


const byte rrResponse[11][4] = {
    {0x28, 0x00, 0x5A, 0x5A},
    {0x2C, 0x00, 0x08, 0x03},
    {0x30, 0x00, 0x07, 0x88},
    {0x34, 0x00, 0x00, 0x00},
    {0x38, 0x00, 0x00, 0x70},
    {0x3C, 0x00, 0x00, 0x00},
    {0x40, 0x00, 0x00, 0x83},
    {0x44, 0x00, 0x00, 0x79},
    {0x48, 0x00, 0x00, 0x00},
    {0x4C, 0x00, 0x00, 0x78},
    {0x50, 0x00, 0x00, 0x00}};


void setup()
{
  ...
  Wire.onRequest(requestHandler);
  ...
}

void requestHandler()
{
  ...
  byte buffer[3];
  buffer[0] = rrResponse[i][1];
  buffer[1] = rrResponse[i][2];
  buffer[2] = rrResponse[i][3];

  Wire.write(buffer, 3);
  ...
}

Master code (this master is for me to test the responses from slave only)

// function used in master
void readRegister(int address, int targetAddr)
{
    Wire.beginTransmission(address);
    Wire.write(0x50);
    Wire.write(targetAddr);
    Wire.endTransmission();

    Wire.requestFrom(address, 3);
}


// main.cpp

int readAddr[11] = {
    0x28,
    0x2C,
    0x30,
    0x34,
    0x38,
    0x3C,
    0x40,
    0x44,
    0x48,
    0x4C,
    0x50};

void setup() {
  ...
  for(size_t i = 0; i < sizeof(readAddr) / sizeof(readAddr[0]); i++) {
     readRegister(SLAVE_ADDR, readAddr[i]);
  }
  ...
}

.
.
.

I then perform a test where the master is changed to an Arduino board. The new master basically just outputs the same requests (I copied the original master's signal from the oscilloscope). They are correct.

I'm pretty sure the original master (non Arduino) is able to receive multiple bytes. At this point, I'm not sure what's the culprit.

I think that the buffer should be a static or global array that persists even after exit from the handler.

Tried both static and global but still getting the same signal

@DrDiettrich the Wire.write() puts data in a buffer (a buffer inside the Wire library), so it can be temporary data.

@johnnycheng1628 The Arduino as a Target uses clock pulse stretching, because the I2C interface is both hardware and software.
What is the original Master ?
I can help to make a working Controller and Target sketch, but I would like to see two simple full sketches.
I wrote a few things here.

I'm using "Controller" and "Target", see: New I2C standard document by NXP : "Controller" and "Target"

That's true for a master write but may be different for a slave write. Master transfers are fully buffered and executed only in endTransmission(). Slave transmissions instead occur in real time, on demand from the master.

I'd try to write only single bytes in the onRequest() handler.

I doubt that, I2C clock stretching is nowhere mentioned in the ATmega datasheets.

That new terminology will confuse readers of the old school Arduino documentation and datasheets.

The wire.write() puts data in a buffer in Controller mode here. In Target mode it passes the data on to twi_transmit() which also puts the data in a buffer.

The Arduino Wire library uses clock pulse stretching. Grab a Logic Analyzer and see for yourself.
I think that "clock pulse stretching" is the common name for it. In the datasheet you may find: "The slave can extend the SCL low period by pulling the SCL line low".

As you can read in my link, NXP sets the standard. Get used to "Controller" and "Target".

How can I force stretched clock cycles? The scope shot does not show any clock stretching.

NXP makes an attempt, questionable when it will become commonly accepted.

The scope does not show clock pulse stretching ? It should show that.
Sorry that I don't have all the thing nearby to show screendumps of the signals.

A normal sensor shows a normal SCL signal, about 50% high and 50% low.
A Arduino as a Target shows that the SCL signal is longer low. Running the onRequest handler takes the most time, then SCL is low for a long time.

NXP (Philips) created the I2C bus. What they say goes. I suppose that all manufacturers will use "Controller" and "Target" in their datasheets from now on. So I suppose that in a few years the "Controller" and "Target" names are common.

See image in #1.

But that is the problem. The signal is going nuts, maybe after a shortcut or timing problem because of the clock pulse stretching.

Is your non-arduino master a 5 volt or 3.3 volt device? Do you have the proper pull-up resistors?

Thank you all @Koepel @DrDiettrich @david_2018 . I feel like I should give more context to my current project. Please see the test-master project for the overall project description & test-master codes: GitHub - johnnyhoichuen/wheelchair-master
And slave project: wheelchair-slave/main.cpp at main · johnnyhoichuen/wheelchair-slave · GitHub

@Koepel Is there a way to fix it?
The original master is Renasas R7F0C019L2. Unfortunately, I have no control over this non-arduino master. The master I mentioned is only for testing the slave.

More can info can be found in the README.md in the above github link.

@david_2018 I'm not sure if this is correct. The Vin of the arduino slave is 3.3V (from the original position sensor's port, for more please see my above github project link) and 4.7ohm resistors are connected to it from SCL & SDA pin.
Since the position sensor has 3.3V supply, then I assume the non-arduino master is a 3.3V device?

The Renesas User Manual is confusing.

The Renesas user manual mentions a "simplified" I2C bus. It is explained at page 417 and 553. There is says: "Functions not supported by simplified I2C : Wait detection functions".
I think that means that it does not allow clock pulse stretching. That means it is impossible to use an Arduino board as a Target :scream:

On page 571 it says: "The duty ratio of the SCL signal output by the simplified I2C is 50%". After the signal goes nuts it is no longer 50%, so there is definitely something bad going on in the MCU.

Then at page 601 is says that a wait is used when someone keeps SCL low. That means it does support clock pulse stretching. I don't understand everything, it seems that the wait can not occur everywhere. Maybe there has to be something in software as well.

Can you do more testing ? Please show a complete sketch.

Your image of the Test System seems to connect the Arduinos only by 2 wires, no common GND?

1 Like

@Koepel That would be very daunting if it does not work. Do you know other possible solutions? Like which board should I replace?

I checked the SCL from the master is only 13-15kHz, this shouldn't be too fast for arduino?

By sketch do you mean arduino sketch? like the codes of my slave and test-master arduino? They are in my github repo. Or you mean more info of my non-arduino master?

@DrDiettrich Yes thank you for pointing that. I forgot about those. But somehow the test system works perfectly fine

Most probably because both controllers are connected to the same PC, sharing the USB GND.

2 Likes

@Koepel @DrDiettrich some update on my findings:

First, I realised there's no ACK right after the 1st & 2nd byte of the slave, could be the simplified I2C mentioned above?

Second the test system which only contains arduinos seems to be following a "normal" I2C protocol where ACK is present after 2nd & 3rd bytes. Not sure if the slave arduino can only communicate in this way.




Lastly, I found that there's a weird jump right between the first byte and the 0xFF. That weird signal might trigger start & stop conditions and cause the system to fail? Or simply arduino is not compatible with the master...

Interesting and scary pictures.

There is a lot of noise and too much crosstalk between SDA and SCL.
The SDA has a dip at the falling edge of SCL.
I don't worry about the dips on the scope, I worry about the dips that the scope does not sample.

Spikes are bad, but they happen with some I2C devices. I've seen many spikes that no one cares about, so I guess that it is not a big deal. Sometimes a Controller releases the SDA and then the Target pulls SDA low. That can overlap, or there can be a gap causing a spike. In this case, it might be cause by a shortcut ?

The picture with the Arduino Controller and Arduino Target look okay. You can see the rising slope by the pullup resistor for both SDA and SCL. There should be no ACK before a STOP when the Controller requests data. All the low levels and all the high levels are the same voltage.

The picture with original Controller and Arduino Target are super scary, there is no slope. They are straight digital pulses. There are different high level and low level voltages. That is impossible unless there is a strong output or a shortcut.

This is super scary. I would run away from that.

Why are the signals so noisy ? Can you verify that the GND is okay now ? Keep SDA away from SCL. Use short wires.

What is the Target (Slave) device. Is that a normal sensor, or a processor as well.

I have no idea about the missing ACK of the original sensor, sorry.

I get the feeling that both the original Controller and Target don't behave like standard I2C devices, and somehow that is fixed in a bad way to make it work.

P.S.: You might want to consider a new name for the Target System, because the new name for "Slave" is "Target".

Isn't the somewhat noisy low SDA signal caused by the slave, in detail for the ACK signal?