Sketchhelpmyrealdash

Good morning. I created a sketch for my 1980 Suzuki GS550 (an inline-4 model) using a wasted-spark ignition system. Everything works, except for the engine RPM—the needle moves jerkily—and the speed isn’t being detected by the Hall sensor. I’ve attached the sketch and the XML file. Could someone tell me what I’m doing wrong? Thanks a lot.

sketch:#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// =========================================================================
// 1. MAPPATURA PIN (Invariata, specifica per la tua Suzuki GS 500 del 1977)
// =========================================================================
static const uint8_t PIN_OIL = A5;
static const uint8_t PIN_SPEED = 3; // Sensore Hall ruota (Interrupt 1)
static const uint8_t PIN_LOWBEAM = 4;
static const uint8_t PIN_PARKING = A4; // Spia luci di posizione
static const uint8_t PIN_HIGHBEAM = 5;
static const uint8_t PIN_LEFT = 6;
static const uint8_t PIN_RIGHT = 7;
static const uint8_t PIN_NEUTRAL = 8;

static const uint8_t PIN_GEAR_1 = 9;
static const uint8_t PIN_GEAR_2 = 10;
static const uint8_t PIN_GEAR_3 = 11;
static const uint8_t PIN_GEAR_4 = 12;
static const uint8_t PIN_GEAR_5 = A2;
static const uint8_t PIN_GEAR_6 = A0;

static const uint8_t PIN_RPM = 2; // Segnale bobina via optoisolatore (Interrupt 0)
static const uint8_t PIN_TEMP_BUS = A1; // Bus OneWire per DS18B20

// =========================================================================
// 2. CONFIGURAZIONI GENERALI
// =========================================================================
// 1.0f assume 1 impulso a giro (Scintilla persa su 4 cilindri con 2 bobine)
static const float RPM_PULSES_PER_REV = 1.0f;
static const uint16_t GEAR_DEBOUNCE_MS = 50;
static const uint16_t TEMP_CYCLE_MS = 1500; // Ciclo totale di gestione DS18B20

OneWire oneWire(PIN_TEMP_BUS);
DallasTemperature sensors(&oneWire);

// =========================================================================
// 3. VARIABILI VOLATILI PER INTERRUPT (PROTETTE)
// =========================================================================
volatile uint32_t lastRpmMicros = 0, durationRpmMicros = 0;
volatile bool newRpmPulse = false;
volatile uint32_t lastSpdMicros = 0, durationSpdMicros = 0;
volatile bool newSpdPulse = false;

// ISR Giri Motore (Filtro hardware/software integrato contro i rimbalzi dell’optoisolatore)
void onRpmPulse() {
uint32_t now = micros();
uint32_t diff = now - lastRpmMicros;
if (diff > 2500) { // Anti-rimbalzo hardware (Taglia spurie sopra i 24000 RPM)
durationRpmMicros = diff;
lastRpmMicros = now;
newRpmPulse = true;
}
}

// ISR Velocità Ruota
void onSpeedPulse() {
uint32_t now = micros();
uint32_t diff = now - lastSpdMicros;
if (diff > 5000) {
durationSpdMicros = diff;
lastSpdMicros = now;
newSpdPulse = true;
}
}

// =========================================================================
// 4. STRUTTURE DATI E VARIABILI DI STATO
// =========================================================================
struct Telemetry {
bool lowBeam, highBeam, oilWarn, left, right, neutralLamp, parkingLight;
int8_t gear;
int16_t tempWater, tempEngine;
uint16_t rpm, speed;
};

Telemetry t;
int8_t lastGearRaw = -1;
int8_t stableGear = -1;
uint32_t gearChangeMs = 0;
uint32_t lastTempCycleMs = 0;
uint8_t tempStep = 0;

static inline bool readActiveLow(uint8_t pin) { return digitalRead(pin) == LOW; }

// Lettura selettore marce a massa
int8_t readGear() {
if (readActiveLow(PIN_NEUTRAL)) return 0;
if (readActiveLow(PIN_GEAR_1)) return 1;
if (readActiveLow(PIN_GEAR_2)) return 2;
if (readActiveLow(PIN_GEAR_3)) return 3;
if (readActiveLow(PIN_GEAR_4)) return 4;
if (readActiveLow(PIN_GEAR_5)) return 5;
if (readActiveLow(PIN_GEAR_6)) return 6;
return -1;
}

// =========================================================================
// 5. FUNZIONI DI CALCOLO FLUIDE (SENZA BLOCCHI CRITICI)
// =========================================================================
uint16_t computeRpm() {
static float filteredRpm = 0;
uint32_t localLastRpmMicros;
uint32_t localDuration = 0;
bool localNewPulse;

// Sezione protetta: copia rapida delle variabili dell’interrupt
noInterrupts();
localLastRpmMicros = lastRpmMicros;
localNewPulse = newRpmPulse;
if (localNewPulse) {
localDuration = durationRpmMicros;
newRpmPulse = false;
}
interrupts();

// Se il motore è spento o si ferma (nessun impulso da 500ms), azzera i giri
if (micros() - localLastRpmMicros > 500000) {
filteredRpm = 0;
return 0;
}

if (localNewPulse && localDuration > 0) {
float raw = 60000000.0f / (localDuration * RPM_PULSES_PER_REV);
if (raw > 15000) raw = filteredRpm; // Protezione da picchi assurdi

// Filtro Passa-Basso ottimizzato per la fluidità (Reattività al 30%)
filteredRpm = (filteredRpm * 0.70f) + (raw * 0.30f); 

}
return (uint16_t)filteredRpm;
}

uint16_t computeSpeed() {
static float filteredSpeed = 0;
uint32_t localLastSpdMicros;
uint32_t localDuration = 0;
bool localNewPulse;

// Sezione protetta per la velocità
noInterrupts();
localLastSpdMicros = lastSpdMicros;
localNewPulse = newSpdPulse;
if (localNewPulse) {
localDuration = durationSpdMicros;
newSpdPulse = false;
}
interrupts();

// Timeout velocità (1 secondo senza impulsi = 0 km/h)
if (micros() - localLastSpdMicros > 1000000) {
filteredSpeed = 0;
return 0;
}

if (localNewPulse && localDuration > 0) {
float raw = (3870.0f * 1000.0f) / localDuration;
if (raw < 350.0f) { // Filtra letture palesemente errate in accelerazione
// Filtro Passa-Basso velocità (Reattività al 40%)
filteredSpeed = (filteredSpeed * 0.60f) + (raw * 0.40f);
}
}
return (uint16_t)filteredSpeed;
}

void sendRealDashFrames();

// =========================================================================
// 6. SETUP
// =========================================================================
void setup() {
// Configurazione seriale a 115200 baud (ideale per moduli Bluetooth HC-05/HC-06)
Serial.begin(115200);

// Inizializzazione Pin in Input con PullUp interna attivata
pinMode(PIN_OIL, INPUT_PULLUP);
pinMode(PIN_LOWBEAM, INPUT_PULLUP);
pinMode(PIN_PARKING, INPUT_PULLUP);
pinMode(PIN_HIGHBEAM, INPUT_PULLUP);
pinMode(PIN_LEFT, INPUT_PULLUP);
pinMode(PIN_RIGHT, INPUT_PULLUP);
pinMode(PIN_NEUTRAL, INPUT_PULLUP);

pinMode(PIN_GEAR_1, INPUT_PULLUP);
pinMode(PIN_GEAR_2, INPUT_PULLUP);
pinMode(PIN_GEAR_3, INPUT_PULLUP);
pinMode(PIN_GEAR_4, INPUT_PULLUP);
pinMode(PIN_GEAR_5, INPUT_PULLUP);
pinMode(PIN_GEAR_6, INPUT_PULLUP);

pinMode(PIN_RPM, INPUT_PULLUP);
pinMode(PIN_SPEED, INPUT_PULLUP);

// Configurazione sensori Dallas senza blocchi logici
sensors.begin();
sensors.setWaitForConversion(false);
sensors.requestTemperatures(); // Primo avvio della conversione

// Attivazione Interrupt hardware stabili
attachInterrupt(digitalPinToInterrupt(PIN_RPM), onRpmPulse, RISING);
attachInterrupt(digitalPinToInterrupt(PIN_SPEED), onSpeedPulse, FALLING);
}

// =========================================================================
// 7. LOOP PRINCIPALE (ASINCRONO E FLUIDO)
// =========================================================================
void loop() {
uint32_t now = millis();
static uint32_t lastDashUpdate = 0;

// 1. Lettura Spie digitali
t.lowBeam = readActiveLow(PIN_LOWBEAM);
t.parkingLight = readActiveLow(PIN_PARKING);
t.highBeam = readActiveLow(PIN_HIGHBEAM);
t.oilWarn = readActiveLow(PIN_OIL);
t.left = readActiveLow(PIN_LEFT);
t.right = readActiveLow(PIN_RIGHT);
t.neutralLamp = readActiveLow(PIN_NEUTRAL);

// 2. Debounce e lettura Marce digitale
int8_t g = readGear();
if (g != lastGearRaw) {
lastGearRaw = g;
gearChangeMs = now;
}
if ((now - gearChangeMs) >= GEAR_DEBOUNCE_MS) {
stableGear = lastGearRaw;
}
t.gear = stableGear;

// 3. Macchina a Stati Finiti per lettura sensori di temperatura (NO BLOCCHI)
if (now - lastTempCycleMs >= TEMP_CYCLE_MS) {
if (tempStep == 0) {
float tw = sensors.getTempCByIndex(0);
if (tw > -55.0f && tw < 150.0f) {
t.tempWater = (int16_t)(tw * 10.0f);
}
tempStep = 1;
// Forza l’esecuzione del prossimo step (Sensore 2) tra soli 200ms
lastTempCycleMs = now - (TEMP_CYCLE_MS - 200);
}
else if (tempStep == 1) {
float te = sensors.getTempCByIndex(1);
if (te > -55.0f && te < 150.0f) {
t.tempEngine = (int16_t)(te * 10.0f);
}
sensors.requestTemperatures(); // Avvia la conversione asincrona in background
tempStep = 0;
lastTempCycleMs = now; // Pausa totale fino al prossimo ciclo esteso
}
}

// 4. Calcolo frequenze (Giri e Velocità)
t.rpm = computeRpm();
t.speed = computeSpeed();

// 5. Invio dati seriale/Bluetooth fisso a 50Hz (Ogni 20 millisecondi precisi)
if (now - lastDashUpdate >= 20) {
sendRealDashFrames();
lastDashUpdate = now;
}
}

// =========================================================================
// 8. INVIO FRAME CAN-STREAM SU SERIALE PER REALDASH
// =========================================================================
void sendRealDashFrames() {
// Header fisso RealDash CAN (DASH in caratteri ASCII)
static const byte header = { 0x44, 0x41, 0x53, 0x48 };

// ----- FRAME 0x100 (Spie e Marce) -----
uint8_t f100[8] = {0};
if (t.lowBeam) f100[0] |= (1 << 0);
if (t.highBeam) f100[0] |= (1 << 1);
if (t.oilWarn) f100[0] |= (1 << 2);
if (t.neutralLamp) f100[0] |= (1 << 3);
if (t.left) f100[0] |= (1 << 4);
if (t.right) f100[0] |= (1 << 5);
if (t.parkingLight) f100[0] |= (1 << 6);
f100[1] = (t.gear == -1) ? 255 : (uint8_t)t.gear;

Serial.write(header, 4);
uint32_t id100 = 0x100;
Serial.write((byte*)&id100, 4);
Serial.write(f100, 8);

// ----- FRAME 0x101 (RPM, Temperature, Velocità) -----
uint8_t f101[8] = {0};
memcpy(&f101[0], &t.rpm, 2);
memcpy(&f101[2], &t.tempWater, 2);
memcpy(&f101[4], &t.tempEngine, 2);
memcpy(&f101[6], &t.speed, 2);

Serial.write(header, 4);
uint32_t id101 = 0x101;
Serial.write((byte*)&id101, 4);
Serial.write(f101, 8);
}
Xml:<?xml version="1.0" encoding="utf-8"?>

<frame id="0x100" endianess="little">
  <!-- Spie Bitmask (Offset 0) -->
  <value name="LowBeam"      targetId="116" offset="0" length="1" bit="0"></value>
  <value name="HighBeam"     targetId="117" offset="0" length="1" bit="1"></value>
  <value name="OilWarning"   targetId="151" offset="0" length="1" bit="2"></value>
  <value name="NeutralLamp"  targetId="156" offset="0" length="1" bit="3"></value>
  <value name="LeftArrow"    targetId="104" offset="0" length="1" bit="4"></value> <!-- Corretto TargetId a 104 -->
  <value name="RightArrow"   targetId="105" offset="0" length="1" bit="5"></value> <!-- Corretto TargetId a 105 -->
  <value name="ParkingLight" targetId="115" offset="0" length="1" bit="6"></value>

  <!-- Marcia Corrente (Offset 1) -->
  <value name="Gear"         targetId="34"  offset="1" length="1"></value>
</frame>

<frame id="0x101" endianess="little">
  <!-- Giri Motore -->
  <value name="RPM"          targetId="37"  units="RPM"     offset="0" length="2"></value>
  
  <!-- Temperatura Acqua (V/10 divide per 10 il valore ricevuto dall'Arduino) -->
  <value name="CoolantTemp"  targetId="14"  units="Celsius" offset="2" length="2" conversion="V/10"></value>
  
  <!-- Temperatura Testa/Motore -->
  <value name="EngineTemp"   targetId="15"  units="Celsius" offset="4" length="2" conversion="V/10"></value>
  
  <!-- Velocità Ruota -->
  <value name="WheelSpeed"   targetId="81"  units="km/h"    offset="6" length="2"></value>
</frame>