Wire Library [repeated start]

Please don't hesitate to tell me if I am barking up the wrong tree.

Lets set the stage.
I am trying to communicate with an old microprocessor via I2C (2Wire).
This microprocessor does not have inbuilt I2C, it uses soft I2C.
This means it is not listening while it is doing other work, it listens periodically.
So when another processor communicates with it on the I2C bus, the master has to hold the line and keep trying until an ACK is received.

I have a data logger to record this.
The top capture254 is the what I have discussed above, the bottom Capture256 is Arduino.
Link to Captures.

As I understand it. Arduino Wire.endTransmission(Stop Boolean) now has a Boolean to keep the line open.
If true, the transmission ends with a "Stop"
If false, the master holds the bus and does a repeated start with the next piece of data/Write.

So if the following code is used.
The bus will be held indefinitely until the slave replies with an ACK.

byte x = 1;
byte WriteState = 0;
Wire.beginTransmission(0x4B);
Wire.write(x);
WriteState = Wire.endTransmission(false);
while (WriteState == 2) {
	Wire.write(x);
	WriteState = Wire.endTransmission(false);
	delay(10);
}
Wire.write(x);
WriteState = Wire.endTransmission(true);

It does, look at the bottom Capture256.
BUT.
Wire.endTransmission(false) should not be sending a "Stop".

The way I see it:
When the last clock signal should not be sent when "repeated start" is invoked.
The data line should not go low at this point.

If you look at the two Data Logs in the Capture you can see the differences.
We need it to register an "Sr" instead of the "P" and "S"

Hope all this made some sense to someone who can sort it or if it is the library can modify it.

I don't think this is an issue if just using Arduino devices, the microprocessor I am trying to talk to is an old one from an old robot.
Most microprocessors used today have hard internal I2C, so it would not be a problem.

Tim

This would suggest to me that the µC acts as a master that can start a transmission whenever it likes.

For a slave your conclusion may be okay in general, but what does it mean in detail?
If the controller starts somewhere within a transmission, what can it do at which point in the transmission?

Ordinary bits change SDA only when SCL is low - all that can be skipped. Start and Stop are signaled when SDA changes while SCL is high. This should allow the slave to definitely detect a valid Start after whatever else. Now it can react to the address byte with an ACK so that the transmission can proceed.

The delay() in your while loop may be too long so that the slave stops polling and does something else.

This may be overridden by a NACK.

About our forum:
We like to the see the full sketch and we really like to see the picture (not a link).
If you go to the top of your first post, you can change the Category. Change it to: "Networking, Protocols, and Devices category".
Your picture uploaded to this forum:

About the Wire library:
You don't have to test for error number 2, since the numbers have different meanings on different boards. Just check if it is not zero to see if "something" is wrong.
If something is wrong, you can not continue with a Wire.write() and Wire.endTransmission(), you have to do the whole thing over again.
See also my alternative explanation of the functions of the Wire library.

Totally nuts:
A repeated start to claim the I2C bus while testing the NACK/ACK from a Slave is totally nuts. I have never seen that before and it makes no sense. The repeated start should be used while communicating with a Slave, not to start a communication.
Not sending a STOP, until the Slave is ready and then send the data and a STOP after that is not possible with the Wire library. It hurts my brain to even think about it, because it makes no sense. I'm pretty sure that it breaks the I2C standard.
The I2C hardware follows the I2C standard, so it might be impossible to do that.
With a software I2C library you can change the source code and do whatever you want. I hope that is not needed.
Can your try first without that repeated start ?

To delay or not, that is the question:
When the I2C is done in software, a Master in hardware is sometimes too fast. When the Master delays too much, the Slave might stop listening (as DrDiettrich already wrote). I suggest some delay, for example 10 to 100 µs.

Can you try this:

// Testing the ACK to its I2C address does not need a databyte
// Try 50 times with 5ms delay in between.
int error = 0;
for( int i=0; i<50 and error==0; i++)
{
  delay(5);
  Wire.beginTransmission(0x4B);
  error = Wire.endTransmission();  // use 'false' to claim the bus, try without first.
}

if( error == 0)             // was there really a succesfull ACK from the Slave ?
{
  delayMicroseconds(50);         // some delay, since the Slave uses software

  // The Slave is responding, send the real data. 
  Wire.beginTransmission( 0x4B);
  Wire.write( 0x01);
  error = Wire.endTransmission();   // no parameter, the I2C bus is releases after this.
  if( error == 0)
  {
    Serial.println( "Success, data delivered to Slave");
  }
  else
  {
    Serial.println( "Error, no success after all");
  }
}
else
{
  Serial.println( "The Slave was never ready during testing");
}
1 Like

Sorry guys, I thought the Data Log explained it all.
When using the wire library and you tell it NOT to send a Stop "P" but do a Repeated Start "Sr"
I should not see any Stops "P" or Starts "S" in the data Log. (until it is acknowledged)
We have to go back in time 20 years using cheap processors, I think this is a Elan EM78P459 processor (as requested I won't put a link to the data sheet).

Oh, may be this may help. I am trying to add an Arduino to an existing robot that is working perfectly.
It has several processors in it that talk to each other via I2C, a very slow I2C, but as it is clock based it should not be a problem.
Why do I want to add an Arduino? Because I want to add Bluetooth or Wi-Fi, Currently it is IR.

@ Koepel
Do what you preach, did you test your code?
This is the result after fixing your code.


I didn't realize upload was for IMG images, thought it was for code files. (hope it is I don't see preview as I write)

I put my post in Libraries because I don't think the library is correct.
Capture258

So back to my whole code.

#include <Wire.h>

#define MOTOR_ADDRESS_L 0x4B
#define MOTOR_ADDRESS_R 0x48

byte x = 0;
byte WriteState = 0;
byte BytesWritten = 0;

void setup()
{
	Serial.begin(115200);

	Wire.begin();
	Wire.setTimeout(250);
	Wire.setClock(1250);

	/*
		Speed Tests
			  Target  =   23  -   44

			  10000   =   250 -   4
			  5000    =   125 -   8
			  2500    =   63  -   16
			  1250    =   31  -   32
			  625     =   31  -   32


	  There are some interesting aspects to the endTransmission() function. First, it returns a byte value to let you know what the status of the data transmission was�

		Value Returned	Name						Description
		0				SUCCESS						The attempt to write to your slave component was successful.
		1				DATA EXCEEDS BUFFER LENGTH	You are attempting to send more than 32 bytes of data.
		2				NACK ON ADDRESS TRANSMIT	Either the component you're attempting to talk to is offline, or you mistyped the I2C address for it. Which do you think is more likely?
		3				NACK ON DATA TRANSMIT		The slave component responded to its address with an ACK, but did not respond that it received the data successfully.
		4				OTHER ERROR					This means your I2C write request to the slave component failed in some other fashion, most likely dealing with gremlins.

	*/
}

void loop()
{

	//x = 7;
	x = B00000111;
	WriteState = 0;
	BytesWritten = 0;

	Wire.beginTransmission(MOTOR_ADDRESS_L);
	BytesWritten = Wire.write(x);
	WriteState = Wire.endTransmission(false);    // false = Do NOT send a Stop, do a repeat start. 

	while (WriteState == 2) { // Wait untill acknolages master. write state = 0
		Wire.write(x);
		WriteState = Wire.endTransmission(false);    // false = Do NOT send a Stop, do a repeat start. 

		/*
		The delay does not need to be here.
		Its just so the data loger does not get over tasked.
		The resut should be the same.
		I still should not be seeing any stops in this section.
		*/
		delay(10);
	}
	Wire.write(x); // dose not matter that I send again.
	WriteState = Wire.endTransmission(true);    // when slave replies, write state = 0. finish with a Stop.

	delay(500); // Delay before trying again.
}

Notes before you say:
I2C clock, You will see in comments I tested clock speeds to see how Slow I could get it. (Old processor remember)

This is what I am trying to achieve:


The robot is working perfectly, it is doing wat it is supposed to.
You can see that the master is keeps sending until the slave responds.
You will see that the master does NOT send any STOPs.
There is a REPEAT START between each attempt to send the data.
There is only a STOP after the slave sends an ACK.

It is what I am trying to get with my code, but I am getting.


This is stuck in the loop (while).
You will see, every time it attempts to send data it finishes with a STOP "P", according to the Wire Library, the Boolean false = DONT send a STOP.
In the loop (while) in my code if no Stop "P" was sent, then the START "S" would be a Start repeat "Sr".

It looks to me like the Wire Library, when you tell it not to send a stop, it just flags the code to remember the address so that you don't have to use the "beginTransmission" while you continue to write data to that address.

The screenshots show signals on the wires.
It's the Wire Library that controls that.
Difference between a Start "S" and Start repeat "Sr". There is no Stop "P" before a Start repeat "Sr".

If my code is wrong and it should be sending Stops, please explain why.

I think the slave, when it looks at the I2C, it checks to see if it is being used, if so, it waits for a new start and then checks to see if it is for the slave.
I think there is some bespoke stuff on this robot.
All my data logs of the robot communication have had a least two attempts to send data before an acknowledgement.

This is why having a Stop at the end of each attempt (Arduino Library) to contact the slave does not work.
Having a stop, releases the bus, so the slave, when it comes to check if the bus is in use, it see that it is not, and goes of about its other tasks.

I'm afraid we are not getting closer.
Did you try to move your topic to a other Category ?
Which Arduino board do you use ?
No, I did not test my code. What did you fix ?
You may not call Wire.write() and Wire.endTransmission() after a Wire.endTransmission returned an error. You have start the whole thing again with Wire.beginTransmission and everything.

It looks like I need to go to GitHub.
Which means I will have to try and fix it my self.
Looks like I am going to learn something new :face_with_raised_eyebrow:.
I thought I had found a place to discuss the issue about the Library.
I thought I may have found someone who knows the internal workings of the Library.

I did not try to move to another category. I think this is about the Wire library it's self, not about using it.
I know what the I2C protocols are. ***.i2c-bus.org explain it very well. (didn't make a link as requested)

I'm using an AVR board at the moment.
Perhaps I should try an ESP32 it will use a different Library. One that may work if it has Repeated Start "Sr" :wink: .
The thing is, with the ES32, I may not be able to get the clock to go slow enough.

About your code. It was just the 'and' to '&&'. it was just to make the point you was making.

There is No Error returned.
A "NACK" just means there was no acknowledgment from the slave, it is the master that sends sends (puts on the line) the "NACK" after the timeout.
The whole reason for NOT sending a STOP, is so you don't have to start the whole thing again.

Oh I found preview btw, it was hidden under a message.
Sorry for my sarcasm.

ACK and NACK are just the modulation (done by the Slave) of the SDA line being LOW or NOT LOW during the 9th SCL sampling clock. These conditions are automatically sensed by the TWI/I2C Logic of the Master to take next action.

NACK :frowning:
The bus is in idle state (both signals high) before a Start. I.e. the slave can detect a Start regardless of preceding transitions.

NACK :frowning:
Not sending a Stop prevents other masters from taking ownership of the bus and do their own transmissions.

Unfortunately that's not true :frowning:
ACK is generated by the data receiver which is the master in a requestFrom(). That's why neither the master nor the slave can know then how many vaild bytes have been transferred from slave to master.

I'm not so sure. Wire.endTransmission() does the entire transmission, starting with the address and followed by all the data bytes stored before. The Wire.beginTransmission() is only required to set the slave address in the stored record.

1 Like

In I2C protocol, every Slave is assigned 7-bit address which I like to designate as "Slave/device Address". When the Master intends to write data to Slave, it appends 0 at the right most position of Slave Address to build 8-bit "I2C (Bus) Address".

Wire.beginTransmission(slaveAddress) code queues 8-bit I2C Bus Address in the buffer for subsequent transmission to Slave following Wire.endTransmission() command.

The Wire library allows for up to 23 bit I2C addresses.

As far as I know, it is 8-bit for UNO/NANO/MEGA.

Dear @reincarnated, I can still help, be we have to get closer somehow. English is not my native language and I don't understand a number of things that you write. I have no problem to admit my mistakes.

The "and" instead of the "&&" is allowed. So now I think that you don't use the newest Arduino IDE. Those things are important for me, because when I do a test, I could be doing a different test then you. I also need to know which Arduino board you use, because when I read what a library is doing I want to make sure that I'm reading the right library.

Do you think that I can be of help ? Then I will read this topic once more.

It is the Master who generates SCL to clock out data from Slave.

Yes! In this case, the Master receives data; but, it is not a Slave. It is the Master who monitors the ACK signal to generate Bus Status while data goes from Master to Slave (write mode). What is about ACK signal when data comes from Slave to Master (read mode) ro know which I need to review data sheet.

Thank you people for your comments, I have read this: i2c_bus_specification_1995.pdf
(I was told not to put links, but its a large document)
I am moving to the GitHub, after I have finished modifying the library, I am still working on it.
That's if I am able to make a compatible version. I still have a bit to learn about it.
After reading i2c_bus_specification_1995.pdf there is something else I would like added.

For a recap I am trying to get this:

The current Arduino Library gives this:

So far with my Edited Arduino library, I have managed to get it to this stage:


As you ca see I no longer have any Stops "P", each new starts is a repeat Start "Sr".
This is how I believe it should be.
(But as the Arduino Library stands at the moment, it still puts in stops)

My only issue now is the 9th clock pulse.
Reading the specification it seams the bus state is controlled by the TWCR (TWI control register).
It may be that I cannot do anything about the low state of the SCL line.
This leads me to suspect that the I2C version (soft) on my robot (20 year old) is bespoke.
Which means I will probably have to write a bespoke (bit bang) soft version to communicate with it.
Everything that people use with Arduino connected to the I2C bus these days has inbuilt I2C (hard wired) so this issue does not exist in those cases.
I have a robot (TIM-01) I have built with todays modern devices, I have no issue with all the processors communicating together on the current Arduino (I2C) Wire Library.

My original post was to see if any one here had the skills to achieve what I was after, perhaps I didn't explain it enough.
I will leave the post at that for now, as I will be a long time trying to make a version to talk to the old chip, if I can. :slightly_smiling_face:

Thank you all.

I can confirm that with a Wire.endTransmission(false); the Master sends a STOP if the Slave did not acknowledge:
afbeelding
Test conditions: Arduino Uno in Wokwi (the Wokwi simulator is good enough to simulate the Uno I2C accurately).

There are two branches for the AVR microcontrollers: The ArduinoCore-avr and ArduinoCore-megaavr. By not telling us which Arduino board you have and which build environment and why the 'and' did not work, I can not help you.

That's a misunderstanding! Links are appreciated but never should go to some temporary or restricted URL.

If you read the quoted page carefully you'll find that a broadcast Read should be issued (addr. 0) to allow a software implementation to catch the SDA line LOW for some time. This is what you should achieve before everything else. The handling of a broadcast message differs between Arduino and PDF documentation - what's the current standard state?

Your edited library generates a wrong (inverted?) SCL signal. I'd think that you misunderstood clock stretching what's reserved to slaves in order to delay a transmission until a byte has been processed.

If the Slave does not assert ACK for some reasons (power off or gone bad), then the Master must come out from the infinte loop by timeout scheme which the user must incorporate in the sketch. Is it correct?

No. The NACK is generated when the master releases SCL while SDA is high. For ACK the receiver should have pulled SDA low with the 9th clock pulse. So the NACK is recognized immediately and no timeout is required to stop the transmission. A timeout applies if the slave holds the SCL line low (clock stretching) for a too long time.