XBee, external libraries and passing structures as arguments - c++

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.

Related

BLE using ESP 32

I am trying to program a BLE client using an ESP32. I have used the attached code (which is the example code from the example section). My problem right now is, how do I get it to show the MAC address of the scanned device only, and how do I change this MAC address into a JSON format for a LoRa chip to transmit? Thank you so much for your time to read this post.
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
int scanTime = 5; //In seconds
BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
}
};
void setup() {
Serial.begin(115200);
Serial.println("Scanning...");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
}
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}
The result of your scan is foundDevices, which contains a list of all found devices. We can iterate through it to access the devices. The List contains BLEAdvertisedDevices which have a method called getAddress(). The address can be converted to a string using toString(). You can print the string or put it into your json container.
Your loop-code could look like this:
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
for (int i = 0; i < foundDevices.getCount(); ++i)
{
std::string address = foundDevices.getDevice(i).getAddress().toString();
int rssi = foundDevices.getDevice(i).getRSSI();
Serial.print(address.c_str());
Serial.print(rssi);
// TODO: Insert into JSON
}
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}

Arduino - MagStripe card.available pauses and no other code can run

I am using the MagStrip library (https://github.com/carlosefr/magstripelib). In my main loop I call the function which runs the code:
void magcardFunc()
{
static const byte DATA_BUFFER_LEN = 108;
static char magcard[DATA_BUFFER_LEN];
// Don't do anything if there isn't a card present
if (!card.available()) {
return;
}
// Read the card into the buffer "magcard" (as a null-terminated string)
short chars = card.read(magcard, DATA_BUFFER_LEN);
if (chars < 0) {
Serial.print("bad read");
return;
}
// Send the data to the computer if data was read
if (chars != 0) {
Serial.print(magcardstr);
}
}
The code successfully reads a magnetic card when inserted.
The problem is, I have other functions which are called from the main loop but they do not get called as the code pauses at:
if (!card.available()) {
return;
}
// Read the card into the buffer "magcard" (as a null-terminated string)
short chars = card.read(magcard, DATA_BUFFER_LEN);
If I insert a card into my mag reader (and leave it there), the program continues and the other functions are called. I want the main loop to continue and the other functions be called without having to leave a card inserted.
I have tried removing the IF statement:
if (!card.available()) {
return;
}
and I have tried reversing it
if (card.available()) {
return;
}
If I reverse it as above, the other functions run but when I do swipe a card, it is not read.
I get no compiler errors.
EDIT:
Here is an example sketch which replicates the problem. I noticed that this sketch actually did call the otherFunc when the mag card reader was unplugged. I am not sure why when it is plugged in it pauses. Maybe an interrupt is always on?
#include <MagStripe.h>
void magcardFunc();
void otherFunc();
MagStripe card;
static const byte DATA_BUFFER_LEN = 108;
static char data[DATA_BUFFER_LEN];
void setup()
{
// The card data will be sent over serial...
Serial.begin(9600);
// Initialize the library for reading track 2...
card.begin(2);
}
void loop()
{
magcardFunc();
otherFunc();
}
void magcardFunc()
{
// Don't do anything if there isn't a card present...
if (!card.available()) {
return;
}
Any help is appreciated.

How to make encoder work with two libraries?

First I'd like to say I'm very new to c++, that's why I'm using the Arduino core and libraries on ESP32, and I would like to apologize for the dumpster fire you're about to see below.
Simply making a custom keyboard with buttons and encoders. When booted, select one of two modes: blekeyboard or ble midi control surface.
The button works in both modes but the encoder only works in whichever mode is declared last. (so in this script order, both the encoder and button in mode 1 blekeyboard works, while only the button works in mode 2.)
What did I do wrong and what can I do? Any suggestions regarding the problem or the overall script is welcome.
Thank you in advance.
#include <Arduino.h>
#include <BleKeyboard.h>
BleKeyboard bleKeyboard;
#define ESP32
#include <encoder.h>
#include <Control_Surface.h>
#include <MIDI_Interfaces/BluetoothMIDI_Interface.hpp>
BluetoothMIDI_Interface midi;
const int usermodebutton1 = 2;
const int usermodebutton2 = 0;
int usermode = 0;
// ---------------------- mode 2 MIDI Input Elements ------------------------ //
using namespace MIDI_Notes;
NoteButton csButton1 = {
2,
note(C, 4),
};
CCRotaryEncoder csEnc1 = {
{26, 25}, // pins
MCU::V_POT_1, // MIDI address (CC number + optional channel)
1, // optional multiplier if the control isn't fast enough
};
// -------------------------- mode 1 blekeyboard --------------------------- //
int kbutton1 = 2;
int kbutton1State;
int keyInterval = 400000;
Encoder kencoder1(25, 26);
int encInterval = 5000;
TickType_t currentTime;
TickType_t previousTime;
long enc1_oldPos = -999;
// ============================================================================= //
void setup()
{
pinMode(usermodebutton1, INPUT_PULLUP);
pinMode(usermodebutton2, INPUT_PULLUP);
Serial.begin(115200);
Serial.println("");
Serial.println("select mode:");
// ----------------------------------------------------------------------------- //
while (true)
{
if (digitalRead(usermodebutton1) == LOW)
{
usermode = 1;
Serial.println("mode 1 selected");
break;
}
if (digitalRead(usermodebutton2) == LOW)
{
usermode = 2;
Serial.println("mode 2 selected");
break;
}
delay(1000);
}
// ----------------------------------------------------------------------------- //
if (usermode == 1)
{
Serial.println("setup mode 1");
Serial.println("Starting BLE work...");
bleKeyboard.begin();
pinMode(kbutton1, INPUT_PULLUP);
previousTime = 0;
}
if (usermode == 2)
{
Serial.println("setup mode 2");
Serial.println("Control Surface BLE starting...");
RelativeCCSender::setMode(relativeCCmode::TWOS_COMPLEMENT);
Control_Surface.begin(); // Initialize Control Surface
}
}
// ============================================================================= //
void loop()
{
while (usermode == 1)
{
while (bleKeyboard.isConnected())
{
// mode 1 encoders
long enc1_newPos = kencoder1.read();
currentTime = esp_timer_get_time();
if (enc1_newPos < enc1_oldPos && currentTime - previousTime > encInterval)
{
enc1_oldPos = enc1_newPos;
previousTime = currentTime;
// bleKeyboard.write(KEY_MEDIA_VOLUME_DOWN);
Serial.print("enc1: ");
Serial.println(enc1_newPos);
}
if (enc1_newPos > enc1_oldPos && currentTime - previousTime > encInterval)
{
enc1_oldPos = enc1_newPos;
previousTime = currentTime;
// bleKeyboard.write(KEY_MEDIA_VOLUME_UP);
Serial.print("enc1: ");
Serial.println(enc1_newPos);
}
// mode 1 keys
kbutton1State = digitalRead(kbutton1);
if (kbutton1State == LOW && currentTime - previousTime > keyInterval)
{
previousTime = currentTime;
Serial.println("button 1 pressed");
bleKeyboard.print("1");
}
}
}
while (usermode == 2)
{
Control_Surface.loop(); // Refresh all elements
}
}
First of all, your code needs general tidying up. Do not instantiate an object after the #include or #define section.
Avoid doing this:
#include <BleKeyboard.h>
BleKeyboard bleKeyboard;
#include <MIDI_Interfaces/BluetoothMIDI_Interface.hpp>
BluetoothMIDI_Interface midi;
I generally organise my code as:
// Include libraries
#include <lib1.h>
#include <lib2.h>
...
// Macros
#define SOMETHING_FUNNY value // see the Macro name in capitals?
#define SOMETHING_USEFUL anothervalue
...
/*
* Variables Section.
* Also, I usually categorize my variables section by type: floats, integers, chars, etc. If I ever use booleans I pack them in a section called 'flags'. Also, if a boolean is used inside an interrupt it should be volatile
*/
Type var_name1 = initial_value; // the initaliztion is optional
AnotherType var_name2;
...
// Object instantiation
ClassName object1;
AnotherClassName object2 = new AnotherClassName(constructor_parameters);
...
//Setup Function
void setup(){
// Serial Port initialization goes first
Serial.begin(baudrate);
// Initialization routines --> use functions!
}
void loop(){
// Check for states of your FSM and call the respective function
}
// Functions definitions
output type myFunction1(args){
// Routine
}
...
Second, your code shouts to me 'Finite-States Machines', which is basically what you are implementing but not in a conventional way. Please read Nick Gammon's amazing tutorial on FSMs. This way, you will enumerate your states and trigger actions or events depending on the user's selection or hardware inputs without blocking the flow of the code.
Now, I assume that you have read both the .cpp files from your two libraries and checked if there are no conflicts between them? Also, which encoder.h library are you using? It looks like Paul Stoffregen's library, is it? I ask because his library is heavily based on interrupts and it was coded with some ASM blocks that might be not optimized for the ESP32, but please don't quote me on that. As enhzflep said:
I'd suspect there to be a problem with clashing interrupts.
and I could not agree more on that. If you are using that library, there is a Macro that could save your life:
#define ENCODER_DO_NOT_USE_INTERRUPTS
used in this example from the library's repo

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);

String gets corrupted - stack overflow?

I'm despairing! I want to have my ESP8266 receive a string from a TCP client, execute the corresponding function and give a TCP response. But unfortunately the response string gets corrupted in some strange way:
Assuming that I enter an 'unknown command', the first 11 bytes are printed correctly via the serial interface (the rest is dump) and the first 11 bytes received by the client are dump, but the rest is correct (see comments in script below). But when I enter the 'dim' command the result is correct (but the return string is also shorter than "error: unknown command").
For the moment I have absolutely no idea how to fix this, even though I've tried a lot.
#include <WiFiClient.h>
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <string.h>
struct parsed_query{
String command;
String arguments;
};
struct parsed_query parser(void){
// this function receives and parses a query
struct parsed_query result;
result.command="entered command";
result.arguments="entered arguments"
return result
}
char* str2char(String as_string){
int i_0=0;
while(as_string[i_0]!='\0'){i_0++;}
char as_char[i_0+1];
as_char[i_0]='\0';
for(int i=0;i<i_0;i++){
as_char[i]=as_string[i];
}
return as_char;
}
String executor(String command,String arguments){
String response;
if(command=="dim"){
response="dimming";
}
else if(command=="on"){
response="switching ON";
}
else{
response="error: unknown command";
}
return response;
}
void setup(){
// initialize serial interface, wifi & tcp-server
Serial.begin(115200);
WiFi.begin("<SSID>","<PASSWORD>");
while (WiFi.status() != WL_CONNECTED){delay(500);}
TCPserver.begin();
}
void loop() {
if(!client.connected()){
client=TCPserver.available();
}else{
struct parsed_query query=parser();
// This prints "error: unkno??*/???*??"
Serial.println(str2char(executor(query.command,query.arguments)));
// here, the client receives "????**?*??*?wn command"
client.write(str2char(executor(query.command,query.arguments))));
}
}
I have two ideas what could be causing this result (even though I don't know where to fix it in my code):
Case 1:
Maybe, I meshed up call-by-reference and call-by-value at some point (if yes, where??)
Case 2:
My programm is causing a stack overflow (if yes, where??)
Any help highly appreciated as I don't want to spend one more night.
In str2char you're returning a pointer to a local array, but like every local variable, it doesn't exist anymore after the function has returned. So reading from the returned pointer causes undefined behavior.
Compiling with warnings enabled (which is highly recommended) should output something like:
warning: address of local variable 'as_char' returned
(one) correct code would be
#include <WiFiClient.h>
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <string.h>
#define TCP_RESPONSE_L 1024
struct parsed_query{
String command;
String arguments;
};
struct parsed_query parser(void){
// this function receives and parses a query
struct parsed_query result;
result.command="entered command";
result.arguments="entered arguments"
return result
}
int str2char(char *as_char, String as_string, int max_length){
int i_0=0;
while(as_string[i_0]!='\0'){
if(i_0>=max_length){as_string="error: caught an overflow! increase TCP_BUFFER_L";break;}
i_0++;
}
as_char[i_0]='\0';
for(int i=0;i<i_0;i++){
as_char[i]=as_string[i];
}
return 1;
}
String executor(String command,String arguments){
String response;
if(command=="dim"){
response="dimming";
}
else if(command=="on"){
response="switching ON";
}
else{
response="error: unknown command";
}
return response;
}
void setup(){
// initialize serial interface, wifi & tcp-server
Serial.begin(115200);
WiFi.begin("<SSID>","<PASSWORD>");
while (WiFi.status() != WL_CONNECTED){delay(500);}
TCPserver.begin();
}
void loop() {
if(!client.connected()){
client=TCPserver.available();
}else{
struct parsed_query query=parser();
char response[TCP_RESPONSE_L];
str2char(response,executor(query.command,query.arguments),TCP_RESPONSE_L);
//prints fine
Serial.println(str2char(executor(query.command,query.arguments)));
//correctly sending to client
client.write(str2char(executor(query.command,query.arguments))));
}
}