Hi!
I am using the GPS shield from Adafruit(Adafruit Ultimate GPS Logger Shield - Includes GPS Module : ID 1272 : $29.95 : Adafruit Industries, Unique & fun DIY electronics and kits) and I am planning to use ultrasonic sensors with the Uno along side the shield. I tried using them together but the ultrasonic sensor sometimes spits out a bad reading and when I just use the ultrasonic sensors, and they work consistently. My assumption is that the GPS and ultrasonic sensors are using the same pins which leads to bad readings from the ultrasonic sensor.
So my question is, what Digital pins are being used by the GPS shield so I can plug my ultrasonic sensor around it. My latest attempt was using pins 4 and 5 but it seemed that the readings were still off. Here is my code:
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <SD.h>
#include <NewPing.h>
#define TRIGGER_PIN 5
#define ECHO_PIN 4
#define MAX_DISTANCE 200
SoftwareSerial mySerial(8, 7);
const int chipSelect = 10;
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
Adafruit_GPS GPS(&mySerial);
#define GPSECHO true
boolean usingInterrupt = false;
void useInterrupt(boolean);
int total;
void setup()
{
Serial.begin(115200);
Serial.println("Adafruit GPS library basic test!");
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
GPS.sendCommand(PGCMD_ANTENNA);
useInterrupt(true);
pinMode(10, OUTPUT);
SD.begin(chipSelect);
delay(1000);
mySerial.println(PMTK_Q_RELEASE);
}
SIGNAL(TIMER0_COMPA_vect) {
char c = GPS.read();
#ifdef UDR0
if (GPSECHO)
if (c) UDR0 = c;
#endif
}
void useInterrupt(boolean v) {
if (v) {
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
} else {
TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}
uint32_t timer = millis();
void loop()
{
int final = 0; //sonar sensor part
int myInts[25];
for (int pin = 0; pin < 25; pin++) {
unsigned int uS = sonar.ping();
myInts[pin]=uS;
delay(200);
}
for (int i=0; i<25;i++){
if (myInts[i] > 1){
total = total + myInts[i];
final = final + 1;
}
}
total = total/final;
if (total < 0){
total = total*-1;
}
Serial.println(total);
if (! usingInterrupt) { // gps stuff
char c = GPS.read();
if (GPSECHO)
if (c) Serial.print(c);
}
if (GPS.newNMEAreceived()) {
if (!GPS.parse(GPS.lastNMEA()))
return;
}
if (timer > millis()) timer = millis();
if (millis() - timer > 2000) { //file stuff
timer = millis();
File dataFile = SD.open("datalog.txt", FILE_WRITE);
dataFile.print(total);
dataFile.print(" : ");
dataFile.print(GPS.altitude);
dataFile.print(" ; ");
dataFile.print(GPS.hour, DEC);
dataFile.print(".");
dataFile.print(GPS.minute, DEC);
dataFile.print(".");
dataFile.println(GPS.seconds, DEC);
dataFile.close();
}
total = 0;
}
The issue is that SoftwareSerial uses interrupts, and spends a lot of time receiving GPS data, so characters arrive while NewPing is trying to take a reading, using timer interrupts which are a lower priority than pin change interrupts. So, the time that NewPing things has elapsed for the echo to return is wrong.
You need a Mega or other Arduino with multiple hardware serial ports.
Or, you need to explain why you need ping sensors and GPS. I need a good laugh.
PaulS:
The issue is that SoftwareSerial uses interrupts, and spends a lot of time receiving GPS data, so characters arrive while NewPing is trying to take a reading, using timer interrupts which are a lower priority than pin change interrupts. So, the time that NewPing things has elapsed for the echo to return is wrong.
You need a Mega or other Arduino with multiple hardware serial ports.
Or, you need to explain why you need ping sensors and GPS. I need a good laugh.
Would the Arduino Due work with the shield?
As for the use, we are sending up a weather balloon and we wanted to see how much the speed of sound changed by seeing the difference in ping times. We also needed the altitude which is where the GPS comes in.
As for the use, we are sending up a weather balloon and we wanted to see how much the speed of sound changed by seeing the difference in ping times.
What are you going to ping from the balloon? The signal sent by the sensor has to bounce off of something. In your case, something a known distance away.
We also needed the altitude which is where the GPS comes in.
My GPS does a lousy job of determining altitude. A barometric pressure sensor will be much more accurate, if temperature compensated.
We have the sensors in a closed styrofoam box where they will ping off the opposite side from a fixed position. From the testing of the GPS shield for me, the attitude has stayed between a range of about 15 feet which is more than acceptable for this particular use. Though if GPS loses a fix, then the barometer idea would be much better. Thanks for the help!
You could disable the GPS input while the ping is going. After the ping time is determined, do the same GPS.begin you did in setup, and handle the GPS data for a second or two, which gets the latest fix data. Then do a GPS.end(). Don't use_interrupt(true)! This will also mess up the ping timings.
You are then free to print or save some things after the two seconds of GPS processing. Printing before this will cause interrupts, which interferes with SoftwareSerial. Finally, do a Serial.flush() to make sure the printing is completed before exiting loop() and starting the ping again:
void loop()
{
// Ping 25 times and average
...
// Handle GPS data for 2 seconds
GPS.begin( ... )
uint32_t start_time = millis();
do {
while (GPS.available()) {
GPS.read();
if (GPS.newNMEAreceived())
GPS.parse( GPS.lastNMEA() );
}
} while (millis() - start_time < 2000);
GPS.end()
// Write to SD
...
// Print some things
...
Serial.flush();
This is more of a round-robin approach, instead of trying to do multiple things at the same time. Printing is one of those things.
Maybe someone else knows for sure, but I think you should switch to SDFat instead of the older SD. And make sure you're appending to the SD file, not replacing it every two seconds. Note that the GPS processing introduces a 2-second pace to loop(), so you don't need to wait before doing the SD write.
// Handle GPS data for 2 seconds
GPS.begin( ... )
uint32_t start_time = millis();
do {
while (GPS.available()) {
GPS.read();
if (GPS.newNMEAreceived())
GPS.parse( GPS.lastNMEA() );
}
} while (millis() - start_time < 2000);
GPS.end()
// Write to SD
...
I get this error message when trying to run the code:
sketch_nov03b.ino:94:16: error: 'class Adafruit_GPS' has no member named 'available'
sketch_nov03b.ino:101:7: error: 'class Adafruit_GPS' has no member named 'end'
To answer your original question, the shield uses pins 7, 8, 10 and 11-13. Those last three are the SPI bus so they can be shared. You could have looked this up yourself by reading the Adafruit tutorial. Their documentation is one of the reasons to buy stuff from them. And their support too.
Paul is right about SoftwareSerial. Buying a different board is one way to get around it but you don't absolutely have to. There are other software serial libraries that don't block the way SoftwareSerial does. You could use the UART on the Uno, with some care. Or you could do what /dev is suggesting and turn off SoftwareSerial while reading your sensors.
I think it is just pinging inside the insulated box. A fixed distance, with ping times that change with altitude.
gSoftSerial could be used instead, should probably be used, even. NeoGPS would require more changes, so I was reluctant to suggest it at this point. If RAM becomes an issue, it would help. The round-robin approach means speed probably won't be an issue. Besides, we've only said its name twice.