diff options
author | Stanley Huang <stanleyhuangyc@gmail.com> | 2013-03-25 00:57:28 +0800 |
---|---|---|
committer | Stanley Huang <stanleyhuangyc@gmail.com> | 2013-03-25 00:57:28 +0800 |
commit | 8e57c683d3d16f1dee320f10c411602ea38ba651 (patch) | |
tree | 75df3040d13c3f1850d93e29eb93a3836cae133c | |
download | 2021-arduino-obd-8e57c683d3d16f1dee320f10c411602ea38ba651.tar.gz 2021-arduino-obd-8e57c683d3d16f1dee320f10c411602ea38ba651.tar.bz2 2021-arduino-obd-8e57c683d3d16f1dee320f10c411602ea38ba651.zip |
initial commit
53 files changed, 7298 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ebd21a --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/libraries/OBD/OBD.cpp b/libraries/OBD/OBD.cpp new file mode 100644 index 0000000..b3da34e --- /dev/null +++ b/libraries/OBD/OBD.cpp @@ -0,0 +1,269 @@ +/************************************************************************* +* OBD-II (ELM327) data accessing library for Arduino +* Distributed under GPL v2.0 +* Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <avr/pgmspace.h> +#include "OBD.h" + +#define INIT_CMD_COUNT 7 +#define MAX_CMD_LEN 6 + +const char PROGMEM s_initcmd[INIT_CMD_COUNT][MAX_CMD_LEN] = {"ATZ\r","ATE0\r","ATL1\r","ATI\r","0100\r","0120\r","0140\r"}; +const char PROGMEM s_searching[] = "SEARCHING"; +const char PROGMEM s_cmd_fmt[] = "%02X%02X 1\r"; +const char PROGMEM s_cmd_sleep[MAX_CMD_LEN] = "atlp\r"; +const char PROGMEM s_response_begin[] = "41 "; + +unsigned int hex2uint16(const char *p) +{ + char c = *p; + unsigned int i = 0; + for (char n = 0; c && n < 4; c = *(++p)) { + if (c >= 'A' && c <= 'F') { + c -= 7; + } else if (c>='a' && c<='f') { + c -= 39; + } else if (c == ' ') { + continue; + } else if (c < '0' || c > '9') { + break; + } + i = (i << 4) | (c & 0xF); + n++; + } + return i; +} + +unsigned char hex2uint8(const char *p) +{ + unsigned char c1 = *p; + unsigned char c2 = *(p + 1); + if (c1 >= 'A' && c1 <= 'F') + c1 -= 7; + else if (c1 >='a' && c1 <= 'f') + c1 -= 39; + else if (c1 < '0' || c1 > '9') + return 0; + + if (c2 >= 'A' && c2 <= 'F') + c2 -= 7; + else if (c2 >= 'a' && c2 <= 'f') + c2 -= 39; + else if (c2 < '0' || c2 > '9') + return 0; + + return c1 << 4 | (c2 & 0xf); +} + +void COBD::Query(unsigned char pid) +{ + char cmd[8]; + sprintf_P(cmd, s_cmd_fmt, dataMode, pid); + WriteData(cmd); +} + +bool COBD::ReadSensor(byte pid, int& result, bool passive) +{ + if (passive) { + bool hasData; + unsigned long tick = millis(); + while (!(hasData = DataAvailable()) && millis() - tick < OBD_TIMEOUT_SHORT); + if (!hasData) { + errors++; + return false; + } + } else { + Query(pid); + } + return GetResponse(pid, result); +} + +bool COBD::DataAvailable() +{ + return OBDUART.available(); +} + +char COBD::ReadData() +{ + return OBDUART.read(); +} + +void COBD::WriteData(const char* s) +{ + OBDUART.write(s); +} + +void COBD::WriteData(const char c) +{ + OBDUART.write(c); +} + +char* COBD::GetResponse(byte pid, char* buffer) +{ + unsigned long startTime = millis(); + byte i = 0; + + for (;;) { + if (DataAvailable()) { + char c = ReadData(); + buffer[i] = c; + if (++i == OBD_RECV_BUF_SIZE - 1) { + // buffer overflow + break; + } + if (c == '>' && i > 6) { + // prompt char reached + break; + } + } else { + buffer[i] = 0; + unsigned int timeout; + if (dataMode != 1 || strstr_P(buffer, s_searching)) { + timeout = OBD_TIMEOUT_LONG; + } else { + timeout = OBD_TIMEOUT_SHORT; + } + if (millis() - startTime > timeout) { + // timeout + errors++; + break; + } + } + } + buffer[i] = 0; + + char *p = buffer; + while ((p = strstr_P(p, s_response_begin))) { + p += 3; + if (pid == 0 || hex2uint8(p) == pid) { + errors = 0; + p += 2; + if (*p == ' ') + return p + 1; + } + } + return 0; +} + +bool COBD::GetParsedData(byte pid, char* data, int& result) +{ + switch (pid) { + case PID_RPM: + result = GetLargeValue(data) >> 2; + break; + case PID_FUEL_PRESSURE: + result = GetSmallValue(data) * 3; + break; + case PID_COOLANT_TEMP: + case PID_INTAKE_TEMP: + case PID_AMBIENT_TEMP: + result = GetTemperatureValue(data); + break; + case PID_ABS_ENGINE_LOAD: + result = GetLargeValue(data) * 100 / 255; + break; + case PID_MAF_FLOW: + result = GetLargeValue(data) / 100; + break; + case PID_THROTTLE: + case PID_ENGINE_LOAD: + case PID_FUEL_LEVEL: + result = GetPercentageValue(data); + break; + case PID_SPEED: + case PID_BAROMETRIC: + case PID_INTAKE_PRESSURE: + result = GetSmallValue(data); + break; + case PID_TIMING_ADVANCE: + result = (GetSmallValue(data) - 128) >> 1; + break; + case PID_DISTANCE: + case PID_RUNTIME: + result = GetLargeValue(data); + break; + default: + return false; + } + return true; +} + +bool COBD::GetResponse(byte pid, int& result) +{ + char buffer[OBD_RECV_BUF_SIZE]; + char* data = GetResponse(pid, buffer); + if (!data) { + // try recover next time + WriteData('\r'); + return false; + } + return GetParsedData(pid, data, result); +} + +bool COBD::GetResponsePassive(byte& pid, int& result) +{ + char buffer[OBD_RECV_BUF_SIZE]; + char* data = GetResponse(0, buffer); + if (!data) { + // try recover next time + return false; + } + pid = hex2uint8(data - 3); + return GetParsedData(pid, data, result); +} + +void COBD::Sleep(int seconds) +{ + char cmd[MAX_CMD_LEN]; + strcpy_P(cmd, s_cmd_sleep); + WriteData(cmd); + if (seconds) { + delay((unsigned long)seconds << 10); + WriteData('\r'); + } +} + +bool COBD::Init(bool passive) +{ + unsigned long currentMillis; + unsigned char n; + char prompted; + char buffer[OBD_RECV_BUF_SIZE]; + + for (unsigned char i = 0; i < INIT_CMD_COUNT; i++) { + if (!passive) { + char cmd[MAX_CMD_LEN]; + strcpy_P(cmd, s_initcmd[i]); + WriteData(cmd); + } + n = 0; + prompted = 0; + currentMillis = millis(); + for (;;) { + if (DataAvailable()) { + char c = ReadData(); + if (c == '>') { + buffer[n] = 0; + prompted++; + } else if (n < OBD_RECV_BUF_SIZE - 1) { + buffer[n++] = c; + } + } else if (prompted) { + break; + } else { + unsigned long elapsed = millis() - currentMillis; + if (elapsed > OBD_TIMEOUT_INIT) { + // init timeout + //WriteData("\r"); + return false; + } + } + } + } + errors = 0; + return true; +} diff --git a/libraries/OBD/OBD.h b/libraries/OBD/OBD.h new file mode 100644 index 0000000..1247c49 --- /dev/null +++ b/libraries/OBD/OBD.h @@ -0,0 +1,84 @@ +/************************************************************************* +* OBD-II (ELM327) data accessing library for Arduino +* Distributed under GPL v2.0 +* Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#define OBD_TIMEOUT_SHORT 2000 /* ms */ +#define OBD_TIMEOUT_LONG 7000 /* ms */ +#define OBD_TIMEOUT_INIT 3000 /* ms */ +#define OBD_SERIAL_BAUDRATE 38400 +#define OBD_RECV_BUF_SIZE 48 + +#ifndef OBDUART +#ifdef __AVR_ATmega32U4__ /* for Leonardo */ +#define OBDUART Serial1 +#else +#define OBDUART Serial +#endif +#endif + +// mode 0 pids +#define PID_RPM 0x0C +#define PID_SPEED 0x0D +#define PID_THROTTLE 0x11 +#define PID_ENGINE_LOAD 0x04 +#define PID_COOLANT_TEMP 0x05 +#define PID_INTAKE_TEMP 0x0F +#define PID_MAF_FLOW 0x10 +#define PID_ABS_ENGINE_LOAD 0x43 +#define PID_AMBIENT_TEMP 0x46 +#define PID_FUEL_PRESSURE 0x0A +#define PID_INTAKE_PRESSURE 0x0B +#define PID_BAROMETRIC 0x33 +#define PID_TIMING_ADVANCE 0x0E +#define PID_FUEL_LEVEL 0x2F +#define PID_RUNTIME 0x1F +#define PID_DISTANCE 0x31 + +unsigned int hex2uint16(const char *p); +unsigned char hex2uint8(const char *p); + +class COBD +{ +public: + COBD() + { + dataMode = 1; + errors = 0; + } + bool Init(bool passive = false); + bool ReadSensor(byte pid, int& result, bool passive = false); + void Sleep(int seconds); + // Query and GetResponse for advanced usage only + void Query(byte pid); + virtual char* GetResponse(byte pid, char* buffer); + virtual bool GetResponse(byte pid, int& result); + virtual bool GetResponsePassive(byte& pid, int& result); + virtual bool DataAvailable(); + byte dataMode; + byte errors; + //char recvBuf[OBD_RECV_BUF_SIZE]; +protected: + static bool GetParsedData(byte pid, char* data, int& result); + static int GetPercentageValue(char* data) + { + return (int)hex2uint8(data) * 100 / 255; + } + static int GetLargeValue(char* data) + { + return hex2uint16(data); + } + static int GetSmallValue(char* data) + { + return hex2uint8(data); + } + static int GetTemperatureValue(char* data) + { + return (int)hex2uint8(data) - 40; + } + virtual char ReadData(); + virtual void WriteData(const char* s); + virtual void WriteData(const char c); +}; diff --git a/obdlogger/LCD4Bit_mod.cpp b/obdlogger/LCD4Bit_mod.cpp new file mode 100644 index 0000000..c100c19 --- /dev/null +++ b/obdlogger/LCD4Bit_mod.cpp @@ -0,0 +1,209 @@ +/* +LCD4Bit v0.1 16/Oct/2006 neillzero http://abstractplain.net + +What is this? +An arduino library for comms with HD44780-compatible LCD, in 4-bit mode (saves pins) + +Sources: +- The original "LiquidCrystal" 8-bit library and tutorial + http://www.arduino.cc/en/uploads/Tutorial/LiquidCrystal.zip + http://www.arduino.cc/en/Tutorial/LCDLibrary +- DEM 16216 datasheet http://www.maplin.co.uk/Media/PDFs/N27AZ.pdf +- Massimo's suggested 4-bit code (I took initialization from here) http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1144924220/8 +See also: +- glasspusher's code (probably more correct): http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1160586800/0#0 + +Tested only with a DEM 16216 (maplin "N27AZ" - http://www.maplin.co.uk/Search.aspx?criteria=N27AZ) +If you use this successfully, consider feeding back to the arduino wiki with a note of which LCD it worked on. + +Usage: +see the examples folder of this library distribution. + +*/ + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#include "LCD4Bit_mod.h" + +//command bytes for LCD +#define CMD_CLR 0x01 +#define CMD_RIGHT 0x1C +#define CMD_LEFT 0x18 +#define CMD_HOME 0x02 + +//pulse the Enable pin high (for a microsecond). +//This clocks whatever command or data is in DB4~7 into the LCD controller. +void LCD4Bit_mod::pulseEnablePin(){ + digitalWrite(Enable,LOW); + delayMicroseconds(1); + // send a pulse to enable + digitalWrite(Enable,HIGH); + delayMicroseconds(1); + digitalWrite(Enable,LOW); + delay(1); // pause 1 ms. TODO: what delay, if any, is necessary here? +} + +//push a nibble of data through the the LCD's DB4~7 pins, clocking with the Enable pin. +//We don't care what RS and RW are, here. +void LCD4Bit_mod::pushNibble(byte value) +{ + byte val_nibble= value & 0x0F; //clean the value. (unnecessary) + + for (byte i=DB[0]; i <= DB[3]; i++) { + digitalWrite(i,val_nibble & 01); + val_nibble >>= 1; + } + pulseEnablePin(); +} + +//push a byte of data through the LCD's DB4~7 pins, in two steps, clocking each with the enable pin. +void LCD4Bit_mod::pushByte(byte value) +{ + byte val_lower = value & 0x0F; + byte val_upper = value >> 4; + pushNibble(val_upper); + pushNibble(val_lower); +} + +void LCD4Bit_mod::commandWriteNibble(byte nibble) +{ + digitalWrite(RS, LOW); + if (USING_RW) { digitalWrite(RW, LOW); } + pushNibble(nibble); +} + + +void LCD4Bit_mod::commandWrite(byte value) +{ + digitalWrite(RS, LOW); + if (USING_RW) { digitalWrite(RW, LOW); } + pushByte(value); + //TODO: perhaps better to add a delay after EVERY command, here. many need a delay, apparently. +} + + + + +//print the given character at the current cursor position. overwrites, doesn't insert. +void LCD4Bit_mod::write(byte c) +{ + //set the RS and RW pins to show we're writing data + digitalWrite(RS, HIGH); + if (USING_RW) { digitalWrite(RW, LOW); } + + //let pushByte worry about the intricacies of Enable, nibble order. + pushByte(c); +} + + +//print the given string to the LCD at the current cursor position. overwrites, doesn't insert. +//While I don't understand why this was named printIn (PRINT IN?) in the original LiquidCrystal library, I've preserved it here to maintain the interchangeability of the two libraries. +void LCD4Bit_mod::print(const char* msg) +{ + byte i; //fancy int. avoids compiler warning when comparing i with strlen()'s uint8_t + byte l = strlen(msg); + for (i=0; i < l; i++){ + if (msg[i] >= 20) write(msg[i]); + } +} + + +//send the clear screen command to the LCD +void LCD4Bit_mod::clear() +{ + commandWrite(CMD_CLR); + delay(1); +} + + +// initiatize lcd after a short pause +//while there are hard-coded details here of lines, cursor and blink settings, you can override these original settings after calling .init() +void LCD4Bit_mod::begin () +{ + pinMode(Enable,OUTPUT); + pinMode(RS,OUTPUT); + if (USING_RW) { pinMode(RW,OUTPUT); } + pinMode(DB[0],OUTPUT); + pinMode(DB[1],OUTPUT); + pinMode(DB[2],OUTPUT); + pinMode(DB[3],OUTPUT); + + delay(50); + + //The first 4 nibbles and timings are not in my DEM16217 SYH datasheet, but apparently are HD44780 standard... + commandWriteNibble(0x03); + delay(5); + commandWriteNibble(0x03); + delayMicroseconds(100); + commandWriteNibble(0x03); + delay(5); + + // needed by the LCDs controller + //this being 2 sets up 4-bit mode. + commandWriteNibble(0x02); + commandWriteNibble(0x02); + //todo: make configurable by the user of this library. + //NFXX where + //N = num lines (0=1 line or 1=2 lines). + //F= format (number of dots (0=5x7 or 1=5x10)). + //X=don't care + + byte num_lines_ptn = (num_lines - 1) << 3; + byte dot_format_ptn = 0x00; //5x7 dots. 0x04 is 5x10 + + commandWriteNibble(num_lines_ptn | dot_format_ptn); + delayMicroseconds(60); + + //The rest of the init is not specific to 4-bit mode. + //NOTE: we're writing full bytes now, not nibbles. + + // display control: + // turn display on, cursor off, no blinking + commandWrite(0x0C); + delayMicroseconds(60); + + //clear display + commandWrite(0x01); + delay(3); + + // entry mode set: 06 + // increment automatically, display shift, entire shift off + commandWrite(0x06); + + delay(1);//TODO: remove unnecessary delays +} + + +//non-core stuff -------------------------------------- +//move the cursor to the given absolute position. line numbers start at 1. +//if this is not a 2-line LCD4Bit_mod instance, will always position on first line. +void LCD4Bit_mod::setCursor(byte x, byte line) +{ + //first, put cursor home + commandWrite(CMD_HOME); + //offset 40 chars in if second line requested + x += line * 40; + + //advance the cursor to the right according to position. (second line starts at position 40). + for (byte i=0; i<x; i++) { + commandWrite(0x14); + } +} + +//scroll whole display to left +void LCD4Bit_mod::leftScroll(byte num_chars, unsigned int delay_time) +{ + for (byte i=0; i<num_chars; i++) { + commandWrite(CMD_LEFT); + delay(delay_time); + } +} + +//Improvements ------------------------------------------------ +//Remove the unnecessary delays (e.g. from the end of pulseEnablePin()). +//Allow the user to pass the pins to be used by the LCD in the constructor, and store them as member variables of the class instance. +//------------------------------------------------------------- diff --git a/obdlogger/LCD4Bit_mod.h b/obdlogger/LCD4Bit_mod.h new file mode 100644 index 0000000..7cd5115 --- /dev/null +++ b/obdlogger/LCD4Bit_mod.h @@ -0,0 +1,47 @@ +#ifndef LCD4Bit_mod_h +#define LCD4Bit_mod_h + +#include <inttypes.h> + +class LCD4Bit_mod { +public: + LCD4Bit_mod(byte num_lines = 2):USING_RW(false),RS(8),RW(11),Enable(9) + { + DB[0] = 4; + DB[1] = 5; + DB[2] = 6; + DB[3] = 7; + this->num_lines = num_lines; + } + void commandWrite(byte value); + void begin(); + void write(byte value); + void print(const char* string); + void clear(); + //non-core--------------- + void setCursor(byte x, byte line); + void leftScroll(byte chars, unsigned int delay_time); + //end of non-core-------- + + //4bit only, therefore ideally private but may be needed by user + void commandWriteNibble(byte nibble); +private: + void pulseEnablePin(); + void pushNibble(byte nibble); + void pushByte(byte value); + // --------- PINS ------------------------------------- + //is the RW pin of the LCD under our control? If we're only ever going to write to the LCD, we can use one less microcontroller pin, and just tie the LCD pin to the necessary signal, high or low. + //this stops us sending signals to the RW pin if it isn't being used. + bool USING_RW; + //RS, RW and Enable can be set to whatever you like + byte RS; + byte RW; + byte Enable; + //DB should be an unseparated group of pins - because of lazy coding in pushNibble() + byte DB[4]; //wire these to DB4~7 on LCD. + //how many lines has the LCD? (don't change here - specify on calling constructor) + int num_lines; + +}; + +#endif diff --git a/obdlogger/MPU6050.cpp b/obdlogger/MPU6050.cpp new file mode 100644 index 0000000..302a0c4 --- /dev/null +++ b/obdlogger/MPU6050.cpp @@ -0,0 +1,194 @@ +// MPU-6050 Accelerometer + Gyro +// ----------------------------- +// +// By arduino.cc user "Krodal". +// June 2012 +// Open Source / Public Domain +// +// Using Arduino 1.0.1 +// It will not work with an older version, +// since Wire.endTransmission() uses a parameter +// to hold or release the I2C bus. +// +// Documentation: +// - The InvenSense documents: +// - "MPU-6000 and MPU-6050 Product Specification", +// PS-MPU-6000A.pdf +// - "MPU-6000 and MPU-6050 Register Map and Descriptions", +// RM-MPU-6000A.pdf or RS-MPU-6000A.pdf +// - "MPU-6000/MPU-6050 9-Axis Evaluation Board User Guide" +// AN-MPU-6000EVB.pdf +// +// The accuracy is 16-bits. +// +// Temperature sensor from -40 to +85 degrees Celsius +// 340 per degrees, -512 at 35 degrees. +// +// At power-up, all registers are zero, except these two: +// Register 0x6B (PWR_MGMT_2) = 0x40 (I read zero). +// Register 0x75 (WHO_AM_I) = 0x68. +// + +#include "MPU6050.h" + +// -------------------------------------------------------- +// MPU6050_read +// +// This is a common function to read multiple bytes +// from an I2C device. +// +// It uses the boolean parameter for Wire.endTransMission() +// to be able to hold or release the I2C-bus. +// This is implemented in Arduino 1.0.1. +// +// Only this function is used to read. +// There is no function for a single byte. +// +int MPU6050_read(int start, uint8_t *buffer, int size) +{ + int i, n, error; + + Wire.beginTransmission(MPU6050_I2C_ADDRESS); + n = Wire.write(start); + if (n != 1) + return (-10); + + n = Wire.endTransmission(false); // hold the I2C-bus + if (n != 0) + return (n); + + // Third parameter is true: relase I2C-bus after data is read. + Wire.requestFrom(MPU6050_I2C_ADDRESS, size, true); + i = 0; + while(Wire.available() && i<size) + { + buffer[i++]=Wire.read(); + } + if ( i != size) + return (-11); + + return (0); // return : no error +} + + +// -------------------------------------------------------- +// MPU6050_write +// +// This is a common function to write multiple bytes to an I2C device. +// +// If only a single register is written, +// use the function MPU_6050_write_reg(). +// +// Parameters: +// start : Start address, use a define for the register +// pData : A pointer to the data to write. +// size : The number of bytes to write. +// +// If only a single register is written, a pointer +// to the data has to be used, and the size is +// a single byte: +// int data = 0; // the data to write +// MPU6050_write (MPU6050_PWR_MGMT_1, &c, 1); +// +int MPU6050_write(int start, const uint8_t *pData, int size) +{ + int n, error; + + Wire.beginTransmission(MPU6050_I2C_ADDRESS); + n = Wire.write(start); // write the start address + if (n != 1) + return (-20); + + n = Wire.write(pData, size); // write data bytes + if (n != size) + return (-21); + + error = Wire.endTransmission(true); // release the I2C-bus + if (error != 0) + return (error); + + return (0); // return : no error +} + +// -------------------------------------------------------- +// MPU6050_write_reg +// +// An extra function to write a single register. +// It is just a wrapper around the MPU_6050_write() +// function, and it is only a convenient function +// to make it easier to write a single register. +// +int MPU6050_write_reg(int reg, uint8_t data) +{ + int error; + + error = MPU6050_write(reg, &data, 1); + + return (error); +} + +// -------------------------------------------------------- +// MPU6050_init +// +// Initialization +// +int MPU6050_init() +{ + // default at power-up: + // Gyro at 250 degrees second + // Acceleration at 2g + // Clock source at internal 8MHz + // The device is in sleep mode. + // + uint8_t c; + int error; + error = MPU6050_read (MPU6050_WHO_AM_I, &c, 1); + if (error) return error; + + // According to the datasheet, the 'sleep' bit + // should read a '1'. But I read a '0'. + // That bit has to be cleared, since the sensor + // is in sleep mode at power-up. Even if the + // bit reads '0'. + error = MPU6050_read (MPU6050_PWR_MGMT_2, &c, 1); + if (error) return error; + + // Clear the 'sleep' bit to start the sensor. + MPU6050_write_reg (MPU6050_PWR_MGMT_1, 0); + return 0; +} + +// -------------------------------------------------------- +// MPU6050_readout +// +// Perform an complete read-out +// + +int MPU6050_readout(accel_t_gyro_union* accel_t_gyro) +{ + int error; + + // Read the raw values. + // Read 14 bytes at once, + // containing acceleration, temperature and gyro. + // With the default settings of the MPU-6050, + // there is no filter enabled, and the values + // are not very stable. + error = MPU6050_read (MPU6050_ACCEL_XOUT_H, (uint8_t *)accel_t_gyro, sizeof(accel_t_gyro_union)); + if (error) return error; + + // Swap all high and low bytes. + // After this, the registers values are swapped, + // so the structure name like x_accel_l does no + // longer contain the lower byte. + uint8_t swap; + #define SWAP(x,y) swap = x; x = y; y = swap + + SWAP (accel_t_gyro->reg.x_accel_h, accel_t_gyro->reg.x_accel_l); + SWAP (accel_t_gyro->reg.y_accel_h, accel_t_gyro->reg.y_accel_l); + SWAP (accel_t_gyro->reg.z_accel_h, accel_t_gyro->reg.z_accel_l); + SWAP (accel_t_gyro->reg.t_h, accel_t_gyro->reg.t_l); + SWAP (accel_t_gyro->reg.x_gyro_h, accel_t_gyro->reg.x_gyro_l); + SWAP (accel_t_gyro->reg.y_gyro_h, accel_t_gyro->reg.y_gyro_l); + SWAP (accel_t_gyro->reg.z_gyro_h, accel_t_gyro->reg.z_gyro_l); +} diff --git a/obdlogger/MPU6050.h b/obdlogger/MPU6050.h new file mode 100644 index 0000000..8f3b29b --- /dev/null +++ b/obdlogger/MPU6050.h @@ -0,0 +1,644 @@ +#ifndef MPU6050_H_INCLUDED +#define MPU6050_H_INCLUDED + +#include <Wire.h> + +// The temperature sensor is -40 to +85 degrees Celsius. +// It is a signed integer. +// According to the datasheet: +// 340 per degrees Celsius, -512 at 35 degrees. +// At 0 degrees: -512 - (340 * 35) = -12412 +#define MPU6050_GET_TEMPERATURE(t) (( (float)t + 12412.0) / 340.0) + +// The name of the sensor is "MPU-6050". +// For program code, I omit the '-', +// therefor I use the name "MPU6050....". + + +// Register names according to the datasheet. +// According to the InvenSense document +// "MPU-6000 and MPU-6050 Register Map +// and Descriptions Revision 3.2", there are no registers +// at 0x02 ... 0x18, but according other information +// the registers in that unknown area are for gain +// and offsets. +// +//#define MPU6050_AUX_VDDIO 0x01 // R/W +#define MPU6050_SMPLRT_DIV 0x19 // R/W +#define MPU6050_CONFIG 0x1A // R/W +#define MPU6050_GYRO_CONFIG 0x1B // R/W +#define MPU6050_ACCEL_CONFIG 0x1C // R/W +#define MPU6050_FF_THR 0x1D // R/W +#define MPU6050_FF_DUR 0x1E // R/W +#define MPU6050_MOT_THR 0x1F // R/W +#define MPU6050_MOT_DUR 0x20 // R/W +#define MPU6050_ZRMOT_THR 0x21 // R/W +#define MPU6050_ZRMOT_DUR 0x22 // R/W +//#define MPU6050_FIFO_EN 0x23 // R/W +#define MPU6050_I2C_MST_CTRL 0x24 // R/W +#define MPU6050_I2C_SLV0_ADDR 0x25 // R/W +#define MPU6050_I2C_SLV0_REG 0x26 // R/W +#define MPU6050_I2C_SLV0_CTRL 0x27 // R/W +#define MPU6050_I2C_SLV1_ADDR 0x28 // R/W +#define MPU6050_I2C_SLV1_REG 0x29 // R/W +#define MPU6050_I2C_SLV1_CTRL 0x2A // R/W +#define MPU6050_I2C_SLV2_ADDR 0x2B // R/W +#define MPU6050_I2C_SLV2_REG 0x2C // R/W +#define MPU6050_I2C_SLV2_CTRL 0x2D // R/W +#define MPU6050_I2C_SLV3_ADDR 0x2E // R/W +#define MPU6050_I2C_SLV3_REG 0x2F // R/W +#define MPU6050_I2C_SLV3_CTRL 0x30 // R/W +#define MPU6050_I2C_SLV4_ADDR 0x31 // R/W +#define MPU6050_I2C_SLV4_REG 0x32 // R/W +#define MPU6050_I2C_SLV4_DO 0x33 // R/W +#define MPU6050_I2C_SLV4_CTRL 0x34 // R/W +#define MPU6050_I2C_SLV4_DI 0x35 // R +#define MPU6050_I2C_MST_STATUS 0x36 // R +#define MPU6050_INT_PIN_CFG 0x37 // R/W +#define MPU6050_INT_ENABLE 0x38 // R/W +#define MPU6050_INT_STATUS 0x3A // R +#define MPU6050_ACCEL_XOUT_H 0x3B // R +#define MPU6050_ACCEL_XOUT_L 0x3C // R +#define MPU6050_ACCEL_YOUT_H 0x3D // R +#define MPU6050_ACCEL_YOUT_L 0x3E // R +#define MPU6050_ACCEL_ZOUT_H 0x3F // R +#define MPU6050_ACCEL_ZOUT_L 0x40 // R +#define MPU6050_TEMP_OUT_H 0x41 // R +#define MPU6050_TEMP_OUT_L 0x42 // R +#define MPU6050_GYRO_XOUT_H 0x43 // R +#define MPU6050_GYRO_XOUT_L 0x44 // R +#define MPU6050_GYRO_YOUT_H 0x45 // R +#define MPU6050_GYRO_YOUT_L 0x46 // R +#define MPU6050_GYRO_ZOUT_H 0x47 // R +#define MPU6050_GYRO_ZOUT_L 0x48 // R +#define MPU6050_EXT_SENS_DATA_00 0x49 // R +#define MPU6050_EXT_SENS_DATA_01 0x4A // R +#define MPU6050_EXT_SENS_DATA_02 0x4B // R +#define MPU6050_EXT_SENS_DATA_03 0x4C // R +#define MPU6050_EXT_SENS_DATA_04 0x4D // R +#define MPU6050_EXT_SENS_DATA_05 0x4E // R +#define MPU6050_EXT_SENS_DATA_06 0x4F // R +#define MPU6050_EXT_SENS_DATA_07 0x50 // R +#define MPU6050_EXT_SENS_DATA_08 0x51 // R +#define MPU6050_EXT_SENS_DATA_09 0x52 // R +#define MPU6050_EXT_SENS_DATA_10 0x53 // R +#define MPU6050_EXT_SENS_DATA_11 0x54 // R +#define MPU6050_EXT_SENS_DATA_12 0x55 // R +#define MPU6050_EXT_SENS_DATA_13 0x56 // R +#define MPU6050_EXT_SENS_DATA_14 0x57 // R +#define MPU6050_EXT_SENS_DATA_15 0x58 // R +#define MPU6050_EXT_SENS_DATA_16 0x59 // R +#define MPU6050_EXT_SENS_DATA_17 0x5A // R +#define MPU6050_EXT_SENS_DATA_18 0x5B // R +#define MPU6050_EXT_SENS_DATA_19 0x5C // R +#define MPU6050_EXT_SENS_DATA_20 0x5D // R +#define MPU6050_EXT_SENS_DATA_21 0x5E // R +#define MPU6050_EXT_SENS_DATA_22 0x5F // R +#define MPU6050_EXT_SENS_DATA_23 0x60 // R +#define MPU6050_MOT_DETECT_STATUS 0x61 // R +#define MPU6050_I2C_SLV0_DO 0x63 // R/W +#define MPU6050_I2C_SLV1_DO 0x64 // R/W +#define MPU6050_I2C_SLV2_DO 0x65 // R/W +#define MPU6050_I2C_SLV3_DO 0x66 // R/W +#define MPU6050_I2C_MST_DELAY_CTRL 0x67 // R/W +#define MPU6050_SIGNAL_PATH_RESET 0x68 // R/W +#define MPU6050_MOT_DETECT_CTRL 0x69 // R/W +#define MPU6050_USER_CTRL 0x6A // R/W +#define MPU6050_PWR_MGMT_1 0x6B // R/W +#define MPU6050_PWR_MGMT_2 0x6C // R/W +#define MPU6050_FIFO_COUNTH 0x72 // R/W +#define MPU6050_FIFO_COUNTL 0x73 // R/W +#define MPU6050_FIFO_R_W 0x74 // R/W +#define MPU6050_WHO_AM_I 0x75 // R + + +// Defines for the bits, to be able to change +// between bit number and binary definition. +// By using the bit number, programming the sensor +// is like programming the AVR microcontroller. +// But instead of using "(1<<X)", or "_BV(X)", +// the Arduino "bit(X)" is used. +#define MPU6050_D0 0 +#define MPU6050_D1 1 +#define MPU6050_D2 2 +#define MPU6050_D3 3 +#define MPU6050_D4 4 +#define MPU6050_D5 5 +#define MPU6050_D6 6 +#define MPU6050_D7 7 + +// AUX_VDDIO Register +#define MPU6050_AUX_VDDIO MPU6050_D7 // I2C high: 1=VDD, 0=VLOGIC + +// CONFIG Register +// DLPF is Digital Low Pass Filter for both gyro and accelerometers. +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_DLPF_CFG0 MPU6050_D0 +#define MPU6050_DLPF_CFG1 MPU6050_D1 +#define MPU6050_DLPF_CFG2 MPU6050_D2 +#define MPU6050_EXT_SYNC_SET0 MPU6050_D3 +#define MPU6050_EXT_SYNC_SET1 MPU6050_D4 +#define MPU6050_EXT_SYNC_SET2 MPU6050_D5 + +// Combined definitions for the EXT_SYNC_SET values +#define MPU6050_EXT_SYNC_SET_0 (0) +#define MPU6050_EXT_SYNC_SET_1 (bit(MPU6050_EXT_SYNC_SET0)) +#define MPU6050_EXT_SYNC_SET_2 (bit(MPU6050_EXT_SYNC_SET1)) +#define MPU6050_EXT_SYNC_SET_3 (bit(MPU6050_EXT_SYNC_SET1)|bit(MPU6050_EXT_SYNC_SET0)) +#define MPU6050_EXT_SYNC_SET_4 (bit(MPU6050_EXT_SYNC_SET2)) +#define MPU6050_EXT_SYNC_SET_5 (bit(MPU6050_EXT_SYNC_SET2)|bit(MPU6050_EXT_SYNC_SET0)) +#define MPU6050_EXT_SYNC_SET_6 (bit(MPU6050_EXT_SYNC_SET2)|bit(MPU6050_EXT_SYNC_SET1)) +#define MPU6050_EXT_SYNC_SET_7 (bit(MPU6050_EXT_SYNC_SET2)|bit(MPU6050_EXT_SYNC_SET1)|bit(MPU6050_EXT_SYNC_SET0)) + +// Alternative names for the combined definitions. +#define MPU6050_EXT_SYNC_DISABLED MPU6050_EXT_SYNC_SET_0 +#define MPU6050_EXT_SYNC_TEMP_OUT_L MPU6050_EXT_SYNC_SET_1 +#define MPU6050_EXT_SYNC_GYRO_XOUT_L MPU6050_EXT_SYNC_SET_2 +#define MPU6050_EXT_SYNC_GYRO_YOUT_L MPU6050_EXT_SYNC_SET_3 +#define MPU6050_EXT_SYNC_GYRO_ZOUT_L MPU6050_EXT_SYNC_SET_4 +#define MPU6050_EXT_SYNC_ACCEL_XOUT_L MPU6050_EXT_SYNC_SET_5 +#define MPU6050_EXT_SYNC_ACCEL_YOUT_L MPU6050_EXT_SYNC_SET_6 +#define MPU6050_EXT_SYNC_ACCEL_ZOUT_L MPU6050_EXT_SYNC_SET_7 + +// Combined definitions for the DLPF_CFG values +#define MPU6050_DLPF_CFG_0 (0) +#define MPU6050_DLPF_CFG_1 (bit(MPU6050_DLPF_CFG0)) +#define MPU6050_DLPF_CFG_2 (bit(MPU6050_DLPF_CFG1)) +#define MPU6050_DLPF_CFG_3 (bit(MPU6050_DLPF_CFG1)|bit(MPU6050_DLPF_CFG0)) +#define MPU6050_DLPF_CFG_4 (bit(MPU6050_DLPF_CFG2)) +#define MPU6050_DLPF_CFG_5 (bit(MPU6050_DLPF_CFG2)|bit(MPU6050_DLPF_CFG0)) +#define MPU6050_DLPF_CFG_6 (bit(MPU6050_DLPF_CFG2)|bit(MPU6050_DLPF_CFG1)) +#define MPU6050_DLPF_CFG_7 (bit(MPU6050_DLPF_CFG2)|bit(MPU6050_DLPF_CFG1)|bit(MPU6050_DLPF_CFG0)) + +// Alternative names for the combined definitions +// This name uses the bandwidth (Hz) for the accelometer, +// for the gyro the bandwidth is almost the same. +#define MPU6050_DLPF_260HZ MPU6050_DLPF_CFG_0 +#define MPU6050_DLPF_184HZ MPU6050_DLPF_CFG_1 +#define MPU6050_DLPF_94HZ MPU6050_DLPF_CFG_2 +#define MPU6050_DLPF_44HZ MPU6050_DLPF_CFG_3 +#define MPU6050_DLPF_21HZ MPU6050_DLPF_CFG_4 +#define MPU6050_DLPF_10HZ MPU6050_DLPF_CFG_5 +#define MPU6050_DLPF_5HZ MPU6050_DLPF_CFG_6 +#define MPU6050_DLPF_RESERVED MPU6050_DLPF_CFG_7 + +// GYRO_CONFIG Register +// The XG_ST, YG_ST, ZG_ST are bits for selftest. +// The FS_SEL sets the range for the gyro. +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_FS_SEL0 MPU6050_D3 +#define MPU6050_FS_SEL1 MPU6050_D4 +#define MPU6050_ZG_ST MPU6050_D5 +#define MPU6050_YG_ST MPU6050_D6 +#define MPU6050_XG_ST MPU6050_D7 + +// Combined definitions for the FS_SEL values +#define MPU6050_FS_SEL_0 (0) +#define MPU6050_FS_SEL_1 (bit(MPU6050_FS_SEL0)) +#define MPU6050_FS_SEL_2 (bit(MPU6050_FS_SEL1)) +#define MPU6050_FS_SEL_3 (bit(MPU6050_FS_SEL1)|bit(MPU6050_FS_SEL0)) + +// Alternative names for the combined definitions +// The name uses the range in degrees per second. +#define MPU6050_FS_SEL_250 MPU6050_FS_SEL_0 +#define MPU6050_FS_SEL_500 MPU6050_FS_SEL_1 +#define MPU6050_FS_SEL_1000 MPU6050_FS_SEL_2 +#define MPU6050_FS_SEL_2000 MPU6050_FS_SEL_3 + +// ACCEL_CONFIG Register +// The XA_ST, YA_ST, ZA_ST are bits for selftest. +// The AFS_SEL sets the range for the accelerometer. +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_ACCEL_HPF0 MPU6050_D0 +#define MPU6050_ACCEL_HPF1 MPU6050_D1 +#define MPU6050_ACCEL_HPF2 MPU6050_D2 +#define MPU6050_AFS_SEL0 MPU6050_D3 +#define MPU6050_AFS_SEL1 MPU6050_D4 +#define MPU6050_ZA_ST MPU6050_D5 +#define MPU6050_YA_ST MPU6050_D6 +#define MPU6050_XA_ST MPU6050_D7 + +// Combined definitions for the ACCEL_HPF values +#define MPU6050_ACCEL_HPF_0 (0) +#define MPU6050_ACCEL_HPF_1 (bit(MPU6050_ACCEL_HPF0)) +#define MPU6050_ACCEL_HPF_2 (bit(MPU6050_ACCEL_HPF1)) +#define MPU6050_ACCEL_HPF_3 (bit(MPU6050_ACCEL_HPF1)|bit(MPU6050_ACCEL_HPF0)) +#define MPU6050_ACCEL_HPF_4 (bit(MPU6050_ACCEL_HPF2)) +#define MPU6050_ACCEL_HPF_7 (bit(MPU6050_ACCEL_HPF2)|bit(MPU6050_ACCEL_HPF1)|bit(MPU6050_ACCEL_HPF0)) + +// Alternative names for the combined definitions +// The name uses the Cut-off frequency. +#define MPU6050_ACCEL_HPF_RESET MPU6050_ACCEL_HPF_0 +#define MPU6050_ACCEL_HPF_5HZ MPU6050_ACCEL_HPF_1 +#define MPU6050_ACCEL_HPF_2_5HZ MPU6050_ACCEL_HPF_2 +#define MPU6050_ACCEL_HPF_1_25HZ MPU6050_ACCEL_HPF_3 +#define MPU6050_ACCEL_HPF_0_63HZ MPU6050_ACCEL_HPF_4 +#define MPU6050_ACCEL_HPF_HOLD MPU6050_ACCEL_HPF_7 + +// Combined definitions for the AFS_SEL values +#define MPU6050_AFS_SEL_0 (0) +#define MPU6050_AFS_SEL_1 (bit(MPU6050_AFS_SEL0)) +#define MPU6050_AFS_SEL_2 (bit(MPU6050_AFS_SEL1)) +#define MPU6050_AFS_SEL_3 (bit(MPU6050_AFS_SEL1)|bit(MPU6050_AFS_SEL0)) + +// Alternative names for the combined definitions +// The name uses the full scale range for the accelerometer. +#define MPU6050_AFS_SEL_2G MPU6050_AFS_SEL_0 +#define MPU6050_AFS_SEL_4G MPU6050_AFS_SEL_1 +#define MPU6050_AFS_SEL_8G MPU6050_AFS_SEL_2 +#define MPU6050_AFS_SEL_16G MPU6050_AFS_SEL_3 + +// FIFO_EN Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_SLV0_FIFO_EN MPU6050_D0 +#define MPU6050_SLV1_FIFO_EN MPU6050_D1 +#define MPU6050_SLV2_FIFO_EN MPU6050_D2 +#define MPU6050_ACCEL_FIFO_EN MPU6050_D3 +#define MPU6050_ZG_FIFO_EN MPU6050_D4 +#define MPU6050_YG_FIFO_EN MPU6050_D5 +#define MPU6050_XG_FIFO_EN MPU6050_D6 +#define MPU6050_TEMP_FIFO_EN MPU6050_D7 + +// I2C_MST_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_MST_CLK0 MPU6050_D0 +#define MPU6050_I2C_MST_CLK1 MPU6050_D1 +#define MPU6050_I2C_MST_CLK2 MPU6050_D2 +#define MPU6050_I2C_MST_CLK3 MPU6050_D3 +#define MPU6050_I2C_MST_P_NSR MPU6050_D4 +#define MPU6050_SLV_3_FIFO_EN MPU6050_D5 +#define MPU6050_WAIT_FOR_ES MPU6050_D6 +#define MPU6050_MULT_MST_EN MPU6050_D7 + +// Combined definitions for the I2C_MST_CLK +#define MPU6050_I2C_MST_CLK_0 (0) +#define MPU6050_I2C_MST_CLK_1 (bit(MPU6050_I2C_MST_CLK0)) +#define MPU6050_I2C_MST_CLK_2 (bit(MPU6050_I2C_MST_CLK1)) +#define MPU6050_I2C_MST_CLK_3 (bit(MPU6050_I2C_MST_CLK1)|bit(MPU6050_I2C_MST_CLK0)) +#define MPU6050_I2C_MST_CLK_4 (bit(MPU6050_I2C_MST_CLK2)) +#define MPU6050_I2C_MST_CLK_5 (bit(MPU6050_I2C_MST_CLK2)|bit(MPU6050_I2C_MST_CLK0)) +#define MPU6050_I2C_MST_CLK_6 (bit(MPU6050_I2C_MST_CLK2)|bit(MPU6050_I2C_MST_CLK1)) +#define MPU6050_I2C_MST_CLK_7 (bit(MPU6050_I2C_MST_CLK2)|bit(MPU6050_I2C_MST_CLK1)|bit(MPU6050_I2C_MST_CLK0)) +#define MPU6050_I2C_MST_CLK_8 (bit(MPU6050_I2C_MST_CLK3)) +#define MPU6050_I2C_MST_CLK_9 (bit(MPU6050_I2C_MST_CLK3)|bit(MPU6050_I2C_MST_CLK0)) +#define MPU6050_I2C_MST_CLK_10 (bit(MPU6050_I2C_MST_CLK3)|bit(MPU6050_I2C_MST_CLK1)) +#define MPU6050_I2C_MST_CLK_11 (bit(MPU6050_I2C_MST_CLK3)|bit(MPU6050_I2C_MST_CLK1)|bit(MPU6050_I2C_MST_CLK0)) +#define MPU6050_I2C_MST_CLK_12 (bit(MPU6050_I2C_MST_CLK3)|bit(MPU6050_I2C_MST_CLK2)) +#define MPU6050_I2C_MST_CLK_13 (bit(MPU6050_I2C_MST_CLK3)|bit(MPU6050_I2C_MST_CLK2)|bit(MPU6050_I2C_MST_CLK0)) +#define MPU6050_I2C_MST_CLK_14 (bit(MPU6050_I2C_MST_CLK3)|bit(MPU6050_I2C_MST_CLK2)|bit(MPU6050_I2C_MST_CLK1)) +#define MPU6050_I2C_MST_CLK_15 (bit(MPU6050_I2C_MST_CLK3)|bit(MPU6050_I2C_MST_CLK2)|bit(MPU6050_I2C_MST_CLK1)|bit(MPU6050_I2C_MST_CLK0)) + +// Alternative names for the combined definitions +// The names uses I2C Master Clock Speed in kHz. +#define MPU6050_I2C_MST_CLK_348KHZ MPU6050_I2C_MST_CLK_0 +#define MPU6050_I2C_MST_CLK_333KHZ MPU6050_I2C_MST_CLK_1 +#define MPU6050_I2C_MST_CLK_320KHZ MPU6050_I2C_MST_CLK_2 +#define MPU6050_I2C_MST_CLK_308KHZ MPU6050_I2C_MST_CLK_3 +#define MPU6050_I2C_MST_CLK_296KHZ MPU6050_I2C_MST_CLK_4 +#define MPU6050_I2C_MST_CLK_286KHZ MPU6050_I2C_MST_CLK_5 +#define MPU6050_I2C_MST_CLK_276KHZ MPU6050_I2C_MST_CLK_6 +#define MPU6050_I2C_MST_CLK_267KHZ MPU6050_I2C_MST_CLK_7 +#define MPU6050_I2C_MST_CLK_258KHZ MPU6050_I2C_MST_CLK_8 +#define MPU6050_I2C_MST_CLK_500KHZ MPU6050_I2C_MST_CLK_9 +#define MPU6050_I2C_MST_CLK_471KHZ MPU6050_I2C_MST_CLK_10 +#define MPU6050_I2C_MST_CLK_444KHZ MPU6050_I2C_MST_CLK_11 +#define MPU6050_I2C_MST_CLK_421KHZ MPU6050_I2C_MST_CLK_12 +#define MPU6050_I2C_MST_CLK_400KHZ MPU6050_I2C_MST_CLK_13 +#define MPU6050_I2C_MST_CLK_381KHZ MPU6050_I2C_MST_CLK_14 +#define MPU6050_I2C_MST_CLK_364KHZ MPU6050_I2C_MST_CLK_15 + +// I2C_SLV0_ADDR Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV0_RW MPU6050_D7 + +// I2C_SLV0_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV0_LEN0 MPU6050_D0 +#define MPU6050_I2C_SLV0_LEN1 MPU6050_D1 +#define MPU6050_I2C_SLV0_LEN2 MPU6050_D2 +#define MPU6050_I2C_SLV0_LEN3 MPU6050_D3 +#define MPU6050_I2C_SLV0_GRP MPU6050_D4 +#define MPU6050_I2C_SLV0_REG_DIS MPU6050_D5 +#define MPU6050_I2C_SLV0_BYTE_SW MPU6050_D6 +#define MPU6050_I2C_SLV0_EN MPU6050_D7 + +// A mask for the length +#define MPU6050_I2C_SLV0_LEN_MASK 0x0F + +// I2C_SLV1_ADDR Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV1_RW MPU6050_D7 + +// I2C_SLV1_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV1_LEN0 MPU6050_D0 +#define MPU6050_I2C_SLV1_LEN1 MPU6050_D1 +#define MPU6050_I2C_SLV1_LEN2 MPU6050_D2 +#define MPU6050_I2C_SLV1_LEN3 MPU6050_D3 +#define MPU6050_I2C_SLV1_GRP MPU6050_D4 +#define MPU6050_I2C_SLV1_REG_DIS MPU6050_D5 +#define MPU6050_I2C_SLV1_BYTE_SW MPU6050_D6 +#define MPU6050_I2C_SLV1_EN MPU6050_D7 + +// A mask for the length +#define MPU6050_I2C_SLV1_LEN_MASK 0x0F + +// I2C_SLV2_ADDR Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV2_RW MPU6050_D7 + +// I2C_SLV2_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV2_LEN0 MPU6050_D0 +#define MPU6050_I2C_SLV2_LEN1 MPU6050_D1 +#define MPU6050_I2C_SLV2_LEN2 MPU6050_D2 +#define MPU6050_I2C_SLV2_LEN3 MPU6050_D3 +#define MPU6050_I2C_SLV2_GRP MPU6050_D4 +#define MPU6050_I2C_SLV2_REG_DIS MPU6050_D5 +#define MPU6050_I2C_SLV2_BYTE_SW MPU6050_D6 +#define MPU6050_I2C_SLV2_EN MPU6050_D7 + +// A mask for the length +#define MPU6050_I2C_SLV2_LEN_MASK 0x0F + +// I2C_SLV3_ADDR Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV3_RW MPU6050_D7 + +// I2C_SLV3_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV3_LEN0 MPU6050_D0 +#define MPU6050_I2C_SLV3_LEN1 MPU6050_D1 +#define MPU6050_I2C_SLV3_LEN2 MPU6050_D2 +#define MPU6050_I2C_SLV3_LEN3 MPU6050_D3 +#define MPU6050_I2C_SLV3_GRP MPU6050_D4 +#define MPU6050_I2C_SLV3_REG_DIS MPU6050_D5 +#define MPU6050_I2C_SLV3_BYTE_SW MPU6050_D6 +#define MPU6050_I2C_SLV3_EN MPU6050_D7 + +// A mask for the length +#define MPU6050_I2C_SLV3_LEN_MASK 0x0F + +// I2C_SLV4_ADDR Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV4_RW MPU6050_D7 + +// I2C_SLV4_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_MST_DLY0 MPU6050_D0 +#define MPU6050_I2C_MST_DLY1 MPU6050_D1 +#define MPU6050_I2C_MST_DLY2 MPU6050_D2 +#define MPU6050_I2C_MST_DLY3 MPU6050_D3 +#define MPU6050_I2C_MST_DLY4 MPU6050_D4 +#define MPU6050_I2C_SLV4_REG_DIS MPU6050_D5 +#define MPU6050_I2C_SLV4_INT_EN MPU6050_D6 +#define MPU6050_I2C_SLV4_EN MPU6050_D7 + +// A mask for the delay +#define MPU6050_I2C_MST_DLY_MASK 0x1F + +// I2C_MST_STATUS Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV0_NACK MPU6050_D0 +#define MPU6050_I2C_SLV1_NACK MPU6050_D1 +#define MPU6050_I2C_SLV2_NACK MPU6050_D2 +#define MPU6050_I2C_SLV3_NACK MPU6050_D3 +#define MPU6050_I2C_SLV4_NACK MPU6050_D4 +#define MPU6050_I2C_LOST_ARB MPU6050_D5 +#define MPU6050_I2C_SLV4_DONE MPU6050_D6 +#define MPU6050_PASS_THROUGH MPU6050_D7 + +// I2C_PIN_CFG Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_CLKOUT_EN MPU6050_D0 +#define MPU6050_I2C_BYPASS_EN MPU6050_D1 +#define MPU6050_FSYNC_INT_EN MPU6050_D2 +#define MPU6050_FSYNC_INT_LEVEL MPU6050_D3 +#define MPU6050_INT_RD_CLEAR MPU6050_D4 +#define MPU6050_LATCH_INT_EN MPU6050_D5 +#define MPU6050_INT_OPEN MPU6050_D6 +#define MPU6050_INT_LEVEL MPU6050_D7 + +// INT_ENABLE Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_DATA_RDY_EN MPU6050_D0 +#define MPU6050_I2C_MST_INT_EN MPU6050_D3 +#define MPU6050_FIFO_OFLOW_EN MPU6050_D4 +#define MPU6050_ZMOT_EN MPU6050_D5 +#define MPU6050_MOT_EN MPU6050_D6 +#define MPU6050_FF_EN MPU6050_D7 + +// INT_STATUS Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_DATA_RDY_INT MPU6050_D0 +#define MPU6050_I2C_MST_INT MPU6050_D3 +#define MPU6050_FIFO_OFLOW_INT MPU6050_D4 +#define MPU6050_ZMOT_INT MPU6050_D5 +#define MPU6050_MOT_INT MPU6050_D6 +#define MPU6050_FF_INT MPU6050_D7 + +// MOT_DETECT_STATUS Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_MOT_ZRMOT MPU6050_D0 +#define MPU6050_MOT_ZPOS MPU6050_D2 +#define MPU6050_MOT_ZNEG MPU6050_D3 +#define MPU6050_MOT_YPOS MPU6050_D4 +#define MPU6050_MOT_YNEG MPU6050_D5 +#define MPU6050_MOT_XPOS MPU6050_D6 +#define MPU6050_MOT_XNEG MPU6050_D7 + +// IC2_MST_DELAY_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_I2C_SLV0_DLY_EN MPU6050_D0 +#define MPU6050_I2C_SLV1_DLY_EN MPU6050_D1 +#define MPU6050_I2C_SLV2_DLY_EN MPU6050_D2 +#define MPU6050_I2C_SLV3_DLY_EN MPU6050_D3 +#define MPU6050_I2C_SLV4_DLY_EN MPU6050_D4 +#define MPU6050_DELAY_ES_SHADOW MPU6050_D7 + +// SIGNAL_PATH_RESET Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_TEMP_RESET MPU6050_D0 +#define MPU6050_ACCEL_RESET MPU6050_D1 +#define MPU6050_GYRO_RESET MPU6050_D2 + +// MOT_DETECT_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_MOT_COUNT0 MPU6050_D0 +#define MPU6050_MOT_COUNT1 MPU6050_D1 +#define MPU6050_FF_COUNT0 MPU6050_D2 +#define MPU6050_FF_COUNT1 MPU6050_D3 +#define MPU6050_ACCEL_ON_DELAY0 MPU6050_D4 +#define MPU6050_ACCEL_ON_DELAY1 MPU6050_D5 + +// Combined definitions for the MOT_COUNT +#define MPU6050_MOT_COUNT_0 (0) +#define MPU6050_MOT_COUNT_1 (bit(MPU6050_MOT_COUNT0)) +#define MPU6050_MOT_COUNT_2 (bit(MPU6050_MOT_COUNT1)) +#define MPU6050_MOT_COUNT_3 (bit(MPU6050_MOT_COUNT1)|bit(MPU6050_MOT_COUNT0)) + +// Alternative names for the combined definitions +#define MPU6050_MOT_COUNT_RESET MPU6050_MOT_COUNT_0 + +// Combined definitions for the FF_COUNT +#define MPU6050_FF_COUNT_0 (0) +#define MPU6050_FF_COUNT_1 (bit(MPU6050_FF_COUNT0)) +#define MPU6050_FF_COUNT_2 (bit(MPU6050_FF_COUNT1)) +#define MPU6050_FF_COUNT_3 (bit(MPU6050_FF_COUNT1)|bit(MPU6050_FF_COUNT0)) + +// Alternative names for the combined definitions +#define MPU6050_FF_COUNT_RESET MPU6050_FF_COUNT_0 + +// Combined definitions for the ACCEL_ON_DELAY +#define MPU6050_ACCEL_ON_DELAY_0 (0) +#define MPU6050_ACCEL_ON_DELAY_1 (bit(MPU6050_ACCEL_ON_DELAY0)) +#define MPU6050_ACCEL_ON_DELAY_2 (bit(MPU6050_ACCEL_ON_DELAY1)) +#define MPU6050_ACCEL_ON_DELAY_3 (bit(MPU6050_ACCEL_ON_DELAY1)|bit(MPU6050_ACCEL_ON_DELAY0)) + +// Alternative names for the ACCEL_ON_DELAY +#define MPU6050_ACCEL_ON_DELAY_0MS MPU6050_ACCEL_ON_DELAY_0 +#define MPU6050_ACCEL_ON_DELAY_1MS MPU6050_ACCEL_ON_DELAY_1 +#define MPU6050_ACCEL_ON_DELAY_2MS MPU6050_ACCEL_ON_DELAY_2 +#define MPU6050_ACCEL_ON_DELAY_3MS MPU6050_ACCEL_ON_DELAY_3 + +// USER_CTRL Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_SIG_COND_RESET MPU6050_D0 +#define MPU6050_I2C_MST_RESET MPU6050_D1 +#define MPU6050_FIFO_RESET MPU6050_D2 +#define MPU6050_I2C_IF_DIS MPU6050_D4 // must be 0 for MPU-6050 +#define MPU6050_I2C_MST_EN MPU6050_D5 +#define MPU6050_FIFO_EN MPU6050_D6 + +// PWR_MGMT_1 Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_CLKSEL0 MPU6050_D0 +#define MPU6050_CLKSEL1 MPU6050_D1 +#define MPU6050_CLKSEL2 MPU6050_D2 +#define MPU6050_TEMP_DIS MPU6050_D3 // 1: disable temperature sensor +#define MPU6050_CYCLE MPU6050_D5 // 1: sample and sleep +#define MPU6050_SLEEP MPU6050_D6 // 1: sleep mode +#define MPU6050_DEVICE_RESET MPU6050_D7 // 1: reset to default values + +// Combined definitions for the CLKSEL +#define MPU6050_CLKSEL_0 (0) +#define MPU6050_CLKSEL_1 (bit(MPU6050_CLKSEL0)) +#define MPU6050_CLKSEL_2 (bit(MPU6050_CLKSEL1)) +#define MPU6050_CLKSEL_3 (bit(MPU6050_CLKSEL1)|bit(MPU6050_CLKSEL0)) +#define MPU6050_CLKSEL_4 (bit(MPU6050_CLKSEL2)) +#define MPU6050_CLKSEL_5 (bit(MPU6050_CLKSEL2)|bit(MPU6050_CLKSEL0)) +#define MPU6050_CLKSEL_6 (bit(MPU6050_CLKSEL2)|bit(MPU6050_CLKSEL1)) +#define MPU6050_CLKSEL_7 (bit(MPU6050_CLKSEL2)|bit(MPU6050_CLKSEL1)|bit(MPU6050_CLKSEL0)) + +// Alternative names for the combined definitions +#define MPU6050_CLKSEL_INTERNAL MPU6050_CLKSEL_0 +#define MPU6050_CLKSEL_X MPU6050_CLKSEL_1 +#define MPU6050_CLKSEL_Y MPU6050_CLKSEL_2 +#define MPU6050_CLKSEL_Z MPU6050_CLKSEL_3 +#define MPU6050_CLKSEL_EXT_32KHZ MPU6050_CLKSEL_4 +#define MPU6050_CLKSEL_EXT_19_2MHZ MPU6050_CLKSEL_5 +#define MPU6050_CLKSEL_RESERVED MPU6050_CLKSEL_6 +#define MPU6050_CLKSEL_STOP MPU6050_CLKSEL_7 + +// PWR_MGMT_2 Register +// These are the names for the bits. +// Use these only with the bit() macro. +#define MPU6050_STBY_ZG MPU6050_D0 +#define MPU6050_STBY_YG MPU6050_D1 +#define MPU6050_STBY_XG MPU6050_D2 +#define MPU6050_STBY_ZA MPU6050_D3 +#define MPU6050_STBY_YA MPU6050_D4 +#define MPU6050_STBY_XA MPU6050_D5 +#define MPU6050_LP_WAKE_CTRL0 MPU6050_D6 +#define MPU6050_LP_WAKE_CTRL1 MPU6050_D7 + +// Combined definitions for the LP_WAKE_CTRL +#define MPU6050_LP_WAKE_CTRL_0 (0) +#define MPU6050_LP_WAKE_CTRL_1 (bit(MPU6050_LP_WAKE_CTRL0)) +#define MPU6050_LP_WAKE_CTRL_2 (bit(MPU6050_LP_WAKE_CTRL1)) +#define MPU6050_LP_WAKE_CTRL_3 (bit(MPU6050_LP_WAKE_CTRL1)|bit(MPU6050_LP_WAKE_CTRL0)) + +// Alternative names for the combined definitions +// The names uses the Wake-up Frequency. +#define MPU6050_LP_WAKE_1_25HZ MPU6050_LP_WAKE_CTRL_0 +#define MPU6050_LP_WAKE_2_5HZ MPU6050_LP_WAKE_CTRL_1 +#define MPU6050_LP_WAKE_5HZ MPU6050_LP_WAKE_CTRL_2 +#define MPU6050_LP_WAKE_10HZ MPU6050_LP_WAKE_CTRL_3 + + +// Default I2C address for the MPU-6050 is 0x68. +// But only if the AD0 pin is low. +// Some sensor boards have AD0 high, and the +// I2C address thus becomes 0x69. +#define MPU6050_I2C_ADDRESS 0x68 + + +// Declaring an union for the registers and the axis values. +// The byte order does not match the byte order of +// the compiler and AVR chip. +// The AVR chip (on the Arduino board) has the Low Byte +// at the lower address. +// But the MPU-6050 has a different order: High Byte at +// lower address, so that has to be corrected. +// The register part "reg" is only used internally, +// and are swapped in code. +typedef union accel_t_gyro_union +{ + struct + { + uint8_t x_accel_h; + uint8_t x_accel_l; + uint8_t y_accel_h; + uint8_t y_accel_l; + uint8_t z_accel_h; + uint8_t z_accel_l; + uint8_t t_h; + uint8_t t_l; + uint8_t x_gyro_h; + uint8_t x_gyro_l; + uint8_t y_gyro_h; + uint8_t y_gyro_l; + uint8_t z_gyro_h; + uint8_t z_gyro_l; + } reg; + struct + { + int x_accel; + int y_accel; + int z_accel; + int temperature; + int x_gyro; + int y_gyro; + int z_gyro; + } value; +}; + +int MPU6050_init(); +int MPU6050_readout(accel_t_gyro_union* accel_t_gyro); + +#endif // MPU6050_H_INCLUDED diff --git a/obdlogger/MultiLCD.cpp b/obdlogger/MultiLCD.cpp new file mode 100644 index 0000000..f1e8664 --- /dev/null +++ b/obdlogger/MultiLCD.cpp @@ -0,0 +1,163 @@ +#include <Arduino.h> +#include "MultiLCD.h" + +const PROGMEM unsigned char font16x16[][32] = { +{0x00,0xE0,0xF8,0xFC,0xFE,0x1E,0x07,0x07,0x07,0x07,0x1E,0xFE,0xFC,0xF8,0xF0,0x00,0x00,0x07,0x0F,0x3F,0x3F,0x7C,0x70,0x70,0x70,0x70,0x7C,0x3F,0x1F,0x1F,0x07,0x00},/*0*/ +{0x00,0x00,0x00,0x06,0x07,0x07,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x7F,0x7F,0x7F,0x00,0x00,0x00,0x00,0x00,0x00},/*1*/ +{0x00,0x38,0x3C,0x3E,0x3E,0x0F,0x07,0x07,0x07,0xCF,0xFF,0xFE,0xFE,0x38,0x00,0x00,0x00,0x40,0x40,0x60,0x70,0x78,0x7C,0x7E,0x7F,0x77,0x73,0x71,0x70,0x70,0x00,0x00},/*2*/ +{0x00,0x18,0x1C,0x1E,0x1E,0x0F,0xC7,0xC7,0xE7,0xFF,0xFE,0xBE,0x9C,0x00,0x00,0x00,0x00,0x0C,0x1C,0x3C,0x3C,0x78,0x70,0x70,0x70,0x79,0x7F,0x3F,0x1F,0x0F,0x00,0x00},/*3*/ +{0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x1E,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x06,0x07,0x07,0x07,0x06,0x06,0x06,0x06,0x06,0x7F,0x7F,0x7F,0x7F,0x06,0x06,0x00},/*4*/ +{0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xE7,0xE7,0xE7,0xE7,0xC7,0x87,0x00,0x00,0x00,0x00,0x38,0x78,0x71,0x70,0x70,0x70,0x70,0x70,0x39,0x3F,0x3F,0x1F,0x0F,0x00},/*5*/ +{0x00,0x80,0xE0,0xF0,0xF8,0xFC,0x7F,0x7F,0x6F,0x67,0xE1,0xE1,0xC0,0x80,0x00,0x00,0x00,0x0F,0x1F,0x3F,0x3F,0x78,0x70,0x70,0x70,0x70,0x78,0x3F,0x3F,0x1F,0x0F,0x00},/*6*/ +{0x00,0x07,0x07,0x07,0x07,0x07,0xC7,0xE7,0xF7,0xFF,0x7F,0x3F,0x1F,0x07,0x03,0x01,0x00,0x20,0x38,0x7C,0x7E,0x3F,0x0F,0x07,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*7*/ +{0x00,0x00,0x00,0x1C,0xBE,0xFE,0xFF,0xE7,0xC3,0xC3,0xE7,0xFF,0xFE,0xBE,0x1C,0x00,0x00,0x00,0x0E,0x3F,0x3F,0x7F,0x71,0x60,0x60,0x60,0x71,0x7F,0x3F,0x3F,0x0F,0x00},/*8*/ +{0x00,0x78,0xFC,0xFE,0xFE,0x8F,0x07,0x07,0x07,0x07,0x8F,0xFE,0xFE,0xFC,0xF8,0x00,0x00,0x00,0x00,0x01,0x43,0x43,0x73,0x7B,0x7F,0x7F,0x1F,0x0F,0x07,0x03,0x00,0x00},/*9*/ +}; + +// The 7-bit ASCII character set... +const PROGMEM unsigned char font5x8[][5] = { + { 0x00, 0x00, 0x00, 0x00, 0x00 }, // 20 space + { 0x00, 0x00, 0x5f, 0x00, 0x00 }, // 21 ! + { 0x00, 0x07, 0x00, 0x07, 0x00 }, // 22 " + { 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // 23 # + { 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // 24 $ + { 0x23, 0x13, 0x08, 0x64, 0x62 }, // 25 % + { 0x36, 0x49, 0x55, 0x22, 0x50 }, // 26 & + { 0x00, 0x05, 0x03, 0x00, 0x00 }, // 27 ' + { 0x00, 0x1c, 0x22, 0x41, 0x00 }, // 28 ( + { 0x00, 0x41, 0x22, 0x1c, 0x00 }, // 29 ) + { 0x14, 0x08, 0x3e, 0x08, 0x14 }, // 2a * + { 0x08, 0x08, 0x3e, 0x08, 0x08 }, // 2b + + { 0x00, 0x50, 0x30, 0x00, 0x00 }, // 2c , + { 0x08, 0x08, 0x08, 0x08, 0x08 }, // 2d - + { 0x00, 0x60, 0x60, 0x00, 0x00 }, // 2e . + { 0x20, 0x10, 0x08, 0x04, 0x02 }, // 2f / + { 0x3e, 0x51, 0x49, 0x45, 0x3e }, // 30 0 + { 0x00, 0x42, 0x7f, 0x40, 0x00 }, // 31 1 + { 0x42, 0x61, 0x51, 0x49, 0x46 }, // 32 2 + { 0x21, 0x41, 0x45, 0x4b, 0x31 }, // 33 3 + { 0x18, 0x14, 0x12, 0x7f, 0x10 }, // 34 4 + { 0x27, 0x45, 0x45, 0x45, 0x39 }, // 35 5 + { 0x3c, 0x4a, 0x49, 0x49, 0x30 }, // 36 6 + { 0x01, 0x71, 0x09, 0x05, 0x03 }, // 37 7 + { 0x36, 0x49, 0x49, 0x49, 0x36 }, // 38 8 + { 0x06, 0x49, 0x49, 0x29, 0x1e }, // 39 9 + { 0x00, 0x36, 0x36, 0x00, 0x00 }, // 3a : + { 0x00, 0x56, 0x36, 0x00, 0x00 }, // 3b ; + { 0x08, 0x14, 0x22, 0x41, 0x00 }, // 3c < + { 0x14, 0x14, 0x14, 0x14, 0x14 }, // 3d = + { 0x00, 0x41, 0x22, 0x14, 0x08 }, // 3e > + { 0x02, 0x01, 0x51, 0x09, 0x06 }, // 3f ? + { 0x32, 0x49, 0x79, 0x41, 0x3e }, // 40 @ + { 0x7e, 0x11, 0x11, 0x11, 0x7e }, // 41 A + { 0x7f, 0x49, 0x49, 0x49, 0x36 }, // 42 B + { 0x3e, 0x41, 0x41, 0x41, 0x22 }, // 43 C + { 0x7f, 0x41, 0x41, 0x22, 0x1c }, // 44 D + { 0x7f, 0x49, 0x49, 0x49, 0x41 }, // 45 E + { 0x7f, 0x09, 0x09, 0x09, 0x01 }, // 46 F + { 0x3e, 0x41, 0x49, 0x49, 0x7a }, // 47 G + { 0x7f, 0x08, 0x08, 0x08, 0x7f }, // 48 H + { 0x00, 0x41, 0x7f, 0x41, 0x00 }, // 49 I + { 0x20, 0x40, 0x41, 0x3f, 0x01 }, // 4a J + { 0x7f, 0x08, 0x14, 0x22, 0x41 }, // 4b K + { 0x7f, 0x40, 0x40, 0x40, 0x40 }, // 4c L + { 0x7f, 0x02, 0x0c, 0x02, 0x7f }, // 4d M + { 0x7f, 0x04, 0x08, 0x10, 0x7f }, // 4e N + { 0x3e, 0x41, 0x41, 0x41, 0x3e }, // 4f O + { 0x7f, 0x09, 0x09, 0x09, 0x06 }, // 50 P + { 0x3e, 0x41, 0x51, 0x21, 0x5e }, // 51 Q + { 0x7f, 0x09, 0x19, 0x29, 0x46 }, // 52 R + { 0x46, 0x49, 0x49, 0x49, 0x31 }, // 53 S + { 0x01, 0x01, 0x7f, 0x01, 0x01 }, // 54 T + { 0x3f, 0x40, 0x40, 0x40, 0x3f }, // 55 U + { 0x1f, 0x20, 0x40, 0x20, 0x1f }, // 56 V + { 0x3f, 0x40, 0x38, 0x40, 0x3f }, // 57 W + { 0x63, 0x14, 0x08, 0x14, 0x63 }, // 58 X + { 0x07, 0x08, 0x70, 0x08, 0x07 }, // 59 Y + { 0x61, 0x51, 0x49, 0x45, 0x43 }, // 5a Z + { 0x00, 0x7f, 0x41, 0x41, 0x00 }, // 5b [ + { 0x02, 0x04, 0x08, 0x10, 0x20 }, // 5c backslash + { 0x00, 0x41, 0x41, 0x7f, 0x00 }, // 5d ] + { 0x04, 0x02, 0x01, 0x02, 0x04 }, // 5e ^ + { 0x40, 0x40, 0x40, 0x40, 0x40 }, // 5f _ + { 0x00, 0x01, 0x02, 0x04, 0x00 }, // 60 ` + { 0x20, 0x54, 0x54, 0x54, 0x78 }, // 61 a + { 0x7f, 0x48, 0x44, 0x44, 0x38 }, // 62 b + { 0x38, 0x44, 0x44, 0x44, 0x20 }, // 63 c + { 0x38, 0x44, 0x44, 0x48, 0x7f }, // 64 d + { 0x38, 0x54, 0x54, 0x54, 0x18 }, // 65 e + { 0x08, 0x7e, 0x09, 0x01, 0x02 }, // 66 f + { 0x0c, 0x52, 0x52, 0x52, 0x3e }, // 67 g + { 0x7f, 0x08, 0x04, 0x04, 0x78 }, // 68 h + { 0x00, 0x44, 0x7d, 0x40, 0x00 }, // 69 i + { 0x20, 0x40, 0x44, 0x3d, 0x00 }, // 6a j + { 0x7f, 0x10, 0x28, 0x44, 0x00 }, // 6b k + { 0x00, 0x41, 0x7f, 0x40, 0x00 }, // 6c l + { 0x7c, 0x04, 0x18, 0x04, 0x78 }, // 6d m + { 0x7c, 0x08, 0x04, 0x04, 0x78 }, // 6e n + { 0x38, 0x44, 0x44, 0x44, 0x38 }, // 6f o + { 0x7c, 0x14, 0x14, 0x14, 0x08 }, // 70 p + { 0x08, 0x14, 0x14, 0x18, 0x7c }, // 71 q + { 0x7c, 0x08, 0x04, 0x04, 0x08 }, // 72 r + { 0x48, 0x54, 0x54, 0x54, 0x20 }, // 73 s + { 0x04, 0x3f, 0x44, 0x40, 0x20 }, // 74 t + { 0x3c, 0x40, 0x40, 0x20, 0x7c }, // 75 u + { 0x1c, 0x20, 0x40, 0x20, 0x1c }, // 76 v + { 0x3c, 0x40, 0x30, 0x40, 0x3c }, // 77 w + { 0x44, 0x28, 0x10, 0x28, 0x44 }, // 78 x + { 0x0c, 0x50, 0x50, 0x50, 0x3c }, // 79 y + { 0x44, 0x64, 0x54, 0x4c, 0x44 }, // 7a z + { 0x00, 0x08, 0x36, 0x41, 0x00 }, // 7b { + { 0x00, 0x00, 0x7f, 0x00, 0x00 }, // 7c | + { 0x00, 0x41, 0x36, 0x08, 0x00 }, // 7d } + { 0x10, 0x08, 0x08, 0x10, 0x08 }, // 7e ~ + { 0x00, 0x00, 0x00, 0x00, 0x00 } // 7f +}; + +void LCD_OLED::print(const char* s) +{ + ScI2cMxDisplay8x16Str(OLED_ADDRESS, m_line, m_column, s); +} + +void LCD_OLED::printLarge(const char* s) +{ + delay(5); + while (*s) { + if (*s >= '0' && *s <= '9') { + char data[32]; + memcpy_P(data, font16x16[*s - '0'], 32); + ScI2cMxDisplayDot16x16(OLED_ADDRESS, m_line, m_column, data); + } else { + ScI2cMxFillArea(OLED_ADDRESS, m_line, m_line + 1, m_column, m_column + 16, 0); + } + m_column = (m_column + 16) & 0x7f; + s++; + } +} + +void LCD_OLED::clear() +{ + ScI2cMxFillArea(OLED_ADDRESS, 0, 7, 0, 127, 0); + delay(10); +} + +void LCD_OLED::begin() +{ + I2cInit(); + ScI2cMxReset(OLED_ADDRESS); + delay(10); +} + +void LCD_PCD8544::printLarge(const char* s) +{ + while (*s) { + if (*s >= '0' && *s <= '9') { + unsigned char data[32]; + memcpy_P(data, font16x16[*s - '0'], 32); + drawBitmap(data, 16, 16); + } else { + column = (column + 16) % 84; + } + s++; + } +} diff --git a/obdlogger/MultiLCD.h b/obdlogger/MultiLCD.h new file mode 100644 index 0000000..25137d1 --- /dev/null +++ b/obdlogger/MultiLCD.h @@ -0,0 +1,43 @@ +extern const PROGMEM unsigned char font16x32[][32]; +extern const PROGMEM unsigned char font5x8[][5]; + +#include "PCD8544.h" + +class LCD_PCD8544 : public PCD8544 { +public: + void printLarge(const char* s); + void backlight(bool on) + { + pinMode(7, OUTPUT); + digitalWrite(7, on ? HIGH : LOW); + } +}; + +#include "ZtLib.h" + +#define OLED_ADDRESS 0x27 + +class LCD_OLED : public ZtLib { +public: + void setCursor(unsigned char column, unsigned char line) + { + m_column = column << 3; + m_line = line << 1; + } + void print(const char* s); + void printLarge(const char* s); + void clear(); + void begin(); + void backlight(bool on) {} +private: + unsigned char m_column; + unsigned char m_line; +}; + +#include "LCD4Bit_mod.h" +class LCD_1602 : public LCD4Bit_mod { +public: + void printLarge(const char* s) { print(s); } + void backlight(bool on) {} +}; + diff --git a/obdlogger/OBD.cpp b/obdlogger/OBD.cpp new file mode 100644 index 0000000..b3da34e --- /dev/null +++ b/obdlogger/OBD.cpp @@ -0,0 +1,269 @@ +/************************************************************************* +* OBD-II (ELM327) data accessing library for Arduino +* Distributed under GPL v2.0 +* Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <avr/pgmspace.h> +#include "OBD.h" + +#define INIT_CMD_COUNT 7 +#define MAX_CMD_LEN 6 + +const char PROGMEM s_initcmd[INIT_CMD_COUNT][MAX_CMD_LEN] = {"ATZ\r","ATE0\r","ATL1\r","ATI\r","0100\r","0120\r","0140\r"}; +const char PROGMEM s_searching[] = "SEARCHING"; +const char PROGMEM s_cmd_fmt[] = "%02X%02X 1\r"; +const char PROGMEM s_cmd_sleep[MAX_CMD_LEN] = "atlp\r"; +const char PROGMEM s_response_begin[] = "41 "; + +unsigned int hex2uint16(const char *p) +{ + char c = *p; + unsigned int i = 0; + for (char n = 0; c && n < 4; c = *(++p)) { + if (c >= 'A' && c <= 'F') { + c -= 7; + } else if (c>='a' && c<='f') { + c -= 39; + } else if (c == ' ') { + continue; + } else if (c < '0' || c > '9') { + break; + } + i = (i << 4) | (c & 0xF); + n++; + } + return i; +} + +unsigned char hex2uint8(const char *p) +{ + unsigned char c1 = *p; + unsigned char c2 = *(p + 1); + if (c1 >= 'A' && c1 <= 'F') + c1 -= 7; + else if (c1 >='a' && c1 <= 'f') + c1 -= 39; + else if (c1 < '0' || c1 > '9') + return 0; + + if (c2 >= 'A' && c2 <= 'F') + c2 -= 7; + else if (c2 >= 'a' && c2 <= 'f') + c2 -= 39; + else if (c2 < '0' || c2 > '9') + return 0; + + return c1 << 4 | (c2 & 0xf); +} + +void COBD::Query(unsigned char pid) +{ + char cmd[8]; + sprintf_P(cmd, s_cmd_fmt, dataMode, pid); + WriteData(cmd); +} + +bool COBD::ReadSensor(byte pid, int& result, bool passive) +{ + if (passive) { + bool hasData; + unsigned long tick = millis(); + while (!(hasData = DataAvailable()) && millis() - tick < OBD_TIMEOUT_SHORT); + if (!hasData) { + errors++; + return false; + } + } else { + Query(pid); + } + return GetResponse(pid, result); +} + +bool COBD::DataAvailable() +{ + return OBDUART.available(); +} + +char COBD::ReadData() +{ + return OBDUART.read(); +} + +void COBD::WriteData(const char* s) +{ + OBDUART.write(s); +} + +void COBD::WriteData(const char c) +{ + OBDUART.write(c); +} + +char* COBD::GetResponse(byte pid, char* buffer) +{ + unsigned long startTime = millis(); + byte i = 0; + + for (;;) { + if (DataAvailable()) { + char c = ReadData(); + buffer[i] = c; + if (++i == OBD_RECV_BUF_SIZE - 1) { + // buffer overflow + break; + } + if (c == '>' && i > 6) { + // prompt char reached + break; + } + } else { + buffer[i] = 0; + unsigned int timeout; + if (dataMode != 1 || strstr_P(buffer, s_searching)) { + timeout = OBD_TIMEOUT_LONG; + } else { + timeout = OBD_TIMEOUT_SHORT; + } + if (millis() - startTime > timeout) { + // timeout + errors++; + break; + } + } + } + buffer[i] = 0; + + char *p = buffer; + while ((p = strstr_P(p, s_response_begin))) { + p += 3; + if (pid == 0 || hex2uint8(p) == pid) { + errors = 0; + p += 2; + if (*p == ' ') + return p + 1; + } + } + return 0; +} + +bool COBD::GetParsedData(byte pid, char* data, int& result) +{ + switch (pid) { + case PID_RPM: + result = GetLargeValue(data) >> 2; + break; + case PID_FUEL_PRESSURE: + result = GetSmallValue(data) * 3; + break; + case PID_COOLANT_TEMP: + case PID_INTAKE_TEMP: + case PID_AMBIENT_TEMP: + result = GetTemperatureValue(data); + break; + case PID_ABS_ENGINE_LOAD: + result = GetLargeValue(data) * 100 / 255; + break; + case PID_MAF_FLOW: + result = GetLargeValue(data) / 100; + break; + case PID_THROTTLE: + case PID_ENGINE_LOAD: + case PID_FUEL_LEVEL: + result = GetPercentageValue(data); + break; + case PID_SPEED: + case PID_BAROMETRIC: + case PID_INTAKE_PRESSURE: + result = GetSmallValue(data); + break; + case PID_TIMING_ADVANCE: + result = (GetSmallValue(data) - 128) >> 1; + break; + case PID_DISTANCE: + case PID_RUNTIME: + result = GetLargeValue(data); + break; + default: + return false; + } + return true; +} + +bool COBD::GetResponse(byte pid, int& result) +{ + char buffer[OBD_RECV_BUF_SIZE]; + char* data = GetResponse(pid, buffer); + if (!data) { + // try recover next time + WriteData('\r'); + return false; + } + return GetParsedData(pid, data, result); +} + +bool COBD::GetResponsePassive(byte& pid, int& result) +{ + char buffer[OBD_RECV_BUF_SIZE]; + char* data = GetResponse(0, buffer); + if (!data) { + // try recover next time + return false; + } + pid = hex2uint8(data - 3); + return GetParsedData(pid, data, result); +} + +void COBD::Sleep(int seconds) +{ + char cmd[MAX_CMD_LEN]; + strcpy_P(cmd, s_cmd_sleep); + WriteData(cmd); + if (seconds) { + delay((unsigned long)seconds << 10); + WriteData('\r'); + } +} + +bool COBD::Init(bool passive) +{ + unsigned long currentMillis; + unsigned char n; + char prompted; + char buffer[OBD_RECV_BUF_SIZE]; + + for (unsigned char i = 0; i < INIT_CMD_COUNT; i++) { + if (!passive) { + char cmd[MAX_CMD_LEN]; + strcpy_P(cmd, s_initcmd[i]); + WriteData(cmd); + } + n = 0; + prompted = 0; + currentMillis = millis(); + for (;;) { + if (DataAvailable()) { + char c = ReadData(); + if (c == '>') { + buffer[n] = 0; + prompted++; + } else if (n < OBD_RECV_BUF_SIZE - 1) { + buffer[n++] = c; + } + } else if (prompted) { + break; + } else { + unsigned long elapsed = millis() - currentMillis; + if (elapsed > OBD_TIMEOUT_INIT) { + // init timeout + //WriteData("\r"); + return false; + } + } + } + } + errors = 0; + return true; +} diff --git a/obdlogger/OBD.h b/obdlogger/OBD.h new file mode 100644 index 0000000..1247c49 --- /dev/null +++ b/obdlogger/OBD.h @@ -0,0 +1,84 @@ +/************************************************************************* +* OBD-II (ELM327) data accessing library for Arduino +* Distributed under GPL v2.0 +* Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#define OBD_TIMEOUT_SHORT 2000 /* ms */ +#define OBD_TIMEOUT_LONG 7000 /* ms */ +#define OBD_TIMEOUT_INIT 3000 /* ms */ +#define OBD_SERIAL_BAUDRATE 38400 +#define OBD_RECV_BUF_SIZE 48 + +#ifndef OBDUART +#ifdef __AVR_ATmega32U4__ /* for Leonardo */ +#define OBDUART Serial1 +#else +#define OBDUART Serial +#endif +#endif + +// mode 0 pids +#define PID_RPM 0x0C +#define PID_SPEED 0x0D +#define PID_THROTTLE 0x11 +#define PID_ENGINE_LOAD 0x04 +#define PID_COOLANT_TEMP 0x05 +#define PID_INTAKE_TEMP 0x0F +#define PID_MAF_FLOW 0x10 +#define PID_ABS_ENGINE_LOAD 0x43 +#define PID_AMBIENT_TEMP 0x46 +#define PID_FUEL_PRESSURE 0x0A +#define PID_INTAKE_PRESSURE 0x0B +#define PID_BAROMETRIC 0x33 +#define PID_TIMING_ADVANCE 0x0E +#define PID_FUEL_LEVEL 0x2F +#define PID_RUNTIME 0x1F +#define PID_DISTANCE 0x31 + +unsigned int hex2uint16(const char *p); +unsigned char hex2uint8(const char *p); + +class COBD +{ +public: + COBD() + { + dataMode = 1; + errors = 0; + } + bool Init(bool passive = false); + bool ReadSensor(byte pid, int& result, bool passive = false); + void Sleep(int seconds); + // Query and GetResponse for advanced usage only + void Query(byte pid); + virtual char* GetResponse(byte pid, char* buffer); + virtual bool GetResponse(byte pid, int& result); + virtual bool GetResponsePassive(byte& pid, int& result); + virtual bool DataAvailable(); + byte dataMode; + byte errors; + //char recvBuf[OBD_RECV_BUF_SIZE]; +protected: + static bool GetParsedData(byte pid, char* data, int& result); + static int GetPercentageValue(char* data) + { + return (int)hex2uint8(data) * 100 / 255; + } + static int GetLargeValue(char* data) + { + return hex2uint16(data); + } + static int GetSmallValue(char* data) + { + return hex2uint8(data); + } + static int GetTemperatureValue(char* data) + { + return (int)hex2uint8(data) - 40; + } + virtual char ReadData(); + virtual void WriteData(const char* s); + virtual void WriteData(const char c); +}; diff --git a/obdlogger/PCD8544.cpp b/obdlogger/PCD8544.cpp new file mode 100644 index 0000000..9c85662 --- /dev/null +++ b/obdlogger/PCD8544.cpp @@ -0,0 +1,318 @@ +/* + * PCD8544 - Interface with Philips PCD8544 (or compatible) LCDs. + * + * Copyright (c) 2010 Carlos Rodrigues <cefrodrigues@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "PCD8544.h" + +#if ARDUINO < 100 +#include <WProgram.h> +#else +#include <Arduino.h> +#endif + +#include <avr/pgmspace.h> + + +#define PCD8544_CMD LOW +#define PCD8544_DATA HIGH + +extern const PROGMEM unsigned char font5x8[][5]; + +/* + * If this was a ".h", it would get added to sketches when using + * the "Sketch -> Import Library..." menu on the Arduino IDE... + */ + +PCD8544::PCD8544(unsigned char sclk, unsigned char sdin, + unsigned char dc, unsigned char reset, + unsigned char sce): + pin_sclk(sclk), + pin_sdin(sdin), + pin_dc(dc), + pin_reset(reset), + pin_sce(sce) +{} + + +void PCD8544::begin(unsigned char width, unsigned char height, unsigned char model) +{ + this->width = width; + this->height = height; + + this->column = 0; + this->line = 0; + + // Sanitize the custom glyphs... + memset(this->custom, 0, sizeof(this->custom)); + + // All pins are outputs (these displays cannot be read)... + pinMode(this->pin_sclk, OUTPUT); + pinMode(this->pin_sdin, OUTPUT); + pinMode(this->pin_dc, OUTPUT); + pinMode(this->pin_reset, OUTPUT); + pinMode(this->pin_sce, OUTPUT); + + // Reset the controller state... + digitalWrite(this->pin_reset, HIGH); + digitalWrite(this->pin_sce, HIGH); + digitalWrite(this->pin_reset, LOW); + delay(100); + digitalWrite(this->pin_reset, HIGH); + + // Set the LCD parameters... + this->send(PCD8544_CMD, 0x21); // extended instruction set control (H=1) + this->send(PCD8544_CMD, 0x13); // bias system (1:48) + + if (model == CHIP_ST7576) { + this->send(PCD8544_CMD, 0xe0); // higher Vop, too faint at default + this->send(PCD8544_CMD, 0x05); // partial display mode + } else { + this->send(PCD8544_CMD, 0xc2); // default Vop (3.06 + 66 * 0.06 = 7V) + } + + this->send(PCD8544_CMD, 0x20); // extended instruction set control (H=0) + this->send(PCD8544_CMD, 0x09); // all display segments on + + // Clear RAM contents... + this->clear(); + + // Activate LCD... + this->send(PCD8544_CMD, 0x08); // display blank + this->send(PCD8544_CMD, 0x0c); // normal mode (0x0d = inverse mode) + delay(100); + + // Place the cursor at the origin... + this->send(PCD8544_CMD, 0x80); + this->send(PCD8544_CMD, 0x40); +} + + +void PCD8544::stop() +{ + this->clear(); + this->setPower(false); +} + + +void PCD8544::clear() +{ + this->setCursor(0, 0); + + for (unsigned short i = 0; i < this->width * (this->height/8); i++) { + this->send(PCD8544_DATA, 0x00); + } + + this->setCursor(0, 0); +} + + +void PCD8544::clearLine() +{ + this->setCursor(0, this->line); + + for (unsigned char i = 0; i < this->width; i++) { + this->send(PCD8544_DATA, 0x00); + } + + this->setCursor(0, this->line); +} + + +void PCD8544::setPower(bool on) +{ + this->send(PCD8544_CMD, on ? 0x20 : 0x24); +} + + +inline void PCD8544::display() +{ + this->setPower(true); +} + + +inline void PCD8544::noDisplay() +{ + this->setPower(false); +} + + +void PCD8544::setInverse(bool inverse) +{ + this->send(PCD8544_CMD, inverse ? 0x0d : 0x0c); +} + + +void PCD8544::home() +{ + this->setCursor(0, this->line); +} + + +void PCD8544::setCursor(unsigned char column, unsigned char line) +{ + this->column = (column % this->width); + this->line = (line % (this->height/9 + 1)); + + this->send(PCD8544_CMD, 0x80 | this->column); + this->send(PCD8544_CMD, 0x40 | this->line); +} + + +void PCD8544::createChar(unsigned char chr, const unsigned char *glyph) +{ + // ASCII 0-31 only... + if (chr >= ' ') { + return; + } + + this->custom[chr] = glyph; +} + + +#if ARDUINO < 100 +void PCD8544::write(uint8_t chr) +#else +size_t PCD8544::write(uint8_t chr) +#endif +{ + // ASCII 7-bit only... + if (chr >= 0x80) { +#if ARDUINO < 100 + return; +#else + return 0; +#endif + } + + const unsigned char *glyph; + unsigned char pgm_buffer[5]; + + if (chr >= ' ') { + // Regular ASCII characters are kept in flash to save RAM... + memcpy_P(pgm_buffer, &font5x8[chr - ' '], sizeof(pgm_buffer)); + glyph = pgm_buffer; + } else { + // Custom glyphs, on the other hand, are stored in RAM... + if (custom[chr]) { + glyph = custom[chr]; + } else { + // Default to a space character if unset... + memcpy_P(pgm_buffer, &font5x8[0], sizeof(pgm_buffer)); + glyph = pgm_buffer; + } + } + + // Output one column at a time... + for (unsigned char i = 0; i < 5; i++) { + this->send(PCD8544_DATA, glyph[i]); + } + + // One column between characters... + this->send(PCD8544_DATA, 0x00); + + // Update the cursor position... + this->column = (this->column + 6) % this->width; + + if (this->column == 0) { + this->line = (this->line + 1) % (this->height/9 + 1); + } + +#if ARDUINO >= 100 + return 1; +#endif +} + + +void PCD8544::drawBitmap(const unsigned char *data, unsigned char columns, unsigned char lines) +{ + unsigned char scolumn = this->column; + unsigned char sline = this->line; + + // The bitmap will be clipped at the right/bottom edge of the display... + unsigned char mx = (scolumn + columns > this->width) ? (this->width - scolumn) : columns; + unsigned char my = (sline + lines > this->height/8) ? (this->height/8 - sline) : lines; + + for (unsigned char y = 0; y < my; y++) { + this->setCursor(scolumn, sline + y); + + for (unsigned char x = 0; x < mx; x++) { + this->send(PCD8544_DATA, data[y * columns + x]); + } + } + + // Leave the cursor in a consistent position... + this->setCursor(scolumn + columns, sline); +} + + +void PCD8544::drawColumn(unsigned char lines, unsigned char value) +{ + unsigned char scolumn = this->column; + unsigned char sline = this->line; + + // Keep "value" within range... + if (value > lines*8) { + value = lines*8; + } + + // Find the line where "value" resides... + unsigned char mark = (lines*8 - 1 - value)/8; + + // Clear the lines above the mark... + for (unsigned char line = 0; line < mark; line++) { + this->setCursor(scolumn, sline + line); + this->send(PCD8544_DATA, 0x00); + } + + // Compute the byte to draw at the "mark" line... + unsigned char b = 0xff; + for (unsigned char i = 0; i < lines*8 - mark*8 - value; i++) { + b <<= 1; + } + + this->setCursor(scolumn, sline + mark); + this->send(PCD8544_DATA, b); + + // Fill the lines below the mark... + for (unsigned char line = mark + 1; line < lines; line++) { + this->setCursor(scolumn, sline + line); + this->send(PCD8544_DATA, 0xff); + } + + // Leave the cursor in a consistent position... + this->setCursor(scolumn + 1, sline); +} + + +void PCD8544::send(unsigned char type, unsigned char data) +{ + digitalWrite(this->pin_dc, type); + + digitalWrite(this->pin_sce, LOW); + shiftOut(this->pin_sdin, this->pin_sclk, MSBFIRST, data); + digitalWrite(this->pin_sce, HIGH); +} + + +/* vim: set expandtab ts=4 sw=4: */ diff --git a/obdlogger/PCD8544.h b/obdlogger/PCD8544.h new file mode 100644 index 0000000..927ec47 --- /dev/null +++ b/obdlogger/PCD8544.h @@ -0,0 +1,119 @@ +/* + * PCD8544 - Interface with Philips PCD8544 (or compatible) LCDs. + * + * Copyright (c) 2010 Carlos Rodrigues <cefrodrigues@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef PCD8544_H +#define PCD8544_H + + +#if ARDUINO < 100 +#include <WProgram.h> +#else +#include <Arduino.h> +#endif + + +// Chip variants supported... +#define CHIP_PCD8544 0 +#define CHIP_ST7576 1 + + +class PCD8544: public Print { + public: + // All the pins can be changed from the default values... + PCD8544(unsigned char sclk = 2, /* clock (display pin 2) */ + unsigned char sdin = 3, /* data-in (display pin 3) */ + unsigned char dc = 4, /* data select (display pin 4) */ + unsigned char reset = 6, /* reset (display pin 8) */ + unsigned char sce = 5); /* enable (display pin 5) */ + + // Display initialization (dimensions in pixels)... + void begin(unsigned char width=84, unsigned char height=48, unsigned char model=CHIP_PCD8544); + void stop(); + + // Erase everything on the display... + void clear(); + void clearLine(); // ...or just the current line + + // Control the display's power state... + void setPower(bool on); + + // For compatibility with the LiquidCrystal library... + void display(); + void noDisplay(); + + // Activate white-on-black mode (whole display)... + void setInverse(bool inverse); + + // Place the cursor at the start of the current line... + void home(); + + // Place the cursor at position (column, line)... + void setCursor(unsigned char column, unsigned char line); + + // Assign a user-defined glyph (5x8) to an ASCII character (0-31)... + void createChar(unsigned char chr, const unsigned char *glyph); + + // Write an ASCII character at the current cursor position (7-bit)... +#if ARDUINO < 100 + virtual void write(uint8_t chr); +#else + virtual size_t write(uint8_t chr); +#endif + + // Draw a bitmap at the current cursor position... + void drawBitmap(const unsigned char *data, unsigned char columns, unsigned char lines); + + // Draw a chart element at the current cursor position... + void drawColumn(unsigned char lines, unsigned char value); + + protected: + // Current cursor position... + unsigned char column; + unsigned char line; + + private: + unsigned char pin_sclk; + unsigned char pin_sdin; + unsigned char pin_dc; + unsigned char pin_reset; + unsigned char pin_sce; + + // The size of the display, in pixels... + unsigned char width; + unsigned char height; + + + // User-defined glyphs (below the ASCII space character)... + const unsigned char *custom[' ']; + + // Send a command or data to the display... + void send(unsigned char type, unsigned char data); +}; + + +#endif /* PCD8544_H */ + + +/* vim: set expandtab ts=4 sw=4: */ diff --git a/obdlogger/README.txt b/obdlogger/README.txt new file mode 100644 index 0000000..112b3b5 --- /dev/null +++ b/obdlogger/README.txt @@ -0,0 +1,9 @@ +This is the source code for the Arduino OBD-II data logger, which displays (on a 128¡Á64 OLED display module) and records (to a SD card) a selected set of OBD-II data. For hardware configuration and wiring guide, please refer to: +http://obd.arduinodev.com + +The recorded data is stored in CSV format and the file can be illustrated into a graphic chart by a free service at: +http://obd.arduinodev.com/view.html + +To open the project file (obdlogger.cbp), please download CodeBlocks Arduino Edition (http://www.arduinodev.com/codeblocks). + +The source code is distributed under GPL license.
\ No newline at end of file diff --git a/obdlogger/TinyGPS.cpp b/obdlogger/TinyGPS.cpp new file mode 100644 index 0000000..00a683c --- /dev/null +++ b/obdlogger/TinyGPS.cpp @@ -0,0 +1,426 @@ +/* +TinyGPS - a small GPS library for Arduino providing basic NMEA parsing +Based on work by and "distance_to" and "course_to" courtesy of Maarten Lamers. +Suggestion to add satellites(), course_to(), and cardinal(), by Matt Monson. +Copyright (C) 2008-2012 Mikal Hart +All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "TinyGPS.h" + +#define _GPRMC_TERM "GPRMC" +#define _GPGGA_TERM "GPGGA" + +TinyGPS::TinyGPS() + : _time(GPS_INVALID_TIME) + , _date(GPS_INVALID_DATE) + , _latitude(GPS_INVALID_ANGLE) + , _longitude(GPS_INVALID_ANGLE) + , _altitude(GPS_INVALID_ALTITUDE) + , _speed(GPS_INVALID_SPEED) + , _course(GPS_INVALID_ANGLE) + , _hdop(GPS_INVALID_HDOP) + , _numsats(GPS_INVALID_SATELLITES) + , _last_time_fix(GPS_INVALID_FIX_TIME) + , _last_position_fix(GPS_INVALID_FIX_TIME) + , _parity(0) + , _is_checksum_term(false) + , _sentence_type(_GPS_SENTENCE_OTHER) + , _term_number(0) + , _term_offset(0) + , _gps_data_good(false) +#ifndef _GPS_NO_STATS + , _encoded_characters(0) + , _good_sentences(0) + , _failed_checksum(0) +#endif +{ + _term[0] = '\0'; +} + +// +// public methods +// + +bool TinyGPS::encode(char c) +{ + bool valid_sentence = false; + +#ifndef _GPS_NO_STATS + ++_encoded_characters; +#endif + switch(c) + { + case ',': // term terminators + _parity ^= c; + case '\r': + case '\n': + case '*': + if (_term_offset < sizeof(_term)) + { + _term[_term_offset] = 0; + valid_sentence = term_complete(); + } + ++_term_number; + _term_offset = 0; + _is_checksum_term = c == '*'; + return valid_sentence; + + case '$': // sentence begin + _term_number = _term_offset = 0; + _parity = 0; + _sentence_type = _GPS_SENTENCE_OTHER; + _is_checksum_term = false; + _gps_data_good = false; + return valid_sentence; + } + + // ordinary characters + if (_term_offset < sizeof(_term) - 1) + _term[_term_offset++] = c; + if (!_is_checksum_term) + _parity ^= c; + + return valid_sentence; +} + +#ifndef _GPS_NO_STATS +void TinyGPS::stats(unsigned long *chars, unsigned short *sentences, unsigned short *failed_cs) +{ + if (chars) *chars = _encoded_characters; + if (sentences) *sentences = _good_sentences; + if (failed_cs) *failed_cs = _failed_checksum; +} +#endif + +// +// internal utilities +// +int TinyGPS::from_hex(char a) +{ + if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else + return a - '0'; +} + +unsigned long TinyGPS::parse_decimal() +{ + char *p = _term; + bool isneg = *p == '-'; + if (isneg) ++p; + unsigned long ret = 100UL * gpsatol(p); + while (gpsisdigit(*p)) ++p; + if (*p == '.') + { + if (gpsisdigit(p[1])) + { + ret += 10 * (p[1] - '0'); + if (gpsisdigit(p[2])) + ret += p[2] - '0'; + } + } + return isneg ? -ret : ret; +} + +unsigned long TinyGPS::parse_degrees() +{ + char *p; + unsigned long left = gpsatol(_term); + unsigned long tenk_minutes = (left % 100UL) * 10000UL; + for (p=_term; gpsisdigit(*p); ++p); + if (*p == '.') + { + unsigned long mult = 1000; + while (gpsisdigit(*++p)) + { + tenk_minutes += mult * (*p - '0'); + mult /= 10; + } + } + return (left / 100) * 100000 + tenk_minutes / 6; +} + +#define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number) + +// Processes a just-completed term +// Returns true if new sentence has just passed checksum test and is validated +bool TinyGPS::term_complete() +{ + if (_is_checksum_term) + { + byte checksum = 16 * from_hex(_term[0]) + from_hex(_term[1]); + if (checksum == _parity) + { + if (_gps_data_good) + { +#ifndef _GPS_NO_STATS + ++_good_sentences; +#endif + _last_time_fix = _new_time_fix; + _last_position_fix = _new_position_fix; + + switch(_sentence_type) + { + case _GPS_SENTENCE_GPRMC: + _time = _new_time; + _date = _new_date; + _latitude = _new_latitude; + _longitude = _new_longitude; + _speed = _new_speed; + _course = _new_course; + break; + case _GPS_SENTENCE_GPGGA: + _altitude = _new_altitude; + _time = _new_time; + _latitude = _new_latitude; + _longitude = _new_longitude; + _numsats = _new_numsats; + _hdop = _new_hdop; + break; + } + + return true; + } + } + +#ifndef _GPS_NO_STATS + else + ++_failed_checksum; +#endif + return false; + } + + // the first term determines the sentence type + if (_term_number == 0) + { + if (!gpsstrcmp(_term, _GPRMC_TERM)) + _sentence_type = _GPS_SENTENCE_GPRMC; + else if (!gpsstrcmp(_term, _GPGGA_TERM)) + _sentence_type = _GPS_SENTENCE_GPGGA; + else + _sentence_type = _GPS_SENTENCE_OTHER; + return false; + } + + if (_sentence_type != _GPS_SENTENCE_OTHER && _term[0]) + switch(COMBINE(_sentence_type, _term_number)) + { + case COMBINE(_GPS_SENTENCE_GPRMC, 1): // Time in both sentences + case COMBINE(_GPS_SENTENCE_GPGGA, 1): + _new_time = parse_decimal(); + _new_time_fix = millis(); + break; + case COMBINE(_GPS_SENTENCE_GPRMC, 2): // GPRMC validity + _gps_data_good = _term[0] == 'A'; + break; + case COMBINE(_GPS_SENTENCE_GPRMC, 3): // Latitude + case COMBINE(_GPS_SENTENCE_GPGGA, 2): + _new_latitude = parse_degrees(); + _new_position_fix = millis(); + break; + case COMBINE(_GPS_SENTENCE_GPRMC, 4): // N/S + case COMBINE(_GPS_SENTENCE_GPGGA, 3): + if (_term[0] == 'S') + _new_latitude = -_new_latitude; + break; + case COMBINE(_GPS_SENTENCE_GPRMC, 5): // Longitude + case COMBINE(_GPS_SENTENCE_GPGGA, 4): + _new_longitude = parse_degrees(); + break; + case COMBINE(_GPS_SENTENCE_GPRMC, 6): // E/W + case COMBINE(_GPS_SENTENCE_GPGGA, 5): + if (_term[0] == 'W') + _new_longitude = -_new_longitude; + break; + case COMBINE(_GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC) + _new_speed = parse_decimal(); + break; + case COMBINE(_GPS_SENTENCE_GPRMC, 8): // Course (GPRMC) + _new_course = parse_decimal(); + break; + case COMBINE(_GPS_SENTENCE_GPRMC, 9): // Date (GPRMC) + _new_date = gpsatol(_term); + break; + case COMBINE(_GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA) + _gps_data_good = _term[0] > '0'; + break; + case COMBINE(_GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA) + _new_numsats = (unsigned char)atoi(_term); + break; + case COMBINE(_GPS_SENTENCE_GPGGA, 8): // HDOP + _new_hdop = parse_decimal(); + break; + case COMBINE(_GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA) + _new_altitude = parse_decimal(); + break; + } + + return false; +} + +long TinyGPS::gpsatol(const char *str) +{ + long ret = 0; + while (gpsisdigit(*str)) + ret = 10 * ret + *str++ - '0'; + return ret; +} + +int TinyGPS::gpsstrcmp(const char *str1, const char *str2) +{ + while (*str1 && *str1 == *str2) + ++str1, ++str2; + return *str1; +} + +/* static */ +float TinyGPS::distance_between (float lat1, float long1, float lat2, float long2) +{ + // returns distance in meters between two positions, both specified + // as signed decimal-degrees latitude and longitude. Uses great-circle + // distance computation for hypothetical sphere of radius 6372795 meters. + // Because Earth is no exact sphere, rounding errors may be up to 0.5%. + // Courtesy of Maarten Lamers + float delta = radians(long1-long2); + float sdlong = sin(delta); + float cdlong = cos(delta); + lat1 = radians(lat1); + lat2 = radians(lat2); + float slat1 = sin(lat1); + float clat1 = cos(lat1); + float slat2 = sin(lat2); + float clat2 = cos(lat2); + delta = (clat1 * slat2) - (slat1 * clat2 * cdlong); + delta = sq(delta); + delta += sq(clat2 * sdlong); + delta = sqrt(delta); + float denom = (slat1 * slat2) + (clat1 * clat2 * cdlong); + delta = atan2(delta, denom); + return delta * 6372795; +} + +float TinyGPS::course_to (float lat1, float long1, float lat2, float long2) +{ + // returns course in degrees (North=0, West=270) from position 1 to position 2, + // both specified as signed decimal-degrees latitude and longitude. + // Because Earth is no exact sphere, calculated course may be off by a tiny fraction. + // Courtesy of Maarten Lamers + float dlon = radians(long2-long1); + lat1 = radians(lat1); + lat2 = radians(lat2); + float a1 = sin(dlon) * cos(lat2); + float a2 = sin(lat1) * cos(lat2) * cos(dlon); + a2 = cos(lat1) * sin(lat2) - a2; + a2 = atan2(a1, a2); + if (a2 < 0.0) + { + a2 += TWO_PI; + } + return degrees(a2); +} + +const char *TinyGPS::cardinal (float course) +{ + static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; + + int direction = (int)((course + 11.25f) / 22.5f); + return directions[direction % 16]; +} + +// lat/long in hundred thousandths of a degree and age of fix in milliseconds +void TinyGPS::get_position(long *latitude, long *longitude, unsigned long *fix_age) +{ + if (latitude) *latitude = _latitude; + if (longitude) *longitude = _longitude; + if (fix_age) *fix_age = _last_position_fix == GPS_INVALID_FIX_TIME ? +GPS_INVALID_AGE : millis() - _last_position_fix; +} + +// date as ddmmyy, time as hhmmsscc, and age in milliseconds +void TinyGPS::get_datetime(unsigned long *date, unsigned long *time, unsigned long *age) +{ + if (date) *date = _date; + if (time) *time = _time; + if (age) *age = _last_time_fix == GPS_INVALID_FIX_TIME ? +GPS_INVALID_AGE : millis() - _last_time_fix; +} + +void TinyGPS::f_get_position(float *latitude, float *longitude, unsigned long *fix_age) +{ + long lat, lon; + get_position(&lat, &lon, fix_age); + *latitude = lat == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : (lat / 100000.0); + *longitude = lat == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : (lon / 100000.0); +} + +void TinyGPS::crack_datetime(int *year, byte *month, byte *day, + byte *hour, byte *minute, byte *second, byte *hundredths, unsigned long *age) +{ + unsigned long date, time; + get_datetime(&date, &time, age); + if (year) + { + *year = date % 100; + *year += *year > 80 ? 1900 : 2000; + } + if (month) *month = (date / 100) % 100; + if (day) *day = date / 10000; + if (hour) *hour = time / 1000000; + if (minute) *minute = (time / 10000) % 100; + if (second) *second = (time / 100) % 100; + if (hundredths) *hundredths = time % 100; +} + +float TinyGPS::f_altitude() +{ + return _altitude == GPS_INVALID_ALTITUDE ? GPS_INVALID_F_ALTITUDE : _altitude / 100.0; +} + +float TinyGPS::f_course() +{ + return _course == GPS_INVALID_ANGLE ? GPS_INVALID_F_ANGLE : _course / 100.0; +} + +float TinyGPS::f_speed_knots() +{ + return _speed == GPS_INVALID_SPEED ? GPS_INVALID_F_SPEED : _speed / 100.0; +} + +float TinyGPS::f_speed_mph() +{ + float sk = f_speed_knots(); + return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_MPH_PER_KNOT * f_speed_knots(); +} + +float TinyGPS::f_speed_mps() +{ + float sk = f_speed_knots(); + return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_MPS_PER_KNOT * f_speed_knots(); +} + +float TinyGPS::f_speed_kmph() +{ + float sk = f_speed_knots(); + return sk == GPS_INVALID_F_SPEED ? GPS_INVALID_F_SPEED : _GPS_KMPH_PER_KNOT * f_speed_knots(); +} + +const float TinyGPS::GPS_INVALID_F_ANGLE = 1000.0; +const float TinyGPS::GPS_INVALID_F_ALTITUDE = 1000000.0; +const float TinyGPS::GPS_INVALID_F_SPEED = -1.0; diff --git a/obdlogger/TinyGPS.h b/obdlogger/TinyGPS.h new file mode 100644 index 0000000..89d4148 --- /dev/null +++ b/obdlogger/TinyGPS.h @@ -0,0 +1,153 @@ +/* +TinyGPS - a small GPS library for Arduino providing basic NMEA parsing +Based on work by and "distance_to" and "course_to" courtesy of Maarten Lamers. +Suggestion to add satellites(), course_to(), and cardinal(), by Matt Monson. +Copyright (C) 2008-2012 Mikal Hart +All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef TinyGPS_h +#define TinyGPS_h + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#define _GPS_VERSION 12 // software version of this library +#define _GPS_MPH_PER_KNOT 1.15077945 +#define _GPS_MPS_PER_KNOT 0.51444444 +#define _GPS_KMPH_PER_KNOT 1.852 +#define _GPS_MILES_PER_METER 0.00062137112 +#define _GPS_KM_PER_METER 0.001 +// #define _GPS_NO_STATS + +class TinyGPS +{ +public: + enum { + GPS_INVALID_AGE = 0xFFFFFFFF, GPS_INVALID_ANGLE = 999999999, + GPS_INVALID_ALTITUDE = 999999999, GPS_INVALID_DATE = 0, + GPS_INVALID_TIME = 0xFFFFFFFF, GPS_INVALID_SPEED = 999999999, + GPS_INVALID_FIX_TIME = 0xFFFFFFFF, GPS_INVALID_SATELLITES = 0xFF, + GPS_INVALID_HDOP = 0xFFFFFFFF + }; + + static const float GPS_INVALID_F_ANGLE, GPS_INVALID_F_ALTITUDE, GPS_INVALID_F_SPEED; + + TinyGPS(); + bool encode(char c); // process one character received from GPS + TinyGPS &operator << (char c) {encode(c); return *this;} + + // lat/long in hundred thousandths of a degree and age of fix in milliseconds + void get_position(long *latitude, long *longitude, unsigned long *fix_age = 0); + + // date as ddmmyy, time as hhmmsscc, and age in milliseconds + void get_datetime(unsigned long *date, unsigned long *time, unsigned long *age = 0); + + // signed altitude in centimeters (from GPGGA sentence) + inline long altitude() { return _altitude; } + + // course in last full GPRMC sentence in 100th of a degree + inline unsigned long course() { return _course; } + + // speed in last full GPRMC sentence in 100ths of a knot + inline unsigned long speed() { return _speed; } + + // satellites used in last full GPGGA sentence + inline unsigned short satellites() { return _numsats; } + + // horizontal dilution of precision in 100ths + inline unsigned long hdop() { return _hdop; } + + void f_get_position(float *latitude, float *longitude, unsigned long *fix_age = 0); + void crack_datetime(int *year, byte *month, byte *day, + byte *hour, byte *minute, byte *second, byte *hundredths = 0, unsigned long *fix_age = 0); + float f_altitude(); + float f_course(); + float f_speed_knots(); + float f_speed_mph(); + float f_speed_mps(); + float f_speed_kmph(); + + static int library_version() { return _GPS_VERSION; } + + static float distance_between (float lat1, float long1, float lat2, float long2); + static float course_to (float lat1, float long1, float lat2, float long2); + static const char *cardinal(float course); + +#ifndef _GPS_NO_STATS + void stats(unsigned long *chars, unsigned short *good_sentences, unsigned short *failed_cs); +#endif + +private: + enum {_GPS_SENTENCE_GPGGA, _GPS_SENTENCE_GPRMC, _GPS_SENTENCE_OTHER}; + + // properties + unsigned long _time, _new_time; + unsigned long _date, _new_date; + long _latitude, _new_latitude; + long _longitude, _new_longitude; + long _altitude, _new_altitude; + unsigned long _speed, _new_speed; + unsigned long _course, _new_course; + unsigned long _hdop, _new_hdop; + unsigned short _numsats, _new_numsats; + + unsigned long _last_time_fix, _new_time_fix; + unsigned long _last_position_fix, _new_position_fix; + + // parsing state variables + byte _parity; + bool _is_checksum_term; + char _term[15]; + byte _sentence_type; + byte _term_number; + byte _term_offset; + bool _gps_data_good; + +#ifndef _GPS_NO_STATS + // statistics + unsigned long _encoded_characters; + unsigned short _good_sentences; + unsigned short _failed_checksum; + unsigned short _passed_checksum; +#endif + + // internal utilities + int from_hex(char a); + unsigned long parse_decimal(); + unsigned long parse_degrees(); + bool term_complete(); + bool gpsisdigit(char c) { return c >= '0' && c <= '9'; } + long gpsatol(const char *str); + int gpsstrcmp(const char *str1, const char *str2); +}; + +#if !defined(ARDUINO) +// Arduino 0012 workaround +#undef int +#undef char +#undef long +#undef byte +#undef float +#undef abs +#undef round +#endif + +#endif diff --git a/obdlogger/ZtLib.cpp b/obdlogger/ZtLib.cpp new file mode 100644 index 0000000..e458f06 --- /dev/null +++ b/obdlogger/ZtLib.cpp @@ -0,0 +1,601 @@ +/* + ZtLib.cpp - ZT module Drive Library for Wiring & Arduino + Copyright (c) 2012 Alvin Li(Kozig/www.kozig.com). All right reserved. + This library is free software; + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Version:V1.1 +*/ + +extern "C" { + #include <stdlib.h> + #include <string.h> + #include <inttypes.h> + #include "../../Wire/utility/twi.h" +} + +#include "ZtLib.h" + +///ZT.SEG8B4A036A PART///-------------------------------------------------------------------s +unsigned char codetable[] = +{ + 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, 0x77, 0x7C,0x39,0x5E,0x79,0x71,0x00 +}; + +// Public Methods ////////////////////////////////////////////////////////////// +/* + * Function I2cInit + * Desc TWI/I2C init + * Input none + * Output none + */ +void ZtLib::I2cInit(void) +{ + twi_init(); +} +/* + * Function Seg8b4a036aSleep + * Desc Set ZT.SEG8B4A036A Go to Sleep + * Input addr:ZT.SEG8B4A036A Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aSleep(uint8_t addr) +{ + uint8_t buff[5]={REG_SLEEP, SLEEP_ON, 0, 0, 0}; + + return twi_writeTo(addr, buff, 5, 1, 1); +} +/* + * Function Seg8b4a036aUnSleep + * Desc Set ZT.SEG8B4A036A Wait Up From Sleep + * Input addr:ZT.SEG8B4A036A Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aUnSleep(uint8_t addr) +{ + uint8_t buff[5]={REG_SLEEP, SLEEP_OFF, 0, 0, 0}; + + return twi_writeTo(addr, buff, 5, 1, 1); +} +/* + * Function Seg8b4a036aReadState + * Desc Read ZT.SEG8B4A036A Status + * Input addr:ZT.SEG8B4A036A Address + * Output !=0xFF ZT.SC-I2CMx Status + * 0xFF .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aReadState(uint8_t addr) +{ + uint8_t state = 0xFF; + uint8_t temp; + uint8_t buff[1] = {REG_STATUS}; + temp = twi_writeTo(addr, buff, 1, 1, 0); // no stop + if (temp ==0) + { + temp = twi_readFrom(addr, buff, 1, 1); + } + if (temp==1) + { + state = buff[0]; + } + + return state; +} +/* + * Function Seg8b4a036aReadVersion + * Desc Read ZT.SEG8B4A036A Fireware Version + * Input addr:ZT.SEG8B4A036A Address + *buf:Version Buffer + * Output .. number bytes of Version Read out + */ +int ZtLib::Seg8b4a036aReadVersion(uint8_t addr, uint8_t *buf) +{ + uint8_t state = 0xFF; + uint8_t temp; + uint8_t regv[1] = {REG_VERSION}; + temp = twi_writeTo(addr, regv, 1, 1, 0); // no stop + if (temp ==0) + { + temp = twi_readFrom(addr, &(*buf), 19, 1); + } + return temp; +} +/* + * Function Seg8b4a036aDisplayDec + * Desc ZT.SEG8B4A036A Display decimal numeral + * Input addr:ZT.SEG8B4A036A Address + val: Display Val + bitnum:Display Bit Number + dotbit: Dot Display + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aDisplayDec(uint8_t addr,unsigned short val, uint8_t bitnum, uint8_t dotbit) +{ + uint8_t i; + uint8_t segnum[5]; + if (val>9999) return 0xFF; + + segnum[0] = REG_DAT; + segnum[1] = val%10; + segnum[2] = (val%100)/10; + segnum[3] = (val/100)%10; + segnum[4] = val/1000; + for (i=1; i<5; i++) + { + segnum[i] = codetable[segnum[i]]; + if (dotbit&0x01) + { + segnum[i] |= 0x80; + } + dotbit >>= 1; + } + + if (bitnum==DISP_0BIT) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;segnum[1] = 0;} + else if (bitnum==DISP_1BIT) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;} + else if (bitnum==DISP_2BIT) {segnum[4] = 0;segnum[3] = 0;} + else if (bitnum==DISP_3BIT) {segnum[4] = 0;} + else if (bitnum==DISP_AUTO) + { + if (val<10) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;} + else if (val<100) {segnum[4] = 0;segnum[3] = 0;} + else if (val<1000) {segnum[4] = 0;} + } + + return twi_writeTo(addr, segnum, 5, 1, 1); +} +/* + * Function Seg8b4a036aDisplayHex + * Desc Read ZT.SEG8B4A036A Display hexadecimal number + * Input addr:ZT.SEG8B4A036A Address + val: Display Val + bitnum:Display Bit Number + dotbit: Dot Display + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aDisplayHex(uint8_t addr,unsigned short val, uint8_t bitnum, uint8_t dotbit) +{ + uint8_t i; + unsigned short temp; + uint8_t segnum[5]; + segnum[0] = REG_DAT; + temp = val; + for (i=1; i<5; i++) + { + segnum[i] = temp&0x000F; + temp >>= 4; + segnum[i] = codetable[segnum[i]]; + if (dotbit&0x01) + { + segnum[i] |= 0x80; + } + dotbit >>= 1; + } + + if (bitnum==DISP_0BIT) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;segnum[1] = 0;} + else if (bitnum==DISP_1BIT) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;} + else if (bitnum==DISP_2BIT) {segnum[4] = 0;segnum[3] = 0;} + else if (bitnum==DISP_3BIT) {segnum[4] = 0;} + else if (bitnum==DISP_AUTO) + { + if (!(val&0xFFF0)) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;} + else if (!(val&0xFF00)) {segnum[4] = 0;segnum[3] = 0;} + else if (!(val&0xF000)) {segnum[4] = 0;} + } + + return twi_writeTo(addr, segnum, 5, 1, 1); +} +/* + * Function Seg8b4a036aSetBrightness + * Desc Set ZT.SEG8B4A036A Brightness + * Input addr:ZT.SEG8B4A036A Address + OnDelay: + OffDelay: + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aSetBrightness(uint8_t addr, uint8_t OnDelay, uint8_t OffDelay) +{ + uint8_t buff[5] = {REG_BRIGHTNESS, OnDelay, OffDelay, 0, 0}; + return twi_writeTo(addr, buff, 5, 1, 1); +} +/* + * Function Seg8b4a036aSetAddress + * Desc Set ZT.SEG8B4A036A New Address + * Input val:ZT.SEG8B4A036A Address New Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aSetAddress(uint8_t val) +{ + uint8_t buff[2] = {REG_ADDRESS, val}; + return twi_writeTo(ZTSEG8B4A036A_DADDR, buff, 2, 1, 1); +} +/* + * Function Seg8b4a036aDisplayBuff + * Desc Set ZT.SEG8B4A036A Brightness + * Input addr:ZT.SEG8B4A036A Address + *buf: Display buffer + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aDisplayBuff(uint8_t addr,uint8_t *buf) +{ + uint8_t buff[5]={REG_DAT, buf[0], buf[1], buf[2], buf[3]}; + + return twi_writeTo(addr, buff, 5, 1, 1); +} + + +///ZT.ScI2cMx PART///------------------------------------------------------------------- +/* + * Function ScI2cMxReadState + * Desc Read ZT.SC-I2CMx Status + * Input addr:ZT.SC-I2CMx Address + * Output !=0xFF ZT.SC-I2CMx Status + * 0xFF .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxReadState(uint8_t addr) +{ + uint8_t state = 0xFF; + uint8_t temp; + uint8_t buff[1] = {REG_STATUS}; + temp = twi_writeTo(addr, buff, 1, 1, 0); // no stop + if (temp ==0) + { + temp = twi_readFrom(addr, buff, 1, 1); + } + if (temp==1) + { + state = buff[0]; + } + + return state; +} + +/* + * Function ScI2cMxReadVersion + * Desc Read ZT.SC-I2CMx Fireware Version + * Input addr:ZT.SC-I2CMx Address + *buf:Version Buffer + * Output !=0xFF ZT.SC-I2CMx Status + * othe .. number bytes of Version Read out + */ +int ZtLib::ScI2cMxReadVersion(uint8_t addr, uint8_t *buf) +{ + uint8_t state = 0xFF; + uint8_t temp; + uint8_t regv[1] = {REG_VERSION}; + temp = twi_writeTo(addr, regv, 1, 1, 0); // no stop + if (temp ==0) + { + temp = twi_readFrom(addr, &(*buf), 16, 1); + } + return temp; +} +/* + * Function ScI2cMxSetAddress + * Desc Set ZT.SC-I2CMx New Address + * Input val:ZT.SC-I2CMx Address New Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxSetAddress(uint8_t newaddr) +{ + uint8_t buff[2] = {REG_ADDRESS, newaddr}; + return twi_writeTo(ZTSCI2CMX_DADDRESS, buff, 2, 1, 1); +} + +/* + * Function ScI2cMxSetBrightness + * Desc Set ZT.SC-I2CMx Brightness + * Input addr:ZT.SC-I2CMx Address + val: Brightness 0~0xFF + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxSetBrightness(uint8_t addr, uint8_t val) +{ + uint8_t buff[2] = {REG_BRIGHTNESS, val}; + return twi_writeTo(addr, buff, 2, 1, 1); +} +/* + * Function ScI2cMxSetVcomH + * Desc Set ZT.SC-I2CMx VcomH + * Input addr:ZT.SC-I2CMx Address + val: Brightness 0~7 + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxSetVcomH(uint8_t addr, uint8_t val) +{ + uint8_t buff[2] = {REG_VCOMH, val}; + return twi_writeTo(addr, buff, 2, 1, 1); +} + +/* + * Function ScI2cMxDisplay8x16Str + * Desc ZT.SC-I2CMx Display 8x16 English String + * Input addr:ZT.SC-I2CMx Address + page: location page + column: location column + *str: 8X16 English String + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxDisplay8x16Str(uint8_t addr, uint8_t page, uint8_t column, const char *str) +{ + uint8_t i=0; + uint8_t buff[19]; + buff[0] = REG_8X16STR; + buff[1] = page; + buff[2] = column; + i=0; + while ((*str != '\0') && (i<16)) + { + buff[i+3] = (uint8_t)*str++; + i++; + } + return twi_writeTo(addr, buff, i+3, 1, 1); +} +/* + * Function ScI2cMxFillArea + * Desc ZT.SC-I2CMx Fill Area + * Input addr:ZT.SC-I2CMx Address + spage: start page + epage: end page + scolumn: start column + ecolumn: end column + filldata: fill data + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxFillArea(uint8_t addr, uint8_t spage, uint8_t epage,uint8_t scolumn, uint8_t ecolumn,uint8_t filldata) +{ + uint8_t buff[6] = {REG_FILL_AREA, spage, epage, scolumn, ecolumn, filldata}; + return twi_writeTo(addr, buff, 6, 1, 1); +} +/* + * Function ScI2cMxScrollingHorizontal + * Desc ZT.SC-I2CMx Scrolling Horizontal + * Input addr:ZT.SC-I2CMx Address + lr: Scroll direction + spage: start page + epage: end page + frames: Scroll fram + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxScrollingHorizontal(uint8_t addr, uint8_t lr, uint8_t spage, uint8_t epage,uint8_t frames) +{ + uint8_t buff[9] = {REG_CMD, 0x2E, 0x00, spage, frames, epage, 0x00, 0xFF, 0x2F}; + twi_writeTo(addr, buff, 2, 1, 1); + buff[0] = REG_CMD; + buff[1] = lr; + for (int i=0; i<10; i++); + return twi_writeTo(addr, buff, 9, 1, 1); +} +/* + * Function ScI2cMxScrollingHorizontal + * Desc ZT.SC-I2CMx Scrolling Vertical + * Input addr:ZT.SC-I2CMx Address + lr: Scroll direction + rowsfixed: rows fixed + rowsscroll: rows scroll + scrollstep: scroll step + stepdelay: step delay + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxScrollingVertical(uint8_t addr, uint8_t scrollupdown, uint8_t rowsfixed, uint8_t rowsscroll, uint8_t scrollstep, uint8_t stepdelay) +{ + uint8_t buff[6] = {REG_SCROVER, scrollupdown, rowsfixed, rowsscroll, scrollstep, stepdelay}; + return twi_writeTo(addr, buff, 6, 1, 1); +} +/* + * function ScI2cMxScrollingVerticalHorizontal + * Desc Continuous Vertical / Horizontal / Diagonal Scrolling (Partial or Full Screen) + * input : + Sdirection: Scrolling Direction + "0x00" (Vertical & Rightward) + "0x01" (Vertical & Leftward) + spage: Define Start Page Address (Horizontal / Diagonal Scrolling) + epage: Define End Page Address (Horizontal / Diagonal Scrolling) + fixedarea: Set Top Fixed Area (Vertical Scrolling) + scrollarea: Set Vertical Scroll Area (Vertical Scrolling) + frames: Set Time Interval between Each Scroll Step in Terms of Frame Frequency + offset: Set Numbers of Row Scroll per Step (Vertical / Diagonal Scrolling) + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxScrollingVerticalHorizontal(uint8_t addr, uint8_t Sdirection, uint8_t spage, uint8_t epage, uint8_t fixedarea, uint8_t scrollarea, uint8_t offset, uint8_t frames) +{ + uint8_t buff[8] = {REG_SCROVERHOR, Sdirection, spage, epage, fixedarea, scrollarea, offset, frames}; + return twi_writeTo(addr, buff, 8, 1, 1); +} +/* + * Function ScI2cMxDeactivateScroll + * Desc ZT.SC-I2CMx Deactivate Scroll + * Input addr:ZT.SC-I2CMx Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxDeactivateScroll(uint8_t addr) +{ + uint8_t buff[2] = {REG_CMD, 0x2E}; + return twi_writeTo(addr, buff, 2, 1, 1); +} + +/* + * Function ScI2cMxReset + * Desc ZT.SC-I2CMx Reset OLED + * Input addr:ZT.SC-I2CMx Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxReset(uint8_t addr) +{ + uint8_t buff[2] = {REG_RESET,RESET_OLED}; + return twi_writeTo(addr, buff, 2, 1, 1); +} + +/* + * Function ScI2cMxSetLocation + * Desc Set ZT.SC-I2CMx SetLocation + * Input addr:ZT.SC-I2CMx Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxSetLocation(uint8_t addr, uint8_t page,uint8_t column) +{ + uint8_t buff[4] = {REG_CMD, (0xB0|page), (column%16), (column/16+0x10)}; + return twi_writeTo(addr, buff, 4, 1, 1); +} +/* + * Function ScI2cMxDisplayDot16x16 + * Desc Set ZT.SC-I2CMx Display 16*16 Dot + * Input addr:ZT.SC-I2CMx Address + page:page + column:column + *str:16*16 Dot Data + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +void ZtLib::ScI2cMxDisplayDot16x16(uint8_t addr, uint8_t page, uint8_t column, const char *str) +{ + uint8_t buff[17]; + buff[0] = REG_DAT; + ScI2cMxSetLocation(addr, page, column); + for (int i=0; i<16; i++) + { + buff[i+1] = str[i]; + } + twi_writeTo(addr, buff, 17, 1, 1); + ScI2cMxSetLocation(addr, page+1, column); + for (int i=0; i<16; i++) + { + buff[i+1] = str[i+16]; + } + twi_writeTo(addr, buff, 17, 1, 1); +} +/* + * Function ScI2cMxDisplayArea + * Desc Set ZT.SC-I2CMx Display Area + * Input addr:ZT.SC-I2CMx Address + spage: start page + epage: end page + scolumn: start column + ecolumn: end column + *pt: Data + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +void ZtLib::ScI2cMxDisplayArea(uint8_t addr, uint8_t spage, uint8_t epage, uint8_t scolumn, uint8_t ecolumn, const char *pt) +{ + uint8_t i = 0; + uint8_t j = 0; + uint8_t h = 0; + uint8_t w = 0; + uint16_t cnt = 0; + uint8_t buff[32]; + buff[0] = REG_DAT; + + h = epage - spage; + w = ecolumn - scolumn; + + while ( j<h ) + { + ScI2cMxSetLocation(addr, spage + j, scolumn); + uint8_t p=w; + while(p) + { + if(p>=31) + { + for (int n=0; n<31; n++) + { + buff[1+n] = pt[cnt++]; + } + twi_writeTo(addr, buff, 32, 1, 1); + p -= 31; + } + else + { + int n; + for (n=0; n<p; n++) + { + buff[1+n] = pt[cnt++]; + } + twi_writeTo(addr, buff, n+1, 1, 1); + p -= n; + } + } + j++; + } +} + +// Preinstantiate Objects ////////////////////////////////////////////////////// + diff --git a/obdlogger/ZtLib.h b/obdlogger/ZtLib.h new file mode 100644 index 0000000..b93ccc1 --- /dev/null +++ b/obdlogger/ZtLib.h @@ -0,0 +1,127 @@ +/* + ZtLib.cpp - ZT module Drive Library for Wiring & Arduino + Copyright (c) 2012 Alvin Li(Kozig/www.kozig.com). All right reserved. + This library is free software; + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Version:V1.1 +*/ + +#ifndef __ZTLIB_H__ +#define __ZTLIB_H__ + +#include <inttypes.h> + +////////////////// +#define ZTSEG8B4A036A_DADDR 0x51 +#define SET_ADDR 0x61 +#define WRITE_CODE 0xAA +#define WRITE_CMD 0x55 + +#define DOT_NONE (0) +#define DOT_BIT1 (1<<0) +#define DOT_BIT2 (1<<1) +#define DOT_BIT3 (1<<2) +#define DOT_BIT4 (1<<3) + +#define DISP_0BIT (0) +#define DISP_1BIT (1) +#define DISP_2BIT (2) +#define DISP_3BIT (3) +#define DISP_4BIT (4) +#define DISP_AUTO (5) +/////////////////////////////////// + +#define ZTSCI2CMX_DADDRESS 0x51 +// Ä£¿é¼Ä´æÆ÷µØÖ· +#define REG_CMD 0x01 +#define REG_DAT 0x02 +#define REG_RESET 0x03 + #define RESET_OLED 0x06 +#define REG_VERSION 0x1F +#define REG_SLEEP 0x04 + #define SLEEP_ON 0xA5 + #define SLEEP_OFF 0xA1 +#define REG_VCOMH 0x05 +#define REG_STATUS 0x06 + #define STATUS_RUN 0x00 + #define STATUS_RUN 0x00 + #define STATUS_SLEEP 0x01 + #define STATUS_SET_ADDRESS 0x02 + #define STATUS_TEST 0x04 + #define STATUS_BUSY 0x10 + +#define REG_ADDRESS 0x08 +#define REG_BRIGHTNESS 0x0A +#define REG_8X16STR 0x52 +#define REG_OLED_XY 0x60 +#define REG_FILL_AREA 0x61 +#define REG_SCROHOR 0x62 +#define REG_SCROVER 0x63 +#define REG_SCROVERHOR 0x64 + +#define PAGE0 0x00 +#define PAGE1 0x01 +#define PAGE2 0x02 +#define PAGE3 0x03 +#define PAGE4 0x04 +#define PAGE5 0x05 +#define PAGE6 0x06 +#define PAGE7 0x07 + +#define SCROLL_UP 0x01 +#define SCROLL_DOWN 0x00 +#define SCROLL_RIGHT 0x26 +#define SCROLL_LEFT 0x27 +#define SCROLL_VR 0x29 +#define SCROLL_VL 0x2A + +#define FRAMS_2 0x07 +#define FRAMS_3 0x04 +#define FRAMS_4 0x05 +#define FRAMS_5 0x00 +#define FRAMS_25 0x06 +#define FRAMS_64 0x01 +#define FRAMS_128 0x02 +#define FRAMS_256 0x03 + +class ZtLib +{ + private: + + public: + void I2cInit(void); +// Module ZT.SEG8B4A036A FUNCTION + int Seg8b4a036aSleep(uint8_t); + int Seg8b4a036aUnSleep(uint8_t); + int Seg8b4a036aReadState(uint8_t addr); + int Seg8b4a036aReadVersion(uint8_t addr, uint8_t *buf); + int Seg8b4a036aDisplayDec(uint8_t,unsigned short, uint8_t, uint8_t); + int Seg8b4a036aDisplayHex(uint8_t,unsigned short, uint8_t, uint8_t); + int Seg8b4a036aSetBrightness(uint8_t, uint8_t, uint8_t); + int Seg8b4a036aSetAddress(uint8_t); + int Seg8b4a036aDisplayBuff(uint8_t,uint8_t *); +// Module ZT.SC-I2CMx + int ScI2cMxReadState(uint8_t); + int ScI2cMxReadVersion(uint8_t, uint8_t *); + int ScI2cMxSetAddress(uint8_t); + int ScI2cMxSetBrightness(uint8_t, uint8_t); + int ScI2cMxSetVcomH(uint8_t, uint8_t); + int ScI2cMxDisplay8x16Str(uint8_t, uint8_t, uint8_t, const char *); + int ScI2cMxFillArea(uint8_t, uint8_t, uint8_t,uint8_t, uint8_t,uint8_t); + int ScI2cMxScrollingHorizontal(uint8_t, uint8_t, uint8_t, uint8_t,uint8_t); + int ScI2cMxScrollingVertical(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); + int ScI2cMxScrollingVerticalHorizontal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); + int ScI2cMxDeactivateScroll(uint8_t); + int ScI2cMxReset(uint8_t); + int ScI2cMxSetLocation(uint8_t, uint8_t, uint8_t); + void ScI2cMxDisplayDot16x16(uint8_t, uint8_t, uint8_t, const char *); + void ScI2cMxDisplayArea(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, const char *); +}; + + +extern ZtLib ZT; + +#endif + diff --git a/obdlogger/cores/CDC.cpp b/obdlogger/cores/CDC.cpp new file mode 100644 index 0000000..1af755e --- /dev/null +++ b/obdlogger/cores/CDC.cpp @@ -0,0 +1,3 @@ +/* Stub for CDC.cpp */ + +#include <CDC.cpp>
\ No newline at end of file diff --git a/obdlogger/cores/HardwareSerial.cpp b/obdlogger/cores/HardwareSerial.cpp new file mode 100644 index 0000000..33f301e --- /dev/null +++ b/obdlogger/cores/HardwareSerial.cpp @@ -0,0 +1,3 @@ +/* Stub for HardwareSerial.cpp */ + +#include <HardwareSerial.cpp>
\ No newline at end of file diff --git a/obdlogger/cores/Print.cpp b/obdlogger/cores/Print.cpp new file mode 100644 index 0000000..dedc1af --- /dev/null +++ b/obdlogger/cores/Print.cpp @@ -0,0 +1 @@ +#include <Print.cpp>
\ No newline at end of file diff --git a/obdlogger/cores/Stream.cpp b/obdlogger/cores/Stream.cpp new file mode 100644 index 0000000..5e18362 --- /dev/null +++ b/obdlogger/cores/Stream.cpp @@ -0,0 +1 @@ +#include <Stream.cpp>
\ No newline at end of file diff --git a/obdlogger/cores/USBCore.cpp b/obdlogger/cores/USBCore.cpp new file mode 100644 index 0000000..76ff084 --- /dev/null +++ b/obdlogger/cores/USBCore.cpp @@ -0,0 +1,2 @@ +#include <HID.cpp> +#include <USBCore.cpp> diff --git a/obdlogger/cores/WInterrupts.c b/obdlogger/cores/WInterrupts.c new file mode 100644 index 0000000..67e386e --- /dev/null +++ b/obdlogger/cores/WInterrupts.c @@ -0,0 +1 @@ +#include <WInterrupts.c>
\ No newline at end of file diff --git a/obdlogger/cores/WMath.cpp b/obdlogger/cores/WMath.cpp new file mode 100644 index 0000000..22ac58c --- /dev/null +++ b/obdlogger/cores/WMath.cpp @@ -0,0 +1 @@ +#include <WMath.cpp>
\ No newline at end of file diff --git a/obdlogger/cores/WString.cpp b/obdlogger/cores/WString.cpp new file mode 100644 index 0000000..835c753 --- /dev/null +++ b/obdlogger/cores/WString.cpp @@ -0,0 +1 @@ +#include <WString.cpp>
\ No newline at end of file diff --git a/obdlogger/cores/main.cpp b/obdlogger/cores/main.cpp new file mode 100644 index 0000000..14a9885 --- /dev/null +++ b/obdlogger/cores/main.cpp @@ -0,0 +1 @@ +#include <main.cpp>
\ No newline at end of file diff --git a/obdlogger/cores/new.cpp b/obdlogger/cores/new.cpp new file mode 100644 index 0000000..2ba8b6b --- /dev/null +++ b/obdlogger/cores/new.cpp @@ -0,0 +1 @@ +#include <new.cpp>
\ No newline at end of file diff --git a/obdlogger/cores/wiring.c b/obdlogger/cores/wiring.c new file mode 100644 index 0000000..b986fad --- /dev/null +++ b/obdlogger/cores/wiring.c @@ -0,0 +1 @@ +#include <wiring.c>
\ No newline at end of file diff --git a/obdlogger/cores/wiring_analog.c b/obdlogger/cores/wiring_analog.c new file mode 100644 index 0000000..0c23337 --- /dev/null +++ b/obdlogger/cores/wiring_analog.c @@ -0,0 +1 @@ +#include <wiring_analog.c>
\ No newline at end of file diff --git a/obdlogger/cores/wiring_digital.c b/obdlogger/cores/wiring_digital.c new file mode 100644 index 0000000..420e9c6 --- /dev/null +++ b/obdlogger/cores/wiring_digital.c @@ -0,0 +1 @@ +#include <wiring_digital.c> diff --git a/obdlogger/cores/wiring_pulse.c b/obdlogger/cores/wiring_pulse.c new file mode 100644 index 0000000..9b11811 --- /dev/null +++ b/obdlogger/cores/wiring_pulse.c @@ -0,0 +1 @@ +#include <wiring_pulse.c> diff --git a/obdlogger/cores/wiring_shift.c b/obdlogger/cores/wiring_shift.c new file mode 100644 index 0000000..cd996d4 --- /dev/null +++ b/obdlogger/cores/wiring_shift.c @@ -0,0 +1 @@ +#include <wiring_shift.c> diff --git a/obdlogger/libraries/SD.cpp b/obdlogger/libraries/SD.cpp new file mode 100644 index 0000000..d922fc8 --- /dev/null +++ b/obdlogger/libraries/SD.cpp @@ -0,0 +1,5 @@ +#include <SD.cpp> +#include <Sd2Card.cpp> +#include <SdFile.cpp> +#include <SdVolume.cpp> +#include <File.cpp> diff --git a/obdlogger/libraries/SoftwareSerial.cpp b/obdlogger/libraries/SoftwareSerial.cpp new file mode 100644 index 0000000..4ae6f36 --- /dev/null +++ b/obdlogger/libraries/SoftwareSerial.cpp @@ -0,0 +1 @@ +#include <SoftwareSerial.cpp> diff --git a/obdlogger/libraries/Wire.cpp b/obdlogger/libraries/Wire.cpp new file mode 100644 index 0000000..d56e040 --- /dev/null +++ b/obdlogger/libraries/Wire.cpp @@ -0,0 +1 @@ +#include <Wire.cpp> diff --git a/obdlogger/libraries/twi.c b/obdlogger/libraries/twi.c new file mode 100644 index 0000000..4621167 --- /dev/null +++ b/obdlogger/libraries/twi.c @@ -0,0 +1 @@ +#include <twi.c> diff --git a/obdlogger/obdlogger.cbp b/obdlogger/obdlogger.cbp new file mode 100644 index 0000000..714897e --- /dev/null +++ b/obdlogger/obdlogger.cbp @@ -0,0 +1,338 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="6" /> + <Project> + <Option title="obdlogger" /> + <Option pch_mode="2" /> + <Option compiler="avrgcc" /> + <Build> + <Target title="Simulator - Release"> + <Option output="obdlogger_sim.exe" prefix_auto="1" extension_auto="0" /> + <Option object_output="obj/Release/" /> + <Option type="1" /> + <Option compiler="GCC" /> + <Compiler> + <Add option="-Os" /> + <Add option="-DARDUSIM" /> + <Add option="-DENABLE_API_NAME" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add directory="$(ARDUINO_DIR)/arduino/cores" /> + <Add directory="$(ARDUINO_DIR)/arduino/variants/standard" /> + <Add directory="$(ARDUINO_DIR)/include" /> + </Compiler> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\ardusim" /> + </Environment> + </Target> + <Target title="Arduino Uno"> + <Option output="bin/Release/obdlogger_${BOARD_ID}_$(UPLOAD_PORT).elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-Os" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" /> + </Compiler> + <Linker> + <Add option="-s" /> + <Add option="-mmcu=$(MCU)" /> + </Linker> + <ExtraCommands> + <Add after="avr-objcopy -O ihex -R .eeprom -R .eesafe $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex" /> + <Add after="avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).eep.hex" /> + <Add after="avr-size --mcu=$(MCU) --format=avr $(TARGET_OUTPUT_FILE)" /> + </ExtraCommands> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\arduino" /> + <Variable name="BOARD" value="Arduino Uno" /> + <Variable name="BOARD_ID" value="uno" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="UPLOAD_BAUDRATE" value="115200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Leonardo"> + <Option output="bin/Release/obdlogger_${BOARD_ID}_$(UPLOAD_PORT).elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-Os" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-D__AVR_ATmega32U4__" /> + <Add option="-DUSB_VID=0x2341" /> + <Add option="-DUSB_PID=0x8036" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/leonardo" /> + </Compiler> + <Linker> + <Add option="-s" /> + <Add option="-mmcu=$(MCU)" /> + </Linker> + <ExtraCommands> + <Add after="avr-objcopy -O ihex -R .eeprom -R .eesafe $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex" /> + <Add after="avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).eep.hex" /> + <Add after="avr-size --mcu=$(MCU) --format=avr $(TARGET_OUTPUT_FILE)" /> + </ExtraCommands> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\arduino" /> + <Variable name="BOARD" value="Arduino Leonardo" /> + <Variable name="BOARD_ID" value="leonardo" /> + <Variable name="MCU" value="atmega32u4" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Duemilanove (328)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}_$(UPLOAD_PORT).elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-O2" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" /> + </Compiler> + <Linker> + <Add option="-s" /> + <Add option="-mmcu=$(MCU)" /> + </Linker> + <ExtraCommands> + <Add after="avr-objcopy -O ihex -R .eeprom -R .eesafe $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex" /> + <Add after="avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).eep.hex" /> + <Add after="avr-size --mcu=$(MCU) --format=avr $(TARGET_OUTPUT_FILE)" /> + </ExtraCommands> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\arduino" /> + <Variable name="BOARD" value="Arduino Duemilanove (328)" /> + <Variable name="BOARD_ID" value="duemilanove328" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Nano (328)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}_$(UPLOAD_PORT).elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-Os" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/eightanaloginputs" /> + </Compiler> + <Linker> + <Add option="-s" /> + <Add option="-mmcu=$(MCU)" /> + </Linker> + <ExtraCommands> + <Add after="avr-objcopy -O ihex -R .eeprom -R .eesafe $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex" /> + <Add after="avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).eep.hex" /> + <Add after="avr-size --mcu=$(MCU) --format=avr $(TARGET_OUTPUT_FILE)" /> + </ExtraCommands> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\arduino" /> + <Variable name="BOARD" value="Arduino Nano (328)" /> + <Variable name="BOARD_ID" value="nano328" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Mini (328)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}_$(UPLOAD_PORT).elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-Os" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/eightanaloginputs" /> + </Compiler> + <Linker> + <Add option="-s" /> + <Add option="-mmcu=$(MCU)" /> + </Linker> + <ExtraCommands> + <Add after="avr-objcopy -O ihex -R .eeprom -R .eesafe $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex" /> + <Add after="avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).eep.hex" /> + <Add after="avr-size --mcu=$(MCU) --format=avr $(TARGET_OUTPUT_FILE)" /> + </ExtraCommands> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\arduino" /> + <Variable name="BOARD" value="Arduino Mini (328)" /> + <Variable name="BOARD_ID" value="mini328" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Pro Mini (328)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}_$(UPLOAD_PORT).elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-Os" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" /> + </Compiler> + <Linker> + <Add option="-s" /> + <Add option="-mmcu=$(MCU)" /> + </Linker> + <ExtraCommands> + <Add after="avr-objcopy -O ihex -R .eeprom -R .eesafe $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex" /> + <Add after="avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).eep.hex" /> + <Add after="avr-size --mcu=$(MCU) --format=avr $(TARGET_OUTPUT_FILE)" /> + </ExtraCommands> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\arduino" /> + <Variable name="BOARD" value="Arduino Pro Mini (328)" /> + <Variable name="BOARD_ID" value="promini328" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Mega 2560/ADK"> + <Option output="bin/Release/obdlogger_${BOARD_ID}_$(UPLOAD_PORT).elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-O2" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-D__AVR_ATmega2560__" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/mega" /> + </Compiler> + <Linker> + <Add option="-s" /> + <Add option="-mmcu=$(MCU)" /> + </Linker> + <ExtraCommands> + <Add after="avr-objcopy -O ihex -R .eeprom -R .eesafe $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex" /> + <Add after="avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).eep.hex" /> + <Add after="avr-size --mcu=$(MCU) --format=avr $(TARGET_OUTPUT_FILE)" /> + </ExtraCommands> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\arduino" /> + <Variable name="BOARD" value="Arduino Mega 2560\ADK" /> + <Variable name="BOARD_ID" value="mega2560" /> + <Variable name="MCU" value="atmega2560" /> + <Variable name="UPLOAD_BAUDRATE" value="115200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Mega 1280"> + <Option output="bin/Release/obdlogger_${BOARD_ID}_$(UPLOAD_PORT).elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-O2" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-D__AVR_ATmega1280__" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/mega" /> + </Compiler> + <Linker> + <Add option="-s" /> + <Add option="-mmcu=$(MCU)" /> + </Linker> + <ExtraCommands> + <Add after="avr-objcopy -O ihex -R .eeprom -R .eesafe $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex" /> + <Add after="avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).eep.hex" /> + <Add after="avr-size --mcu=$(MCU) --format=avr $(TARGET_OUTPUT_FILE)" /> + </ExtraCommands> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\arduino" /> + <Variable name="BOARD" value="Arduino Mega 1280" /> + <Variable name="BOARD_ID" value="mega1280" /> + <Variable name="MCU" value="atmega1280" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + </Build> + <Compiler> + <Add option="-fno-exceptions" /> + <Add option="-ffunction-sections" /> + <Add option="-fdata-sections" /> + <Add option="-s" /> + <Add option="-DF_CPU=16000000L" /> + <Add directory="." /> + <Add directory="include" /> + </Compiler> + <Linker> + <Add option="-Wl,--gc-sections" /> + </Linker> + <Unit filename="LCD4Bit_mod.cpp" /> + <Unit filename="LCD4Bit_mod.h" /> + <Unit filename="MPU6050.cpp" /> + <Unit filename="MPU6050.h" /> + <Unit filename="MultiLCD.cpp" /> + <Unit filename="MultiLCD.h" /> + <Unit filename="OBD.cpp" /> + <Unit filename="OBD.h" /> + <Unit filename="PCD8544.cpp" /> + <Unit filename="PCD8544.h" /> + <Unit filename="TinyGPS.cpp" /> + <Unit filename="TinyGPS.h" /> + <Unit filename="ZtLib.cpp" /> + <Unit filename="ZtLib.h" /> + <Unit filename="cores/CDC.cpp" /> + <Unit filename="cores/HardwareSerial.cpp" /> + <Unit filename="cores/Print.cpp" /> + <Unit filename="cores/Stream.cpp" /> + <Unit filename="cores/USBCore.cpp" /> + <Unit filename="cores/WInterrupts.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="cores/WMath.cpp" /> + <Unit filename="cores/WString.cpp" /> + <Unit filename="cores/main.cpp" /> + <Unit filename="cores/new.cpp" /> + <Unit filename="cores/wiring.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="cores/wiring_analog.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="cores/wiring_digital.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="cores/wiring_pulse.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="cores/wiring_shift.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="libraries/SD.cpp" /> + <Unit filename="libraries/SoftwareSerial.cpp" /> + <Unit filename="libraries/Wire.cpp" /> + <Unit filename="libraries/twi.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="obdlogger.ino"> + <Option compile="1" /> + <Option link="1" /> + <Option compiler="avrgcc" use="1" buildCommand="$compiler $options -x c++ $includes -c $file -o $object" /> + </Unit> + <Extensions> + <code_completion /> + <debugger /> + </Extensions> + </Project> +</CodeBlocks_project_file> diff --git a/obdlogger/obdlogger.ino b/obdlogger/obdlogger.ino new file mode 100644 index 0000000..de9dd03 --- /dev/null +++ b/obdlogger/obdlogger.ino @@ -0,0 +1,323 @@ +/************************************************************************* +* Arduino OBD-II Data Logger +* Distributed under GPL v2.0 +* Copyright (c) 2013 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <OBD.h> +#include <SD.h> +#include <Wire.h> +#include <Multilcd.h> +#include <TinyGPS.h> +#include "MPU6050.h" + +#define DATASET_INTERVAL 1000 /* ms */ +#define SD_CS_PIN 10 +//#define SD_CS_PIN 4 // ethernet shield + +// addition PIDs (non-OBD) +#define PID_GPS_DATETIME 0xF01 +#define PID_GPS_COORDINATE 0xF02 +#define PID_GPS_ALTITUDE 0xF03 +#define PID_GPS_SPEED 0xF04 + +// GPS logging can only be enabled when there is additional serial UART +#if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega644p) +#define ENABLE_GPS +#endif + +// OBD-II +COBD obd; + +// GPS +#ifdef ENABLE_GPS +TinyGPS gps; +#endif + +// SD card +Sd2Card card; +SdVolume volume; +File sdfile; + +// LCD +//LCD_OLED lcd; /* for I2C OLED module */ +//LCD_PCD8544 lcd; /* for LCD4884 shield or Nokia 5100 screen module */ +LCD_1602 lcd; /* for LCD1602 shield */ + +static uint32_t filesize = 0; +static uint32_t datacount = 0; + +void ProcessGPSData(char c); + +bool ShowCardInfo() +{ + if (card.init(SPI_HALF_SPEED, SD_CS_PIN)) { + const char* type; + char buf[20]; + + switch(card.type()) { + case SD_CARD_TYPE_SD1: + type = "SD1"; + break; + case SD_CARD_TYPE_SD2: + type = "SD2"; + break; + case SD_CARD_TYPE_SDHC: + type = "SDHC"; + break; + default: + type = "N/A"; + } + + sprintf(buf, "SD Type: %s", type); + lcd.setCursor(0, 0); + lcd.print(buf); + if (!volume.init(card)) { + lcd.setCursor(0, 1); + lcd.print("No FAT!"); + return false; + } + + uint32_t volumesize = volume.blocksPerCluster(); + volumesize >>= 1; // 512 bytes per block + volumesize *= volume.clusterCount(); + volumesize >>= 10; + + sprintf(buf, "SD Size: %dMB", (int)volumesize); + lcd.setCursor(0, 1); + lcd.print(buf); + return true; + } else { + lcd.setCursor(0, 1); + lcd.print("No SD Card "); + return false; + } +} + +static uint32_t lastTime; +static int startDistance = 0; + +static void CheckSD() +{ + uint16_t fileidx = 0; + char filename[13]; + for (;;) { + if (!ShowCardInfo()) { + delay(1000); + continue; + } + + SD.begin(SD_CS_PIN); + + // determine file name + for (uint16_t index = 1; index < 65535; index++) { + sprintf(filename, "OBD%05d.CSV", index); + if (!SD.exists(filename)) { + fileidx = index; + break; + } + } + if (fileidx) break; + lcd.setCursor(0, 2); + lcd.print("SD error "); + } + + lcd.setCursor(0, 2); + lcd.print(filename); + + filesize = 0; + sdfile = SD.open(filename, FILE_WRITE); + if (sdfile) { + return; + } + + lcd.setCursor(0, 2); + lcd.print("File error"); +} + +void InitScreen() +{ + lcd.clear(); + lcd.backlight(true); + lcd.setCursor(92, 0); + lcd.print("kph"); + lcd.setCursor(92, 1); + lcd.print("rpm"); +} + +void setup() +{ + // start serial communication at the adapter defined baudrate + OBDUART.begin(OBD_SERIAL_BAUDRATE); +#ifdef ENABLE_GPS + Serial2.begin(4800); +#endif + + lcd.begin(); + lcd.clear(); + lcd.backlight(true); + lcd.print("Initializing"); + + // init SD card + pinMode(SD_CS_PIN, OUTPUT); + CheckSD(); + + // initiate OBD-II connection until success + lcd.setCursor(0, 3); + lcd.print("Waiting OBD Data"); + + while (!obd.Init()); + obd.ReadSensor(PID_DISTANCE, startDistance); + + lcd.setCursor(0, 3); + lcd.print("OBD Connected! "); + delay(1000); + + InitScreen(); + lastTime = millis(); +} + +static char databuf[32]; +static int len = 0; +static int value = 0; + +#ifdef ENABLE_GPS +void ProcessGPSData(char c) +{ + if (!gps.encode(c)) + return; + + // parsed GPS data is ready + uint32_t curTime = millis(); + unsigned long fix_age; + + { + unsigned long date, time; + gps.get_datetime(&date, &time, &fix_age); + len = sprintf(databuf, "%d,F01,%ld %ld\n", (int)(curTime - lastTime), date, time); + sdfile.write((uint8_t*)databuf, len); + } + + { + long lat, lon; + gps.get_position(&lat, &lon, &fix_age); + len = sprintf(databuf, "%d,F02,%ld %ld\n", (int)(curTime - lastTime), lat, lon); + sdfile.write((uint8_t*)databuf, len); + // display LAT/LON + sprintf(databuf, "%ld", lat); + lcd.setCursor(0, 2); + lcd.print(databuf); + sprintf(databuf, "%ld", lon); + lcd.setCursor(8 * 8, 2); + lcd.print(databuf); + } + len = sprintf(databuf, "%d,F03,%ld %ld\n", (int)(curTime - lastTime), gps.speed() * 1852 / 100); + sdfile.write((uint8_t*)databuf, len); + + len = sprintf(databuf, "%d,F04,%ld %ld\n", (int)(curTime - lastTime), gps.altitude()); + sdfile.write((uint8_t*)databuf, len); + lastTime = curTime; +} +#endif + +void RetrieveData(byte pid) +{ + // issue a query for OBD + obd.Query(pid); + + // flush data in the buffer + if (len > 0) { + sdfile.write((uint8_t*)databuf, len); + + char* buf = databuf; // data in buffer saved, free for other use + if (datacount % 100 == 99) { + sdfile.flush(); + sprintf(buf, "%4u KB", (int)(filesize >> 10)); + lcd.setCursor(72, 3); + lcd.print(buf); + } + + switch (pid) { + case PID_RPM: + sprintf(buf, "%4d", value); + lcd.setCursor(0, 0); + lcd.printLarge(buf); + break; + case PID_SPEED: + sprintf(buf, "%3d", value); + lcd.setCursor(16, 1); + lcd.printLarge(buf); + break; + case PID_DISTANCE: + if (value >= startDistance) { + sprintf(buf, "%d km ", value - startDistance); + lcd.setCursor(0, 3); + lcd.print(buf); + } + break; + } + } + +#ifdef ENABLE_GPS + while (Serial2.available()) { + ProcessGPSData(Serial2.read()); + } +#endif + + if (obd.GetResponse(pid, value)) { + uint32_t curTime = millis(); + len = sprintf(databuf, "%d,%X,%d\n", (int)(curTime - lastTime), pid, value); + filesize += len; + datacount++; + lastTime = curTime; + return; + } + len = 0; +} + +void loop() +{ + static char count = 0; + static unsigned long t = millis(); + + switch (count++) { + case 0: + case 64: + case 128: + case 192: + RetrieveData(PID_DISTANCE); + break; + case 4: + RetrieveData(PID_COOLANT_TEMP); + break; + case 20: + RetrieveData(PID_INTAKE_TEMP); + break; + } + + RetrieveData(PID_RPM); + RetrieveData(PID_SPEED); + //RetrieveData(PID_THROTTLE); + //RetrieveData(PID_ABS_ENGINE_LOAD); + + if (obd.errors >= 5) { + sdfile.close(); + lcd.clear(); + lcd.print("Reconnecting..."); + digitalWrite(SD_CS_PIN, LOW); + for (int i = 0; !obd.Init(); i++) { + if (i == 10) lcd.clear(); + } + digitalWrite(SD_CS_PIN, HIGH); + CheckSD(); + delay(1000); + InitScreen(); + count = 0; + return; + } + + t = millis() - t; + if (t < DATASET_INTERVAL) delay(DATASET_INTERVAL - t); +} diff --git a/samples/dashboard_1602/LCD4Bit_mod.cpp b/samples/dashboard_1602/LCD4Bit_mod.cpp new file mode 100644 index 0000000..ef93daa --- /dev/null +++ b/samples/dashboard_1602/LCD4Bit_mod.cpp @@ -0,0 +1,233 @@ +/* +LCD4Bit v0.1 16/Oct/2006 neillzero http://abstractplain.net + +What is this? +An arduino library for comms with HD44780-compatible LCD, in 4-bit mode (saves pins) + +Sources: +- The original "LiquidCrystal" 8-bit library and tutorial + http://www.arduino.cc/en/uploads/Tutorial/LiquidCrystal.zip + http://www.arduino.cc/en/Tutorial/LCDLibrary +- DEM 16216 datasheet http://www.maplin.co.uk/Media/PDFs/N27AZ.pdf +- Massimo's suggested 4-bit code (I took initialization from here) http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1144924220/8 +See also: +- glasspusher's code (probably more correct): http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1160586800/0#0 + +Tested only with a DEM 16216 (maplin "N27AZ" - http://www.maplin.co.uk/Search.aspx?criteria=N27AZ) +If you use this successfully, consider feeding back to the arduino wiki with a note of which LCD it worked on. + +Usage: +see the examples folder of this library distribution. + +*/ + +#include "LCD4Bit_mod.h" + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +//command bytes for LCD +#define CMD_CLR 0x01 +#define CMD_RIGHT 0x1C +#define CMD_LEFT 0x18 +#define CMD_HOME 0x02 + +// --------- PINS ------------------------------------- +//is the RW pin of the LCD under our control? If we're only ever going to write to the LCD, we can use one less microcontroller pin, and just tie the LCD pin to the necessary signal, high or low. +//this stops us sending signals to the RW pin if it isn't being used. +int USING_RW = false; + +//RS, RW and Enable can be set to whatever you like +int RS = 8; +int RW = 11; +int Enable = 9; +//DB should be an unseparated group of pins - because of lazy coding in pushNibble() +int DB[] = {4, 5, 6, 7}; //wire these to DB4~7 on LCD. + +//-------------------------------------------------------- + +//how many lines has the LCD? (don't change here - specify on calling constructor) +int g_num_lines = 2; + +//pulse the Enable pin high (for a microsecond). +//This clocks whatever command or data is in DB4~7 into the LCD controller. +void LCD4Bit_mod::pulseEnablePin(){ + digitalWrite(Enable,LOW); + delayMicroseconds(1); + // send a pulse to enable + digitalWrite(Enable,HIGH); + delayMicroseconds(1); + digitalWrite(Enable,LOW); + delay(1); // pause 1 ms. TODO: what delay, if any, is necessary here? +} + +//push a nibble of data through the the LCD's DB4~7 pins, clocking with the Enable pin. +//We don't care what RS and RW are, here. +void LCD4Bit_mod::pushNibble(int value){ + int val_nibble= value & 0x0F; //clean the value. (unnecessary) + + for (int i=DB[0]; i <= DB[3]; i++) { + digitalWrite(i,val_nibble & 01); + val_nibble >>= 1; + } + pulseEnablePin(); +} + +//push a byte of data through the LCD's DB4~7 pins, in two steps, clocking each with the enable pin. +void LCD4Bit_mod::pushByte(int value){ + int val_lower = value & 0x0F; + int val_upper = value >> 4; + pushNibble(val_upper); + pushNibble(val_lower); +} + + +//stuff the library user might call--------------------------------- +//constructor. num_lines must be 1 or 2, currently. +LCD4Bit_mod::LCD4Bit_mod (int num_lines) { + g_num_lines = num_lines; + if (g_num_lines < 1 || g_num_lines > 2) + { + g_num_lines = 1; + } +} + +void LCD4Bit_mod::commandWriteNibble(int nibble) { + digitalWrite(RS, LOW); + if (USING_RW) { digitalWrite(RW, LOW); } + pushNibble(nibble); +} + + +void LCD4Bit_mod::commandWrite(int value) { + digitalWrite(RS, LOW); + if (USING_RW) { digitalWrite(RW, LOW); } + pushByte(value); + //TODO: perhaps better to add a delay after EVERY command, here. many need a delay, apparently. +} + + + + +//print the given character at the current cursor position. overwrites, doesn't insert. +void LCD4Bit_mod::print(int value) { + //set the RS and RW pins to show we're writing data + digitalWrite(RS, HIGH); + if (USING_RW) { digitalWrite(RW, LOW); } + + //let pushByte worry about the intricacies of Enable, nibble order. + pushByte(value); +} + + +//print the given string to the LCD at the current cursor position. overwrites, doesn't insert. +//While I don't understand why this was named printIn (PRINT IN?) in the original LiquidCrystal library, I've preserved it here to maintain the interchangeability of the two libraries. +void LCD4Bit_mod::printIn(const char* msg) { + uint8_t i; //fancy int. avoids compiler warning when comparing i with strlen()'s uint8_t + uint8_t l = strlen(msg); + for (i=0; i < l; i++){ + if (msg[i] >= 20) print(msg[i]); + } +} + + +//send the clear screen command to the LCD +void LCD4Bit_mod::clear(){ + commandWrite(CMD_CLR); + delay(1); +} + + +// initiatize lcd after a short pause +//while there are hard-coded details here of lines, cursor and blink settings, you can override these original settings after calling .init() +void LCD4Bit_mod::init () { + pinMode(Enable,OUTPUT); + pinMode(RS,OUTPUT); + if (USING_RW) { pinMode(RW,OUTPUT); } + pinMode(DB[0],OUTPUT); + pinMode(DB[1],OUTPUT); + pinMode(DB[2],OUTPUT); + pinMode(DB[3],OUTPUT); + + delay(50); + + //The first 4 nibbles and timings are not in my DEM16217 SYH datasheet, but apparently are HD44780 standard... + commandWriteNibble(0x03); + delay(5); + commandWriteNibble(0x03); + delayMicroseconds(100); + commandWriteNibble(0x03); + delay(5); + + // needed by the LCDs controller + //this being 2 sets up 4-bit mode. + commandWriteNibble(0x02); + commandWriteNibble(0x02); + //todo: make configurable by the user of this library. + //NFXX where + //N = num lines (0=1 line or 1=2 lines). + //F= format (number of dots (0=5x7 or 1=5x10)). + //X=don't care + + int num_lines_ptn = (g_num_lines - 1) << 3; + int dot_format_ptn = 0x00; //5x7 dots. 0x04 is 5x10 + + commandWriteNibble(num_lines_ptn | dot_format_ptn); + delayMicroseconds(60); + + //The rest of the init is not specific to 4-bit mode. + //NOTE: we're writing full bytes now, not nibbles. + + // display control: + // turn display on, cursor off, no blinking + commandWrite(0x0C); + delayMicroseconds(60); + + //clear display + commandWrite(0x01); + delay(3); + + // entry mode set: 06 + // increment automatically, display shift, entire shift off + commandWrite(0x06); + + delay(1);//TODO: remove unnecessary delays +} + + +//non-core stuff -------------------------------------- +//move the cursor to the given absolute position. line numbers start at 1. +//if this is not a 2-line LCD4Bit_mod instance, will always position on first line. +void LCD4Bit_mod::cursorTo(int line_num, int x){ + //first, put cursor home + commandWrite(CMD_HOME); + + //if we are on a 1-line display, set line_num to 1st line, regardless of given + if (g_num_lines==1){ + line_num = 1; + } + //offset 40 chars in if second line requested + if (line_num == 2){ + x += 40; + } + //advance the cursor to the right according to position. (second line starts at position 40). + for (int i=0; i<x; i++) { + commandWrite(0x14); + } +} + +//scroll whole display to left +void LCD4Bit_mod::leftScroll(int num_chars, int delay_time){ + for (int i=0; i<num_chars; i++) { + commandWrite(CMD_LEFT); + delay(delay_time); + } +} + +//Improvements ------------------------------------------------ +//Remove the unnecessary delays (e.g. from the end of pulseEnablePin()). +//Allow the user to pass the pins to be used by the LCD in the constructor, and store them as member variables of the class instance. +//------------------------------------------------------------- diff --git a/samples/dashboard_1602/LCD4Bit_mod.h b/samples/dashboard_1602/LCD4Bit_mod.h new file mode 100644 index 0000000..7318c32 --- /dev/null +++ b/samples/dashboard_1602/LCD4Bit_mod.h @@ -0,0 +1,27 @@ +#ifndef LCD4Bit_mod_h +#define LCD4Bit_mod_h + +#include <inttypes.h> + +class LCD4Bit_mod { +public: + LCD4Bit_mod(int num_lines); + void commandWrite(int value); + void init(); + void print(int value); + void printIn(const char* value); + void clear(); + //non-core--------------- + void cursorTo(int line_num, int x); + void leftScroll(int chars, int delay_time); + //end of non-core-------- + + //4bit only, therefore ideally private but may be needed by user + void commandWriteNibble(int nibble); +private: + void pulseEnablePin(); + void pushNibble(int nibble); + void pushByte(int value); +}; + +#endif diff --git a/samples/dashboard_1602/dashboard_1602.ino b/samples/dashboard_1602/dashboard_1602.ino new file mode 100644 index 0000000..563a0af --- /dev/null +++ b/samples/dashboard_1602/dashboard_1602.ino @@ -0,0 +1,172 @@ +/************************************************************************* +* Sample sketch based on OBD-II library for Arduino +* Using a LCD1602 shield to display realtime vehicle data +* Distributed under GPL v2.0 +* Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <arduino.h> +#include <LCD4Bit_mod.h> +#include <OBD.h> + +//create object to control an LCD. +LCD4Bit_mod lcd = LCD4Bit_mod(2); + +COBD obd; + +//Key message +unsigned int adc_key_val[5] ={30, 150, 360, 535, 760 }; +int NUM_KEYS = 5; +int adc_key_in; +char key=-1; +char oldkey=-1; +unsigned long lastTick = millis(); +uint8_t modes[2] = {0, 2}; + +const char modePids[] = {PID_RPM, PID_SPEED, PID_THROTTLE, PID_ENGINE_LOAD, + PID_COOLANT_TEMP, PID_INTAKE_TEMP, PID_AMBIENT_TEMP, PID_MAF_FLOW, + PID_ABS_ENGINE_LOAD, PID_FUEL_PRESSURE, PID_INTAKE_PRESSURE, PID_BAROMETRIC, + PID_TIMING_ADVANCE, PID_FUEL_LEVEL, PID_RUNTIME, PID_DISTANCE}; + +const char* modeLabels[] = { + "Engine rpm", "Speed km/h", "Throttle %", "Engine Load %", + "Coolant C", "Intake Air C", "Env. Temp C", "MAF Flow kpa", + "Abs. Load %", "Fuel kpa", "Intake kpa", "Barometer kpa", + "Timing Adv. ", "Fuel Level %", "Run Time", "Distance km"}; + +const char modePos[] = {8, 8, 11, 12, + 11, 11, 11, 9, + 11, 9, 9, 10, + 12, 11, 8, 10}; + +const char* modeFmts[] = {"%4u", "%3u", "%3u", "%u", + "%3d", "%3d", "%3d", "%3u", + "%3u", "%3u", "%3u", "%u", + "%3d", "%3u", "%4u:%02u", "%04u"}; + +#define TOTAL_PIDS (sizeof(modePids) / sizeof(modePids[0])) + +// Convert ADC value to key number +char get_key(unsigned int input) +{ + char k; + for (k = 0; k < NUM_KEYS; k++) { + if (input < adc_key_val[k]) + return k; + } + return -1; +} + +void updateMode() +{ + lcd.cursorTo(1, 0); + lcd.printIn((char*)modeLabels[modes[0]]); + lcd.cursorTo(2, 0); + lcd.printIn((char*)modeLabels[modes[1]]); +} + +bool showData(int index) +{ + char buf[16]; + int value; + uint8_t mode = modes[index]; + uint8_t pid = modePids[mode]; + digitalWrite(13, HIGH); // set the LED on + if (!obd.ReadSensor(pid, value)) { + // display received data on error + lcd.cursorTo(index + 1, 0); + lcd.printIn("Error"); + delay(2000); + updateMode(); + return false; + } + digitalWrite(13, LOW); // set the LED off + + if (pid == PID_RUNTIME) { + sprintf(buf, modeFmts[mode], (unsigned int)value / 60, (unsigned int)value % 60); + } else { + sprintf(buf, modeFmts[mode], value); + } + lcd.cursorTo(index + 1, modePos[mode]); + lcd.printIn(buf); + return true; +} + +bool setupConnection() +{ + uint8_t errors = 0; + char buf[16]; + lcd.clear(); + lcd.printIn("Connecting..."); + while (!obd.Init()) { + lcd.cursorTo(2, 0); + sprintf(buf, "Attempts #%d", ++errors); + lcd.printIn(buf); + } + lcd.clear(); + lcd.printIn("Connected!"); + delay(1000); + updateMode(); + return true; +} + +void setup() +{ + pinMode(13, OUTPUT); //we'll use the debug LED to output a heartbeat + lcd.init(); + OBDUART.begin(OBD_SERIAL_BAUDRATE); + setupConnection(); +} + +void loop() +{ + adc_key_in = analogRead(0); // read the value from the sensor + key = get_key(adc_key_in); // convert into key press + + if (key != oldkey) { + delay(50); // wait for debounce time + adc_key_in = analogRead(0); // read the value from the sensor + key = get_key(adc_key_in); // convert into key press + if (key != oldkey) + { + oldkey = key; + if (key >=0){ + switch (key) { + case 3: // left key + do { + modes[0] = modes[0] > 0 ? modes[0] - 1 : TOTAL_PIDS - 1; + } while (modes[0] == modes[1]); + break; + case 0: // right key + do { + modes[0] = modes[0] < TOTAL_PIDS - 1 ? modes[0] + 1 : 0; + } while (modes[0] == modes[1]); + break; + case 1: // up key + do { + modes[1] = modes[1] > 0 ? modes[1] - 1 : TOTAL_PIDS - 1; + } while (modes[0] == modes[1]); + break; + case 2: // down key + do { + modes[1] = modes[1] < TOTAL_PIDS - 1 ? modes[1] + 1 : 0; + } while (modes[0] == modes[1]); + break; + } + updateMode(); + } + } + } + + unsigned long curTick = millis(); + if (curTick - lastTick > 500) { + showData(0); + showData(1); + if (obd.errors > 10) { + setupConnection(); + } + lastTick = curTick; + } +} + diff --git a/samples/dashboard_4884/LCD4884.cpp b/samples/dashboard_4884/LCD4884.cpp new file mode 100644 index 0000000..522826d --- /dev/null +++ b/samples/dashboard_4884/LCD4884.cpp @@ -0,0 +1,294 @@ +/* +Modified by Lauren +version 0.3 + +Any suggestions are welcome. + +Editors : Lauren from DFRobot <Lauran.pan@gmail.com> + Stanley Huang <stanleyhuangyc@gmail.com> +Date : Feb. 11, 2012 + +* Added LCD_putchar for basic console display +* Have the back light under control. +* Update the library and sketch to compatible with IDE V1.0 and earlier + +*/ + +#include "LCD4884.h" +#include "font_6x8.h" +#include "font_big.h" + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#include "WConstants.h" +#endif + + +extern "C" +{ +#include <avr/pgmspace.h> +#include <avr/io.h> +} + +#define NUM_COL 14 +#define NUM_ROW 5 + +LCD4884::LCD4884():prev_char(0),char_mode(MENU_NORMAL) +{}; + +LCD4884 lcd = LCD4884(); + +void LCD4884::backlight(unsigned char dat) +{ + if(dat==1) + digitalWrite(LCD_BL,HIGH); + else + digitalWrite(LCD_BL,LOW); +} + +void LCD4884::LCD_init(void) +{ + for(unsigned char i = 2; i < 8; i++) { + pinMode(i,OUTPUT); + digitalWrite(i,LOW); + } + + digitalWrite(LCD_RST,LOW); + delayMicroseconds(1); + digitalWrite(LCD_RST,HIGH); + + digitalWrite(SPI_CS,LOW); + delayMicroseconds(1); + digitalWrite(SPI_CS,HIGH); + delayMicroseconds(1); + digitalWrite(LCD_BL,HIGH); + + LCD_write_byte(0x21, 0); + LCD_write_byte(0xc0, 0); + LCD_write_byte(0x06, 0); + LCD_write_byte(0x13, 0); + LCD_write_byte(0x20, 0); + LCD_clear(); + LCD_write_byte(0x0c, 0); + + digitalWrite(SPI_CS,LOW); +} + +void LCD4884::LCD_write_byte(unsigned char dat, unsigned char dat_type) +{ + digitalWrite(SPI_CS,LOW); + if (dat_type == 0) + digitalWrite(LCD_DC,LOW); + else + digitalWrite(LCD_DC,HIGH); + + for(unsigned char i=0; i<8; i++) { + if(dat & 0x80) { + digitalWrite(SPI_MOSI,HIGH); + } else { + digitalWrite(SPI_MOSI,LOW); + } + digitalWrite(SPI_SCK,LOW); + dat = dat << 1; + digitalWrite(SPI_SCK,HIGH); + } + digitalWrite(SPI_CS,HIGH); +} + +void LCD4884::LCD_draw_bmp_pixel(unsigned char X,unsigned char Y,unsigned char *map, + unsigned char Pix_x,unsigned char Pix_y) +{ + unsigned int i,n; + unsigned char row; + + if (Pix_y%8==0) + row=Pix_y/8; + else + row=Pix_y/8+1; + + for (n=0;n<row;n++) { + LCD_set_XY(X,Y); + for(i=0; i<Pix_x; i++) { + LCD_write_byte(map[i+n*Pix_x], 1); + } + Y++; + } +} + +void LCD4884::LCD_write_string(unsigned char X,unsigned char Y,char *s, char mode) + { + LCD_set_XY(X,Y); + while (*s) { + LCD_write_char(*s, mode); + s++; + } + } + +void LCD4884::LCD_write_string(char *s, char mode) + { + while (*s) { + LCD_write_char(*s, mode); + s++; + } + } + +void LCD4884::LCD_write_chinese(unsigned char X, unsigned char Y,unsigned char *c,unsigned char ch_with,unsigned char num,unsigned char line,unsigned char row) +{ + LCD_set_XY(X,Y); + for (unsigned char i=0;i<num;) { + for (unsigned char n=0; n<ch_with*2; n++) { + if (n==ch_with) { + if (i==0) + LCD_set_XY(X,Y+1); + else + LCD_set_XY((X+(ch_with+row)*i),Y+1); + } + LCD_write_byte(c[(i*ch_with*2)+n],1); + } + i++; + LCD_set_XY((X+(ch_with+row)*i),Y); + } +} + + +void LCD4884::LCD_write_string_big ( unsigned char X,unsigned char Y, char *string, char mode ) +{ + while ( *string ){ + LCD_write_char_big( X, Y, *string , mode ); + if(*string++ == '.') + X += 5; + else + X += 12; + } +} + +/* write char in big font */ +void LCD4884::LCD_write_char_big (unsigned char X,unsigned char Y, unsigned char ch, char mode) +{ + unsigned char i, j; + unsigned char *pFont; + unsigned char ch_dat; + + pFont = (unsigned char *) big_number; + + switch (ch) { + case '.': + ch = 10; + break; + case '+': + ch = 11; + break; + case '-': + ch = 12; + break; + case ' ': + for(i=0;i<3;i++) { + LCD_set_XY ( X, Y+i); + for(j=0; j<16; j++) { + LCD_write_byte( (mode == MENU_NORMAL)? 0 : 0xff, 1); + } + } + return; + break; + default: + ch = ch & 0x0f; + } + + for(i=0;i<3;i++) { + LCD_set_XY ( X, Y+i); + for(j=0; j<16; j++){ + ch_dat = pgm_read_byte(pFont+ch*48 + i*16 +j); + LCD_write_byte( (mode == MENU_NORMAL)? ch_dat : (ch_dat^0xff), 1); + } + } + + +} + +void LCD4884::LCD_write_char(unsigned char c, char mode) +{ + unsigned char line; + unsigned char *pFont; + byte ch; + + pFont = (unsigned char *)font6_8; + c -= 32; + + for (line=0; line<6; line++) { + ch = pgm_read_byte(pFont+c*6+line); + LCD_write_byte( (mode==MENU_NORMAL)? ch: (ch^ 0xff) , 1); + } + x = (x + 1) % NUM_COL; +} + + +void LCD4884::LCD_set_XY(unsigned char X, unsigned char Y) + { + LCD_write_byte(0x40 | Y, 0); // column + LCD_write_byte(0x80 | X, 0); // row + x = X; + } + + +void LCD4884::LCD_clear(void) + { + unsigned int i; + + LCD_write_byte(0x0c, 0); + LCD_write_byte(0x80, 0); + + for (i=0; i<504; i++) + LCD_write_byte(0, 1); + + x = 0; + } + +void LCD4884::LCD_write_title(char* title) +{ + LCD_set_XY(0, 0); + for (char n = 0; n < NUM_COL; n++) + LCD_write_char(' ', MENU_HIGHLIGHT); + LCD_write_string((NUM_COL - strlen(title)) * 3, 0, title, MENU_HIGHLIGHT); + LCD_set_XY(0, 1); +} + +void LCD4884::LCD_putchar(char c) +{ + if (prev_char == 27) { + switch (c) { + case '0': + LCD_clear(); + break; + case '1': + LCD_set_XY(0, 0); + break; + case '2': + char_mode = MENU_NORMAL; + break; + case '3': + char_mode = MENU_HIGHLIGHT; + break; + case '4': + backlight(ON); + break; + case '5': + backlight(OFF); + break; + } + } else { + switch (c) { + case '\r': + for (char n = x; n < NUM_COL; n++) { + LCD_write_char(' ', char_mode); + } + break; + case '\n': + break; + default: + LCD_write_char(c, char_mode); + } + } + prev_char = c; +}
\ No newline at end of file diff --git a/samples/dashboard_4884/LCD4884.h b/samples/dashboard_4884/LCD4884.h new file mode 100644 index 0000000..3145601 --- /dev/null +++ b/samples/dashboard_4884/LCD4884.h @@ -0,0 +1,67 @@ +/* +Modified by Lauren +version 0.3 + +Any suggestions are welcome. + +Editors : Lauren from DFRobot <Lauran.pan@gmail.com> + Stanley Huang <stanleyhuangyc@gmail.com> +Date : Feb. 11, 2012 + +* Added LCD_putchar for basic console display +* Have the back light under control. +* Update the library and sketch to compatible with IDE V1.0 and earlier + +*/ + +#ifndef LCD4884_h +#define LCD4884_h + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#define SPI_SCK 2 +#define SPI_MOSI 3 +#define LCD_DC 4 +#define SPI_CS 5 +#define LCD_RST 6 +#define LCD_BL 7 + + +//display mode -- normal / highlight +#define MENU_NORMAL 0 +#define MENU_HIGHLIGHT 1 +#define OFF 0 +#define ON 1 +#define FLAG_TITLE 1 + +class LCD4884 +{ +public: + LCD4884(); + void LCD_init(void); + void backlight(unsigned char dat); + void LCD_write_byte(unsigned char dat, unsigned char dat_type); + void LCD_draw_bmp_pixel(unsigned char X,unsigned char Y,unsigned char *map,unsigned char Pix_x,unsigned char Pix_y); + void LCD_write_string(unsigned char X,unsigned char Y,char *s, char mode = MENU_NORMAL); + void LCD_write_string(char *s, char mode = MENU_NORMAL); + void LCD_write_chinese(unsigned char X, unsigned char Y,unsigned char *c,unsigned char ch_with,unsigned char num,unsigned char line,unsigned char row); + void LCD_write_string_big ( unsigned char X,unsigned char Y, char *string, char mode = MENU_NORMAL); + void LCD_write_char_big (unsigned char X,unsigned char Y, unsigned char ch, char mode = MENU_NORMAL); + void LCD_write_char(unsigned char c, char mode = MENU_NORMAL); + void LCD_set_XY(unsigned char X, unsigned char Y); + void LCD_clear(void); + void LCD_write_title(char* title); + void LCD_putchar(char c); + unsigned char x; +private: + char prev_char; + char char_mode; +}; + +extern LCD4884 lcd; + +#endif // diff --git a/samples/dashboard_4884/dashboard_4884.ino b/samples/dashboard_4884/dashboard_4884.ino new file mode 100644 index 0000000..8c44eeb --- /dev/null +++ b/samples/dashboard_4884/dashboard_4884.ino @@ -0,0 +1,468 @@ +/************************************************************************* +* Sample sketch based on OBD-II library for Arduino +* Using a LCD4884 shield to display realtime vehicle data +* Distributed under GPL v2.0 +* Copyright (c) 2012 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <OBD.h> +#include <LCD4884.h> + +// the following line toggles between hardware serial and software serial +// #define USE_SOFTSERIAL + +#ifdef USE_SOFTSERIAL +#include <SoftwareSerial.h> +SoftwareSerial mySerial(11, 12); // RX, TX +#endif + +//keypad debounce parameter +#define DEBOUNCE_MAX 15 +#define DEBOUNCE_ON 10 +#define DEBOUNCE_OFF 3 + +#define NUM_KEYS 5 +#define NUM_MODES 3 + +// joystick number +#define LEFT_KEY 0 +#define CENTER_KEY 1 +#define DOWN_KEY 2 +#define RIGHT_KEY 3 +#define UP_KEY 4 + +int adc_key_val[5] ={ + 50, 200, 400, 600, 800 }; + +// debounce counters +byte button_count[NUM_KEYS]; +// button status - pressed/released +byte button_status[NUM_KEYS]; +// button on flags for user program +byte button_flag[NUM_KEYS]; +// initial gauge mode +char mode = 0; + +// The followinging are interrupt-driven keypad reading functions +// which includes DEBOUNCE ON/OFF mechanism, and continuous pressing detection + +// Convert ADC value to key number +char get_key(unsigned int input) +{ + char k; + + for (k = 0; k < NUM_KEYS; k++) + { + if (input < adc_key_val[k]) + { + + return k; + } + } + + if (k >= NUM_KEYS) + k = -1; // No valid key pressed + + return k; +} + +void update_adc_key(){ + int adc_key_in; + char key_in; + byte i; + + adc_key_in = analogRead(0); + key_in = get_key(adc_key_in); + for(i=0; i<NUM_KEYS; i++) + { + if(key_in==i) //one key is pressed + { + if(button_count[i]<DEBOUNCE_MAX) + { + button_count[i]++; + if(button_count[i]>DEBOUNCE_ON) + { + if(button_status[i] == 0) + { + button_flag[i] = 1; + button_status[i] = 1; //button debounced to 'pressed' status + } + + } + } + + } + else // no button pressed + { + if (button_count[i] >0) + { + button_flag[i] = 0; + button_count[i]--; + if(button_count[i]<DEBOUNCE_OFF){ + button_status[i]=0; //button debounced to 'released' status + } + } + } + + } +} + +void ShowProgressBarV(byte x, byte y, byte val /* 0~10 */) +{ + byte j = 10 - val; + for (char y1 = j >> 1; y1 >= 0; y1--) { + lcd.LCD_set_XY(x, y1 + y); + for (byte x = 0; x < 14; x++) { + lcd.LCD_write_byte(0, 1); + } + } + if (j & 1 == 1) { + j >>= 1; + lcd.LCD_set_XY(x, y + j); + for (byte x = 0; x < 14; x++) { + lcd.LCD_write_byte(0xE0, 1); + } + j++; + } else { + j >>= 1; + } + for (byte y1 = j; y1 <= 5; y1++) { + lcd.LCD_set_XY(x, y1 + y); + for (byte x = 0; x < 14; x++) { + lcd.LCD_write_byte(0xEE, 1); + } + } +} + +byte CheckPressedKey() +{ + for(int i=0; i<NUM_KEYS; i++){ + if(button_flag[i] !=0){ + button_flag[i]=0; // reset button flag + return i; + } + } + return -1; +} + +// waiting for center key press +void waitfor_OKkey(){ + byte i; + byte key = 0xFF; + update_adc_key(); + while (key!= CENTER_KEY){ + for(i=0; i<NUM_KEYS; i++){ + if(button_flag[i] !=0){ + button_flag[i]=0; // reset button flag + if(i== CENTER_KEY) key=CENTER_KEY; + } + } + } + +} + +class COBDDash : public COBD +{ +public: + void Connect() + { + lcd.LCD_clear(); + lcd.LCD_write_string(0, 0, "Connecting..", MENU_NORMAL); + for (int n = 0; !Init(); n++) { + lcd.LCD_putchar('.'); + if (n == 3) lcd.backlight(OFF); + } + + lcd.backlight(ON); //Turn on the backlight + lcd.LCD_clear(); + lcd.LCD_write_string(0, 0, "Connected!", MENU_NORMAL); + + int value; + lcd.LCD_write_string(0, 1, "Wait ECU start", MENU_NORMAL); + do { + delay(1000); + } while (!ReadSensor(PID_RPM, value)); + lcd.LCD_write_string(0, 2, "ECU started ", MENU_NORMAL); + lcd.LCD_write_string(0, 3, "Wait ignition ", MENU_NORMAL); + do { + delay(100); + } while (!ReadSensor(PID_RPM, value) || value == 0); + lcd.LCD_write_string(0, 4, "Engine started", MENU_NORMAL); + delay(1000); + } + void Loop() + { + unsigned long lastTime = millis(); + + Connect(); + + byte count = 0; + byte key; + DisplayBG(mode); + dataMode = 1; + lcd.backlight(ON); + for (;;) { + update_adc_key(); + key = CheckPressedKey(); + if (key != -1) { + switch (key) { + case CENTER_KEY: + mode = (mode + 1) % NUM_MODES; + DisplayBG(mode); + count = 0; + break; + case LEFT_KEY: + if (mode > 0) { + mode--; + DisplayBG(mode); + count = 0; + } + break; + case RIGHT_KEY: + if (mode < NUM_MODES - 1) { + mode++; + DisplayBG(mode); + count = 0; + } + break; + case UP_KEY: + lcd.backlight(ON); + break; + case DOWN_KEY: + lcd.backlight(OFF); + break; + } + } + if (millis() - lastTime < 250) { + continue; + } + lastTime = millis(); + + switch (mode) { + case 0: + DisplayData1(); + break; + case 1: + DisplayData2(); + switch (count) { + case 0: + DisplayData21(); + break; + case 5: + DisplayData22(); + break; + case 10: + DisplayData23(); + break; + } + break; + case 2: + DisplayData3(); + break; + } + if (errors > 5) { + lcd.backlight(OFF); + return; + } + count++; + } + } +private: + void DisplayData1() + { + if (ReadSensor(PID_RPM, value)) { + ShowRPM(value); + } + if (ReadSensor(PID_SPEED, value)) { + ShowSpeed(value); + } + if (ReadSensor(PID_ENGINE_LOAD, value)) { + ShowEngineLoad(value); + } + } + void DisplayData2() + { + if (ReadSensor(PID_RPM, value)) { + ShowRPM(value); + } + if (ReadSensor(PID_SPEED, value)) { + ShowSpeed2(value); + } + } + void DisplayData21() + { + if (ReadSensor(PID_COOLANT_TEMP, value)) { + ShowTemperature(value, 42, 3); + } + } + void DisplayData22() + { + if (ReadSensor(PID_INTAKE_TEMP, value)) { + ShowTemperature(value, 42, 4); + } + } + void DisplayData23() + { + if (ReadSensor(PID_AMBIENT_TEMP, value)) { + ShowTemperature(value, 42, 5); + } + } + void DisplayData3() + { + if (ReadSensor(PID_SPEED, value)) { + ShowSpeed2(value); + } + if (ReadSensor(PID_INTAKE_PRESSURE, value)) { + char buf[8]; + sprintf(buf, "%3u", value); + lcd.LCD_write_string(24, 4, buf, MENU_NORMAL); + int boost = (value - 101); + if (boost < 0) boost = 0; + sprintf(buf, "%d.%02d", boost / 100, boost % 100); + lcd.LCD_write_string_big(0, 0, buf, MENU_NORMAL); + } + if (ReadSensor(PID_FUEL_PRESSURE, value)) { + char buf[8]; + sprintf(buf, "%3u", value); + lcd.LCD_write_string(24, 5, buf, MENU_NORMAL); + } + } + void ShowEngineLoad(uint8_t value) + { + ShowProgressBarV(70, 1, value / 10); + lcd.LCD_write_string(78, 1, "%", MENU_NORMAL); + } + void ShowRPM(int value) + { + char buf[15]; + if (value <= 9999) { + sprintf(buf, "%4u", value); + lcd.LCD_write_string_big(0, 0, buf, MENU_NORMAL); + lcd.LCD_write_string(48, 2, "R", MENU_NORMAL); + } + } + void ShowSpeed(uint8_t value) + { + char buf[8]; + sprintf(buf, "%3u", value); + lcd.LCD_write_string_big(6, 3, buf, MENU_NORMAL); + lcd.LCD_write_string(42, 5, "k", MENU_NORMAL); + } + void ShowSpeed2(uint8_t value) + { + char buf[8]; + ShowProgressBarV(70, 1, value / 25); + sprintf(buf, "%3u", value); + lcd.LCD_write_string(66, 0, buf, MENU_NORMAL); + lcd.LCD_write_string(66, 1, "kph", MENU_NORMAL); + } + void ShowTemperature(uint8_t value, byte x, byte y) + { + char buf[8]; + sprintf(buf, "%3d", value); + lcd.LCD_write_string(x, y, buf, MENU_NORMAL); + } + void DisplayBG(char mode) + { + lcd.LCD_clear(); + switch (mode) { + case 0: + lcd.LCD_write_string(48, 2, "RPM", MENU_NORMAL); + lcd.LCD_write_string(42, 5, "kph", MENU_NORMAL); + lcd.LCD_write_string(66, 0, "ENG", MENU_NORMAL); + break; + case 1: + lcd.LCD_write_string(48, 2, "RPM", MENU_NORMAL); + lcd.LCD_write_string(0, 3, "COOLANT C", MENU_NORMAL); + lcd.LCD_write_string(0, 4, "INTAKE C", MENU_NORMAL); + lcd.LCD_write_string(0, 5, "AMBIENT C", MENU_NORMAL); + break; + case 2: + lcd.LCD_write_string(48, 2, "bar", MENU_NORMAL); + lcd.LCD_write_string(0, 3, "PRESSURES", MENU_NORMAL); + lcd.LCD_write_string(0, 4, "AIR kpa", MENU_NORMAL); + lcd.LCD_write_string(0, 5, "FUEL kpa", MENU_NORMAL); + break; + } + } +#ifdef USE_SOFTSERIAL + // override data communication functions + bool DataAvailable() { return mySerial.available(); } + char ReadData() + { + char c = mySerial.read(); + Serial.write(c); + return c; + } + void WriteData(const char* s) { mySerial.write(s); } + void WriteData(const char c) { mySerial.write(c); } +#endif + char displayMode; + int value; +}; + +COBDDash obd; + +void loop() +{ + obd.Loop(); +} + +void setup() +{ + + // setup interrupt-driven keypad arrays + // reset button arrays + for(byte i=0; i<NUM_KEYS; i++){ + button_count[i]=0; + button_status[i]=0; + button_flag[i]=0; + } + +#ifdef __AVR_ATmega32U4__ + // Setup timer2 -- Prescaler/256 + TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); + TCCR2B &= ~(1<<WGM22); + TCCR2B = (1<<CS22)|(1<<CS21); + + ASSR |=(0<<AS2); + + // Use normal mode + TCCR2A =0; + //Timer2 Overflow Interrupt Enable + TIMSK2 |= (0<<OCIE2A); + TCNT2=0x6; // counting starts from 6; + TIMSK2 = (1<<TOIE2); + + SREG|=1<<SREG_I; +#endif + + lcd.LCD_init(); + lcd.LCD_clear(); + + lcd.backlight(ON); // Turn on the backlight + + pinMode(13, OUTPUT); + +#ifndef USE_SOFTSERIAL + OBDUART.begin(OBD_SERIAL_BAUDRATE); +#else + Serial.begin(9600); + mySerial.begin(OBD_SERIAL_BAUDRATE); +#endif +} + +// Timer2 interrupt routine - +// 1/(160000000/256/(256-6)) = 4ms interval + +#ifdef __AVR_ATmega32U4__ + +ISR(TIMER2_OVF_vect) { + TCNT2 = 6; + update_adc_key(); +} + +#endif + diff --git a/samples/dashboard_4884/font_6x8.h b/samples/dashboard_4884/font_6x8.h new file mode 100644 index 0000000..cc5855f --- /dev/null +++ b/samples/dashboard_4884/font_6x8.h @@ -0,0 +1,101 @@ +// 6 x 8 font +// 1 pixel space at left and bottom +// index = ASCII - 32 +#include <avr/pgmspace.h> + +unsigned char font6_8[][6] PROGMEM = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // sp + { 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00 }, // ! + { 0x00, 0x00, 0x07, 0x00, 0x07, 0x00 }, // " + { 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // # + { 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // $ + { 0x00, 0x62, 0x64, 0x08, 0x13, 0x23 }, // % + { 0x00, 0x36, 0x49, 0x55, 0x22, 0x50 }, // & + { 0x00, 0x00, 0x05, 0x03, 0x00, 0x00 }, // ' + { 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00 }, // ( + { 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00 }, // ) + { 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14 }, // * + { 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08 }, // + + { 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00 }, // , + { 0x00, 0x08, 0x08, 0x08, 0x08, 0x08 }, // - + { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00 }, // . + { 0x00, 0x20, 0x10, 0x08, 0x04, 0x02 }, // / + { 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E }, // 0 + { 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00 }, // 1 + { 0x00, 0x42, 0x61, 0x51, 0x49, 0x46 }, // 2 + { 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31 }, // 3 + { 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10 }, // 4 + { 0x00, 0x27, 0x45, 0x45, 0x45, 0x39 }, // 5 + { 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30 }, // 6 + { 0x00, 0x01, 0x71, 0x09, 0x05, 0x03 }, // 7 + { 0x00, 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8 + { 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E }, // 9 + { 0x00, 0x00, 0x36, 0x36, 0x00, 0x00 }, // : + { 0x00, 0x00, 0x56, 0x36, 0x00, 0x00 }, // ; + { 0x00, 0x08, 0x14, 0x22, 0x41, 0x00 }, // < + { 0x00, 0x14, 0x14, 0x14, 0x14, 0x14 }, // = + { 0x00, 0x00, 0x41, 0x22, 0x14, 0x08 }, // > + { 0x00, 0x02, 0x01, 0x51, 0x09, 0x06 }, // ? + { 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E }, // @ + { 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C }, // A + { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36 }, // B + { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22 }, // C + { 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C }, // D + { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41 }, // E + { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01 }, // F + { 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A }, // G + { 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F }, // H + { 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00 }, // I + { 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01 }, // J + { 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41 }, // K + { 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40 }, // L + { 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F }, // M + { 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F }, // N + { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E }, // O + { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06 }, // P + { 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E }, // Q + { 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46 }, // R + { 0x00, 0x46, 0x49, 0x49, 0x49, 0x31 }, // S + { 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01 }, // T + { 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F }, // U + { 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F }, // V + { 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F }, // W + { 0x00, 0x63, 0x14, 0x08, 0x14, 0x63 }, // X + { 0x00, 0x07, 0x08, 0x70, 0x08, 0x07 }, // Y + { 0x00, 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z + { 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00 }, // [ + { 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55 }, // 55 + { 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00 }, // ] + { 0x00, 0x04, 0x02, 0x01, 0x02, 0x04 }, // ^ + { 0x00, 0x40, 0x40, 0x40, 0x40, 0x40 }, // _ + { 0x00, 0x00, 0x01, 0x02, 0x04, 0x00 }, // ' + { 0x00, 0x20, 0x54, 0x54, 0x54, 0x78 }, // a + { 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38 }, // b + { 0x00, 0x38, 0x44, 0x44, 0x44, 0x20 }, // c + { 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F }, // d + { 0x00, 0x38, 0x54, 0x54, 0x54, 0x18 }, // e + { 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02 }, // f + { 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C }, // g + { 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78 }, // h + { 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00 }, // i + { 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00 }, // j + { 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00 }, // k + { 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00 }, // l + { 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78 }, // m + { 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78 }, // n + { 0x00, 0x38, 0x44, 0x44, 0x44, 0x38 }, // o + { 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18 }, // p + { 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC }, // q + { 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08 }, // r + { 0x00, 0x48, 0x54, 0x54, 0x54, 0x20 }, // s + { 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20 }, // t + { 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C }, // u + { 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C }, // v + { 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C }, // w + { 0x00, 0x44, 0x28, 0x10, 0x28, 0x44 }, // x + { 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C }, // y + { 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44 }, // z + { 0x00,0x00, 0x06, 0x09, 0x09, 0x06 } // horiz lines +}; + diff --git a/samples/dashboard_4884/font_big.h b/samples/dashboard_4884/font_big.h new file mode 100644 index 0000000..099a69b --- /dev/null +++ b/samples/dashboard_4884/font_big.h @@ -0,0 +1,141 @@ +// big font +#include <avr/pgmspace.h> + + + +//******* VERY LARGE FONTS ********** +//used here for displaying temperature + +unsigned char big_number[13][3][16] PROGMEM = { + +0,128,192,224,224,96,224,224, //'0' +192,128,0,0,0,0,0,0 +, +112,255,255,1,0,0,0,0, +255,255,254,0,0,0,0,0 +, +0,15,31,60,56,48,56,56, +31,15,3,0,0,0,0,0 +, + +0,0,0,0,128,224,224,0, //'1' +0,0,0,0,0,0,0,0 +, +0,0,3,3,3,255,255,0, +0,0,0,0,0,0,0,0 +, +0,0,56,56,56,63,63,56, +56,56,0,0,0,0,0,0 +, + +0,192,192,224,96,96,224,224, //'2' +192,128,0,0,0,0,0,0 +, +0,1,0,0,128,192,224,249, +63,31,0,0,0,0,0,0 +, +0,60,62,63,63,59,57,56, +56,56,56,0,0,0,0,0 +, + +0,192,224,224,96,96,224,224, //'3' +192,192,0,0,0,0,0,0 +, +0,1,0,0,48,48,56,125, +239,207,0,0,0,0,0,0 +, +0,28,56,56,48,48,56,60, +31,15,1,0,0,0,0,0 +, + +0,0,0,0,0,128,192,224, //'4' +224,0,0,0,0,0,0,0 +, +224,240,248,222,207,199,193,255, +255,192,192,0,0,0,0,0 +, +0,0,0,0,0,0,0,63, +63,0,0,0,0,0,0,0 +, + +0,224,224,224,224,224,224,224, //'5' +224,224,224,0,0,0,0,0 +, +0,63,63,63,56,56,48,112, +240,224,0,0,0,0,0,0 +, +0,28,56,56,48,48,56,60, +31,15,1,0,0,0,0,0 +, + +0,0,128,192,192,224,96,96, //'6' +224,224,0,0,0,0,0,0 +, +224,254,255,55,57,24,24,56, +240,240,192,0,0,0,0,0 +, +0,15,31,28,56,48,48,56, +31,15,7,0,0,0,0,0 +, + +0,224,224,224,224,224,224,224, //'7' +224,224,224,0,0,0,0,0 +, +0,0,0,0,128,224,248,126, +31,7,1,0,0,0,0,0 +, +0,0,56,62,31,7,1,0, +0,0,0,0,0,0,0,0 +, + +0,128,192,224,224,96,96,224, //'8' +192,192,0,0,0,0,0,0 +, +0,207,255,127,56,48,112,112, +255,239,199,0,0,0,0,0 +, +3,15,31,60,56,48,48,56, +31,31,15,0,0,0,0,0 +, + +0,128,192,224,224,96,224,224, //'9' +192,128,0,0,0,0,0,0 +, +12,63,127,241,224,192,192,225, +255,255,254,0,0,0,0,0 +, +0,0,56,48,48,56,56,30, +15,7,0,0,0,0,0,0 +, + + +0,0,0,0,0,0,0,0, //'.' +0,0,0,0,0,0,0,0 +, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0 +, +60,60,60,0,0,0,0,0, +0,0,0,0,0,0,0,0 +, + +0,0,0,0,0,0,0,0, //'+' +0,0,0,0,0,0,0,0 +, +0,0,64,64,64,64,64,254, +254,64,64,64,64,64,0,0 +, +0,0,0,0,0,0,0,15, +15,0,0,0,0,0,0,0 +, + +0,0,0,0,0,0,0,0, //'-' +0,0,0,0,0,0,0,0 +, +0,64,64,64,64,64,64,0, +0,0,0,0,0,0,0,0 +, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0 +}; + diff --git a/samples/dashboard_oled/ZtLib.cpp b/samples/dashboard_oled/ZtLib.cpp new file mode 100644 index 0000000..8eaec6e --- /dev/null +++ b/samples/dashboard_oled/ZtLib.cpp @@ -0,0 +1,603 @@ +/* + ZtLib.cpp - ZT module Drive Library for Wiring & Arduino + Copyright (c) 2012 Alvin Li(Kozig/www.kozig.com). All right reserved. + This library is free software; + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Version:V1.1 +*/ + +extern "C" { + #include <stdlib.h> + #include <string.h> + #include <inttypes.h> + #include "..\Wire\utility/twi.h" +} + +#include "ZtLib.h" + +///ZT.SEG8B4A036A PART///-------------------------------------------------------------------s +unsigned char codetable[] = +{ + 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, 0x77, 0x7C,0x39,0x5E,0x79,0x71,0x00 +}; + +// Public Methods ////////////////////////////////////////////////////////////// +/* + * Function I2cInit + * Desc TWI/I2C init + * Input none + * Output none + */ +void ZtLib::I2cInit(void) +{ + twi_init(); +} +/* + * Function Seg8b4a036aSleep + * Desc Set ZT.SEG8B4A036A Go to Sleep + * Input addr:ZT.SEG8B4A036A Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aSleep(uint8_t addr) +{ + uint8_t buff[5]={REG_SLEEP, SLEEP_ON, 0, 0, 0}; + + return twi_writeTo(addr, buff, 5, 1, 1); +} +/* + * Function Seg8b4a036aUnSleep + * Desc Set ZT.SEG8B4A036A Wait Up From Sleep + * Input addr:ZT.SEG8B4A036A Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aUnSleep(uint8_t addr) +{ + uint8_t buff[5]={REG_SLEEP, SLEEP_OFF, 0, 0, 0}; + + return twi_writeTo(addr, buff, 5, 1, 1); +} +/* + * Function Seg8b4a036aReadState + * Desc Read ZT.SEG8B4A036A Status + * Input addr:ZT.SEG8B4A036A Address + * Output !=0xFF ZT.SC-I2CMx Status + * 0xFF .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aReadState(uint8_t addr) +{ + uint8_t state = 0xFF; + uint8_t temp; + uint8_t buff[1] = {REG_STATUS}; + temp = twi_writeTo(addr, buff, 1, 1, 0); // no stop + if (temp ==0) + { + temp = twi_readFrom(addr, buff, 1, 1); + } + if (temp==1) + { + state = buff[0]; + } + + return state; +} +/* + * Function Seg8b4a036aReadVersion + * Desc Read ZT.SEG8B4A036A Fireware Version + * Input addr:ZT.SEG8B4A036A Address + *buf:Version Buffer + * Output .. number bytes of Version Read out + */ +int ZtLib::Seg8b4a036aReadVersion(uint8_t addr, uint8_t *buf) +{ + uint8_t state = 0xFF; + uint8_t temp; + uint8_t regv[1] = {REG_VERSION}; + temp = twi_writeTo(addr, regv, 1, 1, 0); // no stop + if (temp ==0) + { + temp = twi_readFrom(addr, &(*buf), 19, 1); + } + return temp; +} +/* + * Function Seg8b4a036aDisplayDec + * Desc ZT.SEG8B4A036A Display decimal numeral + * Input addr:ZT.SEG8B4A036A Address + val: Display Val + bitnum:Display Bit Number + dotbit: Dot Display + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aDisplayDec(uint8_t addr,unsigned short val, uint8_t bitnum, uint8_t dotbit) +{ + uint8_t i; + uint8_t segnum[5]; + if (val>9999) return 0xFF; + + segnum[0] = REG_DAT; + segnum[1] = val%10; + segnum[2] = (val%100)/10; + segnum[3] = (val/100)%10; + segnum[4] = val/1000; + for (i=1; i<5; i++) + { + segnum[i] = codetable[segnum[i]]; + if (dotbit&0x01) + { + segnum[i] |= 0x80; + } + dotbit >>= 1; + } + + if (bitnum==DISP_0BIT) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;segnum[1] = 0;} + else if (bitnum==DISP_1BIT) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;} + else if (bitnum==DISP_2BIT) {segnum[4] = 0;segnum[3] = 0;} + else if (bitnum==DISP_3BIT) {segnum[4] = 0;} + else if (bitnum==DISP_AUTO) + { + if (val<10) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;} + else if (val<100) {segnum[4] = 0;segnum[3] = 0;} + else if (val<1000) {segnum[4] = 0;} + } + + return twi_writeTo(addr, segnum, 5, 1, 1); +} +/* + * Function Seg8b4a036aDisplayHex + * Desc Read ZT.SEG8B4A036A Display hexadecimal number + * Input addr:ZT.SEG8B4A036A Address + val: Display Val + bitnum:Display Bit Number + dotbit: Dot Display + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aDisplayHex(uint8_t addr,unsigned short val, uint8_t bitnum, uint8_t dotbit) +{ + uint8_t i; + unsigned short temp; + uint8_t segnum[5]; + segnum[0] = REG_DAT; + temp = val; + for (i=1; i<5; i++) + { + segnum[i] = temp&0x000F; + temp >>= 4; + segnum[i] = codetable[segnum[i]]; + if (dotbit&0x01) + { + segnum[i] |= 0x80; + } + dotbit >>= 1; + } + + if (bitnum==DISP_0BIT) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;segnum[1] = 0;} + else if (bitnum==DISP_1BIT) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;} + else if (bitnum==DISP_2BIT) {segnum[4] = 0;segnum[3] = 0;} + else if (bitnum==DISP_3BIT) {segnum[4] = 0;} + else if (bitnum==DISP_AUTO) + { + if (!(val&0xFFF0)) {segnum[4] = 0;segnum[3] = 0;segnum[2] = 0;} + else if (!(val&0xFF00)) {segnum[4] = 0;segnum[3] = 0;} + else if (!(val&0xF000)) {segnum[4] = 0;} + } + + return twi_writeTo(addr, segnum, 5, 1, 1); +} +/* + * Function Seg8b4a036aSetBrightness + * Desc Set ZT.SEG8B4A036A Brightness + * Input addr:ZT.SEG8B4A036A Address + OnDelay: + OffDelay: + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aSetBrightness(uint8_t addr, uint8_t OnDelay, uint8_t OffDelay) +{ + uint8_t buff[5] = {REG_BRIGHTNESS, OnDelay, OffDelay, 0, 0}; + return twi_writeTo(addr, buff, 5, 1, 1); +} +/* + * Function Seg8b4a036aSetAddress + * Desc Set ZT.SEG8B4A036A New Address + * Input val:ZT.SEG8B4A036A Address New Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aSetAddress(uint8_t val) +{ + uint8_t buff[2] = {REG_ADDRESS, val}; + return twi_writeTo(ZTSEG8B4A036A_DADDR, buff, 2, 1, 1); +} +/* + * Function Seg8b4a036aDisplayBuff + * Desc Set ZT.SEG8B4A036A Brightness + * Input addr:ZT.SEG8B4A036A Address + *buf: Display buffer + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::Seg8b4a036aDisplayBuff(uint8_t addr,uint8_t *buf) +{ + uint8_t buff[5]={REG_DAT, buf[0], buf[1], buf[2], buf[3]}; + + return twi_writeTo(addr, buff, 5, 1, 1); +} + + +///ZT.ScI2cMx PART///------------------------------------------------------------------- +/* + * Function ScI2cMxReadState + * Desc Read ZT.SC-I2CMx Status + * Input addr:ZT.SC-I2CMx Address + * Output !=0xFF ZT.SC-I2CMx Status + * 0xFF .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxReadState(uint8_t addr) +{ + uint8_t state = 0xFF; + uint8_t temp; + uint8_t buff[1] = {REG_STATUS}; + temp = twi_writeTo(addr, buff, 1, 1, 0); // no stop + if (temp ==0) + { + temp = twi_readFrom(addr, buff, 1, 1); + } + if (temp==1) + { + state = buff[0]; + } + + return state; +} + +/* + * Function ScI2cMxReadVersion + * Desc Read ZT.SC-I2CMx Fireware Version + * Input addr:ZT.SC-I2CMx Address + *buf:Version Buffer + * Output !=0xFF ZT.SC-I2CMx Status + * othe .. number bytes of Version Read out + */ +int ZtLib::ScI2cMxReadVersion(uint8_t addr, uint8_t *buf) +{ + uint8_t state = 0xFF; + uint8_t temp; + uint8_t regv[1] = {REG_VERSION}; + temp = twi_writeTo(addr, regv, 1, 1, 0); // no stop + if (temp ==0) + { + temp = twi_readFrom(addr, &(*buf), 16, 1); + } + return temp; +} +/* + * Function ScI2cMxSetAddress + * Desc Set ZT.SC-I2CMx New Address + * Input val:ZT.SC-I2CMx Address New Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxSetAddress(uint8_t newaddr) +{ + uint8_t buff[2] = {REG_ADDRESS, newaddr}; + return twi_writeTo(ZTSCI2CMX_DADDRESS, buff, 2, 1, 1); +} + +/* + * Function ScI2cMxSetBrightness + * Desc Set ZT.SC-I2CMx Brightness + * Input addr:ZT.SC-I2CMx Address + val: Brightness 0~0xFF + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxSetBrightness(uint8_t addr, uint8_t val) +{ + uint8_t buff[2] = {REG_BRIGHTNESS, val}; + return twi_writeTo(addr, buff, 2, 1, 1); +} +/* + * Function ScI2cMxSetVcomH + * Desc Set ZT.SC-I2CMx VcomH + * Input addr:ZT.SC-I2CMx Address + val: Brightness 0~7 + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxSetVcomH(uint8_t addr, uint8_t val) +{ + uint8_t buff[2] = {REG_VCOMH, val}; + return twi_writeTo(addr, buff, 2, 1, 1); +} + +/* + * Function ScI2cMxDisplay8x16Str + * Desc ZT.SC-I2CMx Display 8x16 English String + * Input addr:ZT.SC-I2CMx Address + page: location page + column: location column + *str: 8X16 English String + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxDisplay8x16Str(uint8_t addr, uint8_t page, uint8_t column, const char *str) +{ + uint8_t i=0; + uint8_t buff[19]; + buff[0] = REG_8X16STR; + buff[1] = page; + buff[2] = column; + i=0; + while ((*str != '\0') && (i<16)) + { + buff[i+3] = (uint8_t)*str++; + i++; + } + return twi_writeTo(addr, buff, i+3, 1, 1); +} +/* + * Function ScI2cMxFillArea + * Desc ZT.SC-I2CMx Fill Area + * Input addr:ZT.SC-I2CMx Address + spage: start page + epage: end page + scolumn: start column + ecolumn: end column + filldata: fill data + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxFillArea(uint8_t addr, uint8_t spage, uint8_t epage,uint8_t scolumn, uint8_t ecolumn,uint8_t filldata) +{ + uint8_t buff[6] = {REG_FILL_AREA, spage, epage, scolumn, ecolumn, filldata}; + return twi_writeTo(addr, buff, 6, 1, 1); +} +/* + * Function ScI2cMxScrollingHorizontal + * Desc ZT.SC-I2CMx Scrolling Horizontal + * Input addr:ZT.SC-I2CMx Address + lr: Scroll direction + spage: start page + epage: end page + frames: Scroll fram + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxScrollingHorizontal(uint8_t addr, uint8_t lr, uint8_t spage, uint8_t epage,uint8_t frames) +{ + uint8_t buff[9] = {REG_CMD, 0x2E, 0x00, spage, frames, epage, 0x00, 0xFF, 0x2F}; + twi_writeTo(addr, buff, 2, 1, 1); + buff[0] = REG_CMD; + buff[1] = lr; + for (int i=0; i<10; i++); + return twi_writeTo(addr, buff, 9, 1, 1); +} +/* + * Function ScI2cMxScrollingHorizontal + * Desc ZT.SC-I2CMx Scrolling Vertical + * Input addr:ZT.SC-I2CMx Address + lr: Scroll direction + rowsfixed: rows fixed + rowsscroll: rows scroll + scrollstep: scroll step + stepdelay: step delay + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxScrollingVertical(uint8_t addr, uint8_t scrollupdown, uint8_t rowsfixed, uint8_t rowsscroll, uint8_t scrollstep, uint8_t stepdelay) +{ + uint8_t buff[6] = {REG_SCROVER, scrollupdown, rowsfixed, rowsscroll, scrollstep, stepdelay}; + return twi_writeTo(addr, buff, 6, 1, 1); +} +/* + * function ScI2cMxScrollingVerticalHorizontal + * Desc Continuous Vertical / Horizontal / Diagonal Scrolling (Partial or Full Screen) + * input : + Sdirection: Scrolling Direction + "0x00" (Vertical & Rightward) + "0x01" (Vertical & Leftward) + spage: Define Start Page Address (Horizontal / Diagonal Scrolling) + epage: Define End Page Address (Horizontal / Diagonal Scrolling) + fixedarea: Set Top Fixed Area (Vertical Scrolling) + scrollarea: Set Vertical Scroll Area (Vertical Scrolling) + frames: Set Time Interval between Each Scroll Step in Terms of Frame Frequency + offset: Set Numbers of Row Scroll per Step (Vertical / Diagonal Scrolling) + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxScrollingVerticalHorizontal(uint8_t addr, uint8_t Sdirection, uint8_t spage, uint8_t epage, uint8_t fixedarea, uint8_t scrollarea, uint8_t offset, uint8_t frames) +{ + uint8_t buff[8] = {REG_SCROVERHOR, Sdirection, spage, epage, fixedarea, scrollarea, offset, frames}; + return twi_writeTo(addr, buff, 8, 1, 1); +} +/* + * Function ScI2cMxDeactivateScroll + * Desc ZT.SC-I2CMx Deactivate Scroll + * Input addr:ZT.SC-I2CMx Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxDeactivateScroll(uint8_t addr) +{ + uint8_t buff[2] = {REG_CMD, 0x2E}; + return twi_writeTo(addr, buff, 2, 1, 1); +} + +/* + * Function ScI2cMxReset + * Desc ZT.SC-I2CMx Reset OLED + * Input addr:ZT.SC-I2CMx Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxReset(uint8_t addr) +{ + uint8_t buff[2] = {REG_RESET,RESET_OLED}; + return twi_writeTo(addr, buff, 2, 1, 1); +} + +/* + * Function ScI2cMxSetLocation + * Desc Set ZT.SC-I2CMx SetLocation + * Input addr:ZT.SC-I2CMx Address + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +int ZtLib::ScI2cMxSetLocation(uint8_t addr, uint8_t page,uint8_t column) +{ + uint8_t buff[4] = {REG_CMD, (0xB0|page), (column%16), (column/16+0x10)}; + return twi_writeTo(addr, buff, 4, 1, 1); +} +/* + * Function ScI2cMxDisplayDot16x16 + * Desc Set ZT.SC-I2CMx Display 16*16 Dot + * Input addr:ZT.SC-I2CMx Address + page:page + column:column + *str:16*16 Dot Data + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +void ZtLib::ScI2cMxDisplayDot16x16(uint8_t addr, uint8_t page, uint8_t column, const char *str) +{ + uint8_t buff[17]; + buff[0] = REG_DAT; + ScI2cMxSetLocation(addr, page, column); + for (int i=0; i<16; i++) + { + buff[i+1] = str[i]; + } + twi_writeTo(addr, buff, 17, 1, 1); + ScI2cMxSetLocation(addr, page+1, column); + for (int i=0; i<16; i++) + { + buff[i+1] = str[i+16]; + } + twi_writeTo(addr, buff, 17, 1, 1); +} +/* + * Function ScI2cMxDisplayArea + * Desc Set ZT.SC-I2CMx Display Area + * Input addr:ZT.SC-I2CMx Address + spage: start page + epage: end page + scolumn: start column + ecolumn: end column + *pt: Data + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +void ZtLib::ScI2cMxDisplayArea(uint8_t addr, uint8_t spage, uint8_t epage, uint8_t scolumn, uint8_t ecolumn, const char *pt) +{ + uint8_t i = 0; + uint8_t j = 0; + uint8_t h = 0; + uint8_t w = 0; + uint16_t cnt = 0; + uint8_t buff[32]; + buff[0] = REG_DAT; + + h = epage - spage; + w = ecolumn - scolumn; + + while ( j<h ) + { + ScI2cMxSetLocation(addr, spage + j, scolumn); + uint8_t p=w; + while(p) + { + if(p>=31) + { + for (int n=0; n<31; n++) + { + buff[1+n] = pt[cnt++]; + } + twi_writeTo(addr, buff, 32, 1, 1); + p -= 31; + } + else + { + int n; + for (n=0; n<p; n++) + { + buff[1+n] = pt[cnt++]; + } + twi_writeTo(addr, buff, n+1, 1, 1); + p -= n; + } + } + j++; + } +} + +// Preinstantiate Objects ////////////////////////////////////////////////////// + +ZtLib ZT; + diff --git a/samples/dashboard_oled/ZtLib.h b/samples/dashboard_oled/ZtLib.h new file mode 100644 index 0000000..b93ccc1 --- /dev/null +++ b/samples/dashboard_oled/ZtLib.h @@ -0,0 +1,127 @@ +/* + ZtLib.cpp - ZT module Drive Library for Wiring & Arduino + Copyright (c) 2012 Alvin Li(Kozig/www.kozig.com). All right reserved. + This library is free software; + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Version:V1.1 +*/ + +#ifndef __ZTLIB_H__ +#define __ZTLIB_H__ + +#include <inttypes.h> + +////////////////// +#define ZTSEG8B4A036A_DADDR 0x51 +#define SET_ADDR 0x61 +#define WRITE_CODE 0xAA +#define WRITE_CMD 0x55 + +#define DOT_NONE (0) +#define DOT_BIT1 (1<<0) +#define DOT_BIT2 (1<<1) +#define DOT_BIT3 (1<<2) +#define DOT_BIT4 (1<<3) + +#define DISP_0BIT (0) +#define DISP_1BIT (1) +#define DISP_2BIT (2) +#define DISP_3BIT (3) +#define DISP_4BIT (4) +#define DISP_AUTO (5) +/////////////////////////////////// + +#define ZTSCI2CMX_DADDRESS 0x51 +// Ä£¿é¼Ä´æÆ÷µØÖ· +#define REG_CMD 0x01 +#define REG_DAT 0x02 +#define REG_RESET 0x03 + #define RESET_OLED 0x06 +#define REG_VERSION 0x1F +#define REG_SLEEP 0x04 + #define SLEEP_ON 0xA5 + #define SLEEP_OFF 0xA1 +#define REG_VCOMH 0x05 +#define REG_STATUS 0x06 + #define STATUS_RUN 0x00 + #define STATUS_RUN 0x00 + #define STATUS_SLEEP 0x01 + #define STATUS_SET_ADDRESS 0x02 + #define STATUS_TEST 0x04 + #define STATUS_BUSY 0x10 + +#define REG_ADDRESS 0x08 +#define REG_BRIGHTNESS 0x0A +#define REG_8X16STR 0x52 +#define REG_OLED_XY 0x60 +#define REG_FILL_AREA 0x61 +#define REG_SCROHOR 0x62 +#define REG_SCROVER 0x63 +#define REG_SCROVERHOR 0x64 + +#define PAGE0 0x00 +#define PAGE1 0x01 +#define PAGE2 0x02 +#define PAGE3 0x03 +#define PAGE4 0x04 +#define PAGE5 0x05 +#define PAGE6 0x06 +#define PAGE7 0x07 + +#define SCROLL_UP 0x01 +#define SCROLL_DOWN 0x00 +#define SCROLL_RIGHT 0x26 +#define SCROLL_LEFT 0x27 +#define SCROLL_VR 0x29 +#define SCROLL_VL 0x2A + +#define FRAMS_2 0x07 +#define FRAMS_3 0x04 +#define FRAMS_4 0x05 +#define FRAMS_5 0x00 +#define FRAMS_25 0x06 +#define FRAMS_64 0x01 +#define FRAMS_128 0x02 +#define FRAMS_256 0x03 + +class ZtLib +{ + private: + + public: + void I2cInit(void); +// Module ZT.SEG8B4A036A FUNCTION + int Seg8b4a036aSleep(uint8_t); + int Seg8b4a036aUnSleep(uint8_t); + int Seg8b4a036aReadState(uint8_t addr); + int Seg8b4a036aReadVersion(uint8_t addr, uint8_t *buf); + int Seg8b4a036aDisplayDec(uint8_t,unsigned short, uint8_t, uint8_t); + int Seg8b4a036aDisplayHex(uint8_t,unsigned short, uint8_t, uint8_t); + int Seg8b4a036aSetBrightness(uint8_t, uint8_t, uint8_t); + int Seg8b4a036aSetAddress(uint8_t); + int Seg8b4a036aDisplayBuff(uint8_t,uint8_t *); +// Module ZT.SC-I2CMx + int ScI2cMxReadState(uint8_t); + int ScI2cMxReadVersion(uint8_t, uint8_t *); + int ScI2cMxSetAddress(uint8_t); + int ScI2cMxSetBrightness(uint8_t, uint8_t); + int ScI2cMxSetVcomH(uint8_t, uint8_t); + int ScI2cMxDisplay8x16Str(uint8_t, uint8_t, uint8_t, const char *); + int ScI2cMxFillArea(uint8_t, uint8_t, uint8_t,uint8_t, uint8_t,uint8_t); + int ScI2cMxScrollingHorizontal(uint8_t, uint8_t, uint8_t, uint8_t,uint8_t); + int ScI2cMxScrollingVertical(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); + int ScI2cMxScrollingVerticalHorizontal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); + int ScI2cMxDeactivateScroll(uint8_t); + int ScI2cMxReset(uint8_t); + int ScI2cMxSetLocation(uint8_t, uint8_t, uint8_t); + void ScI2cMxDisplayDot16x16(uint8_t, uint8_t, uint8_t, const char *); + void ScI2cMxDisplayArea(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, const char *); +}; + + +extern ZtLib ZT; + +#endif + diff --git a/samples/dashboard_oled/dashboard_oled.ino b/samples/dashboard_oled/dashboard_oled.ino new file mode 100644 index 0000000..96dd460 --- /dev/null +++ b/samples/dashboard_oled/dashboard_oled.ino @@ -0,0 +1,158 @@ +/************************************************************************* +* Sample sketch based on OBD-II library for Arduino +* Using a OLED module to display realtime vehicle data +* Distributed under GPL v2.0 +* Copyright (c) 2013 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <OBD.h> +#include <ZtLib.h> +#include <Wire.h> + +#define OLED_ADDRESS 0x27 +#define LOOP_COUNT 50 + +const char PROGMEM font16x32[][32] = { +{0x00,0xE0,0xF8,0xFC,0xFE,0x1E,0x07,0x07,0x07,0x07,0x1E,0xFE,0xFC,0xF8,0xF0,0x00,0x00,0x07,0x0F,0x3F,0x3F,0x7C,0x70,0x70,0x70,0x70,0x7C,0x3F,0x1F,0x1F,0x07,0x00},/*0*/ +{0x00,0x00,0x00,0x06,0x07,0x07,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x7F,0x7F,0x7F,0x00,0x00,0x00,0x00,0x00,0x00},/*1*/ +{0x00,0x38,0x3C,0x3E,0x3E,0x0F,0x07,0x07,0x07,0xCF,0xFF,0xFE,0xFE,0x38,0x00,0x00,0x00,0x40,0x40,0x60,0x70,0x78,0x7C,0x7E,0x7F,0x77,0x73,0x71,0x70,0x70,0x00,0x00},/*2*/ +{0x00,0x18,0x1C,0x1E,0x1E,0x0F,0xC7,0xC7,0xE7,0xFF,0xFE,0xBE,0x9C,0x00,0x00,0x00,0x00,0x0C,0x1C,0x3C,0x3C,0x78,0x70,0x70,0x70,0x79,0x7F,0x3F,0x1F,0x0F,0x00,0x00},/*3*/ +{0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x1E,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x06,0x07,0x07,0x07,0x06,0x06,0x06,0x06,0x06,0x7F,0x7F,0x7F,0x7F,0x06,0x06,0x00},/*4*/ +{0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xE7,0xE7,0xE7,0xE7,0xC7,0x87,0x00,0x00,0x00,0x00,0x38,0x78,0x71,0x70,0x70,0x70,0x70,0x70,0x39,0x3F,0x3F,0x1F,0x0F,0x00},/*5*/ +{0x00,0x80,0xE0,0xF0,0xF8,0xFC,0x7F,0x7F,0x6F,0x67,0xE1,0xE1,0xC0,0x80,0x00,0x00,0x00,0x0F,0x1F,0x3F,0x3F,0x78,0x70,0x70,0x70,0x70,0x78,0x3F,0x3F,0x1F,0x0F,0x00},/*6*/ +{0x00,0x07,0x07,0x07,0x07,0x07,0xC7,0xE7,0xF7,0xFF,0x7F,0x3F,0x1F,0x07,0x03,0x01,0x00,0x20,0x38,0x7C,0x7E,0x3F,0x0F,0x07,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*7*/ +{0x00,0x00,0x00,0x1C,0xBE,0xFE,0xFF,0xE7,0xC3,0xC3,0xE7,0xFF,0xFE,0xBE,0x1C,0x00,0x00,0x00,0x0E,0x3F,0x3F,0x7F,0x71,0x60,0x60,0x60,0x71,0x7F,0x3F,0x3F,0x0F,0x00},/*8*/ +{0x00,0x78,0xFC,0xFE,0xFE,0x8F,0x07,0x07,0x07,0x07,0x8F,0xFE,0xFE,0xFC,0xF8,0x00,0x00,0x00,0x00,0x01,0x43,0x43,0x73,0x7B,0x7F,0x7F,0x1F,0x0F,0x07,0x03,0x00,0x00},/*9*/ +}; + +class COBDDash : public COBD +{ +public: + void Connect() + { + char buf[16]; + + InitScreen(); + for (int n = 1; !Init(); n++) { + sprintf(buf, "Connecting [%d]", n); + if (n <= 20) + DisplayString(buf); + else if (n == 21) + ClearScreen(); // blank screen after a while + } + DisplayString("Connected! "); + + int value; + DisplayString("Wait ECU start", 0 , 2); + do { + delay(500); + } while (!ReadSensor(PID_RPM, value)); + DisplayString("ECU started ", 0 , 4); + delay(500); + DisplayString("Wait ignition ", 0 , 6); + delay(500); + do { + delay(500); + } while (!ReadSensor(PID_RPM, value) || value == 0); + DisplayString("Engine started!", 0 , 6); + delay(1000); + } + void Loop() + { + int value; + byte count = 0; + ClearScreen(); + DisplayString("rpm", 84, 0); + DisplayString("km/h", 84, 3); + for (;;) { + char buf[16]; + + if (count == 0) { + DisplayString("AIR: ", 0, 6); + if (ReadSensor(PID_INTAKE_TEMP, value)) { + sprintf(buf, "%4dC", value); + DisplayString(buf, 11 * 8, 6); + } + } else if (count == LOOP_COUNT / 2) { + DisplayString("ENGINE: ", 0, 6); + if (ReadSensor(PID_COOLANT_TEMP, value)) { + sprintf(buf, "%4dC", value); + DisplayString(buf, 11 * 8, 6); + } + } + if (count < LOOP_COUNT / 2) { + if (ReadSensor(PID_INTAKE_PRESSURE, value)) { + sprintf(buf, "%dkPa ", value); + DisplayString(buf, 5 * 8, 6); + } + } else { + if (ReadSensor(PID_ENGINE_LOAD, value)) { + sprintf(buf, "%d%% ", value); + DisplayString(buf, 8 * 8, 6); + } + } + + if (ReadSensor(PID_RPM, value)) { + sprintf(buf, "%4d", value); + DisplayLargeNumber(buf, 16, 0); + } + + if (ReadSensor(PID_SPEED, value)) { + sprintf(buf, "%3d", value); + DisplayLargeNumber(buf, 32, 3); + } + + if (errors > 5) { + return; + } + count = (count + 1) % LOOP_COUNT; + } + } +private: + void DisplayString(const char* s, char x = 0, char y = 0) + { + ZT.ScI2cMxDisplay8x16Str(OLED_ADDRESS, y, x, s); + } + void DisplayLargeNumber(const char* s, char x = 0, char y = 0) + { + char data[32]; + while (*s) { + if (*s >= '0' && *s <= '9') { + memcpy_P(data, font16x32[*s - '0'], 32); + ZT.ScI2cMxDisplayDot16x16(OLED_ADDRESS, y , x, data); + } else { + ZT.ScI2cMxFillArea(OLED_ADDRESS, y, y + 1, x, x + 16, 0); + } + x += 16; + s++; + } + } + void ClearScreen() + { + ZT.ScI2cMxFillArea(OLED_ADDRESS, 0, 7, 0, 127, 0); + delay(10); + } + void InitScreen() + { + ZT.I2cInit(); + ZT.ScI2cMxReset(OLED_ADDRESS); + delay(10); + ClearScreen(); + } +}; + +COBDDash dash; + +void loop() +{ + dash.Connect(); + dash.Loop(); +} + +void setup() +{ + OBDUART.begin(OBD_SERIAL_BAUDRATE); +} + diff --git a/samples/obdtest/obdtest.ino b/samples/obdtest/obdtest.ino new file mode 100644 index 0000000..27854eb --- /dev/null +++ b/samples/obdtest/obdtest.ino @@ -0,0 +1,243 @@ +#include <Arduino.h> +#include <Wire.h> +#include <MultiLCD.h> +#include <OBD.h> +#include <MPU6050.h> + +#define INIT_CMD_COUNT 8 +#define MAX_CMD_LEN 6 + +const char initcmd[INIT_CMD_COUNT][MAX_CMD_LEN] = {"ATZ\r","ATE0\r","ATL1\r","ATI\r","0100\r","0120\r","0140\r","0145\r"}; + +//SoftwareSerial softSerial(2, 3); // RX, TX + +unsigned int adc_key_val[5] ={30, 150, 360, 535, 760 }; +int NUM_KEYS = 5; +int adc_key_in; +char key=-1; +char oldkey=-1; + +byte index = 0; +uint16_t pid = 0x0145; +int stateMPU6050; + +//create object to control an LCD. +LCD_1602 lcd; + +class COBDTester : public COBD +{ +public: + bool Init(bool passive = false) + { + unsigned long currentMillis; + unsigned char n; + char prompted; + char buffer[OBD_RECV_BUF_SIZE]; + + for (unsigned char i = 0; i < INIT_CMD_COUNT; i++) { + lcd.clear(); + lcd.setCursor(0, 0); + lcd.print(initcmd[i]); + lcd.setCursor(0, 1); + WriteData(initcmd[i]); + n = 0; + prompted = 0; + currentMillis = millis(); + for (;;) { + if (DataAvailable()) { + char c = ReadData(); + if (c == '>') { + buffer[n] = 0; + prompted++; + } else if (n < OBD_RECV_BUF_SIZE - 1) { + buffer[n++] = c; + + if (c == '\r' || c == '\n') + lcd.setCursor(0, 1); + else + lcd.write(c); + } + } else if (prompted) { + break; + } else { + unsigned long elapsed = millis() - currentMillis; + if (elapsed > OBD_TIMEOUT_INIT) { + // init timeout + //WriteData("\r"); + return false; + } + } + } + delay(200); + } + errors = 0; + return true; + } +}; + +COBDTester obd; + +// Convert ADC value to key number +char get_key(unsigned int input) +{ + char k; + for (k = 0; k < NUM_KEYS; k++) { + if (input < adc_key_val[k]) + return k; + } + return -1; +} + +void query() +{ + char buf[17]; + + switch (index) { + case 0: + sprintf(buf, "[%04X]", pid); + break; + case 1: + sprintf(buf, "%03X[%01X]", pid >> 4, pid & 0xf); + break; + case 2: + sprintf(buf, "%02X[%01X]%01X", pid >> 8, (pid >> 4) & 0xf, pid & 0xf); + break; + case 3: + sprintf(buf, "%01X[%01X]%02X", pid >> 12, (pid >> 8) & 0xf, pid & 0xff); + break; + case 4: + sprintf(buf, "[%01X]%03X", pid >> 12, pid & 0xfff); + break; + } + + lcd.setCursor(0, 0); + lcd.print(buf); + + obd.dataMode = (byte)(pid >> 8); + obd.Query((byte)pid); + + if (stateMPU6050 == 0) { + accel_t_gyro_union data; + char buf[20]; + int ret = MPU6050_readout(&data); + if (ret == 0) { + sprintf(buf, "%d/%d/%d", data.value.x_accel, data.value.y_accel, data.value.z_accel); + } else { + sprintf(buf, "6050 error: %d", ret); + } + } +} + +void setup() +{ + lcd.begin(); + pinMode(13, OUTPUT); //we'll use the debug LED to output a heartbeat + digitalWrite(13, LOW); + OBDUART.begin(38400); + digitalWrite(13, HIGH); + + + lcd.clear(); + lcd.print("Init MPU6050..."); + lcd.setCursor(0, 1); + stateMPU6050 = MPU6050_init(); + if (stateMPU6050 == 0) { + lcd.print("Success!"); + } else { + char buf[16]; + sprintf(buf, "Error: %d", stateMPU6050); + lcd.print(buf); + } + delay(1000); + + do { + lcd.clear(); + lcd.print("Init OBD..."); + delay(500); + } while(!obd.Init()); + + char buf[16]; + lcd.setCursor(0, 1); + lcd.print("CONNECTED! "); + delay(1000); + lcd.clear(); + query(); +} + +void loop() +{ + if (Serial.available()) { + char c = Serial.read(); + if (c == '\r' || c == '\n') { + lcd.setCursor(1, 6); + } else if (c == '>') { + lcd.setCursor(15, 0); + lcd.write(c); + lcd.setCursor(1, 6); + query(); + } else { + lcd.write(c); + } + } + + adc_key_in = analogRead(0); // read the value from the sensor + key = get_key(adc_key_in); // convert into key press + if (key != oldkey) { + delay(50); // wait for debounce time + adc_key_in = analogRead(0); // read the value from the sensor + key = get_key(adc_key_in); // convert into key press + if (key != oldkey) + { + oldkey = key; + if (key >=0){ + switch (key) { + case 2: // down key + switch (index) { + case 0: + pid--; + break; + case 1: + pid = (pid & 0xfff0) | (((pid & 0xf) - 1) & 0xf); + break; + case 2: + pid = (pid & 0xff0f) | (((pid & 0xf0) - 0x10) & 0xf0); + break; + case 3: + pid = (pid & 0xf0ff) | (((pid & 0xf00) - 0x100) & 0xf00); + break; + case 4: + pid = (pid & 0x0fff) | (((pid & 0xf000) - 0x1000) & 0xf000); + break; + } + break; + case 1: // up key + switch (index) { + case 0: + pid++; + break; + case 1: + pid = (pid & 0xfff0) | (((pid & 0xf) + 1) & 0xf); + break; + case 2: + pid = (pid & 0xff0f) | (((pid & 0xf0) + 0x10) & 0xf0); + break; + case 3: + pid = (pid & 0xf0ff) | (((pid & 0xf00) + 0x100) & 0xf00); + break; + case 4: + pid = (pid & 0x0fff) | (((pid & 0xf000) + 0x1000) & 0xf000); + } + break; + case 0: // right key + if (index > 0) index--; + break; + case 3: // left key + if (index < 4) index++; + break; + } + lcd.clear(); + query(); + } + } + } +} diff --git a/samples/rpm_led/rpm_led.ino b/samples/rpm_led/rpm_led.ino new file mode 100644 index 0000000..415f119 --- /dev/null +++ b/samples/rpm_led/rpm_led.ino @@ -0,0 +1,31 @@ +/************************************************************************* +* Sample sketch based on OBD-II library for Arduino +* Distributed under GPL v2.0 +* Copyright (c) 2012-2013 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <OBD.h> + +COBD obd; + +void setup() +{ + // we'll use the debug LED as output + pinMode(13, OUTPUT); + // start serial communication at the adapter defined baudrate + OBDUART.begin(OBD_SERIAL_BAUDRATE); + // initiate OBD-II connection until success + while (!obd.Init()); +} + +void loop() +{ + int value; + if (obd.ReadSensor(PID_RPM, value)) { + // RPM is read and stored in 'value' + // light on LED when RPM exceeds 5000 + digitalWrite(13, value > 5000 ? HIGH : LOW); + } +} |