Double Tap Parameter Configuration can not set from Internal Code - c++

I am writing code for the double-tap event but it's not working perfectly so I think that configuring the below internal registers might help to get better results:
doubleTapDur,
doubleTapQuiet,
tapThs,
axes
To set the value of the registers, I'm using ADB bridge via the Movesense showcase adb bridge app. here is the Link of the APK:Showcaseapp-debug_adb-bridge-build.apk
I use the below command to read and write double-tap parameters. and it's working.
for read:
adb.exe shell am broadcast -a "android.intent.action.MOVESENSE" --es type get --es path Component/LSM6DS3/TapParams --es value '''{}'''
for write:
adb.exe shell am broadcast -a "android.intent.action.MOVESENSE" --es type put --es path Component/LSM6DS3/TapParams --es value '''{\"newParams\":{\"doubleTapDur\": 8, \"doubleTapQuiet\": 3, \"tapThs\": 8, \"axes\": 8}}'''
To set these parameters internally (inside code), I wrote a function DoubleTAPConfig(), which sets the value in params. But it's not working. I am not sure what I'm missing. Any help would be greatly appreciated.
Here is my code:-
#include "DOUBLETAP.h"
#include "common/core/debug.h"
#include "component_led/resources.h"
#include "component_lsm6ds3/resources.h"
#include "movesense.h"
#include "system_mode/resources.h"
#include "system_states/resources.h"
#include "ui_ind/resources.h"
const size_t BLINK_PERIOD_MS = 1000;
#define AVAILABILITY_TIME 5000
// LED blinking period in adertsing mode
#define LED_BLINKING_PERIOD 100
const char* const DOUBLETAP::LAUNCHABLE_NAME = "DOUBLETAP";
DOUBLETAP::DOUBLETAP()
: ResourceClient(WBDEBUG_NAME(__FUNCTION__), WB_EXEC_CTX_APPLICATION),
LaunchableModule(LAUNCHABLE_NAME, WB_EXEC_CTX_APPLICATION),
DOUBLETAPEnabled(false),
mTimer(wb::ID_INVALID_TIMER),
mCounter(0)
{
}
DOUBLETAP::~DOUBLETAP()
{
}
bool DOUBLETAP::initModule()
{
DEBUGLOG("DOUBLETAP::initModule");
this->mModuleState = WB_RES::ModuleStateValues::INITIALIZED;
return true;
}
void DOUBLETAP::deinitModule()
{
DEBUGLOG("DOUBLETAP::deinitModule");
this->mModuleState = WB_RES::ModuleStateValues::UNINITIALIZED;
}
bool DOUBLETAP::startModule()
{
DEBUGLOG("DOUBLETAP::startModule");
this->mModuleState = WB_RES::ModuleStateValues::STARTED;
this->mTimer = startTimer(BLINK_PERIOD_MS, true); // Start LED timer. true = trigger repeatedly
// set internal resistor value for doubletap mode
DoubleTAPConfig();
// Subscribe to DOUBLETAP detection
this->asyncSubscribe(WB_RES::LOCAL::SYSTEM_STATES_STATEID(), AsyncRequestOptions::Empty, WB_RES::StateIdValues::DOUBLETAP);
return true;
}
void DOUBLETAP::stopModule()
{
DEBUGLOG("DOUBLETAP::stopModule");
this->stopTimer(mTimer); // Stop LED timer
this->mTimer = wb::ID_INVALID_TIMER;
// Unsubscribe DOUBLETAP detection
this->asyncUnsubscribe(WB_RES::LOCAL::SYSTEM_STATES_STATEID(), AsyncRequestOptions::Empty, WB_RES::StateIdValues::DOUBLETAP);
this->mModuleState = WB_RES::ModuleStateValues::STOPPED;
}
void DOUBLETAP::onTimer(wb::TimerId timerId)
{
if (DOUBLETAPEnabled)
{
this->asyncPut(WB_RES::LOCAL::UI_IND_VISUAL(), AsyncRequestOptions::Empty, WB_RES::VisualIndTypeValues::SHORT_VISUAL_INDICATION);
// break;
}
else
{
DOUBLETAPEnabled = false;
// break;
}
}
void DOUBLETAP::onNotify(wb::ResourceId resourceId, const wb::Value& value, const wb::ParameterList& parameters)
{
DEBUGLOG("DOUBLETAP::onNotify");
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::SYSTEM_STATES_STATEID::LID: {
const WB_RES::StateChange& stateChange = value.convertTo<const WB_RES::StateChange&>();
if (stateChange.stateId == WB_RES::StateIdValues::DOUBLETAP)
{
DEBUGLOG("Lead state updated. newState: %d", stateChange.newState);
DOUBLETAPEnabled = !DOUBLETAPEnabled;
}
}
}
}
void DOUBLETAP::DoubleTAPConfig()
{
WB_RES::TapParams TapConfig;
TapConfig.doubleTapDur = 8; // set 0-15 Default:_ 0000
TapConfig.doubleTapQuiet = 3; // set 0-3 Default value:_ 00
TapConfig.tapThs = 8; // set 0-31 Default value:_ 00000
TapConfig.axes = 8; // set 0-14 bitfield:_ x=8, y=4, z=2
asyncPut(WB_RES::LOCAL::COMPONENT_LSM6DS3_TAPPARAMS(), AsyncRequestOptions::Empty, TapConfig);
// also try with this coomund line
// asyncPut(WB_RES::LOCAL::COMPONENT_LSM6DS3_WAKEUP(),AsyncRequestOptions(NULL, 0, true), TapConfig);
}
I am not sure what I'm missing. Any help would be greatly appreciated.

The /Component/LSM6DS3/TapParams -resource accessed the LSM6 chip registers directly, so in your firmware code the following happens:
You set the tap-params into the registers (DoubleTAPConfig method)
You start DOUBLE_TAP detection, which immediately resets the tap parameters in the registers to defaults
You'll need to:
first subscribe to the DOUBLE_TAP service
only when it's running (safest to wait until you receive the onSubscribeResult-callback with resultCode == 200), call the DoubleTAPConfig().
Full disclosure: I work for the Movesense team

Related

Can't create non-infinite loop starting from infinite loop

Second edit: I've now somehow managed to let the script run (changing int main(void) to int main()) with a defined "for", however it egregiously ignores me and runs endlessly anyways:
int main() { // was int main(void)!!
// We'll start by performing hardware and peripheral setup.
SetupHardware();
// We'll then enable global interrupts for our use.
GlobalInterruptEnable();
//int counter = 0;
//int x_times = 1000;
// Once that's done, we'll enter an infinite loop.
for (day = 0;day = 2;day++) // RE-ENABLE THIS TO PUT THE INFINITE LOOP BACK IN!
// trying to make a loop that will only run a certain number of times (5)...
{
// We need to run our task to process and deliver data for our IN and OUT endpoints.
HID_Task();
// We also need to run the main USB management task.
USB_USBTask();
}
return 0;
}
Edit 1: with some clarifications:
This code is taken from a project to make the Arduino look like a joystick for the Nintendo Switch, based off https://github.com/shinyquagsire23/Switch-Fightstick .
I think the Arduino is using a different firmware from the one it comes with, as the whole script is flashed on the Arduino after 'make' compiles a .hex file including Joystick.c and other scripts as well. I'll include the entire content of Joystick.c below with comments from the original developer in hopes of adding clarity to this request.
/*
Nintendo Switch Fightstick - Proof-of-Concept
Based on the LUFA library's Low-Level Joystick Demo
(C) Dean Camera
Based on the HORI's Pokken Tournament Pro Pad design
(C) HORI
This project implements a modified version of HORI's Pokken Tournament Pro Pad
USB descriptors to allow for the creation of custom controllers for the
Nintendo Switch. This also works to a limited degree on the PS3.
Since System Update v3.0.0, the Nintendo Switch recognizes the Pokken
Tournament Pro Pad as a Pro Controller. Physical design limitations prevent
the Pokken Controller from functioning at the same level as the Pro
Controller. However, by default most of the descriptors are there, with the
exception of Home and Capture. Descriptor modification allows us to unlock
these buttons for our use.
*/
#include "Joystick.h"
typedef enum {
UP,
DOWN,
LEFT,
RIGHT,
X,
Y,
A,
B,
L,
R,
THROW,
NOTHING,
TRIGGERS,
HOME
} Buttons_t;
typedef struct {
Buttons_t button;
uint16_t duration;
} command;
static const command step[] = {
// Setup controller
{ NOTHING, 250 },
{ TRIGGERS, 5 },
{ NOTHING, 150 },
{ TRIGGERS, 5 },
{ NOTHING, 150 },
{ TRIGGERS, 5 },
{ NOTHING, 250 },
// Talk to Pondo
{ NOTHING, 100 },
{ A, 10 }, // Start
{ NOTHING, 20 },
{ A, 10 },
{ NOTHING, 20 },
{ A, 10 },
{ NOTHING, 20 },
{ A, 10 },
{ NOTHING, 50 },
{ HOME, 10 },
{ NOTHING, 30 },
{ DOWN, 5 },
{ NOTHING, 5 },
{ RIGHT, 5 },
{ NOTHING, 5 },
{ RIGHT, 5 },
{ NOTHING, 5 },
{ RIGHT, 5 },
{ NOTHING, 5 },
{ RIGHT, 5 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 20 },
{ DOWN, 60 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 5 },
{ DOWN, 5 },
{ NOTHING, 5 },
{ DOWN, 5 },
{ NOTHING, 5 },
{ DOWN, 5 },
{ NOTHING, 5 },
{ DOWN, 5 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 10 },
{ DOWN, 5 },
{ NOTHING, 5 },
{ DOWN, 5 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 10 },
{ UP, 5 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 5 },
{ A, 10 },
{ NOTHING, 5 },
{ HOME, 10 },
{ NOTHING, 20 },
{ HOME, 10 },
{ NOTHING, 30 },
{ B, 10 },
{ NOTHING, 20 },
{ A , 10 },
{ NOTHING, 100 }
};
// Main entry point.
int main(void) {
// We'll start by performing hardware and peripheral setup.
SetupHardware();
// We'll then enable global interrupts for our use.
GlobalInterruptEnable();
// Once that's done, we'll enter an infinite loop.
for (;;)
{
// We need to run our task to process and deliver data for our IN and OUT endpoints.
HID_Task();
// We also need to run the main USB management task.
USB_USBTask();
}
}
// Configures hardware and peripherals, such as the USB peripherals.
void SetupHardware(void) {
// We need to disable watchdog if enabled by bootloader/fuses.
MCUSR &= ~(1 << WDRF);
wdt_disable();
// We need to disable clock division before initializing the USB hardware.
clock_prescale_set(clock_div_1);
// We can then initialize our hardware and peripherals, including the USB stack.
#ifdef ALERT_WHEN_DONE
// Both PORTD and PORTB will be used for the optional LED flashing and buzzer.
#warning LED and Buzzer functionality enabled. All pins on both PORTB and \
PORTD will toggle when printing is done.
DDRD = 0xFF; //Teensy uses PORTD
PORTD = 0x0;
//We'll just flash all pins on both ports since the UNO R3
DDRB = 0xFF; //uses PORTB. Micro can use either or, but both give us 2 LEDs
PORTB = 0x0; //The ATmega328P on the UNO will be resetting, so unplug it?
#endif
// The USB stack should be initialized last.
USB_Init();
}
// Fired to indicate that the device is enumerating.
void EVENT_USB_Device_Connect(void) {
// We can indicate that we're enumerating here (via status LEDs, sound, etc.).
}
// Fired to indicate that the device is no longer connected to a host.
void EVENT_USB_Device_Disconnect(void) {
// We can indicate that our device is not ready (via status LEDs, sound, etc.).
}
// Fired when the host set the current configuration of the USB device after enumeration.
void EVENT_USB_Device_ConfigurationChanged(void) {
bool ConfigSuccess = true;
// We setup the HID report endpoints.
ConfigSuccess &= Endpoint_ConfigureEndpoint(JOYSTICK_OUT_EPADDR, EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1);
ConfigSuccess &= Endpoint_ConfigureEndpoint(JOYSTICK_IN_EPADDR, EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1);
// We can read ConfigSuccess to indicate a success or failure at this point.
}
// Process control requests sent to the device from the USB host.
void EVENT_USB_Device_ControlRequest(void) {
// We can handle two control requests: a GetReport and a SetReport.
// Not used here, it looks like we don't receive control request from the Switch.
}
// Process and deliver data from IN and OUT endpoints.
void HID_Task(void) {
// If the device isn't connected and properly configured, we can't do anything here.
if (USB_DeviceState != DEVICE_STATE_Configured)
return;
// We'll start with the OUT endpoint.
Endpoint_SelectEndpoint(JOYSTICK_OUT_EPADDR);
// We'll check to see if we received something on the OUT endpoint.
if (Endpoint_IsOUTReceived())
{
// If we did, and the packet has data, we'll react to it.
if (Endpoint_IsReadWriteAllowed())
{
// We'll create a place to store our data received from the host.
USB_JoystickReport_Output_t JoystickOutputData;
// We'll then take in that data, setting it up in our storage.
while(Endpoint_Read_Stream_LE(&JoystickOutputData, sizeof(JoystickOutputData), NULL) != ENDPOINT_RWSTREAM_NoError);
// At this point, we can react to this data.
// However, since we're not doing anything with this data, we abandon it.
}
// Regardless of whether we reacted to the data, we acknowledge an OUT packet on this endpoint.
Endpoint_ClearOUT();
}
// We'll then move on to the IN endpoint.
Endpoint_SelectEndpoint(JOYSTICK_IN_EPADDR);
// We first check to see if the host is ready to accept data.
if (Endpoint_IsINReady())
{
// We'll create an empty report.
USB_JoystickReport_Input_t JoystickInputData;
// We'll then populate this report with what we want to send to the host.
GetNextReport(&JoystickInputData);
// Once populated, we can output this data to the host. We do this by first writing the data to the control stream.
while(Endpoint_Write_Stream_LE(&JoystickInputData, sizeof(JoystickInputData), NULL) != ENDPOINT_RWSTREAM_NoError);
// We then send an IN packet on this endpoint.
Endpoint_ClearIN();
}
}
typedef enum {
SYNC_CONTROLLER,
SYNC_POSITION,
BREATHE,
PROCESS,
CLEANUP,
DONE
} State_t;
State_t state = SYNC_CONTROLLER;
#define ECHOES 2
int echoes = 0;
USB_JoystickReport_Input_t last_report;
int report_count = 0;
int xpos = 0;
int ypos = 0;
int bufindex = 0;
int duration_count = 0;
int portsval = 0;
// Prepare the next report for the host.
void GetNextReport(USB_JoystickReport_Input_t* const ReportData) {
// Prepare an empty report
memset(ReportData, 0, sizeof(USB_JoystickReport_Input_t));
ReportData->LX = STICK_CENTER;
ReportData->LY = STICK_CENTER;
ReportData->RX = STICK_CENTER;
ReportData->RY = STICK_CENTER;
ReportData->HAT = HAT_CENTER;
// Repeat ECHOES times the last report
if (echoes > 0)
{
memcpy(ReportData, &last_report, sizeof(USB_JoystickReport_Input_t));
echoes--;
return;
}
// States and moves management
switch (state)
{
case SYNC_CONTROLLER:
state = BREATHE;
break;
case SYNC_POSITION:
bufindex = 0;
ReportData->Button = 0;
ReportData->LX = STICK_CENTER;
ReportData->LY = STICK_CENTER;
ReportData->RX = STICK_CENTER;
ReportData->RY = STICK_CENTER;
ReportData->HAT = HAT_CENTER;
state = BREATHE;
break;
case BREATHE:
state = PROCESS;
break;
case PROCESS:
switch (step[bufindex].button)
{
case UP:
ReportData->LY = STICK_MIN;
break;
case LEFT:
ReportData->LX = STICK_MIN;
break;
case DOWN:
ReportData->LY = STICK_MAX;
break;
case RIGHT:
ReportData->LX = STICK_MAX;
break;
case A:
ReportData->Button |= SWITCH_A;
break;
case B:
ReportData->Button |= SWITCH_B;
break;
case R:
ReportData->Button |= SWITCH_R;
break;
case THROW:
ReportData->LY = STICK_MIN;
ReportData->Button |= SWITCH_R;
break;
case TRIGGERS:
ReportData->Button |= SWITCH_L | SWITCH_R;
break;
case HOME:
ReportData->Button |= SWITCH_HOME;
break;
default:
ReportData->LX = STICK_CENTER;
ReportData->LY = STICK_CENTER;
ReportData->RX = STICK_CENTER;
ReportData->RY = STICK_CENTER;
ReportData->HAT = HAT_CENTER;
break;
}
duration_count++;
if (duration_count > step[bufindex].duration)
{
bufindex++;
duration_count = 0;
}
if (bufindex > (int)( sizeof(step) / sizeof(step[0])) - 1)
{
// state = CLEANUP;
bufindex = 7;
duration_count = 0;
state = BREATHE;
ReportData->LX = STICK_CENTER;
ReportData->LY = STICK_CENTER;
ReportData->RX = STICK_CENTER;
ReportData->RY = STICK_CENTER;
ReportData->HAT = HAT_CENTER;
// state = DONE;
// state = BREATHE;
}
break;
case CLEANUP:
state = DONE;
break;
case DONE:
#ifdef ALERT_WHEN_DONE
portsval = ~portsval;
PORTD = portsval; //flash LED(s) and sound buzzer if attached
PORTB = portsval;
_delay_ms(250);
#endif
return;
}
// Prepare to echo this report
memcpy(&last_report, ReportData, sizeof(USB_JoystickReport_Input_t));
echoes = ECHOES;
}
Hi all, first time posting and mostly a C++ noob - please have mercy!
I think I have an unusual problem, in that I have a C++ script for an Arduino, which repeats itself indefinitely. I would like for this script to only execute itself a set number of times, but everything I've tried so far is failing.
This is the original piece of script with the infinite loop:
int main(void) {
SetupHardware();
GlobalInterruptEnable();
for (;;)
{
HID_Task();
USB_USBTask();
}
}
And this is what I've tried so far, to make the script run just twice:
int main(void) {
SetupHardware();
GlobalInterruptEnable();
int day = 0;
for (day = 0; day <= 2; day++)
{
HID_Task();
USB_USBTask();
}
return;
}
I've tried getting rid of "return", and according to 'make' the code looks ok, as I don't get any error message. However, when I flash the Arduino with this script, it doesn't work at all. The moment I restore the infinite loop, everything gets back to working normally (and endlessly).
For reference, here's the link to the GitHub project, the file I'm trying to edit is Joystick.c: https://github.com/bertrandom/snowball-thrower
I'm a bit lost and all ears/eyes for advice.
Thanks!
Could you try changing the script to
int main(void) {
SetupHardware();
GlobalInterruptEnable();
int counter = 0;
int x_times = 1000;
for (;;)
{
HID_Task();
USB_USBTask();
++counter;
if (counter >= x_times) return;
}
}
and see if it works to some degree?

Why I receive no WT_PACKETEXT window messages after initializing Wintab extensions?

I'm currently trying to process the absolute value of a drawing tablet's touch ring, through the Wintab API. However, despite following instructions as they are described in the documentation, it seems like the WTOpen call doesn't set any extension settings. Using the touch ring after initializing Wintab still triggers the default events, while the default events for pen inputs are suppressed and all pen inputs related to my application instead.
Here are the relevant segments of code:
...
#include "wintab.h"
#define PACKETDATA (PK_X | PK_Y | PK_Z | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_TIME | PK_BUTTONS)
#define PACKETMODE 0
#define PACKETTOUCHSTRIP PKEXT_ABSOLUTE
#define PACKETTOUCHRING PKEXT_ABSOLUTE
#include "pktdef.h"
...
internal b32
InitWinTab(HWND Window, window_mapping *Map)
{
if(!LoadWintabFunctions())
return false;
LOGCONTEXT Tablet;
AXIS TabletX, TabletY, TabletZ, Pressure;
if(!gpWTInfoA(WTI_DEFCONTEXT, 0, &Tablet))
return false;
gpWTInfoA(WTI_DEVICES, DVC_X, &TabletX);
gpWTInfoA(WTI_DEVICES, DVC_Y, &TabletY);
gpWTInfoA(WTI_DEVICES, DVC_Z, &TabletZ);
gpWTInfoA(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
UINT TouchStripOffset = 0xFFFF;
UINT TouchRingOffset = 0xFFFF;
for(UINT i = 0, ScanTag = 0; gpWTInfoA(WTI_EXTENSIONS + i, EXT_TAG, &ScanTag); i++)
{
if (ScanTag == WTX_TOUCHSTRIP)
TouchStripOffset = i;
if (ScanTag == WTX_TOUCHRING)
TouchRingOffset = i;
}
Tablet.lcOptions |= CXO_MESSAGES;
Tablet.lcPktData = PACKETDATA;
Tablet.lcPktMode = PACKETMODE;
Tablet.lcMoveMask = PACKETDATA;
Tablet.lcBtnUpMask = Tablet.lcBtnDnMask;
Tablet.lcInOrgX = 0;
Tablet.lcInOrgY = 0;
Tablet.lcInExtX = TabletX.axMax;
Tablet.lcInExtY = TabletY.axMax;
if(TouchStripOffset != 0xFFFF)
{
WTPKT DataMask;
gpWTInfoA(WTI_EXTENSIONS + TouchStripOffset, EXT_MASK, &DataMask);
Tablet.lcPktData |= DataMask;
}
if(TouchRingOffset != 0xFFFF)
{
WTPKT DataMask;
gpWTInfoA(WTI_EXTENSIONS + TouchRingOffset, EXT_MASK, &DataMask);
Tablet.lcPktData |= DataMask;
}
Map->AxisMax.x = (r32)TabletX.axMax;
Map->AxisMax.y = (r32)TabletY.axMax;
Map->AxisMax.z = (r32)TabletZ.axMax;
Map->PressureMax = (r32)Pressure.axMax;
if(!gpWTOpenA(Window, &Tablet, TRUE))
return false;
return(TabletX.axMax && TabletY.axMax && TabletZ.axMax && Pressure.axMax);
}
...
case WT_PACKET:
{
PACKET Packet;
if(gpWTPacket((HCTX)LParam, (UINT)WParam, &Packet))
{
...
}
} break;
case WT_PACKETEXT:
{
PACKETEXT Packet;
if(gpWTPacket((HCTX)LParam, (UINT)WParam, &Packet))
{
...
}
} break;
...
The bitmask for the packet data in the initialization have sensible bits set for both extensions and don't overlap with the existing bitmask. No stage of the initialization fails. WT_PACKET gets called only with valid packet data while WT_PACKETEXT never gets called. Furthermore, calling WTPacketsGet with a PACKETEXT pointer on the HCTX returned by WTOpen fills the packet with garbage from the regular packet queue. This leaves me with the conclusion that somehow WTOpen didn't receive notification that the extensions should be loaded and I'm unable to find what else I should define in the LOGCONTEXT data structure to change that.
Is there a mistake in my initialization? Or is there a way to get a better readout to why the extensions didn't load?
It turned out that a driver setting prevented the extension packets from being sent, in favor of using the touch ring for different function. Changing this setting resolved the issue. The code didn't contain any errors itself.

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

How to simulate digital logic circuits with feedback loops?

I learning how to simulate digital logic circuits .
I am presenting the source code of my first attempt here.
It is small program for simulating circuits consisting
of AND,OR and NOT gates.
This code works well for circuits without loops.
When circuit loops are introduced it causes a stack overflow because of endless recursion.
Please help me to remove this bug.
Please note that this is a hobby project and any help will be appreciated.
Source code :
#include <cstdlib>
#include <iostream>
using namespace std;
class LogicGate
{
int type;//gate type: 0 for NOT, 1 for OR, 2 for AND
//pins
bool ina;//input a
bool inb;//input b::this pin is not used for NOT gate
bool outc;//output
//fan-in
LogicGate* ga;//gate connected to input a
LogicGate* gb;//gate connected to input b
//fan-out
LogicGate* gc;//gate connected to output
int gctarget;//target input to which the gate "gc" is connected, 0 for input a, 1 for input c
public:
char* name;
LogicGate()
{
ina = inb = outc = false;
ga = gb = gc = (LogicGate*)0;
type = 0;
}
LogicGate(bool a, bool b)
{
ina = a; inb = b; outc = false;
ga = gb = gc = (LogicGate*)0;
type = 0;
}
//set logic
void settype(int t){if(t>=0&&t<3)type=t;else type=0;}
//set input
void seta(bool a){ ina = a; }
void setb(bool b){ inb = b; }
void setab(bool a, bool b){ina = a; inb = b; }
//connect gate
void cona(LogicGate* cga){ ga = cga; }
void conb(LogicGate* cgb){ gb = cgb; }
void conab(LogicGate* cga, LogicGate* cgb){ ga = cga; gb = cgb; }
//connect the output of this gate to another gate's input
void chainc(LogicGate* cgc, int target)
{
gc = cgc;
gctarget = target;
if(target==0) cgc->cona(this); else cgc->conb(this);
}
//calculate output
bool calcout()
{
//if the input is not available make it available by forcing the connected gates to calculate
if(ga){ ina = ga->calcout(); } //BUG:this may cause Stack overflow for circuits with loops
if(gb){ inb = gb->calcout(); }//BUG:this may cause Stack overflow for circuits with loops
//do the logic when inputs are available
switch(type)
{
case 0:
outc = !ina; break;
case 1:
outc = ina || inb; break;
case 2:
outc = ina && inb; break;
}
//if a gate is connected to output pin transfer the output value to the target input pin of the gate
if(gc){
if(gctarget==0){
gc->seta(outc);
}else{
gc->setb(outc);
}
}
//for debugging only
cout<<name<<" outputs "<<outc<<endl;
return outc;
}
};
int main(int argc, char *argv[])
{
LogicGate x,z;
//AND gate
z.settype(2);
z.seta(false);
z.setb(true);
z.name = "ZZZ";
//OR gate
x.settype(1);
x.cona(&z); // take one input from AND gate's output
x.setb(true);
x.name = "XXX";
//z.cona(&x);// take one input from OR gate's output to make a loop:: results in stack overflow
x.chainc(&z,0);//connect the output to AND gate's input "a" to form loop:: results in stack overflow
cout<<"final output"<<x.calcout()<<endl;
return 0;
}
The Problem here is that you are Looping infinitely. A program behaves somehow different than real logic gates. I see two possibilities here:
1) Implement cycles
You can implement it like a cpu works. a call to calcout only calculates to Output of one gate and iterates to the next one. You could create a Container class for your gates:
class GateContainer
{
//Contains all gates of your "circuit"
std::vector<LogicalGate> allGates;
//Contains all current gates to be processed
std::queue<LogicalGate*> currentGates;
void nextStep();
}
The nextStep function could look like this:
void GateContainer::nextStep()
{
//Get first gate from queue
LogicalGate *current = currentGates.front();
currentGates.pop();
//Do all the calculations with th current gate
//Push the gate connected to the Output to the list
currentGates.push(current->gc);
}
Please not that this code is untested and may also Need some error checks
2) Try to catch Loops
You can also try to catch Loops in calcout. You could achieve this by creating a flag in LogicalGate and reset it every time before calling calcout:
class LogicalGate
{
...
bool calculated;
...
}
Now before calling calcout() You Need to set calculated to false for every gate. Then, calcout could look something like this:
bool calcout()
{
calculated = true;
if(ga && !ga->calculated){ ina = ga->calcout(); }
if(gb && !ga->calculated){ inb = gb->calcout(); }
...
}