In my car I have an Arduino UNO running an Adafruit 1.44" TFT (non eye-spi) screen which receives CAN BUS data from an Arduino Nano via software serial. The UNO is also running other functions and as a result has all of its digital pins (sans 0 and 1) consumed. The TFT's backlight can be dimmed by sending a PWM signal to the board's Lite pin, which is normally high (100% ground = off).
As the Nano has spare pins, it controls the PWM signal (setting analogWrite() to either 127 or 255 depending on an analogue input connected to the car's radio dimming wire) to dim the TFT . As the Nano shares a ground with the UNO to enable the software serial to work, the dimming also works.
However - either the TFT makes erroneous displays and crashes/resets about 2 seconds after the car's headlights are switched on, which activates the dimming. The screen usually goes completely white (which it does both on initial start and when no signal is input) but other times I have seen it wash random colors over the screen and looks as though it is receiving a garbage signal.
The backlight always responds correctly - full brightness when dimming 'disactivated' (analogWrite(255)) and dimmed when activated (analogWrite(127)). But when I disable the dimming, the TFT remains borked. If I reset the UNO whilst the dimming signal is still active from the Nano, the same thing happens - clear display and signal from the Uno for about 2 or 3 seconds, then back to white display/random colors. The TFT also shows random colors and pixels all over the place on top of the bitmap I have set to display for a second during setup, after a reset.
I have checked that no wires are shorted and there is no solder overlap on any of the pins. Everything seems to be in order hardware wise, and the program functions perfectly, continuously, when the dimming is inactive. Any kind of ground signal on the Lite pin appears to break the TFT - I have tried stripping the wire and manually pressing it against ground with my hand, it always breaks the TFT, despite the backlight responding correctly.
Is it possible I have a dud TFT? I am willing to purchase and test another one, but I would like to know if perhaps there is a coding issue or something I'm missing or have done incorrectly.
Thanks in advance.
UNO Code:
Code: Select all
// Arduino Uno R3 board
// Adafruit 1.44" 128x128 TFT screen
// Wire from Oil Temperature Sensor on engine
// Voltage divider sourced from 12v socket for Battery Voltage
// 2 modes, 0 and 1
// Auto-Switch for 86 VSC buttons - all off on startup
//softwareSerial for reading data from Nano in glovebox - Expected message: <#,#,#>
#define VSC_out 5 // pin to control state of VSC
#define max_pin 7 // button to display maximumn recorded value for each parameter
int max_state = 0; // state of max button
int modeButton = 0; // counter for the number of mode button presses
// TFT (use tft.### functions i.e. tft.println() )
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SdFat.h> // SD card & FAT filesystem library
#include <Adafruit_SPIFlash.h> // SPI / QSPI flash library
#include <Adafruit_ImageReader.h> // Image-reading functions
#define SD_CS 6 // SD card select pin
#define TFT_DC 8 // TFT display/command pin
#define TFT_RST 9 // Or set to -1 and connect to Arduino RESET pin
#define TFT_CS 10 // TFT select pin
#define USE_SD_CARD
//SD Card stuff
SdFat SD; // SD card filesystem
Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
Adafruit_Image img; // An image loaded into RAM
int32_t width = 0, // BMP image dimensions
height = 0;
float p = 3.1415926;
//variables to hold cursor coordinates
int x = 0;
int y = 0;
uint32_t black = 0x0000;
uint32_t white = 0xFFFF;
uint32_t red = 0xF800;
uint32_t blue = 0x001F;
uint32_t yellow = 0xFFE0;
uint32_t green = 0x07E0;
uint32_t cyan = 0x07FF;
uint32_t magenta = 0xF81F;
//Software Serial https://forum.arduino.cc/t/serial-input-basics-updated/382007/3
#include <SoftwareSerial.h>
#include <SPI.h>
#define rxPin 4
#define txPin 1
// Set up a new SoftwareSerial object
SoftwareSerial softSerial (rxPin, txPin);
const byte numChars = 32; //32 bit serial buffer
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing
// variables to hold the parsed data
int int1 = 0;
int int2 = 0;
int int3 = 0;
//variables to clear screen when bigger number
boolean newData = false;
//for blanking screen over bigger/smaller numbers
bool start = 0;
bool big1 = 0;
bool change1 = 0;
// Oil Temp Sensor and OBD int's
const int analogIn = A0; //for oil temp sensor voltage meter
float oilRawValue = 0;
int oilTemperature = 0;
int oilReading = 0;
int lastOil = 0;
int oilTempDisplay = 0;
//battery voltage
const int batAnalogIn = A5;
float batVoltage = 0;
float batLast = 0;
float batAvg = 0;
float batVoltDisplay = 0;
//millis to set delay between cycles of program
unsigned long millisStart = 0;
// __ ______ _____ _____
// \ \ / / __ \_ _| __ \
// \ \ / / | | || | | | | |
// \ \/ /| | | || | | | | |
// \ / | |__| || |_| |__| |
// \/ \____/_____|_____/
//============
void oilTemp (void)
{
oilRawValue = analogRead(analogIn);
oilReading = ((log(0.15905837 * ((oilRawValue * 5.0) / 1023.0))) / -0.0207);
}
void batVolts (void)
{
batVoltage = (((analogRead(batAnalogIn)) * 5.0) / 1023.0) * 3.0545;
batAvg = (batVoltage + batLast) / 2;
batVoltDisplay = batAvg;
batLast = batAvg;
}
//============
void mode_counter_increase (void)
{
modeButton = 1;
}
//============
void mode_counter_decrease (void)
{
modeButton = 0;
}
//============
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (softSerial.available() > 0 && newData == false) {
rc = softSerial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
//============
void parseData() { // split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
// strtokIndx = strtok(tempChars,","); // get the first part - the string
// strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC - make messageFromPC into strtokIndx
strtokIndx = strtok(tempChars, ","); //NULL is after first delimiter, before first delimiter use tempChars
int1 = atoi(strtokIndx); // atoi = conver string to integer
//following code to make it indent right - blanks numbers on screen when necessary
if (int1 >= 100){
big1 = 1;
start = 1; //stops int3 being empty when program runs
}
else {
if (big1 == 1); {
change1 = 1;
}
big1 = 0;
}
//2nd digit in serial sequence
strtokIndx = strtok(NULL, ",");
int2 = atoi(strtokIndx);
//3rd digit etc..
strtokIndx = strtok(NULL, ",");
int3 = atoi(strtokIndx);
}
// _____ ______ _______ _ _ _____
// / ____| ____|__ __| | | | __ \
// | (___ | |__ | | | | | | |__) |
// \___ \| __| | | | | | | ___/
// ____) | |____ | | | |__| | |
// |_____/|______| |_| \____/|_|
void setup()
{
Serial.begin(9600);
softSerial.begin(9600);
// TFT
tft.initR(INITR_144GREENTAB); // Init ST7735R chip, green tab
tft.fillScreen(black);
//SD Card stuff
ImageReturnCode stat; // Status from image-reading functions
// The Adafruit_ImageReader constructor call (above, before setup())
// accepts an uninitialized SdFat or FatVolume object. This MUST
// BE INITIALIZED before using any of the image reader functions!
Serial.print(F("Initializing filesystem..."));
#if defined(USE_SD_CARD)
if(!SD.begin(SD_CS, SD_SCK_MHZ(10))) { // Breakouts require 10 MHz limit due to longer wires
Serial.println(F("SD begin() failed"));
for(;;); // Fatal error, do not continue
}
#else
// SPI or QSPI flash requires two steps, one to access the bare flash
// memory itself, then the second to access the filesystem within...
if(!flash.begin()) {
Serial.println(F("flash begin() failed"));
for(;;);
}
if(!filesys.begin(&flash)) {
Serial.println(F("filesys begin() failed"));
for(;;);
}
#endif
Serial.println(F("OK!"));
// Load full-screen BMP file 'lily128.bmp' at position (0,0) (top left).
// Notice the 'reader' object performs this, with 'tft' as an argument.
Serial.print(F("Loading lily128.bmp to screen..."));
stat = reader.drawBMP("/lily128.bmp", tft, 0, 0);
reader.printStatus(stat); // How'd we do?
//establish pin IO
pinMode(max_pin, INPUT); // max button
pinMode(VSC_out, OUTPUT); // VSC output
pinMode(analogIn, INPUT); // Oil Temp sensor
//establish pins 2 and 3 as interrupts to select mode
attachInterrupt(digitalPinToInterrupt(2), mode_counter_increase, FALLING); // modeButton = 1
attachInterrupt(digitalPinToInterrupt(3), mode_counter_decrease, FALLING); // modeButton = 0
delay(2000);
digitalWrite(VSC_out, HIGH);
delay(1500);
digitalWrite(VSC_out, LOW);
//bitmaps, bars, static numbers
tft.fillScreen(black);
//tft.drawBitmap(0, 0, oil_lamp, 47, 32, white);
//oil temp gauge
tft.fillRect(0,34,2,2,white);
// tft.fillRect(25,34,2,2,white);
// tft.fillRect(55,34,2,2,white);
// tft.fillRect(110,34,2,2,white);
// tft.fillRect(126,34,2,2,white);
// tft.fillRect(83,34,2,2,white);
// tft.fillRect(0,36,128,1,white);
// tft.setTextColor(white);
// tft.setTextSize(1);
// tft.setCursor(21,39);
// tft.print(40);
// tft.setCursor(51,39);
// tft.print(85);
// tft.setCursor(75,39);
// tft.print(125);
// tft.setCursor(102,39);
// tft.print(170);
millisStart = millis();
}
// _ ____ ____ _____
// | | / __ \ / __ \| __ \
// | | | | | | | | | |__) |
// | | | | | | | | | ___/
// | |___| |__| | |__| | |
// |______\____/ \____/|_|
void loop()
{
max_state = digitalRead(max_pin);
if ( millis() >= millisStart + 200 ) {
oilTemp();
batVolts();
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
//blank bigger numbers
if (change1 == 1 && start == 1) {
tft.fillRect(50,0,12,28,black); //(x,y,w,h,color)
change1 = 0;
}
tft.setTextSize(3);
tft.setTextColor(white,black); //(textcolor,background)
//print data
//int1 - Oil temp digits
if (big1 == 0) {
tft.setCursor(67,7);
tft.print(int1);
}
else {
tft.setCursor(50,7);
tft.print(int1);
}
tft.drawChar(102,7,0x09,white,black,2);
tft.setTextSize(2);
tft.setCursor(112,13);
tft.print("C");
/*//Oil temp gauge
if (int1 < 85) {
tft.fillRect(0,31,2,3,blue);
tft.fillRect(2,31,(int1*0.6484375),3,blue);
tft.fillRect(int1*0.6484375,31,(128-(int1*0.6484375+2)),3,black);
}
if (int1 >= 85 && int1 <=125) {
tft.fillRect(0,31,2,3,green);
tft.fillRect(2,31,(int1*0.6484375),3,green);
tft.fillRect(int1*0.6484375,31,(128-(int1*0.6484375+2)),3,black);
}
if (int1 >= 126) {
tft.fillRect(0,31,2,3,red);
tft.fillRect(2,31,(int1*0.6484375),3,red);
tft.fillRect(int1*0.6484375,31,(128-(int1*0.6484375+2)),3,black);
}
*/
//test volts/analog oil temp
tft.setTextSize(2);
tft.setTextColor(red,black);
tft.setCursor(10,100);
if (modeButton == 0); {
tft.println(oilTempDisplay);
}
if (modeButton == 1); {
tft.println(batVoltDisplay);
}
tft.setTextColor(blue,black);
tft.setCursor(100,100);
tft.println(modeButton);
newData = false;
}
millisStart = millis();
}
}
Code: Select all
//RPM Shift Lights using an MCP2515 CAN bus board.
//Adafruit NeoPixel LED Sticks, two 8-led RGBW warm-white sticks.
//NeoPixel codes is (#,#,#,#,#) = (numberOfLed,Red,Green,Blue,White) numberOfLed = 0-15, Color is in brightness 0-255
//4N25 Opto-coupler input for dimming. 12v on diode side, ground on switch.
#include <mcp2515.h>
#include <Adafruit_NeoPixel.h>
#include <SoftwareSerial.h>
#define rxPin 4
#define txPin 3
// Set up a new SoftwareSerial object
SoftwareSerial softSerial (rxPin, txPin);
int RPMno = 6000; //base RPM for LED activation
int vehicleRPM = 0; //rpm value to be provided by CAN bus
int oilTemp = 0; //oil temperature from CAN bus
int coolant = 0; // coolant temperature from CAN bus
struct can_frame canMsg;
MCP2515 mcp2515(7); // bracketed number is number of CS pin
int ex = 0;
int why = 0;
//NeoPixels
#define PIN 6
#define NUM_LEDS 16
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRBW + NEO_KHZ800);
//full brightness
uint32_t r = strip.Color (200, 0, 0, 0);
uint32_t g = strip.Color (0, 180, 0, 0);
uint32_t b = strip.Color (0, 0, 255, 0);
uint32_t w = strip.Color (0, 0, 0, 120);
uint32_t y = strip.Color (200, 120, 0, 0);
//dimmed
uint32_t rd = strip.Color (16, 0, 0, 0);
uint32_t gd = strip.Color (0, 14, 0, 0);
uint32_t bd = strip.Color (0, 0, 10, 0);
uint32_t wd = strip.Color (0, 0, 0, 6);
uint32_t yd = strip.Color (20, 10, 0, 0);
//off
uint32_t o = strip.Color (0, 0, 0, 0);
int space = 100; //delay for led startup sequence
//Input for headlight circuit
#define headlights 2
bool val = 1;
unsigned long millisStart = 0;
// _____ ______ _______ _ _ _____
// / ____| ____|__ __| | | | __ \
// | (___ | |__ | | | | | | |__) |
// \___ \| __| | | | | | | ___/
// ____) | |____ | | | |__| | |
// |_____/|______| |_| \____/|_|
void setup()
{
//initiate neopixels
strip.begin();
//set strip to nothing, flushes potential random colors
strip.clear();
strip.fill(o);
strip.show();
ledStartup();
//assign input for Optocoupler and PWM Output
pinMode(headlights, INPUT_PULLUP);
pinMode(10, OUTPUT);
//setup the CANBus module
mcp2515.reset();
mcp2515.setBitrate(CAN_500KBPS, MCP_16MHZ);
mcp2515.setListenOnlyMode();
softSerial.begin(9600);
Serial.begin(9600);
millisStart = millis();
}
// _ ____ ____ _____
// | | / __ \ / __ \| __ \
// | | | | | | | | | |__) |
// | | | | | | | | | ___/
// | |___| |__| | |__| | |
// |______\____/ \____/|_|
void loop()
{
//get CAN Message
if ((mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK)) {
getMessage();
}
strip.clear();
dimmer();
if (millis() >= millisStart + 200) {
softSerial.println("<");
softSerial.println(oilTemp);
softSerial.println(",");
softSerial.println(coolant);
softSerial.println(",");
softSerial.println(vehicleRPM);
softSerial.println(">");
millisStart = millis();
}
Serial.println(val);
}
//__ ______ _____ _____
// \ \ / / __ \_ _| __ \
// \ \ / / | | || | | | | |
// \ \/ /| | | || | | | | |
// \ / | |__| || |_| |__| |
// \/ \____/_____|_____/
//
// _
// __ _ ___| |_ _ __ _ __ _ __ ___
// / _` |/ _ \ __| '__| '_ \| '_ ` _ \
//| (_| | __/ |_| | | |_) | | | | | |
// \__, |\___|\__|_| | .__/|_| |_| |_|
// |___/ |_|
void getMessage (void) { //formerly getRPM
if (canMsg.can_id == 0x140) {
ex = canMsg.data[2];
why = canMsg.data[3];
//combine the 2 digit hex for field 2 and field 3 to get decimal rpm
vehicleRPM = (why & 0x3f) * 256 + ex;
//Serial.print("RPM: ");
//Serial.println(vehicleRPM);
}
if (canMsg.can_id == 0x360) {
oilTemp = (canMsg.data[2]) - 40;
coolant = (canMsg.data[3]) - 40;
//Serial.println("Oil:");
//Serial.println(oilTemp);
//Serial.println("Coolant:");
//Serial.println(coolant);
}
}
// _ _
// __| (_)_ __ ___ _ __ ___ ___ _ __
// / _` | | '_ ` _ \| '_ ` _ \ / _ \ '__|
//| (_| | | | | | | | | | | | | __/ |
// \__,_|_|_| |_| |_|_| |_| |_|\___|_|
void dimmer (void) {
val = digitalRead(headlights);
if (val == HIGH) {
leds();
analogWrite(10,255); //100% backlight - diming of TFT - PWM output
}
else {
ledsDimmed();
analogWrite(10,127); //50% backlight - diming of TFT - PWM output
}
}
// _ _
// | | ___ __| |___
// | |/ _ \/ _` / __|
// | | __/ (_| \__ \
// |_|\___|\__,_|___/ font = ogre
void leds(void) {
if (vehicleRPM > 0 && vehicleRPM < (RPMno - 1000) )
{
strip.fill(o); //----------------
strip.show();
}
if (vehicleRPM >= (RPMno - 1000) && vehicleRPM < (RPMno - 500) )
{
strip.fill(g, 14); //gg--------------
strip.show();
}
if (vehicleRPM >= (RPMno - 500) && vehicleRPM < RPMno )
{
strip.fill(g, 12); //gggg------------
strip.show();
}
if (vehicleRPM >= (RPMno) && vehicleRPM < (RPMno + 167))
{
strip.fill(b, 10); //yyyyyy----------
strip.show();
}
if (vehicleRPM >= (RPMno + 167) && vehicleRPM < (RPMno + 334))
{
strip.fill(b, 8); //yyyyyyyy--------
strip.show();
}
if (vehicleRPM >= (RPMno + 334) && vehicleRPM < (RPMno + 501))
{
strip.fill(b, 6); //yyyyyyyyyy------
strip.show();
}
if (vehicleRPM >= (RPMno + 501) && vehicleRPM < (RPMno + 668))
{
strip.fill(r, 4); //yyyyyyyyyyyy----
strip.show();
}
if (vehicleRPM >= (RPMno + 668) && vehicleRPM < (RPMno + 835))
{
strip.fill(r, 2); //rrrrrrrrrrrrrr--
strip.show();
}
if (vehicleRPM >= (RPMno + 835) && vehicleRPM < (RPMno + 1000))
{
strip.fill(r, 0); //rrrrrrrrrrrrrr
strip.show();
}
if (vehicleRPM >= (RPMno + 1000) && vehicleRPM < (RPMno + 1500))
{
strip.fill(w); //wwwwwwwwwwwwwwww
strip.show();
}
if (vehicleRPM >= (RPMno + 1500) && vehicleRPM < (RPMno + 2000) )
{
strip.fill(o);
strip.show();
}
if (vehicleRPM > 10000)
{
strip.fill(o);
strip.show();
}
}
// _ _ ___ _ _
// | | ___ __| |___ / (_)_ __ ___ _ __ ___ ___ __| |
// | |/ _ \/ _` / __| / /\ / | '_ ` _ \| '_ ` _ \ / _ \/ _` |
// | | __/ (_| \__ \/ /_//| | | | | | | | | | | | __/ (_| |
// |_|\___|\__,_|___/___,' |_|_| |_| |_|_| |_| |_|\___|\__,_|
void ledsDimmed(void) {
if (vehicleRPM > 0 && vehicleRPM < (RPMno - 1000) )
{
strip.fill(o); //----------------
strip.show();
}
if (vehicleRPM >= (RPMno - 1000) && vehicleRPM < (RPMno - 500) )
{
strip.fill(gd, 14); //gg--------------
strip.show();
}
if (vehicleRPM >= (RPMno - 500) && vehicleRPM < RPMno )
{
strip.fill(gd, 12); //gggg------------
strip.show();
}
if (vehicleRPM >= (RPMno) && vehicleRPM < (RPMno + 167))
{
strip.fill(bd, 10); //yyyyyy----------
strip.show();
}
if (vehicleRPM >= (RPMno + 167) && vehicleRPM < (RPMno + 334))
{
strip.fill(bd, 8); //yyyyyyyy--------
strip.show();
}
if (vehicleRPM >= (RPMno + 334) && vehicleRPM < (RPMno + 501))
{
strip.fill(bd, 6); //yyyyyyyyyy------
strip.show();
}
if (vehicleRPM >= (RPMno + 501) && vehicleRPM < (RPMno + 668))
{
strip.fill(rd, 4); //yyyyyyyyyyyy----
strip.show();
}
if (vehicleRPM >= (RPMno + 668) && vehicleRPM < (RPMno + 835))
{
strip.fill(rd, 2); //rrrrrrrrrrrrrr--
strip.show();
}
if (vehicleRPM >= (RPMno + 835) && vehicleRPM < (RPMno + 1000))
{
strip.fill(rd, 0); //rrrrrrrrrrrrrr
strip.show();
}
if (vehicleRPM >= (RPMno + 1000) && vehicleRPM < (RPMno + 1500))
{
strip.fill(wd); //wwwwwwwwwwwwwwww
strip.show();
}
if (vehicleRPM >= (RPMno + 1500) && vehicleRPM < (RPMno + 2000) )
{
strip.fill(o);
strip.show();
}
if (vehicleRPM > 10000)
{
strip.fill(o);
strip.show();
}
}
// _ _ __ _ _
// | | ___ __| |___ / _\ |_ __ _ _ __| |_ _ _ _ __
// | |/ _ \/ _` / __|\ \| __/ _` | '__| __| | | | '_ \
// | | __/ (_| \__ \_\ \ || (_| | | | |_| |_| | |_) |
// |_|\___|\__,_|___/\__/\__\__,_|_| \__|\__,_| .__/
// |_|
void ledStartup(void)
{
strip.fill(o);
strip.show();
delay(space);
strip.fill(gd, 14);
strip.show();
delay(space);
strip.fill(gd, 12);
strip.show();
delay(space);
strip.fill(gd, 10);
strip.show();
delay(space);
strip.fill(bd, 8);
strip.show();
delay(space);
strip.fill(bd, 6);
strip.show();
delay(space);
strip.fill(bd, 4);
strip.show();
delay(space);
strip.fill(rd, 2);
strip.show();
delay(space);
strip.fill(rd);
strip.show();
delay(space);
//---------------------------
strip.fill(wd);
strip.show();
delay(space * 2);
//----------------------------
strip.clear();
strip.fill(rd);
strip.show();
delay(space);
strip.clear();
strip.fill(rd, 2);
strip.show();
delay(space);
strip.clear();
strip.fill(bd, 4);
strip.show();
delay(space);
strip.clear();
strip.fill(bd, 6);
strip.show();
delay(space);
strip.clear();
strip.fill(bd, 8);
strip.show();
delay(space);
strip.clear();
strip.fill(gd, 10);
strip.show();
delay(space);
strip.clear();
strip.fill(gd, 12);
strip.show();
delay(space);
strip.clear();
strip.fill(gd, 14);
strip.show();
delay(space);
strip.fill(o);
strip.show();
}