TwoPortRX ...but with multiple software serial and a hardware serial

I'm hoping to build a system that logs brain wave and GPS data to an Openlog and ideally also shows some of the output on an SPI OLED display.

The brain wave set-up is as detailed here: How to Hack Toy EEGs | Frontier Nerds
A mindflex toy with wires soldered onto ground and TX points on its circuitboard and then connected to the arduino (I'm using a nano 328) via ground and the RX pin (removed during programming).

The brain library that parses the raw data coming out of the toy is available in 2 versions: hardware serial (for the set-up described above) and also a software serial version (which I've been unable to get to work - error messages relating to the library when I try and compile the example).

I can get the hardware serial version of the library working and parsing the data to a csv style output on the serial monitor. But I'm struggling to combine this in a sketch with the GPS module.

Here's a slightly abridged version of the code:

#include <SoftwareSerial.h> //includes the software serial library
#include <TinyGPS.h>  // includes the TinyGPS library
#include <Brain.h>


TinyGPS gps;
SoftwareSerial gpsmodule(3, 4);  
Brain brain(Serial);


//define GPS variables

float flat, flon; // +/- latitude/longitude in degrees
unsigned long date, time, age; // what they say on the tin
long lat, lon; // +/- lat/long in 100000ths of a degree



void setup()  
{
  Serial.begin(9600);  // serial communication between the microcontroller and the computer
  gpsmodule.begin(4800); // serial communication between the gps module and the microcontroller

} //end setup



void loop() 
{
  getGPS(); // get some data to work with


// prints GPS coordinates to serial monitor
  Serial.print(flon, 6);Serial.print(","); Serial.print(flat, 6); Serial.print(",");

  
if (brain.update()) {
  
  		Serial.println("brain updated");

  		//Serial.println(brain.readErrors());
		Serial.println(brain.readCSV());
}

} //end loop. 

void getGPS(){
gpsmodule.listen();
  
// TinyGPS code here

} //end getGPS

This will give me the GPS coordinates as expected, but none of the brain data or the Serial.println("brain updated");

Why might that part of the code not be executing?

I know that if I'm using two software serial instances I need to put [i]name[/i].listen(); before I start working with each data stream, is there something similar I need to do before if (brain.update()) { in order to switch which one the arduino is listening to?

Thanks

Are you trying to talk to the brain thing with the hardware serial port? Or, are you trying to talk to the PC? Both is the wrong answer.

Hi PaulS,

During use (not uploading a sketch) the microcontroller's listening to the brain thing, but not talking to it. It can therefore talk to the PC, but not listen.

From the brain library readme:

Note that the connection to the Neurosky headset is half-duplex — it will use up the RX pin on your Arduino, but you will still be able to send data back to a PC via USB. (Although in this case you won't be able to send data to the Arduino from the PC.)

Hope that clarifies.

What is gpsModule.listen() supposed to do? I can't make sense of the softwareSerial Reference material.

It doesn't seem to collect data from a serial input and save it somewhere.

...R

Hi Robin2,

You've found the abridged bit :slight_smile:

The gpsModule.listen() bit itself is like in this example for TwoPortReceive with SoftwareSerial. When I start to add in other components communicating with the arduino over SoftwareSerial, each will need to be prefaced with a listen()

In order to listen on a software port, you call port.listen(). When using two software serial ports, you have to switch ports by listen()ing on each one in turn.

Below it,

// TinyGPS code here

is a placeholder for TinyGPS library functions that get the data, assign them to variables and print them out over serial etc. They're there in the code I'm running, but I snipped them out to make my post more concise.

They're there in the code I'm running, but I snipped them out to make my post more concise.

Well, good luck with your problem, then. Whatever it is. "I'm struggling..." doesn't tell us squat.

PaulS:
"I'm struggling..." doesn't tell us squat.

Heh, well, I can't argue with that statement...

Here's some complete code if anyone would like to see it:

#include <SoftwareSerial.h> //includes the software serial library
#include <TinyGPS.h>  // includes the TinyGPS library

#include <Brain.h>


TinyGPS gps;
SoftwareSerial gpsmodule(3, 4);  
Brain brain(Serial);


//define GPS variables 

float flat, flon; // +/- latitude/longitude in degrees
unsigned long date, time, age; // what they say on the tin
long lat, lon; // +/- lat/long in 100000ths of a degree


void setup()  
{

  gpsmodule.begin(4800); // serial communication between the gps module and the microcontroller

  Serial.begin(9600);  // serial communication between the microcontroller and the computer


} //end setup



void loop()
{
  getGPS(); // get some data to work with

  Serial.print(flon, 6);
  Serial.print(","); 
  Serial.print(flat, 6); 
  Serial.print(",");

  if (brain.update()) {

    Serial.println("brain updated");

    //Serial.println(brain.readErrors());
    Serial.println(brain.readCSV());
  }

} //end loop.


void getGPS(){
  gpsmodule.listen();
  bool newData = false;
  unsigned long chars;
  unsigned short sentences, failed;
  while (newData == false){
    // For one second we parse GPS data and report some key values
    for (unsigned long start = millis(); millis() - start < 1000;)
    {
      while (gpsmodule.available())
      {
        char c = gpsmodule.read();
        // Serial.write(c); // uncomment this line if you want to see the GPS data flowing
        if (gps.encode(c)) // Did a new valid sentence come in?
          newData = true;
      }
    }

    if (newData)
    {
      gps.get_datetime(&date, &time, &age); // Use this line of code to get the time in hhmmsscc and date in ddmmyy formats

      gps.f_get_position(&flat, &flon, &age);  // Use this line of code to get the latitude and longitude values with a decimal point eg 54.117769


    } //end newData
  }

} //end getGPS

As per original post the GPS coordinates are printed out okay (-x.xxxxxx,xx.xxxxxx,-x.xxxxxx,xx.xxxxxx,-x.xxxxxx,xx.xxxxxx, ... format), but I'm not getting any of the brain data or the Serial.println("brain updated"); debugging statement and I'm confused as to why that section of the code isn't being executed.

That if statement is lifted from this example that comes with the library. That runs fine by itself, and prints new data every second or so - about the same as the GPS data.

I don't have the programming training to know what sort of thing might be causing the problem, but the feeling based on my limited experience is that it's a similar situation to what I've encountered before that turned out to be the port.listen(), hence starting with the question of is there an equivalent for this situation?

I really (I mean REALLY) think you need a Mega, with multiple hardware serial ports. Get the brain thing and the PC on separate serial ports. SoftwareSerial uses interrupts, which may be causing conflicts with the Brain class.

You could add Serial.print() statements to determine if getGPS() ever returns, or it brain.update() ever returns true.

You could add Serial.print() statements to the Brain class, to see what it is doing.

Why not write a short sketch to test reading the brain thing with hardware serial then, when that works try transferring it to software serial.

...R

PaulS:
a Mega, with multiple hardware serial ports

Hmm, that's an idea, I had forgotten about those, thanks. Sounds like it would be a tidy solution.
I don't have a spare Mega to hand at the moment, but I've one on order and may try to liberate one from an existing project if I get too impatient waiting for that!

In the meantime I might have a stab at some Serial.print() statements in the class, or, alternatively, a convoluted work-around with some xbees I have, just so I can get to a proof of concept stage.

Robin2:
Why not write a short sketch to test reading the brain thing with hardware serial then, when that works try transferring it to software serial.

...R

The example sketch reading the brain thing with hardware serial works okay, but there are issues with transferring it to software serial. For starters, the library documentation says that the SoftwareSerial included in the Arduino IDE isn't quite up to the task and that the NewSoftSerial library is better. Trying to use this causes compiling errors with v1.0+. These kick in as soon as you add #include <NewSoftSerial.h> to the working code, so I've not pursued that any further.

I got compiling errors when I tried to use the NewSoftSerial branch of the library too, although IIRC slightly different ones in this case. Same when I tried it with an older (022) version of the IDE. I dropped that line of enquiry because I wanted to be able to use up-to-date libraries etc for the other components and I felt using an older version of the IDE would be likely to cause more problems further down the line...

I thought the recent versions of the IDE use new soft serial by default?

...R

Apparently they're not quite the same beast:

THIS IS THE SOFTWARE SERIAL BRANCH

Note, this branch is intended for use with the NewSoftSerial library. (The core SoftwareSerial library doesn't cut it, because it's missing certain function calls.) (source)

A quick update...

Well, I decided that I could do without my thermostat for a few hours and give this a go with a Mega, however without success. It seems there's a conflict with the tinyGPS library somewhere - all was working well on multiple serial ports until I started calling the gps functions.

I then switched to working with two adjacent Nanos.

Nano #1
Essentially loaded with the BrainSerial example

// Arduino Brain Library
// Serial out example, 	grabs the brain data and sends CSV out over the hardware serial.
// Eric Mika, 2010

#include <Brain.h>

// Set up the brain parser, pass it the hardware serial object you want to listen on.
Brain brain(Serial);


void setup() {
  // Start the hardware serial.
  Serial.begin(9600);
}

void loop() {
  if (brain.update()) {
    //Serial.println(brain.readErrors());
    Serial.print(brain.readCSV());
  }

}

As well as being connected to the brain thing, this board shares a common ground with Nano #2 and has wires between its hardware RX and TX pins going to SoftwareSerial pins on Nano #2.

Nano #2
Receives the brain csv data from the first Nano over a SoftwareSerial connection. Each time this data comes in it appends data from the GPS and is currently displaying some of this on an OLED display:

#include <SoftwareSerial.h>
#include <TinyGPS.h>  // includes the TinyGPS library

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

TinyGPS gps;
SoftwareSerial gpsmodule(5, 6);
SoftwareSerial brainIn(2, 3); // RX, TX

//define GPS variables 

float flat, flon; // +/- latitude/longitude in degrees
unsigned long date, time, age; // what they say on the tin
long lat, lon; // +/- lat/long in 100000ths of a degree


#define OLED_DC 11
#define OLED_CS 12
#define OLED_CLK 10
#define OLED_MOSI 9
#define OLED_RESET 13
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

void setup()  {
  gpsmodule.begin(4800); // serial communication between the gps module and the microcontroller
  Serial.begin(9600); //connection to host computer
  brainIn.begin(9600);    //connection to arduino parsing the brain data

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC);
  // init done


  display.clearDisplay();   // clears the screen and buffer

}

void loop()                     
{

  getGPS(); // get GPS data


  brainIn.listen();

  //give some time for data to come in on the brainIn port
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
  }
  if (brainIn.available()){
    while (brainIn.available())
    {
      Serial.print((char)brainIn.read());  //grab avail data from brain parsing arduino
    }
    Serial.print(",");

    Serial.print(flon, 6); //print lat and lon coordinates
    Serial.print(","); 
    Serial.print(flat, 6); 
    Serial.print(","); 
    Serial.print(date); 
    Serial.print(","); 
    Serial.println(time); 

    display.clearDisplay();   // clears the screen and buffer
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0,0);
    display.println(time);

    display.setCursor(0,10);
    display.print(flon, 6);
    display.print(", ");
    display.println(flat, 6);

    display.setCursor(0,20);
    display.print(flon, 6);
    display.print(", ");
    display.println(flat, 6);

    display.display();


  }

} //end loop


void getGPS(){
  gpsmodule.listen();
  bool newData = false;
  unsigned long chars;
  unsigned short sentences, failed;
  while (newData == false){
    // For one second we parse GPS data and report some key values
    for (unsigned long start = millis(); millis() - start < 1000;)
    {
      while (gpsmodule.available())
      {
        char c = gpsmodule.read();
        // Serial.write(c); // uncomment this line if you want to see the GPS data flowing
        if (gps.encode(c)) // Did a new valid sentence come in?
          newData = true;
      }
    }

    if (newData)
    {
      gps.get_datetime(&date, &time, &age); // time in hhmmsscc and date in ddmmyy formats

      gps.f_get_position(&flat, &flon, &age);  // latitude and longitude values with a decimal point eg 54.117769


    } //end newData
  }

} //end getGPS

I've not added in the logger yet, but it seems reasonably stable printing to the serial monitor, although there's occasional ingress of garbled characters.

I'd like to be able to print the first 3 values in the comma separated list coming through from the brain thing onto the OLED screen. Typically something like : "0, 23, 85" (The first is a measure of quality of connection, max value 200; and the others are measures of 'concentration' and 'meditation', max values 100)

Would I be right in thinking the way to approach this would involve putting the arriving characters into a buffer array as they arrive so I can then split it into variables and later use them to print out?

Would I be right in thinking the way to approach this would involve putting the arriving characters into a buffer array as they arrive so I can then split it into variables and later use them to print out?

Yes. strtok() can do the tokenizing, then.

It seems there's a conflict with the tinyGPS library somewhere - all was working well on multiple serial ports until I started calling the gps functions.

Perhaps you could elaborate. What code, what problems, etc.

PaulS:
Yes. strtok() can do the tokenizing, then.

Thanks, will start with Googling both of those!

PaulS:
Perhaps you could elaborate. What code, what problems, etc.

Only vaguely from memory (it was late last night).
I set up the example I linked to above to work on Serial1, leaving Serial to deal with communication with the PC. That worked fine. When I started incrementally adding in the GPS functionality on Serial2 (previously tested independently), it continued to work (printing out the CSV style list of brain wave values to the serial monitor) with the library and variables added in, but when I added in getGPS() and the getGPS() subroutine (is that the right word for it?) I only got the GPS data and the brain stuff disappeared again.

Possibly even what was happening in the first place (original post)...

I'm curious to find out more about what's going on and why there are problems, but I'll be away for a bit so I'm not likely to get a chance to delve into it for a while.