C++ variable changed during interrupt resets after interrupt - c++

I'm trying to use a flag to see if an event has finished.
Via the debugger, I have seen that the interrupt triggers correctly and does set the value of transmitCompleteI2c to 1. When I return to the if statement which checks whether or not the flag has been set to 1, it has been reset to 0.
The only 2 locations where i alter the value of transmitCompleteI2c are after the if statement and in the interrupt routine.
I'm running the following bits of code. The declaration of transmitCompleteI2c is done in the header file of the class. fireEvent() is a member function of the class I2c.
volatile uint8_t transmitCompleteI2c;
void I2c::foo() {
if (transmitCompleteI2c) {
transmitCompleteI2c = 0;
// trigger event which sets transmitComplete back to 0 when done
fireEvent();
}
}
void I2c::sendHandler(XIic *InstancePtr) {
transmitCompleteI2c = 1;
}
After some extensive digging, it turned out that the address the interrupt routine writes a transmitCompleteI2c to is not the same as the variable in the class. Even after renaming the variable, it is still happening. Below are the header file and source code of my class.
Header file:
#define SLAVE_ADDRESS 0xA0/2
#define SEND_COUNT 16
#define RECEIVE_COUNT 16
#define IIC_INTR_ID XPAR_FABRIC_AXI_IIC_0_IIC2INTC_IRPT_INTR
#define IIC_DEVICE_ID XPAR_AXI_IIC_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
class I2c{
private:
void sendHandler(void* InstancePtr, int);
void receiveHandler(XIic* InstancePtr, int);
void statusHandler(XIic* InstancePtr, int);
XIic_Config* configPtr;
const XScuGic& intcInterrupt;
XIic iicDevice;
int status;
uint8_t writeBuffer[SEND_COUNT];
uint8_t readBuffer[RECEIVE_COUNT];
volatile uint8_t transmitCompleteI2c, receiveComplete;
volatile bool test;
public:
I2c() : intcInterrupt{IntcInstance} {};
uint8_t initialize();
int writeData(u16 ByteCount);
int readData(u8 *BufferPtr, u16 ByteCount);
};
Implementation:
Initialization function:
uint8_t I2c::initialize() {
// init driver
configPtr = XIic_LookupConfig(IIC_DEVICE_ID);
XIic_CfgInitialize(&iicDevice, configPtr, configPtr->BaseAddress);
//init interrupt system
XScuGic_SetPriorityTriggerType((XScuGic*) &intcInterrupt, IIC_INTR_ID, 0xA0, 0x3);
status = XScuGic_Connect((XScuGic*) &intcInterrupt, IIC_INTR_ID, (Xil_ExceptionHandler) XIic_InterruptHandler, &iicDevice);
XScuGic_Enable((XScuGic*) &intcInterrupt, IIC_INTR_ID);
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler, &IntcInstance);
Xil_ExceptionEnable();
// set transmit flag
transmitCompleteI2c = true;
xil_printf("%08x\n", &transmitCompleteI2c);
// attach interrupts to i2c device
XIic_SetSendHandler(&iicDevice, &iicDevice, (XIic_Handler) (&I2c::sendHandler));
XIic_SetRecvHandler(&iicDevice, &iicDevice, (XIic_Handler) &I2c::receiveHandler);
XIic_SetStatusHandler(&iicDevice, &iicDevice, (XIic_StatusHandler) &I2c::statusHandler);
// set slave address
status = XIic_SetAddress(&iicDevice, XII_ADDR_TO_SEND_TYPE, SLAVE_ADDRESS);
// start device
status = XIic_Start(&iicDevice);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
return 0;
}
Write data function:
int I2c::writeData(u16 ByteCount) {
xil_printf("%08x\n", &transmitCompleteI2c);
/*
* Set flag
*/
transmitCompleteI2c = false;
/*
* Send the data
*/
status = XIic_MasterSend(&iicDevice, &writeBuffer[0], 6);
if (status != XST_SUCCESS) {
xil_printf("%d\n", status);
return XST_FAILURE;
}
return XST_SUCCESS;
}
Interrupt routine:
void I2c::sendHandler(void *InstancePtr, int byteCount) {
transmitCompleteI2c = true;
xil_printf("%08x\n", &transmitCompleteI2c);
}

"...it should all be in the same class"
but transmitCompleteI2c isn't a static member, so it has to be the same object, not just the same class. Every instance of I2c gets its own copy of each non-static data member.
You can just make it static if you know only one copy is required (this effectively makes it a global, although still only accessible by I2c) - but don't forget it needs a single non-inline definition.
Alternatively, you need to figure out what I2c objects exist, and where they're created. You can implement all constructor and assignment operator overloads with logging (or to set breakpoints) if you're having trouble figuring out where they get created.

Related

no matching function for call unresolved overloaded function type

First I'm a newbie to C++ so my question might be already answered somewhere but I couldn't find a straightforward answer to it.
I'm creating a simple library for my hardware. I'm using a Scheduler library which is working fine on Arduino IDE (here is the example), but when I compile the code with my own IDE (Atom+PlatformIO) this error comes up:
lib\SRF08\SRF08.cpp:43:30: error: no matching function for call to 'SchedulerClass::startLoop(<unresolved overloaded functi
on type>)'
I removed some of the codes but if you need the rest I can put it.
SRF08.h
#ifndef SRF08_h
#define SRF08_h
#include "Arduino.h"
class SRF08
{
public:
//main constructor
SRF08(uint8_t address=address_1);
// init the sensor
void begin(void);
//change sensor address from oldAddress to newAddress
void changeAddress(uint16_t oldAddress, uint16_t newAddress);
// scan for a single sensor address
int8_t scanner(void);
// scan for multiple sensors and return the table of addresses
struct table_value scan_all(void);
uint16_t output_value;
void read(void);
private:
// the main I2C address of Sensor
uint16_t _address;
//read sansor value base on centimiter
};
#endif
SRF08.cpp
#include "Wire.h"
#include "SRF08.h"
// Include Scheduler since we want to manage multiple tasks.
#include "Scheduler.h"
SRF08::SRF08(uint8_t address)
{
//main constructor, address is the sensor address if u dont know it try scanner first
//address must be an integer number between 1 to 9
if (address == 1) _address = address_1;
else _address = address_1;
}
void SRF08::begin(){
//initilize I2C
Wire.begin();
output_value = 0;
Scheduler.startLoop(SRF08::read); //here is my error
}
void SRF08::read(){
int reading = 0;
// step 1: instruct sensor to read echoes
Wire.beginTransmission(_address); // transmit to device #112 (0x70)
// the address specified in the datasheet is 224 (0xE0)
// but i2c adressing uses the high 7 bits so it's 112
Wire.write(byte(0x00)); // sets register pointer to the command register (0x00)
Wire.write(byte(0x51)); // command sensor to measure in "inches" (0x50)
// use 0x51 for centimeters
// use 0x52 for ping microseconds
Wire.endTransmission(); // stop transmitting
// step 2: wait for readings to happen
delay(70); // datasheet suggests at least 65 milliseconds
// step 3: instruct sensor to return a particular echo reading
Wire.beginTransmission(_address); // transmit to device #112
Wire.write(byte(0x02)); // sets register pointer to echo #1 register (0x02)
Wire.endTransmission(); // stop transmitting
// step 4: request reading from sensor
Wire.requestFrom(_address, 2); // request 2 bytes from slave device #112
// step 5: receive reading from sensor
if (2 <= Wire.available()) { // if two bytes were received
reading = Wire.read(); // receive high byte (overwrites previous reading)
reading = reading << 8; // shift high byte to be high 8 bits
reading |= Wire.read(); // receive low byte as lower 8 bits
output_value = reading; // print the reading
}
//yield();
}
Scheduler.h
#ifndef _SCHEDULER_H_
#define _SCHEDULER_H_
#include <Arduino.h>
extern "C" {
typedef void (*SchedulerTask)(void);
typedef void (*SchedulerParametricTask)(void *);
}
class SchedulerClass {
public:
SchedulerClass();
static void startLoop(SchedulerTask task, uint32_t stackSize = 1024);
static void start(SchedulerTask task, uint32_t stackSize = 1024);
static void start(SchedulerParametricTask task, void *data, uint32_t stackSize = 1024);
static void yield() { ::yield(); };
};
extern SchedulerClass Scheduler;
#endif
Scheduler.cpp
#include "Scheduler.h"
extern "C" {
#define NUM_REGS 10 // r4-r11, sp, pc
typedef struct CoopTask {
uint32_t regs[NUM_REGS];
void* stackPtr;
struct CoopTask* next;
struct CoopTask* prev;
} CoopTask;
static CoopTask *cur = 0;
...
void yield(void) {
coopDoYield(cur);
}
}; // extern "C"
SchedulerClass::SchedulerClass() {
coopInit();
}
static void startLoopHelper(void *taskData) {
SchedulerTask task = reinterpret_cast<SchedulerTask>(taskData);
while (true)
task();
}
void SchedulerClass::startLoop(SchedulerTask task, uint32_t stackSize) {
coopSpawn(startLoopHelper, reinterpret_cast<void *>(task), stackSize);
}
static void startTaskHelper(void *taskData) {
SchedulerTask task = reinterpret_cast<SchedulerTask>(taskData);
task();
}
void SchedulerClass::start(SchedulerTask task, uint32_t stackSize) {
coopSpawn(startTaskHelper, reinterpret_cast<void *>(task), stackSize);
}
void SchedulerClass::start(SchedulerParametricTask task, void *taskData, uint32_t stackSize) {
coopSpawn(task, taskData, stackSize);
}
SchedulerClass Scheduler;
Thanks to #Someprogrammerdude to help. I needed to declare the read function as static.
SRF08.h
#ifndef SRF08_h
#define SRF08_h
#include "Arduino.h"
class SRF08
{
public:
//main constructor
SRF08(uint8_t address=address_1);
// init the sensor
void begin(void);
//change sensor address from oldAddress to newAddress
void changeAddress(uint16_t oldAddress, uint16_t newAddress);
// scan for a single sensor address
int8_t scanner(void);
// scan for multiple sensors and return the table of addresses
struct table_value scan_all(void);
static uint16_t output_value;
static void read(void);
static uint16_t static_address;
private:
// the main I2C address of Sensor
uint16_t _address;
//read sansor value base on centimiter
};
#endif
SRF08.cpp
#include "Wire.h"
#include "SRF08.h"
// Include Scheduler since we want to manage multiple tasks.
#include "Scheduler.h"
//initilize static members
uint16_t SRF08::output_value;
uint16_t SRF08::static_address;
SRF08::SRF08(uint8_t address)
{
//main constructor, address is the sensor address if u dont know it try scanner first
//address must be an integer number between 1 to 9
if (address == 1) _address = address_1;
else _address = address_1;
static_address = _address;
//begin();
}
void SRF08::begin(){
//initilize I2C
Wire.begin();
output_value = 0;
Scheduler.startLoop(read); //here is my error
}
void SRF08::read(){
int reading = 0;
// step 1: instruct sensor to read echoes
Wire.beginTransmission(static_address); // transmit to device #112 (0x70)
// the address specified in the datasheet is 224 (0xE0)
// but i2c adressing uses the high 7 bits so it's 112
Wire.write(byte(0x00)); // sets register pointer to the command register (0x00)
Wire.write(byte(0x51)); // command sensor to measure in "inches" (0x50)
// use 0x51 for centimeters
// use 0x52 for ping microseconds
Wire.endTransmission(); // stop transmitting
// step 2: wait for readings to happen
delay(70); // datasheet suggests at least 65 milliseconds
// step 3: instruct sensor to return a particular echo reading
Wire.beginTransmission(static_address); // transmit to device #112
Wire.write(byte(0x02)); // sets register pointer to echo #1 register (0x02)
Wire.endTransmission(); // stop transmitting
// step 4: request reading from sensor
Wire.requestFrom(static_address, 2); // request 2 bytes from slave device #112
// step 5: receive reading from sensor
if (2 <= Wire.available()) { // if two bytes were received
reading = Wire.read(); // receive high byte (overwrites previous reading)
reading = reading << 8; // shift high byte to be high 8 bits
reading |= Wire.read(); // receive low byte as lower 8 bits
output_value = reading; // print the reading
//output_value = reading;
}
yield();
}

Wire.onReceive() function on Arduino

I am trying to take what is in this example given by the Arduino Wire library, and apply it to a program I am writing.
This is my code. The Comm.NDP[] statements are other class instances not saved in this file, so I believe you can ignore them.
**
* I2C.cpp handles sending of event messages
* between the LORA and MEGA via I2C protocol.
*/
#include "I2C.h"
#include "DATA.h"
#include "Globals.h"
#include <Wire.h>
#include <Arduino.h>
/**
* Constructor used to reference all other variables & functions.
*/
I2C::I2C() {
}
/**
* Assigns the proper address to the current micro controller.
*/
void I2C::initialize() {
//Sets the address for the current micro controller.
// Mega - 0
// LoRa - 1
Wire.begin(0);
Wire.setClock(8000000);
//Registers recieveEvent as a interrupt.
Wire.onReceive(receiveEvent); // register event
}
/**
* Receives byte over I2C Connection.
*/
static void receiveEvent(int howmany) {
//Iterator used below.
int i = 0;
for(i=0;i<120;i++) {
Comm.NDP[i] = ' ';
}
//Resets iterator.
i = 0;
//Checks to see if serial port is empty.
while (1 < Wire.available()) {
//Reads in single character from serial port.
char character = Wire.read();
NDP[i] = character;
i++;
}
Serial.println(Comm.NDP);
}
The example code from Arduino's Wire.h library
#include <Wire.h>
void setup() {
Wire.begin(8); // join i2c bus with address #8
Wire.onReceive(receiveEvent); // register event
Serial.begin(9600); // start serial for output
}
void loop() {
delay(100);
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
while (1 < Wire.available()) { // loop through all but the last
char c = Wire.read(); // receive byte as a character
Serial.print(c); // print the character
}
int x = Wire.read(); // receive byte as an integer
Serial.println(x); // print the integer
}
I am getting this error from the Arduino IDE.
error: invalid use of non-static member function
Wire.onReceive(receiveEvent); // register event
^
exit status 1
invalid use of non-static member function
You're missing declaration of receiveEvent before first use. Either move it's definition before begin or add there:
void receiveEvent(int howMany);

Values initialized and set outside interrupt are zero within interrupt C++ on Xilinx Zynq

I am experiencing a rather odd issue with my code where I have a value that I initialize in the main. Once I try to call that variable inside an interrupt it is 0. What am I missing here? Do different variables live within an interrupt?
I would like to note that this is a more theoretical question about interrupts and whether I can not share values between idle and interrupts.
Supervisor supervisor;
XScuTimer TimerInstance;
XScuGic IntcInstance;
uint32_t offset;
void interruptRoutine(void *CallBackRef) {
// Define pointer to timer
XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
// If timer is expired, clear interrupt status
if (XScuTimer_IsExpired(TimerInstancePtr)) {
XScuTimer_ClearInterruptStatus(TimerInstancePtr);
// Why is offset 0 within the interrupt routine?
xil_printf("Interrupt: %u\n", offset);
}
}
//.. I omitted the interrupt init method ..//
int main() {
while (true) {
// Get all sensor data
offset = 1;
}
}

C++ app performance varies when using threads

I have a C++ app with 2 threads. The app displays a gauge on the screen, with an indicator that rotates based on an angle received via UDP socket. My problem is that the indicator should be rotating at a constant rate but it behaves like time slows down at times, and it also fast-forwards to catch up quickly at other times, with some pauses intermittently.
Each frame, the display (main) thread guards a copy of the angle from the UDP thread. The UDP thread also guards writing to the shared variable. I use a Windows CriticalSection object to guard the 'communication' between threads. The UDP packet is received at approximately the same rate as the display update. I am using Windows 7, 64 bit, with a 4-core processor.
I am using a separate python app to broadcast the UDP packet. I use the python function, time.sleep, to keep the broadcast at a constant rate.
Why does the application slow down?
Why does the application seem to fast-forward instead of snapping to the latest angle?
What is the proper fix?
EDIT: I am not 100% sure all angle values are remembered when the app seems to 'fast forward'. The app is snapping to some value (not sure if it is the 'latest') at times.
EDIT 2: per request, some code.
void App::udp_update(DWORD thread_id)
{
Packet p;
_socket.recv(p); // edit: blocks until transmission is received
{
Locker lock(_cs);
_packet = p;
}
}
void App::main_update()
{
float angle_copy = 0.f;
{
Locker lock(_cs);
angle_copy = _packet.angle;
}
draw(angle_copy); // edit: blocks until monitor refreshes
}
Thread.h
class CS
{
private:
friend Locker;
CRITICAL_SECTION _handle;
void _lock();
void _unlock();
// not implemented by design
CS(CS&);
CS& operator=(const CS&);
public:
CS();
~CS();
};
class Locker
{
private:
CS& _cs;
// not implemented by design
Locker();
Locker(const Locker&);
Locker& operator=(const Locker&);
public:
Locker(CS& c)
: _cs(c)
{
_cs._lock();
}
~Locker()
{
_cs._unlock();
}
};
class Win32ThreadPolicy
{
public:
typedef Functor<void,TYPELIST_1(DWORD)> Callback;
private:
Callback _callback;
//SECURITY_DESCRIPTOR _sec_descr;
//SECURITY_ATTRIBUTES _sec_attrib;
HANDLE _handle;
//DWORD _exitValue;
#ifdef USE_BEGIN_API
unsigned int _id;
#else // USE_BEGIN_API
DWORD _id;
#endif // USE_BEGIN_API
/*volatile*/ bool _is_joined;
#ifdef USE_BEGIN_API
static unsigned int WINAPI ThreadProc( void* lpParameter );
#else // USE_BEGIN_API
static DWORD WINAPI ThreadProc( LPVOID lpParameter );
#endif // USE_BEGIN_API
DWORD _run();
void _join();
// not implemented by design
Win32ThreadPolicy();
Win32ThreadPolicy(const Win32ThreadPolicy&);
Win32ThreadPolicy& operator=(const Win32ThreadPolicy&);
public:
Win32ThreadPolicy(Callback& func);
~Win32ThreadPolicy();
void Spawn();
void Join();
};
/// helps to manage parallel operations.
/// attempts to mimic the C++11 std::thread interface, but also passes the thread ID.
class Thread
{
public:
typedef Functor<void,TYPELIST_1(DWORD)> Callback;
typedef Win32ThreadPolicy PlatformPolicy;
private:
PlatformPolicy _platform;
/// not implemented by design
Thread();
Thread(const Thread&);
Thread& operator=(const Thread&);
public:
/// begins parallel execution of the parameter, func.
/// \param func, the function object to be executed.
Thread(Callback& func)
: _platform(func)
{
_platform.Spawn();
}
/// stops parallel execution and joins with main thread.
~Thread()
{
_platform.Join();
}
};
Thread.cpp
#include "Thread.h"
void CS::_lock()
{
::EnterCriticalSection( &_handle );
}
void CS::_unlock()
{
::LeaveCriticalSection( &_handle );
}
CS::CS()
: _handle()
{
::memset( &_handle, 0, sizeof(CRITICAL_SECTION) );
::InitializeCriticalSection( &_handle );
}
CS::~CS()
{
::DeleteCriticalSection( &_handle );
}
Win32ThreadPolicy::Win32ThreadPolicy(Callback& func)
: _handle(NULL)
//, _sec_descr()
//, _sec_attrib()
, _id(0)
, _is_joined(true)
, _callback(func)
{
}
void Win32ThreadPolicy::Spawn()
{
// for an example of managing descriptors, see:
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa446595%28v=vs.85%29.aspx
//BOOL success_descr = ::InitializeSecurityDescriptor( &_sec_descr, SECURITY_DESCRIPTOR_REVISION );
//TODO: do we want to start with CREATE_SUSPENDED ?
// TODO: wrap this with exception handling
#ifdef USE_BEGIN_END
// http://msdn.microsoft.com/en-us/library/kdzttdcb%28v=vs.100%29.aspx
_handle = (HANDLE) _beginthreadex( NULL, 0, &Thread::ThreadProc, this, 0, &_id );
#else // USE_BEGIN_END
_handle = ::CreateThread( NULL, 0, &Win32ThreadPolicy::ThreadProc, this, 0, &_id );
#endif // USE_BEGIN_END
}
void Win32ThreadPolicy::_join()
{
// signal that the thread should complete
_is_joined = true;
// maybe ::WFSO is not the best solution.
// "Except that WaitForSingleObject and its big brother WaitForMultipleObjects are dangerous.
// The basic problem is that these calls can cause deadlocks,
// if you ever call them from a thread that has its own message loop and windows."
// http://marc.durdin.net/2012/08/waitforsingleobject-why-you-should-never-use-it/
//
// He advises to use MsgWaitForMultipleObjects instead:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684242%28v=vs.85%29.aspx
DWORD result = ::WaitForSingleObject( _handle, INFINITE );
// _handle must have THREAD_QUERY_INFORMATION security access enabled to use the following:
//DWORD exitCode = 0;
//BOOL success = ::GetExitCodeThread( _handle, &_exitValue );
}
Win32ThreadPolicy::~Win32ThreadPolicy()
{
}
void Win32ThreadPolicy::Join()
{
if( !_is_joined )
{
_join();
}
// this example shows that it is correct to pass the handle returned by CreateThread
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682516%28v=vs.85%29.aspx
::CloseHandle( _handle );
_handle = NULL;
}
DWORD Win32ThreadPolicy::_run()
{
// TODO: do we need to make sure _id has been assigned?
while( !_is_joined )
{
_callback(_id);
::Sleep(0);
}
// TODO: what should we return?
return 0;
}
#ifdef USE_BEGIN_END
unsigned int WINAPI Thread::ThreadProc( LPVOID lpParameter )
#else // USE_BEGIN_END
DWORD WINAPI Win32ThreadPolicy::ThreadProc( LPVOID lpParameter )
#endif // USE_BEGIN_END
{
Win32ThreadPolicy* tptr = static_cast<Win32ThreadPolicy*>( lpParameter );
tptr->_is_joined = false;
// when this function (ThreadProc) returns, ::ExitThread is used to terminate the thread with an "implicit" call.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx
return tptr->_run();
}
I know this is a bit in the assumption space but:
The rate you are talking about is set in "server" and "client" via a sleep that controls the speed with which the packets are sent. This is not necessarily the rate of actual transmission, as the OS can schedule your processes in a very asymmetric way (time wise).
This can mean that when the server gets more time, it will fill an OS buffer with packets (the client will get less processor time, thus, consumming at a lower rate => slowing down the meter). Then, when the client gets more time that the server, it will consume fast all packets, while the update thread will still do some waiting. But this doesn't mean it will "snap", because you are using a critical section to lock the packet update, so probably you don't get to consume too many packages from the OS buffer until a new update. (you may have a "snap to", but with a small step). I am basing this on the fact that i see no actual sleeping in your receive or update methods (the only sleep is done on server side).

XBee, external libraries and passing structures as arguments

I have very weird problem with a library I am creating. The library will be used to communicate between Arduino modules using XBee Series 1 modules. Library is very simple wrapper library around Arduino XBee library.
I have one function that reads received packet and sends it back. At the moment it is implemented as a simple "echo" service - the function just displays the data received and sends it back to per-defined address.
At the moment I have three versions of this function, out of which one is not working.
A function taking no arguments: void processPacket()
A function taking structure as a value as an argument: void processPacket(valuesStruct valuesStructData) - THIS VERSION OF THE FUNCTION IS NOT WORKING!
A function taking pointer to the structure as an argument: void processPacket(valuesStruct* valuesStructData)
At this moment I noticed strange behavior in the 2nd version of the function. I do nothing with the passed argument - the content of all three functions is the same. In 2nd case the function reads wrong values from received XBee packet. In the 1st and 3rd case the function performs correctly.
Code:
ExampleLib.h
#ifndef ExampleLib_h
#define ExampleLib_h
#include "Arduino.h"
#include <XBee.h>
#define ADDRESS_BROADCAST 0xffff
#define ADDRESS_PC 0x3333
typedef struct
{
int valA;
int valB;
int valC;
} valuesStruct;
class ExampleLib
{
public:
ExampleLib();
void setSerial(Stream &serial);
boolean tryReceivePacket();
void processPacket();
// THIS FUNCTION IS NOT WORKING!
void processPacket(valuesStruct valuesStructData);
void processPacket(valuesStruct* valuesStructData);
private:
XBee xbee;
Rx16Response rx16;
};
#endif
ExampleLib.cpp
The value read in line byte* packetData = rx16.getData(); is wrong when we trigger processPacket(valuesStruct valuesStructData) function. In other cases the behavior is correct.
#include "Arduino.h"
#include <XBee.h>
#include "ExampleLib.h"
ExampleLib::ExampleLib()
{
xbee = XBee();
rx16 = Rx16Response();
}
void ExampleLib::setSerial(Stream &serial)
{
xbee.setSerial(serial);
}
boolean ExampleLib::tryReceivePacket()
{
xbee.readPacket();
if (xbee.getResponse().isAvailable()) {
// got something
if (xbee.getResponse().getApiId() == RX_16_RESPONSE) {
// got a rx packet
xbee.getResponse().getRx16Response(rx16);
return true;
}
else {
return false;
}
}
else if (xbee.getResponse().isError()) {
//nss.print("Error reading packet. Error code: ");
//nss.println(xbee.getResponse().getErrorCode());
// or flash error led
return false;
}
return false;
}
void ExampleLib::processPacket()
{
byte* packetData = rx16.getData();
byte dataLength = rx16.getDataLength();
Serial.print("START L:");
Serial.println(dataLength);
for (int i = 0; i < dataLength; i++) {
Serial.print(packetData[i]);
Serial.print(" - ");
}
Serial.println("END");
//16-bit addressing: Enter address of remote XBee, typically the coordinator
Tx16Request tx = Tx16Request(ADDRESS_PC, packetData, sizeof(packetData));
xbee.send(tx);
}
void ExampleLib::processPacket(valuesStruct valuesStructData)
{
processPacket();
}
void ExampleLib::processPacket(valuesStruct* valuesStructData)
{
processPacket();
}
Arduino sketch
#include <XBee.h>
#include <ExampleLib.h>
ExampleLib exampleLibObj = ExampleLib();
void setup()
{
Serial.begin(9600);
exampleLibObj.setSerial(Serial);
}
void loop()
{
boolean isPacketReceived = exampleLibObj.tryReceivePacket();
if (isPacketReceived) {
// leave only one section, the rest should be commented
//Section 1: working
exampleLibObj.processPacket();
//Section 2: not working
// valuesStruct test;
// test.valA = 0;
// test.valB = 0;
// test.valC = 0;
// exampleLibObj.processPacket(test);
//Section 3: working
// valuesStruct* test;
// test->valA = 0;
// test->valB = 0;
// test->valC = 0;
// exampleLibObj.processPacket(test);
}
}
I am really puzzled why in this one case function is performing differently. Looking forward to any suggestions to that issue.
Thanks,
Michal
Are you sure it isn't your section 3 that's causing problems? Because you're declaring a pointer to a structure, but not allocating memory for that structure.
You'd typically write your code like this:
valuesStruct test;
test.valA = 0;
test.valB = 0;
test.valC = 0;
//Section 2: not working
exampleLibObj.processPacket(test);
//Section 3: working
exampleLibObj.processPacket(&test);
But you also wouldn't typically pass a structure to a function -- you'd pass a pointer to that structure. There really isn't a need for your second sample.