I have a problem trying to use classes in Arduino and my particular problem is that my code won't work as i have thought. I'll explain:
I made a library that allows me to control a series of LEDs making them blink, and I'd like to repeat them as many times as programmed. However, I've found a problem with it: when I run the code the cycle will repeat just once and it won't blink all of the LEDs again, as the code is supposed to do. This is the code:
Header
#ifndef Cluster_h
#define Cluster_h
#include "Storm.h"
#include "Arduino.h"
class Cluster{
public:
Cluster(int pin[]);
void lightning(unsigned long g_glag[]);
private:
unsigned long currentmillis,prevmillis,g_flag[];
int counter,u_flag;
Storm* _led;
};
#endif
CPP file
#include <Cluster.h>
Cluster::Cluster(int pin[]){
Storm led[7]={Storm(pin[0]),Storm(pin[1]),Storm(pin[2]),Storm(pin[3]),Storm(pin[4]),Storm(pin[5]),Storm(pin[6])};
_led=led;
prevmillis=0;
counter=0;
}
void Cluster::lightning(unsigned long g_flag[]){
if(counter<=5){
currentmillis=millis();
if(currentmillis-prevmillis>=g_flag[0]){
_led[0].blinkled();
}
if(currentmillis-prevmillis>=g_flag[1]){
_led[1].blinkled();
}
if(currentmillis-prevmillis>=g_flag[2]){
_led[2].blinkled();
}
if(currentmillis-prevmillis>=g_flag[3]){
_led[3].blinkled();
}
if(currentmillis-prevmillis>=g_flag[4]){
_led[4].blinkled();
}
if(currentmillis-prevmillis>=g_flag[5]){
_led[5].blinkled();
}
if(currentmillis-prevmillis>=g_flag[6]){
prevmillis=currentmillis;
counter=counter+1;
}
}
}
The blinkled function from the Storm class is imported from the header file and what this function does is blinking a particular LED (this is already defined). With this code I would expect that the blinking sequence would repeat for 5 times but as I mentioned at the beginning it will blink all the LEDs just once. I have not found what could be wrong with the code, if you could help me that would be awesome.
I appreciate your time, I'll also put the code in the implementation part, just in case.
#include <Cluster.h>
int p[7]={2,3,4,5,6,7,8};
unsigned long gf[7]={50,70,110,150,220,240,1000};
Cluster cluster(p);
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
cluster.lightning(gf);
}
You are not setting prevmillis
Change your function
void Cluster::lightning(unsigned long g_flag[]){
static unsigned long prevmillis=millis();
if(counter<=5){
currentmillis=millis();
if(currentmillis-prevmillis>=g_flag[0]){
_led[0].blinkled();
}
if(currentmillis-prevmillis>=g_flag[1]){
_led[1].blinkled();
}
if(currentmillis-prevmillis>=g_flag[2]){
_led[2].blinkled();
}
if(currentmillis-prevmillis>=g_flag[3]){
_led[3].blinkled();
}
if(currentmillis-prevmillis>=g_flag[4]){
_led[4].blinkled();
}
if(currentmillis-prevmillis>=g_flag[5]){
_led[5].blinkled();
}
if(currentmillis-prevmillis>=g_flag[6]){
prevmillis=currentmillis;
counter=counter+1;
}
}
}
Related
I'm working on a quite complex and large sketch for my ESP32 and I'm dividing it into threads and classes, splitting everything in different files. For sake of simplicity I'm gonna show you just the idea of my project setup.
For instance, I'm using a BME280 sensor to read temperature, humidity, and pressure values. Therefore, I created an header file called bme280.h and an associated cpp file called bme280.cpp. Here's the content of the two files.
bme280.h
#ifndef BME280_H
#define BME280_H
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define SEALEVELPRESSURE_HPA (1013.25)
typedef struct {
float temperature;
float pressure;
float humidity;
}bmeData;
class BME280_sensor {
public:
BME280_sensor();
bmeData readBmeData();
private:
Adafruit_BME280 bme;
};
#endif
bme280.cpp
#include "bme280.h"
BME280_sensor::BME280_sensor() {
bool status;
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
Serial.println("BME280 sensor correctly initialized!");
}
bmeData BME280_sensor::readBmeData() {
bmeData bmeValues;
bmeValues.temperature = bme.readTemperature();
bmeValues.pressure = bme.readPressure() / 100.0F;
bmeValues.humidity = bme.readHumidity();
return bmeValues;
}
This is basically how I'm using every sensor.
Now, my .ino file is busy doing some other job, so I used the pthread library for creating a different thread in charge of reading sensors values. Hence, my .ino file, before doing its job, starts this thread, which I named mainThread. Here's an example:
file.ino
#include <pthread.h>
#include "main.h"
void setup() {
delay(1000);
Serial.begin(115200);
Serial.println("Serial initialized");
pthread_t mainThreadRef;
int mainValue;
mainValue = pthread_create(&mainThreadRef, NULL, mainThread, (void*)NULL);
if (mainValue) {
Serial.println("Main thread error occured");
}
}
void loop() {
// Some other job
}
The main thread, instead, is implemented using a main.h file and a main.cpp file. Here's an example:
main.h
#ifndef MAIN_H
#define MAIN_H
#include <Arduino.h>
void *mainThread();
#endif
main.cpp
#include "main.h"
#include "bme280.h"
void *mainThread() {
BME280_sensor bme;
while (1) {
bmeData bmeValues = bme.readBmeData();
Serial.println(bmeValues.temperature);
Serial.println(bmeValues.humidity);
Serial.println(bmeValues.pressure);
delay(3000);
}
}
Now, I wonder if this whole structure of the project is good, because I'm facing weird values reading, like temperature over 100 or pressure under 0, and some other weird stuff. To be more precise:
Is it "safe" to have a thread acting as the main thread doing all the jobs?
Is it good to have a different class for each sensor that I am using or does it interfere with sensor readings?
Thank you all in advance for you help!
Is it "safe" to have a thread acting as the main thread doing all the jobs?
Yes. The thing where setup() and loop() functions get executed is also a thread. It's probably the first thread in the system, but otherwise there's no difference between it and the threads that you yourself create.
The hard part is not running an isolated process in its own thread - that's usually easy, and often a good idea. The hard part is getting data across different threads. I recommend Mastering the FreeRTOS Real Time Kernel for reading on how FreeRTOS threads (that's what the ESP32 is really using, pthreads is just a wrapper around it) work and how to communicate between them.
Is it good to have a different class for each sensor that I am using or does it interfere with sensor readings?
If you have one class per type of sensor which wraps the mundane details of how to talk to that sensor, then this is generally considered good design (encapsulation etc). But it depends, really. The devil is in the details. Note that the Arduino or Adafruit sensor libraries already do that anyway - they tend to provide a nice, simple interface that you can use without knowing the details of how it works. Don't bother wrapping those (unless you have a clear purpose).
I have noticed the following code, which is obviously invalid C++, compiles in Arduino IDE (using AVR-GCC):
// The program compiles even when the following
// line is commented.
// void someRandomFunction();
void setup() {
// put your setup code here, to run once:
someRandomFunction();
}
void loop() {
// put your main code here, to run repeatedly:
}
void someRandomFunction() {
}
What is going on here? C++ requires functions to be declared before they are used. When the compiler comes to the line someRandomFunction() in the setup() function, how does it know it will be declared?
This is what we call Forward declaration and in C++ it only requires you to declare the prototype of the function before attempting to use it, instead of defining the whole function.:
Taking as example the following two pieces of code:
CODE A:
#include <Arduino.h>
void setup(){}
void AA(){
// any code here
}
void loop(){
AA();
}
CODE B:
#include <Arduino.h>
void setup(){}
void loop(){
BB();
}
void BB(){
// any code here
}
Strictly speaking C requires that functions be forward declared for the compiler to compile and link them. So in CODE A we do not have declared the function but it defined, which makes it legal for proper C code. But the CODE B has the function definition after the loop, which would be illegal for plain C. A solution would be the following one:
#include <Arduino.h>
void BB();
void setup(){}
void loop(){
BB();
}
void BB(){
// any code here
}
This, however, to fit the Arduino script format of having a void setup() following from a void loop(), required Arduino to include a script on its IDE that automatically looks for functions and generate prototypes up top for you so you do not need to worry about it. So despite being written in C++, you will NOT see Arduino sketches using Forward declaration often in their sketches, as it works out fine and it is often easier to read having first setup() and loop().
Your file is .ino not .cpp. .ino is 'Arduino language'.
The main ino file with additional ino files are processed by the Arduino builder to C++ source and only then processed as C++ (preprocessor, compilation).
Although I read a few google-results for that error I can't find my problem for this error, not even while trying to reduce everything to its very basic content.
That's my testclass.h:
class TESTCLASS {
public:
TESTCLASS();
};
int x; // I added this for testing if the file is included from my main code file
x=10; // It is and throws this error: testclass.h:8:1: error: 'x' does not name a type, which I don't understand neither, but it't not the main problem here
testclass.cpp:
#include "testclass.h"
TESTCLASS::TESTCLASS() {
// do some stuff
}
and here's my main code file:
#include "lib/testclass.h"
TESTCLASS test;
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
This throws the error
/var/folders/b5/qc8dstcn02v_hyvgxsq4w9vr0000gq/T//ccQOziAu.ltrans0.ltrans.o: In function `_GLOBAL__sub_I_test':
/Volumes/Daten/stefanherzog/Documents/Nextcloud/Programmierung/Arduino/200515_growboxLibrary_test/200515_growboxLibrary_test.ino:3: undefined reference to `TESTCLASS::TESTCLASS()'
collect2: error: ld returned 1 exit status
exit status 1
So even this is very basic I can't see the problem! I'm using an avr-g++ compiler within my Arduino IDE (v1.8.12).
Can someone please explain me what I'm doing wrong?
it looks like you don't send testclass.cpp to your compiler. If so, your problem does'nt come from your code but your compiling command line.
Using gcc you should have something like :
g++ main.cpp lib/testclass.cpp -o testclass
I don't know the compilation process for arduino but i hope it will helps you finding the solution.
Easiest put testclass.cpp into the same folder as your .ino file. It should show up as a separate tab.
Put testclass.h there as well. and remove the lib subfolder.
And remove the int x=10; definition from the .h file. If both units are including testclass.h, that should end up in a duplicate error.
BTW: an assignment x=10; outside a function is nonsense anyway.
When using subdirectories with the Arduino IDE the subdirectory needs to be named as utility. That's actually it!
Having this structure for example (in ../Arduino/libraries/):
./testclass
./testclass/testclass.h
./testclass/testclass.cpp
./testclass/sub
./testclass/sub/sub.h
./testclass/sub/sub.cpp
testclass.h:
#ifndef __TESTCLASS_H__
#define __TESTCLASS_H__
#include "utility/sub.h"
class TESTCLASS {
public:
TESTCLASS();
};
#endif
testclass.cpp:
#include "testclass.h"
TESTCLASS::TESTCLASS() {
// do some stuff
}
sub.h:
class SUBCLASS {
public:
SUBCLASS();
};
sub.cpp:
#include "sub.h"
SUBCLASS::SUBCLASS() {
// do some stuff
}
You can simply include the "main" testclass.h in your project and instantiate the class and even the subclass:
#include <testclass.h>
TESTCLASS test;
SUBCLASS sub;
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
I want to make a class in cpp for arduino uno that writes on a display. I'm using the LiquidCrystal_I2C library but I can't use it in my class. I know how to do it without a class, but right now I want to build a class and I cant get it to work.
My .h file:
// WriteDisplay.h
#ifndef _WRITEDISPLAY_h
#define _WRITEDISPLAY_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include <Wire/Wire.h>
#include <LiquidCrystal_I2C2004V1/LiquidCrystal_I2C.h>
class WriteDisplayClass
{
public:
WriteDisplayClass();
void write(String text);
private:
LiquidCrystal_I2C lcd(0x27,20,4);
};
extern WriteDisplayClass WriteDisplay;
#endif
My .cpp:
#include "WriteDisplay.h"
WriteDisplayClass::WriteDisplayClass()
{
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
}
WriteDisplayClass::write(String text)
{
lcd.clear();
lcd.print(text);
}
WriteDisplayClass WriteDisplay;
My .ino:
#include "WriteDisplay.h"
WriteDisplayClass wdc;
void setup()
{
wdc.write("Hello World");
}
void loop()
{
}
I'm using AtmelStudio with Visual Micro. I'm getting it to work when I'm only using my .ino-file, but I can't do the same thing in cpp. I'm getting errors that LiquidCrystal_I2C.h can't be found and stuff like that. How should I do to get it to work the way I want it to? Or is it even possible?
Thanks for answer.
Sorry I misread the question the first time.
To use libraries in the .cpp file of an Arduino sketch you must also include them in the master .ino file. They are only compiled if found in the .ino
You can add the includes manually or use the "Project>Add/Import Sketch Library" menu item which will add them to the .ino for you.
Based on this post here the problem with your code is related the class being instantiated as global. The problem comes that the compiler doesn't guarantee the order of global variables processing, so in order to guarantee that the object concerning the display is executed lastly after all the library ones, you have to instantiate it in the setup() function!
The solution to your .ino code is to set a global pointer and then you assign the object inside the setup() function, like so:
#include "WriteDisplay.h"
WriteDisplayClass *wdc;
void setup()
{
wdc = new WriteDisplayClass();
wdc->write("Hello World");
}
void loop()
{
}
I'm trying to do Serial.println() within a class in the Arduino IDE. However, the compiler is saying that Serial was not declared in this scope. Here is the code:
Code in Menu.h
class Menu
{
public:
int options[4];
void test() {
Serial.println("here");
}
private:
};
Code in main file:
#include "Menu.h"
Menu menu;
void setup() {
Serial.begin(9600);
menu.test();
}
void loop() {
}
The right #include is probably added automatically by Arduino to your .pde. Try #include <WProgram.h> in the top of your Menu.h.
I'm not sure if the code snippet is complete (if it isn't, please post a complete one) but it looks like you forgot to include the appropriate header file which declares the class Serial.