UPDATE: Please see my last reply for the description and repro case for this issue.
Hi all, hope someone can help point me in the right direction to resolve this. When I use Workbench to compile locally and flash the firmware, I end up with a device flashing red and rebooting.
When I use the option to use the cloud compiler everything works as expected. There aren’t any compile errors and the binaries in the target folder always get updated after a local compile.
Any tips on how I can figure this out?
I installed everything using the Visual Studio Marketplace.
Yes, tried all of the above and no luck. Will keep digging around to see if I can figure it out. For now cloud compile is working great so can make progress that way, but would really like to get local compile working. Might start from scratch and install everything to see if that helps tomorrow.
It seems the root cause is as below, my guess is some difference in how linking is done in the local tool chain vs. the cloud tool chain.
You need to globally instantiate a class with the following key points:
– in the constructor you are calling pinSetFast()
– the constructor must be in a different compilation unit (different CPP file)
If the above conditions apply.
– When you build in the cloud and flash everything works.
– When you build locally and flash you get the flashing red light.
See code below or download from the link I posted above.
SomeClass.h
#include "application.h"
class SomeClass {
public:
SomeClass();
};
I think you are running into the problem in C++ that constructors of global objects can be run in order by the compiler (it is not defined in the language) so sometimes your object is constructed before the underlying objects used by pinMode() and pinSetFast().
I think you could have failures or non-failures with either compiler platform depending on what other objects were in global memory.
I think the solution is to move your pin handling code in your object to a begin() method, similar to what the other Arduino objects do, and call it in setup which is guaranteed to be after all the global objects are constructed.
I’d think pinMode() and pinSetFast() are (or should be) not dependent on pre-instantiated objects. If they were, I’d consider that a bug as the official stand is/was that pinMode()is allowed in a constructor.
I think we’d need an official statement of a dev like @avtolstoy about that.
@ScruffR: No difference when using Particle.h vs. application.h. I use to always use Particle.h but I think it was workbench that was auto including application.h so I switched too . Thanks for the note that it was deprecated!
As for the #pragma once guard, yes, I use that, just didn’t add it to the sample and should have to avoid confusion. Apologies for that.
@bko Your explanation makes sense and I have changed my implementation to do that after finding this issue, though I do wish this was documented if indeed we should not use any of the hardware related functions till setup() is called to make sure. I started doing this after learning it works quite well, especially when the entire class was in the .h file. When I refactored is when I hit this, it was just a coincidence that I switched to using Workbench at the same time.
Both, I am happy to help update the documentation if we confirm this is undefined behavior. Hopefully that will help others save time.
Thanks for reporting this issue. As it has been mentioned already, the issue is most likely caused by the unspecified order in which global objects are initialized in C++ when you have multiple translation units. As a workaround I suggest to perform any low-level initialization in setup(), or use C++ singletons instead of global variables:
With that said, we’ll try to make initialization of the system and Wiring library in Device OS more robust, but I’m not sure about the timeline just yet.