Go Down

Topic: Using serial.print to debug a library (Read 1 time) previous topic - next topic

fretless_kb

Ah Thanks I will clean that up, That would be better practice eh?  I appreciate the feedback. - Kb

PaulS

Quote
That would be better practice eh?

Well, it's better in the sense that it is absolutely necessary, so, yeah.

fretless_kb


Quote
Now to try to find out why it doesn't output anything to the port.

Well, that's easy. When is your class constructor called? When is the HardwareSerial constructor called? You don't know, do you? Therefore, you should not write code that relies on them being called in the correct order.

You need a begin() method in your class. You pass nothing to the constructor. You pass a serial instance to the begin method. You call the begin method in the setup() function.


So I'm trying to learn and I appreciate your patience and help, but I don't quite follow the recommendation to pass nothing to the constructor.  I went to the C++ tutorial website here: http://www.cplusplus.com/doc/tutorial/ and reviewed examples of constructors and destructors  for classes.  I tested a few things. I Thought the constructor, being outside of setup() or loop(), is called first. and therefore passing the Serial object to the library when declaring an instance seemed reasonable. 

I tried to construct an example where I did not pass Serial in the constructor, but I could not get it to compile.  However I was successful in creating an example where I pass Serial in the constructor (for the instance myLibrary) and later calling a begin() function. 


This code works, Is there some reason it is incorrect?
.ino
Code: [Select]

#include "MyLibrary.h"

MyLibrary myLibrary(Serial);

void setup() {
  myLibrary.begin();
  myLibrary.test();
}
void loop() {
  myLibrary.test();
  delay(1000);
}

.cpp
Code: [Select]
#include "MyLibrary.h"
#include "HardwareSerial.h"
    //pass a reference to a Print object
    MyLibrary::MyLibrary( HardwareSerial &print ) {
      printer = &print; //operate on the address of print
    }
    void MyLibrary::begin() {
      printer->begin(115200);
    }
    void MyLibrary::test() {
      printer->println("Hello library with serial connectivity!");
    }

and .h
Code: [Select]
#ifndef _MYLIBRARY_H
#define _MYLIBRARY_H
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

class MyLibrary {
  public:
    //pass a reference to a Print object
    MyLibrary(HardwareSerial &print);
    void begin( );
    void test();
  private:
    HardwareSerial* printer;
};
#endif


My Other code does not But I was unsure what to put in the coonstructor I tried it 2 ways, with nothing (assuming the pointer to HardwareSerial has storage already assigned so it could safely be pointed to, and one declaring storage for the Hardware serial, but I get compiler errors trying to pass Serial in the begin call.

Here's the last round of my attempt to not pass anything in the constructor and pass the reference in the call to begin. I'm not following how to code what you suggested.
.ino
Code: [Select]

#include "MyLibrary.h"

MyLibrary myLibrary();

void setup() {
  myLibrary.begin(Serial);
  myLibrary.test();
}
void loop() {
  myLibrary.test();
  delay(1000);
}

.cpp
Code: [Select]
#include "MyLibrary.h"
#include "HardwareSerial.h"
    //pass a reference to a Print object
    MyLibrary::MyLibrary(  ) {
    printer = new HardwareSerial;
    }

    MyLibrary::~MyLibrary(  ) {
    delete printer;
    }

    void MyLibrary::begin(HardwareSerial &print) {
      printer = &print; //operate on the address of print
      printer->begin(115200);
    }

    void MyLibrary::test() {
      printer->println("Hello library with serial connectivity!");
    }

.h
Code: [Select]
#ifndef _MYLIBRARY_H
#define _MYLIBRARY_H
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

class MyLibrary {
  public:
    //pass a reference to a Print object
    MyLibrary();
    ~MyLibrary();
    void begin(HardwareSerial &print);
    void test();
  private:
    HardwareSerial* printer;
};
#endif


Errors:
HardwareSerialClassExampleSplit_V2.cpp: In function 'void setup()':
HardwareSerialClassExampleSplit_V2:6: error: request for member 'begin' in 'myLibrary', which is of non-class type 'MyLibrary ()()'
HardwareSerialClassExampleSplit_V2:7: error: request for member 'test' in 'myLibrary', which is of non-class type 'MyLibrary ()()'
HardwareSerialClassExampleSplit_V2.cpp: In function 'void loop()':
HardwareSerialClassExampleSplit_V2:10: error: request for member 'test' in 'myLibrary', which is of non-class type 'MyLibrary ()()'


Note if the example I posted first is 'safe' I'm happy to code it per that example. if there is something inherently dangerous in that approach even though it works please point that out.  - Thanks again - Kb





PaulS

Quote
I tested a few things. I Thought the constructor, being outside of setup() or loop(), is called first. and therefore passing the Serial object to the library when declaring an instance seemed reasonable. 

It does, and it would, IF you knew that the HardwareSerial instance had been created. Do you KNOW that? Or, are you only hoping that your constructor gets called after the HardwareSerial constructor?

Code: [Select]
MyLibrary myLibrary(Serial);
Here you are passing a possibly unvalued variable to the constructor of your class. Does that really seem like a good idea?

It would be so much easier to pass a pointer to your class than to pass an instance to your class. Why is that not an acceptable possibility?

Frisky

Paul
If you passed a pointer instead of a instance would you test to see if the pointer = NULL in the class member Begin()? before trying to use it. I'm trying to follow along here so get more experience.
I wonder of possible use myself.
Don

PaulS

Quote
If you passed a pointer instead of a instance would you test to see if the pointer = NULL in the class member Begin()? before trying to use it.

Yes, and anywhere else you want to dereference the pointer:
Code: [Select]
if(printer)
  printer->print("whatever");

fretless_kb


It does, and it would, IF you knew that the HardwareSerial instance had been created. Do you KNOW that? Or, are you only hoping that your constructor gets called after the HardwareSerial constructor?
Code: [Select]
MyLibrary myLibrary(Serial);
Here you are passing a possibly unvalued variable to the constructor of your class. Does that really seem like a good idea?
It would be so much easier to pass a pointer to your class than to pass an instance to your class. Why is that not an acceptable possibility?


I hear what your saying, and I believe in a C++ program I should be able to deterministically tell the order of creation, but I have no idea what the Arduino IDE is doing under the hood. so I made some assumptions as bad as that might seem.  

If I pass a pointer to my class of type HardwareSerial I still need to ensure the Serial class is created first.

I'll need to think on this for a while.  Thanks for your input today.

Cheers Kb

PaulS

Quote
but I have no idea what the Arduino IDE is doing under the hood. so I made some assumptions as bad as that might seem. 

It's not bad, as long as you understand that they are assumptions, and that you are will to make changes when the assumptions prove incorrect.

Quote
If I pass a pointer to my class of type HardwareSerial I still need to ensure the Serial class is created first.

That depends. If you (try to) pass the pointer to the constructor, then, yes, it is a problem. If, instead, you pass the pointer to the begin() method, and you call the begin() method in setup(), then you can be confident that the Serial instance will exist, and you can pass a pointer to it to your method.



fretless_kb

Well I seem to be stuck trying to implement what you are referring to.  I understand that if I don't declare a constructor C++ will create a default one for me so in this simple example I should be able to get away with not declaring a constructor. so I re-wrote the example to call the constructor with no arguments, I created a begin function which should accept a HardwareSerial pointer. But When I compile this I get compiler errors.

.ino
Code: [Select]
#include "MyLibrary.h"

MyLibrary myLibrary();

void setup() {
  HardwareSerial* hwPrint;
  hwPrint = &Serial; 
  myLibrary.begin(hwPrint);
  myLibrary.test();
}
void loop() {
  myLibrary.test();
  delay(1000);
}

.cpp
Code: [Select]
#include "MyLibrary.h"
#include "HardwareSerial.h"

    void MyLibrary::begin(HardwareSerial* hwPrint) {
      printer = hwPrint; //operate on the address of print
      if(printer) {
      printer->begin(115200);
      }     
    }

    void MyLibrary::test() {
      if(printer) {
      printer->println("Hello library with serial connectivity!");
      }
    }

.h
Code: [Select]
#ifndef _MYLIBRARY_H
#define _MYLIBRARY_H
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

class MyLibrary {
  public:
    //pass a reference to a Print object
    void begin(HardwareSerial* hwPrint);
    void test();
  private:
    HardwareSerial* printer;
};
#endif


Errors:
HardwareSerialClassExampleSplit_V2.cpp: In function 'void setup()':
HardwareSerialClassExampleSplit_V2:8: error: request for member 'begin' in 'myLibrary', which is of non-class type 'MyLibrary ()()'
HardwareSerialClassExampleSplit_V2:9: error: request for member 'test' in 'myLibrary', which is of non-class type 'MyLibrary ()()'
HardwareSerialClassExampleSplit_V2.cpp: In function 'void loop()':
HardwareSerialClassExampleSplit_V2:12: error: request for member 'test' in 'myLibrary', which is of non-class type 'MyLibrary ()()'

This is probably something trivial, but I can't seem to find a good reference to what the error is cause by.

Cheers Kb



PaulS

Code: [Select]
MyLibrary myLibrary();
The parentheses are needed to pass arguments to the constructor. If there are no arguments, there are no parentheses.

Remove them, and see what that does to the error messages.

fretless_kb

Thanks, That solves it. No errors and the code functions, i.e. I get prints to the serial port. 

Thanks again so much.  Now on to designing my library properly. (that could take a while) But I really appreciate learning how to use these objects correctly.

Cheers Kb

PaulS

Quote
No errors and the code functions, i.e. I get prints to the serial port.

Great. It's easy after 30 years.

Go Up