1

Im getting this error:

iotHook.ino: In function 'void setup()':
iotHook:13: error: request for member 'begin' in 'esp', which is of non-class type 'Esp8266()'
request for member 'begin' in 'esp', which is of non-class type 'Esp8266()'

When I try to lunch my begin function of my class:

class Esp8266 {

  public:
    Esp8266();
    void begin(HardwareSerial *wifiCom, HardwareSerial *debugCom);
    ...
  private:

    HardwareSerial *wifiCom;
    HardwareSerial *debugCom;
    ...

}

Esp8266::Esp8266() {
}

void Esp8266::begin(HardwareSerial *wifiCom, HardwareSerial *debugCom) {
  this->wifiCom = wifiCom;
  this->debugCom = debugCom;

  this->debugCom->begin(115200);
  while (!this->debugCom) {
  ;
  }
}

enter image description here

EDIT 1: Updated image with new code enter image description here

Why is not picking up the begin function?

3
  • Remove the brackets from your constructor.
    – Majenko
    Commented Aug 26, 2015 at 22:44
  • @Majenko You mean from this Esp8266::Esp8266() {} to Esp8266::Esp8266() ???
    – DomingoSL
    Commented Aug 26, 2015 at 22:47
  • No, what Ignacio says. The constructor in your sketch.
    – Majenko
    Commented Aug 26, 2015 at 22:52

3 Answers 3

2

Your current problem is that the USB Serial interface of the Leonardo isn't a hardware serial device, it's a CDC/ACM device, and the class for that isn't HardwareSerial, it's Serial_.

So you can't pass it to a function that expects a HardwareSerial.

What you should be using, instead, is the Stream class. That will allow you to pass any object that is based on the Stream class, such as HardwareSerial, Serial_, Client, etc. Many different things, including serial and networking protocols, use Stream as their base class.

It does have the down side, though, that you have to initialize the Stream objects in your sketch (that is, do the Serial.begin(...) and Serial1.begin(...) in your sketch not your class) because the Stream class doesn't have the concept of a baud rate, and so no begin(baud) function.

This is what I mean:

class Esp8266 {

public:
    Esp8266();
    void begin(Stream &wifiCom, Stream &debugCom);
private:

    Stream *_wifiCom;
    Stream *_debugCom;

};

Esp8266::Esp8266() {
}

void Esp8266::begin(Stream &wifiCom, Stream &debugCom) {
    _wifiCom = &wifiCom;
    _debugCom = &debugCom;

    while (!_debugCom) {
        ;
    }
}

Esp8266 esp;

void setup() {
    Serial.begin(115200);
    Serial1.begin(115200);
    esp.begin(Serial, Serial1);
}  // end of setup

void loop() {
}  // end of

Note, as well, my use of pass-by-reference (Stream &whatever) so you don't have to pass a pointer in your begin function. Also notice the use of unique variable names in the class (I always prefix my member variables with _) so you don't have to keep putting this-> in everywhere.


To be able to move the .begin() function inside your class you will have to use both HardwareSerial and Serial_ object types within your class. There's two ways this may be done.

First is dynamic casting:

void Esp8266::begin(Stream &wifiCom, Stream *debugCom) {
    _wifiCom = &wifiCom;
    _debugCom = &debugCom;

    HardwareSerial *hard;
    Serial_ *cdc;

    if (hard = dynamic_cast<HardwareSerial*>(_wifiCom)) {
        hard->begin(115200);
    } else if (cdc = dynamic_cast<Serial_*>(_wifiCom)) {
        cdc->begin(115200);
    }

    if (hard = dynamic_cast<HardwareSerial*>(_debugCom)) {
        hard->begin(115200);
    } else if (cdc = dynamic_cast<Serial_*>(_debugCom)) {
        cdc->begin(115200);
    }

}

I haven't tested this, and I don't know if the Arduino's cut-down C++ library is even capable of it, but it's worth a try.

The other method is to overload the .begin() function of your class with multiple versions that take different parameters:

void Esp8266::begin(Serial_ &wifiCom, HardwareSerial *debugCom) {
    _wifiCom = &wifiCom;
    _debugCom = &debugCom;
    wifiCom.begin(115200);
    debugCom.begin(115200);
}

void Esp8266::begin(HardwareSerial &wifiCom, HardwareSerial *debugCom) {
    _wifiCom = &wifiCom;
    _debugCom = &debugCom;
    wifiCom.begin(115200);
    debugCom.begin(115200);
}

void Esp8266::begin(HardwareSerial &wifiCom, Serial_ *debugCom) {
    _wifiCom = &wifiCom;
    _debugCom = &debugCom;
    wifiCom.begin(115200);
    debugCom.begin(115200);
}

You're still storing them internally as Stream objects in all cases, but within the begin() function itself you also have them available as their child classes. It does mean you have duplicate code copied between the different versions of begin(). You could modularize it a little more though by moving the duplicated code into functions which would only need to be duplicated twice, not three times - one to act on a single Serial_ object and one to act on a single HardwareSerial object:

void Esp8266::initSerial(Serial_ *ser) {
    ser->begin(115200);
}

void Esp8266::initSerial(HardwareSerial *ser) {
    ser->begin(115200);
}

void Esp8266::begin(Serial_ &wifiCom, HardwareSerial *debugCom) {
    _wifiCom = &wifiCom;
    _debugCom = &debugCom;
    initSerial(&wifiCom);
    initSerial(&debugCom);
}
... etc x3 ...
3
  • Thank you, this is the cleaner answer. Is not very clear to me why I have to initialize the stream object outside of my class. Is important to me because my code have a guessing routine inside my begin function that tries different baud rates till it finds the correct one (pastebin.com/GYjE48yj), since I cant use .begin(speed) inside my class, do you know any workaround?
    – DomingoSL
    Commented Aug 27, 2015 at 9:31
  • @DomingoSL It's because the .begin(baud) function only exists in the HardwareSerial and Serial_ child classes and not the Stream parent class. You could try using dynamic casting to attempt to convert the Stream objects into first a HardwareSerial object and then (if that fails) a Serial_ object, but how that would perform (if it's even possible) on an Arduino is anyone's guess. The other option is to provide multiple begin() functions or constructors that take all the possible different combinations of object types and deals with them accordingly.
    – Majenko
    Commented Aug 27, 2015 at 9:38
  • @DomingoSL I have added more detail to my answer.
    – Majenko
    Commented Aug 27, 2015 at 9:50
1

You've confused the compiler by making it think that esp is a function that takes no arguments and returns a Esp8266. But the no-argument constructor is called by default, so you don't need the parens there regardless.

Esp8266 esp;
3
  • Updated code according to your answer but same error
    – DomingoSL
    Commented Aug 26, 2015 at 22:50
  • Please check the edit I made
    – DomingoSL
    Commented Aug 26, 2015 at 22:51
  • 1
    You do see that that's a completely different problem, right? Commented Aug 26, 2015 at 22:52
0

The line:

esp.begin (Serial, Serial1);

Should be:

esp.begin (&Serial, &Serial1);

It is expecting a pointer to a HardwareSerial instance.


Also make the instantiation line:

Esp8266 esp;   // <--------- no brackets!

This compiles without errors for me:

class Esp8266 {

  public:
    Esp8266();
    void begin(HardwareSerial *wifiCom, HardwareSerial *debugCom);
  private:

    HardwareSerial *wifiCom;
    HardwareSerial *debugCom;

};

Esp8266::Esp8266() {
}

void Esp8266::begin(HardwareSerial *wifiCom, HardwareSerial *debugCom) {
  this->wifiCom = wifiCom;
  this->debugCom = debugCom;

  this->debugCom->begin(115200);
  while (!this->debugCom) {
  ;
  }
}

Esp8266 esp;

void setup ()
  {
  esp.begin (&Serial, &Serial1);
  }  // end of setup

void loop ()
  {
  }  // end of loop

I would rework to use references anyway, saves all that mucking around with pointers:

class Esp8266 {
  public:
    Esp8266(HardwareSerial & wifiCom, HardwareSerial & debugCom);
    void begin();
  private:
    HardwareSerial & wifiCom;
    HardwareSerial & debugCom;
  };

// constructor
Esp8266::Esp8266(HardwareSerial &wifiCom_, HardwareSerial &debugCom_)
  : wifiCom (wifiCom_), debugCom (debugCom_)
  {
  }

// initialize stuff
void Esp8266::begin()
{
  debugCom.begin(115200);
  while (!debugCom)
  {
  }
}

Esp8266 esp (Serial, Serial1);

void setup ()
{
  esp.begin ();
}  // end of setup

void loop ()
{
}  // end of loop

Corrected for Leonardo

Sorry, my reply above was compiled for the Mega2560, I didn't notice the Leonardo tag.

Majenko is quite right, you need to rework for the Leonardo. My example using references can reduce down to this now:

class Esp8266 {
  public:
    Esp8266(Stream & wifiCom, Stream & debugCom)
      : wifiCom_ (wifiCom), debugCom_ (debugCom) { }

  private:
    Stream & wifiCom_;
    Stream & debugCom_;
  };

Esp8266 esp (Serial, Serial1);

void setup ()
{
  Serial.begin (115200);
  while (!Serial) { }
  Serial1.begin (115200);
  while (!Serial1) { }
}  // end of setup

void loop ()
{
}  // end of loop

By storing a Stream type you can still print to them, however you need to initialize in setup, not in the begin function, which now becomes redundant.

I agree about not using this-> although my preference is for trailing underscores as the use of leading underscores is reserved in quite a few cases, depending on the location and capitalization.

8
  • Now the error is:Esp.h:42:6: note: no known conversion for argument 1 from 'Serial_' to 'HardwareSerial' no matching function for call to 'Esp8266::begin(Serial_, HardwareSerial)'
    – DomingoSL
    Commented Aug 26, 2015 at 22:53
  • With arduino ide 1.6.5: Esp.h:42:6: note: no known conversion for argument 1 from 'Serial_' to 'HardwareSerial' no matching function for call to 'Esp8266::begin(Serial_, HardwareSerial)'
    – DomingoSL
    Commented Aug 26, 2015 at 22:58
  • You're thinking double-underscore. Builtin (predefined) macros have the format __BLAH_BLAH (sometimes with trailing __ too) although some older compiler versions have some non-standard ones with just one underscore (they are naughty). Certainly for avr-gcc version 5.2.0 all the predefined macros (with the exception of #define AVR 1) are in the standardized double-underscore format.
    – Majenko
    Commented Aug 26, 2015 at 23:36
  • According to the C++ Standard: Reserved in the global namespaces: identifiers beginning with an underscore. Thus all identifiers in global namespace, starting with an underscore, are reserved. Also reserved in any context are identifiers beginning with an underscore and an uppercase letter. In addition: identifiers containing adjacent underscores (or "double underscore"). So you are only one capitalization away from a reserved identifier, by using leading underscores. You might be tempted, for example, to make a class constant like _ITEM_COUNT which has now become reserved.
    – Nick Gammon
    Commented Aug 26, 2015 at 23:53
  • Reserved doesn't mean unavailable. If you like, all my member variables are reserved - I don't want anyone else using them. Any my constants are most static and ProperlyCapitalized (as are my class names) since they aren't reserved.
    – Majenko
    Commented Aug 27, 2015 at 9:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.