This shows you the differences between two versions of the page.
pm:prj2022:apredescu:speedometer [2022/06/01 20:35] victor.hincu created |
pm:prj2022:apredescu:speedometer [2022/06/01 20:59] (current) victor.hincu [Bibliografie/Resurse] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Nume proiect ====== | + | ===== Vitezometru si Odometru pentru bicicleta, cu recorder pentru 99 de lap-uri. ===== |
===== Introducere ===== | ===== Introducere ===== | ||
<note tip> | <note tip> | ||
Prezentarea pe scurt a proiectului vostru: | Prezentarea pe scurt a proiectului vostru: | ||
- | * ce face | + | * Vitezometru si Odometru, bazat pe o placa de baza Arduino Uno |
- | * care este scopul lui | + | * monitorizarea vitezei si a performantei pe durata a 99 de ture |
- | * care a fost ideea de la care aţi pornit | + | * nevoia unei cunostinte de a avea un vitezometru |
- | * de ce credeţi că este util pentru alţii şi pentru voi | + | * elimina nevoia de a purta un telefon in buzunar pentru a monitoriza performantele ce pot fi eventual eronate |
</note> | </note> | ||
===== Descriere generală ===== | ===== Descriere generală ===== | ||
Line 21: | Line 21: | ||
<note tip> | <note tip> | ||
Aici puneţi tot ce ţine de hardware design: | Aici puneţi tot ce ţine de hardware design: | ||
- | * listă de piese | + | * Arduino Uno - 1 |
- | * scheme electrice (se pot lua şi de pe Internet şi din datasheet-uri, e.g. http://www.captain.at/electronic-atmega16-mmc-schematic.png) | + | * Hall Effect Sensor - 1 |
- | * diagrame de semnal | + | * Push Buttons - 2 |
- | * rezultatele simulării | + | * 16x2 LCD Display - 1 |
+ | * 10kΩ Potentiometer - 1 | ||
+ | * 10kΩ Resistor - 2 | ||
+ | |||
+ | {{:pm:prj2022:apredescu:image_2022-06-01_205619478.png?200|}} | ||
</note> | </note> | ||
Line 31: | Line 36: | ||
<note tip> | <note tip> | ||
- | Descrierea codului aplicaţiei (firmware): | + | |
- | * mediu de dezvoltare (if any) (e.g. AVR Studio, CodeVisionAVR) | + | * Arduino IDE |
- | * librării şi surse 3rd-party (e.g. Procyon AVRlib) | + | * Additional Libraries: Wire/EEPROM/RTClib/LiquidCrystal |
- | * algoritmi şi structuri pe care plănuiţi să le implementaţi | + | |
- | * (etapa 3) surse şi funcţii implementate | + | <code cvp> |
+ | #include <LiquidCrystal.h> | ||
+ | |||
+ | LiquidCrystal lcd(12, 11, 8, 7, 6, 5); | ||
+ | |||
+ | float bicycleWheelCircumference = 2.1206; | ||
+ | |||
+ | const int pauseButton = 2; | ||
+ | boolean lastPauseButton = LOW; | ||
+ | boolean currentPauseButton = LOW; | ||
+ | |||
+ | const int displayModeButton = 3; | ||
+ | boolean lastDisplayModeButton = LOW; | ||
+ | boolean currentDisplayModeButton = LOW; | ||
+ | |||
+ | const int revolutionButton = 4; | ||
+ | boolean lastRevolutionButton = LOW; | ||
+ | boolean currentRevolutionButton = LOW; | ||
+ | |||
+ | boolean startShown = HIGH; | ||
+ | |||
+ | boolean paused = LOW; | ||
+ | boolean pausedShown = LOW; | ||
+ | unsigned long pausedStartTime = 0; | ||
+ | |||
+ | boolean wheelTurningShown = LOW; | ||
+ | unsigned long wheelTurningStartTime = 0; | ||
+ | |||
+ | boolean cycleSafelyShown = LOW; | ||
+ | unsigned long cycleSafelyStartTime = 0; | ||
+ | |||
+ | unsigned long lastRevolutionStartTime = 0; | ||
+ | unsigned long revolutionTime = 0; | ||
+ | |||
+ | int currentDisplayMode = 0; | ||
+ | int showLap = 0; | ||
+ | int lapCurrentlyShown = 100; | ||
+ | int currentLap = 0; | ||
+ | |||
+ | float currentDistance; | ||
+ | unsigned long currentDuration; | ||
+ | int currentMaximumKPH; | ||
+ | int currentAverageKPH; | ||
+ | int currentKPH; | ||
+ | |||
+ | float arrayDistance[100]; | ||
+ | unsigned long arrayDuration[100]; | ||
+ | int arrayMaximumKPH[100]; | ||
+ | int arrayAverageKPH[100]; | ||
+ | |||
+ | unsigned long revolutionCount = 0; | ||
+ | unsigned long currentTime = 0; | ||
+ | unsigned long lapStartTime = 0; | ||
+ | |||
+ | float km = 0.00; | ||
+ | float kph = 0.00; | ||
+ | int intHours; | ||
+ | int intMinutes; | ||
+ | int intSeconds; | ||
+ | |||
+ | unsigned long milliSecondsInSecond = 1000; | ||
+ | unsigned long milliSecondsInMinute = 60000; | ||
+ | unsigned long milliSecondsInHour = 3600000; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | |||
+ | pinMode (revolutionButton, INPUT); | ||
+ | pinMode (pauseButton, INPUT); | ||
+ | pinMode (displayModeButton, INPUT); | ||
+ | |||
+ | arrayMaximumKPH[0] = 0; | ||
+ | |||
+ | lcd.begin(16, 2); | ||
+ | lcd.clear(); | ||
+ | lcd.setCursor(2, 0); | ||
+ | lcd.print("PRESS BUTTON"); | ||
+ | lcd.setCursor(4, 1); | ||
+ | lcd.print("TO START"); | ||
+ | |||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | |||
+ | currentTime = millis(); | ||
+ | |||
+ | currentRevolutionButton = debounce(lastRevolutionButton, revolutionButton); | ||
+ | if (lastRevolutionButton == HIGH && currentRevolutionButton == LOW) { | ||
+ | |||
+ | if (!startShown && !paused) { | ||
+ | |||
+ | revolutionCount++; | ||
+ | |||
+ | lcd.setCursor(0, 0); | ||
+ | lcd.print("+"); | ||
+ | wheelTurningShown = HIGH; | ||
+ | wheelTurningStartTime = currentTime; | ||
+ | |||
+ | if (lastRevolutionStartTime > 0) { | ||
+ | |||
+ | revolutionTime = currentTime - lastRevolutionStartTime; | ||
+ | |||
+ | kph = (3600000 / revolutionTime) * bicycleWheelCircumference / 1000; | ||
+ | currentKPH = kph; | ||
+ | |||
+ | if (currentMaximumKPH < currentKPH) { | ||
+ | currentMaximumKPH = currentKPH; | ||
+ | } | ||
+ | } | ||
+ | lastRevolutionStartTime = currentTime; | ||
+ | } | ||
+ | } | ||
+ | lastRevolutionButton = currentRevolutionButton; | ||
+ | |||
+ | currentPauseButton = debounce(lastPauseButton, pauseButton); | ||
+ | if (lastPauseButton == LOW && currentPauseButton == HIGH) { | ||
+ | |||
+ | if (startShown) { | ||
+ | |||
+ | startShown = LOW; | ||
+ | |||
+ | showCycleSafely(); | ||
+ | cycleSafelyShown = HIGH; | ||
+ | cycleSafelyStartTime = currentTime; | ||
+ | |||
+ | currentLap = 1; | ||
+ | resetLapVariables(); | ||
+ | currentDisplayMode = 1; | ||
+ | |||
+ | } | ||
+ | else { | ||
+ | |||
+ | if (paused) { | ||
+ | |||
+ | paused = LOW; | ||
+ | |||
+ | showCycleSafely(); | ||
+ | cycleSafelyShown = HIGH; | ||
+ | cycleSafelyStartTime = currentTime; | ||
+ | |||
+ | currentLap++; | ||
+ | |||
+ | if (currentLap > 99) { | ||
+ | currentLap = 99; | ||
+ | lapCurrentlyShown = 100; | ||
+ | } | ||
+ | |||
+ | resetLapVariables(); | ||
+ | currentDisplayMode = 1; | ||
+ | } | ||
+ | |||
+ | else { | ||
+ | |||
+ | paused = HIGH; | ||
+ | |||
+ | currentDuration = currentTime - lapStartTime; | ||
+ | |||
+ | if (currentDuration < 2000) { | ||
+ | currentLap--; | ||
+ | } | ||
+ | else { | ||
+ | |||
+ | if (revolutionCount > 0) { | ||
+ | currentDistance = revolutionCount * bicycleWheelCircumference / 1000; | ||
+ | currentAverageKPH = currentDistance * 3600000 / currentDuration; | ||
+ | } | ||
+ | |||
+ | arrayDistance[currentLap] = currentDistance; | ||
+ | arrayDuration[currentLap] = currentDuration; | ||
+ | arrayAverageKPH[currentLap] = currentAverageKPH; | ||
+ | arrayMaximumKPH[currentLap] = currentMaximumKPH; | ||
+ | |||
+ | arrayDistance[0] = arrayDistance[0] + currentDistance; | ||
+ | arrayDuration[0] = arrayDuration[0] + currentDuration; | ||
+ | arrayAverageKPH[0] = arrayDistance[0] * 3600000 / arrayDuration[0]; | ||
+ | if (currentMaximumKPH > arrayMaximumKPH[0]) { | ||
+ | arrayMaximumKPH[0] = currentMaximumKPH; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | cycleSafelyShown = LOW; | ||
+ | |||
+ | showPaused(); | ||
+ | pausedShown = HIGH; | ||
+ | pausedStartTime = currentTime; | ||
+ | |||
+ | showLap = currentLap; | ||
+ | currentDisplayMode = 3; | ||
+ | |||
+ | lapCurrentlyShown = 100; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | lastPauseButton = currentPauseButton; | ||
+ | |||
+ | currentDisplayModeButton = debounce(lastDisplayModeButton, displayModeButton); | ||
+ | if (lastDisplayModeButton == LOW && currentDisplayModeButton == HIGH) { | ||
+ | |||
+ | if (startShown) { | ||
+ | |||
+ | startShown = LOW; | ||
+ | |||
+ | showCycleSafely(); | ||
+ | cycleSafelyShown = HIGH; | ||
+ | cycleSafelyStartTime = currentTime; | ||
+ | |||
+ | currentLap = 1; | ||
+ | resetLapVariables(); | ||
+ | currentDisplayMode = 1; | ||
+ | |||
+ | } | ||
+ | else { | ||
+ | |||
+ | if (!cycleSafelyShown && !pausedShown) { | ||
+ | |||
+ | if (!paused) { | ||
+ | |||
+ | if (currentDisplayMode == 1) { | ||
+ | currentDisplayMode = 2; | ||
+ | } | ||
+ | else { | ||
+ | currentDisplayMode = 1; | ||
+ | } | ||
+ | |||
+ | showLabels(currentDisplayMode); | ||
+ | } | ||
+ | |||
+ | else { | ||
+ | currentDisplayMode = 3; | ||
+ | showLap++; | ||
+ | if (showLap > currentLap) { | ||
+ | showLap = 0; // Show totals | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | lastDisplayModeButton = currentDisplayModeButton; | ||
+ | |||
+ | if (wheelTurningShown && !startShown && !paused && (currentTime >= (wheelTurningStartTime + 250))) { | ||
+ | wheelTurningShown = LOW; | ||
+ | lcd.setCursor(0, 0); | ||
+ | lcd.print(" "); | ||
+ | } | ||
+ | |||
+ | if (!startShown && !paused && (currentTime >= (lastRevolutionStartTime + 10000)) && currentKPH > 0) { | ||
+ | currentKPH = 0; | ||
+ | } | ||
+ | |||
+ | if (cycleSafelyShown && (currentTime >= (cycleSafelyStartTime + 2000))) { | ||
+ | cycleSafelyShown = LOW; | ||
+ | showLabels(currentDisplayMode); | ||
+ | } | ||
+ | |||
+ | if (pausedShown && (currentTime >= (pausedStartTime + 2000))) { | ||
+ | pausedShown = LOW; | ||
+ | showLabels(currentDisplayMode); | ||
+ | } | ||
+ | |||
+ | if (!startShown && !paused) { | ||
+ | |||
+ | currentDuration = currentTime - lapStartTime; | ||
+ | |||
+ | if (revolutionCount > 0) { | ||
+ | currentDistance = revolutionCount * bicycleWheelCircumference / 1000; | ||
+ | |||
+ | currentAverageKPH = currentDistance * 3600000 / currentDuration; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if (!startShown && !cycleSafelyShown && !pausedShown) { | ||
+ | |||
+ | if (currentDisplayMode < 3) { | ||
+ | |||
+ | lcd.setCursor(1, 0); | ||
+ | lcd.print(currentDistance); | ||
+ | lcd.print(" km"); | ||
+ | |||
+ | lcd.setCursor(14, 0); | ||
+ | if (currentKPH < 10) { | ||
+ | lcd.print(" "); | ||
+ | } | ||
+ | lcd.print(currentKPH); | ||
+ | |||
+ | computeHMS(currentDuration); | ||
+ | lcd.setCursor(1, 1); | ||
+ | if (intHours < 10) { | ||
+ | lcd.print("0"); | ||
+ | } | ||
+ | lcd.print(intHours); | ||
+ | |||
+ | lcd.print(":"); | ||
+ | if (intMinutes < 10) { | ||
+ | lcd.print("0"); | ||
+ | } | ||
+ | lcd.print(intMinutes); | ||
+ | |||
+ | lcd.print(":"); | ||
+ | if (intSeconds < 10) { | ||
+ | lcd.print("0"); | ||
+ | } | ||
+ | lcd.print(intSeconds); | ||
+ | |||
+ | lcd.setCursor(12, 1); | ||
+ | lcd.print("A"); | ||
+ | |||
+ | if (currentDisplayMode == 1) { | ||
+ | lcd.setCursor(12, 1); | ||
+ | lcd.print("A"); | ||
+ | lcd.setCursor(14, 1); | ||
+ | if (currentAverageKPH < 10) { | ||
+ | lcd.print(" "); | ||
+ | } | ||
+ | lcd.print(currentAverageKPH); | ||
+ | } | ||
+ | else { | ||
+ | lcd.setCursor(12, 1); | ||
+ | lcd.print("M"); | ||
+ | lcd.setCursor(14, 1); | ||
+ | if (currentMaximumKPH < 10) { | ||
+ | lcd.print(" "); | ||
+ | } | ||
+ | lcd.print(currentMaximumKPH); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | else { | ||
+ | |||
+ | if (lapCurrentlyShown != showLap) { | ||
+ | |||
+ | lapCurrentlyShown = showLap; | ||
+ | |||
+ | lcd.clear(); | ||
+ | |||
+ | lcd.setCursor(0, 0); | ||
+ | if (showLap == 0) { | ||
+ | lcd.print("T "); | ||
+ | } | ||
+ | else { | ||
+ | lcd.print(showLap); | ||
+ | } | ||
+ | |||
+ | lcd.setCursor(3, 0); | ||
+ | lcd.print("Avg"); | ||
+ | lcd.setCursor(7, 0); | ||
+ | lcd.print(arrayAverageKPH[showLap]); | ||
+ | if (arrayAverageKPH[showLap] < 10) { | ||
+ | lcd.print(" "); | ||
+ | } | ||
+ | |||
+ | lcd.setCursor(10, 0); | ||
+ | lcd.print("Max"); | ||
+ | lcd.setCursor(14, 0); | ||
+ | lcd.print(arrayMaximumKPH[showLap]); | ||
+ | if (arrayMaximumKPH[showLap] < 10) { | ||
+ | lcd.print(" "); | ||
+ | } | ||
+ | |||
+ | lcd.setCursor(0, 1); | ||
+ | lcd.print(" "); | ||
+ | lcd.setCursor(0, 1); | ||
+ | lcd.print(arrayDistance[showLap]); | ||
+ | |||
+ | computeHMS(arrayDuration[showLap]); | ||
+ | lcd.setCursor(8, 1); | ||
+ | if (intHours < 10) { | ||
+ | lcd.print("0"); | ||
+ | } | ||
+ | lcd.print(intHours); | ||
+ | |||
+ | lcd.print(":"); | ||
+ | |||
+ | if (intMinutes < 10) { | ||
+ | lcd.print("0"); | ||
+ | } | ||
+ | lcd.print(intMinutes); | ||
+ | |||
+ | lcd.print(":"); | ||
+ | |||
+ | if (intSeconds < 10) { | ||
+ | lcd.print("0"); | ||
+ | } | ||
+ | lcd.print(intSeconds); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void computeHMS(unsigned long duration) { | ||
+ | |||
+ | float floatHours; | ||
+ | float floatMinutes; | ||
+ | float floatSeconds; | ||
+ | |||
+ | intHours = 0; | ||
+ | intMinutes = 0; | ||
+ | intSeconds = 0; | ||
+ | |||
+ | if (duration >= 1000) { | ||
+ | floatSeconds = duration / milliSecondsInSecond % 60; | ||
+ | intSeconds = floatSeconds; | ||
+ | |||
+ | floatMinutes = duration / milliSecondsInMinute % 60; | ||
+ | intMinutes = floatMinutes; | ||
+ | |||
+ | floatHours = duration / milliSecondsInHour % 24; | ||
+ | intHours = floatHours; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void resetLapVariables() { | ||
+ | revolutionCount = 0; | ||
+ | |||
+ | lapStartTime = currentTime; | ||
+ | |||
+ | currentDistance = 0; | ||
+ | currentDuration = 0; | ||
+ | currentMaximumKPH = 0; | ||
+ | currentAverageKPH = 0; | ||
+ | } | ||
+ | |||
+ | void showCycleSafely() { | ||
+ | lcd.clear(); | ||
+ | lcd.setCursor(5, 0); | ||
+ | lcd.print("CYCLE"); | ||
+ | lcd.setCursor(4, 1); | ||
+ | lcd.print("SAFELY!"); | ||
+ | } | ||
+ | |||
+ | void showPaused() { | ||
+ | lcd.clear(); | ||
+ | lcd.setCursor(4, 0); | ||
+ | lcd.print("PAUSED!"); | ||
+ | } | ||
+ | |||
+ | void showLabels(int currentDisplayMode) { | ||
+ | |||
+ | lcd.clear(); | ||
+ | switch (currentDisplayMode) { | ||
+ | case 1: | ||
+ | lcd.setCursor(12, 0); | ||
+ | lcd.print("S"); | ||
+ | lcd.setCursor(12, 1); | ||
+ | lcd.print("A"); | ||
+ | break; | ||
+ | case 2: | ||
+ | lcd.setCursor(12, 0); | ||
+ | lcd.print("S"); | ||
+ | lcd.setCursor(12, 1); | ||
+ | lcd.print("M"); | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | boolean debounce(boolean last, int pin) | ||
+ | { | ||
+ | boolean current = digitalRead(pin); | ||
+ | if (last != current) { | ||
+ | delay(5); | ||
+ | current = digitalRead(pin); | ||
+ | } | ||
+ | return current; | ||
+ | } | ||
+ | </code> | ||
</note> | </note> | ||
Line 63: | Line 531: | ||
<note> | <note> | ||
- | Listă cu documente, datasheet-uri, resurse Internet folosite, eventual grupate pe **Resurse Software** şi **Resurse Hardware**. | + | * [[https://www.arduino.cc/reference/en/language/functions/communication/wire/|WIRE Library]] |
+ | *[[https://docs.arduino.cc/learn/built-in-libraries/eeprom|EEPROM Library]] | ||
+ | * [[https://www.arduino.cc/reference/en/libraries/rtclib/|RTC Library]] | ||
+ | *[[https://www.arduino.cc/reference/en/libraries/liquidcrystal/|Liquid Crystal]] | ||
</note> | </note> | ||
<html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | ||