1

The setup is a raspberryPi driving two AdaFruit DRV2605s through an AdaFruit TCA i2c multiplexer and it all works correctly the bulk of the time. I adapted the sample Arduino code and by and large we're all good.

Periodically, I will receive an error writing to the DRV2605 with no apparent pattern so far. I assumed it was a timing error and would require sleep time between writes, but I'm not sure what the range I should be expecting is in there. Adding sleeps up to 100 millis between writing to the two channels does not seem to make a difference.

The error happens in writeRegister8() and often fails up to the maximum retry count which I've run up to 50 with longer sleeps, to no avail.

I'm looking for a better way to recover from the error would be after a failed write or what could be causing the error to preempt it?

Sample output:

 i2c setting address: [5a] on socket [3]
    write8:  reg: 0x2 val: 0x0
 Error - errno is: [79][Remote I/O error] reg: 0x2 val: 0x0
 retry attempt 0 reg: 0x2 val: 0x0
 retry: 0 error - errno is: [79][Remote I/O error] reg: 0x2 val: 0x0
 retry attempt 1 reg: 0x2 val: 0x0
 retry: 1 error - errno is: [79][Remote I/O error] reg: 0x2 val: 0x0
 retry attempt 2 reg: 0x2 val: 0x0
 retry: 2 error - errno is: [79][Remote I/O error] reg: 0x2 val: 0x0
 retry attempt 3 reg: 0x2 val: 0x0
 retry: 3 error - errno is: [79][Remote I/O error] reg: 0x2 val: 0x0
 retry attempt 4 reg: 0x2 val: 0x0
 retry: 4 error - errno is: [79][Remote I/O error] reg: 0x2 val: 0x0
 done write8:  fd: 0x3 reg: 0x2 val: 0x0

Code to reproduce the problem, using wiringPi and adapted from the Arduino sample code.

#include <stdio.h>
#include <thread>
#include <iostream>
#include <errno.h>
#include <string.h>
#include <bitset>
#include <sys/ioctl.h>
#include <wiringPiI2C.h>


static const uint8_t DRV_ADDR = 0x5A;
static const uint8_t TCA_ADDR = 0x70;

static const uint8_t REG_STATUS = 0x00;
static const uint8_t REG_MODE = 0x01;
static const uint8_t MODE_INTTRIG = 0x00;
static const uint8_t MODE_EXTTRIGEDGE = 0x01;
static const uint8_t MODE_EXTTRIGLVL = 0x02;
static const uint8_t MODE_PWMANALOG = 0x03;
static const uint8_t MODE_AUDIOVIBE = 0x04;
static const uint8_t MODE_REALTIME = 0x05;
static const uint8_t MODE_DIAGNOS = 0x06;
static const uint8_t MODE_AUTOCAL = 0x07;

static const uint8_t REG_RTPIN = 0x02;
static const uint8_t REG_LIBRARY = 0x03;
static const uint8_t REG_WAVESEQ1 = 0x04;
static const uint8_t REG_WAVESEQ2 = 0x05;
static const uint8_t REG_WAVESEQ3 = 0x06;
static const uint8_t REG_WAVESEQ4 = 0x07;
static const uint8_t REG_WAVESEQ5 = 0x08;
static const uint8_t REG_WAVESEQ6 = 0x09;
static const uint8_t REG_WAVESEQ7 = 0x0A;
static const uint8_t REG_WAVESEQ8 = 0x0B;

static const uint8_t REG_GO = 0x0C;
static const uint8_t REG_OVERDRIVE = 0x0D;
static const uint8_t REG_SUSTAINPOS = 0x0E;
static const uint8_t REG_SUSTAINNEG = 0x0F;
static const uint8_t REG_BREAK = 0x10;
static const uint8_t REG_AUDIOCTRL = 0x11;
static const uint8_t REG_AUDIOLVL = 0x12;
static const uint8_t REG_AUDIOMAX = 0x13;
static const uint8_t REG_RATEDV = 0x16;
static const uint8_t REG_CLAMPV = 0x17;
static const uint8_t REG_AUTOCALCOMP = 0x18;
static const uint8_t REG_AUTOCALEMP = 0x19;
static const uint8_t REG_FEEDBACK = 0x1A;
static const uint8_t REG_CONTROL1 = 0x1B;
static const uint8_t REG_CONTROL2 = 0x1C;
static const uint8_t REG_CONTROL3 = 0x1D;
static const uint8_t REG_CONTROL4 = 0x1E;
static const uint8_t REG_VBAT = 0x21;
static const uint8_t REG_LRARESON = 0x22;

static const uint8_t REG_TCA_SELECTOR = 0x04;


void calibrate(int32_t fd)
{
    uint8_t inputs = 0;
//    inputs |= 
}

int32_t readRegister8(int32_t fd, uint8_t reg)
{
    uint8_t x = wiringPiI2CReadReg8(fd, reg);
    if (x == -1) {
        int32_t errsv = errno;
        std::cout << "Error READ - errno is: ["
                                           << errsv << "]["
                                           << strerror(errsv) << "]"
                                           << " reg: 0x" << std::hex << static_cast<int32_t>(reg) << std::endl;
    }
    return x;
}

void writeRegister8(int32_t fd, uint8_t reg, uint8_t val)
{
    std::cout << "===================================================" << std::endl;
    std::cout << "      write8: "
              << " reg: 0x" << std::hex << static_cast<int32_t>(reg)
              << " val: 0x" << std::hex << static_cast<int32_t>(val) << std::endl;

    // use i2c
    if (wiringPiI2CWriteReg8(fd, reg, val) < 0)
    {
        int32_t errsv = errno;
        std::cout << "Error - errno is: ["
                                      << errsv << "]["
                                      << strerror(errsv) << "]"
                                      << " reg: 0x" << std::hex << static_cast<int32_t>(reg)
                                      << " val: 0x" << std::hex << static_cast<int32_t>(val) << std::endl;

        /* try again a couple times */
        for (int i=0; i<5; ++i) {
            std::cout << "retry attempt " << i
                                    << " reg: 0x" << std::hex << static_cast<int32_t>(reg)
                                    << " val: 0x" << std::hex << static_cast<int32_t>(val) << std::endl;
            std::this_thread::sleep_for(std::chrono::microseconds(25000));
            if (wiringPiI2CWriteReg8(fd, reg, val) < 0) { break; }
            else {
                std::cout << "retry: " << i << " error - errno is: ["
                                              << errsv << "][" << strerror(errsv) << "]"
                                              << " reg: 0x" << std::hex << static_cast<int32_t>(reg)
                                              << " val: 0x" << std::hex << static_cast<int32_t>(val) << std::endl;
            }
        }
    }
    std::cout << " done write8: "
            << " fd: 0x" << std::hex << static_cast<int32_t>(fd)
            << " reg: 0x" << std::hex << static_cast<int32_t>(reg)
            << " val: 0x" << std::hex << static_cast<int32_t>(val) << std::endl;
}

int32_t setAddress(int32_t fd, int32_t address)
{
    std::cout << "\t i2c setting address: [" << std::hex << address << "] on socket [" << fd << "]" << std::endl;
    if (ioctl(fd, 0x0703/*I2C_SLAVE*/, address) < 0)
    {
        int32_t errsv = errno;
        std::cout << "Error setting address [" << std::hex << address << "] err: [" << strerror(errsv) << "]" << std::endl;
    }
}

void tcaSelect(int32_t fd, uint8_t idx)
{
    setAddress(fd, TCA_ADDR);
    if (idx > 7) {
        std::cout << "Invalid addr passed to tcaselect: " << idx << std::endl;
        return;
    }

    std::cout << "i2c multiplex select: [" << static_cast<int32_t>(idx) << "]" << std::endl;
    writeRegister8(fd, 0x04, 1 << idx);
    setAddress(fd, DRV_ADDR);
}

void tcaReset(int32_t fd)
{
    std::cout << "i2c multiplex reset" << std::endl;
    setAddress(fd, TCA_ADDR);
    writeRegister8(fd, 0x04, 0);
    setAddress(fd, DRV_ADDR);
}

void initialize(int32_t fd_tca, int32_t fd_drv, int32_t channel)
{
    tcaSelect(fd_tca, channel);
    writeRegister8(fd_drv, REG_MODE, 0x00); // out of standby
    writeRegister8(fd_drv, REG_RTPIN, 0x00); // no real-time-playback

    writeRegister8(fd_drv, REG_OVERDRIVE, 0); // no overdrive

    writeRegister8(fd_drv, REG_SUSTAINPOS, 0);
    writeRegister8(fd_drv, REG_SUSTAINNEG, 0);
    writeRegister8(fd_drv, REG_BREAK, 0);
    writeRegister8(fd_drv, REG_AUDIOMAX, 0x64);

    // turn off N_ERM_LRA
    writeRegister8(fd_drv, REG_FEEDBACK, readRegister8(fd_drv, REG_FEEDBACK) & 0x7F);
    // turn on ERM_OPEN_LOOP
    writeRegister8(fd_drv, REG_CONTROL3, readRegister8(fd_drv, REG_CONTROL3) | 0x20);

    writeRegister8(fd_drv, REG_LIBRARY, 1);
    writeRegister8(fd_drv, REG_MODE, MODE_REALTIME);
}

void set_buzz(int32_t fd_tca, int32_t fd_drv, int32_t channel)
{
    uint8_t reg_value = 0;

    tcaSelect(fd_tca, channel);
    writeRegister8(fd_drv, REG_RTPIN, 118);
}

void stop_buzz(int32_t fd_tca, int32_t fd_drv, int32_t channel)
{
    uint8_t reg_value = 0;

    tcaSelect(fd_tca, channel);
    writeRegister8(fd_drv, REG_RTPIN, 0);
}

void diagnostic(int32_t fd_tca, int32_t fd_drv, int32_t channel)
{
    tcaSelect(fd_tca, channel);

    // Diagnostic
    writeRegister8(fd_drv, REG_MODE, 0x00); // out of standby
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    writeRegister8(fd_drv, REG_MODE, 0x06); // diagnostic
    writeRegister8(fd_drv, REG_GO, 1);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    uint8_t diagnostic = readRegister8(fd_drv, REG_STATUS);
    writeRegister8(fd_drv, REG_MODE, 0x00); // out of standby


    std::cout << "Diagnostic on channel[" << channel << "] ==> " << std::bitset<8>(diagnostic) << std::endl;
}


int main()
{
    int fd_tca = -1;
    int fd_drv = -1;

    fd_tca = wiringPiI2CSetup(DRV_ADDR);
    fd_drv = fd_tca;

    diagnostic(fd_tca, fd_drv, 1);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    diagnostic(fd_tca, fd_drv, 2);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    initialize(fd_tca, fd_drv, 1);
    initialize(fd_tca, fd_drv, 2);

    /* start playing */
    for (int i=0; i<15; ++i) {
        /* start */
        set_buzz(fd_tca, fd_drv, 1);
        set_buzz(fd_tca, fd_drv, 2);

        std::this_thread::sleep_for(std::chrono::milliseconds(50));

        /* stop */
        stop_buzz(fd_tca, fd_drv, 1);
        stop_buzz(fd_tca, fd_drv, 2);

        std::this_thread::sleep_for(std::chrono::milliseconds(2000));

        std::cout << "Iteration " << std::dec << i << std::endl;
    }

    tcaReset(fd_tca);
}

UPDATE: Worth noting that if I only trigger a single channel (either 1 or 2) of the multiplexer, I will never see the error. So changing the contents of the inner loop to the following results in runs with no errors:

set_buzz(fd_tca, fd_drv, 1);

std::this_thread::sleep_for(std::chrono::milliseconds(50));

/* stop */
stop_buzz(fd_tca, fd_drv, 1);
  • 2
    I2C IO errors are generally poor wiring so that would be the first thing to double check. – joan Aug 29 '18 at 14:17
  • 1
    Thanks Joan. I feel fairly confident that that's not the issue (definitely not 100% sure though) is that is not the issue, because if I run the same sample, but only send to one of the two channels (still using the multiplexer) I won't ever see the error. – Jmc Aug 29 '18 at 14:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.