I2C Communication for Mbed Program between Two NUCLEO-WB55RG Boards Fail

eally hope if there is someone have a quick look and give out a solution as soon as possible as the project is going to due very soon.

Through the following code, I can allow sender board sending signal to listener board and have listener print out its received value “master” easily. We differentiate sender and receiver by commenting out the while loop.

#include "mbed.h"

int main(){
    #define SLAVE

    #define SLAVE_ADDR 0xA0
    #ifdef SLAVE
    I2CSlave slave(PB_9, PB_8);

    char buf[10];
    char msg[] = "Slave!";

    slave.address(SLAVE_ADDR);
    while (1) {
        int i = slave.receive();
        switch (i) {
            case I2CSlave::ReadAddressed:
                slave.write(msg, strlen(msg) + 1); // Includes null char
                break;
            case I2CSlave::WriteGeneral:
                slave.read(buf, 10);
                printf("Read G: %s\n", buf);
                break;
            case I2CSlave::WriteAddressed:
                slave.read(buf, 10);
                printf("Read A: %s\n", buf);
                break;
            default:
                printf("nada\n");
        }
        for (int i = 0; i < 10; i++) {
            buf[i] = 0;    // Clear buffer
        }
        wait_us(10);
    }

    #else
    I2C master(PB_9, PB_8);
    I2CSlave slave(PB_9, PB_8);

    char buf[10];
    char msg[] = "Master!";

    while(true){
        master.write(SLAVE_ADDR, msg, sizeof(msg));
        wait_us(10);
    }
    #endif
}

However, when I use the above example and apply it to a larger program, it forms the the following code. The signal transmission no longer works. Satellite doesn’t transmite signals to controller. Through debugging, I found the sender never sends the data. The variable i in the send_data() function is ‘int i = i2c->receive();’ and is always 0. If we dig deeper, the variable “j” in the mian function is found to be 0, which means the slave is not addressible. But we just assign its adress the line before and thus j shouldn’t be 0. I don’t understand why j is 0 in the following code file but not in the previous code file.

Really need help in debugging. Would really appreciate it.

The second code file is the following:

#include <cstdint>
#include <string>
#include <memory>
#include <stdio.h>

#include "mbed.h"
#include "ble/BLE.h"

#include "plant_care_util.hpp"
#include "DataCollector.hpp"
#include "Sensor.hpp"
// #include "BluetoothModules.hpp"

#define SLAVE_ADDR 0xA0

class DutyCycle{
    private:
    PwmOut* output;

    public:
    float frac;
    uint32_t interval;
    std::string name;

    DutyCycle(PinName pin, const char* n, float f = 0.5, uint32_t i = 5){
        output = new PwmOut(pin);
        name = std::string(n);
        frac = f;
        interval = i;

        output->period((float) interval);
        output->write(frac);
    }

    void set_frac(float f){
        frac = f;
        output->write(frac);
    }

    void set_interval(uint32_t i){
        interval = i;
        output->period((float) interval);
    }
};

EventQueue sensor_queue(16 * EVENTS_EVENT_SIZE);

int th_event, l_event;
Sensor* s1;
Sensor* s2;
DigitalOut led(LED1);

int t_adjust_event, l_adjust_event;
DutyCycle* TempDuty;
DutyCycle* LightDuty;

void set_time(char const *date, char const *time){
    // set_time function taken from:
    // https://os.mbed.com/users/joeata2wh/code/compile_time_to_system_time/file/5f3730f44e19/compile_time_to_system_time.h/

    char s_month[5];
    int year;
    struct tm t;
    static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
    sscanf(date, "%s %d %d", s_month, &t.tm_mday, &year);
    sscanf(time, "%2d %*c %2d %*c %2d", &t.tm_hour, &t.tm_min, &t.tm_sec);

    t.tm_mon = (strstr(month_names, s_month) - month_names) / 3;    
    t.tm_year = year - 1900;

    time_t compile_time = mktime(&t);
}

void sense(Sensor* s){
    led = 1;
    s->collect();
    wait_ns(500);
    led = 0;
}

void adjust(DutyCycle* d, int32_t* curr, int32_t* target){
    if(*curr < *target && d->frac <= 0.99){
        d->set_frac(d->frac + 0.01);
    } else if(*curr > *target && d->frac >= 0.01){
        d->set_frac(d->frac - 0.01);
    }
    uint32_t percent = (d->frac * 100);
    printf("%s duty: %d\n", d->name.c_str(), percent);
}

void send_data(I2CSlave* i2c){
    printf("send_data\n");
    // packet current_data;
    // current_data.temp = *((TempHumiditySensor*)s1)->get_temp();
    // current_data.humidity = *((TempHumiditySensor*)s1)->get_humidity();
    // current_data.light = *((LightSensor*)s2)->get_light();

    int i = i2c->receive();
    char msg[] = "banana";
    switch (i) {
        case I2CSlave::ReadAddressed:
            // i2c->write((char*) &current_data, sizeof(packet));
            i2c->write(msg, strlen(msg) + 1);
            wait_us(10);
            break;
        case I2CSlave::WriteGeneral:
            break;
        case I2CSlave::WriteAddressed:
            break;
        default:
            break;
    }
}

void recv_data(int32_t* temp_p, int32_t* light_p, I2C* i2c){
    printf("recv_data\n");

    // char input[sizeof(packet)];
    char input[7];

    i2c->read(SLAVE_ADDR, input, sizeof(input));
    // wait_us(10);

    printf("input: %s\n", input);

    // *temp_p = ((packet*)input)->temp;
    // *light_p = ((packet*)input)->light;

    // printf("received target temp:\t%04x\n", *temp_p);
    // printf("received target light:\t%04x\n", *light_p);
}

// main() runs in its own thread in the OS
int main(){
    // init date and time
    set_time(__DATE__, __TIME__);
    DataCollector* d = new DataCollector();
    I2C sensor_i2c(PB_9, PB_8);

    s1 = new TempHumiditySensor(d, &sensor_i2c);
    s2 = new LightSensor(d, &sensor_i2c);

    th_event = sensor_queue.call_every(5s, sense, s1);
    l_event = sensor_queue.call_every(5s, sense, s2);

#define CONTROLLER

#ifdef CONTROLLER
    TempDuty = new DutyCycle(PA_0, "Temp");
    LightDuty = new DutyCycle(PA_1, "Light");

    int32_t light_target = 100;
    int32_t temp_target = 250;

    t_adjust_event = sensor_queue.call_every(5s, adjust, TempDuty, ((TempHumiditySensor*)s1)->get_temp(), &temp_target);
    l_adjust_event = sensor_queue.call_every(5s, adjust, LightDuty, ((LightSensor*)s2)->get_light(), &light_target);

    auto update_target_event = sensor_queue.call_every(5s, recv_data, &light_target, &temp_target, &sensor_i2c);
#endif

#ifdef SATELLITE
    I2CSlave slave_i2c(PB_9, PB_8);
    slave_i2c.address(SLAVE_ADDR);
    int j = slave_i2c.receive();
    //Problem: j printed out at this line is always 0. 

    auto send_target_event = sensor_queue.call_every(5s, send_data, &slave_i2c);
#endif

    // BLE &ble = BLE::Instance();
    // MyGATTServer server;
    // GattServerProcess ble_process(sensor_queue, ble);
    // ble_process.on_init(callback(&server, &MyGATTServer::start));
    // ble_process.start();

    sensor_queue.dispatch_forever();

    while (true) {

    }
}

Hi there,

If the example is working then must be a collision in your project.
You have initialized I2C and I2CSlave on same I2c interface. I am not sure that is possible or good practice. There can be also a collision during clock generation. Probably best will be two separate I2C interfaces, one for each role.

In the example is the I2C::receive() method in a while loop, because that method probably read a status flag, standard polling process. You read it once per five seconds, in your code, so you can miss the right time especially when you do another transactions on same bus.

BR, Jan

Thanks for your response. So do you have any suggestions in modifying the code?