Software I2C via PlatformIO (Arduino Nano BLE 33)

Hello!

I have trouble to use Software I2C connections on my Arduino Nano BLE 33. I’ve tried the Softwire.h - Library with PlatformIO (and Arduino IDE), but I can’t find any I2C devices (even, the code compiled without errors).

Now, I found the SoftwareI2C.h (GitHub - micooke/SoftwareI2C: Software I2C library for Arduino (Not architecture specific) from Seeed Studio), which works very well on my Arduino Nano BLE 33 (I can find my two I2C devices on pin 6 and 7) - BUT only by compiling the script with the Arduino IDE. By compiling the same script with the same lib’s in PlatformIO, i can’t find any devices (even the script compiles without errors).

The script I used:

#define USE_SOFTWAREI2C
#define SOFTI2C_PIN_SDA 6
#define SOFTI2C_PIN_SCL 7
#ifdef USE_SOFTWAREI2C
#include <Arduino.h>
#include <SoftwareI2C.h>  // https://github.com/micooke/SoftwareI2C

SoftwareI2C _i2c;
#else
#include <Wire.h>
#define _i2c Wire
#endif

#include <i2c_device_list.h> //adds 7952 bytes, so comment it out if you dont need it

bool device_found(uint8_t i2c_address)
{
  _i2c.beginTransmission(i2c_address);
  uint8_t device_found = (_i2c.endTransmission() == 0);

  return device_found;
}

void setup()
{
    Serial.begin(9600);
    Serial.println(__FILE__);
    Serial.println(__TIME__);
    #ifdef USE_SOFTWAREI2C
    Serial.print(F("Using SoftwareI2C. SDA,SCL = "));
    Serial.print(SOFTI2C_PIN_SDA);
    Serial.print(F(","));
    Serial.println(SOFTI2C_PIN_SCL);
    _i2c.init(SOFTI2C_PIN_SDA, SOFTI2C_PIN_SCL); // sda, scl
    #else
    Serial.println(F("Using Wire"));
    #endif
    _i2c.begin();

}

void loop() {

Serial.println(F("Scanning..."));
uint8_t numDevicesFound = 0;

// if there is something found at 0x74 then there is an error with the sda/scl setup
// there shouldnt be anything here - its reserved
if (device_found(0x7F) == false) 
{
    for(uint8_t address=1; address<128; ++address)
    {
        bool deviceFound = device_found(address);

        if(deviceFound)
        {
            
            Serial.print(F("Found : 0x"));
            if (address < 16) Serial.print(F("0"));
            Serial.println(address, HEX);
            #ifdef I2C_DEVICE_LIST_H
            Serial.println(F("which could be one of the following devices"));
            Serial.println(F("--------"));
            String device_name = i2c_device_list(address);
            Serial.println(device_name.c_str());
            Serial.println(F("--------"));
            #endif
            
            ++numDevicesFound;
        }
    }
}

if (!numDevicesFound)
{
  Serial.println(F("No I2C devices found"));
}
delay(4000);

}

And my platfomio.ini:

env:nano33ble]
platform = nordicnrf52
board = nano33ble
framework = arduino

Can someone give me a hint, how to use Software I2C with PlatformIO?

There are some warning, when I compile with Arduino IDE or PlatformIO:

In file included from src\main.cpp:6:
src/SoftwareI2C.h: In constructor 'SoftwareI2C::SoftwareI2C(uint8_t, uint8_t)':
src/SoftwareI2C.h:48:13: warning: 'SoftwareI2C::_pinScl' will be initialized after [-Wreorder]
     uint8_t _pinScl;
             ^~~~~~~
src/SoftwareI2C.h:47:13: warning:   'uint8_t SoftwareI2C::_pinSda' [-Wreorder]
     uint8_t _pinSda;
             ^~~~~~~
src/SoftwareI2C.h:85:5: warning:   when initialized here [-Wreorder]
     SoftwareI2C(uint8_t pinSda = 7, uint8_t pinScl = 8) : _pinScl(pinScl), _pinSda(pinSda),     _transmissionBegun(false), _i2c_address(0x3C) { setClock(100000); }
     ^~~~~~~~~~~
src/SoftwareI2C.h:57:10: warning: 'SoftwareI2C::_transmissionBegun' will be initialized after     [-Wreorder]
     bool _transmissionBegun;
          ^~~~~~~~~~~~~~~~~~
src/SoftwareI2C.h:55:13: warning:   'uint8_t SoftwareI2C::_i2c_address' [-Wreorder]
     uint8_t _i2c_address;
             ^~~~~~~~~~~~
src/SoftwareI2C.h:85:5: warning:   when initialized here [-Wreorder]
     SoftwareI2C(uint8_t pinSda = 7, uint8_t pinScl = 8) : _pinScl(pinScl), _pinSda(pinSda),     _transmissionBegun(false), _i2c_address(0x3C) { setClock(100000); }
     ^~~~~~~~~~~

Maybe this could help, to solve the problem?

Thanks a lot!
Klaus

Hello again.
Now I used the library BitBang_I2C to communicate via I2C on Pin 6 and 7. This library also works with the Arduino IDE (compiles and find my I2C devices). When I import the exact same files to PlatformIO, I can’t find any I2C devices (but the script compiles without errors).

This is how it looks like with my Logic-adapter, when I compile with Arduino IDE and use a I2C Scanner:

The second image (with PIO compiler) is in the next post (because i’m a new user).

Concluding: There are some SDA SCL signals with the PlatformIO compiler, but they are different compared to the first image (Arduino IDE compiler).

So what is wrong with PlatformIO? Both libraries (Software_I2C and BitBang_I2C) have trouble by using Software I2C?
I would be glad, if someone can help me with this error!

And this image is with the PlatformIO compiler (same script):

I believe these warnings come up because you are initialising the class variables in a different order to that which they were defined.

Try swapping the first two around in the following line in your constructor. That should get rid of the warnings.

I don’t see why a warning should be raised, but I’m no guru!

Unfortunately, I don’t know what your I2C problem is.

I assume you have tried scanning for devices using wire? Theres an example sketch in the Arduino IDE, under TWI (might be under Wire). Does that work? Import into PlatformIO and try again.

At least then you’ll know whether the problem is in SoftwareI2C or not.

Cheers,
Norm.

Hey Norm,

thanks so much for replying! I can get rid of some warnings by changing the pins:

src/SoftwareI2C.h: In constructor 'SoftwareI2C::SoftwareI2C(uint8_t, uint8_t)':
src/SoftwareI2C.h:57:10: warning: 'SoftwareI2C::_transmissionBegun' will be initialized after [-Wreorder]
     bool _transmissionBegun;
      ^~~~~~~~~~~~~~~~~~
src/SoftwareI2C.h:55:13: warning:   'uint8_t SoftwareI2C::_i2c_address' [-Wreorder]
     uint8_t _i2c_address;
         ^~~~~~~~~~~~
src/SoftwareI2C.h:85:5: warning:   when initialized here [-Wreorder]
     SoftwareI2C(uint8_t pinSda = 7, uint8_t pinScl = 8) : _pinSda(pinSda), _pinScl(pinScl),      _transmissionBegun(false), _i2c_address(0x3C) { setClock(100000); }
     ^~~~~~~~~~~
In file included from src\SoftwareI2C.cpp:25:
src\SoftwareI2C.h: In constructor 'SoftwareI2C::SoftwareI2C(uint8_t, uint8_t)':
src\SoftwareI2C.h:57:10: warning: 'SoftwareI2C::_transmissionBegun' will be initialized after     [-Wreorder]
     bool _transmissionBegun;
          ^~~~~~~~~~~~~~~~~~
src\SoftwareI2C.h:55:13: warning:   'uint8_t SoftwareI2C::_i2c_address' [-Wreorder]
     uint8_t _i2c_address;
             ^~~~~~~~~~~~
src\SoftwareI2C.h:85:5: warning:   when initialized here [-Wreorder]
     SoftwareI2C(uint8_t pinSda = 7, uint8_t pinScl = 8) : _pinSda(pinSda), _pinScl(pinScl),          _transmissionBegun(false), _i2c_address(0x3C) { setClock(100000); }
     ^~~~~~~~~~~
In file included from C:/Users/LB/Documents/PlatformIO/Projects/200901-194355-nano33ble    /src/SoftwareI2C_Scan2.ino:14:
src/i2c_device_list.h: In function 'arduino::String i2c_device_list(uint8_t)':
src/i2c_device_list.h:292:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^

But the I2C problem still remains… Yes, I already tried the normal wire library with pin A4 and A5 and this is working well. But i need to use pin D6 and D7 for SDA and SCL…

I think I found the header file you are using at SoftwareI2C/SoftwareI2C.h at master · micooke/SoftwareI2C · GitHub.

The initialisation needs to be this order: _pinSda, _pinScl, _i2c_address, _transmissionBegun. That’s the order they are defined in the class.

In I2C/TWI, addresses 0x00 through 0x07 and 0x78 through 0x7F are reserved 7 bit addresses. Just FYI as the sketch you posted checks from 0x01 to 0x7F.

None of which solves your actual problem. I wonder if it’s a timing issue? The code has this:

#ifdef __AVR__
    inline void tunedDelay(uint16_t delay_us) { _delay_loop_2(delay_us / _avr_bit_delay_scaling); }
#else
    inline void tunedDelay(uint16_t delay_us)
    {
      // TODO: Measure how long digitalWrite, pinMode etc take and compensate by this
      // maybe use ___attribute__( ( always_inline ) ) static __INLINE void __NOP(void) { __ASM volatile ("nop"); }
      // to create a _delay_loop_2() ?
      delayMicroseconds(delay_us);
    }
#endif

I wonder if __AVR__ is not defined in PlatformIO?

Can you add something like this to your sketch, and compile in the Arduino IDE and under PlatformIO:

#ifdef __AVR__
#warning __AVR__ is defined.
#else
#warning __AVR__ is not defined.
#endif

That will tell us if I’m barking up the wrong tree, or just barking! :wink:

Cheers,
Norm.

Hey Norm,thanks for the explanations!

And very good idea with the Delay.

I wonder if __AVR__ is not defined in PlatformIO?

I added the lines and received a #warning __AVR__ is not defined. [-Wcpp]. So the problem could be connected to the “ToDo” in the library… :wink: Sadly, I don’t understand this part of the library. Why should I need a delay? The author noted:

tunedDelay should be used to throttle comms by appropriately delaying the SCL toggling
However the I2C devices i work with are tolerant to non-standard rates, so this is not implemented

And why is there a difference between the Arduino IDE and the PlatformIO IDE? The Arduino IDE also gives me a #warning __AVR__ is not defined. [-Wcpp] by adding the lines. That is strange…

I had a wander through the code. The delay is used to ensure that the state of the data line, SDA, is read or changed, half way through the clock pulse on the clock line, SCL.

For example, to transmit a byte:

void SoftwareI2C::sendByte(uint8_t ucDta)
{
    for(uint8_t i=0; i<8; ++i)
    {
        sdaSet((ucDta&0x80)!=0);
        sclSet(HIGH);
        tunedDelay(_half_bit_delay_us);
        sclSet(LOW);
        tunedDelay(_half_bit_delay_us);
        ucDta <<= 1;
    }
}

The SDA line is raised or lowered, the SCL line is raised, then a delay, then lowered then another delay, then the next data bit is processed. That’s pretty much all that’s needed.

It’s not PlatformIO, it’s AVR and non-AVR microcontrollers.

In your code, somewhere, add #define __AVR__. Does that make any difference? Probably above where the header for the SoftwareI2C library is included.

Cheers,
Norm.

Hello Norm,

thank you for keeping up with this problem!

It’s not PlatformIO, it’s AVR and non-AVR microcontrollers.

I don’t think so! I’m compiling the Arduino Nano BLE 33 with the Arduino IDE (no errors and SoftwareI2C is working) and then with the PlatformIO IDE (nor errors but SoftwareI2C is not working). Both times without the AVR framework (because the Arduino Nano BLE 33 has an nRF52 nordic chip). The same issue occurs by using other SoftwareI2C librarys (Bitbang_I2C etc.).

The problem must be the compiler of the PlatformIO IDE. In the meantime I’ve read a lot of threads in this forum and this compiler-problem occurs to multiple users (not SoftwareI2C, but other issues). Often without a solution…

I the moment, i have no further ideas, how to investigate in this problem =(
The only thing, what I could do, is to use the SoftwareI2C library with mbed-framework (I didn’t test this so far). Or to use the Arudino IDE, but that would be too bad (because of thousands lines of code and a lot of includes…

Sorry, what Iwas meaning was that the check in the code was looking to do one thing with AVRs and another for other microcontrollers.

Possibly. But, at least in the case if AVR chips, the Arduino IDE and PlatformIO both use the avr-g++ compiler. It might be different with other microcontrollers, although I would suspect not. Maybe different versions?

However, that would make the underlying problem one for the compiler as opposed to PlatformIO per se.

I’ve had a few problems with code not working in ine or other environment. What I did was compile both versions, ran an objdump against the .elf files and compared them. If I remember, the only option I needed was -S to dump out my C++ code as comments. Output goes to stdout, so you’ll need to redirect. Something like:

cd somewhere the elf file lives
/full/path/to/correct/objdump -S whatever.elf >whatever.txt

I think it’s -S but maybe it’s -R. It’s uppercase whichever one it is. I’m not near my workstation just now.

Maybe you’ll get a clue from that?

I was working on the assumption that your Nano was a Nano. I forgot there’s a couple of new ones in the wild. Apologies.

Cheers,
Norm.

Strangely, the Arduino Store has this to say:

An Improved Arduino Nano
If you used Arduino Nano in your projects in the past, the Nano 33 BLE is a pin-equivalent substitute. Your code will still work, but remember, it operates at 3.3V.

Yet it has an ARM Cortex processor. I wonder if the problem might be with the core software used by the Arduino IDE and PlatformIO?

Just a thought.

Cheers,
Norm.