Wire.h function outputs

Hi,

I'm trying to figure out what the different Wire.h functions output to the pins. I know what outputs I want, but I'm not sure which functions would give me the ones I want. For example, I'm pretty sure Wire.beginTransmission([7bit address]) tells the SDA pin to output ([7bit address] 0) and wait for an acknowledge from the slave, and the SCK pin to clock accordingly. The output I am particularly interested in is ([7bit address] 1), or just a signal for the selected slave to start using the SDA wire.

Some notes

  • The wiring is most likely correct, the i2c scanner example correctly identified the address of the slave
  • I'm only able to tinker with the master, the slave is an off-the-shelf sensor
  • The setup has worked before with a third-party library made for the sensor (there is no official library), however, said library has proved to be unreliable, and other libraries I've found haven't fared much better
  • I'm using a teensy 3.5 with the teensy Wire library

The I2C bus is a bus that uses both SDA and SCL. Both signals go in two directions (from Master to Slave and from Slave to Master).

If you want to see the signals on the I2C, then a logic analyzer is very helpful. A LHT00SU1 is only 25 dollars and with PulseView/sigrok the I2C can be decoded.

The functions for an Arduino Uno might behave different than what you expect.
The Wire.beginTransmission() does nothing on the bus. It resets a buffer and variables.
The Wire.write() still does nothing on the bus. It writes to a buffer.
The Wire.endTransmission() does the START, the I2C address, check acknowledge, write the data from the buffer and a STOP.

The Teensy 3.5 is a 120 MHz ARM Cortex-M4. I don't know how compatible it is with Arduino or how compatible its Wire library is.

Which sensor is it ?
How long are your wires ?
Do you use a cable ?
Did you connect the GNDs ?
Can you show the unreliable code ?
What are your pullup resistors ?

Here's a slightly better description of what I have:
The sensor is a MS5611 pressure sensor (datasheet) surface mounted on a PCB. The only wires are the internal etchings on the PCB and the header pins on the teensy. Pullup resistors should also be OK, as it's sometimes worked with the unreliable libraries. Teensy is made to be fully compatible with arduino, and the example code for the teensy wire library uses the same functions as arduino examples. The sensor is also definitely wired in i2c mode as the i2c scanner was able to find it.

The setup (with the same hardware) has mostly worked over the past year, with everything working fine until revisions to the code are uploaded to the teensy (about once a month). The problems have always been from the library running the pressure sensor. Once that gets sorted out, everything works fine. However, having to debug the libraries every time I want to upload new code is a hassle, so I'm trying to get rid of the library by just having the teensy directly interface with the sensor rather than through a library. For now I'm just trying to get them to communicate.

Here is the code used to test that:

#include <Wire.h>

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Wire.begin();
  while(!Serial);
  Serial.println("Serial and Wire begun");
  Wire.beginTransmission(0x76); //this address is correct
  Wire.write(0x1E); //reset command, tells sensor to calibrate using internal PROM values
  Wire.endTransmission();
  Serial.println("sent reset command");
  Wire.beginTransmission(0x76);
  Wire.write(0xA6); // tells sensor to read a calibration value off PROM and send it on READ
  Wire.endTransmission();
  Serial.println("PROM read request sent");
  delay(50); // maybe sensor is just slow, so give it some time to think?
  Wire.requestFrom(0x76, 2); // calibration value should be two words: 1100xxxx xxxxxxxx
  Serial.println("requested 2 bytes");
  if(Wire.available() <= 2){
    Serial.println(Wire.read()); // word 1
    Serial.println(Wire.read()); // word 2
  }
  Serial.println("PROM has been read");
  

}

and the output:

Serial and Wire begun
sent reset command
PROM read request sent
requested 2 bytes
-1
-1
PROM has been read

a -1 from Wire.requestFrom() means that no data was sent

This is obviously wrong

  if(Wire.available() <= 2){

but it may not matter as requestFrom() is a complete transaction on standard Arduinos.

I changed the if(Wire.available() <= 2) to a while(Wire.available()). Both methods are taken from sample code from different places. Either way, the sensor never tries to send anything back.

I've now checked with two different sensors and three different boards, still no luck. The sensors were working fine as of two weeks ago, and they've only been sitting in a box since then so bad sensor/board is also ruled out.

Edit: I've checked the result of the acknowledge/not acknowledge on endTransmission(). The reset command works fine (endTransmsiision() returns 0), but any other command (such as reading the PROM or starting a conversion) gets a NACK after the command is sent (error code 3). However, this doesn't seem to be an error in sending more than one command as sending the reset command any number of times always gets full ACK.

Time to figure that one out

Edit 2: I found a different thread (Reading data from a MS5611 sensor without libraries - Sensors - Arduino Forum) of someone trying to do the same thing, I've managed to get their code to work for me. Thanks for the help!

We don't care about sample code that you can find. We prefer that you have reliable code. Even the working code elsewhere on this forum is still not okay.

You could check the error returned by Wire.endTransmission() and the number of bytes returned by Wire.requestFrom(). If you do that every time, then you will immediately know if something is wrong.
When you have trouble with a sensor, you should not start a calculation when there was a problem and the value could be -1.
Some sensors are always available on the I2C bus, some sensors are not visible on the I2C bus when they are busy.
I don't know how this sensor behaves, but I think that you have to wait after a conversion.
This part of the datasheet is not very clear: "A conversion can be started by sending the command to MS5611-01BA. When command is sent to the system it stays busy until conversion is done. When conversion is finished the data can be accessed by sending a Read command, when an acknowledge appears from the MS5611-01BA, 24 SCLK cycles may be sent to receive all result bits".
There are more cryptic parts in the datasheet.

For the Wire.requestFrom() I prefer this:

int n = Wire.requestFrom( 0x76, 2);  // request 2 bytes
if( n == 2)   // did we get the 2 bytes ?
{
  Serial.println( Wire.read()); // word 1
  Serial.println( Wire.read()); // word 2
}
else
{
  Serial.println( "Wire.request failed");
}

When there are less than 2 bytes read, then there was a severe bus error or the sensor is not connected or busy. When there are more than 2 bytes, well, that is not possible. So if you request 2 bytes, then you should get 2 bytes.

You have no delay after the reset command. The other code on this forum does. I did not read the datasheet thoroughly, so I don't know what that delay should be, but it makes sense to have that delay. Most sensors need a delay after a reset.
Perhaps the sensor needs delays in more situations.

You do this:

while(!Serial);

Does that mean, that the Teensy will stop there when the computer is not connected. I hope that code is only used when testing.

When you say that the pullup resistors should be okay, that is not good enough :wink: Can you measure them ? Perhaps they have accidently the wrong value, for example 1k or 100k would be wrong.

The code on this forum that you gave a link to is still not okay.
A "while(!Wire.available())" after a Wire.requestFrom() is not okay. The first version was using Wire.beginTransmission() and Wire.endTransmission() with a Wire.requestFrom(), that can mess up the sensor.
I wrote about the common mistakes and have an alternative explanation of the functions.

At the end of that other thread, someone mentions a "repeated stop". I have never heard of a "repeated stop", I think he means a "repeated start". I can not find in the datasheet that it needs a repeated start. But if it does, the Wire library can do that without problem.

Are you satisfied that you have working code ? Even when it is not okay yet ?

I was planning on pulling apart that code and seeing how it worked, since I had to tweak some parts (like while(!Wire.available())) to get it to entirely work for me. The multiple resets worked back to back with no delay, but I've added delays just in case (50ms should be more than enough). This specific sketch won't get used outside of testing so while(!Serial) is ok for now, as the teensy sometimes moves a bit too fast (either way, good catch).

With the delays, it seems to work better, returning the expected two bytes. I haven't had the time to check any other outputs yet though.