Created
April 12, 2024 05:31
-
-
Save oesmith/82a75ec3cf0ef888971456fe8fc461bb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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