Page 1 of 1

Serial 7-Segment Display with OBD-II UART

Unread postPosted:Mon Dec 09, 2013 4:04 am
by abnormalbill
I'm trying to program the firmware of a [url=https://www.sparkfun.com/products/11441]Sparkfun Serial 7 Segment Display[/url] to display my car's speed.  The display comes with an ATmega328 preloaded with the Arduino bootloader, so I've been programming it using an [url=https://www.sparkfun.com/products/9716]FTDI Basic board[/url].

Everything has been going smoothly, but I haven't been able to get it to communicate with the OBD-II UART connector.  It can get power from the OBD, but it doesn't seem to be able to get data.

It gets stuck in "while (!obd.Init());", and if I delete that part of the code it will never go into the "if (obd.ReadSensor(PID_SPEED, kph))".

The only thing I can think of is the TX and RX lines on the display's ATmega328 are a little different than a real Arduino's TX and RX?

Note: I'm using an older version of the OBD library, but it works fine when I have an actual Arduino Uno.


Has anyone else tried to use Sparkfun's display with OBD?  Any help is appreciated.

Re: Serial 7-Segment Display with OBD-II UART

Unread postPosted:Mon Dec 09, 2013 2:02 pm
by abnormalbill
Yeah, I tested the RX and TX and they work fine.

Here's my code, most of it is stuff from Sparkfun that I don't completely understand, but the stuff that I added is enclosed in the "/**********/":


When I plug it into the OBD, it flashes the "----", which is how I know it gets stuck in the "while (!obd.Init());" function.  It never displays the "5678".  If I remove the "while (!obd.Init());" code, it does display the "5678".

[code]
/* Serial 7 Segment Display Firmware
Version: 3.0.1
By: Jim Lindblom (SparkFun Electronics)
Date: August 24, 2012
License: This code is beerware: feel free to use it, with or without attribution,
in your own projects. If you find it helpful, buy me a beer next time you see me
at the local pub.

Description: This firmware goes on the SparkFun Serial 7-Segment displays.
[url]https://www.sparkfun.com/search/results?term=serial+7+segment&what=products[/url]

You can send the display serial data over either UART, SPI, or I2C. It'll
sequentially display what it reads. There are special commands to control
individual segments, clear the display, reset the cursor, adjust the display's
brightness, UART baud rate, i2c address or factory reset.

Note: To use the additional pins, PB6 and PB7, on the ATmega328 we have to add
some maps to the pins_arduino.h file. This allows Arduino to identify PB6 as
digital pin 22, and PB7 as digital pin 23. Because the Serial 7-Segment runs on
the ATmega328's internal oscillator, these two pins open up for our use.

Hardware: You can find the Serial 7-Segment Display Schematic here:
!!! Add schematic link

*/
#include <Wire.h>  // Handles I2C
#include <EEPROM.h>  // Brightness, Baud rate, and I2C address are stored in EEPROM
#include "settings.h"  // Defines command bytes, EEPROM addresses, display data
#include "SevSeg.h" //Library to control generic seven segment displays

SevSeg myDisplay; //Create an instance of the object

//This firmware works on three different hardware layouts
//Serial7Segment was the original and drives the segments directly from the ATmega
//OpenSegment uses PNP and NPN transistors to drive larger displays
//Serial7SegmentShield also drives the segments directly from the ATmega
#define S7S            1
#define OPENSEGMENT    2
#define S7SHIELD      3
#define DISPLAY_TYPE S7S

//Global variables
unsigned int analogValue6 = 0; //These are used in analog meter mode
unsigned int analogValue7 = 0;
unsigned char deviceMode;
// This variable is useds to select which mode the device should be in

unsigned char commandMode = 0;
// Used to indicate if a commandMode byte has been received

// Struct for circular data buffer data received over UART, SPI and I2C are all sent into a
// single buffer
struct dataBuffer
{
  unsigned char data[BUFFER_SIZE];  // THE data buffer
  unsigned int head;  // store new data at this index
  unsigned int tail;  // read oldest data from this index
}
buffer;  // our data buffer is creatively named - buffer

// Struct for 4-digit, 7-segment display
// Stores display value (digits),  decimal status (decimals) for each digit,
// and cursor for overall display
struct display
{
  char digits[4];
  unsigned char decimals;
  unsigned char cursor;
}
display;  // displays be displays


/******************************************************************/
#include <OBD.h>
#include <LiquidCrystal.h>

COBD obd;

int kph = 0;

#define sseg Serial
/******************************************************************/

void setup()

  setupDisplay(); //Initialize display stuff (common cathode, digits, brightness, etc)

  //We need to check emergency after we have initialized the display
  //so that we can use the display during an emergency reset
  checkEmergencyReset(); //Look to see if the RX pin is being pulled low

  setupTimer();  // Setup timer to control interval reading from buffer
  setupUART();  // initialize UART stuff (interrupts, enable, baud)
  setupSPI();    // Initialize SPI stuff (enable, mode, interrupts)
  setupTWI();    // Initialize I2C stuff (address, interrupt, enable)
  setupAnalog(); // Initialize the analog inputs
  setupMode();  // Determine which mode we should be in

  interrupts();  // Turn interrupts on, and les' go

  //Preload the display buffer with a default
  /*display.digits[0] = 1;
  display.digits[1] = 2;
  display.digits[2] = 3;
  display.digits[3] = 4;*/
 
 
 
/******************************************************************/
  //clear the display
  display.decimals = 0;  // clear all decimals
  display.cursor = 0;  // reset the cursor
 
  while (!obd.Init()) myDisplay.DisplayString("----",0);
/******************************************************************/
}

// The display is constantly PWM'd in the loop()
void loop()

  if(deviceMode == MODE_DATA)
  {
    displayData();
  }
  else if(deviceMode == MODE_COUNTER)
  {
    displayCounter();
  }
  else if(deviceMode == MODE_ANALOG)
  {
    displayAnalog();
  }

  //We will loop if we've received a new device mode command
}

//This is the normal mode where we display whatever data is coming
//  in over UART, SPI, and I2C

void displayData()
{
  while(deviceMode == MODE_DATA)
  {
    /******************************************************************/
    /*if (obd.ReadSensor(PID_SPEED, kph))
    {
    }*/
    myDisplay.DisplayString("5678",0);
    /******************************************************************/
   
    //Just hang out and update the display as new data comes in
    //myDisplay.DisplayString(display.digits, display.decimals);
    //(numberToDisplay, decimal point location)

    //serialEvent(); //Check the serial buffer for new data
    //Serial.println(display.digits);
  }
}

//Turn off the SPI and watch for increment pulses on the SDO pin, decrement on SDI
void displayCounter()
{
  SPCR = 0; //Disable all SPI interrupts that may be turned on

  int counterIncrement = SPI_MOSI; //Labeled SDI
  int counterDecrement = SPI_MISO; //Labeled SDO

  pinMode(counterIncrement, INPUT_PULLUP);
  pinMode(counterDecrement, INPUT_PULLUP);

  int counter = 0; //Watches the overall count
  boolean incrementCounted = false; //Watches the toggle the counter pins
  boolean decrementCounted = false;

  while(deviceMode == MODE_COUNTER) //Loop until we receive a different mode command
  {
    //Check to see if there has been a low/high pulse on increment
    if(digitalRead(counterIncrement) == LOW)
    {
      delay(1); //Check the pin 1 ms later - this is for debounce
      myDisplay.DisplayString(display.digits, 0); //Update display so that it doesn't blink

      if(digitalRead(counterIncrement) == LOW)
      {
        if(incrementCounted == false) //Only increment counter if this is a new pulse
        {
          counter++;
          incrementCounted = true; //We have now counted this pulse
        }
      }
    }
    else
    {
      //The increment pin is high, so sdo can be counted again
      incrementCounted = false;
    }

    //Check to see if there has been a low/high pulse on increment
    if(digitalRead(counterDecrement) == LOW)
    {
      delay(1); //Check the pin 1 ms later - this is for debounce
      myDisplay.DisplayString(display.digits, 0); //Update display so that it doesn't blink
     
      if(digitalRead(counterDecrement) == LOW)
      {
        if(decrementCounted == false) //Only increment counter if this is a new pulse
        {
          counter--;
          decrementCounted = true; //We have now counted this pulse
        }
      }
    }
    else
    {
      //The increment pin is high, so sdo can be counted again
      decrementCounted = false;
    }

    //Display this count
    //char tempString[10]; //Used for sprintf
    sprintf(display.digits, "%4d", counter);
    //Convert counter into a string that is right adjusted

   
    //int tempCounter = counter;
    // for(int x = 0 ; x < 4 ; x++)
    // {
    // display.digits[3 - x] = (tempCounter % 10);
    ////Pull off the right most digit and store in display array
    //
    // tempCounter /= 10; //Shave number down by one digit
    // }

    myDisplay.DisplayString(display.digits, 0);
    //(numberToDisplay, no decimals during counter mode)

    serialEvent(); //Check the serial buffer for new data
  } 
}

//Do nothing but analog reads
void displayAnalog()
{
  while(deviceMode == MODE_ANALOG)
  {
    analogValue6 = analogRead(A6);
    analogValue7 = analogRead(A7);

    //Serial.print("A6: ");
    //Serial.print(analogValue6);
    //Serial.print(" A7: ");
    //Serial.print(analogValue7);

    //Do calculation for 1st voltage meter
    float fvoltage6 = ((analogValue6 * 50) / (float)1024);
    int voltage6 = round(fvoltage6);
    display.digits[0] = voltage6 / 10;
    display.digits[1] = voltage6 % 10;

    //Do calculation for 2nd voltage meter
    float fvoltage7 = ((analogValue7 * 50) / (float)1024);
    int voltage7 = round(fvoltage7);
    display.digits[2] = voltage7 / 10;
    display.digits[3] = voltage7 % 10;

    display.decimals = ((1<<DECIMAL1) | (1<<DECIMAL3));
    //Turn on the decimals next to digit1 and digit3
   
    myDisplay.DisplayString(display.digits, display.decimals);
    //(numberToDisplay, decimal point location)

    serialEvent(); //Check the serial buffer for new data
  } 
}

// updateBufferData(): This dfrobot of a function is called by the Timer 1 ISR
//    if there is new data in the buffer.
// If the data controls display data, that'll be updated.
// If the data relates to a command, commandmode will be set accordingly or a command
// will be executed from this function.
void updateBufferData()
{

  // First we read from the oldest data in the buffer
  unsigned char c = buffer.data[buffer.tail];
  buffer.tail = (buffer.tail + 1) % BUFFER_SIZE;  // and update the tail to the next oldest

  // if the last byte received wasn't a command byte (commandMode=0)
  // and if the data is displayable (0-0x76 or 0x78), the display will be updated
  if ((commandMode == 0) && ((c < 0x76) || (c == 0x78)))
  {
    display.digits[display.cursor] = c;
    // just store the read data into the cursor-active digit
   
    display.cursor = ((display.cursor + 1) % 4);
    // Increment cursor, set back to 0 if necessary
  }
  else if (c == RESET_CMD)  // If the received char is the reset command
  {
    for(int i = 0 ; i < 4 ; i++)
      display.digits[i] = 'x';  // clear all digits
    display.decimals = 0;  // clear all decimals
    display.cursor = 0;  // reset the cursor
  }
  else if (commandMode != 0)
  // Otherwise, if data is non-displayable and we're in a commandMode
  {
    switch (commandMode)
    {
    case DECIMAL_CMD:  // Decimal setting mode
      display.decimals = c;  // decimals are set by one byte
      break;
    case BRIGHTNESS_CMD:  // Brightness setting mode
      EEPROM.write(BRIGHTNESS_ADDRESS, c);    // write the new value to EEPROM
      myDisplay.SetBrightness(c); //Set the display to this brightness level
      break;
    case BAUD_CMD:  // Baud setting mode
      EEPROM.write(BAUD_ADDRESS, c);  // Update EEPROM with new baud setting
      setupUART();
      //Checks to see if this baud rate is valid and turns on UART at this speed
     
      break;
    case CURSOR_CMD:  // Set the cursor
      if (c <= 3)  // Limited error checking, if >3 cursor command will have no effect
        display.cursor = c;  // Update the cursor value
      break;
    case TWI_ADDRESS_CMD:  // Set the I2C Address
      EEPROM.write(TWI_ADDRESS_ADDRESS, c); // Update the EEPROM value
      setupTWI(); //Checks to see if I2C address is valid and begins I2C
      break;
    case MODE_CMD:  // Set the device mode (example: data, analog, counter)
      EEPROM.write(MODE_ADDRESS, c); // Update the EEPROM value
      setupMode(); //Checks to see if this mode is valid and then enters new mode
      break;
    case FACTORY_RESET_CMD:  // Factory reset
      setDefaultSettings();  // Reset baud, brightness, and TWI address
      break;
    case DIGIT1_CMD:  // Single-digit control for digit 1
      display.digits[0] = c | 0x80;  // set msb to indicate single digit control mode
      break;
    case DIGIT2_CMD:  // Single-digit control for digit 2
      display.digits[1] = c | 0x80;
      break;
    case DIGIT3_CMD:  // Single-digit control for digit 3
      display.digits[2] = c | 0x80;
      break;
    case DIGIT4_CMD:  // Single-digit control for digit 4
      display.digits[3] = c | 0x80;
      break;
    }
    // Leaving commandMode
    // !!! If the commandMode isn't a valid command, we'll leave command mode,
    //      should be checked below?
    commandMode = 0;
  }
  else  // Finally, if we weren't in command mode, if the byte isn't displayable,
  //        we'll enter command mode
  {
    commandMode = c;  // which command mode is reflected by value of commandMode
  }
}
[/code]

Re: Serial 7-Segment Display with OBD-II UART

Unread postPosted:Wed Dec 11, 2013 3:44 am
by abnormalbill
Okay thanks. So I did that and it does receive data from the OBD port and it is displaying correctly.


Except now I have an unrelated problem: The display keeps blinking on and off when it's communicating with the OBD.  I noticed that when I increase the "OBD_TIMEOUT_SHORT" in the OBD.h library file, the time between blinks increases, and it decreases when I decrease the timeout number.  Setting the timeout to zero just makes the display flicker faster.

The display only flickers when I read data, even the actual display function isn't inside the if statement.

For example, this produces a blinking display even though there's nothing inside the if statement:
[code]if (obd.read(PID_SPEED, kph))
{
 
}
myDisplay.DisplayString("1234",0);[/code]

But if I comment out the if statement, it produces a normal display:
[code]/*if (obd.read(PID_SPEED, kph))
{
 
}*/
myDisplay.DisplayString("1234",0);[/code]

Re: Serial 7-Segment Display with OBD-II UART

Unread postPosted:Sat Dec 14, 2013 7:44 pm
by abnormalbill
The problem seems to be in the dataIdleLoop();

The SevSeg.h library works by turning on and off each digit very rapidly in succession, too quickly for the naked eye to see.

I guess it normally just keeps going through the digits rapidly until it gets a new number to display, and it looks likes a normal, non-blinking display.

But with the OBD library, I think when the OBD is going through this:
[code]
do {
  dataIdleLoop();
}
while (!(hasData = available()) && millis() - tick < OBD_TIMEOUT_SHORT);[/code]
It doesn't keep refreshing the display somehow.  So the SevSeg library goes through the digits once, and then stops until it gets the next number.

Any way to fix this?

Re: Serial 7-Segment Display with OBD-II UART

Unread postPosted:Thu Dec 03, 2020 7:02 am
by pandoralook
A particular Omega chronograph watch is to the particular lunar surface area certainly not once, however six to eight instances. In doing so the particular cheap Omega Speedmaster has become really the iconic watch among enthusiasts. There are many Apollo mission limited edition designs and we shall eventually review each one independently but for right now,
Omega Speedmaster Moonwatch Apollo 7 Bad side from the Silent celestial body Review
One thing concerning this watch that will blow you away is the impressive dial having its laser-ablated lunar structure which is often seen over the skeletonized face that enhances the blackened quality within just.