Hey it's me again.
I finished all necessary function for my ghostbusters proton pack. I am currently trying to build the "main function" of the state machine that figures out in which mode i am and depending on the current mode in which sub routine i'll be.
i am currently only on the "main function".
I am trying to get it to work with the serial.print functions.
if the main switch (SW_Vent) is in "off position" the function should write "turnedOff" -> that works
if the main switch (SW_Vent) is in "on position" the function should write "Bustin" -> that also works
if I have pushed "SW_WandTip" and then turn on "SW_Vent" the function should write "modeConfigMenu" -> that does not work.
So I do not know why the function does not pass the test and sets my variable currentMainProgramm to MainProgramm::modeConfigMenu.
and then i want to ask why arduino does not return an enum class object in a function, because i get a compile error if i try to compile:
and it seems that i cannot include and use the c++ arrays defined in "std::array" of c++11.
aren't they supported? I am currently using arduino IDE 2.3.2
this here is my code so far and i included the "button" class on top of my code so that you don't have to import it.
/*
Button - a small library for Arduino to handle button debouncing
MIT licensed.
*/
#include <Arduino.h>
class Button {
public:
Button(uint8_t pin);
void begin();
bool read();
bool toggled();
bool pressed();
bool released();
bool has_changed();
const static bool PRESSED = LOW;
const static bool RELEASED = HIGH;
private:
uint8_t _pin;
uint16_t _delay;
bool _state;
bool _has_changed;
uint32_t _ignore_until;
};
Button::Button(uint8_t pin)
: _pin(pin), _delay(500), _state(HIGH), _ignore_until(0), _has_changed(false) {
}
void Button::begin() {
pinMode(_pin, INPUT_PULLUP);
}
//
// public methods
//
bool Button::read() {
// ignore pin changes until after this delay time
if (_ignore_until > millis()) {
// ignore any changes during this period
}
// pin has changed
else if (digitalRead(_pin) != _state) {
_ignore_until = millis() + _delay;
_state = !_state;
_has_changed = true;
}
return _state;
}
// has the button been toggled from on -> off, or vice versa
bool Button::toggled() {
read();
return has_changed();
}
// mostly internal, tells you if a button has changed after calling the read() function
bool Button::has_changed() {
if (_has_changed == true) {
_has_changed = false;
return true;
}
return false;
}
// has the button gone from off -> on
bool Button::pressed() {
if (read() == PRESSED && has_changed() == true)
return true;
else
return false;
}
// has the button gone from on -> off
bool Button::released() {
if (read() == RELEASED && has_changed() == true)
return true;
else
return false;
}
/*
####################################
Button class is above
####################################
*/
//#include <Button.h>
/* std:array C++11 not allowed in arduino?
std::array<int, 5> scores ={0,1,2,3,4};
*/
// Button and Switches
Button SW_WandTip(10);
Button SW_StartUp1(8);
Button SW_StartUp2(7);
Button SW_Intensify(4);
Button SW_BarGraph(3);
Button SW_Vent(2);
void setup() {
Serial.begin(9600);
// Buttons
SW_WandTip.begin();
SW_StartUp1.begin();
SW_StartUp2.begin();
SW_Intensify.begin();
SW_BarGraph.begin();
SW_Vent.begin();
}
int ButtonStates[6];
void updateButtonStates() {
ButtonStates[0] = SW_WandTip.read();
ButtonStates[1] = SW_StartUp1.read();
ButtonStates[2] = SW_StartUp2.read();
ButtonStates[3] = SW_Intensify.read();
ButtonStates[4] = SW_BarGraph.read();
ButtonStates[5] = SW_Vent.read();
}
void print_buttonStates() {
Serial.print(ButtonStates[0]);
Serial.print("|");
Serial.print(ButtonStates[2]);
Serial.print(ButtonStates[1]);
Serial.print("|");
Serial.print(ButtonStates[3]);
Serial.print(ButtonStates[4]);
Serial.println(ButtonStates[5]);
}
enum class MainProgramm {
modeTurnedOff,
modeBustin,
modeMusicPlayer,
modeConfigMenu,
modeERROR
};
MainProgramm currentMainProgramm = MainProgramm::modeTurnedOff;
void print_switch_states() {
switch (currentMainProgramm) {
case MainProgramm::modeTurnedOff:
Serial.println("State: turnedOff");
break;
case MainProgramm::modeBustin:
Serial.println("State: Bustin");
break;
case MainProgramm::modeMusicPlayer:
Serial.println("State: MusicPlayer");
break;
case MainProgramm::modeConfigMenu:
Serial.println("State: ConfigMenu");
break;
case MainProgramm::modeERROR:
Serial.println("State: ERROR");
break;
default:
Serial.println("SWITCH ERROR");
break; // It's good practice to include a break in the default case as well
}
}
void updateMainProgrammStatus() {
if (currentMainProgramm == MainProgramm::modeTurnedOff) {
if (!SW_Vent.read()) currentMainProgramm = MainProgramm::modeTurnedOff;
else if (!SW_WandTip.read() && SW_Vent.read() && SW_Vent.toggled()) MainProgramm::modeConfigMenu;
else if (SW_Vent.read()) currentMainProgramm = MainProgramm::modeBustin;
else currentMainProgramm = MainProgramm::modeERROR;
}
}
//not working? compile error -> does not name a type?
/*
MainProgramm updateCurrentState() {
if (!SW_Vent.read()) return MainProgramm::turnedOff;
else return MainProgramm::ERROR;
}
*/
void loop() {
updateButtonStates();
print_buttonStates();
updateMainProgrammStatus();
print_switch_states();
if (!SW_Vent.read()) currentMainProgramm = MainProgramm::modeTurnedOff;
}
When compiled with warnings turned on, the following warning occurs (amongst others):
/home/me/Documents/sketchbook/Uno_R3/test/test.ino: In function 'void updateMainProgrammStatus()':
/home/me/Documents/sketchbook/Uno_R3/test/test.ino:190:101: warning: statement has no effect [-Wunused-value]
else if (!SW_WandTip.read() && SW_Vent.read() && SW_Vent.toggled()) MainProgramm::modeConfigMenu;
When the function in question is formatted, the problem jumps out clearly:
I can't study your code just now, but copying library code into your sketch so you don't have to "import" is not a good idea.
If you can't say why you did that, don't do that. Would you do the same for any other libraries if your sketch needed a bunch of them? At some point you would start srsly inconveniencing yourself.
I think you may be missing the entire point of using libraries.
Unless maybe you did that for us, so we wouldn't have to install the library to play with your code. Trust me, that's not what in this case would make someone spend more or less time on this.
I look at the code when I am not moving L8R.
Meanwhile at least one real omission has been found by @van_der_decken. That didn't jump out at me, I must say, but I do see it after a bit of perusal.
i did it for your convenience. i posted my code a while a go with 3 includes and the echo was we don‘t want to include many libs of strange sources ;). that‘s why i included it this time.
i‘ll check the missing definition tomorrow. thank you for you fast answers.
will fix your "does not name a type" error in your commented out code.
Why?
The Arduino compilation process generates function declarations that aren't explicitly included in your file automagically for you and adds them to the top of the file. Before the definition of your enum. Which is an "oops". The way around it is to explicitly add the function declaration yourself after declaring the enum. The compilation process sees the declaration, respects it, and doesn't add it to the top.
Usually, the Arduino compilation process gets it right. functions returning enums and functions with variable argument lists (though I'm prepared to be corrected on this one as I'm relying on memory) are a couple of instances where it doesn't.
It would be unusual. TPTB bent over backwards to try and make programming for these boards easier.
The trouble is that the process is (still) flawed, and the fix is sometimes to use prototypes.
You won't see it done too much, I wouldn't bother here.
Also, you may by now have seen that errors can be misleading… suddenly something you can see right in front of you is undefined because of something a few, or even a few more, lines above.
Make changes with a strategy that allows you to verify small steps you make. nothing will slow you down more than making copious changes and then starting to see spurious errors thrown.
The IDE warnings are good, but they do not include all possible warnings. Most of us would be surprised to see some of those coming from our own code.
I see why you "included" the library code. This will not be practical when you do something with, say, an RTC or LCD or stepper motor or all three. Here I would say stick to more commonly used libraries that do those chores, and a glance at my library folder would tell you not everyone is adverse to installing libraries they'd never use otherwise.
You can write code such that presenting a particular problem can be done without all the junk you have needed to make your sketch. In fact many time this will be the request - boil this mess down to the smallest sketch that demonstrates the problem you think you have, called a "minimal reproducible example":
Many times, never mind how many precisely, it is in the effort to make such a sketch that I discover something and answer my own question. Something dumb a fair percentage of the time.
I really like calming the output by reporting on change or prehaps periodically. Even 9600 baud is too fast for me to read.
Consider:
void print_buttonStates() {
int myButtonStates = 0;
for (int ii = 0; ii < 6; ++ii) {
myButtonStates |= ButtonStates[ii] << ii;
}
static int last = -1;
if (myButtonStates != last) {
last = myButtonStates;
Serial.print(ButtonStates[0]);
Serial.print("|");
Serial.print(ButtonStates[2]);
Serial.print(ButtonStates[1]);
Serial.print("|");
Serial.print(ButtonStates[3]);
Serial.print(ButtonStates[4]);
Serial.println(ButtonStates[5]);
}
}
void print_switch_states() {
static MainProgramm last = MainProgramm::modeERROR;
if (currentMainProgramm != last) {
last = currentMainProgramm;
switch (currentMainProgramm) {
case MainProgramm::modeTurnedOff:
Serial.println("State: turnedOff");
break;
case MainProgramm::modeBustin:
Serial.println("State: Bustin");
break;
case MainProgramm::modeMusicPlayer:
Serial.println("State: MusicPlayer");
break;
case MainProgramm::modeConfigMenu:
Serial.println("State: ConfigMenu");
break;
case MainProgramm::modeERROR:
Serial.println("State: ERROR");
break;
default:
Serial.println("SWITCH ERROR");
break; // It's good practice to include a break in the default case as well
}
}
}
hey i tried your work around and it worked perfectly.
I just do not get why putting the prototyp after the enum class works but not putting the complete function after the enum class but i have to read into it on another day.
are there other quirks to know while coding with the arduino ide? ^^.
I'll try to complete my statemachine code code and post it later for suggestion on how to make it cleaner :).
as always thank you guys for the help :-).
and yeah this time i did not miss to vote the solution ;-).
i'll put in the toggle statement, so that all the return stuff only happens if any button has changed it's state and then i'll put the print function to only if the state has changed.
i just did it this way because it was easy and fast and i get to know if changes happen at all, like the "modeConfig" that has not changed.
when i'll have the full code running as i like i'll delete all the print stuff.
btw, i cannot debug in the arduino ide? for debugging i need a debug compatible board am I right?