Skip to content

Instantly share code, notes, and snippets.

@macchina
Created October 11, 2016 23:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save macchina/fc4bdfe5ecce844d2a79a3dc7fdebd43 to your computer and use it in GitHub Desktop.
Save macchina/fc4bdfe5ecce844d2a79a3dc7fdebd43 to your computer and use it in GitHub Desktop.
/*
Heavily modified from below code - works with NEOPIXEL 24 pos ring and M2 hardware.
Program Name: blueShift
Version: v0.7
Author: Pete Mills
Website: petemills.blogspot.com
Email: mills.pete@gmail.com
License: CC BY-NC-SA 3.0 ( Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported )
Description:
This program intends to read automotive engine parameters over a bluetooth connection from a Ford OpenXC VI.
This OpenXC VI is called an API to OBDII Data, where parameters such as engine speed, gearbox position &c are sent.
This program parses these bluetooth serial data and displays them on an array or arrangement of individually
addressable RGB LED's ( WS2812 ).
Presently RPM is displayed in a radial pattern, increasing clockwise.
Bugs/ToDo:
investigate proper json parsing
exit btWaitThrobber( ) when a connection is made, not a timeout
display dimming based on headlamp Status message only occurs after an RPM "redraw" of the display - resolved with a kludge
during power-on the transition from btWaitThrobber() to normal run causes all of the LEDs to go off - resolved
make the last led "sticky" for a time period such as a "tell tale" or peak hold indicator - implemented
make the ring draw operation a function - implemented
make the last LED Red - implemented
delete functions used for grid display, will use them for later versions of blueShift - complete
add hysteresis to the RPM display - not really necessary after using the blueShift
*/
#include <OBD2.h>
#include <DueTimer.h>
#include "SamNonDuePin.h"
//******************************************** INCLUDES *********************************************//
#include <Adafruit_NeoPixel.h> // https://github.com/adafruit/Adafruit_NeoPixel
//******************************************** INCLUDES *********************************************//
//create the CANport acqisition schedulers
cAcquireCAN CANport0(CAN_PORT_0);
/***** DEFINITIONS FOR OBD MESSAGES ON CAN PORT 0, see https://en.wikipedia.org/wiki/OBD-II_PIDs to add your own ***************/
//char _name[10], char _units[10], OBD_PID pid, uint8_t OBD_PID_SIZE size, bool _signed, OBD_MODE_REQ mode, float32 slope, float32 offset, cAcquireCAN *, extended ID;
cOBDParameter OBD_Speed( "Speed " , " KPH" , SPEED , _8BITS, false, CURRENT, 1, 0, &CANport0, false);
cOBDParameter OBD_EngineSpeed("Engine Speed " , " RPM" , ENGINE_RPM , _16BITS, false, CURRENT, 0.25, 0, &CANport0, false);
cOBDParameter OBD_Throttle( "Throttle " , " %" , THROTTLE_POS, _8BITS, false, CURRENT, 0.3922, 0, &CANport0, false);
cOBDParameter OBD_Coolant( "Coolant " , " C" , COOLANT_TEMP, _8BITS, false , CURRENT, 1, -40, &CANport0, false);
cOBDParameter OBD_EngineLoad( "Load " , " %" , ENGINE_LOAD , _8BITS, false, CURRENT, 0.3922, 0, &CANport0, false);
cOBDParameter OBD_MAF( "MAF " , " grams/s" , ENGINE_MAF , _16BITS, false, CURRENT, 0.01, 0, &CANport0, false);
cOBDParameter OBD_IAT( "IAT " , " C" , ENGINE_IAT , _8BITS, false , CURRENT, 1, -40, &CANport0, false);
//******************************************* DEFINITIONS *******************************************//
#define RINGPIN 14
//******************************************* DEFINITIONS *******************************************//
//***************************************** GLOBAL VARIABLES ****************************************//
uint8_t previousBrightness = 0; // typical operation display brightness
uint8_t currentBrightness = 255; // typical operation diaplay brightness
uint8_t daytimeBrightness = 255; // brightness for when headlampStatus is TRUE
uint8_t nighttimeBrightness = 127; // brightness for when headlampStatus is FALSE
uint8_t numberOfRingLED = 24; // total number of LED's in the ring display
Adafruit_NeoPixel ring = Adafruit_NeoPixel(numberOfRingLED, RINGPIN, NEO_RGBW + NEO_KHZ800); // an instance for the LED ring display
int engineSpeed = 0;
String headlampStatusString = ""; // contains "t" if headlamps are on, "f" if they are off
String curHeadlampStatus = ""; // to detect changes in state
String prevHeadlampStatus = "";
int newEngineSpeedData = 0;
int newHeadlampStatusData = 0;
int RPM = 0;
int minRPM = 500; // RPM where the first LED will light
int maxRPM = 4000; // RPM where the last LED will light
int LEDsForRPM = numberOfRingLED; // the number of LED's used for RPM display - it could be less than the max if you like
int peakHoldLedPos = 0; // position of the sticky LED
int peakHoldTime = 3000; // number of milliseconds to keep the peak rpm lit up for
long peakHoldStartMs = 0; // the millis() value when setting the sticky LED
// we dont need to redraw the display if it is the same as the last time
int RPMpreviousNumLED = 0; // the number of RPM LED previously lit up
int RPMcurrentNumLED = 0; // the current number of RPM LED lit up
float incrementRPM = ( maxRPM - minRPM ) / LEDsForRPM;
String stringToParse;
int btConnection = 0; //1 = connected
long prevMillis = 0; // for the sticky LED
long myMillis = 0;
//******************************************* FUNCTIONS *********************************************//
#define Serial SerialUSB
const int LOWPOW = PIN_EMAC_ERX0;
void setup() {
pinModeNonDue(LOWPOW, OUTPUT);
digitalWriteNonDue(LOWPOW, HIGH);
pinMode(28, OUTPUT);
digitalWrite(28, LOW);
// pin directions
// pinMode( RINGPIN, OUTPUT );
// pinMode( BTPOWER, OUTPUT );
// setup for the ring display
ring.begin();
ring.show(); // Initialize all pixels to 'off'
ring.setBrightness(25); // 0::255
//set up the transmission/reception of messages to occur at 500Hz (2mS) timer interrupt
Timer3.attachInterrupt(PrintScreen).setFrequency(1).start();
//start CAN ports, enable interrupts and RX masks, set the baud rate here
CANport0.initialize(_500K);
}
UINT8 i;
UINT32 maxTime;
void drawRing( void ) {
// if there is valid data then we should update the display.
if ( newEngineSpeedData == 1 ) {
// turn everything off
for ( int i = 0; i < numberOfRingLED; i++ ) {
ring.setPixelColor( i, 0, 0, 0 );
}
// calculate the number of LEDs to light up
RPMcurrentNumLED = ( engineSpeed - minRPM ) / incrementRPM; // round the number of LEDs to light to the lowest integer
// prepare to turn them on, if the new number of LED to light is different than the old number to light
if ( RPMcurrentNumLED != RPMpreviousNumLED ) {
// set all LEDs to light to green to start
for ( int i = 0; i < RPMcurrentNumLED; i++ ) {
ring.setPixelColor( i, currentBrightness, 0, 0 ); // green
}
// if the RPM is high, set some intermediary yellows
if ( RPMcurrentNumLED >= ( LEDsForRPM - 7 ) ) {
for ( int i = ( LEDsForRPM - 7 ); i < RPMcurrentNumLED; i++ ) {
ring.setPixelColor( i, currentBrightness, currentBrightness, 0 ); // yellow
}
}
// if the RPM is really high, set the last 4 LED's red
if ( RPMcurrentNumLED >= ( LEDsForRPM - 4 ) ) {
for ( int i = ( LEDsForRPM - 4 ); i < RPMcurrentNumLED; i++ ) {
ring.setPixelColor( i, 0, currentBrightness, 0 ); // red
}
}
// also, I want whichever the highest LED that is currently on to be red, like a pointer
// this is probably redundant with the sticky led?
ring.setPixelColor( ( RPMcurrentNumLED - 1 ), currentBrightness, 0, 0 ); // red
// now figure out the sticky led
// if the current RPM LED is higher than the current sticky led position
// this continues in the main loop
if ( RPMcurrentNumLED > peakHoldLedPos ) {
peakHoldLedPos = RPMcurrentNumLED; // set the current value as the peak
peakHoldStartMs = millis(); // record the start time of the new peak
}
// if the sticky led has been lit for too long
if ( millis() - peakHoldStartMs > peakHoldTime ) {
peakHoldLedPos = RPMcurrentNumLED; // set the current value as the peak
peakHoldStartMs = millis(); // record the start time of the new peak
}
// I also think the sticky LED should be reset if the RPM begins increasing,
// but not if the RPM is steady state or decreasing.
if ( RPMcurrentNumLED > RPMpreviousNumLED ) {
peakHoldLedPos = RPMcurrentNumLED; // set the current value as the peak
peakHoldStartMs = millis(); // record the start time of the new peak
}
// and finally, set the position of the sticky LED
ring.setPixelColor( ( peakHoldLedPos - 1 ), currentBrightness, 0, 0 ); // red
ring.show(); // show the new LED display
RPMpreviousNumLED = RPMcurrentNumLED;
}
}
}
void clearRing( void ) {
for ( int i = 0; i < numberOfRingLED; i++ ) {
ring.setPixelColor( i, 0 );
}
}
void loop() {
CANport0.run(TIMER_2mS);
// if (newEngineSpeedData == 1) {
engineSpeed = (OBD_EngineSpeed.getData());
Serial.println (engineSpeed);
// engineSpeed = RPM;
drawRing();
}
void PrintScreen()
{
newEngineSpeedData = 1; // force a redraw of the ring to occur
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment