First post, and please go easy on me since I'm a hardware guy much more than a coder. I have a 1974 Challenger with a Hellcat crate engine and a Vintage Air A/C system. For a number of reasons there is no good way to get the ECM to natively control the A/C compressor on this system and it instead cycles entirely based off of the trinary switch. That works fine, but I have zero protection against excessive RPM or a WOT lockout like you'd have in a factory application and it'll even engage the compressor with the engine off if the A/C is turned on.
My plan is to have my Arduino Uno R3 installed such that it reads "LISTEN ONLY" the 500k CANBUS for the existing RPM and APS messages that I've already sniffed and decoded. It is also installed so that the ground output from the VA controller that would normally go to the A/C relay is wired to the Arduino to serve as an A/C Request input on pin 6.
Eventually, I want it to control the factory PWM fan, send a tach output signal, and maybe broadcast an ambient air temp message on the bus if I get really crazy. I've already built the Arduino in question with a Seeed V2.0 Can Shield, 12v to 5v DC-DC power supply, and octocouplers on the hardwired inputs to protect the device from the electrical system.
I'd like for it to allow A/C operation by grounding pin 7 (which goes to the relay) only when RPM is between 600 and 4500, TPS <80%, and it has been 4 seconds since the last actuation to have some hysteresis against a steady cruise or APS right around the lockout settings.
RPM is contained in message ID 0x7E8 in the A and B bits per standard OBD2 routines.
APS is a little weird; it is in message ID 0x22f but it is in the very first two bits of the message like:
07D595800843C. I have verified this during the CAN sniffing testing that I did and it does in fact mirror APS position on a scan tool. The APS OBD2 standard PID's are NOT being broadcast, and my Dakota Digital gauges use a CAN bus polling device so I don't want to mess with conflicting request messages bringing down the bus.
Could I get some input on my code and let me know if this looks like it'll work before I go waste time testing it? It compiled ok, but I'm not sure the CAN message coding is going to read the messages and convert into the actions correctly as it is written. I won't pretend that I wrote this all myself; I modified existing example sketch and internet searched code to get to where I am at right now. The CAN sniffing and decoding went a lot easier than I expected, but this is at the limit of my ability. Appreciate the help!
#include <SPI.h>
#include <mcp_can.h>
#define CAN_2515
// #define CAN_2518FD
// Set SPI CS Pin according to your hardware
#if defined(SEEED_WIO_TERMINAL) && defined(CAN_2518FD)
// For Wio Terminal w/ MCP2518FD RPi Hat:
// Channel 0 SPI_CS Pin: BCM 8
// Channel 1 SPI_CS Pin: BCM 7
// Interupt Pin: BCM25
const int SPI_CS_PIN = BCM8;
const int CAN_INT_PIN = BCM25;
#else
// For Arduino MCP2515 Hat:
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
#endif
#ifdef CAN_2518FD
#include "mcp2518fd_can.h"
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin
#endif
#ifdef CAN_2515
#include "mcp2515_can.h"
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin
#endif
const int OUTPUT_PIN = 7; // Output pin to control
const unsigned long cycleInterval = 4000; // 4 seconds
unsigned long lastCycleTime = 0;
void setup() {
pinMode(OUTPUT_PIN, OUTPUT);
pinMode(6, INPUT);
SERIAL_PORT_MONITOR.begin(115200);
while (CAN_OK != CAN.begin(CAN_500KBPS)) { // init can bus : baudrate = 500k
SERIAL_PORT_MONITOR.println("CAN init fail, retry...");
delay(100);
}
SERIAL_PORT_MONITOR.println("CAN init ok!");
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - lastCycleTime >= cycleInterval) {
lastCycleTime = currentMillis;
// Read CAN message
unsigned char len = 0;
unsigned char buf[8];
if (digitalRead(6) == LOW) {
if (CAN.checkReceive() == CAN_MSGAVAIL) {
CAN.readMsgBuf(&len, buf);
// Check if it's the Engine RPM message (0x7E8)
if (buf[1] == 0x08 && buf[2] == 0x01 && buf[3] == 0x0C && buf[4] == 0x00 && buf[5] == 0x00 && buf[6] == 0x00 && buf[7] == 0x00) {
// Extract RPM value
unsigned int rpm = ((unsigned int)buf[0] * 256 + buf[1]) / 4;
// Check conditions for Engine RPM and TPS
if (rpm >= 600 && rpm <= 4500) {
// Check for TPS message (0x22F)
if (CAN.checkReceive() == CAN_MSGAVAIL) {
CAN.readMsgBuf(&len, buf);
if (buf[0] == 0x22 && buf[1] == 0xF0) {
// Extract TPS value
unsigned int tps = (buf[2] & 0x03) * 100 / 3; // Get the first two bits
// Check TPS condition
if (tps < 80) {
digitalWrite(OUTPUT_PIN, HIGH); // Set output pin high
} else {
digitalWrite(OUTPUT_PIN, LOW); // Set output pin low
}
}
}
}
}
}
}
}
}