Skip to content

Instantly share code, notes, and snippets.

@oesmith
Created April 12, 2024 05:31
Show Gist options
  • Save oesmith/82a75ec3cf0ef888971456fe8fc461bb to your computer and use it in GitHub Desktop.
Save oesmith/82a75ec3cf0ef888971456fe8fc461bb to your computer and use it in GitHub Desktop.
#include <Wire.h>
#include <ACAN2040.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// NeoPixel strip, for showing shift lights.
//
#define NEOPIXEL_PIN 15
#define NEOPIXEL_COUNT 8
#define NEOPIXEL_INTERVAL 50
uint32_t neopixel_ts = 0;
// const uint16_t RPM_THRESH[] = { 4000, 4500, 5000, 5500 };
const uint16_t RPM_THRESH[] = { 1000, 1300, 1600, 1900 };
const uint32_t RPM_COLORS[] = { 0xff00, 0xff00, 0xffff00, 0xff0000 };
const uint16_t RPM_FLASH = 2200; // 6000;
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
// OLED display, for displaying diagnostics.
//
#define OLED_WIDTH 128
#define OLED_HEIGHT 32
#define OLED_RESET -1
#define OLED_ADDRESS 0x3c
#define OLED_INTERVAL 100
uint32_t oled_ts = 0;
Adafruit_SSD1306 oled(OLED_WIDTH, OLED_HEIGHT, &Wire, OLED_RESET);
// CanBus interface, for reading car diagnostic data.
//
const uint8_t PIONUM0 = 0;
const uint8_t TXPIN0 = 17;
const uint8_t RXPIN0 = 16;
const uint32_t BITRATE0 = 500000UL;
const uint32_t SYSCLK = F_CPU;
void event_handler(struct can2040 *cd, uint32_t notify, struct can2040_msg *msg);
ACAN2040 can2040(PIONUM0, TXPIN0, RXPIN0, BITRATE0, SYSCLK, event_handler);
bool got_msg = false;
struct can2040_msg rx_msg;
struct can2040_stats stats;
uint32_t stats_ts = 0;
#define STATS_INTERVAL 1000
// MBE ECU
//
#define MBE_ID_EASIMAP 0xcbe1101lu
#define MBE_ID_ECU 0xcbe0111lu
struct can2040_msg tx_msg_part1 = {
.id = (CAN2040_ID_EFF | MBE_ID_EASIMAP),
.dlc = 8,
.data = { 0x10, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0xf8 },
};
struct can2040_msg tx_msg_part2 = {
.id = (CAN2040_ID_EFF | MBE_ID_EASIMAP),
.dlc = 8,
// RPM - ( 0x7d, 0x7c )
// Coolant temp - ( 0x45, 0x44 )
// Voltage - ( 0x9f, 0x9e )
.data = { 0x21, 0x7d, 0x7c, 0x45, 0x44, 0x9f, 0x9e },
};
uint16_t rpm = 0;
float temp = 0;
float volts = 0;
uint32_t sent_ts = 1;
uint32_t recv_ts = 0;
#define POLL_INTERVAL 50
#define POLL_TIMEOUT 500
//
void error_state(uint8_t index);
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
can2040.begin();
pixels.begin();
pixels.setBrightness(5);
if (!oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
error_state(0);
}
can2040.get_statistics(&stats);
}
void loop() {
if (got_msg) {
got_msg = false;
if (rx_msg.id == (CAN2040_ID_EFF | MBE_ID_ECU) && rx_msg.data[1] == 0x81) {
rpm = 0x100 * rx_msg.data[2] + rx_msg.data[3];
temp = (0x100 * rx_msg.data[4] + rx_msg.data[5]) * 160.0f / 65535.0f - 30.0f;
volts = (0x100 * rx_msg.data[6] + rx_msg.data[7]) * 20.0f / 65535.0f;
}
recv_ts = millis();
}
if (millis() > stats_ts + STATS_INTERVAL) {
can2040.get_statistics(&stats);
stats_ts = millis();
}
if (recv_ts >= sent_ts
&& millis() > sent_ts + POLL_INTERVAL
|| millis() > sent_ts + POLL_TIMEOUT) {
if (can2040.ok_to_send()) {
can2040.send_message(&tx_msg_part1);
can2040.send_message(&tx_msg_part2);
}
sent_ts = millis();
}
if (millis() > oled_ts + OLED_INTERVAL) {
oled.clearDisplay();
oled.setTextSize(1); // Normal 1:1 pixel scale
oled.setTextColor(SSD1306_WHITE); // Draw white text
oled.setCursor(0,0); // Start at top-left corner
oled.print(F("R:"));
oled.println(rpm, DEC);
oled.print(F("T:"));
oled.print(temp, 1);
oled.print(F(" V:"));
oled.println(volts, 1);
oled.display();
oled_ts = millis();
}
if (millis() > neopixel_ts + NEOPIXEL_INTERVAL) {
pixels.clear();
if (rpm < RPM_FLASH) {
for (int i = 0; i < 4; i++) {
if (rpm >= RPM_THRESH[i]) {
pixels.setPixelColor(i, RPM_COLORS[i]);
pixels.setPixelColor(NEOPIXEL_COUNT - i - 1, RPM_COLORS[i]);
}
}
} else {
uint32_t t = millis();
if ((t / 100) % 2 == 0) {
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
pixels.setPixelColor(i, RPM_COLORS[3]);
}
}
}
pixels.show();
neopixel_ts = millis();
}
}
void error_state(uint8_t index) {
for(;;) {
pixels.clear();
pixels.setPixelColor(index, 0xff0000);
pixels.show();
delay(500);
pixels.clear();
pixels.show();
delay(500);
}
}
void event_handler(struct can2040 *cd, uint32_t notify, struct can2040_msg *msg) {
(void)(cd);
if (notify == CAN2040_NOTIFY_RX) {
rx_msg = *msg;
got_msg = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment