UART as parameter to a method

I'm trying to get a number of Mega's to talk to each other using a packeting protocol I wrote.
I'm using multiple UARTs, and I would like to use the same functions to send and receive to and from all the ports.

To do this, I tried passing a pointer to a HardwareSerial class:

void Update(HardwareSerial * thisSerial=&Serial);

This doesn't seem to work (which doesn't really surprise me).

Can anyone tell me what the proper way to approach this problem would be?

EDIT: it does work

A reference:

void Update(HardwareSerial & thisSerial = Serial)
  {
  thisSerial.println ("hi there");
  }

void setup ()
  {
  Serial.begin (115200);
  Update ();
  }  // end of setup

void loop ()
  {
    
  }  // end of loop

That looks useful, but not clear to this non software guru.

You call the Update() function with no arguments, but the function definition has two arguments, WTF? Or is "HardwareSerial & thisSerial = Serial" not an argument(s), but rather some C++ magic concoction?

What changes would be needed in the example to use it on say hardware serial 3 port?

Thanks;
Lefty

The function declaration had one argument with a default value.

eg.

void foo (int bar = 3)
  {
  // whatever
  }

In that example there is one argument "bar" which has a default value of 3 if you call foo with no arguments.


What changes would be needed in the example to use it on say hardware serial 3 port?

This:

void Update(HardwareSerial & thisSerial = Serial)
  {
  thisSerial.println ("hi there");
  }

void setup ()
  {
  Serial3.begin (115200);
  Update (Serial3);
  }  // end of setup

void loop () { }

Default arguments are fairly commonly used. For example, from SoftwareSerial:

SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);

In this case you have two arguments (receive and transmit pin) which do not take defaults, and a third argument "inverse_logic" which defaults to false.

The function declaration had one argument with a default value.

eg.

Code:
void foo (int bar = 3)
{
// whatever
}

In that example there is one argument "bar" which has a default value of 3 if you call foo with no arguments.

Cool, I didn't even know it was legal to do that (call with no argument but definition has default argument(s)), is that a C++ only thing?

Lefty

Yes, I think C++ introduced that.

Or Algol maybe.

That takes me back ...

I meant, compared to C.

Cheers for the help Nick!
It turns out my version does actually work, but having to use pointers like that is probably not as elegant:

thisSerial->write(newbyte);

I don't really understand your use of the & operator. What does that do in this case?

It's conceptually similar to a pointer, but a reference is better in this sort of case, it's a bit hard to explain exactly why.

With a pointer you have to dereference it (hence thisSerial->write) but with a reference, you just use it "normally" (eg. thisSerial.write).

Google:

c++ reference vs pointer

There are quite a few articles about the differences.

Is it possible to then also store the reference as part of my class just like a normal variable?

This is the constructor prototype of my class:

SerialNode(HardwareSerial & UART = Serial);

This is the variable inside the class:

HardwareSerial thisSerial;

Then, inside the constructor:

thisSerial=UART;

The problem here is that the HardwareSerial class has 12 arguments:
"note: candidate expects 12 arguments, 0 provided"

I'm not quite sure if this is what you have in mind, but it compiles:

class SerialNode 
  {
  public:
    SerialNode(HardwareSerial & UART = Serial) : thisSerial (UART) { }

    // whichever serial port 
    HardwareSerial & thisSerial;
  };

SerialNode foo (Serial2);

void setup ()
  {
  foo.thisSerial.begin (115200);
  }  // end of setup

void loop () { }

What I would like to be able to do is set up a serial node for each UART to simplify the routing of packets.

How does the ':' operator work in the constructor prototype?

It seems to compile there, but I'm unsure how to format my constructor (it complains about a redefinition of HardwareSerial&).

Right now it looks like this:

SerialNode::SerialNode(HardwareSerial & UART){
	_isPacket=0;
	_escaped=0;
}

It's called a constructor initialization list, if you want to look that up.

Can you post all your code? I can't see why your little snippets would, or would not, compile.

At least post something that ought to compile. ie. with setup, loop, the whole class, calling the class, using more than one serial port, etc. Not necessarily the whole project, but enough to demonstrate the thing you are trying to do.

It compiles fine when I remove the constructor for the SerialNode class.

This is the class, 'List' is a linked list.

struct Packet{
			uint8_t address;
			List <uint8_t> data;
			uint8_t checksum;
		};

class SerialNode{
	public:
		SerialNode(HardwareSerial & UART = Serial) : thisSerial (UART){}
		void Update();
		void SendPacket(uint8_t address,List <uint8_t> data);
	private:
		HardwareSerial & thisSerial;//UART of the node
		List <uint8_t> buffer;//data buffer
		void Clear();//clears received info
		uint32_t _lastByte;//time of last inPacket
		bool _isPacket;
		bool _escaped;
		uint8_t _checksum;
		Packet inPacket;
};

My question at this point is: What would the constructor for SerialNode look like?

SerialNode::SerialNode( ???? ) : ??? {
//constructor code
}

See reply #15. That doesn't compile. What is List, for example? Where is setup? Where is loop?

Alright. The reason I was trying to avoid doing this because it's all over the place and there are other problems that I'm still working on (the rest compiles fine though), but here it is:

LinkedList.h:

#ifndef LinkedList_h
#define LinkedList_h

#include "Arduino.h"

template <class T>
class List{
	public:	
		List();
		void push(T data);//LIFO - pushes onto start
		void queue(T data);//FIFO - adds on end
		T pop();
		T tail();
		bool isEmpty();
		uint8_t checksum8bit();
		void insert(T data,T entrypoint);	
		void clear();
	private:
		struct node{
			node *next,*prev;
			T data;
		};
		node *_head,*_tail;
		uint16_t _n;//number of elements
};

template <class T>
List<T>::List(){
	_head=NULL;
	_n=0;
}

template <class T>
void List<T>::push(T data){//pushes a new value to the front of the list
	node * temp=new node;//give memory space
	temp->data=data;//fill space
	temp->next=_head;//connect to list
	_head->prev=temp;
	temp->prev=NULL;
	_head=temp;//redefine start of list
	if(isEmpty()){
		_tail=temp;//first item added is both the tail and the head
	}
	_n++;
}

template <class T>
void List<T>::queue(T data){//queues a new value at the end of the list
	node * temp=new node;//give memory space
	temp->data=data;//fill space
	temp->next=NULL;
	temp->prev=_tail;
	if(isEmpty()){
		_head=temp;
	}else{
		_tail->next=temp;//put new value after current tail
	}
	_tail=temp;//new value is new tail
	_n++;
}

template <class T>
T List<T>::pop(){//pops the head off the list
	node *temp=_head;//pointer to allow deletion
	T temp_data=_head->data;//data to return
	_head=_head->next;//shift list along one
	delete temp;//delete node before head
	_n--;
	return temp_data;//return data
}

template <class T>
T List<T>::tail(){//pops the head off the list
	node *temp=_tail;//pointer to allow deletion
	T temp_data=_tail->data;//data to return
	_tail=_tail->prev;//shift list along one
	delete temp;//delete node before head
	_n--;
	return temp_data;//return data
}

template <class T>
uint8_t List<T>::checksum8bit(){//returns 8-bit checksum of whole list
	uint8_t checksum=0;
	node * temp=_head;
	while(temp->next!=NULL){//run through list
		checksum+=temp->data;
		temp=temp->next;//list should stay intact
	}
	return checksum;
}

template <class T>
bool List<T>::isEmpty(){
	return _head == NULL;
}

template <class T>
void List<T>::clear(){
	while(!isEmpty()){
		pop();
	}
}

#endif

SerialChain.h

#ifndef SerialNode_h
#define SerialNode_h

#include "Arduino.h"
#include "LinkedList.h"

#define START '['
#define STOP ']'
#define ESCAPE '\\'

struct Packet{
			uint8_t address;
			List <uint8_t> data;
			uint8_t checksum;
		};

class SerialNode{
	public:
		SerialNode(HardwareSerial & UART = Serial) : thisSerial (UART){}
		void Update();
		void SendPacket(uint8_t address,List <uint8_t> data);
	private:
		HardwareSerial & thisSerial;//UART of the node
		List <uint8_t> buffer;//data buffer
		void Clear();//clears received info
		uint32_t _lastByte;//time of last inPacket
		bool _isPacket;
		bool _escaped;
		uint8_t _checksum;
		Packet inPacket;
};

SerialNode::SerialNode(HardwareSerial & UART = Serial){

}

void SerialNode::Update(){
	if(thisSerial.available()>0){//only one value per iteration to prevent blocking
		if(abs(_lastByte-millis())>1000){//if more than n millis pass after a packet, ditch data
			Clear();
			_isPacket=0;//no longer in a packet
		}
		uint8_t sbyte=thisSerial.read();//pop byte into variable
		_lastByte=millis();//timestamp the byte
		if(_isPacket){
			if(!_escaped){
				if(sbyte==START){//START byte but already in packet - strange
					Clear();//but stay in packet
				}else if(sbyte==ESCAPE){
					_escaped=1;
				}else if(sbyte==STOP){//pack up the packet
					inPacket.checksum=buffer.tail();//pull checksum off the back of the list
					_checksum=buffer.checksum8bit();//calculate checksum from remaining data
					inPacket.address=buffer.pop();//read address from start of list
					Serial.write(inPacket.checksum);
					if(inPacket.checksum==_checksum){
						//RETURN SUCCESS
						Clear();  
					}else{
						//RETURN FAILED
						Clear();//delete data
					}
					inPacket.data=buffer;
					_isPacket=0;
				}else{//normal character that is part of a packet
					buffer.queue(sbyte);
				}
			}else if(sbyte==START||sbyte==STOP||sbyte==ESCAPE){//escape should be followed by a forbidden character
				buffer.queue(sbyte);
				_escaped=0;
			}else{//pointless escape character - ignore - packet will probably error out
				_escaped=0;
			}
		}else if(sbyte==START){
			_isPacket=1;
		}
		//any data not START or in a packet gets ignored
	}
}

void SerialNode::Clear(){//clear space
	inPacket.address=0;
	inPacket.data.clear();
	inPacket.checksum=0;
}

void SerialNode::SendPacket(uint8_t address,List <uint8_t> data){
	thisSerial.write(START);//start packet
	thisSerial.write(address);//send address
	uint8_t pbyte;
	uint8_t checksum=data.checksum8bit();
	while(!data.isEmpty()){
		pbyte=data.pop();
		if(pbyte==START||pbyte==STOP||pbyte==ESCAPE){
			thisSerial.write(ESCAPE);//escape character out if necessary
		}
		thisSerial.write(pbyte);//write data byte
	}
	thisSerial.write(checksum);
	thisSerial.write(STOP);
}
#endif

Arduino test sketch:

#include <SerialChain.h>

SerialNode node(Serial);

void setup(){
	Serial.begin(9600);
}


void loop(){
	node.Update();
}

Relevant compiler output:

In file included from serialchaintest2.ino:1:0:
/home/scott/sketchbook/libraries/SerialChain/SerialChain.h:23:20: warning: ignoring packed attribute because of unpacked non-POD field ‘HardwareSerial& SerialNode::thisSerial’ [enabled by default]
/home/scott/sketchbook/libraries/SerialChain/SerialChain.h:33:1: error: redefinition of ‘SerialNode::SerialNode(HardwareSerial&)’
/home/scott/sketchbook/libraries/SerialChain/SerialChain.h:19:3: error: ‘SerialNode::SerialNode(HardwareSerial&)’ previously defined here
make: *** [serialchaintest2.o] Error 1

You had two constructors redefined the constructor, as the error message said. Delete these three lines:

SerialNode::SerialNode(HardwareSerial & UART = Serial){

}