Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C bus not working for PI PICO-W #27

Open
revell1 opened this issue Sep 10, 2022 · 10 comments
Open

I2C bus not working for PI PICO-W #27

revell1 opened this issue Sep 10, 2022 · 10 comments

Comments

@revell1
Copy link

revell1 commented Sep 10, 2022

Trying to build/run example code (Simple_MPU6050_Example or Simple_MPU6050_Basic_Example and probably the others) for PI PICO-W code compiles and downloads to target, but I2C is not working.

Serial output reports that detected MPU6050 at 0x68, with WHOAmI=0x00
Does this regardless of what is connected, even when nothing connected.

After a brief test using a new project and just the Simple_Wire.cpp/h and using the following code:

======
#include "Simple_Wire.h"
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println(F("MyTest_I2C:Start:"));

Simple_Wire myWire;

// scan I2C on Pico board, see what we can find.
Serial.println(F("try call I2C_Scanner()"));
myWire.I2C_Scanner();

}

void loop() {
// put your main code here, to run repeatedly:

}

======

Code correctly identifies when DMP6050 (GY-521) is connected or missing.

Proving that the I2C is working.

====

Further exploration resulted in observation that in you example code, you generally have:

"Simple_MPU6050 mpu;" at the top of the file before setup() or loop().
In my case defining the global "mpu" object before the setup() and initial creation of the Serial.begin() appears to kill I2C.
I am guessing that there is some code race condition preventing I2C setup, or breaking it if Serial.begin() comes after the mpu creation.

I managed to find a HACK that works for my PI PICO-W, but probably is not the correct solution.

I changed the "Simple_MPU6050 mpu;" to "Simple_MPU6050 *mpu;", as a pointer.
Changed ALL "mpu." to "mpu->"
Then in Setup() after the Serial.begin() I added:

"
Simple_MPU6050 myMpu;
mpu = &myMpu;
"

This delayed the creation of the mpu object until after the Serial monitor setup.

BUT this then kills code in loop(), so code in loop() then has to be moved inside setup() at the end in a WHILE loop, otherwise exiting setup() destroys the "mpu" object, but the pointer still points to something.

Another thing I could not work out is how to select different pins for the I2C bus, as Simple_MPU6050 and Simple_Wire classes do not appear to expose a method to allow user selection of alternate I2C pins during creation of the mpu object.

@ZHomeSlice
Copy link
Owner

I have not messed with the PI PICO-W can you tell me how the i2c code needs to access the i2c bus

@revell1
Copy link
Author

revell1 commented Sep 12, 2022

I have not messed with the PI PICO-W can you tell me how the i2c code needs to access the i2c bus

Not sure if your question is relating to just this ISSUE, or in part also if relation to the ISSUE #26, where I noted the lack of DEFINES in the PI PICO-W build.

My code fragment test above demonstrated that your "Simple_Wire.h" library when used in isolation works just fine with the default I2C pins (GP4, GP5 [pins 6,7 on board]).

I do not understand what is causing the code to fail when the global mpu object is defined at the top of the file.

I have modified my code fragment and include it below, where I have a few #defines to control where the Simple_Wire object is created and used, and also added conrtrol of the onboard LED to try to observe execution and effects. Read the comments at the top of the fragment for brief results summary.

========================================

// Code to test Simple_Wire.cpp/h
// Tests if MPU6050 detected at 0x68
// For DOWAY=1 :
// This works correctly, suggesting that with this simple code, we get functioning I2C
// as far as a simple device detect goes.
//
// For DOWAY=2 :
// Also works correctly, even though myWire is before Serial.begin, (but inside setup())
//
// For DOWAY=3 :
// Builds and downloads, but PICO-W fails to create USB-COM port, so can not see any output!!!
// Also LED stays OFF, suggesting that code does not even get into setup()

#define DOWAY 3
#define WAITUSB 0 // 1 to wait for users USB monitor to connect. 0 to not wait
//====================================

#include "Simple_Wire.h"

#if DOWAY == 3
Simple_Wire myWire;
#endif

void setup() {
#if DOWAY == 2
Simple_Wire myWire;
#endif

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW); // LED OFF
#if DOWAY == 3
digitalWrite(LED_BUILTIN, HIGH); // ON (see if we even get into setup())
#endif

Serial.begin(115200); // USB so baud is ignored

#if WAITUSB == 1
digitalWrite(LED_BUILTIN, HIGH); // ON (show we are waiting for USB connection)
while (!Serial); // Wait till USB serial port connected
digitalWrite(LED_BUILTIN, LOW); // OFF
#endif

Serial.println(F("MyTest_I2C:Start:"));

#if DOWAY == 1
Simple_Wire myWire;
#endif

// Try to scan I2C on Pico board, see what we can find.
Serial.println(F("try call I2C_Scanner()"));
myWire.I2C_Scanner();

}

void loop() {
delay(2000); // wait 2 seconds
digitalWrite(LED_BUILTIN, HIGH);
Serial.println(F("MyTest_I2C:loop:"));

#if DOWAY == 3
myWire.I2C_Scanner();
#endif

delay(2000); // wait 2 seconds
digitalWrite(LED_BUILTIN, LOW);

}

========================================

Also I did not say before, but from the Arduino IDE, various tab settings are:

Arduino 1.8.19
File - Preferences - Additional Boards Manager URLs:
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json

Tools - Board - Raspberry PI RP2040 Boards (2.5.2)

=================

Also my INO file is in a directory with the following files:

Simple_Wire.cpp, Simple_Wire.h <- YOUR FILES FROM GITHUB
Wire.cpp, Wire.h <- from earlephilhower/arduino-pico (2.5.2)

Have done it this way to try to be clear what files were being used, as have had issues in past with all the MPU_6050 libraries that I have been testing/trying while trying to find one that works with any accuracy, I have found at times that Arduino IDE finds multiple same named libraries and either warns or builds with the WRONG library. Obviously the IDE is also including a whole set of other files from who knows where, hopefully ALL from "earlephilhower's" board support.

=============

NOTE: for DOWAY=3 and WAITUSB=0, the code builds, but does not appear to manage to run as far as Setup(), as the LED does not turn on.

This is what makes me think that there is some issue with the placement of objects outside of Setup() or Loop(), i.e. at the top of the file, but I do not know what that is, but suspect it is either some issue with the "earlephilhower/arduino-pico" support for RP2040 based targets, or an Arduino include/library issue.

I would also note that the behaviour of this simple code is slightly different to the behaviour with your full MPU library, there the code was atleast running, but without functional I2C, but I guess there the "SimpleWire" object was inside the MPU object, so must have modified the code behaviour somehow letting it run and get into Setup() and then Loop().

Hope that this info may be of some help in identifying what is going wrong.

@revell1
Copy link
Author

revell1 commented Sep 12, 2022

Something I have found in a post from 2014, so may not be appropriate.
URL: forum.arduino.cc/t/what-goes-in-setup-what-comes-before/222566/2

Quote:

Quite a lot of things happen before setup() is called.

Many people think that main() is the first bit of code called, but that is not correct.

The first bit of code is called "crt0" (C RunTime stage 0). This performs:

Copying of pre-set variable data from flash to RAM
Configure basic system settings
Executing of global object constructors
Execute main()

On the Arduino main() then does a number of functions, including setting up the timers for millis(), PWM, etc, and configuring such things as interrupts and ADC. It basically gets the system ready for you - all things you would normally be doing manually in a less abstracted system. (check out the init() function in wiring.c)

Then and only then does setup() get called.

Point 3 in the list above is the one that catches most people out. If you have a global object with code in the constructor that does things with peripherals that aren't configured until main() has run then you can have all sorts of strange things happening or just plain not working right. That is why most classes have a .begin() member function to do all the configuration instead of doing it in the constructor.

======

A brief look at your code, and I see that for "Simple_MPU6050::Simple_MPU6050(uint8_t DMPMode)" this does very little, so is probably OK on point 3.

But for "Simple_Wire::Simple_Wire()", this calls "Wire.begin()", so may explain my problem with the test code I posted earlier today which does not run if I set "DOWAY = 3", as this would fall foul of point 3, as calling "begin" on Wire before main() and setup() are called.

My CPP is a bit rusty, but from Simple_MPU6050.h (line 63) looks like Simple_MPU6050 class inherits from "Simple_Wire" class, which I presume will call the "Simple_Wire" constructor when Simple_MPU6050 is declared before setup(), which in turn will cause Wire.begin() to be called at line 28 of "Simple_Wire.h"

Then if that is true it could manifest in different ways depending on the target board.

I also note in Simple_Wire.h there are two constructor versions:
Simple_Wire();
Simple_Wire(uint8_t bus_num);

But only the first appears to exist.

======

Not sure if what I have highlighted above is true or significant to the primary problem of I2C not working with your original code.
But it would appear to make sense given my changes to delay the ACTUAL object creation to once inside setup() made the I2C bus work.

Still not sure about how to go about specifying the GPIO pins (or BUS 0 or 1), that should be used during setup of the hardware, as can not see what potential methods may be exposed via the class inheritance.

[NOTE: I am not sure what is controlling the formatting of this post, but there appears to be some auto formatting going on in the quoted text, which I can't see how to tame it].

@revell1
Copy link
Author

revell1 commented Sep 19, 2022

As you do not appear to have made any changes to the code since my last post,
I offer you what appears to be a fix, based on the notes I posted about THINGS THAT HAPPEN BEFORE SETUP().

If you find these changes acceptable, please merge them in to your current code.
Below are the changes (with some comments which hopefully explain the reasons).
You will also need to change multiple other examples for the MPU6050 code, and anywhere else that you have
used the Simple_Wire library, which hopefully will not be many as you appear to have created the library to
support the Simple_Mpu6050 library.

+++++++++++++++++++++++++++++++++++++++++++++++++
THE CHANGES:

Below are some proposed changes to your Simple_Wire library class, and the Simple_Mpu6050 class, which
appear to fix the issue with the code not detecting the MPU6050 when used on a PI PICO-W (and probably any
other RP2040 based board that uses "earlephilhower" board library for all things RP2040.

NOTE: This fix does not address the other question of how to select different I2C pins or the second bus.

The Changes:-

In Simple_Wire.cpp

Change:
================

Simple_Wire::Simple_Wire() {
    Wire.begin();
    Wire.setClock(400000); // 400kHz I2C clock. 
#ifdef __AVR__
        Wire.setWireTimeout(3000, true); //timeout value in uSec
#elif defined(ESP32)
#else
#endif
}
================
to:
================

Simple_Wire::Simple_Wire() {
  /* MUST NOT CALL BEGIN IN HERE, as will occur before setup() */
 }
// When we use Simple_Wire class in the Simple_Mpu6050 class,
// We must call begin() at earliest point in xxx.ino files setup(),
// before we call any other Simple_Mpu6050 methods
void Simple_Wire::begin() {
    Wire.begin();
    Wire.setClock(400000); // 400kHz I2C clock. 
#ifdef __AVR__
        Wire.setWireTimeout(3000, true); //timeout value in uSec
#elif defined(ESP32)
#else
#endif
}
================


In Simple_Wire.h

Change [At about line 52]:
================
        Simple_Wire(uint8_t bus_num);
================
To:
================
        Simple_Wire(uint8_t bus_num);
        void begin();
================

Also I think there was a compiler warning at about line 108 to 111,
(needing extra ";" at end of inline functions)
I think you need to change:
================
        uint8_t Value(uint8_t * V){return(V[0])};
        int8_t Value(int8_t * V){return(V[0])};
        uint16_t Value(uint16_t * V){return(V[0])};
        int16_t Value(int16_t * V){return(V[0])};
================
To:
================
        uint8_t Value(uint8_t * V){return(V[0]);};
        int8_t Value(int8_t * V){return(V[0]);};
        uint16_t Value(uint16_t * V){return(V[0]);};
        int16_t Value(int16_t * V){return(V[0]);};
================
Those are the changes needed for the Simple_Wire.cpp/h files.

Next you will need to fix any of your example *.ino code files that uses the Simple_Wire library class.

For example: Simple_MPU6050_Example.ino

Change (At about line 219), add a call to the Simple_Mpu6050 object, calling the begin() method.
(Note that the Simple_Mpu6050 class does not have it's own begin() method, but will call the
Simple_Wire's begin() method which it has via the inherited methods).

From:
================
  Serial.println(F("Start:"));
================
To:
================
  Serial.println(F("Start:"));
  mpu.begin(); // Call new begin() method in Simple_Wire class
================


+++++++++++++++++++++++++++++++++++++++++++++++++



@ZHomeSlice
Copy link
Owner

I have been testing on an ESP32 C3 and am facing similar issues I may have code worthy of posting shortly. This is a hobby and I appreciate all the help Thanks.
Z

@ZHomeSlice
Copy link
Owner

uploaded changes including yours Thank you!
Z

@revell1
Copy link
Author

revell1 commented Sep 21, 2022

Hi, just got round to trying the changes, but hit a snag.
I think it is to do with target board type and differences between the library versions of target boards.

I took both your libraries and placed them in the directories:

C:\Users.....\Documents\Arduino\libraries\Simple_Wire
C:\Users.....\Documents\Arduino\libraries\Simple_MPU6050

Opened a new example (Simple_MPU6050_Example) from the list of examples for your library.

If I use board target as Arduino UNO
And build/Verify, the build is without errors or warnings.

But if I Select board target as Raspberry PI PICO-W
And build/Verify, there are errors and warnings.

++++
Warning is about "printfloatx" redefined.
I think all defs of "printfloatx" need removing from the examples, as they are custom defined in the "Simple_MPU6050.h" for board specific cases.
++++

As for the error.
Looks like an issue with the lower level inherited functions of either Wire or TwoWire.

=-========
Error text is:
...\Arduino\libraries\Simple_Wire\Simple_Wire.cpp: In member function 'void Simple_Wire::begin(int, int)':
...\Arduino\libraries\Simple_Wire\Simple_Wire.cpp:59:46: error: no matching function for call to 'TwoWire::begin(int&, int&, uint32_t)'
59 | Wire.begin(sdaPin,sclPin,(uint32_t)400000); // 400kHz I2C clock.
| ^
In file included from ...\Arduino\libraries\Simple_Wire\Simple_Wire.h:28,
from ...\Arduino\libraries\Simple_Wire\Simple_Wire.cpp:24:
...\Local\Arduino15\packages\rp2040\hardware\rp2040\2.5.2\libraries\Wire\src/Wire.h:42:10: note: candidate: 'virtual void TwoWire::begin()'
42 | void begin() override;
| ^~~~~
...\Local\Arduino15\packages\rp2040\hardware\rp2040\2.5.2\libraries\Wire\src/Wire.h:42:10: note: candidate expects 0 arguments, 3 provided
...\Local\Arduino15\packages\rp2040\hardware\rp2040\2.5.2\libraries\Wire\src/Wire.h:44:10: note: candidate: 'virtual void TwoWire::begin(uint8_t)'
44 | void begin(uint8_t address) override;
| ^~~~~
...\Local\Arduino15\packages\rp2040\hardware\rp2040\2.5.2\libraries\Wire\src/Wire.h:44:10: note: candidate expects 1 argument, 3 provided
=-========

I have been trying to untangle the wire/twowire classes to try to work out what needs to change.
In
...\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\2.5.2\libraries\Wire\src\Wire.h
there is a comment at about line: 48


// Select IO pins to use.  Call before ::begin()
bool setSDA(pin_size_t sda);
bool setSCL(pin_size_t scl);

void setClock(uint32_t freqHz) override;

So this suggests either a change in SimpleWire.cpp around lines 28-38 to a another custom compiler test, and use the
setSDA,setSCL and setClock functions before then calling the "Wire.begin()" with no parameters, or I am missing something obvious.

Also it may be worth considering what if the user needs a slower clock speed (due to having longer cables which will not run at higher rates, possibly need to pass clock in through the top level begin() somehow.
At present I don't think you provide a way to select a different clock rate, as it is fixed half way down the begin() chain somewhere between the mpu.begin() call and the SimpleWire class code.

@revell1
Copy link
Author

revell1 commented Sep 21, 2022

I think I have worked out a fix for the compiler error, though leaves a few questions.

In Simple_Wire.cpp, at about line 54, I added the EXTRA test variant "#elif defined(ARDUINO_ARCH_RP2040)" and removed the "ARDUINO_ARCH_RP2040" from the final elif test variant. Not sure this is the cleanest method, but it appears to work.

=-=========
#ifdef AVR
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock.
Wire.setWireTimeout(3000, true); //timeout value in uSec
#elif defined(ARDUINO_ARCH_RP2040)
Wire.setSDA(sdaPin);
Wire.setSCL(sclPin);
Wire.setClock(400000); // 400kHz I2C clock.
Wire.begin(); // 400kHz I2C clock.
#elif defined(ESP8266) || defined(ESP32)
Wire.begin(sdaPin,sclPin,(uint32_t)400000); // 400kHz I2C clock.
#else
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock.
#endif
=-=========

Another thing I needed to do for my PICO-W target, was edit the example code to change the
mpu.begin();
to
mpu.begin(PIN_WIRE0_SDA, PIN_WIRE0_SCL);

to specify my pins (using the default of 4,5), found there are defs of PIN_WIRE0_SDA, PIN_WIRE0_SCL, but not sure if they are specific to the RP2040 targets, or if they are ARDUINO defines.

In Simple_Wire.h -> void begin(int sdaPin = 0, int sclPin = 1);
This is defining default values, not sure if this is good in as far as they are numbers, are there any defined values that could be used? Not realy an issue if you know that you need to call mpu.begin() with values if default 0,1 don't work for target board.

Hopefully that will put this to bed.

@revell1
Copy link
Author

revell1 commented Feb 26, 2023

This issue is still open, I am not sure if it has been fully addressed, but judging by the recent Issue #28, I suspect that my last post in the issue is still waiting a final fix. Which would hopefully sort out both issues #27 and #28 so both can be closed.

But hopefully the info in this issue (Posted on Sept 21 2022) will be sufficient to allow other users to fix it for themselves in the short term, as I know that these libraries were provided as a hobby rather than a fully supported release, and the issue appears to be target specific.

@ZHomeSlice
Copy link
Owner

I am unable to work on projects that involve using the pi Pico-w, because, unfortunately, I don't have access to this device at the moment. I would greatly appreciate any assistance from the community in making this a viable option for my code. If anyone has experience with the pi Pico-w or any suggestions on how to make my code work with it, I would be grateful for your input. Thank you in advance for your help and support!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants