1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
| ///////////////
// Calibration:
///////////////
const byte PulsesPerRevolution = 4; // Set how many pulses there are on each revolution. Default: 2.
// If the period between pulses is too high, or even if the pulses stopped, then we would get stuck showing the
// last value instead of a 0. Because of this we are going to set a limit for the maximum period allowed.
// If the period is above this value, the RPM will show as 0.
// The higher the set value, the longer lag/delay will have to sense that pulses stopped, but it will allow readings
// at very low RPM.
// Setting a low value is going to allow the detection of stop situations faster, but it will prevent having low RPM readings.
// The unit is in microseconds.
const unsigned long ZeroTimeout = 100000; // For high response time, a good value would be 100000.
// For reading very low RPM, a good value would be 300000.
// Calibration for smoothing RPM:
const byte numReadings = 2; // Number of samples for smoothing. The higher, the more smoothing, but it's going to
// react slower to changes. 1 = no smoothing. Default: 2.
/////////////
// Variables:
/////////////
volatile unsigned long LastTimeWeMeasured; // Stores the last time we measured a pulse so we can calculate the period.
volatile unsigned long PeriodBetweenPulses = ZeroTimeout+1000; // Stores the period between pulses in microseconds.
// It has a big number so it doesn't start with 0 which would be interpreted as a high frequency.
volatile unsigned long PeriodAverage = ZeroTimeout+1000; // Stores the period between pulses in microseconds in total, if we are taking multiple pulses.
// It has a big number so it doesn't start with 0 which would be interpreted as a high frequency.
unsigned long FrequencyRaw; // Calculated frequency, based on the period. This has a lot of extra decimals without the decimal point.
unsigned long FrequencyReal; // Frequency without decimals.
unsigned long RPM; // Raw RPM without any processing.
unsigned int PulseCounter = 1; // Counts the amount of pulse readings we took so we can average multiple pulses before calculating the period.
unsigned long PeriodSum; // Stores the summation of all the periods to do the average.
unsigned long LastTimeCycleMeasure = LastTimeWeMeasured; // Stores the last time we measure a pulse in that cycle.
// We need a variable with a value that is not going to be affected by the interrupt
// because we are going to do math and functions that are going to mess up if the values
// changes in the middle of the cycle.
unsigned long CurrentMicros = micros(); // Stores the micros in that cycle.
// We need a variable with a value that is not going to be affected by the interrupt
// because we are going to do math and functions that are going to mess up if the values
// changes in the middle of the cycle.
// We get the RPM by measuring the time between 2 or more pulses so the following will set how many pulses to
// take before calculating the RPM. 1 would be the minimum giving a result every pulse, which would feel very responsive
// even at very low speeds but also is going to be less accurate at higher speeds.
// With a value around 10 you will get a very accurate result at high speeds, but readings at lower speeds are going to be
// farther from eachother making it less "real time" at those speeds.
// There's a function that will set the value depending on the speed so this is done automatically.
unsigned int AmountOfReadings = 1;
unsigned int ZeroDebouncingExtra; // Stores the extra value added to the ZeroTimeout to debounce it.
// The ZeroTimeout needs debouncing so when the value is close to the threshold it
// doesn't jump from 0 to the value. This extra value changes the threshold a little
// when we show a 0.
// Variables for smoothing tachometer:
unsigned long readings[numReadings]; // The input.
unsigned long readIndex; // The index of the current reading.
unsigned long total; // The running total.
unsigned long average; // The RPM value after applying the smoothing.
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
float current;
int termPin = 6; //termistor pin connection
int termNom = 100000; //resistancve du termistor
int refTemp = 25; //
int beta = 3950;
int resistance = 100000; // valeure resistance serie
// OLED 0.96" Display:
#include <Adafruit_GFX.h> // Include core graphics library for the display.
#include <Adafruit_SSD1306.h> // Include Adafruit_SSD1306 library to drive the display.
Adafruit_SSD1306 display(128, 64); // Create display.
#include <Fonts/FreeMonoBold18pt7b.h> // Add a custom font.
const int RXPin = 2, TXPin = 5;
const uint32_t GPSBaud = 9600; //Default baud of NEO-6M is 9600
TinyGPSPlus gps; // the TinyGPS++ object
SoftwareSerial gpsSerial(RXPin, TXPin); // the serial interface to the GPS device
//variable
void setup() { // Start of setup:
{{
Serial.begin(9600); // Begin serial communication.
{gpsSerial.begin(GPSBaud);
{attachInterrupt(digitalPinToInterrupt(3), Pulse_Event, RISING); // Enable interruption pin 2 when going from LOW to HIGH. { Serial.begin(9600);
}
delay(1000); // We sometimes take several readings of the period to average. Since we don't have any readings
// stored we need a high enough value in micros() so if divided is not going to give negative values.
// The delay allows the micros() to be high enough for the first few cycles.
// OLED 0.96" Display:
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Initialize display with the I2C address of 0x3C.
display.clearDisplay(); // Clear the buffer.
display.setTextColor(WHITE); // Set color of the text to white.
display.setRotation(0); // Set orientation. Goes from 0, 1, 2 or 3.
display.setTextWrap(false); // By default, long lines of text are set to automatically wrap back to the leftmost column.
// To override this behavior (so text will run off the right side of the display - useful for
// scrolling marquee effects), use setTextWrap(false). The normal wrapping behavior is restored
// with setTextWrap(true).
display.dim(0); // Set brightness (0 is maximum and 1 is a little dim).
// End of setup.
void Pulse_Event() // The interrupt runs this to calculate the period between pulses:
{
PeriodBetweenPulses = micros() - LastTimeWeMeasured; // Current "micros" minus the old "micros" when the last pulse happens.
// This will result with the period (microseconds) between both pulses.
// The way is made, the overflow of the "micros" is not going to cause any issue.
LastTimeWeMeasured = micros(); // Stores the current micros so the next time we have a pulse we would have something to compare with.
if(PulseCounter >= AmountOfReadings) // If counter for amount of readings reach the set limit:
{
PeriodAverage = PeriodSum / AmountOfReadings; // Calculate the final period dividing the sum of all readings by the
// amount of readings to get the average.
PulseCounter = 1; // Reset the counter to start over. The reset value is 1 because its the minimum setting allowed (1 reading).
PeriodSum = PeriodBetweenPulses; // Reset PeriodSum to start a new averaging operation.
// Change the amount of readings depending on the period between pulses.
// To be very responsive, ideally we should read every pulse. The problem is that at higher speeds the period gets
// too low decreasing the accuracy. To get more accurate readings at higher speeds we should get multiple pulses and
// average the period, but if we do that at lower speeds then we would have readings too far apart (laggy or sluggish).
// To have both advantages at different speeds, we will change the amount of readings depending on the period between pulses.
// Remap period to the amount of readings:
int RemapedAmountOfReadings = map(PeriodBetweenPulses, 40000, 5000, 1, 10); // Remap the period range to the reading range.
// 1st value is what are we going to remap. In this case is the PeriodBetweenPulses.
// 2nd value is the period value when we are going to have only 1 reading. The higher it is, the lower RPM has to be to reach 1 reading.
// 3rd value is the period value when we are going to have 10 readings. The higher it is, the lower RPM has to be to reach 10 readings.
// 4th and 5th values are the amount of readings range.
RemapedAmountOfReadings = constrain(RemapedAmountOfReadings, 1, 10); // Constrain the value so it doesn't go below or above the limits.
AmountOfReadings = RemapedAmountOfReadings; // Set amount of readings as the remaped value.
}
else
{
PulseCounter++; // Increase the counter for amount of readings by 1.
PeriodSum = PeriodSum + PeriodBetweenPulses; // Add the periods so later we can average.
// End of Pulse_Event.
}
void loop() {
if (gpsSerial.available() > 0) {
if (gps.encode(gpsSerial.read())) {
//display.clearDisplay () ;
// display.setCursor (0,0) ;
// display.print(F("- speed: "));
if (gps.speed.isValid()) {
.clearDisplay () ;
display.setTextSize (4) ;
display.setCursor (0,5) ;
display.print(gps.speed.kmph());
display.display () ;
delay (3000) ;
//if (gps.date.isValid() && gps.time.isValid()) {
display.clearDisplay () ;
display.setTextSize (4) ;
// display.setTextColor (WHITE) ;
display.setCursor (0,5) ;
//display.clearDisplay () ;
display.print(gps.time.hour() +1 +1);
display.print(F(":"));
display.print(gps.time.minute());
// display.print(F(":"));
// display.println(gps.time.second());
display.display () ;
delay (1000) ;
current = analogRead(termPin);
current = 1023 / current - 1;
current = resistance / current;
float temperature;
temperature = current / termNom;
temperature = log(temperature);
temperature /= beta;
temperature += 1.0 / (refTemp + 273.15);
temperature = 1.0 / temperature;
temperature -= 273.15;
display.clearDisplay () ;
display.setTextSize (4) ;
display.setCursor ( 0,5) ;
display.print(temperature,1);
display.display () ;
delay (2000);
}}
// The following is going to store the two values that might change in the middle of the cycle.
// We are going to do math and functions with those values and they can create glitches if they change in the
// middle of the cycle.
LastTimeCycleMeasure = LastTimeWeMeasured; // Store the LastTimeWeMeasured in a variable.
CurrentMicros = micros(); // Store the micros() in a variable.
// CurrentMicros should always be higher than LastTimeWeMeasured, but in rare occasions that's not true.
// I'm not sure why this happens, but my solution is to compare both and if CurrentMicros is lower than
// LastTimeCycleMeasure I set it as the CurrentMicros.
// The need of fixing this is that we later use this information to see if pulses stopped.
if(CurrentMicros < LastTimeCycleMeasure)
{
LastTimeCycleMeasure = CurrentMicros;
}
// Calculate the frequency:
FrequencyRaw = 10000000000 / PeriodAverage; // Calculate the frequency using the period between pulses.
// Detect if pulses stopped or frequency is too low, so we can show 0 Frequency:
if(PeriodBetweenPulses > ZeroTimeout - ZeroDebouncingExtra || CurrentMicros - LastTimeCycleMeasure > ZeroTimeout - ZeroDebouncingExtra)
{ // If the pulses are too far apart that we reached the timeout for zero:
FrequencyRaw = 0; // Set frequency as 0.
ZeroDebouncingExtra = 2000; // Change the threshold a little so it doesn't bounce.
}
else
{
ZeroDebouncingExtra = 0; // Reset the threshold to the normal value so it doesn't bounce.
}
FrequencyReal = FrequencyRaw / 10000; // Get frequency without decimals.
// This is not used to calculate RPM but we remove the decimals just in case
// you want to print it.
// Calculate the RPM:
RPM = FrequencyRaw / PulsesPerRevolution * 60; // Frequency divided by amount of pulses per revolution multiply by
// 60 seconds to get minutes.
RPM = RPM / 10000; // Remove the decimals.
// Smoothing RPM:
total = total - readings[readIndex]; // Advance to the next position in the array.
readings[readIndex] = RPM; // Takes the value that we are going to smooth.
total = total + readings[readIndex]; // Add the reading to the total.
readIndex = readIndex + 1; // Advance to the next position in the array.
if (readIndex >= numReadings) // If we're at the end of the array:
{
readIndex = 0; // Reset array index.
}
// Calculate the average:
average = total / numReadings; // The average value it's the smoothed result.
/*
// Print information on the serial monitor:
// Comment this section if you have a display and you don't need to monitor the values on the serial monitor.
// This is because disabling this section would make the loop run faster.
Serial.print("Period: ");
Serial.print(PeriodBetweenPulses);
Serial.print("\tReadings: ");
Serial.print(AmountOfReadings);
Serial.print("\tFrequency: ");
Serial.print(FrequencyReal);
Serial.print("\tRPM: ");
Serial.print(RPM);
Serial.print("\tTachometer: ");
Serial.println(average);
*/
// OLED 0.96" Display:
// Convert variable into a string, so we can change the text alignment to the right:
// It can be also used to add or remove decimal numbers.
char string[10]; // Create a character array of 10 characters
// Convert float to a string:
dtostrf(average, 6, 0, string); // (<variable>,<amount of digits we are going to use>,<amount of decimal digits>,<string name>)
display.clearDisplay(); // Clear the display so we can refresh.
display.setFont(&FreeMonoBold18pt7b); // Set a custom font.
display.setTextSize(0); // Set text size. We are using a custom font so you should always use the text size of 0.
display.setCursor(0, 25); // (x,y).
display.println("RPM:"); // Text or value to print.
// Print variable with right alignment:
display.setCursor(0, 60); // (x,y).
display.println(string); // Text or value to print.
// Print a comma for the thousands separator:
if(average > 999) // If value is above 999:
{
// Draw line (to show a comma):
display.drawLine(63, 60, 61, 65, WHITE); // Draw line (x0,y0,x1,y1,color).
}
display.display(); // Print everything we set previously.
// delay (3000) ;
//} // End of loop.
// End of Pulse_Event.
}
void Pulse_Event() // The interrupt runs this to calculate the period between pulses:
{
PeriodBetweenPulses = micros() - LastTimeWeMeasured; // Current "micros" minus the old "micros" when the last pulse happens.
// This will result with the period (microseconds) between both pulses.
// The way is made, the overflow of the "micros" is not going to cause any issue.
LastTimeWeMeasured = micros(); // Stores the current micros so the next time we have a pulse we would have something to compare with.
if(PulseCounter >= AmountOfReadings) // If counter for amount of readings reach the set limit:
{
PeriodAverage = PeriodSum / AmountOfReadings; // Calculate the final period dividing the sum of all readings by the
// amount of readings to get the average.
PulseCounter = 1; // Reset the counter to start over. The reset value is 1 because its the minimum setting allowed (1 reading).
PeriodSum = PeriodBetweenPulses; // Reset PeriodSum to start a new averaging operation.
// Change the amount of readings depending on the period between pulses.
// To be very responsive, ideally we should read every pulse. The problem is that at higher speeds the period gets
// too low decreasing the accuracy. To get more accurate readings at higher speeds we should get multiple pulses and
// average the period, but if we do that at lower speeds then we would have readings too far apart (laggy or sluggish).
// To have both advantages at different speeds, we will change the amount of readings depending on the period between pulses.
// Remap period to the amount of readings:
int RemapedAmountOfReadings = map(PeriodBetweenPulses, 40000, 5000, 1, 10); // Remap the period range to the reading range.
// 1st value is what are we going to remap. In this case is the PeriodBetweenPulses.
// 2nd value is the period value when we are going to have only 1 reading. The higher it is, the lower RPM has to be to reach 1 reading.
// 3rd value is the period value when we are going to have 10 readings. The higher it is, the lower RPM has to be to reach 10 readings.
// 4th and 5th values are the amount of readings range.
RemapedAmountOfReadings = constrain(RemapedAmountOfReadings, 1, 10); // Constrain the value so it doesn't go below or above the limits.
AmountOfReadings = RemapedAmountOfReadings; // Set amount of readings as the remaped value.
}
else
{
PulseCounter++; // Increase the counter for amount of readings by 1.
PeriodSum = PeriodSum + PeriodBetweenPulses; // Add the periods so later we can average.
// End of Pulse_Event.
}}} |
Partager