I have a bike speedometer based on a GPS module using a Nano Every. It shows me speed, latitude, longitude, a compass, and can datalog my position as I ride and later download that onto Google maps, all of that works fine.
I was calculating the distances travel based on the GPS speed and time. It was always off by about 15%. I've taken a hall effect sensor used on ebikes and have that going into interrupt 0, pin 2. This counts the revolutions of the wheel, some simple math will give me the milage, right now I'm just looking at revolutions.
I have a button to start/stop the datalogging. I only want the revolutions count to increment while datalogging. When I stop datalogging I want the count to remain and not increment. That why I can see how far I've gone. I have it so when I restart datalogging it zero's the count. Again most of that works with one exception.
I put the attachInterrupt function in an if statement, shown below. When I first start the system the interrupt does not increment, When I start datalogging it does start to count. When I stop datalogging it still increments. If I start datalogging again it does zero the count.
if (RunLogger == 1) {
// Wheel sensor interrupt
attachInterrupt(digitalPinToInterrupt(2), distCount, RISING);
Serial.println("Logger = 1");
}
else {
Serial.println("Logger = 0");
}
This is my first project using interrupts and I'm sure it has something to do with my lack of experience or what I can find online with using interrupts.
Why does it keep incrementing after I stop datalogging? Or a better questions is what is the correct way to do this?
Thanks for all help and suggestions, the complete code is below.
John
/*
Changed to Nano Every
with Adafruit 3.5 TFT display
Rev 2 Added touch, made 2 pages for displaing info
No logging function
Rev 6 added compass by G6EJD - David
Rev 7 Changed to Elapsed time method and bike wheel pickup
*/
// Include required libraries
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <SD.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_HX8357.h> // Hardware-specific library
#include "TouchScreen.h"
// GPS Connections
static const int RXPin = 6, TXPin = 7;
// GPS Baud rate (change if required)
static const uint32_t GPSBaud = 9600;
// SD Card Select pin
const int chipSelect = 5;
// Write LED
const int recLED = 8;
// String to hold GPS data
String gpstext;
// GPS write delay counter variables
// Change gpsttlcount as required
int gpscount = 0;
int gpsttlcount = 140;
int fileMade = 0; // check to see if a new file was made on SD card
// TinyGPS++ object
TinyGPSPlus gps;
// SoftwareSerial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);
// display
#define TFT_CS 10
#define TFT_DC 9
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC);
// These are the four touchscreen analog pins
#define YP A2 // must be an analog pin, use "An" notation!
#define XM A3 // must be an analog pin, use "An" notation!
#define YM 3 // can be a digital pin
#define XP 4 // can be a digital pin
// This is calibration data for the raw touch data to the screen coordinates
#define TS_MINX 110
#define TS_MINY 80
#define TS_MAXX 900
#define TS_MAXY 940
#define MINPRESSURE 10
#define MAXPRESSURE 1000
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
// Color definitions
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
bool newData = false;
float log_Dist = 0.0;
unsigned long int scan_time = millis();
int currentPage = 1;
int SD_Status = 0;
int GPS_Status = 0;
float Heading = 0.0;
String RideTime = "";
int RideHr = 0;
int RideMin = 0;
unsigned long RideSec = 0;
unsigned long StartTime = 0;
int StartHr = 0;
int StartMin = 0;
int StartSec = 0;
int readStartTime = 0;
char RideTm[80];
int TestTm = 0;
// Compass
float Bearing;
const int centreX = 160; // Location of the compass display on screen
const int centreY = 280;
const int diameter = 70; // Size of the compass
int dx = centreX, dy = centreY;
int last_dx = centreX, last_dy = centreY - diameter * 0.85;
// the logging file
File logfile;
char logFileName[20] = " "; // current log file name
int errorNumb = 0; // system error number to display message
int RunLogger = 0; // run logging file
int LogPBOld = 0;
int LogPBNew = 0;
File dataFile;
long int lastLogTime = 0; // last time log sample was read
int logDelay = 10000; // time delay between log samples
// for wheel sensor
volatile long int Wheel_Count = 0;
volatile float revolutions = 0;
long DiststartTime = 0;
//long DistelapsedTime;
float Wheel_Dia = 85.0;
float Mileage = 0;
void setup()
{
// Set all chip selects high to avoid bus contention during initialisation of each peripheral
// digitalWrite(21, HIGH); // Touch controller chip select (if used)
digitalWrite(10, HIGH); // TFT screen chip select
digitalWrite( 5, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS)
// Set LED pin as output
pinMode(recLED, OUTPUT);
// Wheel sensor pin
pinMode(2, INPUT_PULLUP);
// Start Serial Monitor for debugging
Serial.begin(115200);
// Start SoftwareSerial
ss.begin(GPSBaud);
// start display driver
tft.begin(); // Initialize screen
//tft.fillScreen(BLACK); //clears screen, sets to Black
tft.setRotation(0); // rotates screen 90' for landscape mode
// load Speedometer page
tft.fillScreen(BLACK); //clears screen, sets to Black
SpeedometerPage();
// Initialize SD card
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
//don't do anything more:
tft.fillCircle(30, 30, 10, RED);
while (1);
}
Serial.println("card initialized.");
// Blink LED so we know we are ready
//digitalWrite(recLED, HIGH);
tft.fillCircle(30, 30, 10, BLACK);
delay(500);
//digitalWrite(recLED, LOW);
tft.fillCircle(30, 30, 10, GREEN);
delay(500);
//digitalWrite(recLED, HIGH);
tft.fillCircle(30, 30, 10, BLACK);
delay(500);
//digitalWrite(recLED, LOW);
tft.fillCircle(30, 30, 10, GREEN);
delay(500);
//digitalWrite(recLED, HIGH);
tft.fillCircle(30, 30, 10, BLACK);
delay(500);
//digitalWrite(recLED, LOW);
tft.fillCircle(30, 30, 10, GREEN);
scan_time = millis(); // reset scan time
} // end setup
void loop()
{
// Turn off LED
digitalWrite(recLED, LOW);
// Retrieve a point on touch screen
TSPoint p = ts.getPoint();
// See if GPS data available
while (ss.available() > 0)
if (gps.encode(ss.read()))
// If we press the Speedometer Button
if ((p.x >= 130) && (p.x <= 290) && (p.y >= 810) && (p.y <= 890)) {
currentPage = 1; // Indicates that we are the first example
tft.fillScreen(BLACK); //clears screen, sets to Black
Serial.println("Speed pressed");
SpeedometerPage(); // It is called only once, because in the next iteration of the loop, this above if statement will be false so this funtion won't be called. This function will draw the graphics of the first example.
} // end if for Speedometer button pressed
// If we press the GPS Info Button
if ((p.x >= 420) && (p.x <= 600) && (p.y >= 810) && (p.y <= 890)) {
currentPage = 2;
tft.fillScreen(BLACK); //clears screen, sets to Black
Serial.println("GPS Pressed");
GPS_Info_Page();
} // end if for GPS Info page
// Start/Stop logging file with Log PB
if ((p.x >= 725) && (p.x <= 890) && (p.y >= 810) && (p.y <= 890)) {
RunLogger = !RunLogger; // toggle RunLogger
delay(500);
} // end RunLogger = !RunLogger
// Data logger control PB
if (RunLogger == 0 && LogPBOld == 1) {
tft.fillRoundRect(240, 410, 70, 50, 10, RED);
tft.setCursor(257, 427);
tft.setTextColor(WHITE, RED);
tft.setTextSize(2);
tft.print("LOG");
LogPBOld = 0;
fileMade = 0;
} // end RunLogger == 0
else if (RunLogger == 1 && LogPBOld == 0) {
tft.fillRoundRect(240, 410, 70, 50, 10, GREEN);
tft.setCursor(257, 427);
tft.setTextColor(BLACK, GREEN);
tft.setTextSize(2);
tft.print("LOG");
LogPBOld = 1;
StartTime = millis();
log_Dist = 0; //clear log distance
RideHr = 0; // clear log hours
RideMin = 0; // clear log minutes
RideSec = 0; // clear log seconds
revolutions = 0; // clear wheel sensor count
} // end else if
// call logger function
if (RunLogger == 1) {
// logger();
} // end if RunLogger == 1
else {
logfile.close();
fileMade = 0;
} // end else
// Update variables
// display GPS status
//if(GPS_Status == 1){
if (gps.location.isValid()) {
tft.fillCircle(290, 30, 10, GREEN);
}
//if(GPS_Status == 0) {
if (!gps.location.isValid()) {
tft.fillCircle(290, 30, 10, RED);
}
if (RunLogger == 1) {
// Wheel sensor interrupt
attachInterrupt(digitalPinToInterrupt(2), distCount, RISING);
Serial.println("Logger = 1");
}
else {
Serial.println("Logger = 0");
}
// calculate mileage
Mileage = revolutions * Wheel_Dia;
Serial.println(Mileage);
newData = true;
if (((millis() - scan_time) > 1000)) { // Update every second
//if (gps.location.isValid())
if (newData == true)
{
newData = false;
// ride start time
if (readStartTime == 0) {
StartHr = gps.time.hour();
StartMin = gps.time.minute();
StartSec = gps.time.second();
readStartTime = 1;
} // end if(readStartTime == 0)
} // if(newdata == true)
else
{
Serial.println(F("INVALID"));
} // end else
// Update screen variables
if (currentPage == 1) {
tft.setCursor(50, 120);
tft.setTextColor(WHITE, BLACK);
tft.setTextSize(6);
tft.print(gps.speed.mph(), 1);
Bearing = gps.course.deg();
Display_Compass(Bearing); // show compass
} // end if current page =1
if (currentPage == 2) {
tft.setCursor(50, 110);
tft.setTextColor(WHITE, BLACK);
tft.setTextSize(4);
tft.print(gps.location.lng(), 4);
// Display Latitude
tft.setCursor(50, 190);
tft.setTextColor(WHITE, BLACK);
tft.setTextSize(4);
tft.print(gps.location.lat(), 4);
// Display log_Dist
tft.setCursor(50, 270);
tft.setTextColor(WHITE, BLACK);
tft.setTextSize(4);
tft.print(log_Dist, 1);
// Display Ride Time
tft.setCursor(50, 350);
tft.setTextColor(WHITE, BLACK);
tft.setTextSize(4);
sprintf(RideTm, "%02d:%02d:%02d", RideHr, RideMin, RideSec);
tft.print(RideTm);
} // end if current page = 2
if (millis() > 5000 && gps.charsProcessed() > 10) {
GPS_Status = 1;
}
if (millis() > 5000 && gps.charsProcessed() < 10)
{
Serial.println(F("No GPS detected: check wiring."));
GPS_Status = 0;
//while(true);
}
// calculate ride time
if (RunLogger == 1) {
RideSec = (millis() - StartTime) / 1000;
if (RideSec >= 60) {
RideMin = RideMin + 1;
RideSec = 0;
StartTime = millis();
}
if (RideHr >= 24) {
RideHr = 0;
}
if (RideMin >= 60) {
RideMin = 0;
RideHr = (RideHr + 1);
}
// calculate log_Dist
if (gps.speed.mph() > 0.5) {
log_Dist = log_Dist + (gps.speed.mph() / 3600);
} // end if(GPS.speed.mph() > 0.5)
} // end if(RunLogger == 1)
/* if(RunLogger == 0){
log_Dist = 0; //clear log distance
RideHr = 0; // clear log hours
RideMin = 0; // clear log minutes
RideSec = 0; // clear log seconds
} // end if(RunLogger == 0)
*/
scan_time = millis(); // reset scan time
} // end (((millis() - scan_time) > 1000)
// Run GPS Logger
if (RunLogger == 1) {
// Turn off LED
digitalWrite(recLED, LOW);
// ************* Dronebot GPS logging code **************
// See if data available
while (ss.available() > 0)
if (gps.encode(ss.read()))
// See if we have a complete GPS data string
if (displayInfo() != "0")
{
// Check GPS Count
// Serial.println(gpscount);
if (gpscount == gpsttlcount) {
// LED On to indicate data to write to SD card
digitalWrite(recLED, HIGH);
// create a new data logging file
if (RunLogger == 1 && fileMade == 0) {
Serial.println("Create file");
char filename[] = "File00.CSV";
for (uint8_t i = 0; i < 100; i++) {
filename[4] = i / 10 + '0';
filename[5] = i % 10 + '0';
if (! SD.exists(filename)) {
// strcpy(logFileName, filename);
// only open a new file if it doesn't exist
dataFile = SD.open(filename, FILE_WRITE);
Serial.println(filename);
strcpy(logFileName, filename);
fileMade = 1;
break; // leave the loop!
} // end if (! SD.exist)
} // end for (uint8_t i = 0; i < 100; i++)
} // end if RunLogger == 1 && fileMade == 0
//Open the file on card for writing
dataFile = SD.open(logFileName, FILE_WRITE);
Serial.println(logFileName);
//File dataFile = SD.open("gpslog.csv", FILE_WRITE);
// Get GPS string
gpstext = displayInfo();
if (dataFile) {
// If the file is available, write to it and close the file
dataFile.println(gpstext);
dataFile.close();
// Serial print GPS string for debugging
Serial.println(gpstext);
}
// If the file isn't open print an error message for debugging
else {
Serial.println("error opening datalog.txt");
}
}
// Increment GPS Count
gpscount = gpscount + 1;
if (gpscount > gpsttlcount) {
gpscount = 0;
}
} // end if (displayInfo() != "0")
} // end run logger
} // end loop()
void SpeedometerPage() {
// Prints the title on the screen
tft.setCursor(65, 20);
tft.setTextColor(WHITE);
tft.setTextSize(3);
tft.print("Speedometer");
tft.drawFastHLine(10, 60, 320, RED);
// Display SS Initialized status
tft.fillCircle(30, 30, 10, GREEN);
// Display Speed
tft.setCursor(50, 90);
tft.setTextColor(WHITE);
tft.setTextSize(2);
tft.print("Speed");
// Draw buttons
// fillRoundRect(X, Y, width, height, radius, color);
// Speedometer page select PB
tft.fillRoundRect(10, 410, 70, 50, 10, BLUE);
tft.setCursor(16, 427);
tft.setTextColor(WHITE, BLUE);
tft.setTextSize(2);
tft.print("Speed");
// GPS Info Page select PB
tft.fillRoundRect(126, 410, 70, 50, 10, MAGENTA);
tft.setCursor(143, 427);
tft.setTextColor(BLACK, MAGENTA);
tft.setTextSize(2);
tft.print("GPS");
// Data logger control PB
if (RunLogger == 0) {
tft.fillRoundRect(240, 410, 70, 50, 10, RED);
tft.setCursor(257, 427);
tft.setTextColor(WHITE, RED);
tft.setTextSize(2);
tft.print("LOG");
} // end RunLogger == 0
else if (RunLogger == 1) {
tft.fillRoundRect(240, 410, 70, 50, 10, GREEN);
tft.setCursor(257, 427);
tft.setTextColor(BLACK, GREEN);
tft.setTextSize(2);
tft.print("LOG");
} // end else if
} // end Speedometer page
void GPS_Info_Page() {
// Prints the title on the screen
tft.setCursor(90, 20);
tft.setTextColor(WHITE);
tft.setTextSize(3);
tft.print("GPS Info");
tft.drawFastHLine(10, 60, 320, RED);
// Display SS Initialized status
tft.fillCircle(30, 30, 10, GREEN);
// Display Longitude
tft.setCursor(50, 80);
tft.setTextColor(WHITE);
tft.setTextSize(2);
tft.print("Longitude");
// Display Latitude
tft.setCursor(50, 160);
tft.setTextColor(WHITE);
tft.setTextSize(2);
tft.print("Latitude");
// Display Distance
tft.setCursor(50, 240);
tft.setTextColor(WHITE);
tft.setTextSize(2);
tft.print("Distance");
// Display Time
tft.setCursor(50, 320);
tft.setTextColor(WHITE);
tft.setTextSize(2);
tft.print("Time");
// Draw buttons
// fillRoundRect(X, Y, width, height, radius, color);
// Speedometer page select PB
tft.fillRoundRect(10, 410, 70, 50, 10, BLUE);
tft.setCursor(16, 427);
tft.setTextColor(WHITE, BLUE);
tft.setTextSize(2);
tft.print("Speed");
// GPS Info Page select PB
tft.fillRoundRect(126, 410, 70, 50, 10, MAGENTA);
tft.setCursor(143, 427);
tft.setTextColor(BLACK, MAGENTA);
tft.setTextSize(2);
tft.print("GPS");
// Data logger control PB
if (RunLogger == 0) {
tft.fillRoundRect(240, 410, 70, 50, 10, RED);
tft.setCursor(257, 427);
tft.setTextColor(WHITE, RED);
tft.setTextSize(2);
tft.print("LOG");
} // end RunLogger == 0
else if (RunLogger == 1) {
tft.fillRoundRect(240, 410, 70, 50, 10, GREEN);
tft.setCursor(257, 427);
tft.setTextColor(BLACK, GREEN);
tft.setTextSize(2);
tft.print("LOG");
} // end else if
} // End GPS_Info_Page
// Function to return GPS string
String displayInfo()
{
// Define empty string to hold output
String gpsdata = "";
// Get latitude and longitude
if (gps.location.isValid())
{
gpsdata = String(gps.location.lat(), 6);
gpsdata += (",");
gpsdata += String(gps.location.lng(), 6);
gpsdata += (",");
}
else
{
return "0";
}
// Get Date
if (gps.date.isValid())
{
gpsdata += String(gps.date.year());
gpsdata += ("-");
if (gps.date.month() < 10) gpsdata += ("0");
gpsdata += String(gps.date.month());
gpsdata += ("-");
if (gps.date.day() < 10) gpsdata += ("0");
gpsdata += String(gps.date.day());
// gpsdata += (",");
}
else
{
return "0";
}
// Space between date and time
gpsdata += (",");
// Get time
if (gps.time.isValid())
{
if (gps.time.hour() < 10) gpsdata += ("0");
gpsdata += String(gps.time.hour() - 4);
gpsdata += (":");
if (gps.time.minute() < 10) gpsdata += ("0");
gpsdata += String(gps.time.minute());
gpsdata += (":");
if (gps.time.second() < 10) gpsdata += ("0");
gpsdata += String(gps.time.second());
}
else
{
return "0";
}
// Return completed string
return gpsdata;
} // end String displayInfo()
//#####################################################################
void Display_Compass(float dBearing) {
int dxo, dyo, dxi, dyi;
tft.setCursor(0, 0);
tft.drawCircle(centreX, centreY, diameter, WHITE); // Draw compass circle
for (float i = 0; i < 360; i = i + 22.5) {
dxo = diameter * cos((i - 90) * 3.14 / 180);
dyo = diameter * sin((i - 90) * 3.14 / 180);
dxi = dxo * 0.9;
dyi = dyo * 0.9;
tft.drawLine(dxo + centreX, dyo + centreY, dxi + centreX, dyi + centreY, WHITE);
}
PrintText((centreX - 5), (centreY - diameter - 18), "N", WHITE, 2);
PrintText((centreX - 5), (centreY + diameter + 5) , "S", WHITE, 2);
PrintText((centreX + diameter + 5), (centreY - 5), "E", WHITE, 2);
PrintText((centreX - diameter - 15), (centreY - 5), "W", WHITE, 2);
dx = (0.85 * diameter * cos((dBearing - 90) * 3.14 / 180)) + centreX; // calculate X position
dy = (0.85 * diameter * sin((dBearing - 90) * 3.14 / 180)) + centreY; // calculate Y position
draw_arrow(last_dx, last_dy, centreX, centreY, 5, 5, BLACK); // Erase last arrow
draw_arrow(dx, dy, centreX, centreY, 5, 5, WHITE); // Draw arrow in new position
last_dx = dx;
last_dy = dy;
// display compass azimuthal points
tft.setTextColor(WHITE);
tft.setTextSize(3);
tft.fillRect(230, 355, 60, 22, BLACK);
tft.setCursor(230, 355);
tft.print(Bearing_to_Ordinal(Bearing));
}// end Display_Compass;
//#####################################################################
void PrintText(int x, int y, String text, int colour, byte text_size) {
tft.setCursor(x, y);
tft.setTextColor(colour);
tft.setTextSize(text_size);
tft.print(text);
tft.setTextColor(YELLOW); // Default colour
tft.setTextSize(2); // Default Text Size
} // end Printtext
//#####################################################################
void draw_arrow(int x2, int y2, int x1, int y1, int alength, int awidth, int colour) {
float distance;
int dx, dy, x2o, y2o, x3, y3, x4, y4, k;
distance = sqrt(pow((x1 - x2), 2) + pow((y1 - y2), 2));
dx = x2 + (x1 - x2) * alength / distance;
dy = y2 + (y1 - y2) * alength / distance;
k = awidth / alength;
x2o = x2 - dx;
y2o = dy - y2;
x3 = y2o * k + dx;
y3 = x2o * k + dy;
x4 = dx - y2o * k;
y4 = dy - x2o * k;
tft.drawLine(x1, y1, x2, y2, colour);
tft.drawLine(x1, y1, dx, dy, colour);
tft.drawLine(x3, y3, x4, y4, colour);
tft.drawLine(x3, y3, x2, y2, colour);
tft.drawLine(x2, y2, x4, y4, colour);
} // end draw_arrow
//#####################################################################
String Bearing_to_Ordinal(float bearing) {
if (bearing >= 348.75 || bearing < 11.25) return "N ";
if (bearing >= 11.25 && bearing < 33.75) return "NNE";
if (bearing >= 33.75 && bearing < 56.25) return "NE ";
if (bearing >= 56.25 && bearing < 78.75) return "ENE";
if (bearing >= 78.75 && bearing < 101.25) return "E ";
if (bearing >= 101.25 && bearing < 123.75) return "ESE";
if (bearing >= 123.75 && bearing < 146.25) return "SE ";
if (bearing >= 146.25 && bearing < 168.75) return "SSE";
if (bearing >= 168.75 && bearing < 191.25) return "S ";
if (bearing >= 191.25 && bearing < 213.75) return "SSW";
if (bearing >= 213.75 && bearing < 236.25) return "SW ";
if (bearing >= 236.25 && bearing < 258.75) return "WSW";
if (bearing >= 258.75 && bearing < 281.25) return "W ";
if (bearing >= 281.25 && bearing < 303.75) return "WNW";
if (bearing >= 303.75 && bearing < 326.25) return "NW ";
if (bearing >= 326.25 && bearing < 348.75) return "NNW";
return "?";
} // end Bearing_to_Ordinal
void distCount() //interrupt service routine
{
revolutions++;
}