How to prevent c++ compiler optimising variable - c++

tl;dr: In the code below, why does the Serial.println(value); statement change the behaviour of the code, and how can I make the code work properly without it ?
I need to save some values on a microcontroller that doesn't have any eeprom (an Arduino 33 BLE sense)
I have found a way to do that by declaring a const variable to reserve a region in the flash memory, and write to it using the microcontroller's NVMC (Non Volatile Memory Controller). This solution works fine but there is some weirdness I'd like to get rid of:
To read the values, I use the following function:
float getFloat(float *ptr) {
float value = *ptr;
Serial.println(value);
return value;
}
This works, but whenever I try to simplify this by
removing the Serial.println statement
not using the function at all and doing what it does inline: something.floatvalue = *ptr
...the code behaves as if the saved values were always 0 (A memory dump shows this is not the case)
I suspect the compiler is optimising stuff in a way I don't understand (I'm not too familiar with c++). How can I avoid that ?
Here ny current code:
#include <Arduino.h>
#include <stdbool.h>
// https://infocenter.nordicsemi.com/pdf/nRF52840_PS_v1.2.pdf
// nFR52 NVMC registers
#define NVMC_BASE (0x4001E000U)
#define NVMC_READY (NVMC_BASE + 0x400U)
#define NVMC_READYNEXT (NVMC_BASE + 0x408U)
#define NVMC_CONFIG (NVMC_BASE + 0x504U)
#define NVMC_ERASEPAGE (NVMC_BASE + 0x508U)
#define NVMC_ERASEALL (NVMC_BASE + 0x50CU)
#define NVMC_ERASEUICR (NVMC_BASE + 0x514U)
#define NVMC_ERASEPAGEPARTIAL (NVMC_BASE + 0X518U)
#define NVMC_ERASEPAGEPARTIALCFG (NVMC_BASE + 0X51CU)
#define NVMC_ICACHECNF (NVMC_BASE + 0x540U)
#define NVMC_IHIT (NVMC_BASE + 0x548U)
#define NVMC_IMISS (NMVC_BASE + 0x54cU)
// nFR52 MVMC values
#define MVMC_READ_MODE 0x00
#define MVMC_WRITE_MODE 0x01
#define MVMC_ERASE_MODE 0x02
typedef struct flash_mem {
float val_1;
float val_2;
// We want to fill a whole page of memory.
// A page is 4096 bytes, each float takes 4 bytes.
char filler[4096 - (4+4) ];
} flash_mem_t;
// This will reserve a space in flash memory for the values we need to save.
const flash_mem_t _values __attribute__((section("FLASH"), aligned(0x1000))) = {};
// A regular, in-memory instance of our values for easy manipulations.
// we'll use the load() and save() functions to move data between values and _values
flash_mem_t values;
void setup() {
// Initialize serial
Serial.begin(115200);
while (!Serial.ready()) { }
delay(500);
// Load and show saved values
load();
Serial.println(values.val_1);
Serial.println(values.val_2);
// Update and save values
values.val_1 += 1;
values.val_2 += 0.642;
save();
}
// Arduino expects this
void loop() {}
void load () {
// TODO: find a way to do this for all values in our struct automatically
values.val_1 = getFloat((float *)&_values.val_1);
values.val_2 = getFloat((float *)&_values.val_2);
}
void writeValues () {
// TODO: find a way to do this for all values in our struct automatically
*(float *)(&_values.val_1) = values.val_1;
*(float *)(&_values.val_2) = values.val_2;
}
bool save() {
// NVMC can only write on "deleted" bytes, so we delete the page _values sits on
deletePage((void *)&_values);
// make sure NVMC is ready
if (*(uint32_t *)NVMC_READY == false) return false;
// write values to flash
*(uint32_t *)NVMC_CONFIG = MVMC_WRITE_MODE;
writeValues();
while(*(uint32_t *)NVMC_READY == false) delayMicroseconds(50);
*(uint32_t *)NVMC_CONFIG = MVMC_READ_MODE;
return true;
}
bool deletePage(void *pageStart) {
if (*(uint32_t *)NVMC_READY == false) return false;
*(uint32_t *)NVMC_CONFIG = MVMC_ERASE_MODE;
*(uint32_t *)NVMC_ERASEPAGE = (uint32_t)pageStart;
while (*(uint32_t *)NVMC_READY == false) delay(85);
*(uint32_t *)NVMC_CONFIG = MVMC_READ_MODE;
return true;
}
float getFloat(float *ptr) {
float value = *ptr;
// This is the weird part: everything works properly whem the following line is here,
// but value is always 0 when it is commented out
Serial.println(value);
return value;
}
I didn't tag this question with the "arduino" tag because I feel like it's not really an arduino problem. If you disagree, I'll be happy to update the tags.
Thanks for any help!

Usually you can use volatile to inform the compiler that the variable can change (for example, in an interrupt), so it wouldn't optimize it away. See Why is volatile needed in C?.

Related

Smart way to transform stm32 function into class

I'm trying to build a class for stm32 peripheral function like i2c, uart. But I ran into some situation where I don't know what to do is the best way.
Below is my I2C init function:
void init_I2C1(void)
{
/* RCC initialization */
RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct = {
.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7,
.GPIO_Mode = GPIO_Mode_AF,
.GPIO_Speed = GPIO_Speed_50MHz,
.GPIO_OType = GPIO_OType_PP,
.GPIO_PuPd = GPIO_PuPd_UP
};
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
I2C_InitTypeDef I2C_InitStruct = {
.I2C_ClockSpeed = 100000,
.I2C_Mode = I2C_Mode_I2C,
.I2C_DutyCycle = I2C_DutyCycle_2,
.I2C_OwnAddress1 = 0x75,
.I2C_Ack = I2C_Ack_Enable,
.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit
};
I2C_Init(I2C1, &I2C_InitStruct);
I2C_Cmd(I2C1, ENABLE);
}
But There is 3 pair of I2C that I can use, so I want to write a general class so I can just specify the number to the class instead of wirte 3 similar functions.
Therefore, I need to find a way to replace those constants.
For those constant just represent a continuous value, I can just use bit shift operation.
ex.
#define GPIO_PinSource6 ((uint8_t)0x06)
#define GPIO_PinSource7 ((uint8_t)0x07)
But other constants are complicated. They are pointers of some structure, so I cannot just just bit shift them. And there are not array either so I also cannot use an interger to go through.
ex.
#define PERIPH_BASE ((uint32_t)0x40000000)
#define APB1PERIPH_BASE PERIPH_BASE
#define I2C1_BASE (APB1PERIPH_BASE + 0x5400)
#define I2C2_BASE (APB1PERIPH_BASE + 0x5800)
#define I2C1 ((I2C_TypeDef *) I2C1_BASE) //the constant I need to use
#define I2C2 ((I2C_TypeDef *) I2C2_BASE) //the constant I need to use
The solution I have now is to trace back to the numerical value then do the bit shifting thing.For example,
I2C_TypeDef * I2C_Type_Ptr = (I2C_TypeDef *)(APB1PERIPH_BASE + (0x5400 << i2c_number));
I2C_Init(I2C_Type_Ptr, &I2C_InitStruct);
But it looks ugly and stupid. I wonder whether there is a more efficient way to do this.
Here is the whole code of the class I have done so far.
//Just for I2C1 & I2C2, there all use portB
I2C::I2C(uint8_t number, uint8_t port_SCL, uint8_t port_SDA)
{
/* RCC initialization */
RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd((RCC_APB1Periph_I2C1 << (number - 1)), ENABLE);
GPIO_InitTypeDef GPIO_InitStruct = {
.GPIO_Pin = (GPIO_Pin_0 << port_SCL) | (GPIO_Pin_0 << port_SDA),
.GPIO_Mode = GPIO_Mode_AF,
.GPIO_Speed = GPIO_Speed_50MHz,
.GPIO_OType = GPIO_OType_PP,
.GPIO_PuPd = GPIO_PuPd_UP
};
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIOB, (GPIO_PinSource0 + port_SCL), GPIO_AF_I2C1); //all I2C AF selection are all the same (0x04)
GPIO_PinAFConfig(GPIOB, (GPIO_PinSource0 + port_SDA), GPIO_AF_I2C1);
I2C_InitTypeDef I2C_InitStruct = {
.I2C_ClockSpeed = 100000,
.I2C_Mode = I2C_Mode_I2C,
.I2C_DutyCycle = I2C_DutyCycle_2,
.I2C_OwnAddress1 = 0x75,
.I2C_Ack = I2C_Ack_Enable,
.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit
};
I2C_TypeDef * I2C_Type_Ptr = (I2C_TypeDef *)(APB1PERIPH_BASE + (0x5400 << number));
I2C_Init(I2C_Type_Ptr, &I2C_InitStruct);
I2C_Cmd(I2C_Type_Ptr, ENABLE);
}
Thank you for your help~~
(P.S. The library I use is "STM32F4xx_StdPeriph_Driver")
If you're not worried about hiding the ST libraries completely, then you could just use the I2C_TypeDef * like so:
I2C::I2C(I2C_TypeDef*, uint8_t port_SCL, uint8_t port_SDA)
Ohterwise, I would just write a function like the below:
I2C_TypeDef* getI2CTypeDef(uint8_t instance)
{
switch (instance) {
case 1U:
return I2C1;
case 2U:
return I2C2;
case 3U:
return I2C3;
}
return I2C1;
}
A couple of observations not directly related to your question:
Instead of using a plain uint8_t for the I2C instance, use an enum class. This will give you a better type safety.
StdPeriph driver is no longer supported. If you care about portability to newer chips, better switch to HAL now.

How do I appropriately call constructors and pass objects around?

I have the following code, but something is wrong with it. It compiles, but nothing happens on the NeoPixel matrix. I've loaded up a strand test to verify that the hardware is working properly. Through some manual debugging, I've worked out that the line containing PixelArt art = PixelArt(matrix); triggers the issue. If I comment that out, I get a panel of orange light coming out of the light matrix as expected. Putting it back in simply results in darkness.
What about including that one line is causing issues? I'm new to C++ and this is driving me up the wall.
For some code context, I plan on including more modes, each with a series of submodes. PixelArt would show off different pixel art pictures as its sub modes. Other modes would include different animation patterns. The different modes should all share a reference to the same matrix object.
#include <Adafruit_NeoMatrix.h>
#include <gamma.h>
#define MODE_PIN 2
#define SUB_MODE_PIN 3
#define MATRIX_PIN 9
#define MATRIX_COLUMNS 16
#define MATRIX_ROWS 16
#define MATRIX_NUM_PIXELS (MATRIX_COLUMNS * MATRIX_ROWS)
class Mode {
public:
Mode(Adafruit_NeoMatrix &neomatrix) : matrix{&neomatrix} {}
virtual void toggleSubMode() = 0;
virtual void update(uint64_t timestamp) = 0;
protected:
Adafruit_NeoMatrix * const matrix;
};
class PixelArt : public Mode {
public:
PixelArt(Adafruit_NeoMatrix &neomatrix):Mode(neomatrix) {}
void toggleSubMode();
void update(uint64_t timestamp);
};
void PixelArt::toggleSubMode() {
// Stubbed.
}
// Green: 0x80c000
// Brown: 0xc04000
// Tan: 0xffa040
uint32_t link[16][16] = { [REDACTED FOR BREVITY] };
void PixelArt::update(uint64_t timestamp) {
matrix->clear();
for (uint16_t row = 0; row < 16; row++) {
for (uint16_t column = 0; column < 16; column++) {
matrix->setPassThruColor(link[row][column]);
matrix->drawPixel(column, row, 0);
matrix->setPassThruColor();
}
}
matrix->show();
}
//
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(
MATRIX_COLUMNS,
MATRIX_ROWS,
MATRIX_PIN,
NEO_MATRIX_TOP | NEO_MATRIX_LEFT | NEO_MATRIX_COLUMNS | NEO_MATRIX_ZIGZAG,
NEO_GRB | NEO_KHZ800
);
PixelArt art = PixelArt(matrix);
void setup() {
matrix.begin();
matrix.show();
matrix.setBrightness(30); // Max ~80
}
uint8_t mode = 0;
void loop() {
// art.update(millis());
matrix.fillScreen(matrix.Color(255, 127, 0));
matrix.show();
}
My suggestion would be that because the code runs on a microcontroller, it may run out of memory. Especially if the target board is an Arduino Uno or similar, with small dynamic memory. After compilation, the memory usage calculation is done for compile-time variables. The run-time variables and function calls may exceed the available memory.
Compiled for an Arduino Uno, the compiler calculates a 1204 bytes (58%) of dynamic memory usage with PixelArt object. Without it is 168 bytes (8%).
Try reducing the inheritance and function call levels.

How to Choose Function Based on Loop Iterator

Part of my problem in finding a solution here is likely that I don't know the correct terms for what it is I am asking. For that, I beg forgiveness in advance.
For a microcontroller, I have a list of pins I wish to initiate at the same time. Each has it's own ISR, and calls the same member of a class for each instance but with a pin number as an argument.
I am trying to attach each pin in the array to its corresponding ISR but I would like to choose which ISR by the pin's index. This is Mailer Codeā„¢ and likely does not compile but I believe it's enough to get the idea:
#define PIN1 4
#define PIN2 9
#define PIN3 10
#define PIN4 8
#define PIN5 12
PinAct *pPinact; // Pointer to Counter class
static ICACHE_RAM_ATTR void HandleInterruptsStatic1(void) {
pPinact->handleInterrupts(1);
}
static ICACHE_RAM_ATTR void HandleInterruptsStatic2(void) {
pPinact->handleInterrupts(2);
}
static ICACHE_RAM_ATTR void HandleInterruptsStatic3(void) {
pPinact->handleInterrupts(3);
}
static ICACHE_RAM_ATTR void HandleInterruptsStatic4(void) {
pPinact->handleInterrupts(4);
}
static ICACHE_RAM_ATTR void HandleInterruptsStatic5(void) {
pPinact->handleInterrupts(5);
}
class PinAct {
public:
PinAct() {};
void handleInterrupts(int);
}
void PinAct::PinAct() {
int actPins[] = {PIN1, PIN2, PIN3, PIN4, PIN5};
for (int i = 0; i <= sizeof(actPins); i++) {
pinMode(actPin[i], INPUT)
attachInterrupt(digitalPinToInterrupt(KEG1), HandleInterruptsStatic + i, FALLING);
}
}
void PinAct::handleInterrupts(int pin) { // Bubble Interrupt handler
// Do something with pin
}
The goal is to actually make the attachInterrupt(digitalPinToInterrupt(KEG1), HandleInterruptsStatic + i, FALLING); work, choosing which ISR by virtue of the index i.
I need to make other decisions about whether or not to assign the ISR, so concatenating the ISR name to be assigned is desirable.
attachInterrupt(/* ... */, HandleInterruptsStatic + i, /* ... */);
// ^^^^^
In order to select the function you want to call at runtime depending on some integer index i you can use an array of function pointers:
typedef void (*FunctionPointer_t)(void);
FunctionPointer_t functions[] = {
HandleInterruptsStatic1,
HandleInterruptsStatic2,
// ...
};
// to use:
functions[i]();

Using Wire.onRequest by passing a class method?

I have an Arduino sketch that will be working on an Arduino UNO and I am trying to get uno to communicate over the i2c connection with a raspberry pi.
Problem is using wire.h library where method Wire.onRequest is working just fine when I use it like this.
#include <Wire.h>
#define COMM_DELAY 50
#define SLAVE_ADDRESS 0x04
int current_rule = 0;
void initI2c() {
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onReceive(receiveData);
}
// callback for received data
void receiveData(int byteCount) {
while (Wire.available()) {
current_rule = Wire.read();
}
}
but when I try to make this exact result with a class method, I get an error :
invalid use of non-static member function
(with Wire.onRequest(this->receiveData) line gets to be marked red)
Just like this:
void (*funptr)();
typedef void (*Callback)(byte);
class Comm{
public:
int callback_list_size = 0;
bool option_debug;
byte option_address;
int option_comm_delay;
void(*callback_list[256]);
byte *rules;
// function for receiving data. raspberry -> arduino
// Whenever the master sends new data, this method will call the appropriate callback.
void receiveData()
{
byte data;
Serial.println("[INFO] Received new data from master");
while (Wire.available())
{
data = Wire.read();
}
for (int i = 0; i < callback_list_size; i++)
{
if (rules[i] == data){
funptr = callback_list[i];
funptr();
}
}
}
// function for sending data. Called when raspberry request data. arduino -> raspberry
// Whenever the master requests data, this method will be called. For now we don't need this but anyway.
void sendData(int s)
{
if (option_debug)
Serial.println("[INFO] Master requests data!");
}
/* Constructor that takes 3 parameters at max. Only the adress is mandatory others are optional and will be filled with default values
:address - adress of slave(arduino) - Example 0x04
:delay - a delay is needed because I2C clock is quite slow compared to the CPU clock - 50
:debug - for debug purposes if true debug info will be sent to Serial interface - true/false
*/
Comm(byte address, int delay = 50, bool debug = false)
{
option_address = address;
option_comm_delay = delay;
option_debug = debug;
if (debug)
Serial.println("[INFO] Comm Object Created!");
}
// Function needs to be called to initialize the communication channel.
void initI2c()
{
Wire.begin(option_address);
Wire.onReceive(this->sendData);
Wire.onRequest(this->receiveData);
if (option_debug)
Serial.println("[INFO] I2C channel initialized");
}
// Function to add new callback for a rule.
// This function returns id of passed callback
int addCallback(Callback func, byte rule)
{
callback_list_size++;
// Enlarge rules array to keep 1 more byte
byte *temp = new byte[callback_list_size]; // create new bigger array.
for (int i = 0; i + 1 < callback_list_size; i++) // reason fo i+1 is if callback_list_size is 1 than this is the first initializition so we don't need any copying.
{
temp[i] = rules[i]; // copy rules to newer array.
}
delete[] rules; // free old array memory.
rules = temp; // now rules points to new array.
callback_list[callback_list_size - 1] = &func;
rules[callback_list_size - 1] = rule;
return callback_list_size;
}
};
Comm *i2c_comm;
void loop()
{
}
void setup()
{
Serial.begin(9600);
initI2C();
}
void initI2C()
{
i2c_comm = new Comm(0x04, 50, true);
i2c_comm->initI2c();
//Callback Definitions
i2c_comm->addCallback(&rule_1, 0x01);
i2c_comm->addCallback(&rule_2, 0x02);
i2c_comm->addCallback(&rule_3, 0x03);
i2c_comm->addCallback(&rule_4, 0x04);
}
I also tried to make the receiveData method to be static.
But in this case I have an error like this:
invalid use of member Com::callback_list_size in static member function
which makes sense to me as static method won't know which callback_list_size I am talking about.
so I am quite confused about how I can handle such a problem?
You're almost there. Generally speaking in C++ you need to pass a static class method for callback functions.
The error you received after changing your method to static is expected as you're trying to access a member of an instance of the class Comm which cannot be done in a static method in which there is no 'this'.
Here's one of many techniques to consider, but please read over the SO post Using a C++ class member function as a C callback function.
Anyway the approach here is to leverage a static pointer to an instance.
class Comm {
private:
static Comm* pSingletonInstance;
static void OnReceiveHandler() {
if (pSingletonInstance)
pSingletonInstance->receiveData();
}
static void OnSendHandler(int s) {
if (pSingletonInstance)
pSingletonInstance->sendData(s);
}
void initI2c() {
Comm::pSingletonInstance = this; // Assign the static singleton used in the static handlers.
Wire.onReceive(Comm::OnSendHandler);
Wire.onRequest(Comm::OnReceiveHandler);
Wire.begin(option_address);
}
}
// static initializer for the static member.
Comm* Comm::pSingletonInstance = 0;
Again there are many ways to get around this issue but above is an easy one and likely suitable for your project. If you need to manage multiple instances of Comm, you'll have to do something quite different.
Good luck!

C++ referencing instances created within a function's scope

Context
The context of the problem is that I am currently writing a small library for use with the Arduino in order to act as a game controller. The problem I am encountering has more to do with C++ than anything Arduino specific however.
I've included the libraries' header and source code below, followed by the Arduino code. I've truncated it where possible.
Problem
In short, only the last switch / action I define actually gets properly handles.
These actions get defined in the Arduino setup function. For example:
controller.addSwitchContinuous(10, 0); // Pin 10; btn index 0
means that pin 10 gets mapped to button 0. When pin 10 is switched closed this is treated as the button being pressed. This works fine for a single action but when I start adding more only the last action actually works. So in the following example only pin 9 is recognized:
controller.addSwitchContinuous(10, 0); // <-- Doesn't work
controller.addSwitchContinuous(9, 1); // <-- Works
This goes for any arbitrary number of actions:
controller.addSwitchContinuous(10, 0); // <-- Doesn't work
controller.addSwitchContinuous(9, 1); // <-- Doesn't work
controller.addSwitchContinuous(8, 2); // <-- Doesn't work
controller.addSwitchContinuous(7, 3); // <-- Works
Potential causes
I am fairly novice with C++ so this I suspect I'm doing something wrong with pointers. More specifically, something seems wrong with how the Joystick_ instance gets passed around.
I have been fiddling with the constructor and trying to use references instead of pointers but I couldn't get it to work properly.
I can confirm the iteration in JFSF::loop does iterate over all actions, if I modify it with:
void JFSF::loop()
{
for (int n = 0; n < _nextActionIndex; n++)
{
if (_actions[n])
{
_actions[n]->loop();
_joystick->setButton(n, PRESSED); // Debug: Set button pressed, regardless of switch.
}
}
if (_doSendState)
{
_joystick->sendState();
}
}
then buttons 0 through n get pressed as expected. It is possible that loop() isn't properly being called, but I would expect it to fail for the N = 1 case as well in that case. Furthermore the fact the last action always succeeds would suggest the iteration is ok.
Full code
// JFSF.h
#ifndef JFSF_h
#define JFSF_h
// ... include for Arduino.h and Joystick.h; bunch of defines
namespace JFSF_PRIV
{
class AbstractAction
{
public:
virtual void loop();
};
/* A Switch that essentially acts as a push button. */
class SwitchContinuousAction : public AbstractAction
{
public:
SwitchContinuousAction(Joystick_ *joystick, int pin, int btnIndex);
void loop();
private:
Joystick_ *_joystick;
int _pin;
int _btnIndex;
};
} // namespace JFSF_PRIV
class JFSF
{
public:
JFSF(Joystick_ *joystick, bool doSendState); // doSendState should be true if Joystick_ does not auto send state.
void loop();
void addSwitchContinuous(int inputPin, int btnIndex);
private:
Joystick_ *_joystick;
JFSF_PRIV::AbstractAction *_actions[MAX_ACTIONS];
int _nextActionIndex;
bool _doSendState;
};
#endif
Source file (trimmed):
// JFSF.cpp
#include "Arduino.h"
#include "Joystick.h"
#include "JFSF.h"
#define PRESSED 1
#define RELEASED 0
// Private classes
namespace JFSF_PRIV
{
SwitchContinuousAction::SwitchContinuousAction(Joystick_ *joystick, int pin, int btnIndex)
{
_joystick = joystick;
_pin = pin;
_btnIndex = btnIndex;
pinMode(_pin, INPUT_PULLUP);
}
void SwitchContinuousAction::loop()
{
int _state = digitalRead(_pin) == LOW ? PRESSED : RELEASED;
_joystick->setButton(_btnIndex, _state);
}
} // namespace JFSF_PRIV
JFSF::JFSF(Joystick_ *joystick, bool doSendState)
{
_joystick = joystick;
_nextActionIndex = 0;
_doSendState = doSendState;
}
void JFSF::addSwitchContinuous(int inputPin, int btnIndex)
{
JFSF_PRIV::SwitchContinuousAction newBtnAction(_joystick, inputPin, btnIndex);
_actions[_nextActionIndex++] = &newBtnAction;
}
void JFSF::loop()
{
for (int n = 0; n < _nextActionIndex; n++)
{
if (_actions[n])
{
_actions[n]->loop();
}
}
if (_doSendState)
{
_joystick->sendState();
}
}
For completeness sake, this is the code for the Arduino, but it is pretty much just declarations:
#include <JFSF.h>
// ... A bunch of const declarations used below. These are pretty self explanatory.
// See: https://github.com/MHeironimus/ArduinoJoystickLibrary#joystick-library-api
Joystick_ joystick(HID_REPORT_ID,
JOYSTICK_TYPE_JOYSTICK, // _JOYSTICK, _GAMEPAD or _MULTI_AXIS
BTN_COUNT, HAT_SWITCH_COUNT,
INCLUDE_X_AXIS, INCLUDE_Y_AXIS, INCLUDE_Z_AXIS,
INCLUDE_RX_AXIS, INCLUDE_RY_AXIS, INCLUDE_RZ_AXIS,
INCLUDE_RUDDER, INCLUDE_THROTTLE,
INCLUDE_ACCELERATOR, INCLUDE_BRAKE, INCLUDE_STEERING);
JFSF controller(&joystick, !DO_AUTO_SEND_STATE);
void setup() {
joystick.begin(DO_AUTO_SEND_STATE);
controller.addSwitchContinuous(10, 0); // <-- Doesn't work
controller.addSwitchContinuous(9, 1); // <-- Works
}
void loop() {
controller.loop();
}
References
ArduinoJoystickLibrary (Source for Joystick_) can be found here: https://github.com/MHeironimus/ArduinoJoystickLibrary#joystick-library-api
I dont really understand your code. Please read How to create a Minimal, Complete and Verifiable example. Anyhow, the following is certainly wrong and likely the cause of your problem:
void JFSF::addSwitchContinuous(int inputPin, int btnIndex)
{
JFSF_PRIV::SwitchContinuousAction newBtnAction(_joystick, inputPin, btnIndex);
_actions[_nextActionIndex++] = &newBtnAction;
}
Lets rewrite it a bit for clarity:
void foo(){
T bar;
container[index] = &bar;
}
What happens here is that bar gets destroyed when it goes out of scope, hence the pointer you put into the container, points to garbage. Presumably somewhere else in your code you are dereferencing those pointers, which is undefined behaviour (aka anything can happen).
Long story short: It is a common pattern among c++ beginners to overuse pointers. Most likely you should make container a container of objects rather than pointers and make use of automatic memory managment instead of trying to fight it.
Thanks to #user463035818 and #drescherjm for identifiying the actual problem.
So in the end I fixed it by simply moving the Action object creation up to the Arduino code (where it's essentially global) and passing references to those objects to the controller.
In code this translates to:
JFSF.cpp
void JFSF::addAction(JFSF_PRIV::AbstractAction *action){
_actions[_nextActionIndex++] = action;
}
Arduino code (ino)
// See code in original post
JFSF controller(&joystick, !DO_AUTO_SEND_STATE);
JFSF_PRIV::SwitchContinuousAction btnOne(&joystick, 10, 0);
JFSF_PRIV::SwitchContinuousAction btnTwo(&joystick, 9, 1);
void setup() {
joystick.begin(DO_AUTO_SEND_STATE);
// controller.addSwitchContinuous(10, 0); // Pin 10; btn index 0
// controller.addSwitchContinuous(9, 1); // Pin 9 ; btn index 1
controller.addAction(&btnOne);
controller.addAction(&btnTwo);
}
// loop() is unchanged