Custom F446RE board fails with both reads and writes to I2C

I have the following code that both reads and parses through can bus messages, while also checking temperatures and humidity levels with a SHT 4XI sensor. The board turns on, reads and writes can bus messages fine but the sensor (which communicates via I2C) does not give any readout. When I attach an Arduino Uno with verified code it does read out on my custom board so the hardware should be fine. The main code is as follows:

#include "CAN.h"
#include "mbed.h"
#include "SHT40 Class.h"
    int oldmilis = 200;
    int milis = 0; 
    int i2caddress = 0x44;
    

    I2C i2c(PC_7, PC_6);

    SHT40 sht40;    
    namespace mbed
{
	FileHandle *mbed_override_console(int fd)
	{
	    static BufferedSerial console(USBTX, USBRX, MBED_CONF_PLATFORM_STDIO_BAUD_RATE);
		return &console;
	}
}

int main()
{
    // printf("CAN Nucleo-F767ZI\n");
    printf("CAN Datahub\n");
    i2c.frequency(100000);

    CAN can(PB_8, PB_9,500000);  
    CAN can2(PB_5, PB_6,500000);  
    DigitalOut led1(PC_8);    
    CANMessage msg1;
    CANMessage msg2;
    if(can.filter(0x03FE,0x03FF,CANStandard)){
        printf("Can 1 Filter applied\n");
    }
    if(can2.filter(0x03FF,0x03FF,CANStandard)){
        printf("Can 2 Filter applied\n");
    }

    // trigger first read of opposite board
    if (can.write(msg1));
    // if (can2.write(CANMessage(1333, &counter, 1))) counter++;

    while (1) 
    {
        sht40.sampleHigh();

        if (can.read(msg1)) {
            printf("Received CAN 1: %d\n", msg1.data[0]);
            ThisThread::sleep_for(500ms);
            if (can2.write(msg1)){
            printf("Sent to CAN 2: %d\n",msg1.data[0]);
            led1 = !led1;
            }
        }
        if (can2.read(msg2)) {
            printf("Received CAN 2: %d\n", msg2.data[0]);
            ThisThread::sleep_for(500ms);
            if (can.write(msg2)){
            printf("Sent to CAN 1: %d\n",msg2.data[0]);
            led1 = !led1;
        }

       if(oldmilis > milis){
        led1 = !led1;
        float Temp = sht40.getTemperature();
        float Hum = sht40.getHumidity();
        printf("Temperature: %d\n", Temp);
        printf("Humidity : %d\n",Hum);
        milis = 0;
        }

    milis++;

    }
  
}
}


// CLASS SHT40 /////////////////////////////////////////////////////////////////
void SHT40::sampleLow(){
    i2c.start();
    ThisThread::sleep_for(10ms);
if(i2c.write(0xE0)==1){        //Set: Read low precision (0xE0)
  i2c.read(i2caddress,SHT_DLC, 6);    // request 6 bytes from slave device #8
    }
    i2c.stop();
}


void SHT40::sampleHigh(){
    i2c.start();
    ThisThread::sleep_for(10ms);
    if(i2c.write(SHT_address[0],"0xFD",1)==0){
        printf("write succesful\n");
    }

    if(i2c.write(0xFD)!=0){
        printf("Faulty write\n");
    }      //Set: Read low precision (0xE0)
    i2c.stop();

    i2c.read(i2caddress,SHT_DLC,6);    // request 6 bytes from slave device #8
    }



float SHT40::getTemperature(){
  uint16_t Temp =  SHT_DLC[0] * 256 + SHT_DLC[1];
  return Temp;
}
float SHT40::getHumidity(){
  uint16_t Temp =  SHT_DLC[3] * 256 + SHT_DLC[4];
  return Temp;
}

I have used the following code to check if the known address is even accessible; but I get all NACK’s back. If I run the same address checking code with Arduino it does pick up the address I’m expecting to find.
This is the code;

#include "mbed.h"

BufferedSerial pc(USBTX, USBRX,9600);

 #define D_SDA                  PB_9 
 #define D_SCL                  PB_8 
   // sda=PB7, scl=PB_6 Pins specific to Nucleo-F303K8
  // must change pins to match your board.

I2C i2c(D_SDA, D_SCL);  

  
DigitalOut myled(PC_8);
 
int ack;   
int address;  
void scanI2C() {
  for(address=1;address<127;address++) {    
    ack = i2c.write(address, "0x94", 1);
    if (ack == 0) {
       printf("\tFound at %3d -- %3x\r\n", address,address);
    }    
    if(ack != 0){
        printf("I2C Error: %d\n", ack);
    }
    ThisThread::sleep_for(5ms);
  } 
}
 
int main() {
i2c.frequency(100000);
  printf("I2C scanner \r\n");
  scanI2C();
  printf("Finished Scan\r\n");
  // just blink to let us know the CPU is alive 
  while(1) { 
      ThisThread::sleep_for(1s);        
      myled = !myled;
  }
}
 

I have no Idea what to do know, it seems to me this must be a software Issue as when I connect the Arduino to a header connected to the SDA & SCK traces it works perfectly fine. But I have no clue what to do from here. Hopefully any of you can help out…

Kind regards,
Ties

Hello,

Most Arduino users just copy&paste an example from internet and then just edit few things. Thanks to that they do not understand basics.

Your i2c address have to be bitshifted;

#define I2C_SHT40_ADDRESS 0x44<<1
//or
int i2caddress = 0x44<<1;

// 0x44h in binary 0010 1100b
// 0x88h in binary 0101 1000b
// 0x44h << 1 = 0x88h

If you do not do it like that then you need to do it in every read/write call.

You also can not use I2C methods like below

void SHT40::sampleLow(){
  i2c.start();
  ThisThread::sleep_for(10ms);
  if(i2c.write(0xE0)==1){        //Set: Read low precision (0xE0)
     i2c.read(i2caddress,SHT_DLC, 6);    // request 6 bytes from slave device #8
  }
  i2c.stop();
}

Overloaded method I2C::write() has two variants, so you can not combinate it like you did.

  • first one is for single byte transactions and Start/Stop have to be handled by you.
  • second one is for complet transaction and everything is managed by that method.

So just simple command should look like this with first method

  i2c.start();
  i2c.write(address); // i2c address bitshifted to left by 1
  i2c.write(0xE0);
  i2c.stop();

But much more better is with second one

void SHT40::sampleLow(){
  if(i2c.write(i2caddress,0xE0,1)==0){        //Set: Read low precision (0xE0)
     i2c.read(i2caddress,SHT_DLC, 6);    // request 6 bytes from slave device #8
  }
}

About the scaner function.
Like above the address have to be bitshifted.

void scanI2C() {
  for(int address=1;address<127;address++) {    
    int ack = i2c.write(address<<1, "0x94", 1); // address should be bitshifted
    if (ack == 0) {
       printf("\tFound at %3d - in 7bit %3x\r\n", address, address <<1);
    }    
    if(ack != 0){
        printf("I2C Error: %d\n", ack);
    }
    ThisThread::sleep_for(5ms);
  } 
}

BR, Jan

Hi Jan,

If I try to use this code:

void SHT40::sampleLow(){
  if(i2c.write(i2caddress,0xE0,1)==0){        //Set: Read low precision (0xE0)
     i2c.read(i2caddress,SHT_DLC, 6);    // request 6 bytes from slave device #8
  }
}

it gives me an error with the data, as it is of type int and not const char. Any Idea how I should fix this? I don’t understand how the data is actually transmitted if it isn’t an int.

KR,
Ties

Oh, ok my bad.

        const char data = 0xE0;
        if(i2c.write(i2caddress,&data,1)==0){        //Set: Read low precision (0xE0)
           i2c.read(i2caddress,SHT_DLC, 6);    // request 6 bytes from slave device #8
        }

or with array if you will need send more than one byte in the future

        char strdata[] = {0xE0};
        if(i2c.write(i2caddress,strdata,1)==0){        //Set: Read low precision (0xE0)
            i2c.read(i2caddress,SHT_DLC, 6);    // request 6 bytes from slave device #8
        }

Do not forget bitshift the address like I wrote in previous post.
You have to do it in your global address variable int i2caddress = 0x44<<1; or directly in read/write methods.

BR, Jan

Awesome, I did infact fix it the same way as you described in the end but now I know it wasn’t just because of a misunderstanding on my part. I do however get %f in my printf result instead of the actual value tho. any clue what could cause it? There is communication on the bus when I check it with an oscilloscope. This is the code:

int main() {
i2c.frequency(100000);
  printf("I2C Readout \r\n");
  scanI2C();
  // just blink to let us know the CPU is alive 
  while(1) { 
      ThisThread::sleep_for(1s);        
      myled = !myled;

        sht40.sampleLow();
  float temp = sht40.getTemperature();
  float hum = sht40.getHumidity();
  printf("Temp: %d & Humidity: %d\n",temp,hum);
  printf("Finished Readout\r\n");
  }}
 

*Edit: The %d should be %f, I tried it with both and with an int it says 0; with %f it just prints %f

The %f is caused by project settings. Mbed OS has disabled floats in default for the printf for memory saving.
You have to create (if you do not have it already) or edit mbed_app.json file in root folder of your project and fill or add this part

{
        "target_overrides": {
        "*": {
            "target.printf_lib": "std"
        }
    }
}

BR, Jan

That doesn’t seem to work, tho I don’t really need it anyway as int gives me at least an idea if it is recieving anything.

Currently when I am running the scanner it does indeed pick up the expected adress so thats awesome! The weird thing is that when I try to run the data write/read part of the temp and humidity it looks like the data pin is active while the clock pin is silent. Presumably this is also the reason why I am not getting the sensor data. Any Idea what could be causing this? I checked with my Arduino setup and the clock has to be generated by the master, so it isn’t the sensor.

The scanner sending one dummy byte to an address (addess byte + dummy byte = 2 bytes in total) if the address is correct the slave will respond with ACK. This is not possible without clock signal. The clock signal starting immediately after the start condition on data line and then are two bytes send (in case of scanner).

BR, Jan

Hi Jan,

Currently I have this piece of code running:

int main() {
i2c.frequency(100000);
  printf("I2C Readout \r\n");
  scanI2C();
  // just blink to let us know the CPU is alive 
  while(1) { 
      ThisThread::sleep_for(1s);      
      myled = !myled;

        sht40.sampleLow();
  float temp = sht40.getTemperature();
  float hum = sht40.getHumidity();
  printf("Temp: %d & Humidity: %d\n",temp,hum);
  printf("Finished Readout\r\n");
  }}

And it reads an address successfully with the scan, but then fails on the write when I try to sent a command to the same address that the scan finds. I am bitshifting the addresses to 8 bit everytime though I have also tried without bitshift just in case and neither work. Any Idea what could be the problem?

KR,
Ties

Try to modified the sampleLow like this

void SHT40::sampleLow(){
  const char data = 0xE0;
  if(i2c.write(i2caddress,&data,1)==0){       //Set: Read low precision (0xE0)
     printf("sent\n");
     thread_sleep_for(50);
     i2c.read(i2caddress,SHT_DLC, 6);    // request 6 bytes from slave device #8
  }
}

I also found this library SHT40 - Adafruit SHT40 basic driver | Mbed

BR, Jan