Files
Carputer-cracker/src/modules/can.cpp

112 lines
3.7 KiB
C++

#include "../core/module.h"
#include "../core/ui.h"
#include "../core/pins.h"
#include <SPI.h>
#include <SD.h>
// CAN Bus sniffer. Listen on MCP2515 over SPI for OBD/automotive frames.
// Passive only; displays frame ID, DLC, data; logs to SD.
class CanSniff : public Module {
static constexpr int CS = pins::PROBE[0];
bool running = false, sdOk = false;
uint32_t fcount = 0, lastMs = 0;
uint8_t speed = 0; // 0=500k, 1=250k, 2=125k
char msg[3][40] = {{0},{0},{0}};
File logfile;
public:
const char* name() const override { return "CAN Bus"; }
const char* blurb() const override { return "OBD/automotive sniffer"; }
void onEnter() override {
running = false; fcount = 0;
sdOk = SD.begin();
initMcp2515();
}
void onExit() override { running = false; if (logfile) logfile.close(); }
bool onKey(char c) override {
if (c == ' ') { running = !running; if (running) fcount = 0; return true; }
if (c == 's') { speed = (speed + 1) % 3; initMcp2515(); return true; }
if (c == 'c') { fcount = 0; say("cleared"); return true; }
return false;
}
void tick() override {
if (!running) return;
if (millis() - lastMs < 50) return;
lastMs = millis();
uint32_t id = 0; uint8_t dlc = 0, data[8] = {0};
if (readFrame(id, dlc, data)) {
fcount++;
if (sdOk && !logfile) {
SD.mkdir("/logs");
logfile = SD.open("/logs/can.txt", FILE_APPEND);
}
if (logfile) logfile.printf("%08lX %d [", (unsigned long)id, dlc);
for (int i = 0; i < dlc; i++) {
if (logfile) logfile.printf("%02X ", data[i]);
}
if (logfile) logfile.println("]");
}
}
void draw() override {
const char* sp[] = {"500k", "250k", "125k"};
ui::lineC(0, ui::accent(), "CAN %s %s", sp[speed], running ? "LISTEN" : "idle");
ui::line(1, "frames: %lu", (unsigned long)fcount);
for (int i = 0; i < 3; i++) ui::line(3 + i, "%s", msg[i]);
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
ui::hintBar("[s]peed [c]lear [space]go [`]back");
}
private:
void say(const char* fmt, ...) {
for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39);
va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap);
}
void initMcp2515() {
SPI.begin(pins::PROBE[3], pins::PROBE[4], pins::PROBE[5], CS);
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
digitalWrite(CS, LOW); SPI.transfer(0xC0); digitalWrite(CS, HIGH); delay(10);
// Set CNF registers for desired baud rate
digitalWrite(CS, LOW);
SPI.transfer(0x80); // Write instruction
SPI.transfer(0x2A); // CNF1 address
if (speed == 0) { SPI.transfer(0x00); SPI.transfer(0xA3); SPI.transfer(0x13); } // 500k
else if (speed == 1) { SPI.transfer(0x00); SPI.transfer(0xA3); SPI.transfer(0x25); } // 250k
else { SPI.transfer(0x00); SPI.transfer(0xA3); SPI.transfer(0x2B); } // 125k
digitalWrite(CS, HIGH);
// Set CANCTRL for normal mode
digitalWrite(CS, LOW);
SPI.transfer(0x80);
SPI.transfer(0x0F); // CANCTRL address
SPI.transfer(0x00); // Normal mode
digitalWrite(CS, HIGH);
say("CAN %s mode ok", speed == 0 ? "500k" : speed == 1 ? "250k" : "125k");
}
bool readFrame(uint32_t& id, uint8_t& dlc, uint8_t* data) {
digitalWrite(CS, LOW);
SPI.transfer(0x03); // Read RX0 buffer status/id
uint8_t sidh = SPI.transfer(0);
uint8_t sidl = SPI.transfer(0);
uint8_t eid8 = SPI.transfer(0);
uint8_t eid0 = SPI.transfer(0);
dlc = SPI.transfer(0) & 0x0F;
for (int i = 0; i < dlc && i < 8; i++) data[i] = SPI.transfer(0);
digitalWrite(CS, HIGH);
id = ((uint32_t)sidh << 3) | ((sidl >> 5) & 0x07);
return dlc > 0;
}
};
Module* makeCan() { return new CanSniff(); }