I2C Setup not working for STS40 Sensor on Nordic nrf52840Dongle

I am a beginner in riot os and trying to implement a i2c compatible sensor for a project, however, i am not getting past the situation of using generic driver provided by the sensirion to riot compatible. Any help regarding obvious issues and directions are much appreciated.

firstly i have downloaded the embedded codes provided by sensirion from https://github.com/Sensirion/embedded-i2c-sts4x to this folder /home/RIOT/sys/sensirion

In this folder i have :

  1. makefile
MODULE = sensirion
include $(RIOTBASE)/Makefile.base
  1. sensirion_common.h and c
  2. sensirion_config.h
  3. sensirion_i2c_hal.h and .c
  4. sensirion_i2c.h and .c
  5. sts4x_i2c.h and .c

Then i have written the implementation code for sensirion_i2c_hal.h in sensirion_i2c_hal.c file. [Note: other files e.g., header file and .C file i didn’t modify as it didn’t require it ]

#include "periph/i2c.h"
#include "xtimer.h"
#include "sensirion_i2c_hal.h"
#include "sensirion_common.h"
#include "sensirion_config.h"


/* int16_t sensirion_i2c_hal_select_bus(uint8_t bus_idx) {
    // No need to implement for single I2C bus
    return 0;
}*/

void sensirion_i2c_hal_init(void) {
    // Initialize the I2C device (bus 0 for nRF52840 Dongle)
    i2c_acquire(I2C_DEV(0));
}

void sensirion_i2c_hal_free(void) {
    // Release the I2C device (bus 0)
    i2c_release(I2C_DEV(0));
}

int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) {
    int res;

    // Acquire the I2C device
    i2c_acquire(I2C_DEV(0));

    // Perform the I2C read operation
    res = i2c_read_bytes(I2C_DEV(1), address, data, count, 0);

    // Release the I2C device
    i2c_release(I2C_DEV(0));

    return (res == count) ? 0 : -1;
}

int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, uint16_t count) {
    int res;

    // Acquire the I2C device
    i2c_acquire(I2C_DEV(0));

    // Perform the I2C write operation
    res = i2c_write_bytes(I2C_DEV(1), address, data, count, 0);

    // Release the I2C device
    i2c_release(I2C_DEV(0));

    return (res == count) ? 0 : -1;
}

void sensirion_i2c_hal_sleep_usec(uint32_t useconds) {
    // Sleep 
    xtimer_usleep(useconds);
}

Then, I have changed/Modified #include “periph_conf.h” file which always redirects to nrf52840dk folder but i am using nrf52840 dongle, so i modified both with desired pin:

/**
 * @name    I2C configuration
 * @{
 */
static const i2c_conf_t i2c_config[] = {
    {
        .dev = NRF_TWIM0,
        .scl = GPIO_PIN(0, 20),
        .sda = GPIO_PIN(0, 22),
        .speed = I2C_SPEED_NORMAL
    }
};
#define I2C_NUMOF           ARRAY_SIZE(i2c_config)
/** @} */

finally the main.c file implementation

#include <stdio.h>
#include <string.h>
#include "xtimer.h"

#include "periph_conf.h"
#include "periph/i2c.h"
#include "periph/gpio.h"

#include "sensirion_common.h"
#include "sensirion_config.h"

#include "sensirion_i2c_hal.h"
#include "sensirion_i2c.h"
#include "sts4x_i2c.h"


int main(void) {
    puts("STS40 check");
    // Initialize the I2C HAL 
    sensirion_i2c_hal_init();
    printf("STS40 check1");
    // Initialize the STS4x driver
    init_driver(ADDR_STS4X);
    printf("STS40 check2");
    // Read the serial number
    uint32_t serial_number;
    int16_t error = sts4x_serial_number(&serial_number);
    if (error) {
        printf("Error reading serial number: %d\n", error);
    } else {
        printf("Sensor serial number: %lu\n", serial_number);
    }
    printf("STS40 check3");
    // Start measurement 
    while (1) {
        int32_t temperature;
        error = sts4x_measure_high_precision(&temperature);
        if (error) {
            printf("Error reading temperature: %d\n", error);
        } else {
            printf("Temperature: %.1f °C\n", temperature / 1000.0f);
        }
        // Sleep for 1 second between measurements
        xtimer_sleep(1);
    }

    return 0;
}

and makefile

Name of your application

APPLICATION = i2c_test

# If no BOARD is found in the environment, use this default:

BOARD = nrf52840dongle

# This has to be the absolute path to the RIOT base directory

RIOTBASE ?= $(CURDIR)/../..

# Include the header files directory

INCLUDES += -I/home/my_home/RIOT/sys/sensirion

# Use I2C module from RIOT

USEMODULE += xtimer

USEMODULE += periph_i2c

USEMODULE += periph_gpio

USEMODULE += sensirion

FEATURES_REQUIRED += periph_i2c

CFLAGS += -DLOG_LEVEL=LOG_DEBUG

DEVELHELP ?= 1

# Change this to 0 to show compiler invocation lines by default:

QUIET ?= 1

# Include the main RIOT build system

include $(RIOTBASE)/Makefile.include

After all these i was expecting it would somehow work but it the code compiled all okay and outputs only until the sensirion_i2c_hal_init(); , that i am assuming my i2c initialization is wrong, and the whole code provided by sensirion somehow is not compatible the way i am implementing.

output:

2024-09-23 13:11:38,513 # main(): This is RIOT! (Version: 2024.10-devel-154-g821a7e)
2024-09-23 13:11:38,513 # STS40 check

I would appreciate any clue on this situation.

A little correction, in the code that i pasted for sensirion_i2c_hal.c , all the I2c reference is I2C_DEV(0), some of the references still I2C_DEV(1) , Because i have changed around from 0 to 1, and when it didn’t work either, i changed back to 0, but forgot some of them when i pasted the code in this discussion.

You are calling i2c_acquire() twice - once in sensirion_i2c_hal_init() and afterwards in sensirion_i2c_hal_write()/sensirion_i2c_hal_read().

i2c_acquire() will acquire a mutex to ensure exclusive access on the I2C bus, so the second call will block indefinitely.