Hi,
Having recently learned that I should be making variables volatile when shared between an ISR and the main program. I have an issue that I can’t have those variables exposed via Particle.variable() as the code doesn’t compile if I do.
Target: DeviceOS@1.4.4 on Particle Photon
Compilation error (Snippet):
../wiring/inc/spark_wiring_cloud.h: In instantiation of 'static bool CloudClass::variable(const T&, const Types& ...) [with T = char [13]; Types = {volatile int}]':
src/volatile.ino:37:60: required from here
../wiring/inc/spark_wiring_cloud.h:83:25: error: call of overloaded '_variable(const char [13], const volatile int&)' is ambiguous
return _variable(name, args...);
^
Example problematic code (not the real program but something hopefully simpler):
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);
// Analog Sensors
#define indoorSensorPin A0
#define outdoorSensorPin A1
const int pollSensorInterval = 100;
volatile int indoorSensorValue = 0;
volatile int outdoorSensorValue = 0;
int currentState = 0;
int desiredState = 0;
bool initialPublishComplete = false;
// Poll IR sensors
Timer pollSensorsTimer(pollSensorInterval, pollSensor_timercallback);
void setup() {
// Usual setup stuff here.
}
void pollSensor_timercallback() {
indoorSensorValue = analogRead(indoorSensorPin);
outdoorSensorValue = analogRead(outdoorSensorPin);
}
void setupParticleCloud() {
if (!Particle.connected()) {
Particle.connect();
initialPublishComplete = false;
} else if (!initialPublishComplete) {
Log.info("Connected publishing variables");
Particle.variable("IndoorSensor", indoorSensorValue);
Particle.variable("OutDoorSensor", outdoorSensorValue);
Particle.variable("Current", currentState);
Particle.variable("Desired", desiredState);
initialPublishComplete = true;
}
}
void loop() {
// Ignore the fact that nothing in this example changes currentState or desiredState
if (currentState == desiredState) {
// Ok to non-critial tasks
setupParticleCloud();
} else {
// Need to do some stuff quickly, i.e. Running a Stepper motor as fast possible
// But also need to monitor for sensor values changing (triggered by timer)
}
}
I must admit I’m not entirely sure about the following, but from the understanding that volatile is primarily meant to force the compile to always use RAM access when dealing with it (rather than having the optimizer substituting that for faster internal registers for “intermediate” mutations) I’d guess casting a volatile int to (int) should do the trick.
However, you might occasionally experience data inconisitencies when the “non-volatile” access happens exactly at the moment a volatile update takes place.
I thought to casting to an int would work too, assuming this is the correct way to do it. Particle.variable("IndoorSensor", (int) indoorSensorValue);
However, this code (using either a volatile int or int) results in nonsense being returned when the variable is queried via Particle cloud. The pins are currently both connected to ground. When declared as a regular int, the published without casting I can call get on the variables and they always return 0 (zero). When using casting during the initial publish, calling get returns a huge range of seemingly random number, (e.g. -1515870811, 134758967, 8, 536958521).
I can make it work by publishing an non-volatile int and in the loop sync between the non-volatile int and the volatile int, but I was hoping to avoid this.
Hmm, that’s odd, but may have to do with the way how Particle.variable() accesses the variable.
You could try Particle.variable("IndoorSensor", *(int*)&indoorSensorValue); instead
That led to the Particle Cloud Showing the value as a bool:
IndoorSensor (bool) = false
Changing to: Particle.variable("IndoorSensor", (int*)&indoorSensorValue, INT);
Looks like it may have done the trick (though I can't be 100% sure as I'm remote from my test device at the moment), so i can't change the input value being read to confirm the variables are updated.
I noticed this a long while ago but then decided to keep 2 int variables, one a volatile int and the other an int. The int variable used in the Particle.variable() is an external (web app) version of the volatile integer with a mapping between the enumeration used. It works well for my use case.
Perhaps you understand how the definition of Particle.variable() works - it is a little too advance C++ for me!
class CloudClass {
public:
template <typename T, class ... Types>
static inline bool variable(const T &name, const Types& ... args)
{
static_assert(!is_string_literal<T>::value || sizeof(name) <= USER_VAR_KEY_LENGTH + 1,
"\n\nIn Particle.variable, name must be " __XSTRING(USER_VAR_KEY_LENGTH) " characters or less\n\n");
return _variable(name, args...);
}