GPS duplicating lines (emergency strobe LED brake)

Hi,

I installed a third led light brake in my motorbike. It works along with the other brake lights.
The Arduino Uno is programmed for:
When I'm above 30mph, if I press the brake, the 3rd LED flashes 20 times 50 milliseconds(Relay open circuit), it catches the attention of other drivers. After that, delay 5 seconds to make sure I'm below 30mph and doesn't trigger the strobe again.

There's a small problem. The monitor is writing duplicated lines. So the loop is always running twice even if speed is below 30mph.

There's no way I can make the monitor writing single lines instead of duplicates. (see monitor capture attached image)
Any ideas how to do that? Cheers

(PS: I'll need to keep month and hour for another function: Daytime running leds connected with a MOSFET.)

#define Relay 8
#include <SoftwareSerial.h>
#include <TinyGPS.h>

SoftwareSerial mySerial(10, 11);
TinyGPS gps;

void gpsdump(TinyGPS &gps);
void printFloat(double f, int digits = 2);
int sensorPin = A0;    // select the input pin for the brake 5v signal
int sensorValue = 0;
int strobeMs = 50; // 50ms is 20times per second
int PauseStrobe = 5000; //1000ms is 1second
int NumBlink = 30; //This is the times it blinks

void setup()  
{
  //Setup all the Arduino Pins
   pinMode(Relay, OUTPUT);
   
   //Turn OFF any power to the Relay channels
   digitalWrite(Relay,HIGH);
  // Oploen serial communications and wait for port to open:
  Serial.begin(9600);
  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);
 
}

void loop() // run over and over
{
  
  bool newdata = false;
  unsigned long start = millis();
  // Every 5 seconds we print an update
  while (millis() - start < 5000) 
  {
    if (mySerial.available()) 
    
    {
      char c = mySerial.read();
      //Serial.print(c);  // uncomment to see raw GPS data
      if (gps.encode(c)) 
      {
        newdata = true;
        break;  // uncomment to print new data immediately!
      }
    }
  }
  
  if (newdata) 
  {
    gpsdump(gps);  
  }
  
}

void gpsdump(TinyGPS &gps)
{
  
  unsigned long age, date, time, chars;
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned short sentences, failed;



  gps.get_datetime(&date, &time, &age);
   Serial.print(" Time(hhmm): ");
    Serial.print(time); 
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  Serial.print(" Date: "); Serial.print(static_cast<int>(month));
    Serial.print(" (kmph): ");
    printFloat(gps.f_speed_kmph()); Serial.println();

  
}

void printFloat(double number, int digits)
{
  // Handle negative numbers
  if (number < 0.0) 
  {
     Serial.print('-');
     number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
  
  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  Serial.print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
    Serial.print("."); 

  // Extract digits from the remainder one at a time
  while (digits-- > 0) 
  {
    remainder *= 10.0;
    int toPrint = int(remainder);
    Serial.print(toPrint);
    remainder -= toPrint;
 
  if ((gps.f_speed_kmph()>=0.60 ) && (sensorValue = analogRead(sensorPin)))
 {

for (int j=1; j<=NumBlink; j=j+1){
  digitalWrite(Relay, HIGH);
  delay(strobeMs);
  digitalWrite(Relay, LOW);
  delay(strobeMs);
}
  digitalWrite (Relay, HIGH);
  delay(PauseStrobe);
  
  }
}
}

Moderator edit: code tags. Why is this so difficult?

After newdata has been tested to be TRUE and you've done the action required when you have new data, why not set it back to FALSE? Then you know there's no new action to be done.

Image embedded for our convenience:

Technique described here.

We can't read that very easily without downloading it... just copy and paste the text into a code block (like you're supposed to do for code).

There are several problems with your sketch:

  • It uses delay, which causes it to lose all GPS data during those times. You should use a "state machine" to blink. See the "multiple things" and "blink without delay" tutorials on the Useful Links page.

  • It has a lot of unused code from the original example. This will make it hard to debug.

  • You're blinking the brake inside the printFloat routine. That's goofy. :slight_smile:

  • The code is not indented properly. This makes it hard to read, which makes it hard to debug. Just press control-T inside the IDE editor, and it will auto-format your sketch.

  • You are comparing the speed value without checking to see if the GPS device really gave you a speed value. It may not be valid.

  • You are using SoftwareSerial, which is very inefficient. It disables interrupts for long periods of time, which can interfere with other parts of your sketch, or with other libraries. Read this for other choices (and connections).

  • You are reading an analog value for the brake signal. This will give you an integer between 0 and 1024. It is rarely a 0. Instead, treat this as a digital signal. The Arduino will convert any voltage below 3.5V into a LOW, and anything above into a HIGH.

  • You are doing an assignment in an if statement, which usually is an error:

    if ((gps.f_speed_kmph()>=0.60 ) && (sensorValue = analogRead(sensorPin)))

And are you sure you wanted 0.6kph (0.37mph) for a threshold?

Here is a version of your sketch that uses my NeoGPS and NeoSWSerial libraries, and incorporates all the above suggestions. For testing, it flashes the on-board LED instead of the relay on pin 8, once every 8 seconds (ignoring the speed).

#include <NeoSWSerial.h>
#include <NMEAGPS.h>

NeoSWSerial gpsPort(10, 11);
NMEAGPS     gps;
gps_fix     fix; //  this holds all the GPS pieces

const float FLASH_SPEED     = 30.0;  // MPH
const int   STROBE_INTERVAL = 50;    // 50ms is 20times per second (100ms might be better)
const int   PAUSE_STROBE    = 5000;  // 5 seconds
const int   NUM_BLINKS      = 40;    // Pick an even number = 2*blinks
const int   BRAKE_PIN       = A0;    // select the input pin for the brake 5v signal
const int   RUNNING_LEDS    = 5;
//const int   RELAY_PIN       = 8;
const int   RELAY_PIN       = 13;  // flash on board LED for testing

bool blinking = false;    // a true/false value
int  blinks   = 0;
unsigned long changeTime; // the last time we toggled the relay


void setup()  
{
  //Setup all the Arduino Pins
  pinMode( RELAY_PIN, OUTPUT );
  pinMode( BRAKE_PIN, INPUT  );
   
  //Turn OFF any power to the RELAY_PIN channels
  digitalWrite( RELAY_PIN,HIGH );

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  
  // set the data rate for the GPS device
  gpsPort.begin(9600);
}


void loop()
{
  //  Check the brake signal
  bool braking = digitalRead( BRAKE_PIN );
  braking = (fix.dateTime.seconds & 0x03) < 3; // <--  for testing

  //  Check for new GPS data
  if (gps.available( gpsPort )) {

    fix = gps.read(); // Get the new data

    //  Check the speed
    if ( //fix.valid.speed && (fix.speed_mph() >= FLASH_SPEED) &&  // test without this
        braking         && not blinking) {

      //  Start the blink "state machine", but only
      //    if it has been more than 5 seconds since the last blinking finished.
      if (millis() - changeTime > PAUSE_STROBE) {
        changeTime = millis();
        blinking   = true;
        blinks     = 0;
        digitalWrite( RELAY_PIN, LOW );
      }
    }
    
    //  Check for day/night conditions
    if (fix.valid.time) {
      bool daytime = (7 <= fix.dateTime.hours) && (fix.dateTime.hours <= 19);
        // true if between 7AM and 7PM?
      digitalWrite( RUNNING_LEDS, daytime ); // true == HIGH
    }
    
    // Do something with fix.dateTime.month?
    if (fix.valid.date) {
      Serial.print( F("Month ") );
      Serial.println( fix.dateTime.month );
    }
  }

  //  Run the blink "state machine"
  if (blinking && (millis() - changeTime >= STROBE_INTERVAL)) {
  
    digitalWrite( RELAY_PIN, !digitalRead(RELAY_PIN) ); // toggle!
    changeTime = millis(); // remember when we toggled it
    blinks++;              // increment the number of times we toggled it

    if (blinks > NUM_BLINKS) {
      // All done!
      blinking = false;
    }
  }
}

For the real system, change the RELAY_PIN number, remove the fake braking statement, and uncomment the speed test. Notice that constants usually have upper-case names.

This is a much smaller program, only 92 lines vs. the original (non-working) 127. I hope it is easier to read. FWIW, many people have trouble modifying the examples from other libraries. The NeoGPS examples are structured properly, and can usually be modified without breaking them.

The original sketch uses 8998 bytes of program space and 520 bytes of RAM.
The NeoGPS version uses 6866 bytes of program space and 388 bytes of RAM, a significant saving. NeoGPS is smaller, faster, more reliable and more accurate than all other libraries.

If you want to try it, NeoGPS and NeoSWSerial are available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

Cheers,
/dev

Thanks MorganS. I will use True False when I learn how to use it. Soon. Karma+

Big Thanks -dev. Karma++ That is far more than I needed. Thanks for the much simpler Sketch&library than the one I was working before. I can even understand much of it. Thanks also for including DRL function. I've been working a few minutes with your sketch and I can print date time and speed correctly, also there's apparently more accuracy in the speed values. I test the device at home using walking pace that was the reason the threshold in mph was low.
I'll try to finish my sketch in the next days after work.

Thanks also for the posting tips, I'm very new.

About my brake signal, using a cigarette socket usb adapter which outputs 3v or 5.2v.
I'm using analogue input because is 3v(on) or near 0v(off). I'll use a conditional to ignore near 0 values.
The other option is 5.20v(on) but I read that this 0.2v above 5v would/might fry the arduino. I could install a voltage divider to get around 4.7v and use digital. If is still recommended to use digital input rather than analogue.

About my brake signal, using a cigarette socket usb adapter which outputs 3v or 5.2v.
I'm using analogue input because is 3v(on) or near 0v(off). I'll use a conditional to ignore near 0 values.

If you haven't been warned yet, you should know that automotive (motorcyclive?) power is the worst power for a sensitive device like an Arduino. There can be high-voltage transients when you start or when high-current devices are turned on or off (e.g., headlights).

Given that warning, I can say that using a cigarette socket USB power supply as an input is better than a direct connection. Depending on how good (or bad) your PS is, it may or may not filter these spikes from the Arduino input pin. I would always suggest a series resistor into the input pin, and a capacitor in parallel with a 5.2V Zener diode, connecting to the other side of the resistor and to ground. And maybe a series resistor into that.
:slight_smile: This is a good topic to search for in the Electronics sub-forum. It's just nasty.

How are you powering the Arduino? The VCC input to the Arduino has the same issue.

I plan to power the Arduino with another USB-cigarette 12-24VDC-5.2VDC. My multimeter picks max. values, so I'll have to run several tests to see if v goes above 5.2v before connecting the Arduino.

:wink: Thanks again for the Sketch, now is almost finished, and walking-pace tests seem to be successful. Just adding the threshold DRL 100% day, 50% when dark, month by month; here in the North winter days are short and there's no reason to blind drivers at dark hours.

The 5.2V is probably "no load". Once something is connected, it should drop. You could add a diode in series to drop the voltage by 0.4 to 0.7V, providing ~4.7 to the Arduino. Then measure the voltage, and I'll bet it's under 5V. If it is, you can remove the diode.

I would also recommend a 5.2V zener (or the next step up) across VCC and GND. A 30V Zener across the inputs to the PS would help protect the PS, too.

LOL, just read this today: the latest horror story, with additional recommendations. Nasty, nasty...