The introductory code in my sketch, before void (setup), includes this line:
void printDetail(uint8_t type, int value);
I don't understand why it needs 'void', as that is defined by the function later in the sketch:
void printDetail(uint8_t type, int value)
{
switch (type)
{
case TimeOut:
Serial.println(F("Time Out!"));
break;
case WrongStack:
Serial.println(F("Stack Wrong!"));
break;
case DFPlayerCardInserted:
Serial.println(F("Card Inserted!"));
break;
etc
It plainly does need it, because if I remove it Verify gives this error (obscure to me): "expected constructor. destructor, or type conversion before ';' token
We can study the issue with two examples; where, return types are void and int . 1.Example-1:
void printDetail(uint8_t type, int value); //function must be declared before it is defined/used
//if not declared, the Arduino IDE compiler does it for us in the background
void setup()
{
Serial.begin(9600);
printDetail(8, 475); //calling program does not expect any value from called program
}
void loop()
{
}
void printDetail(uint8_t type, int value) //function will return no value (hence void) to calling program
{
int x = (int)type + value; //type is casted to int to make it 16-bit;
// x = 0x0008 + 0x01DB = x01E3 (483 in decimal)
Serial.println(x, DEC); //shows: 483
}
2.Example-2:
int printDetail(uint8_t type, int value); //function must be declared before it is defined/used
void setup()
{
Serial.begin(9600);
int y = printDetail(8, 475); //calling program expects value from called program via y
Serial.println(y, DEC); //shows: 483
}
void loop()
{
}
int printDetail(uint8_t type, int value) //returns int-type data to the calling program
{
int x = (int)type + value; // x = 0x0008 + 0x01DB = x01E3 (483 in decimal)
return x; //x is copied into y of the calling program
}
This is a declaration. This line might exist earlier as a short list because the definition is a larger body of text.
Later in the sketch, the function definition does exist, but both need the word void to differentiate the definition or declaration of a function from a constructor as indicated by the error.
A simple web search will produce many results explaining the difference between declare, define, initialize, instantiate etc. Here is one example that also helps to explain why it might exist above, separate from the definition later.
Probably for the same reason why prototypes are required in the first place. The language specification requires prototypes. The compilers compile accordingly. They are probably able to work effectively when they can rely on the prototypes. When they encounter a function call in the code, they can build the code accordingly even if the actuall function has not yet been built up. The compiler does not at that point search for the code for the function to check for return types.
So does that mean the error would not have occurred if I'd placed the function with the other code before void(setup)? If so, is that the recommended location for functions? I chose to put it after void(loop) because that seemed to be what I'd seen in most sketches.
Thanks, that useful article did help. But mostly, like some of the other replies here, over my head as a non-programmer. Most of my coding is in copy/paste mode anyway, so I'll settle for just using it as it stands.
OK, I stand corrected. I suppose my train of thought was simply:
its name is that of a function defined later
it ends with a semicolon
so it's 'calling' for that function to be executed.
Prototypes, constructors, destructors, classes, types, etc, - no, unfortunately not understood here, and not optimistic I ever will without a C++ course.
True, although I didn't think the full code was appropriate. And I bet regulars here will have seen many of the hundreds of posts here by users of DFRobot's MP3 Mini Player module, with its DFRobotDFPlayerMini library?
FWIW here's the original in full.
/***************************************************
DFPlayer - A Mini MP3 Player For Arduino
<https://www.dfrobot.com/index.php?route=product/product&product_id=1121>
***************************************************
This example shows the all the function of library for DFPlayer.
Created 2016-12-07
By [Angelo qiao](Angelo.qiao@dfrobot.com)
GNU Lesser General Public License.
See <http://www.gnu.org/licenses/> for details.
All above must be included in any redistribution
****************************************************/
/***********Notice and Trouble shooting***************
1.Connection and Diagram can be found here
<https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram>
2.This code is tested on Arduino Uno, Leonardo, Mega boards.
****************************************************/
#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
void setup()
{
mySoftwareSerial.begin(9600);
Serial.begin(115200);
Serial.println("");
Serial.println();
Serial.println(F("DFRobot DFPlayer Mini Demo"));
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(mySoftwareSerial)) //Use softwareSerial to communicate with mp3.
{
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while (true);
}
Serial.println(F("DFPlayer Mini online."));
myDFPlayer.setTimeOut(500); //Set serial communictaion time out 500ms
//----Set volume----
myDFPlayer.volume(10); //Set volume value (0~30).
myDFPlayer.volumeUp(); //Volume Up
myDFPlayer.volumeDown(); //Volume Down
//----Set different EQ----
myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
// myDFPlayer.EQ(DFPLAYER_EQ_POP);
// myDFPlayer.EQ(DFPLAYER_EQ_ROCK);
// myDFPlayer.EQ(DFPLAYER_EQ_JAZZ);
// myDFPlayer.EQ(DFPLAYER_EQ_CLASSIC);
// myDFPlayer.EQ(DFPLAYER_EQ_BASS);
//----Set device we use SD as default----
// myDFPlayer.outputDevice(DFPLAYER_DEVICE_U_DISK);
// myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);
// myDFPlayer.outputDevice(DFPLAYER_DEVICE_AUX);
// myDFPlayer.outputDevice(DFPLAYER_DEVICE_SLEEP);
// myDFPlayer.outputDevice(DFPLAYER_DEVICE_FLASH);
//----Mp3 control----
// myDFPlayer.sleep(); //sleep
// myDFPlayer.reset(); //Reset the module
// myDFPlayer.enableDAC(); //Enable On-chip DAC
// myDFPlayer.disableDAC(); //Disable On-chip DAC
// myDFPlayer.outputSetting(true, 15); //output setting, enable the output and set the gain to 15
//----Mp3 play----
// myDFPlayer.next(); //Play next mp3
// delay(1000);
// myDFPlayer.previous(); //Play previous mp3
// delay(1000);
// myDFPlayer.play(1); //Play the first mp3
// delay(5000);
// myDFPlayer.loop(1); //Loop the first mp3
// delay(3000);
// myDFPlayer.pause(); //pause the mp3
// delay(1000);
// myDFPlayer.start(); //start the mp3 from the pause
// delay(1000);
// myDFPlayer.playFolder(15, 4); //play specific mp3 in SD:/15/004.mp3; Folder Name(1~99); File Name(1~255)
// delay(1000);
// myDFPlayer.enableLoopAll(); //loop all mp3 files.
// delay(1000);
// myDFPlayer.disableLoopAll(); //stop loop all mp3 files.
// delay(1000);
// myDFPlayer.playMp3Folder(4); //play specific mp3 in SD:/MP3/0004.mp3; File Name(0~65535)
// delay(1000);
// myDFPlayer.advertise(3); //advertise specific mp3 in SD:/ADVERT/0003.mp3; File Name(0~65535)
// delay(1000);
// myDFPlayer.stopAdvertise(); //stop advertise
// delay(1000);
// myDFPlayer.playLargeFolder(2, 999); //play specific mp3 in SD:/02/004.mp3; Folder Name(1~10); File Name(1~1000)
// delay(1000);
// myDFPlayer.loopFolder(5); //loop all mp3 files in folder SD:/05.
// delay(1000);
myDFPlayer.randomAll(); //Random play all the mp3.
delay(1000);
// myDFPlayer.enableLoop(); //enable loop.
// delay(1000);
// myDFPlayer.disableLoop(); //disable loop.
// delay(1000);
myDFPlayer.play(1); //Play the first mp3
delay(6000);
//----Read imformation----
Serial.println(myDFPlayer.readState()); //read mp3 state
Serial.println(myDFPlayer.readVolume()); //read current volume
Serial.println(myDFPlayer.readEQ()); //read EQ setting
Serial.println(myDFPlayer.readFileCounts()); //read all file counts in SD card
Serial.println(myDFPlayer.readCurrentFileNumber()); //read current play file number
Serial.println(myDFPlayer.readFileCountsInFolder(3)); //read fill counts in folder SD:/03
// while (1 == 1); // Stops program from proceeding
}
void loop()
{
// static unsigned long timer = millis();
//
// if (millis() - timer > 11000)
// {
// timer = millis();
// myDFPlayer.next(); //Play next mp3 every 3 second.
// }
//
// if (myDFPlayer.available())
// {
// printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
// }
}
void printDetail(uint8_t type, int value)
{
switch (type)
{
case TimeOut:
Serial.println(F("Time Out!"));
break;
case WrongStack:
Serial.println(F("Stack Wrong!"));
break;
case DFPlayerCardInserted:
Serial.println(F("Card Inserted!"));
break;
case DFPlayerCardRemoved:
Serial.println(F("Card Removed!"));
break;
case DFPlayerCardOnline:
Serial.println(F("Card Online!"));
break;
case DFPlayerPlayFinished:
Serial.print(F("Number:"));
Serial.print(value);
Serial.println(F(" Play Finished!"));
break;
case DFPlayerError:
Serial.print(F("DFPlayerError:"));
switch (value)
{
case Busy:
Serial.println(F("Card not found"));
break;
case Sleeping:
Serial.println(F("Sleeping"));
break;
case SerialWrongStack:
Serial.println(F("Get Wrong Stack"));
break;
case CheckSumNotMatch:
Serial.println(F("Check Sum Not Match"));
break;
case FileIndexOut:
Serial.println(F("File Index Out of Bound"));
break;
case FileMismatch:
Serial.println(F("Cannot Find File"));
break;
case Advertise:
Serial.println(F("In Advertise"));
break;
default:
break;
}
break;
default:
break;
}
}
If you had placed the definition (full code of the function) ahead of any use, no error.
With the Arduino preprocessor, the intent, and usually successful, is that you can place the definition anywhere you like.
Your "mistake" was to use a declaration that was not complete, an error.
If you had made a complete declaration, then you may still have an error if it turned out you lied… and the error would flag a mismatch.
I don't really like the preprocessor hiding the things "real" programmers have to do, but someone decided it would make noob life easier… not expecting, I guess, anyone to bother even learning about the real world where declaration has to come before use.
I code like the source file is soup. My function definitions end up "somewhere", I never really care too much as long as all the references resolve themselves and the thing works. I use the functionality of my text processor (or even the IDE text editor) to rapidly find the definition if it needs scrutiny.
As with so many things, a matter of personal preference. I'm just bone lazy.
I would say most of the time I have functions before setup(), and loop() and functions after setup and loop and almost always setup() is found before loop(), but don't count on it.
My understanding, in commercial C++ compilers (and maybe the C++ standard); the compiler needs to know that there is a function when compiling the "main" code.
Now the "prototype" function before Setup in Arduino IDE is that information. And here is the answer to your question:
It prototype needs to be the same as the actual function else the compiler will not know the output is void when compiling the main code.
IMHO it is unwise to define anything in more than one place. However if you must the MUST be identical.