How to use a library within a library (Arduino) - c++
My C++ skills are rather amateurish, but I’m trying to write a library for a differential drive robotic platform. The library in question needs to utilise a PID library and a rotary encoder library.
I’d like to utilise “levels of abstraction” to do this, rather than just having a massive “sketch” as it makes things a lot easier to read, and to be honest, is the point of object orientated programming. I.e i can write a class to control the a dc motor and just use multiple instances for each side. (I apologise if I’ve got the terminology wrong).
Essentially I’m having trouble utilising the third party libraries within my library.
If i was to write a simple sketch that used the two third party libraries, it would look like this (paraphrasing) -
#include <Arduino.h>
#include <RotaryEncoder.h>
#include <PID_v1.h>
#include <L298N.h>
char A_IN1 = 8;
char A_IN2 = 9;
char A_EN = 17;
char PIN_IN1 = 4;
char PIN_IN2 = 5;
double Setpoint, Output;
double Input = 0;
double Kp=1.3, Ki=15, Kd=0.01;
RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
void checkPosition(){
//some code that increments the encoder value
}
void setup(){
attachInterrupt(digitalPinToInterrupt(PIN_IN1), checkPosition, CHANGE);
pinMode(PIN_IN1, INPUT);
attachInterrupt(digitalPinToInterrupt(PIN_IN2), checkPosition, CHANGE);
pinMode(PIN_IN2, INPUT);
Setpoint = 0;
myPID.SetSampleTime(20);
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(0,255);
}
void loop(){
//loop code that uses PID to drive motors at appropriate speed etc etc
}
How does one go about implementing the two libraries into a custom library?
E.g two files L298N.cpp and L298N.h? Below is what I’ve tried, but no luck.
L298N.cpp
#include "Arduino.h"
#include "L298N.h"
#include <RotaryEncoder.h>
#include <PID_v1.h>
L298N::L298N(char IN1, char IN2, char EN, char A_INT1, char A_INT2){
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(EN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
pinMode(A_INT1, INPUT);
pinMode(A_INT2, INPUT);
RotaryEncoder encoder(A_INT1, A_INT2, RotaryEncoder::LatchMode::TWO03);
PID pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
pid.SetSampleTime(20);
pid.SetMode(AUTOMATIC);
pid.SetOutputLimits(0,255);
_IN1 = IN1;
_IN2 = IN2;
_EN = EN;
}
void L298N::checkPosition(){
encoder.tick(); // just call tick() to check the state.
}
double L298N::calculate_rpm(){
long new_position = encoder.getPosition();
long position_change;
double RPM;
if (new_position != old_position) {
tick_time = (millis() - last_time);
position_change = old_position - new_position;
RPM = 1 / ((double(tick_time / position_change) * 125)/1000/60); //10041 18538 = ticks per rev, 1 rev = 42.73cm
old_position = new_position;
last_time = millis();
}
else{
RPM = 0.0;
}
delay(20); // required for dagu as encoders are shit and only pulse 125 times per rev
return RPM;
}
void L298N::set_rpm(int rpm){
Setpoint = rpm;//covert_vel_rpm(vel);
Input = calculate_rpm();
pid.Compute();
if (Setpoint > 0.0){
forwards(char(Output));
}
else{
backwards(char(Output));
}
}
void L298N::forwards(char pwm){
digitalWrite(_IN1, HIGH);
digitalWrite(_IN2, LOW);
analogWrite(_EN, pwm);
return;
}
void L298N::backwards(char pwm){
digitalWrite(_IN1, LOW);
digitalWrite(_IN2, HIGH);
analogWrite(_EN, pwm);
return;
}
L298N.h
#ifndef L298N_h
#define L298N_h
#include "Arduino.h"
#include <RotaryEncoder.h>
#include <PID_v1.h>
class L298N
{
public:
L298N(char IN1, char IN2, char EN, char A_INT1, char A_INT2);
void set_rpm(int rpm);
private:
char _IN1, _IN2, _EN, _INT1, _INT2;
double last_time = millis();
double tick_time = 0;
long old_position = 0;
double Setpoint, Output;
double Input = 0;
double Kp=1.3, Ki=15, Kd=0.01;
RotaryEncoder encoder;//(char A_INT1, char A_INT2, RotaryEncoder::LatchMode::TWO03);
PID pid;//(double &Input, double &Output, double &Setpoint, double Kp, double Ki, double Kd, char DIRECT);
void checkPosition();
double calculate_rpm();
void forwards(char pwm);
void backwards(char pwm);
};
#endif
test.ino
#include <L298N.h>
#include <RotaryEncoder.h>
#include <PID_v1.h>
char A_IN1 = 8;
char A_IN2 = 9;
char A_EN = 17;
char A_INT1 = 3;
char A_INT2 = 4;
L298N left(A_IN1, A_IN2, A_EN, A_INT1, A_INT2);
void setup(){
}
void loop(){
left.set_rpm(20);
}
errors:
Arduino: 1.8.15 (Linux), Board: "Arduino Uno"
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp: In constructor
'L298N::L298N(char, char, char, char, char)':
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:7:67: error: no
matching function for call to 'RotaryEncoder::RotaryEncoder()'
L298N::L298N(char IN1, char IN2, char EN, char A_INT1, char A_INT2)
^ In file included from
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:10:0,
from /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:39:3:
note: candidate: RotaryEncoder::RotaryEncoder(int, int,
RotaryEncoder::LatchMode) RotaryEncoder(int pin1, int pin2,
LatchMode mode = LatchMode::FOUR0); ^~~~~~~~~~~~~
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:39:3:
note: candidate expects 3 arguments, 0 provided
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7:
note: candidate: constexpr RotaryEncoder::RotaryEncoder(const
RotaryEncoder&) class RotaryEncoder
^~~~~~~~~~~~~ /home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7:
note: candidate expects 1 argument, 0 provided
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7:
note: candidate: constexpr
RotaryEncoder::RotaryEncoder(RotaryEncoder&&)
/home/ubuntu/Arduino/libraries/RotaryEncoder/src/RotaryEncoder.h:23:7:
note: candidate expects 1 argument, 0 provided
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:7:67: error: no
matching function for call to 'PID::PID()' L298N::L298N(char IN1,
char IN2, char EN, char A_INT1, char A_INT2)
^ In file included from
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:11:0,
from /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:24:5: note: candidate:
PID::PID(double*, double*, double*, double, double, double, int)
PID(double*, double*, double*, // * constructor. links the PID to the Input, Output, and
^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:24:5: note: candidate expects 7 arguments, 0 provided
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:20:5: note: candidate:
PID::PID(double*, double*, double*, double, double, double, int, int)
PID(double*, double*, double*, // * constructor. links the PID to the Input, Output, and
^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:20:5: note: candidate expects 8 arguments, 0 provided
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: note: candidate:
constexpr PID::PID(const PID&) class PID
^~~ /home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: note: candidate expects 1 argument, 0 provided
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: note: candidate:
constexpr PID::PID(PID&&)
/home/ubuntu/Arduino/libraries/PID/PID_v1.h:5:7: note: candidate
expects 1 argument, 0 provided
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:13:70: error:
invalid use of non-static member function 'void
L298N::checkPosition()'
attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
^ In file included from
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:0:
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:33:7: note:
declared here void checkPosition();
^~~~~~~~~~~~~ /home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:14:70: error:
invalid use of non-static member function 'void
L298N::checkPosition()'
attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
^ In file included from
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.cpp:3:0:
/home/ubuntu/Arduino/libraries/L298N_driver/L298N.h:33:7: note:
declared here void checkPosition();
^~~~~~~~~~~~~ exit status 1 Error compiling for board Arduino Uno.
This report would have more information with "Show verbose output
during compilation" option enabled in File -> Preferences.
Any help of how to do this properly would be greatly appreciated. It’s just using a class within a class (terminology might be wrong) but this is quite trivial to do in Python.
Cheers!
You have several problems.
You said
PID and RotaryEncoder libraries are standard arduino libraries
That's wrong, those libraries are not standard, you have to install them using the Library Manager (otherwise it would have compiled).
Those are:
- RotaryEncoder, by Mathias Hertel
http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx
- Arduino PID Library, by Brett Beauregard
https://github.com/br3ttb/Arduino-PID-Library
Local includes should use quotes, system/external includes must use brackets:
#include "L298N.h"
#include <RotaryEncoder.h>
RotaryEncoder does not have a default constructor, so you either initialize it using the initializer list or just remove the member variable.
PID doesn't have a default constructor either.
You cannot use a class member function as an interrupt handler. This:
attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
will never work. You may have to use a singleton or similar:
L298N left(A_IN1, A_IN2, A_EN, A_INT1, A_INT2);
// code
void int_checkPosition() {
left.checkPosition()
}
// more code
// this may go in setup()
attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
I changed the code in the constructor to make it look like this and compiles.
// encoder and pid initialized properly
L298N::L298N(byte IN1, byte IN2, byte mEN, byte A_INT1, byte A_INT2)
: encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03)
, pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT)
{
piL298N::L298N(byte IN1, byte IN2, byte EN, byte A_INT1, byte A_INT2)
: encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03)
, pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT)
{
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(EN, OUTPUT);
// this doesn't work (use the recommendation above)
//attachInterrupt(digitalPinToInterrupt(A_INT1), checkPosition, CHANGE);
//attachInterrupt(digitalPinToInterrupt(A_INT2), checkPosition, CHANGE);
pinMode(A_INT1, INPUT);
pinMode(A_INT2, INPUT);
// you don't need this. in fact, it wasn't going to work
// because you needed it to be a class member.
//RotaryEncoder encoder((int)A_INT1, (int)A_INT2, RotaryEncoder::LatchMode::TWO03);
//PID pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
pid.SetSampleTime(20);
pid.SetMode(AUTOMATIC);
pid.SetOutputLimits(0,255);
_IN1 = IN1;
_IN2 = IN2;
_EN = EN;
}
It is up to you to fix the issue with the interrupt handler.
i think I understand what are you asking. Arduino (and many other IDE) has two different types o library
1) Local libraries
2) External Libraries
Local Libraries are those that you include by the syntaxis:
#include "Your_library.h"
External Libraries are those with the syntaxis:
#include <Your_library.h>
The difference between them is that the Local Libraries are supposed to be in the same folder of the proyect, it means that if you have a folder
Arduino/My_proyect/My_Proyect.ino
the library should be in
Arduino/My_proyect/Your_library.h
Instead, external Libraries are in the Arduino "libraries" folder
Arduino/libraries/Your_library/Your_library.h
Maybe the problem with your code is that you are using the bad type of syntaxis for the library, try to put all the libraries you want to use (.h and .cpp) in the same file and create a folder in the arduino "libraries" folder, then, include your librarys using the external syntaxis
#include <Folder_name.h>
This should work!
Related
How to solve conflicting declaration on the Library?
This is my code to build SHT10 reading. But I got an error message. It says that pin D1 is conflicting between Sensirion.cpp and arduino.h #include <Sensirion.h> #include <SensirionSHT.h> SensirionSHT Sensor = SensirionSHT(4, 5); long delayTime = 1; void setup() { Serial.begin(9600); } void loop() { Sensor.tick(delayTime); Serial.println(Sensor.getTemperature()); delay(delayTime * 1000); } And this is the error messages C:\Users\ASUS\Documents\Arduino\libraries\Sensirion-master\Sensirion.cpp:52:15: error: conflicting declaration 'const float D1' const float D1 = -40.1; // for deg C # 5V ^ In file included from C:\Users\ASUS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.4\cores\esp8266/Arduino.h:296:0, from C:\Users\ASUS\Documents\Arduino\libraries\Sensirion-master\Sensirion.cpp:21: C:\Users\ASUS\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.4\variants\nodemcu/pins_arduino.h:41:22: error: 'D1' has a previous declaration as 'const uint8_t D1' static const uint8_t D1 = 5; ^ exit status 1 Error compiling for board NodeMCU 1.0 (ESP-12E Module).
Undefined Reference in constructors
I have been hammering at this for the past 2 days and i've already tried every single solution on the internet, so here goes. I have a problem with undefined references. I am doing a project to compare 3 algorithms and i have compartmentalized them into 3 different sets of cpp files. I am using Dev C++ with gcc 4.9.2.6 as my compiler. I know it is a linker error, but all my solutions are not working and i can't seem to identify it. My main file is #include <iostream> #include <stdio.h> #include <string> #include <fstream> #include <time.h> #include "SDL.h" #include "bitmap_image.hpp" //ext #include "Bresenham.hpp" #include "XiaolinWu.hpp" #include "GuptaSproull.hpp" void GenerateBMPBlank(int xsize,int ysize,std::string fileid); void BresenhamTest(int xsize,int ysize, std::string TestDataFile); void XiaolinWuTest(int xsize,int ysize, std::string TestDataFile); void GuptaSproullTest(int xsize, int ysize, std::string TestDataFile); void executeTest(int xsize,int ysize, std::string TestDataFile); //resulting BMP file generated will have the format "x_y_algorithmName.bmp" int main() { short x,y; std::string TestFileLocation; std::cout << "Please indicate file path of Test Data textfile"<< std::endl; std::cin>>TestFileLocation; std::cout << "Please indicate file dimensions" << std::endl; std::cin>> x >> y; executeTest(x,y, TestFileLocation); std::cout<< "Procedure executed"<< std::endl; std::cin.get(); return 0; } void GenerateBMPBlank(int xsize,int ysize,std::string fileid) //uses external library http://partow.net/programming/bitmap/ to generate a blank white bmp file { bitmap_image blankBMP(xsize,ysize); //creates bitmap image blankBMP.set_all_channels(255,255,255); //sets entire image to be completely white blankBMP.save_image(fileid); } //tested void executeTest(int xsize,int ysize, std::string TestDataFile) { SDL_Init( SDL_INIT_EVERYTHING ); std::cout<<"Beginning test of data set from "+TestDataFile<<std::endl; std::cout<<"Now executing Bresenham's algorithm"<<std::endl; BresenhamTest(xsize,ysize, TestDataFile); std::cout<<"Now executing Xiaolin Wu's algorithm"<<std::endl; XiaolinWuTest(xsize,ysize,TestDataFile); std::cout<<"Now executing Gupta Sproull 's algorithm"<<std::endl; GuptaSproullTest(xsize,ysize,TestDataFile); SDL_Quit(); } void BresenhamTest(int xsize,int ysize, std::string TestDataFile) { std::string ResultName= std::to_string(xsize) + "_" + std::to_string(ysize) + "_Bresenham.bmp"; GenerateBMPBlank(xsize,ysize,ResultName); clock_t tStart = clock(); Bresenham b(ResultName,TestDataFile); printf("Time taken for Bresenham: %.4fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC); } void XiaolinWuTest(int xsize,int ysize, std::string TestDataFile) { std::string ResultName= std::to_string(xsize) + "_" + std::to_string(ysize) + "_XiaolinWu.bmp"; GenerateBMPBlank(xsize,ysize,ResultName); clock_t tStart = clock(); XiaolinWu w(ResultName,TestDataFile); printf("Time taken for XiaolinWu: %.4fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC); } void GuptaSproullTest(int xsize,int ysize, std::string TestDataFile) { std::string ResultName= std::to_string(xsize) + "_" + std::to_string(ysize) + "_GuptaSproull.bmp"; GenerateBMPBlank(xsize,ysize,ResultName); clock_t tStart = clock(); GuptaSproull g(ResultName,TestDataFile); printf("Time taken for GuptaSproull: %.4fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC); } However, an error is produced as follows C++ files/ComparatorMain.o:ComparatorMain.cpp:(.text+0x544): undefined reference to `Bresenham::Bresenham(std::string, std::string)' C++ files/ComparatorMain.o:ComparatorMain.cpp:(.text+0x76b): undefined reference to `XiaolinWu::XiaolinWu(std::string, std::string)' C++ files/ComparatorMain.o:ComparatorMain.cpp:(.text+0x992): undefined reference to `GuptaSproull::GuptaSproull(std::string, std::string)'collect2.exe: error: ld returned 1 exit status As the implementation of the 3 different cpp files are nearly identical (with the main difference just being the algorithm implemented as well as some misc. functions which have complied and so far aren't showing errors), I will just show the main parts of Bresenham.cpp and hpp where the linker errors are occurring (if additional information is needed, just tell me). The definitions for GuptaSproull.cpp as well as XiaolinWu.cpp are pretty much identical for the code shown below. I cut out most of the function implementations for easier reading and i don't think its relavant (unless i got that part wrong). Bresenham.hpp #ifndef BRESENHAM_H #define BRESENHAM_H #include <iostream> #include "SDL.h" #undef main class Bresenham{ public: Bresenham(std::string BMPName,std::string TestDataFile); SDL_Surface* OpenBMP(std::string BMPName); void CloseBMP(SDL_Surface* surface,std::string Filename); void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel); void bresenhamDrawLine(int x1,int y1,int x2, int y2, SDL_Surface *surface); }; #endif Bresenham.cpp #ifndef BRESENHAM_H #define BRESENHAM_H #include <iostream> #include <cstdio> #include <cmath> #include <fstream> #include "SDL.h" #include "Bresenham.hpp" #undef main #endif class Bresenham { Bresenham(std::string BMPName,std::string TestDataFile) { std::ifstream testFile(TestDataFile); SDL_Surface *image; image=OpenBMP(BMPName); if ( SDL_MUSTLOCK(image) ) //surface must be locked before pixels can be drawn { if ( SDL_LockSurface(image) < 0 ) { fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError()); return; } } int x1,y1,x2,y2; while(testFile>>x1>>y1>>x2>>y2) { bresenhamDrawLine(x1,y1,x2,y2,image); //loops through the dataset and calls the bresenham draw line function } if ( SDL_MUSTLOCK(image) ) { SDL_UnlockSurface(image); } CloseBMP(image,BMPName); } void bresenhamDrawLine(int x1,int y1,int x2, int y2, SDL_Surface *surface) { /* implemented */ } SDL_Surface* OpenBMP(std::string BMPName) { /* implemented */ } void CloseBMP(SDL_Surface *surface,std::string FileName) { /* implemented */ } void putpixel(SDL_Surface *surface, int x, int y, double brightness) //this function allows us to place a pixel at coordinates (x,y) in SDL. { /*implemented*/ } }; Now I have done a few messy attempts to try and fix this problem (such as adding #ifndef BRESENHAM_H #define BRESENHAM_H #include "Bresenham.hpp" into the .cpp file. However, the error above still occurs. Is this linked to the way I implemented my code to do the testing? I used a constructor to basically run my test on the algorithms (which i suspect you might find a shoddy way of implementing such a test). I have done the following (so yeah those didn't work): Verified that all the files are in the build path (under the same project) Tried adding namespaces to see if it fixed the problem (it didn't) I've searched under pretty much every single link in google in order to find a potential fix (none of them seems to work). There are no compiler errors so far (in all the files). I suspect i might need to abandon this style of implementing the test and migrate over to using a static function instead (Could someone comment if this would work?). I'm not really used to C++ (this is my first "big" program in this language so far), so pardon me if I'm missing something glaringly obvious (which i hope i didn't). What should I do?
You actually have two declarations of Bresenham class, one in Bresenham.hpp and one in Bresenham.cpp. Change your cpp file in following way: Bresenham::Bresenham(std::string BMPName,std::string TestDataFile) { std::ifstream testFile(TestDataFile); SDL_Surface *image; image=OpenBMP(BMPName); if ( SDL_MUSTLOCK(image) ) //surface must be locked before pixels can be drawn { if ( SDL_LockSurface(image) < 0 ) { fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError()); return; } } int x1,y1,x2,y2; while(testFile>>x1>>y1>>x2>>y2) { bresenhamDrawLine(x1,y1,x2,y2,image); //loops through the dataset and calls the bresenham draw line function } if ( SDL_MUSTLOCK(image) ) { SDL_UnlockSurface(image); } CloseBMP(image,BMPName); } void Bresenham::bresenhamDrawLine(int x1,int y1,int x2, int y2, SDL_Surface *surface) { /* implemented */ } SDL_Surface* Bresenham::OpenBMP(std::string BMPName) { /* implemented */ } void Bresenham::CloseBMP(SDL_Surface *surface,std::string FileName) { /* implemented */ } void Bresenham::putpixel(SDL_Surface *surface, int x, int y, double brightness) //this function allows us to place a pixel at coordinates (x,y) in SDL. { /*implemented*/ }
What should I do? First of all, you need to set up correct code units: remove #undef main (makes no sense) remove the include guards from your cpp files, they belong only in header files. With these, the code just doesn't get compiled, hence the linking problem ! As CodeFuller states it clearly in his answer, you must separate the class declaration (in .hpp file) and the implementation of the methods (in the .cpp file) For more, you need to gives us an MVCE that demonstrates your problem (I agree, that is some bit of work).
A newbie and the timer module that doesn't compile
I'm totally new to C/C++. I've done some higher languages like Java and C# but that's something else I must say. I'm trying to build a timer module for the Arduino so that I can easily queue work that has to be executed after a certain amount of time without the use of delay(). I would like to create it as intuitively as possible so that I can easily re-use it. So far I've tried a lot of different approaches. What I did is mostly read tutorials on how to achieve a certain thing in C/C++ and created my little project out of that. Main .ino file: #include "Timer.h" Timer timer; void setup() { // Init the timer timer.init(); pinMode(13, OUTPUT); // Define the state int state = 1; // Create the action TimerAction action; action.create(1000, &timerElapsed, 1 -1); action.state = &state; } void loop() { timer.run(); } void timerElapsed(TimerAction action) { int *state = (int*)action.state; if (state == 0) { digitalWrite(13, HIGH); *state = 1; } else { digitalWrite(13, LOW); *state = 0; } } It's very simple. I'm trying to blink the onboard led as a proof of concept. This is the Timer.h file: #ifndef _TIMER_h #define _TIMER_h #if defined(ARDUINO) && ARDUINO >= 100 #include "arduino.h" #else #include "WProgram.h" #endif struct Timer { TimerAction* actions; uint8_t size; void init(); void queue(TimerAction action); void dequeue(TimerAction action); void dequeueIdx(int idx); void run(); }; struct TimerAction { // Sets whether to repeat the action uint8_t repetitive; // Sets how many times to repeat the action, -1 for infinite // Will only be used when repetitive == 1 long finite; void (*callback)(TimerAction sender); void *state; unsigned long last; unsigned long interval; void create(long interval, void(*callback)(TimerAction sender), uint8_t repetitive = 0U, long finite = -1L); }; #endif This is the Timer.ino file: #include "Timer.h" void Timer::init() { size = 0; actions = new TimerAction[10]; } void Timer::queue(TimerAction action) { action.last = millis(); actions[size - 1] = action; size++; } void Timer::dequeue(TimerAction action) { for (int i = 0; i < size; i++) if (&(actions[i]) == &action) { memmove(actions + i, actions + i + 1, (size - (i + 1)) * sizeof(TimerAction)); break; } } void Timer::dequeueIdx(int idx) { memmove(actions + idx, actions + idx + 1, (size - (idx + 1)) * sizeof(TimerAction)); } void Timer::run() { for (int i = 0; i < size; i++) if ((actions[i].last + actions[i].interval) >= millis()) { TimerAction action = actions[i]; action.callback(action); if (action.repetitive == 1) { if (action.finite > 0) action.finite--; else if (action.finite == 0) dequeueIdx(i); } else dequeueIdx(i); } } void TimerAction::create(long _interval, void(*_callback)(TimerAction sender), uint8_t _repetitive = 0U, long _finite = -1L) { interval = _interval; callback = _callback; repetitive = _repetitive; finite = _finite; } These are the errors the compiler spewed out: Arduino: 1.6.1 (Windows 8.1), Board: "Arduino Uno" In file included from Stoplicht.ino:1:0: Timer.h:13:2: error: 'TimerAction' does not name a type TimerAction* actions; ^ Timer.h:17:13: error: 'TimerAction' has not been declared void queue(TimerAction action); ^ Timer.h:18:15: error: 'TimerAction' has not been declared void dequeue(TimerAction action); ^ Timer.ino: In member function 'void Timer::init()': Timer.ino:5:2: error: 'actions' was not declared in this scope Timer.ino: At global scope: Timer.ino:8:6: error: prototype for 'void Timer::queue(TimerAction)' does not match any in class 'Timer' In file included from Stoplicht.ino:1:0: Timer.h:17:7: error: candidate is: void Timer::queue(int) void queue(TimerAction action); ^ Timer.ino:15:6: error: prototype for 'void Timer::dequeue(TimerAction)' does not match any in class 'Timer' In file included from Stoplicht.ino:1:0: Timer.h:18:7: error: candidate is: void Timer::dequeue(int) void dequeue(TimerAction action); ^ Timer.ino: In member function 'void Timer::dequeueIdx(int)': Timer.ino:24:10: error: 'actions' was not declared in this scope Timer.ino: In member function 'void Timer::run()': Timer.ino:29:8: error: 'actions' was not declared in this scope Timer.ino: At global scope: Timer.ino:45:52: error: default argument given for parameter 3 of 'void TimerAction::create(long int, void (*)(TimerAction), uint8_t, long int)' [-fpermissive] In file included from Stoplicht.ino:1:0: Timer.h:36:7: error: after previous specification in 'void TimerAction::create(long int, void (*)(TimerAction), uint8_t, long int)' [-fpermissive] void create(long interval, void(*callback)(TimerAction sender), uint8_t repetitive = 0U, long finite = -1L); ^ Timer.ino:45:52: error: default argument given for parameter 4 of 'void TimerAction::create(long int, void (*)(TimerAction), uint8_t, long int)' [-fpermissive] In file included from Stoplicht.ino:1:0: Timer.h:36:7: error: after previous specification in 'void TimerAction::create(long int, void (*)(TimerAction), uint8_t, long int)' [-fpermissive] void create(long interval, void(*callback)(TimerAction sender), uint8_t repetitive = 0U, long finite = -1L); ^ Let me explain why I'm using Array over a std::vector. My chain of thought was this: the Arduino is quite weak and actions is going to be accessed a lot. So I thought it's a bit more work to implement it initially but it will make sure the timer doesn't use too much of the Arduino's resources. I've tried a lot of things but I don't really understand where the problem lies. So that's why I'm asking an expert to look at my code and maybe put me on the right track.
In Timer.h you have: TimerAction* actions; At the point in time that the compiler sees that line of code it has not seen what a TimerAction is. As such it does not know what the type is and is giving you an error. What you need to do is forward declare TimerAction before Timer so the compiler knows what it is doing. #ifndef _TIMER_h #define _TIMER_h #if defined(ARDUINO) && ARDUINO >= 100 #include "arduino.h" #else #include "WProgram.h" #endif struct TimerAction; struct Timer { //...
pthread: I want to pass a struct pointer in main to a function called by pthread
What I am trying to do is start two threads, each running the crit_area function. I need to pass ptrBank from main() to crit_area() so that the BANK struct balance[0] and balance[1] are updated by the threads sequentially. To accomplish this I've created a semaphore class of my own sem_class-1.h. I've gotten through all of the compile errors except for the following: race1d.cc:49:61: error: invalid conversion from ‘void* (*)(BANK*)’ to ‘void* (*)(void*)’ [-fpermissive] /usr/include/pthread.h:225:12: error: initializing argument 3 of ‘int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’ [-fpermissive] race1d.cc:54:61: error: invalid conversion from ‘void* (*)(BANK*)’ to ‘void* (*)(void*)’ [-fpermissive] /usr/include/pthread.h:225:12: error: initializing argument 3 of ‘int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’ [-fpermissive] race1d.cc: In function ‘void* crit_area(BANK*)’: race1d.cc:105:10: error: too few arguments to function ‘void (* signal(int, __sighandler_t))(int)’ /usr/include/signal.h:101:23: note: declared here I'm not very comfortable with pointers yet (some of this code was given to me to start with), and am not even sure if I can pass a pointer to a structure through pthread to a function. I've tried passing the pointer in various ways in the pthread function call, i.e. pthread_create(&tid1, NULL, crit_area, ptrBank) or pthread_create(&tid1, NULL, crit_area, *ptrBank) all to no avail. I've also spent a few hours online searching for similar problems. Can anyone help? Yes... this is part of a homework assignment, of which my lab partner and I have completed all but this final part. [please don't judge our newb code] #include <unistd.h> /* Symbolic Constants */ #include <sys/types.h> /* Primitive System Data Types */ #include <sys/wait.h> /* System error numbers */ #include <errno.h> /* Errors */ #include <stdio.h> /* Input/Output */ #include <stdlib.h> /* General Utilities */ #include <pthread.h> /* POSIX Threads */ #include <string.h> /* String handling */ //#include <semaphore.h> /* Semaphore */ //#include <fcntl.h> /* Needed for arguments to sem_open */ #include "shm437.h" #include "sem_class-1.h" // BANK Structure, with integer variable 'balance' struct BANK { int balance[2]; // BANK function sets balance variable == 0 BANK() { balance[0]=balance[1]=0; } }; void * crit_area(BANK * a); // Begin main program int main(int argc, char **argv) { pthread_t tid1, tid2; Shm437 *pShmBank = new Shm437(1,sizeof(BANK)); // set up pointers BANK *ptrBank = (BANK*) pShmBank->ShmAlloc(); ptrBank->balance[0] = ptrBank->balance[1] = 100; Semaphore(1); // initialize Semaphore class, pass sig srandom(getpid()); // set seed printf("Init balances 0:%d + 1:%d ==> %d!\n", // print initial ptrBank->balance[0], ptrBank->balance[1], ptrBank->balance[0]+ptrBank->balance[1] ); if(pthread_create(&tid1, NULL, crit_area, ptrBank)) // p thread 1 { printf("\n ERROR creating thread 1"); exit(1); } if(pthread_create(&tid2, NULL, crit_area, ptrBank)) // p thread 2 { printf("\n ERROR creating thread 2"); exit(1); } if(pthread_join(tid1, NULL)) //wait for the thread 1 to finish { printf("\n ERROR joining thread"); exit(1); } if(pthread_join(tid2, NULL)) // wait for the thread 2 to finish { printf("\n ERROR joining thread"); exit(1); } // print new values printf("Let's check the balances 0:%d + 1:%d ==> %d ?= 200\n", ptrBank->balance[0],ptrBank->balance[1], ptrBank->balance[0]+ptrBank->balance[1]); pthread_exit(NULL); Semaphore(1); } /* Thread Function critical area where we will use semaphore and create balance with timing delay */ void * crit_area(BANK * a) { int tmp1, tmp2, rint; double dummy; wait(0); for (int i=0; i < 100; i++) // loop for 99 times { tmp1 = a->balance[0]; // tmp1 var is equal to balance[0] or 100 //line 49 tmp2 = a->balance[1]; // tmp2 var is equal to balance[1] or 100 rint = (rand()%20)-10; // rint var == random number (0 thru 19)-10 or -10 thru 9 // using the seed number (PID) as the starting value if ((tmp1+rint)>=0 && (tmp2-rint)>=0) // if non-negative { a->balance[0] = tmp1 + rint; // if not, change balance[0] to tmp1+rint //line 55 for (int j=0; j < rint*100; j++) // run this math problem for rint*100 times {dummy=2.345*8.765/1.234; } usleep(1); a->balance[1] = tmp2 - rint; // change balance[1] to tmp2-rint //line 63 } } signal(1); }
Pthreads expects functions with a signature that takes void*, not BANK*. You should change the crit_area function as follows: void * crit_area(void* voidA) { BANK *a = (BANK*)voidA; ... // The rest of your function }
Custom Arduino library not instantiating
I have this code in my main arduino file: #include <HIH4030.h> HIH4030 humiditySensor(0); float humidity; void setup() { Serial.begin(9600); } void loop() { delay(1000); humidity = humiditySensor.sensorRH(); } This is my HIH4030.h file contents: #ifndef HIH4030_h #define HIH4030_h #include "Arduino.h" class HIH4030{ public: HIH4030(int pin); float ratioHumidity(); float sensorRH(); float trueSensorRH(float temperature); private: int _pin; float sample; float temperature; }; #endif //data sheet https://www.sparkfun.com/datasheets/Sensors/Weather/SEN-09569-HIH-4030-datasheet.pdf This is my HIH4030.cpp file contents: #include <HIH4030.h> HIH4030::HIH4030(int pin) { _pin = pin; } /* gives humidity as a ratio of VDD */ float HIH4030::ratioHumidity(){ //poll analogue in return analogRead(_pin); } /* gives humidity as a percentage - numbers taken from datasheet */ float HIH4030::sensorRH(){ //poll analogue in sample = analogRead(_pin)*5; //multiply by 5 as sample is a decimal of Vdd return (sample-0.958)/0.0307; } /* gives humidity adjusted for temperature (in degrees C) - numbers taken from datasheet */ float HIH4030::trueSensorRH(float temperature){ float rh = sensorRH(); temperature = temperature*0.00216; return rh/(1.0546 - temperature); } And when I compile I get this error: Gathering compilation infomation... Compiling sketch... Cleaning... Creating /Users/user/Documents/Arduino_Build/QuantifiedGarden/QuantifiedGarden.cpp.o... Creating /Users/user/Documents/Arduino_Build/QuantifiedGarden/QuantifiedGarden.elf... /Users/user/Documents/Arduino_Build/QuantifiedGarden/QuantifiedGarden.cpp.o: In function `__static_initialization_and_destruction_0': /Users/user/Documents/Arduino_Build/QuantifiedGarden/QuantifiedGarden.cpp:7: undefined reference to `HIH4030::HIH4030(int)' /Users/user/Documents/Arduino_Build/QuantifiedGarden/QuantifiedGarden.cpp.o: In function `loop': /Users/user/Documents/Arduino_Build/QuantifiedGarden/QuantifiedGarden.cpp:23: undefined reference to `HIH4030::sensorRH()' [Stino - Error while compiling.] I'm new to arduino, I'm using Stino Sublime Text 2 plugin. I have already converted a more complicated temperature sensor library I had written for a different platform in to Arduino and it worked perfectly - I can't see any difference between this and that other library but I must be missing something. From what I can tell I have followed the How to make a custom class Arduino tutorial to the letter...
Turns out it was some kind of cache error - deleting the Arduino Build folder sorted it out