May 2023
The 2x pressure sensors for the air intake and outlet on the diesel engine aftercooler are 0 to 30 psig / 5V senders.
CAN Tx device attached to the pressure sensors:
/*
Adafruit Feather M4 CAN (SAMD51)
In Arduino IDE, set board to 'Adafruit Feather M4 Express (SAMD51)'
If bootloading frozen, click RST button twice quickly.
The red LED will pulse and the RGB LED will be green when you are
in bootloader mode.
NeoPixel = green if OK, RED on USB failure.
The yellow “charging” LED flickers constantly whenever the Feather is powered by USB
Arduino C++ code or CircuitPython
6x analog input pins: A0,A1,A2,A3,A4,A5 (0 to 3.3V)
WARNING: The Arduino IDE serial monitor causes setup() to wait until
the serial monitor IDE is run.
In Arduino IDE:
Set the board to: "Adafruit Feather M4 CAN (SAME51)"
*/
/////////////////////////////////////////////////////////////////////////////\/
// Show serial messages when DEBUG = true, otherwise minimize them.
#define DEBUG false
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// Built in LED(s)
const uint8_t pinBuiltInLED = 13;
const byte pinLiPoly = A6; // #20
/////////////////////////////////////////////////////////////////////////
// Built-in NeoPixel (RGB LED)
//
// set it up as a single-LED strand on pin 8
const uint8_t pinBuiltInNeoPixel = 8;
#include <Adafruit_NeoPixel.h>
// https://github.com/adafruit/Adafruit_NeoPixel
#define NUMPIXELS 1
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, pinBuiltInNeoPixel, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
/////////////////////////////////////////////////////////////////////////
// CAN bus
// Install library: https://github.com/adafruit/arduino-CAN
// arduino-CAN-master.zip
// Examples -> CAN Adafruit Fork ->
#include <CAN.h>
unsigned char canMsg[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// These functions are written around the concept of using at
// least two bytes to represent a float or integer value.
// This allows you to send up to four separate float or integer
// values within a CAN message.
void updateCanMsgFromFloat(unsigned char *CANmsg, float floatVal, int startBit, int length, int msgOffset, float factor) {
// Update msg[8] within CANmsg[] with the passed parameters
// Assumes length = 16;
// unsigned int floatToIntCANbus(float myFloat, int msgOffset, float factor) {
unsigned int myInt = floatToIntCANbus(floatVal, msgOffset, factor);
byte msb = getMsbFromInt(myInt);
byte lsb = getLsbFromInt(myInt);
switch (startBit) {
case 0:
CANmsg[0] = msb;
CANmsg[1] = lsb;
break;
case 16:
CANmsg[2] = msb;
CANmsg[3] = lsb;
break;
case 32:
CANmsg[4] = msb;
CANmsg[5] = lsb;
break;
case 48:
CANmsg[6] = msb;
CANmsg[7] = lsb;
break;
}
}
unsigned int floatToIntCANbus(float myFloat, int msgOffset, float factor) {
// float myFloat = 2128.5;
// unsigned int myInt = floatToIntCANbus(myFloat, 0, 0.125);
//
// Beginning with float of 2128.5, convert to CAN signal
// values.
// (int val) = ((float val) - msgOffset) / factor;
// (int val) = ((2128.5) - 0.0) / 0.125;
// (int val) = 17028
myFloat = myFloat - (float)msgOffset;
myFloat = myFloat / factor;
unsigned int myInt = (unsigned int) myFloat;
return myInt;
}
byte getMsbFromInt(int myInt) {
// int myInt = 17028;
// byte msb = getMsbFromInt(myInt);
byte msb = myInt & 0xff;
return msb;
}
byte getLsbFromInt(int myInt) {
// int myInt = 17028;
// byte lsb = getLsbFromInt(myInt);
byte lsb = (myInt >>8) & 0xff;
return lsb;
}
//////////////////////////////////////////////////////////////////////////////
// TimerA
const unsigned long timerA = 5000;
unsigned long timerAlap = millis(); // timer
/////////////////////////////////////////////////////////////////////////
// pressure sensors
#define PIN_TRANS A0
#define PIN_CAC_IN A1
#define PIN_CAC_OUT A2
uint32_t trans_press_adc = 0;
uint32_t eng_cac_in_adc = 0;
uint32_t eng_cac_out_adc = 0;
uint32_t samples = 0;
/////////////////////////////////////////////////////////////////////////
void setup() {
// For ATSAMD21 and ATSAMD51:
// ARef pin, use analogReference(AR_EXTERNAL)
// Pin with pullup:
// Use: pinMode(pin, INPUT_PULLUP)
// NOT: pinMode(pin, INPUT)
// digitalWrite(pin, HIGH)
#if DEBUG
Serial.begin(115200);
while (!Serial) {
delay(1);
}
Serial.println("\nSerial ready");
#endif
pinMode(PIN_TRANS, INPUT);
pinMode(PIN_CAC_IN, INPUT);
pinMode(PIN_CAC_OUT, INPUT);
//Below is not necessary
//pinMode(pinBuiltInLED, OUTPUT);
//digitalWrite(pinBuiltInLED, LOW);
// Initialize the built-in NeoPixel (initially set to green)
pixels.begin();
pixels.show();
// turn on both the transceiver and booster:
pinMode(PIN_CAN_STANDBY, OUTPUT); // pin #40
digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
pinMode(PIN_CAN_BOOSTEN, OUTPUT); // #41
digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster
// start the CAN bus at 250 kbps
if (!CAN.begin(250E3)) {
#if DEBUG
Serial.println("CAN init failed!");
#endif
pixels.setPixelColor(0, 255, 0, 0, 4); // red
pixels.show();
while (1);
}
#if DEBUG
Serial.println("\nSetup complete\n");
#endif
} // setup()
void loop() {
// Continuously read the voltage as an ADC value on pins
// PIN_TRANS, PIN_CAC_IN, PIN_CAC_OUT and
// add them to trans_press_adc, eng_cac_in_adc, eng_cac_out_adc.
// Update 'samples' as well, so the average value can be
// calculated within the publishing timer.
//
// Feather M4 CAN SAME51 analog pins are 12-bit (0 to 4095)
// BUT for some reason the range is only 0 to 1023.
// AREF is tied to 3.3V due to a silicon v0 bug.
uint32_t max_samples = 4294967295 / 1023;
//Serial.print("max samples: "); Serial.println(max_samples); // 4198404
if (samples >= max_samples) {
trans_press_adc = 0;
eng_cac_in_adc = 0;
eng_cac_out_adc = 0;
samples = 0;
#if DEBUG
Serial.println("max_samples exceeded!");
#endif
}
uint16_t adc = analogRead(PIN_TRANS); // 0 to 1023
trans_press_adc += adc;
adc = analogRead(PIN_CAC_IN); // 0 to 1023
eng_cac_in_adc += adc;
adc = analogRead(PIN_CAC_OUT); // 0 to 1023
eng_cac_out_adc += adc;
samples++;
// Timer A
if (timerAlap > millis()) timerAlap = millis();
if (millis() - timerAlap > timerA) {
pixels.setPixelColor(0, 0, 0, 255, 4); // blue
pixels.show();
// clear out CANmsg[]
for (int i=0; i<8; i++) {
canMsg[i] = 0;
}
// Send a CAN msg with 11 bit ID and 8 bytes of data
CAN.beginPacket(0x91); // 0x91 = 145
// updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor)
// transmission pressure 0 to 400 psi
float f = (float)trans_press_adc / float(samples) * (3.3/1023.0);
// PSI = (VDC) * 167.364 - 36.82
f = f * 167.364 - 36.82;
updateCanMsgFromFloat(canMsg, f, 0, 16, 0, 0.1);
// CAC in [psi] PSIG = (VDC) * 10.24 - 3.788
f = (float)eng_cac_in_adc / float(samples) * (3.3/1023.0);
f = f * 10.24 - 3.788;
updateCanMsgFromFloat(canMsg, f, 16, 16, 0, 0.01);
// CAC out [psi] PSIG = (VDC) * 10.24 - 3.788
f = (float)eng_cac_out_adc / float(samples) * (3.3/1023.0);
f = f * 10.24 - 3.788;
updateCanMsgFromFloat(canMsg, f, 32, 16, 0, 0.01);
// CAC delta [psi] PSIG = (VDC) * 10.24 - 3.788
f = ((float)eng_cac_out_adc - (float)eng_cac_in_adc) / float(samples) * ((3.3/1023.0));
f = f * 10.24 - 3.788;
updateCanMsgFromFloat(canMsg, f, 48, 16, 0, 0.01);
trans_press_adc = 0;
eng_cac_in_adc = 0;
eng_cac_out_adc = 0;
samples = 0;
for (int i = 0; i < 8; i++) {
CAN.write(canMsg[i]);
}
CAN.endPacket();
#if DEBUG
Serial.print("Sending to ID 0x91");
Serial.print("\t");
for (int i = 0; i < 8; i++) {
Serial.print(canMsg[i], HEX);
Serial.print("\t");
}
Serial.println("\n");
#endif
pixels.setPixelColor(0, 0, 0, 0, 4); // black
pixels.show();
timerAlap = millis(); // reset the timer
}
} // loop()
CAN bus Rx device that displays the pressure values on an OLED:
/*
Adafruit Feather M4 CAN (SAMD51)
Adafruit 128x32 mono OLED FeatherWing
In Arduino IDE, set board to 'Adafruit Feather M4 Express (SAMD51)'
If bootloading frozen, click RST button twice quickly.
The red LED will pulse and the RGB LED will be green when you are
in bootloader mode.
NeoPixel = green if OK, RED on USB failure.
The yellow “charging” LED flickers constantly whenever the Feather is powered by USB
Arduino C++ code or CircuitPython
6x analog input pins: A0,A1,A2,A3,A4,A5 (0 to 3.3V)
WARNING: The Arduino IDE serial monitor causes setup() to wait until
the serial monitor IDE is run.
In Arduino IDE:
Set the board to: "Adafruit Feather M4 CAN (SAME51)"
*/
/////////////////////////////////////////////////////////////////////////////\/
// Show serial messages when DEBUG = true, otherwise minimize them.
#define DEBUG false
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// Built in LED(s)
const uint8_t pinBuiltInLED = 13;
const byte pinLiPoly = A6; // #20
/////////////////////////////////////////////////////////////////////////
// Built-in NeoPixel (RGB LED)
//
// set it up as a single-LED strand on pin 8
const uint8_t pinBuiltInNeoPixel = 8;
#include <Adafruit_NeoPixel.h>
// https://github.com/adafruit/Adafruit_NeoPixel
#define NUMPIXELS 1
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, pinBuiltInNeoPixel, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
/////////////////////////////////////////////////////////////////////////
// CAN bus
// Install library: https://github.com/adafruit/arduino-CAN
// arduino-CAN-master.zip
// Examples -> CAN Adafruit Fork ->
#include <CAN.h>
float getFloatFromCanMsg(unsigned char *CANmsg, int startBit, int msgLen, int msgOffset, float factor) {
// Read the data from msg[8] within CANmsg[]
// convert it with the passed parameters, and return a float value.
// Assumes msgLen = 16
byte msb;
byte lsb;
switch (startBit) {
case 0:
msb = CANmsg[0];
lsb = CANmsg[1];
break;
case 16:
msb = CANmsg[2];
lsb = CANmsg[3];
break;
case 32:
msb = CANmsg[4];
lsb = CANmsg[5];
break;
case 48:
msb = CANmsg[6];
lsb = CANmsg[7];
break;
}
int myInt = (lsb << 8) | msb;
// float CANbusIntToFloat(unsigned int myInt, int msgOffset, float factor) {
float myFloat = CANbusIntToFloat(myInt, msgOffset, factor);
return myFloat;
}
float CANbusIntToFloat(unsigned int myInt, int msgOffset, float factor) {
// value in decimal = (CAN DEC value) * factor + offset
// 17500 * 0.125 + 0 = 2187.5 rpm
float myFloat = (float) myInt * factor + (float) msgOffset;
return myFloat;
}
/////////////////////////////////////////////////////////////////////////
// Adafruit 128x32 mono OLED FeatherWing
#include "SPI.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Configure the buttons, based on the microcontroller core
#define BUTTON_A 9
#define BUTTON_B 6
#define BUTTON_C 5
#define LED 13
#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
//Adafruit_SSD1306 display = Adafruit_SSD1306();
/////////////////////////////////////////////////////////////////////////
uint8_t last_val = 0;
void setup() {
// For ATSAMD21 and ATSAMD51:
// ARef pin, use analogReference(AR_EXTERNAL)
// Pin with pullup:
// Use: pinMode(pin, INPUT_PULLUP)
// NOT: pinMode(pin, INPUT)
// digitalWrite(pin, HIGH)
#if DEBUG
Serial.begin(115200);
while (!Serial) {
delay(1);
}
Serial.println("\nSerial ready");
#endif
// Initialize the built-in NeoPixel (initially set to green)
pixels.begin();
pixels.show();
// initialize with the I2C addr 0x3C (for the 128x32)
// The OLED I2C address is 0x3C and cannot be changed
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
#if DEBUG
Serial.println(F("SSD1306 allocation failed"));
#endif
while(true) {
pixels.setPixelColor(0, 255, 0, 0, 4); // red
pixels.show();
delay(500);
pixels.setPixelColor(0, 255, 255, 255, 4); // white
pixels.show();
delay(500);
}
for(;;); // Don't proceed, loop forever
}
// Configure the three buttons on the OLED FeatherWing
// BUTTON_A: 9, BUTTON_B: 6, BUTTON_C: 5
pinMode(BUTTON_B, INPUT_PULLUP);
pinMode(BUTTON_C, INPUT_PULLUP);
// Clear the display buffer.
display.clearDisplay();
//display.setTextColor(); BLACK,BLUE,RED,GREEN,CYAN,MAGENTA,YELLOW,WHITE
//display.setTextColor(text color, background color);
//Sets the text color and background color the text will print on.
//For monochrome (single-color) displays, colors are always specified as simply 1 (set) or 0 (clear).
display.setTextColor(WHITE);
// .setTextSize(2) will change the character sizes as follows:
// 2*6 x 2*8 = 12x16 pixels.
// For a 128x32 OLED, 128/12 = 10 chars wide; 32/16 = 2 characters tall
display.setTextSize(2);
display.setCursor(0,0);
//display.print("0123456789");
display.print("Knot");
//
display.setCursor(0,16);
//display.print("0123456789");
display.print(" Workin");
display.display();
//Below is not necessary
//pinMode(pinBuiltInLED, OUTPUT);
//digitalWrite(pinBuiltInLED, LOW);
// turn on both the transceiver and booster:
//Serial.print("PIN_CAN_STANDBY: "); Serial.println(PIN_CAN_STANDBY);
pinMode(PIN_CAN_STANDBY, OUTPUT); // pin #40
digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
//Serial.print("PIN_CAN_BOOSTEN: "); Serial.println(PIN_CAN_BOOSTEN);
pinMode(PIN_CAN_BOOSTEN, OUTPUT); // #41
digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster
// start the CAN bus at 250 kbps
if (!CAN.begin(250E3)) {
#if DEBUG
Serial.println("CAN init failed!");
#endif
pixels.setPixelColor(0, 255, 0, 0, 4); // red
pixels.show();
while (1);
}
#if DEBUG
Serial.println("\nSetup complete\n");
#endif
} // setup()
void loop() {
int packetSize = CAN.parsePacket();
if (packetSize) {
pixels.setPixelColor(0, 0, 0, 255, 4); // blue
pixels.show();
#if DEBUG
// received a packet
Serial.print("Received ");
if (CAN.packetExtended()) {
Serial.print("ext 29-bit ");
} else {
Serial.print("std 11-bit ");
}
if (CAN.packetRtr()) {
// Remote transmission request (RTR), packet contains no data
Serial.print("RTR ");
}
Serial.print("packet with id 0x");
Serial.print(CAN.packetId(), HEX);
#endif
unsigned char buf[8];
if (CAN.packetRtr()) {
#if DEBUG
Serial.print(" and requested length ");
Serial.println(CAN.packetDlc());
#endif
} else {
#if DEBUG
Serial.print(" and length ");
Serial.println(packetSize);
#endif
// Get the data for non-RTR packets
int i = 0;
while (CAN.available()) {
buf[i] = (char)CAN.read();
i++;
}
// Decode the CAN message if the ID is recognized and it is not an extended packet.
float val1 = 0.0; float val2 = 0.0; float val3=0.0; float val4=0.0;
switch (CAN.packetId()){
case 0x91:
// transmission pressure 0 to 400 psi
//msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
val1 = getFloatFromCanMsg(buf, 0, 16, 0, 0.1);
// CAC in [psi]
val2 = getFloatFromCanMsg(buf, 16, 16, 0, 0.01);
// CAC out [psi]
val3 = getFloatFromCanMsg(buf, 32, 16, 0, 0.01);
// CAC delta [psi]
val4 = getFloatFromCanMsg(buf, 48, 16, 0, 0.01);
#if DEBUG
Serial.print("\tTrans [psi]: "); Serial.println(val1, 1);
Serial.print("\tCAC in [psi]: "); Serial.println(val2, 1);
Serial.print("\tCAC out [psi]: "); Serial.println(val3, 1);
Serial.print("\tCAC delta [psi]: "); Serial.println(val4, 1);
#endif
switch (last_val) {
case 0:
last_val = 1;
display.clearDisplay();
display.setCursor(0,0);
//display.print("0123456789");
display.print("Trans Oil");
display.setCursor(0,16);
display.print(val1,1);
display.print(" psi");
display.display();
break;
case 1:
last_val = 2;
display.clearDisplay();
display.setCursor(0,0);
//display.print("0123456789");
display.print("CAC IN");
display.setCursor(0,16);
display.print(val2,1);
display.print(" psi");
display.display();
break;
case 2:
last_val = 3;
display.clearDisplay();
display.setCursor(0,0);
//display.print("0123456789");
display.print("CAC OUT");
display.setCursor(0,16);
display.print(val3,1);
display.print(" psi");
display.display();
break;
case 3:
last_val = 0;
display.clearDisplay();
display.setCursor(0,0);
//display.print("0123456789");
display.print("CAC Delta");
display.setCursor(0,16);
display.print(val4,1);
display.print(" psi");
display.display();
break;
} // switch (last_val)
break;
default:
#if DEBUG
Serial.print("Packet raw contents: '");
for (i = 0; i<8; i++) {
Serial.print("0x");
Serial.print(buf[i], HEX);
Serial.print("\t");
}
Serial.println("");
#endif
break;
} // switch()
}
#if DEBUG
Serial.println();
#endif
pixels.setPixelColor(0, 0, 0, 0, 4); // black
pixels.show();
} // packetSize
} // loop()
Do you need help developing or customizing a IoT product for your needs? Send me an email requesting a free one hour phone / web share consultation.
The information presented on this website is for the author's use only. Use of this information by anyone other than the author is offered as guidelines and non-professional advice only. No liability is assumed by the author or this web site.