GPS and servo problem

Hello everyone.
I am a beginner in Arduino stuff, so I am writing here hoping for help. I am creating a project for an on-board computer with a geolocation function and a drive lock. I am using a servo, an HC-05 bluetooth module and a Neo-6M GPS. I wrote separately the code for the servo and for the GPS, everything worked flawlessly, however after combining both codes the servo starts to go crazy. It makes arbitrary movements without any signal. I searched the forum for answers on how to fix this and found a solution using the ServoTimer2.h library. Unfortunately, the library was not able to help me, the servo still moves on its own, although it is better than with Servo.h, but it still does not respond to the values sent from the phone, namely to "1" or "0", it just rotates on its own every so often, so I think the problem must be somewhere in my code.
The action was supposed to be as follows. When the app was turned on with the device paired with the phone, the phone would send a value of "1", which would automatically move the servo, then when the app was turned off, the phone would send a "0" and the servo would return to its initial position(this I did and it worked until the GPS module was connected). As an additional option, I wanted the device to send a location to the phone and show where the device was when I pressed a button in the app. Is there any way to fix this and have it work without problems? I understand that the project may seem pointless because bluetooth has a limited connection distance, so showing the location from such a distance is pointless, but it's simply a provisional device to show how it works.
My equipment is:
-Arduino Uno
-Bluetooth HC-05
-Servo SG90
-GPS Neo-6M

#include <LiquidCrystal_I2C.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <ServoTimer2.h>

ServoTimer2 myservo;
LiquidCrystal_I2C lcd(0x27, 16, 2); 
SoftwareSerial bluetooth(2, 3); 
TinyGPSPlus gps; 
static const int RXPin = 4, TXPin = 5;
static const uint32_t GPSBaud = 9600;
SoftwareSerial ss(RXPin, TXPin);


float speedKMH = 0;
int currentPos = 0; 

void setup()
{
  Serial.begin(9600); 
  bluetooth.begin(9600);
  ss.begin(GPSBaud);
  myservo.attach(7); 
  myservo.write(currentPos); 

  
  lcd.begin();
  lcd.backlight();


  lcd.setCursor(0, 0);
  lcd.print("Predkosciomierz");
}

void loop()
{
  if (bluetooth.available()) { 
    char incomingByte = bluetooth.read(); 
    if (incomingByte == '1') { 
      if (currentPos != 90) { 
        myservo.write(90);
        currentPos = 90; 
      }
    }
    if (incomingByte == '0') { 
      if (currentPos != 0) { 
        myservo.write(0);
        currentPos = 0; 
      }
    }
    if (incomingByte == '2') {
      while (ss.available() > 0) {
        if (gps.encode(ss.read())) {
         
          double latitude = gps.location.lat();
          double longitude = gps.location.lng();
        
          bluetooth.print(latitude, 6);
          bluetooth.print(",");
          bluetooth.println(longitude, 6);
          Serial.print(latitude, 6);
          Serial.print(",");
          Serial.println(longitude, 6);
        }


      }
    }
  }

  float speedMS = gps.speed.mps();
  speedKMH = speedMS * 3.6;
 
  lcd.setCursor(0, 1);
  lcd.print("Speed:");
  lcd.print(speedKMH, 1);
  lcd.print(" km/h ");
}

I don't know much about servos or the hardware PWM in the Arduino, however my guess is that when data is being received, the serial interrupt handler and the software serial interrupt handler delay the running of the servo interrupt handler which causes a variation in the generated pulse width and therefore the servo moves.

As for a solution, you could generate the signal using a dedicated servo driver IC such as the PCA9685.

1 Like

Your using two software serial interfaces, but not switching between them, check out the software serial reference;

But to be realistic, you might never get this to work, lots of things upset software serial, and using more than one instance, is not that easy.

You really should be using an Arduino with two spare hardware serial ports, a Mega for instance.

1 Like

Unfortunately, I am not able to change to a board other than Arduino Uno, so I am looking for a solution on it, if there is one.

With a single servo, you can use a PWM output instead of the Servo library.

Get rid of one of the software serial ports, and use Serial instead (the GPS only really need Rx, and printing to Serial only needs Tx, so you can use the same port for both).

You have a rather major flaw in the sketch. You are only checking the GPS when you receive a '2' from the bluetooth, and then you are only reading characters from the GPS until the receive buffer is empty. There is no guarantee that you will receive a complete GPS message this way, and in fact it is very unlikely that you will ever successfully get any useful data from the GPS.

1 Like

Hi and welcome to the forum

I don't use TimyGPS++ or any GPS library. I just read the sentences and extract the fields I want. Pretty easy. If you use TinyGPS++ I think you need to gain an understanding of how it reacts when there is serial buffer overflow. (David is right -- TinyGPS++ will be losing sentences and it won't tell you that.)

When software serial receives a byte it slows down other things you're trying to do. Try to work out a strategy of dealing with each board sequentially. You can turn on the GPS soft serial immediately before you read it and turn it off when you have the data. It works and you won't get buffer overflow. You don't need to read all the GPS sentences, just the GPRMC one. That contains the lat & long.

I've found there's much more scope to do exactly what you want and understand things properly by simply reading the sentences and not using TinyGPS++.

1 Like
#include <LiquidCrystal_I2C.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <PWMServo.h>

PWMServo myservo;
LiquidCrystal_I2C lcd(0x27, 16, 2); 
SoftwareSerial bluetooth(2, 3); 
TinyGPSPlus gps; 
SoftwareSerial ss(4, 5);


float speedKMH = 0;
int currentPos = 0; 
float lastLat = 0;
float lastLng = 0;

void setup()
{
  Serial.begin(9600); 
  bluetooth.begin(9600); 
  ss.begin(9600);
  myservo.attach(9); 
  myservo.write(currentPos); 

 
  lcd.begin();
  lcd.backlight();

  
  lcd.setCursor(0, 0);
  lcd.print("Predkosciomierz");
}

void loop()
{
  while (ss.available() > 0) {
    if (gps.encode(ss.read())) {
      
      lastLat = gps.location.lat();
      lastLng = gps.location.lng();
      
      float speedMS = gps.speed.mps();
      speedKMH = speedMS * 3.6;
    }
  }
  bluetooth.listen();
  if (bluetooth.available()) { 
    char incomingByte = bluetooth.read(); 
    if (incomingByte == '1') { 
      if (currentPos != 90) { 
        myservo.write(90);
        currentPos = 90; 
      }
    }
    if (incomingByte == '0') { 
      if (currentPos != 0) { 
        myservo.write(0);
        currentPos = 0; 
      }
    }
    if (incomingByte == '2') {
      bluetooth.print(lastLat, 6);
      bluetooth.print(",");
      bluetooth.println(lastLng, 6);
      Serial.print(lastLat, 6);
      Serial.print(",");
      Serial.println(lastLng, 6);
     
      float speedMS = gps.speed.mps();
      speedKMH = speedMS * 3.6;
    }
  }





  lcd.setCursor(0, 1);
  lcd.print("Speed:");
  lcd.print(speedKMH, 1);
  lcd.print(" km/h ");
}

Before reading your responses, I did something like this. I changed the library to PWMServo and added "bluetooth.listen()" and the servo works, but the GPS only returns a string of 000. After removing "bluetooth.listen()" then nothing works, that is, it still collides bluetooth with GPS. Is it possible to do some kind of interrupt that would make the GPS work in the background, but when the bluetooth module is paired with the phone, it would interrupt the GPS, save the last location and send that location to the phone after sending the value "2"?

I agree. @xk2x for the GPS to work, you have to listen to it more or less constantly. Same for receiving randomly timed messages from some other source.

You chose the wrong Arduino for this project. Choose another, with at least two hardware serial ports.

1 Like

please explain why you must use an Arduino UNO.

Because my budget is not enough for me to afford to buy an Arduino Mega, that's why I'm looking for solve to this problem with Arduino Uno.

Consider skipping the next cup of cappuccino and buy an ESP32, which has three hardware serial ports.

1 Like

If you absolutely must use the UNO, then try my suggestion of using the Serial Rx line for the GPS. That would leave you with one SoftwareSerial port for the bluetooth, and one hardware Serial port, with Tx being used for the serial monitor, and Rx for the GPS.

You have already moved the GPS code outside the bluetooth if statements, so that should allow for correct GPS reception.

Note that the GPS will need to be disconnected from the Serial Rx line while uploading code to the UNO.

1 Like

Software serial is already running under 'interrupt' and in the 'background'

However for software serial to work it needs to get the timing right. Unfortuantly anything else going on under 'interrupt' and in the 'background' messes up the timing. Just printing a couple of characters with Serial.print() is enough to upset the timing and cause software serial to miss characters. Miss just one character and the sentance\fix is lost.

Used on its own, reading a GPS, software serial will work, but use any other functions at the same time that also uses interrupts can upset it, you just have to accept that.

You could try NeoSWSerial, that is a bit more robust.

1 Like

To be honest, I did not believe that such a solution as hardware serial will make a difference, but great was my surprise when it turned out that it works, and so unnecessarily tried to figure something out. Thank you all for your help :slight_smile:

Hello, I am currently experiencing the same problem as in this post. I also connected UNO and GPS through the softwareserial function. Do I need to change the code if I use Mega? Do I just need to change the pins of the softwareserial function or use other functions to connect GPS and mega? thank you.

If you use a Mega 2560 then, don't use software serial ! the mega has multiple hardware serial ports. And of course pins must be changed and initialized .

Sorry that I'm a novice at the moment. So what I need to change in code for mega 2560 is to simply “#define GpsModule Serial1;” at the beginning, and then set the baud rate in void setup like "GpsModule.begin(9600);", without using SoftwareSerial, is that correct? thank you a lot!

Sounds like a good starting point . :dotted_line_face:

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.