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!
I am writing code for the Azure IoT Hub, which requires the use of c-functions in the Arduino loop(). The issue that I'm having is that if I pass a pointer to a float created in the c-file to a c++ file and modify the value, what is seen in the c-file after the c++ function returns is gibberish.
Here's a psuedocode example, and a working example is included below:
loop() in ino file:
runs runInLoop(), defined in the c-file RunTest.c
runInLoop() in RunTest.c:
create a float
pass the address to modifyFloat(float *address) defined in FloatTest.cpp
print the value of the float after modifyFloat() returns.
modifyFloat(float *address) in FloatTest.cpp:
assign a vale to *address
print the value
return
I've executed this pseudocode in the working example below and the result in the serial monitor is:
Value assigned in modifyFloat: 22.55
The value that was returned is: 1077316812
I'm using an Adafruit Huzzah Feather, configured exactly as they indicate in their documentation.
Here is a working example:
azure_troubleshoot.ino
#include "RunTest.h"
void setup()
{
initSerial();
}
void loop()
{
Serial.println("Starting main loop!\r\n");
runInLoop();
}
void initSerial()
{
Serial.begin(9600);
}
RunTest.c
#include "FloatTest.h"
void runInLoop(void)
{
while(1)
{
float testValue;
modifyFloat(&testValue);
(void)printf("The value that was returned is: %d\r\n", testValue);
delay(1000);
}
}
RunTest.h
#ifndef RUNTEST_H
#define RUNTEST_H
#ifdef __cplusplus
extern "C" {
#endif
void runInLoop(void);
#ifdef __cplusplus
}
#endif
#endif // RUNTEST_H
FloatTest.cpp
#include <Arduino.h>
#include "FloatTest.h"
void modifyFloat(float *address)
{
*address = 22.55;
Serial.print("Value assigned in modifyFloat: ");
Serial.println(*address);
}
FloatTest.h
#ifndef FLOATTEST_H
#define FLOATTEST_H
#ifdef __cplusplus
extern "C" {
#endif
void modifyFloat(float* address);
#ifdef __cplusplus
}
#endif
#endif // FLOATTEST_H
The issue was the use of the %d in the printf string in RunTest.c. Updating the code to that shown below fixes the issue and makes the output:
Value seen in modifyFloat: 22.55
The value that was returned is: 22.55
RunTest.c
#include "FloatTest.h"
void runInLoop(void)
{
while(1)
{
float testValue;
modifyFloat(&testValue);
char str_tmp[6];
dtostrf(testValue, 4, 2, str_tmp);
(void)printf("The value that was returned is: %s\r\n", str_tmp);
delay(1000);
}
}
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).
I get a lot of undefined references. I don't know what I'm doing wrong.
I'm getting the following errors:
undefined reference to 'LetteroidField::start()'
undefined reference to 'LetteroidField::setTitle(std::string)'
undefined reference to 'Letteroid::setletter(char)'
undefined reference to 'Letteroid::setLetter()'
undefined reference to 'Letteroid::setCoords()'
undefined reference to 'Letteroid::erase()'
and other letteroid references.
I'm not done with the other classes, but I don't know why I'm getting these errors. Am I not using #include "" correctly?
This is my professor's sample code. I contacted him but he is not answering (its an online class).
#include "letteroidfield.h"
#include "letteroid.h"
#include "blinkingletteroid.h"
#include "jumpingletteroid.h"
#include "movingletteroid.h"
#include <stdlib.h> /* srand, rand */
#include <time.h>
/// include your derived classes here
int main()
{
LetteroidField screen;
screen.start();
screen.setTitle("Ken's example for the class, press 'x' to quit");
BlinkingLetteroid one;
BlinkingLetteroid two;
BlinkingLetteroid three;
one.setLetter('!'); /// character
one.setCoords(5, 10); /// row, col
two.setLetter('h');
two.setCoords(7, 9);
three.setLetter('#');
three.setCoords(15, 57);
JumpingLetteroid four;
four.setLetter('j');
four.setCoords(rand() % 21, rand() % 21);
MovingLetteroid five;
five.setLetter('m');
int x = 20;
int y = 20;
while (x >= 1)
{
--x;
}
while (y >= 1)
{
--y;
}
if (x == 1)
{
x = 20;
}
if (y == 1)
{
x = 20;
}
five.setCoords(x,y);
/// create and initialize your letteroids here
while ( screen.waitForKeyPress() ) /// keep going until 'x' is pressed
{
one.blink();
two.blink();
three.blink();
/// call the function that draws your letteroids here
}
screen.end();
return 0;
}
#ifndef _LETTEROIDFIELD_H_
#define _LETTEROIDFIELD_H_
#include <string>
class LetteroidField
{
public:
void start(); /// start up the screen for letteroids
bool waitForKeyPress(); /// wait for any key to be pressed (return
void end(); /// shut down the screen and return it to
void setTitle(std::string); /// diplay the title
};
#endif
#ifndef _LETTEROID_H_
#define _LETTEROID_H_
class Letteroid
{
public:
void setCoords(int, int);// set the position(down, across)
void setLetter(char); // set the character
int getX(); // get the position down
int getY(); // get the position across
void erase(); // erase the letteroid from the screen
void draw(); // draw the letteroid to the screen
private:
int myX;
int myY;
char myLetter;
};
#endif
The question you need to ask yourself is: Where are those classes defined?
If the answer is: "in a shared library (file extension ".so") provided alongside the header", then you'll need to link against it by adding at least the following to your compilation command:
g++ main.cpp -L</path/to/library> -l<library_name>
If the answer is: "in a static library (file extension ".a", AKA archive) provided alongside the header", then you'll need include it in your binary by adding at least the following to your compilation command:
g++ main.cpp <library_name.a>
If the answer is: "in a bunch of source files provided alongside the header", then you'll need to include them in your binary by adding at least the following to your compilation command:
g++ main.cpp <source_file1.cpp> <source_file2.cpp> ...
I'm trying to write a function that can Shift out data to 74HC595 shift registers which can shift out 8, 16, and 32 bit values.
Using an overloaded function, I have this:
/**********************************************************************************
* Software SPI Pin Settings
*********************************************************************************/
#define SPIPINPORT PORTB //The Port that the Pins are on.
#define LatchPin 2 //_RCLK Shift register clock pin
#define DataPin 3 //SER DS Serial data input
#define ClockPin 5
/**********************************************************************************
* Preproccesor PIN to PIN Mask
*********************************************************************************/
#define LATCHMASK (1 << LatchPin)
#define MOSIMASK (1 << DataPin)
#define CLOCKMASK (1 << ClockPin)
/**********************************************************************************
* Macros
*********************************************************************************/
#define tggl(port,bit) (port)^=(1<<(bit))
#define LATCH (SPIPINPORT &=~ LATCHMASK)
#define unLATCH (SPIPINPORT |= LATCHMASK)
#define PULSE { tggl(SPIPINPORT,ClockPin); tggl(SPIPINPORT,ClockPin); }
void zShiftClass::ShiftOut(uint8_t value)
{
LATCH;
for (uint8_t i = 0; i <= 7; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
void zShiftClass::ShiftOut(uint16_t value)
{
LATCH;
for (uint8_t i = 0; i <= 15; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
void zShiftClass::ShiftOut(uint32_t value)
{
LATCH;
for (uint8_t i = 0; i <= 31; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
And I want to use this template to replace these functions:
template<typename TYPE>void Shift(TYPE value)
{
uint8_t loops = (( 8 * sizeof(value) ) - 1 );
LATCH;
for (uint8_t i = 0; i <= loops; i++)
{
if( !!(value&(1<<i)) == true) //If value is not a 1, turn off MOSIMASK
{ SPIPINPORT |= MOSIMASK; }
else
{ SPIPINPORT &= ~MOSIMASK; }
PULSE; //Pulse the Clock
}
unLATCH;
}
When I compile, I get the following errors:
Compiling 'zLEDArray' for 'Arduino Uno'
zLEDArray.ino : variable or field 'Shift' declared void
zLEDArray.ino : 'TYPE' was not declared in this scope
Error compiling
What am I doing wrong?
Okay, this falls in the category of "beware of dev tools bearing gifts". The Arduino sketch tool is your problem. If you turn on verbose compiler output on the preferences menu, you will get some insight into what happens. With your code, I can duplicate your error. When compiling a test project called template1, the same error is reported, but now can see the compiler command line:
D:\arduino-dev\arduino-1.0.3\hardware\tools\avr\bin\avr-g++ -c ...yada yada
more yada... e:\Temp\build3528223623599856131.tmp\template1.cpp ...
template1:14: error: variable or field 'Shift' declared void
template1:14: error: 'TYPE' was not declared in this scope
The key point is that .CPP file. That is a file that the dev environment constructs from your .INO and is what is the actual input to the compiler. If you go grab that file, you will see all your code with some bonus lines included:
#include "Arduino.h"
void Shift(TYPE value);
void setup();
void loop();
The build tool added for you, 4 lines:
the Arduino header (because nobody remembers this)
3 forward declarations for functions that it figured out by parsing code
The attempt at producing a forward declaration from the function template is incorrect, and produces the code that results in the compiler error.
The solution is to move the template out from the .INO file.
Create a library folder, say T1.
Create a .H file in that folder with the template code, say tspi.h.
Import the library to your project.
Make sure the #include line is after the first line of code in your .INO (more weirdness - the tool will insert a #include "Arduino.h" after all the comments but before first line of code. If you leave your include at the top of the .INO file, it will be processed before the Arduino header)
You can get rid of the error by adding the correct forward declaration in your ino file. The inuntuitive thing is that you must also do this if you define the function before you use it:
template <typename TYPE> void Shift(TYPE value);
// you may use the function here or you can have the declaration
// immediately before the definition
template<typename TYPE>void Shift(TYPE value)
{
// your implementation
}
The explanation why this is the case is in jdr5ca's answer. Thanks for clarifying, I found this out by trial and error.