added first code for OBD2 via CAN and K-Line

This commit is contained in:
Marcel Peterkau 2025-05-04 12:03:19 +02:00
parent b627f52272
commit 1ee326fd57
12 changed files with 277 additions and 53 deletions

View File

@ -1,6 +1,6 @@
{
"codegenerator_checksum": "23dff82a4745f67041012080b05ec367c2dd44c6bb02974143b227ba49682b6f",
"timestamp": "2024-01-10 19:06:30",
"codegenerator_checksum": "15e9152fb712bc6ed7c3353a05b1b54dc697f3e489a6e1ff42b7e57f0fa094d4",
"timestamp": "2025-05-04 12:01:55",
"dtc_table_data": [
{
"num": 0,
@ -89,6 +89,31 @@
},
{
"num": 17,
"title": "OBD2-KLine Timeout",
"description": "Die Anfrage über OBD2-KLine konnte nicht gesendet werden. Prüfen Sie den K-Line-Treiber (z.B. L9637D) und die Verkabelung."
},
{
"num": 18,
"title": "OBD2-CAN Timeout",
"description": "Die Anfrage an das Steuergerät über OBD2-CAN konnte nicht gesendet werden. Prüfen Sie die Verbindung zum CAN-Modul und die OBD2-Konfiguration."
},
{
"num": 19,
"title": "Keine OBD2-CAN Antwort",
"description": "Es wurde innerhalb der erwarteten Zeit keine Antwort vom Steuergerät über OBD2-CAN empfangen. Prüfen Sie, ob das Fahrzeug OBD2 unterstützt und die Verbindung korrekt ist."
},
{
"num": 20,
"title": "Keine OBD2-KLine Antwort",
"description": "Es wurde innerhalb der erwarteten Zeit keine Antwort über OBD2-KLine empfangen. Prüfen Sie die Verbindung zum Steuergerät und die Protokollkompatibilität."
},
{
"num": 21,
"title": "OBD2-KLine Antwort ungültig",
"description": "Das empfangene Frame war unvollständig oder entsprach nicht dem erwarteten OBD2-Format. Möglicherweise inkompatibles Steuergerät."
},
{
"num": 22,
"title": "Last Error",
"description": "Last Error"
}

View File

@ -32,7 +32,6 @@ struct can_frame
// Function prototypes
void Init_CAN();
void CAN_Process();
uint32_t Process_CAN_WheelSpeed();
#endif

View File

@ -85,7 +85,9 @@ typedef enum SpeedSource_e
#endif
SOURCE_IMPULSE,
SOURCE_GPS,
SOURCE_CAN
SOURCE_CAN,
SOURCE_OBD2_KLINE,
SOURCE_OBD2_CAN
} SpeedSource_t;
// String representation of SpeedSource enum

View File

@ -7,10 +7,10 @@
* It includes enums for DTC active status, severity levels, and specific DTC codes.
* The file also defines an array of DTC definitions and a timestamp indicating the generation time.
*
* @note This file is auto-generated by a script on 2024-01-10 18:37:05.
* @note This file is auto-generated by a script on 2025-05-04 12:01:55.
*
* @author Marcel Peterkau
* @date 10.01.2024
* @date 04.05.2025
*/
#ifndef DTC_DEFS_H
@ -57,7 +57,12 @@ typedef struct {
#define DTC_FAKE_DTC_INFO 14
#define DTC_FAKE_DTC_WARN 15
#define DTC_FAKE_DTC_CRIT 16
#define DTC_LAST_DTC 17
#define DTC_OBD2_KLINE_TIMEOUT 17
#define DTC_OBD2_CAN_TIMEOUT 18
#define DTC_OBD2_CAN_NO_RESPONSE 19
#define DTC_OBD2_KLINE_NO_RESPONSE 20
#define DTC_OBD2_KLINE_BAD_FRAME 21
#define DTC_LAST_DTC 22
const DTC_t dtc_definitions[] = {
{ DTC_NO_DTC , DTC_NONE }, // No Error
@ -77,9 +82,14 @@ const DTC_t dtc_definitions[] = {
{ DTC_FAKE_DTC_INFO , DTC_INFO }, // Ein Dummy-DTC der Schwere "Info" für Debugging-Zwecke
{ DTC_FAKE_DTC_WARN , DTC_WARN }, // Ein Dummy-DTC der Schwere "Warnung" für Debugging-Zwecke
{ DTC_FAKE_DTC_CRIT , DTC_CRITICAL }, // Ein Dummy-DTC der Schwere "Kritisch" für Debugging-Zwecke
{ DTC_OBD2_KLINE_TIMEOUT , DTC_CRITICAL }, // Die Anfrage über OBD2-KLine konnte nicht gesendet werden. Prüfen Sie den K-Line-Treiber (z.B. L9637D) und die Verkabelung.
{ DTC_OBD2_CAN_TIMEOUT , DTC_CRITICAL }, // Die Anfrage an das Steuergerät über OBD2-CAN konnte nicht gesendet werden. Prüfen Sie die Verbindung zum CAN-Modul und die OBD2-Konfiguration.
{ DTC_OBD2_CAN_NO_RESPONSE , DTC_WARN }, // Es wurde innerhalb der erwarteten Zeit keine Antwort vom Steuergerät über OBD2-CAN empfangen. Prüfen Sie, ob das Fahrzeug OBD2 unterstützt und die Verbindung korrekt ist.
{ DTC_OBD2_KLINE_NO_RESPONSE , DTC_WARN }, // Es wurde innerhalb der erwarteten Zeit keine Antwort über OBD2-KLine empfangen. Prüfen Sie die Verbindung zum Steuergerät und die Protokollkompatibilität.
{ DTC_OBD2_KLINE_BAD_FRAME , DTC_WARN }, // Das empfangene Frame war unvollständig oder entsprach nicht dem erwarteten OBD2-Format. Möglicherweise inkompatibles Steuergerät.
{ DTC_LAST_DTC , DTC_NONE } // Last Error
};
#endif // DTC_DEFS_H
// CODEGENERATOR_CHECKSUM: 23dff82a4745f67041012080b05ec367c2dd44c6bb02974143b227ba49682b6f
// CODEGENERATOR_CHECKSUM: 15e9152fb712bc6ed7c3353a05b1b54dc697f3e489a6e1ff42b7e57f0fa094d4

View File

@ -0,0 +1,10 @@
#ifndef _OBD2_CAN_H_
#define _OBD2_CAN_H_
#include <Arduino.h>
// === Funktionen ===
void Init_OBD2_CAN();
uint32_t Process_OBD2_CAN_Speed();
#endif

View File

@ -0,0 +1,10 @@
#ifndef _OBD2_KLINE_H_
#define _OBD2_KLINE_H_
#include <Arduino.h>
// === Funktionen ===
void Init_OBD2_KLine(Stream &serial); // Übergib z.B. SoftwareSerial oder Serial1
uint32_t Process_OBD2_KLine_Speed(); // liefert mm seit letztem Aufruf
#endif

View File

@ -49,21 +49,6 @@ void Init_CAN()
CAN0.setMode(MCP_NORMAL);
}
/**
* @brief Processes CAN messages and sends a CAN debug message periodically.
*
* This function processes CAN messages and sends a CAN debug message periodically based on a time interval.
*/
void CAN_Process()
{
static uint32_t previousMillis = 0;
if (millis() - previousMillis >= 100)
{
sendCANDebugMessage();
previousMillis = millis();
}
}
/**
* @brief Processes CAN messages to determine the wheel speed based on the configured CAN source.
*
@ -115,6 +100,16 @@ uint32_t Process_CAN_WheelSpeed()
MaintainDTC(DTC_NO_CAN_SIGNAL, (millis() > lastRecTimestamp + 10000 ? true : false));
}
#ifdef CAN_DEBUG_MESSAGE
static uint32_t previousMillis = 0;
if (millis() - previousMillis >= 1000)
{
sendCANDebugMessage();
previousMillis = millis();
}
#endif
return milimeters_to_add;
}

View File

@ -7,8 +7,10 @@ const char *SpeedSourceString[] = {
#endif
"Impuls",
"GPS",
"CAN-Bus"
};
"CAN-Bus",
"OBD2 (K-Line)",
"OBD2 (CAN)",
};
const size_t SpeedSourceString_Elements = sizeof(SpeedSourceString) / sizeof(SpeedSourceString[0]);
@ -19,14 +21,15 @@ const char *GPSBaudRateString[] = {
"19200",
"38400",
"57600",
"115200"
};
"115200",
};
const size_t GPSBaudRateString_Elements = sizeof(GPSBaudRateString) / sizeof(GPSBaudRateString[0]);
// String representation of CANSource enum
const char *CANSourceString[] = {
"KTM 890 Adventure R (2021)",
"KTM 1290 Superduke R (2023)"};
"KTM 1290 Superduke R (2023)",
};
const size_t CANSourceString_Elements = sizeof(CANSourceString) / sizeof(CANSourceString[0]);

View File

@ -1,18 +1,23 @@
# No. | DTC-Constant | Severity | Title | Description
#-----|------------------------------|---------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------
1; DTC_TANK_EMPTY; DTC_CRITICAL; Ölvorrat leer; Ölvorrat ist komplett leer. Den Ölvorrat auffüllen und im Menu 'Wartung' zurück setzen
2; DTC_TANK_LOW; DTC_WARN; Ölvorrat niedrig; Ölvorrat ist unter der Warnschwelle. Den Ölvorrat demnächst auffüllen und im Menu 'Wartung' zurück setzen
3; DTC_NO_EEPROM_FOUND; DTC_CRITICAL; kein EEPROM erkannt; Es wurde kein EEPROM gefunden. Dies lässt einen Hardware-Defekt vermuten.
4; DTC_EEPROM_CFG_BAD; DTC_CRITICAL; EEPROM CFG Checksumme; Die Checksumme der Config-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück
5; DTC_EEPROM_PDS_BAD; DTC_CRITICAL; EEPROM PDS Checksumme; Die Checksumme der Betriebsdaten-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück
6; DTC_EEPROM_PDSADRESS_BAD; DTC_CRITICAL; EEPROM PDS Adresse; Die Adresse der Betriebsdaten-Partition im EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück
7; DTC_EEPROM_VERSION_BAD; DTC_CRITICAL; EEPROM Version falsch; Die Layout-Version des EEPROM stimmt nicht mit der Firmware-Version überein. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück
8; DTC_FLASHFS_ERROR; DTC_CRITICAL; Flashspeicher Fehler; Der Flashspeicher konnte nicht initialisiert werden. Aktualisieren sie Flash & Firmware
9; DTC_FLASHFS_VERSION_ERROR; DTC_CRITICAL; Flashversion falsch; Die Version des Flashspeicher stimmt nicht mit der Firmware-Version überein. Aktualisieren sie den Flash mit der passenden Update-Datei
10; DTC_NO_GPS_SERIAL; DTC_CRITICAL; Keine GPS-Verbindung; Es wurde kein GPS-Signal über die serielle Schnittstelle empfangen, Prüfen sie die Verbindung und das GPS-Modul
11; DTC_CAN_TRANSCEIVER_FAILED; DTC_CRITICAL; CAN-Transceiver Error; Es konnte keine Verbindung zum CAN-Transceiver hergestellt werden. Prüfen Sie die Hardware auf Defekte
12; DTC_NO_CAN_SIGNAL; DTC_WARN; Keine CAN-Verbindung; Es konnte kein CAN-Signal empfangen werden. Prüfen sie die Verbindung und die Einstellungen
13; DTC_EEPROM_CFG_SANITY; DTC_WARN; Config-Validierung; Ein oder mehrer Einstellungswerte sind ausserhalb plausibler Werte. Prüfen Sie Ihre Einstellungen
14; DTC_FAKE_DTC_INFO; DTC_INFO; Dummy-DTC Info; Ein Dummy-DTC der Schwere "Info" für Debugging-Zwecke
15; DTC_FAKE_DTC_WARN; DTC_WARN; Dummy-DTC Warnung; Ein Dummy-DTC der Schwere "Warnung" für Debugging-Zwecke
16; DTC_FAKE_DTC_CRIT; DTC_CRITICAL; Dummy-DTC Kritisch; Ein Dummy-DTC der Schwere "Kritisch" für Debugging-Zwecke
# No. | DTC-Constant | Severity | Title | Description
#-----|------------------------------|---------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------
1; DTC_TANK_EMPTY; DTC_CRITICAL; Ölvorrat leer; Ölvorrat ist komplett leer. Den Ölvorrat auffüllen und im Menu 'Wartung' zurück setzen
2; DTC_TANK_LOW; DTC_WARN; Ölvorrat niedrig; Ölvorrat ist unter der Warnschwelle. Den Ölvorrat demnächst auffüllen und im Menu 'Wartung' zurück setzen
3; DTC_NO_EEPROM_FOUND; DTC_CRITICAL; kein EEPROM erkannt; Es wurde kein EEPROM gefunden. Dies lässt einen Hardware-Defekt vermuten.
4; DTC_EEPROM_CFG_BAD; DTC_CRITICAL; EEPROM CFG Checksumme; Die Checksumme der Config-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück
5; DTC_EEPROM_PDS_BAD; DTC_CRITICAL; EEPROM PDS Checksumme; Die Checksumme der Betriebsdaten-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück
6; DTC_EEPROM_PDSADRESS_BAD; DTC_CRITICAL; EEPROM PDS Adresse; Die Adresse der Betriebsdaten-Partition im EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück
7; DTC_EEPROM_VERSION_BAD; DTC_CRITICAL; EEPROM Version falsch; Die Layout-Version des EEPROM stimmt nicht mit der Firmware-Version überein. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück
8; DTC_FLASHFS_ERROR; DTC_CRITICAL; Flashspeicher Fehler; Der Flashspeicher konnte nicht initialisiert werden. Aktualisieren sie Flash & Firmware
9; DTC_FLASHFS_VERSION_ERROR; DTC_CRITICAL; Flashversion falsch; Die Version des Flashspeicher stimmt nicht mit der Firmware-Version überein. Aktualisieren sie den Flash mit der passenden Update-Datei
10; DTC_NO_GPS_SERIAL; DTC_CRITICAL; Keine GPS-Verbindung; Es wurde kein GPS-Signal über die serielle Schnittstelle empfangen, Prüfen sie die Verbindung und das GPS-Modul
11; DTC_CAN_TRANSCEIVER_FAILED; DTC_CRITICAL; CAN-Transceiver Error; Es konnte keine Verbindung zum CAN-Transceiver hergestellt werden. Prüfen Sie die Hardware auf Defekte
12; DTC_NO_CAN_SIGNAL; DTC_WARN; Keine CAN-Verbindung; Es konnte kein CAN-Signal empfangen werden. Prüfen sie die Verbindung und die Einstellungen
13; DTC_EEPROM_CFG_SANITY; DTC_WARN; Config-Validierung; Ein oder mehrer Einstellungswerte sind ausserhalb plausibler Werte. Prüfen Sie Ihre Einstellungen
14; DTC_FAKE_DTC_INFO; DTC_INFO; Dummy-DTC Info; Ein Dummy-DTC der Schwere "Info" für Debugging-Zwecke
15; DTC_FAKE_DTC_WARN; DTC_WARN; Dummy-DTC Warnung; Ein Dummy-DTC der Schwere "Warnung" für Debugging-Zwecke
16; DTC_FAKE_DTC_CRIT; DTC_CRITICAL; Dummy-DTC Kritisch; Ein Dummy-DTC der Schwere "Kritisch" für Debugging-Zwecke
17; DTC_OBD2_KLINE_TIMEOUT; DTC_CRITICAL; OBD2-KLine Timeout; Die Anfrage über OBD2-KLine konnte nicht gesendet werden. Prüfen Sie den K-Line-Treiber (z.B. L9637D) und die Verkabelung.
18; DTC_OBD2_CAN_TIMEOUT; DTC_CRITICAL; OBD2-CAN Timeout; Die Anfrage an das Steuergerät über OBD2-CAN konnte nicht gesendet werden. Prüfen Sie die Verbindung zum CAN-Modul und die OBD2-Konfiguration.
19; DTC_OBD2_CAN_NO_RESPONSE; DTC_WARN; Keine OBD2-CAN Antwort; Es wurde innerhalb der erwarteten Zeit keine Antwort vom Steuergerät über OBD2-CAN empfangen. Prüfen Sie, ob das Fahrzeug OBD2 unterstützt und die Verbindung korrekt ist.
20; DTC_OBD2_KLINE_NO_RESPONSE; DTC_WARN; Keine OBD2-KLine Antwort; Es wurde innerhalb der erwarteten Zeit keine Antwort über OBD2-KLine empfangen. Prüfen Sie die Verbindung zum Steuergerät und die Protokollkompatibilität.
21; DTC_OBD2_KLINE_BAD_FRAME; DTC_WARN; OBD2-KLine Antwort ungültig; Das empfangene Frame war unvollständig oder entsprach nicht dem erwarteten OBD2-Format. Möglicherweise inkompatibles Steuergerät.

View File

@ -37,6 +37,8 @@
#include "gps.h"
#include "dtc.h"
#include "led_colors.h"
#include "obd2_kline.h"
#include "obd2_can.h"
#ifdef FEATURE_ENABLE_WIFI_CLIENT
#include <ESP8266WiFiMulti.h>
@ -154,6 +156,16 @@ void setup()
wheelSpeedcapture = &Process_Impulse_WheelSpeed;
Serial.print("\nPulse-Input Init done");
break;
case SOURCE_OBD2_KLINE:
Init_OBD2_KLine(Serial);
wheelSpeedcapture = &Process_OBD2_KLine_Speed;
Serial.print("\nOBD2-KLine-Init done");
break;
case SOURCE_OBD2_CAN:
Init_OBD2_CAN();
wheelSpeedcapture = &Process_OBD2_CAN_Speed;
Serial.print("\nOBD2-CAN-Init done");
break;
default:
break;
}
@ -227,19 +239,14 @@ void setup()
void loop()
{
// Run lubrication application with the calculated wheel distance
RunLubeApp(wheelSpeedcapture());
if (wheelSpeedcapture != nullptr)
RunLubeApp(wheelSpeedcapture());
#ifdef FEATURE_ENABLE_OLED
// Update OLED display if enabled
Display_Process();
#endif
// Process CAN messages if the speed source is not impulse
if (LubeConfig.SpeedSource != SOURCE_IMPULSE)
{
CAN_Process();
}
// Process button input, manage LED behavior, perform EEPROM tasks, handle webserver operations,
// process Diagnostic Trouble Codes (DTC), and manage debugging
Button_Process();

84
Software/src/obd2_can.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "obd2_can.h"
#include <mcp_can.h>
#include <SPI.h>
#include "common.h"
#include "globals.h"
#include "dtc.h"
#include "debugger.h"
// === Setup: MCP2515 CS-Pin definieren ===
#define OBD2_CAN_CS_PIN 10
#define OBD2_OBD_REQUEST_ID 0x7DF
#define OBD2_OBD_RESPONSE_ID 0x7E8
MCP_CAN OBD_CAN(OBD2_CAN_CS_PIN);
static uint32_t lastQueryTime = 0;
static uint32_t lastRecvTime = 0;
static uint32_t lastSpeedMMperSec = 0;
#define OBD2_QUERY_INTERVAL 500 // alle 500ms
void Init_OBD2_CAN()
{
if (OBD_CAN.begin(MCP_STD, CAN_500KBPS, MCP_16MHZ) != CAN_OK)
{
Serial.println("OBD2 CAN Init FAILED!");
return;
}
OBD_CAN.setMode(MCP_NORMAL);
delay(100);
Serial.println("OBD2 CAN Init OK");
}
uint32_t Process_OBD2_CAN_Speed()
{
if (millis() - lastQueryTime < OBD2_QUERY_INTERVAL)
return 0;
lastQueryTime = millis();
// Anfrage: 01 0D → Geschwindigkeit
byte obdRequest[8] = {0x02, 0x01, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00};
byte sendStat = OBD_CAN.sendMsgBuf(OBD2_OBD_REQUEST_ID, 0, 8, obdRequest);
if (sendStat != CAN_OK)
{
MaintainDTC(DTC_OBD2_CAN_TIMEOUT, true);
Debug_pushMessage("OBD2_CAN: send failed (%d)\n", sendStat);
return 0;
}
unsigned long rxId;
byte len = 0;
byte rxBuf[8];
uint32_t timeout = millis() + 100;
while (millis() < timeout)
{
if (OBD_CAN.checkReceive() == CAN_MSGAVAIL)
{
OBD_CAN.readMsgBuf(&rxId, &len, rxBuf);
if ((rxId & 0xFFF8) == OBD2_OBD_RESPONSE_ID && rxBuf[1] == 0x0D)
{
MaintainDTC(DTC_OBD2_CAN_NO_RESPONSE, false); // alles ok
uint8_t speed_kmh = rxBuf[3];
uint32_t speed_mm_per_sec = (uint32_t)speed_kmh * 1000000 / 3600;
uint32_t dt = millis() - lastRecvTime;
lastRecvTime = millis();
lastSpeedMMperSec = speed_mm_per_sec;
Debug_pushMessage("OBD2_CAN: %d km/h (%lu mm/s)\n", speed_kmh, speed_mm_per_sec);
return (speed_mm_per_sec * dt) / 1000;
}
}
}
// Keine Antwort erhalten
MaintainDTC(DTC_OBD2_CAN_NO_RESPONSE, true);
Debug_pushMessage("OBD2_CAN: no response within timeout\n");
return 0;
}

View File

@ -0,0 +1,74 @@
#include "obd2_kline.h"
// === Konstante für Anfrageintervalle ===
#define OBD2_QUERY_INTERVAL 500 // alle 500 ms neue Anfrage
// === Private Variablen ===
static Stream *klineSerial = nullptr;
static uint32_t lastQueryTime = 0;
static uint32_t lastRecvTime = 0;
static uint32_t lastSpeedMMperSec = 0;
// === Slow Init nach ISO9141-2 ===
void OBD2_KLine_SlowInit()
{
pinMode(1, OUTPUT); // TXD-Pin (z.B. D1)
digitalWrite(1, HIGH);
delay(3000);
digitalWrite(1, LOW);
delay(200); // 200ms
digitalWrite(1, HIGH);
delay(400); // 400ms
digitalWrite(1, LOW);
delay(400); // 400ms
digitalWrite(1, HIGH);
delay(200); // 200ms
// zurück auf Serialbetrieb
pinMode(1, INPUT);
delay(300);
}
// === Initialisierung ===
void Init_OBD2_KLine(Stream &serial)
{
klineSerial = &serial;
OBD2_KLine_SlowInit();
delay(100);
// Sende 01 0D (Vehicle Speed)
byte speedRequest[] = {0x68, 0x6A, 0xF1, 0x01, 0x0D}; // OBD2 PID-Request
klineSerial->write(speedRequest, sizeof(speedRequest));
}
// === Geschwindigkeit abfragen ===
uint32_t Process_OBD2_KLine_Speed()
{
if (!klineSerial || (millis() - lastQueryTime < OBD2_QUERY_INTERVAL)) return 0;
byte req[] = {0x68, 0x6A, 0xF1, 0x01, 0x0D};
klineSerial->write(req, sizeof(req));
lastQueryTime = millis();
uint8_t buf[16];
size_t len = klineSerial->readBytes(buf, sizeof(buf));
for (size_t i = 0; i < len - 2; ++i)
{
if (buf[i] == 0x48 && buf[i + 1] == 0x6B && buf[i + 2] == 0x10)
{
if (buf[i + 3] == 0x41 && buf[i + 4] == 0x0D)
{
uint8_t speed_kmh = buf[i + 5];
uint32_t speed_mm_per_sec = (uint32_t)speed_kmh * 1000000 / 3600;
uint32_t dt = millis() - lastRecvTime;
lastRecvTime = millis();
lastSpeedMMperSec = speed_mm_per_sec;
return (speed_mm_per_sec * dt) / 1000;
}
}
}
return 0;
}