diff options
-rw-r--r-- | nanologger/MicroLCD.cpp | 638 | ||||
-rw-r--r-- | nanologger/MicroLCD.h | 94 | ||||
-rw-r--r-- | nanologger/README.txt | 11 | ||||
-rw-r--r-- | nanologger/SH1106.cpp | 401 | ||||
-rw-r--r-- | nanologger/SSD1306.cpp | 273 | ||||
-rw-r--r-- | nanologger/SSD1306.h | 119 | ||||
-rw-r--r-- | nanologger/config.h | 50 | ||||
-rw-r--r-- | nanologger/datalogger.h | 340 | ||||
-rw-r--r-- | nanologger/images.h | 5 | ||||
-rw-r--r-- | nanologger/nanologger.cbp | 599 | ||||
-rw-r--r-- | nanologger/nanologger.ino | 401 |
11 files changed, 2931 insertions, 0 deletions
diff --git a/nanologger/MicroLCD.cpp b/nanologger/MicroLCD.cpp new file mode 100644 index 0000000..4e99c83 --- /dev/null +++ b/nanologger/MicroLCD.cpp @@ -0,0 +1,638 @@ +/************************************************************************* +* Arduino Text Display Library for Multiple LCDs +* Distributed under GPL v2.0 +* Copyright (c) 2013 Stanley Huang <stanleyhuangyc@live.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <Wire.h> +#include "MicroLCD.h" + +// fonts data +const PROGMEM unsigned char digits16x24[][48] = { +{0x00,0x00,0x00,0xF0,0xFF,0x0F,0xFC,0xFF,0x3F,0xFE,0xFF,0x7F,0xFE,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0x00,0xE0,0x07,0x00,0xE0,0x07,0x00,0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x7F,0xFE,0xFF,0x7F,0xFC,0xFF,0x3F,0xF0,0xFF,0x0F},/*"0",0*/ +{0x00,0x00,0x00,0x70,0x00,0x00,0x70,0x00,0x00,0x70,0x00,0x00,0x78,0x00,0x00,0xF8,0x00,0x00,0xFC,0xFF,0xFF,0xFE,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"1",0*/ +{0x00,0x00,0x00,0xF8,0x00,0xE0,0xFC,0x00,0xF8,0xFE,0x00,0xFE,0xFE,0x80,0xFF,0xFF,0xC0,0xFF,0x07,0xF0,0xFF,0x07,0xFC,0xFF,0x07,0xFF,0xEF,0xFF,0xFF,0xE3,0xFF,0xFF,0xE1,0xFE,0x7F,0xE0,0xFE,0x3F,0xE0,0xFC,0x0F,0xE0,0xF0,0x03,0x00,0x00,0x00,0x00},/*"2",2*/ +{0x00,0x00,0x00,0xF8,0x80,0x1F,0xFE,0x80,0x3F,0xFE,0x80,0x7F,0xFF,0x80,0x7F,0xFF,0x80,0xFF,0xFF,0x9C,0xFF,0xFF,0x9C,0xFF,0x07,0x1C,0xE0,0x07,0x3E,0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x7F,0xFE,0xF7,0x7F,0xFC,0xF7,0x3F,0xF0,0xE3,0x1F},/*"3",3*/ +{0x00,0xF0,0x0F,0x00,0xFE,0x0F,0x80,0xFF,0x0F,0xE0,0xFF,0x0F,0xFC,0xBF,0x0F,0xFF,0x87,0x0F,0xFF,0x81,0x0F,0x3F,0x80,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x80,0x0F,0x00,0x80,0x0F},/*"4",4*/ +{0x00,0x00,0x00,0xFF,0xC7,0x0F,0xFF,0xC7,0x3F,0xFF,0xC7,0x7F,0xFF,0xC7,0x7F,0xFF,0xC7,0xFF,0xFF,0xC7,0xFF,0x87,0x01,0xE0,0xC7,0x01,0xE0,0xC7,0x01,0xE0,0xC7,0xFF,0xFF,0xC7,0xFF,0xFF,0xC7,0xFF,0x7F,0x87,0xFF,0x7F,0x87,0xFF,0x3F,0x07,0xFE,0x1F},/*"5",5*/ +{0x00,0x00,0x00,0xF0,0xFF,0x0F,0xFC,0xFF,0x3F,0xFE,0xFF,0x7F,0xFE,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0x06,0xE0,0x07,0x07,0xE0,0x07,0x07,0xE0,0x3F,0xFF,0xFF,0x3F,0xFF,0xFF,0x3E,0xFF,0x7F,0x3E,0xFE,0x7F,0x3C,0xFE,0x3F,0x38,0xF8,0x1F},/*"6",6*/ +{0x00,0x00,0x00,0x07,0x00,0x00,0x07,0x00,0x00,0x07,0x00,0xC0,0x07,0x00,0xF8,0x07,0x00,0xFF,0x07,0xE0,0xFF,0x07,0xFE,0xFF,0xC7,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0x07,0xFF,0xFF,0x00,0xFF,0x0F,0x00,0xFF,0x01,0x00,0x1F,0x00,0x00,0x00,0x00,0x00},/*"7",1*/ +{0x00,0x00,0x00,0xF0,0xE3,0x1F,0xFC,0xF7,0x3F,0xFE,0xFF,0x7F,0xFE,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0x1C,0xE0,0x07,0x1C,0xE0,0x07,0x1C,0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x7F,0xFE,0xF7,0x7F,0xFC,0xF7,0x3F,0xF0,0xE3,0x1F},/*"8",8*/ +{0x00,0x00,0x00,0xF8,0x1F,0x1C,0xFC,0x7F,0x3C,0xFE,0x7F,0x7C,0xFE,0xFF,0x7C,0xFF,0xFF,0xFC,0xFF,0xFF,0xFC,0x07,0xE0,0xE0,0x07,0xE0,0xE0,0x07,0x60,0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x7F,0xFE,0xFF,0x7F,0xFC,0xFF,0x3F,0xF0,0xFF,0x0F},/*"9",9*/ +}; + +const PROGMEM unsigned char digits8x8[][8] = { +{0x3C,0x7E,0x83,0x81,0x81,0x7E,0x3C,0x00},/*0*/ +{0x84,0x84,0x82,0xFF,0xFF,0x80,0x80,0x00},/*1*/ +{0x84,0xC6,0xE1,0xA1,0xB1,0x9F,0x8E,0x00},/*2*/ +{0x42,0xC3,0x81,0x89,0x89,0xFF,0x76,0x00},/*3*/ +{0x20,0x38,0x24,0x22,0xFF,0xFF,0x20,0x00},/*4*/ +{0x5F,0xDF,0x99,0x89,0x89,0xF9,0x70,0x00},/*5*/ +{0x3C,0x7E,0x89,0x89,0x89,0xFB,0x72,0x00},/*6*/ +{0x01,0x01,0xE1,0xF9,0x1D,0x07,0x01,0x00},/*7*/ +{0x6E,0xFF,0x89,0x89,0x99,0xFF,0x76,0x00},/*8*/ +{0x4E,0xDF,0x91,0x91,0x91,0x7F,0x3E,0x00},/*9*/ +}; + +// The 7-bit ASCII character set... +const PROGMEM unsigned char font5x8[][5] = { + { 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 ~ +}; + +#ifndef MEMORY_SAVING +const PROGMEM unsigned char digits16x16[][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*/ +}; + +const PROGMEM unsigned char font8x16_terminal[][16] = { +{0x00,0x00,0x00,0x00,0x7C,0x00,0xFE,0x1B,0xFE,0x1B,0x7C,0x00,0x00,0x00,0x00,0x00},/*"!",0*/ +{0x00,0x00,0x0E,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x0E,0x00,0x00,0x00},/*""",1*/ +{0x20,0x01,0xFC,0x0F,0xFC,0x0F,0x20,0x01,0x20,0x01,0xFC,0x0F,0xFC,0x0F,0x20,0x01},/*"#",2*/ +{0x38,0x06,0x7C,0x0C,0x44,0x08,0xFF,0x3F,0xFF,0x3F,0x84,0x08,0x8C,0x0F,0x18,0x07},/*"$",3*/ +{0x1C,0x18,0x14,0x1E,0x9C,0x07,0xE0,0x01,0x78,0x1C,0x1E,0x14,0x06,0x1C,0x00,0x00},/*"%",4*/ +{0xBC,0x1F,0xFE,0x10,0x42,0x10,0xC2,0x10,0xFE,0x1F,0x3C,0x0F,0x80,0x19,0x80,0x10},/*"&",5*/ +{0x00,0x00,0x00,0x00,0x10,0x00,0x1E,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",6*/ +{0x00,0x00,0x00,0x00,0xF0,0x07,0xFC,0x1F,0x0E,0x38,0x02,0x20,0x00,0x00,0x00,0x00},/*"(",7*/ +{0x00,0x00,0x00,0x00,0x02,0x20,0x0E,0x38,0xFC,0x1F,0xF0,0x07,0x00,0x00,0x00,0x00},/*")",8*/ +{0x80,0x00,0xA0,0x02,0xE0,0x03,0xC0,0x01,0xC0,0x01,0xE0,0x03,0xA0,0x02,0x80,0x00},/*"*",9*/ +{0x80,0x00,0x80,0x00,0x80,0x00,0xE0,0x03,0xE0,0x03,0x80,0x00,0x80,0x00,0x80,0x00},/*"+",10*/ +{0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x78,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00},/*",",11*/ +{0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00},/*"-",12*/ +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00},/*".",13*/ +{0x00,0x18,0x00,0x1E,0x80,0x07,0xE0,0x01,0x78,0x00,0x1E,0x00,0x06,0x00,0x00,0x00},/*"/",14*/ +{0xF8,0x07,0xFC,0x0F,0x06,0x18,0xC2,0x10,0xC2,0x10,0x06,0x18,0xFC,0x0F,0xF8,0x07},/*"0",15*/ +{0x00,0x00,0x08,0x10,0x0C,0x10,0xFE,0x1F,0xFE,0x1F,0x00,0x10,0x00,0x10,0x00,0x00},/*"1",16*/ +{0x04,0x1C,0x06,0x1E,0x02,0x13,0x82,0x11,0xC2,0x10,0x62,0x10,0x3E,0x18,0x1C,0x18},/*"2",17*/ +{0x04,0x08,0x06,0x18,0x02,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0xFE,0x1F,0xBC,0x0F},/*"3",18*/ +{0xC0,0x01,0xE0,0x01,0x30,0x01,0x18,0x01,0x0C,0x11,0xFE,0x1F,0xFE,0x1F,0x00,0x11},/*"4",19*/ +{0x7E,0x08,0x7E,0x18,0x42,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0xC2,0x1F,0x82,0x0F},/*"5",20*/ +{0xF8,0x0F,0xFC,0x1F,0x46,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0xC0,0x1F,0x80,0x0F},/*"6",21*/ +{0x06,0x00,0x06,0x00,0x02,0x00,0x02,0x1F,0xC2,0x1F,0xF2,0x00,0x3E,0x00,0x0E,0x00},/*"7",22*/ +{0xBC,0x0F,0xFE,0x1F,0x42,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0xFE,0x1F,0xBC,0x0F},/*"8",23*/ +{0x3C,0x00,0x7E,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0x42,0x18,0xFE,0x0F,0xFC,0x07},/*"9",24*/ +{0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x0C,0x30,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/ +{0x00,0x00,0x00,0x00,0x00,0x20,0x60,0x3C,0x60,0x1C,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/ +{0x80,0x00,0xC0,0x01,0x60,0x03,0x30,0x06,0x18,0x0C,0x0C,0x18,0x04,0x10,0x00,0x00},/*"<",28*/ +{0x40,0x02,0x40,0x02,0x40,0x02,0x40,0x02,0x40,0x02,0x40,0x02,0x40,0x02,0x40,0x02},/*"=",29*/ +{0x04,0x10,0x0C,0x18,0x18,0x0C,0x30,0x06,0x60,0x03,0xC0,0x01,0x80,0x00,0x00,0x00},/*">",30*/ +{0x04,0x00,0x06,0x00,0x02,0x00,0x82,0x1B,0xC2,0x1B,0x62,0x00,0x3E,0x00,0x1C,0x00},/*"?",31*/ +{0xFC,0x0F,0xFE,0x1F,0x02,0x10,0x82,0x11,0xC2,0x13,0xE2,0x13,0xFE,0x13,0xFC,0x03},/*"@",32*/ +{0xF0,0x1F,0xF8,0x1F,0x0C,0x01,0x06,0x01,0x06,0x01,0x0C,0x01,0xF8,0x1F,0xF0,0x1F},/*"A",33*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x42,0x10,0x42,0x10,0x42,0x10,0xFE,0x1F,0xBC,0x0F},/*"B",34*/ +{0xF8,0x07,0xFC,0x0F,0x06,0x18,0x02,0x10,0x02,0x10,0x02,0x10,0x06,0x18,0x0C,0x0C},/*"C",35*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x02,0x10,0x02,0x10,0x06,0x18,0xFC,0x0F,0xF8,0x07},/*"D",36*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x42,0x10,0x42,0x10,0xE2,0x10,0x06,0x18,0x06,0x18},/*"E",37*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x42,0x10,0x42,0x00,0xE2,0x00,0x06,0x00,0x06,0x00},/*"F",38*/ +{0xF8,0x07,0xFC,0x0F,0x06,0x18,0x02,0x10,0x82,0x10,0x82,0x10,0x86,0x0F,0x8C,0x1F},/*"G",39*/ +{0xFE,0x1F,0xFE,0x1F,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0xFE,0x1F,0xFE,0x1F},/*"H",40*/ +{0x00,0x00,0x02,0x10,0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x02,0x10,0x02,0x10,0x00,0x00},/*"I",41*/ +{0x00,0x0C,0x00,0x1C,0x00,0x10,0x00,0x10,0x02,0x10,0xFE,0x1F,0xFE,0x0F,0x02,0x00},/*"J",42*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0xE0,0x00,0xB0,0x01,0x18,0x03,0x0E,0x1E,0x06,0x1C},/*"K",43*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x02,0x10,0x00,0x10,0x00,0x10,0x00,0x18,0x00,0x18},/*"L",44*/ +{0xFE,0x1F,0xFE,0x1F,0x18,0x00,0xF0,0x00,0xF0,0x00,0x18,0x00,0xFE,0x1F,0xFE,0x1F},/*"M",45*/ +{0xFE,0x1F,0xFE,0x1F,0x38,0x00,0x70,0x00,0xE0,0x00,0xC0,0x01,0xFE,0x1F,0xFE,0x1F},/*"N",46*/ +{0xFC,0x0F,0xFE,0x1F,0x02,0x10,0x02,0x10,0x02,0x10,0x02,0x10,0xFE,0x1F,0xFC,0x0F},/*"O",47*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x42,0x10,0x42,0x00,0x42,0x00,0x7E,0x00,0x3C,0x00},/*"P",48*/ +{0xFC,0x0F,0xFE,0x1F,0x02,0x10,0x02,0x1C,0x02,0x38,0x02,0x70,0xFE,0x5F,0xFC,0x0F},/*"Q",49*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x42,0x00,0x42,0x00,0xC2,0x00,0xFE,0x1F,0x3C,0x1F},/*"R",50*/ +{0x1C,0x0C,0x3E,0x1C,0x62,0x10,0x42,0x10,0x42,0x10,0xC2,0x10,0x8E,0x1F,0x0C,0x0F},/*"S",51*/ +{0x06,0x00,0x06,0x00,0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x02,0x10,0x06,0x00,0x06,0x00},/*"T",52*/ +{0xFE,0x0F,0xFE,0x1F,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0xFE,0x1F,0xFE,0x0F},/*"U",53*/ +{0xFE,0x03,0xFE,0x07,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x0C,0xFE,0x07,0xFE,0x03},/*"V",54*/ +{0xFE,0x07,0xFE,0x1F,0x00,0x1C,0xC0,0x07,0xC0,0x07,0x00,0x1C,0xFE,0x1F,0xFE,0x07},/*"W",55*/ +{0x0E,0x1C,0x1E,0x1E,0x30,0x03,0xE0,0x01,0xE0,0x01,0x30,0x03,0x1E,0x1E,0x0E,0x1C},/*"X",56*/ +{0x1E,0x00,0x3E,0x00,0x60,0x10,0xC0,0x1F,0xC0,0x1F,0x60,0x10,0x3E,0x00,0x1E,0x00},/*"Y",57*/ +{0x06,0x1E,0x06,0x1F,0x82,0x11,0xC2,0x10,0x62,0x10,0x32,0x10,0x1E,0x18,0x0E,0x18},/*"Z",58*/ +{0x00,0x00,0x00,0x00,0xFE,0x1F,0xFE,0x1F,0x02,0x10,0x02,0x10,0x00,0x00,0x00,0x00},/*"[",59*/ +{0x00,0x18,0x00,0x1E,0x80,0x07,0xE0,0x01,0x78,0x00,0x1E,0x00,0x06,0x00,0x00,0x00},/*"/",60*/ +{0x00,0x00,0x00,0x00,0x02,0x10,0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x00,0x00,0x00,0x00},/*"]",61*/ +{0x20,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x20,0x00,0x00,0x00},/*"^",62*/ +{0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80},/*"_",63*/ +{0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x78,0x00,0x40,0x00,0x00,0x00,0x00,0x00},/*"`",64*/ +{0x00,0x0E,0x20,0x1F,0x20,0x11,0x20,0x11,0x20,0x11,0xE0,0x0F,0xC0,0x1F,0x00,0x10},/*"a",65*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x0F,0x20,0x10,0x20,0x10,0x60,0x10,0xC0,0x1F,0x80,0x0F},/*"b",66*/ +{0xC0,0x0F,0xE0,0x1F,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x60,0x18,0x40,0x08},/*"c",67*/ +{0x80,0x0F,0xC0,0x1F,0x60,0x10,0x20,0x10,0x22,0x10,0xFE,0x0F,0xFE,0x1F,0x00,0x10},/*"d",68*/ +{0xC0,0x0F,0xE0,0x1F,0x20,0x11,0x20,0x11,0x20,0x11,0x20,0x11,0xE0,0x19,0xC0,0x09},/*"e",69*/ +{0x00,0x00,0x20,0x10,0xFC,0x1F,0xFE,0x1F,0x22,0x10,0x22,0x00,0x06,0x00,0x04,0x00},/*"f",70*/ +{0xC0,0x4F,0xE0,0xDF,0x20,0x90,0x20,0x90,0x20,0x90,0xC0,0xFF,0xE0,0x7F,0x20,0x00},/*"g",71*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x40,0x00,0x20,0x00,0x20,0x00,0xE0,0x1F,0xC0,0x1F},/*"h",72*/ +{0x00,0x00,0x20,0x10,0x20,0x10,0xEC,0x1F,0xEC,0x1F,0x00,0x10,0x00,0x10,0x00,0x00},/*"i",73*/ +{0x00,0x60,0x00,0xC0,0x20,0x80,0x20,0x80,0xEC,0xFF,0xEC,0x7F,0x00,0x00,0x00,0x00},/*"j",74*/ +{0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x80,0x01,0x80,0x03,0xC0,0x06,0x60,0x1C,0x20,0x18},/*"k",75*/ +{0x00,0x00,0x02,0x10,0x02,0x10,0xFE,0x1F,0xFE,0x1F,0x00,0x10,0x00,0x10,0x00,0x00},/*"l",76*/ +{0xE0,0x1F,0xE0,0x1F,0x60,0x00,0xC0,0x0F,0xC0,0x0F,0x60,0x00,0xE0,0x1F,0xC0,0x1F},/*"m",77*/ +{0x20,0x00,0xE0,0x1F,0xC0,0x1F,0x20,0x00,0x20,0x00,0x20,0x00,0xE0,0x1F,0xC0,0x1F},/*"n",78*/ +{0xC0,0x0F,0xE0,0x1F,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0xE0,0x1F,0xC0,0x0F},/*"o",79*/ +{0x20,0x80,0xE0,0xFF,0xC0,0xFF,0x20,0x90,0x20,0x10,0x20,0x10,0xE0,0x1F,0xC0,0x0F},/*"p",80*/ +{0xC0,0x0F,0xE0,0x1F,0x20,0x10,0x20,0x10,0x20,0x90,0xC0,0xFF,0xE0,0xFF,0x20,0x80},/*"q",81*/ +{0x20,0x10,0xE0,0x1F,0xC0,0x1F,0x60,0x10,0x20,0x00,0x20,0x00,0x60,0x00,0x40,0x00},/*"r",82*/ +{0xC0,0x08,0xE0,0x19,0x20,0x11,0x20,0x11,0x20,0x13,0x20,0x12,0x60,0x1E,0x40,0x0C},/*"s",83*/ +{0x20,0x00,0x20,0x00,0xFC,0x0F,0xFE,0x1F,0x20,0x10,0x20,0x18,0x00,0x08,0x00,0x00},/*"t",84*/ +{0xE0,0x0F,0xE0,0x1F,0x00,0x10,0x00,0x10,0x00,0x10,0xE0,0x0F,0xE0,0x1F,0x00,0x10},/*"u",85*/ +{0xE0,0x03,0xE0,0x07,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x0C,0xE0,0x07,0xE0,0x03},/*"v",86*/ +{0xE0,0x0F,0xE0,0x1F,0x00,0x18,0x00,0x0F,0x00,0x0F,0x00,0x18,0xE0,0x1F,0xE0,0x0F},/*"w",87*/ +{0x20,0x10,0x60,0x18,0xC0,0x0C,0x80,0x07,0x80,0x07,0xC0,0x0C,0x60,0x18,0x20,0x10},/*"x",88*/ +{0xE0,0x8F,0xE0,0x9F,0x00,0x90,0x00,0x90,0x00,0x90,0x00,0xD0,0xE0,0x7F,0xE0,0x3F},/*"y",89*/ +{0x60,0x18,0x60,0x1C,0x20,0x16,0x20,0x13,0xA0,0x11,0xE0,0x10,0x60,0x18,0x20,0x18},/*"z",90*/ +{0x00,0x00,0x00,0x00,0x80,0x00,0xFC,0x1F,0x7E,0x3F,0x02,0x20,0x02,0x20,0x00,0x00},/*"{",91*/ +{0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x3E,0x7C,0x3E,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/ +{0x00,0x00,0x02,0x20,0x02,0x20,0x7E,0x3F,0xFC,0x1F,0x80,0x00,0x00,0x00,0x00,0x00},/*"}",93*/ +}; +#endif + +void LCD_Common::printInt(uint16_t value, int8_t padding) +{ + unsigned int den = 10000; + for (byte i = 5; i > 0; i--) { + byte v = (byte)(value / den); + value -= v * den; + den /= 10; + if (v == 0 && padding && den) { + if (padding >= i) { + writeDigit((m_flags & FLAG_PAD_ZERO) ? 0 : -1); + } + continue; + } + padding = 0; + writeDigit(v); + } +} + +void LCD_Common::printLong(uint32_t value, int8_t padding) +{ + unsigned long den = 1000000000; + for (byte i = 10; i > 0; i--) { + byte v = (byte)(value / den); + value -= v * den; + den /= 10; + if (v == 0 && padding && den) { + if (padding >= i) { + writeDigit((m_flags & FLAG_PAD_ZERO) ? 0 : -1); + } + continue; + } + padding = 0; + writeDigit(v); + } +} + +void LCD_SSD1306::setCursor(byte column, byte line) +{ + m_col = column; + m_row = line; + ssd1306_command(0xB0 + m_row);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address +} + +size_t LCD_SSD1306::write(uint8_t c) +{ + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + if (c == '\n') { + setCursor(0, m_row + ((m_font == FONT_SIZE_SMALL) ? 1 : 2)); + return 1; + } else if (c == '\r') { + m_col = 0; + return 1; + } +#ifndef MEMORY_SAVING + if (m_font == FONT_SIZE_SMALL) { +#endif + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + if (c > 0x20 && c < 0x7f) { + c -= 0x21; + for (byte i = 0; i < 5; i++) { + byte d = pgm_read_byte(&font5x8[c][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.write(0); + } else { + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 11 : 6; i > 0; i--) { + Wire.write(0); + } + } + Wire.endTransmission(); + m_col += (m_flags & FLAG_PIXEL_DOUBLE_H) ? 11 : 6; + if (m_col >= 128) { + m_col = 0; + m_row ++; + } +#ifndef MEMORY_SAVING + } else { + if (c > 0x20 && c < 0x7f) { + c -= 0x21; + + ssd1306_command(0xB0 + m_row);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = 0; i <= 14; i += 2) { + byte d = pgm_read_byte(&font8x16_terminal[c][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + + ssd1306_command(0xB0 + m_row + 1);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = 1; i <= 15; i += 2) { + byte d = pgm_read_byte(&font8x16_terminal[c][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + } else { + ssd1306_command(0xB0 + m_row);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 16 : 8; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + + ssd1306_command(0xB0 + m_row + 1);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 16 : 8; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + } + m_col += (m_flags & FLAG_PIXEL_DOUBLE_H) ? 17 : 9; + if (m_col >= 128) { + m_col = 0; + m_row += 2; + } + } +#endif + TWBR = twbrbackup; + return 1; +} + +void LCD_SSD1306::writeDigit(byte n) +{ + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + if (m_font == FONT_SIZE_SMALL) { + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + if (n <= 9) { + n += '0' - 0x21; + for (byte i = 0; i < 5; i++) { + Wire.write(pgm_read_byte(&font5x8[n][i])); + } + Wire.write(0); + } else { + for (byte i = 0; i < 6; i++) { + Wire.write(0); + } + } + Wire.endTransmission(); + m_col += 6; + } else if (m_font == FONT_SIZE_MEDIUM) { + write(n <= 9 ? ('0' + n) : ' '); +#ifndef MEMORY_SAVING + } else if (m_font == FONT_SIZE_LARGE) { + if (n <= 9) { + byte i; + ssd1306_command(0xB0 + m_row);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (i = 0; i < 16; i ++) { + byte d = pgm_read_byte(&digits16x16[n][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + + ssd1306_command(0xB0 + m_row + 1);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (; i < 32; i ++) { + byte d = pgm_read_byte(&digits16x16[n][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + } else { + ssd1306_command(0xB0 + m_row);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + + ssd1306_command(0xB0 + m_row + 1);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + } + m_col += (m_flags & FLAG_PIXEL_DOUBLE_H) ? 30 : 16; +#endif + } else { + if (n <= 9) { + byte i; + ssd1306_command(0xB0 + m_row);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (i = 0; i < 16; i ++) { + byte d = pgm_read_byte(&digits16x24[n][i * 3]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + + ssd1306_command(0xB0 + m_row + 1);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (i = 0; i < 16; i ++) { + byte d = pgm_read_byte(&digits16x24[n][i * 3 + 1]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + + ssd1306_command(0xB0 + m_row + 2);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (i = 0; i < 16; i ++) { + byte d = pgm_read_byte(&digits16x24[n][i * 3 + 2]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + } else { + ssd1306_command(0xB0 + m_row);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + + ssd1306_command(0xB0 + m_row + 1);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + + ssd1306_command(0xB0 + m_row + 2);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + } + m_col += (m_flags & FLAG_PIXEL_DOUBLE_H) ? 30 : 16; + } + TWBR = twbrbackup; +} + +void LCD_SSD1306::draw(const PROGMEM byte* buffer, byte width, byte height) +{ + ssd1306_command(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0 + ssd1306_command(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0 + ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 + + // save I2C bitrate + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + + const PROGMEM byte *p = buffer; + height >>= 3; + width >>= 3; + for (byte i = 0; i < height; i++) { + // send a bunch of data in one xmission + ssd1306_command(0xB0 + i + m_row);//set page address + ssd1306_command(m_col & 0xf);//set lower column address + ssd1306_command(0x10 | (m_col >> 4));//set higher column address + + for(byte j = 0; j < 8; j++){ + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte k = 0; k < width; k++, p++) { + Wire.write(pgm_read_byte(p)); + } + Wire.endTransmission(); + } + } + TWBR = twbrbackup; + m_col += width; +} + +void LCD_SSD1306::clearLine(byte line) +{ + ssd1306_command(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0 + ssd1306_command(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0 + ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 + + // save I2C bitrate + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + + // send a bunch of data in one xmission + ssd1306_command(0xB0 + line);//set page address + ssd1306_command(0);//set lower column address + ssd1306_command(0x10);//set higher column address + + for(byte j = 0; j < 8; j++){ + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte k = 0; k < 16; k++) { + Wire.write(0); + } + Wire.endTransmission(); + } + + TWBR = twbrbackup; +} + +void LCD_SSD1306::clear(byte x, byte y, byte width, byte height) +{ + ssd1306_command(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0 + ssd1306_command(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0 + ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 + + // save I2C bitrate + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + + height >>= 3; + width >>= 3; + y >>= 3; + for (byte i = 0; i < height; i++) { + // send a bunch of data in one xmission + ssd1306_command(0xB0 + i + y);//set page address + ssd1306_command(x & 0xf);//set lower column address + ssd1306_command(0x10 | (x >> 4));//set higher column address + + for(byte j = 0; j < 8; j++){ + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte k = 0; k < width; k++) { + Wire.write(0); + } + Wire.endTransmission(); + } + } + + setCursor(0, 0); + TWBR = twbrbackup; +} diff --git a/nanologger/MicroLCD.h b/nanologger/MicroLCD.h new file mode 100644 index 0000000..e421505 --- /dev/null +++ b/nanologger/MicroLCD.h @@ -0,0 +1,94 @@ +/************************************************************************* +* Arduino Text Display Library for Multiple LCDs +* Distributed under GPL v2.0 +* Copyright (c) 2013 Stanley Huang <stanleyhuangyc@live.com> +* All rights reserved. +*************************************************************************/ + +#if !defined(__AVR_ATmega2560__) && !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega644P__) +//#define MEMORY_SAVING +#endif + +typedef enum { + FONT_SIZE_SMALL = 0, + FONT_SIZE_MEDIUM, + FONT_SIZE_LARGE, + FONT_SIZE_XLARGE +} FONT_SIZE; + +#define FLAG_PAD_ZERO 1 +#define FLAG_PIXEL_DOUBLE_H 2 +#define FLAG_PIXEL_DOUBLE_V 4 +#define FLAG_PIXEL_DOUBLE (FLAG_PIXEL_DOUBLE_H | FLAG_PIXEL_DOUBLE_V) + +extern const PROGMEM unsigned char font5x8[][5]; +extern const PROGMEM unsigned char digits8x8[][8] ; +extern const PROGMEM unsigned char digits16x16[][32]; +extern const PROGMEM unsigned char digits16x24[][48]; +extern const PROGMEM unsigned char font8x16_doslike[][16]; +extern const PROGMEM unsigned char font8x16_terminal[][16]; + +class LCD_Common +{ +public: + LCD_Common():m_font(FONT_SIZE_SMALL),m_flags(0) {} + void setFont(FONT_SIZE size) { m_font = size; } + void setFlags(byte flags) { m_flags = flags; } + virtual void backlight(bool on) {} + virtual void draw(const PROGMEM byte* buffer, byte x, byte y, byte width, byte height) {} + void printInt(uint16_t value, int8_t padding = -1); + void printLong(uint32_t value, int8_t padding = -1); +protected: + virtual void writeDigit(byte n) {} + byte m_font; + byte m_flags; +}; + +class LCD_Null : public LCD_Common, public Print +{ +public: + byte getLines() { return 0; } + byte getCols() { return 0; } + void clearLine(byte line) {} + void clear() {} + void begin() {} + void setCursor(byte column, byte line) {} + size_t write(uint8_t c) { return 0; } +}; + +#include "SSD1306.h" + +class LCD_SSD1306 : public LCD_Common, public SSD1306, public Print +{ +public: + void setCursor(byte column, byte line); + void draw(const PROGMEM byte* buffer, byte width, byte height); + size_t write(uint8_t c); + void clear(byte x = 0, byte y = 0, byte width = 128, byte height = 64); + void clearLine(byte line); + byte getLines() { return 21; } + byte getCols() { return 8; } +private: + void writeDigit(byte n); + byte m_col; + byte m_row; +}; + +class LCD_SH1106 : public LCD_Common, public Print +{ +public: + void begin(); + void setCursor(byte column, byte line); + void draw(const PROGMEM byte* buffer, byte width, byte height); + size_t write(uint8_t c); + void clear(byte x = 0, byte y = 0, byte width = 128, byte height = 64); + void clearLine(byte line); + byte getLines() { return 21; } + byte getCols() { return 8; } +private: + void WriteCommand(unsigned char ins); + void WriteData(unsigned char dat); + void writeDigit(byte n); + byte m_col; + byte m_row; +}; diff --git a/nanologger/README.txt b/nanologger/README.txt new file mode 100644 index 0000000..5a1e5cf --- /dev/null +++ b/nanologger/README.txt @@ -0,0 +1,11 @@ +This is the source code for the Arduino OBD-II data logger, which displays (on a 128x64 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://freematics.com/chart/ + +To compile the code with Arduino IDE, please copy all library files from the libraries directory to Arduino's libraries directory. + +To open the project file (cbp), please download CodeBlocks Arduino Edition (http://arduinodev.com/codeblocks). + +The source code is distributed under GPL license. diff --git a/nanologger/SH1106.cpp b/nanologger/SH1106.cpp new file mode 100644 index 0000000..b1e57ef --- /dev/null +++ b/nanologger/SH1106.cpp @@ -0,0 +1,401 @@ +#include <Arduino.h> +#include <Wire.h> +#include "MicroLCD.h" + +#define I2C_ADDR 0x78 >> 1 + +void LCD_SH1106::WriteCommand(unsigned char ins) +{ + Wire.beginTransmission(I2C_ADDR);//0x78 >> 1 + Wire.write(0x00);//0x00 + Wire.write(ins); + Wire.endTransmission(); +} + +void LCD_SH1106::WriteData(unsigned char dat) +{ + Wire.beginTransmission(I2C_ADDR);//0x78 >> 1 + Wire.write(0x40);//0x40 + Wire.write(dat); + Wire.endTransmission(); +} + +void LCD_SH1106::setCursor(unsigned char x, unsigned char y) +{ + m_col = x + 2; + m_row = y; + WriteCommand(0xb0 + m_row); + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address +} + +void LCD_SH1106::clear(byte x, byte y, byte width, byte height) +{ + WriteCommand(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0 + WriteCommand(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0 + WriteCommand(SSD1306_SETSTARTLINE | 0x0); // line #0 + + // save I2C bitrate + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + + height >>= 3; + width >>= 3; + y >>= 3; + for (byte i = 0; i < height; i++) { + // send a bunch of data in one xmission + WriteCommand(0xB0 + i + y);//set page address + WriteCommand((x + 2) & 0xf);//set lower column address + WriteCommand(0x10 | (x >> 4));//set higher column address + + for(byte j = 0; j < 8; j++){ + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte k = 0; k < width; k++) { + Wire.write(0); + } + Wire.endTransmission(); + } + } + + setCursor(0, 0); + TWBR = twbrbackup; +} + +size_t LCD_SH1106::write(uint8_t c) +{ + if (c == '\n') { + setCursor(0, m_row + ((m_font == FONT_SIZE_SMALL) ? 1 : 2)); + return 1; + } else if (c == '\r') { + m_col = 0; + return 1; + } + + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! +#ifndef MEMORY_SAVING + if (m_font == FONT_SIZE_SMALL) { +#endif + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + if (c > 0x20 && c < 0x7f) { + c -= 0x21; + for (byte i = 0; i < 5; i++) { + byte d = pgm_read_byte(&font5x8[c][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.write(0); + } else { + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 11 : 6; i > 0; i--) { + Wire.write(0); + } + } + Wire.endTransmission(); + m_col += (m_flags & FLAG_PIXEL_DOUBLE_H) ? 11 : 6; + if (m_col >= 128) { + m_col = 0; + m_row ++; + } +#ifndef MEMORY_SAVING + } else { + if (c > 0x20 && c < 0x7f) { + c -= 0x21; + + WriteCommand(0xB0 + m_row);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = 0; i <= 14; i += 2) { + byte d = pgm_read_byte(&font8x16_terminal[c][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + + WriteCommand(0xB0 + m_row + 1);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = 1; i <= 15; i += 2) { + byte d = pgm_read_byte(&font8x16_terminal[c][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + } else { + WriteCommand(0xB0 + m_row);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 16 : 8; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + + WriteCommand(0xB0 + m_row + 1);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 16 : 8; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + } + m_col += (m_flags & FLAG_PIXEL_DOUBLE_H) ? 17 : 9; + if (m_col >= 128) { + m_col = 0; + m_row += 2; + } + } +#endif + TWBR = twbrbackup; + return 1; +} + +void LCD_SH1106::writeDigit(byte n) +{ + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + + if (m_font == FONT_SIZE_SMALL) { + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + if (n <= 9) { + n += '0' - 0x21; + for (byte i = 0; i < 5; i++) { + Wire.write(pgm_read_byte(&font5x8[n][i])); + } + Wire.write(0); + } else { + for (byte i = 0; i < 6; i++) { + Wire.write(0); + } + } + Wire.endTransmission(); + m_col += 6; + } else if (m_font == FONT_SIZE_MEDIUM) { + write(n <= 9 ? ('0' + n) : ' '); +#ifndef MEMORY_SAVING + } else if (m_font == FONT_SIZE_LARGE) { + if (n <= 9) { + byte i; + WriteCommand(0xB0 + m_row);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (i = 0; i < 16; i ++) { + byte d = pgm_read_byte(&digits16x16[n][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + + WriteCommand(0xB0 + m_row + 1);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (; i < 32; i ++) { + byte d = pgm_read_byte(&digits16x16[n][i]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + } else { + WriteCommand(0xB0 + m_row);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + + WriteCommand(0xB0 + m_row + 1);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + } + m_col += (m_flags & FLAG_PIXEL_DOUBLE_H) ? 30 : 16; +#endif + } else { + if (n <= 9) { + byte i; + WriteCommand(0xB0 + m_row);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (i = 0; i < 16; i ++) { + byte d = pgm_read_byte(&digits16x24[n][i * 3]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + + WriteCommand(0xB0 + m_row + 1);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (i = 0; i < 16; i ++) { + byte d = pgm_read_byte(&digits16x24[n][i * 3 + 1]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + + WriteCommand(0xB0 + m_row + 2);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (i = 0; i < 16; i ++) { + byte d = pgm_read_byte(&digits16x24[n][i * 3 + 2]); + Wire.write(d); + if (m_flags & FLAG_PIXEL_DOUBLE_H) Wire.write(d); + } + Wire.endTransmission(); + } else { + WriteCommand(0xB0 + m_row);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + + WriteCommand(0xB0 + m_row + 1);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + + WriteCommand(0xB0 + m_row + 2);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte i = (m_flags & FLAG_PIXEL_DOUBLE_H) ? 32 : 16; i > 0; i--) { + Wire.write(0); + } + Wire.endTransmission(); + } + m_col += (m_flags & FLAG_PIXEL_DOUBLE_H) ? 30 : 16; + } + TWBR = twbrbackup; +} + +void LCD_SH1106::draw(const PROGMEM byte* buffer, byte width, byte height) +{ + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + + WriteCommand(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0 + WriteCommand(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0 + WriteCommand(SSD1306_SETSTARTLINE | 0x0); // line #0 + + const PROGMEM byte *p = buffer; + height >>= 3; + width >>= 3; + for (byte i = 0; i < height; i++) { + // send a bunch of data in one xmission + WriteCommand(0xB0 + i + m_row);//set page address + WriteCommand(m_col & 0xf);//set lower column address + WriteCommand(0x10 | (m_col >> 4));//set higher column address + + for(byte j = 0; j < 8; j++){ + Wire.beginTransmission(I2C_ADDR); + Wire.write(0x40); + for (byte k = 0; k < width; k++, p++) { + Wire.write(pgm_read_byte(p)); + } + Wire.endTransmission(); + } + } + TWBR = twbrbackup; + m_col += width; +} + +void LCD_SH1106::begin() +{ + Wire.begin(); + + WriteCommand(0xAE); /*display off*/ + + WriteCommand(0x02); /*set lower column address*/ + WriteCommand(0x10); /*set higher column address*/ + + WriteCommand(0x40); /*set display start line*/ + + WriteCommand(0xB0); /*set page address*/ + + WriteCommand(0x81); /*contract control*/ + WriteCommand(0x80); /*128*/ + + WriteCommand(0xA1); /*set segment remap*/ + + WriteCommand(0xA6); /*normal / reverse*/ + + WriteCommand(0xA8); /*multiplex ratio*/ + WriteCommand(0x3F); /*duty = 1/32*/ + + WriteCommand(0xad); /*set charge pump enable*/ + WriteCommand(0x8b); /*external VCC */ + + WriteCommand(0x30); /*0X30---0X33 set VPP 9V liangdu!!!!*/ + + WriteCommand(0xC8); /*Com scan direction*/ + + WriteCommand(0xD3); /*set display offset*/ + WriteCommand(0x00); /* 0x20 */ + + WriteCommand(0xD5); /*set osc division*/ + WriteCommand(0x80); + + WriteCommand(0xD9); /*set pre-charge period*/ + WriteCommand(0x1f); /*0x22*/ + + WriteCommand(0xDA); /*set COM pins*/ + WriteCommand(0x12); + + WriteCommand(0xdb); /*set vcomh*/ + WriteCommand(0x40); + + WriteCommand(0xAF); /*display ON*/ + + clear(); +} diff --git a/nanologger/SSD1306.cpp b/nanologger/SSD1306.cpp new file mode 100644 index 0000000..7f7b8e8 --- /dev/null +++ b/nanologger/SSD1306.cpp @@ -0,0 +1,273 @@ +#include <avr/pgmspace.h> +#include <util/delay.h> +#include <stdlib.h> +#include <Wire.h> +#include "SSD1306.h" + +SSD1306::SSD1306(int8_t SCLK, int8_t DC, int8_t RST, int8_t CS) { + cs = CS; + rst = RST; + dc = DC; + sclk = SCLK; +} + +// initializer for I2C - we only indicate the reset pin! + SSD1306::SSD1306(int8_t reset) { + sclk = dc = cs = -1; + rst = reset; +} + + +void SSD1306::begin(uint8_t vccstate, uint8_t i2caddr) { + _i2caddr = i2caddr; + + + // set pin directions + // I2C Init + Wire.begin(); // Is this the right place for this? + + // Setup reset pin direction (used by both SPI and I2C) + pinMode(rst, OUTPUT); + digitalWrite(rst, HIGH); + // VDD (3.3V) goes high at start, lets just chill for a ms + delay(1); + // bring reset low + digitalWrite(rst, LOW); + // wait 10ms + delay(10); + // bring out of reset + digitalWrite(rst, HIGH); + // turn on VCC (9V?) + #if defined SSD1306_128_32 + // Init sequence for 128x32 OLED module + ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE + ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 + ssd1306_command(0x80); // the suggested ratio 0x80 + ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8 + ssd1306_command(0x1F); + ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3 + ssd1306_command(0x0); // no offset + ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 + ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D + if (vccstate == SSD1306_EXTERNALVCC) + { ssd1306_command(0x10); } + else + { ssd1306_command(0x14); } + ssd1306_command(SSD1306_MEMORYMODE); // 0x20 + ssd1306_command(0x00); // 0x0 act like ks0108 + ssd1306_command(SSD1306_SEGREMAP | 0x1); + ssd1306_command(SSD1306_COMSCANDEC); + ssd1306_command(SSD1306_SETCOMPINS); // 0xDA + ssd1306_command(0x02); + ssd1306_command(SSD1306_SETCONTRAST); // 0x81 + ssd1306_command(0x8F); + ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9 + if (vccstate == SSD1306_EXTERNALVCC) + { ssd1306_command(0x22); } + else + { ssd1306_command(0xF1); } + ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB + ssd1306_command(0x40); + ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4 + ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6 + #endif + + #if defined SSD1306_128_64 + // Init sequence for 128x64 OLED module + ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE + ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 + ssd1306_command(0x80); // the suggested ratio 0x80 + ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8 + ssd1306_command(0x3F); + ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3 + ssd1306_command(0x0); // no offset + ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 + ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D + if (vccstate == SSD1306_EXTERNALVCC) + { ssd1306_command(0x10); } + else + { ssd1306_command(0x14); } + ssd1306_command(SSD1306_MEMORYMODE); // 0x20 + ssd1306_command(0x00); // 0x0 act like ks0108 + ssd1306_command(SSD1306_SEGREMAP | 0x1); + ssd1306_command(SSD1306_COMSCANDEC); + ssd1306_command(SSD1306_SETCOMPINS); // 0xDA + ssd1306_command(0x12); + ssd1306_command(SSD1306_SETCONTRAST); // 0x81 + if (vccstate == SSD1306_EXTERNALVCC) + { ssd1306_command(0x9F); } + else + { ssd1306_command(0xCF); } + ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9 + if (vccstate == SSD1306_EXTERNALVCC) + { ssd1306_command(0x22); } + else + { ssd1306_command(0xF1); } + ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB + ssd1306_command(0x40); + ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4 + ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6 + #endif + + ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel + + // clear screen + delay(5); + + ssd1306_command(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0 + ssd1306_command(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0 + ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 + + for (byte i = 0; i < SSD1306_LCDHEIGHT / 8; i++) { + // send a bunch of data in one xmission + ssd1306_command(0xB0 + i);//set page address + ssd1306_command(0);//set lower column address + ssd1306_command(0x10);//set higher column address + + for(byte j = 0; j < 8; j++){ + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte k = 0; k < SSD1306_LCDWIDTH / 8; k++) { + Wire.write(0); + } + Wire.endTransmission(); + } + } +} + + +void SSD1306::invertDisplay(uint8_t i) { + if (i) { + ssd1306_command(SSD1306_INVERTDISPLAY); + } else { + ssd1306_command(SSD1306_NORMALDISPLAY); + } +} + +void SSD1306::ssd1306_command(uint8_t c) { + // I2C + uint8_t control = 0x00; // Co = 0, D/C = 0 + Wire.beginTransmission(_i2caddr); + Wire.write(control); + Wire.write(c); + Wire.endTransmission(); +} + +// startscrollright +// Activate a right handed scroll for rows start through stop +// Hint, the display is 16 rows tall. To scroll the whole display, run: +// display.scrollright(0x00, 0x0F) +void SSD1306::startscrollright(uint8_t start, uint8_t stop){ + ssd1306_command(SSD1306_RIGHT_HORIZONTAL_SCROLL); + ssd1306_command(0X00); + ssd1306_command(start); + ssd1306_command(0X00); + ssd1306_command(stop); + ssd1306_command(0X01); + ssd1306_command(0XFF); + ssd1306_command(SSD1306_ACTIVATE_SCROLL); +} + +// startscrollleft +// Activate a right handed scroll for rows start through stop +// Hint, the display is 16 rows tall. To scroll the whole display, run: +// display.scrollright(0x00, 0x0F) +void SSD1306::startscrollleft(uint8_t start, uint8_t stop){ + ssd1306_command(SSD1306_LEFT_HORIZONTAL_SCROLL); + ssd1306_command(0X00); + ssd1306_command(start); + ssd1306_command(0X00); + ssd1306_command(stop); + ssd1306_command(0X01); + ssd1306_command(0XFF); + ssd1306_command(SSD1306_ACTIVATE_SCROLL); +} + +// startscrolldiagright +// Activate a diagonal scroll for rows start through stop +// Hint, the display is 16 rows tall. To scroll the whole display, run: +// display.scrollright(0x00, 0x0F) +void SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){ + ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); + ssd1306_command(0X00); + ssd1306_command(SSD1306_LCDHEIGHT); + ssd1306_command(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL); + ssd1306_command(0X00); + ssd1306_command(start); + ssd1306_command(0X00); + ssd1306_command(stop); + ssd1306_command(0X01); + ssd1306_command(SSD1306_ACTIVATE_SCROLL); +} + +// startscrolldiagleft +// Activate a diagonal scroll for rows start through stop +// Hint, the display is 16 rows tall. To scroll the whole display, run: +// display.scrollright(0x00, 0x0F) +void SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){ + ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); + ssd1306_command(0X00); + ssd1306_command(SSD1306_LCDHEIGHT); + ssd1306_command(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL); + ssd1306_command(0X00); + ssd1306_command(start); + ssd1306_command(0X00); + ssd1306_command(stop); + ssd1306_command(0X01); + ssd1306_command(SSD1306_ACTIVATE_SCROLL); +} + +void SSD1306::stopscroll(void){ + ssd1306_command(SSD1306_DEACTIVATE_SCROLL); +} + +void SSD1306::ssd1306_data(uint8_t c) { + // I2C + uint8_t control = 0x40; // Co = 0, D/C = 1 + Wire.beginTransmission(_i2caddr); + Wire.write(control); + Wire.write(c); + Wire.endTransmission(); +} + +void SSD1306::fill(unsigned char dat) +{ + unsigned char i,j; + + ssd1306_command(0x00);//set lower column address + ssd1306_command(0x10);//set higher column address + ssd1306_command(0xB0);//set page address + + uint8_t twbrbackup = TWBR; + TWBR = 18; // upgrade to 400KHz! + for (byte i=0; i<(SSD1306_LCDHEIGHT/8); i++) + { + // send a bunch of data in one xmission + ssd1306_command(0xB0 + i);//set page address + ssd1306_command(0);//set lower column address + ssd1306_command(0x10);//set higher column address + + for(byte j = 0; j < 8; j++){ + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for (byte k = 0; k < 16; k++) { + Wire.write(dat); + } + Wire.endTransmission(); + } + } + TWBR = twbrbackup; +} + +void SSD1306::draw8x8(byte* buffer, uint8_t x, uint8_t y) +{ + // send a bunch of data in one xmission + ssd1306_command(0xB0 + y);//set page address + ssd1306_command(x & 0xf);//set lower column address + ssd1306_command(0x10 | (x >> 4));//set higher column address + + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + Wire.write(buffer, 8); + Wire.endTransmission(); +} diff --git a/nanologger/SSD1306.h b/nanologger/SSD1306.h new file mode 100644 index 0000000..aaa3534 --- /dev/null +++ b/nanologger/SSD1306.h @@ -0,0 +1,119 @@ +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#define BLACK 0 +#define WHITE 1 + +#define SSD1306_I2C_ADDRESS 0x3C // 011110+SA0+RW - 0x3C or 0x3D +// Address for 128x32 is 0x3C +// Address for 128x32 is 0x3D (default) or 0x3C (if SA0 is grounded) + +/*========================================================================= + SSD1306 Displays + ----------------------------------------------------------------------- + The driver is used in multiple displays (128x64, 128x32, etc.). + Select the appropriate display below to create an appropriately + sized framebuffer, etc. + + SSD1306_128_64 128x64 pixel display + + SSD1306_128_32 128x32 pixel display + + You also need to set the LCDWIDTH and LCDHEIGHT defines to an + appropriate size + + -----------------------------------------------------------------------*/ + #define SSD1306_128_64 +// #define SSD1306_128_32 +/*=========================================================================*/ + +#if defined SSD1306_128_64 && defined SSD1306_128_32 + #error "Only one SSD1306 display can be specified at once in SSD1306.h" +#endif +#if !defined SSD1306_128_64 && !defined SSD1306_128_32 + #error "At least one SSD1306 display must be specified in SSD1306.h" +#endif + +#if defined SSD1306_128_64 + #define SSD1306_LCDWIDTH 128 + #define SSD1306_LCDHEIGHT 64 +#endif +#if defined SSD1306_128_32 + #define SSD1306_LCDWIDTH 128 + #define SSD1306_LCDHEIGHT 32 +#endif + +#define SSD1306_SETCONTRAST 0x81 +#define SSD1306_DISPLAYALLON_RESUME 0xA4 +#define SSD1306_DISPLAYALLON 0xA5 +#define SSD1306_NORMALDISPLAY 0xA6 +#define SSD1306_INVERTDISPLAY 0xA7 +#define SSD1306_DISPLAYOFF 0xAE +#define SSD1306_DISPLAYON 0xAF + +#define SSD1306_SETDISPLAYOFFSET 0xD3 +#define SSD1306_SETCOMPINS 0xDA + +#define SSD1306_SETVCOMDETECT 0xDB + +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 +#define SSD1306_SETPRECHARGE 0xD9 + +#define SSD1306_SETMULTIPLEX 0xA8 + +#define SSD1306_SETLOWCOLUMN 0x00 +#define SSD1306_SETHIGHCOLUMN 0x10 + +#define SSD1306_SETSTARTLINE 0x40 + +#define SSD1306_MEMORYMODE 0x20 + +#define SSD1306_COMSCANINC 0xC0 +#define SSD1306_COMSCANDEC 0xC8 + +#define SSD1306_SEGREMAP 0xA0 + +#define SSD1306_CHARGEPUMP 0x8D + +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 + +// Scrolling #defines +#define SSD1306_ACTIVATE_SCROLL 0x2F +#define SSD1306_DEACTIVATE_SCROLL 0x2E +#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 +#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 +#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 +#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 +#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A + +class SSD1306 { +public: + SSD1306(int8_t SCLK, int8_t DC, int8_t RST, int8_t CS); + SSD1306(int8_t RST = 4); + + void begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC, uint8_t i2caddr = SSD1306_I2C_ADDRESS); + void ssd1306_command(uint8_t c); + void ssd1306_data(uint8_t c); + + void invertDisplay(uint8_t i); + void draw8x8(byte* buffer, byte x, byte y); + + void startscrollright(uint8_t start, uint8_t stop); + void startscrollleft(uint8_t start, uint8_t stop); + + void startscrolldiagright(uint8_t start, uint8_t stop); + void startscrolldiagleft(uint8_t start, uint8_t stop); + void stopscroll(void); + + void fill(unsigned char dat); + void clearBuffer(); + +protected: + uint8_t _i2caddr; +private: + int8_t sclk, dc, rst, cs; +}; diff --git a/nanologger/config.h b/nanologger/config.h new file mode 100644 index 0000000..e05b85b --- /dev/null +++ b/nanologger/config.h @@ -0,0 +1,50 @@ +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +/************************************** +* Data logging/streaming out +**************************************/ +#define ENABLE_DATA_OUT 0 +#define ENABLE_DATA_LOG 1 +#define USE_SOFTSERIAL 0 +//this defines the format of log file +#define LOG_FORMAT FORMAT_CSV + +/************************************** +* Default working mode +**************************************/ +#define MODE_DEFAULT MODE_LOGGER /* MODE_LOGGER/MODE_TIMER */ +//#define MODE_SWITCH_PIN 8 + +/************************************** +* Choose SD pin here +**************************************/ +//#define SD_CS_PIN SS // generic +//#define SD_CS_PIN 4 // ethernet shield +//#define SD_CS_PIN 7 // microduino +#define SD_CS_PIN 10 // SD breakout + +/************************************** +* Config GPS here +**************************************/ +#define USE_GPS 0 +#define GPS_BAUDRATE 38400 /* bps */ +//#define GPS_OPEN_BAUDRATE 4800 /* bps */ + +/************************************** +* Choose LCD model here +**************************************/ +LCD_SH1106 lcd; +//LCD_SSD1306 lcd; +//LCD_Null lcd; + +/************************************** +* Other options +**************************************/ +#define USE_MPU6050 0 +//#define OBD_MIN_INTERVAL 50 /* ms */ +#define GPS_DATA_TIMEOUT 2000 /* ms */ +//#define DEBUG Serial +#define DEBUG_BAUDRATE 9600 + +#endif // CONFIG_H_INCLUDED diff --git a/nanologger/datalogger.h b/nanologger/datalogger.h new file mode 100644 index 0000000..467131f --- /dev/null +++ b/nanologger/datalogger.h @@ -0,0 +1,340 @@ +typedef enum { + LOG_TYPE_DEFAULT = 0, + LOG_TYPE_0_60, + LOG_TYPE_0_100, + LOG_TYPE_100_200, + LOG_TYPE_400M, + LOG_TYPE_LAPS, + LOG_TYPE_ROUTE, +} LOG_TYPES; + +#define FLAG_CAR 0x1 +#define FLAG_CYCLING 0x2 +#define FLAG_OBD 0x10 +#define FLAG_GPS 0x20 +#define FLAG_ACC 0x40 + +#define FORMAT_BIN 0 +#define FORMAT_CSV 1 + +typedef struct { + uint32_t time; + uint16_t pid; + uint8_t flags; + uint8_t checksum; + float value; +} LOG_DATA; + +typedef struct { + uint32_t time; + uint16_t pid; + uint8_t flags; + uint8_t checksum; + float value[3]; +} LOG_DATA_COMM; + +typedef struct { + uint32_t time; /* e.g. 1307281259 */ + uint16_t pid; + uint8_t message; + uint8_t checksum; + uint16_t fileIndex; + uint16_t fileSize; /* KB */ + uint16_t logFlags; + uint8_t logType; + uint8_t data[5]; +} LOG_DATA_FILE_INFO; + +typedef struct { + uint32_t time; + uint16_t pid; + uint8_t message; + uint8_t checksum; + uint8_t data[12]; +} LOG_DATA_COMMAND; + +typedef struct { + uint32_t id; + uint32_t dataOffset; + uint8_t ver; + uint8_t logType; + uint16_t flags; + uint32_t dateTime; //4, YYMMDDHHMM, e.g. 1305291359 + /* + uint8_t devid[8]; + uint8_t vin[24]; + uint8_t unused[84]; + */ +} HEADER; + +#define HEADER_LEN 128 /* bytes */ + +#define PID_GPS_COORDINATES 0xF00A +#define PID_GPS_ALTITUDE 0xF00C +#define PID_GPS_SPEED 0xF00D +#define PID_GPS_HEADING 0xF00E +#define PID_GPS_SAT_COUNT 0xF00F +#define PID_GPS_TIME 0xF010 + +#define PID_ACC 0xF020 +#define PID_GYRO 0xF021 + +#define PID_MESSAGE 0xFE00 +#define PID_HEART_BEAT 0xFFEE + +#define MSG_FILE_LIST_BEGIN 0x1 +#define MSG_FILE_LIST_END 0x2 +#define MSG_FILE_INFO 0x3 +#define MSG_FILE_REQUEST 0x4 + +#if LOG_FORMAT == FORMAT_BIN +#define FILE_NAME_FORMAT "/DAT%05d.LOG" +#else +#define FILE_NAME_FORMAT "/DAT%05d.CSV" +#endif + +#if ENABLE_DATA_OUT +#if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) + SoftwareSerial mySerial(A8, A9); /* for BLE Shield on MEGA*/ +#elif defined(__AVR_ATmega644P__) + SoftwareSerial mySerial(9, 10); /* for Microduino */ +#else + SoftwareSerial mySerial(A2, A3); /* for BLE Shield on UNO*/ +#endif +#endif + +class CDataLogger { +public: + void initSender() + { +#if ENABLE_DATA_OUT + mySerial.begin(9600); +#endif +#if ENABLE_DATA_LOG && LOG_FORMAT == FORMAT_CSV + m_lastDataTime = 0; +#endif + } +#if ENABLE_DATA_OUT + void sendFileInfo(File& file) + { + if (file.size() < HEADER_LEN) return; + + LOG_DATA_FILE_INFO info = {0}; + info.fileIndex = atol(file.name() + 3); + if (info.fileIndex == 0) return; + + HEADER hdr; + if (file.readBytes((char*)&hdr, sizeof(hdr)) != sizeof(hdr)) return; + + info.pid = PID_MESSAGE; + info.message = MSG_FILE_INFO; + info.fileSize = file.size(); + info.time = hdr.dateTime; + info.logType = hdr.logType; + info.logFlags = hdr.flags; + info.checksum = getChecksum((char*)&info, sizeof(info)); + mySerial.write((uint8_t*)&info, sizeof(info)); + } + void sendCommand(byte message, void* data = 0, byte bytes = 0) + { + LOG_DATA_COMMAND msg = {0, PID_MESSAGE, message}; + if (data) memcpy(msg.data, data, bytes); + msg.checksum = getChecksum((char*)&msg, sizeof(msg)); + mySerial.write((uint8_t*)&msg, sizeof(msg)); + } + bool receiveCommand(LOG_DATA_COMMAND& msg) + { + if (!mySerial.available()) + return false; + + if (mySerial.readBytes((char*)&msg, sizeof(msg)) != sizeof(msg)) + return false; + + uint8_t checksum = msg.checksum; + msg.checksum = 0; + if (getChecksum((char*)&msg, sizeof(msg)) != msg.checksum) { + return false; + } + return true; + } +#endif + void logData(uint16_t pid, int value) + { + LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value}; + ld.checksum = getChecksum((char*)&ld, 12); +#if ENABLE_DATA_OUT + mySerial.write((uint8_t*)&ld, 12); +#endif +#if ENABLE_DATA_LOG +#if LOG_FORMAT == FORMAT_BIN + sdfile.write((uint8_t*)&ld, 12); + dataSize += 12; +#else + dataSize += sdfile.print(dataTime - m_lastDataTime); + dataSize += sdfile.write(','); + dataSize += sdfile.print(pid, HEX); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value); + dataSize += sdfile.write('\n'); + m_lastDataTime = dataTime; +#endif +#endif + } + void logData(uint16_t pid, float value) + { + LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value}; + ld.checksum = getChecksum((char*)&ld, 12); +#if ENABLE_DATA_OUT + mySerial.write((uint8_t*)&ld, 12); +#endif +#if ENABLE_DATA_LOG +#if LOG_FORMAT == FORMAT_BIN + sdfile.write((uint8_t*)&ld, 12); + dataSize += 12; +#else + dataSize += sdfile.print(dataTime - m_lastDataTime); + dataSize += sdfile.write(','); + dataSize += sdfile.print(pid, HEX); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value); + dataSize += sdfile.write('\n'); + m_lastDataTime = dataTime; +#endif +#endif + } + void logData(uint16_t pid, float value1, float value2) + { + LOG_DATA_COMM ld = {dataTime, pid, 2, 0, {value1, value2}}; + ld.checksum = getChecksum((char*)&ld, 16); +#if ENABLE_DATA_OUT + mySerial.write((uint8_t*)&ld, 16); +#endif +#if ENABLE_DATA_LOG +#if LOG_FORMAT == FORMAT_BIN + sdfile.write((uint8_t*)&ld, 16); + dataSize += 16; +#else + dataSize += sdfile.print(dataTime - m_lastDataTime); + dataSize += sdfile.write(','); + dataSize += sdfile.print(pid, HEX); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value1, 6); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value2, 6); + dataSize += sdfile.write('\n'); + m_lastDataTime = dataTime; +#endif +#endif + } + void logData(uint16_t pid, uint32_t value1, uint32_t value2) + { + LOG_DATA_COMM ld = {dataTime, pid, 2, 0, {value1, value2}}; + ld.checksum = getChecksum((char*)&ld, 16); +#if ENABLE_DATA_OUT + mySerial.write((uint8_t*)&ld, 16); +#endif +#if ENABLE_DATA_LOG +#if LOG_FORMAT == FORMAT_BIN + sdfile.write((uint8_t*)&ld, 16); + dataSize += 16; +#else + dataSize += sdfile.print(dataTime - m_lastDataTime); + dataSize += sdfile.write(','); + dataSize += sdfile.print(pid, HEX); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value1); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value2); + dataSize += sdfile.write('\n'); + m_lastDataTime = dataTime; +#endif +#endif + } + void logData(uint16_t pid, int value1, int value2, int value3) + { + LOG_DATA_COMM ld = {dataTime, pid, 3, 0, {value1, value2, value3}}; + ld.checksum = getChecksum((char*)&ld, 20); +#if ENABLE_DATA_OUT + mySerial.write((uint8_t*)&ld, 20); +#endif +#if ENABLE_DATA_LOG +#if LOG_FORMAT == FORMAT_BIN + sdfile.write((uint8_t*)&ld, 20); + dataSize += 20; +#else + dataSize += sdfile.print(dataTime - m_lastDataTime); + dataSize += sdfile.write(','); + dataSize += sdfile.print(pid, HEX); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value1); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value2); + dataSize += sdfile.write(','); + dataSize += sdfile.print(value3); + dataSize += sdfile.write('\n'); + m_lastDataTime = dataTime; +#endif +#endif + } +#if ENABLE_DATA_LOG + uint16_t openFile(LOG_TYPES logType, uint16_t logFlags = 0, uint32_t dateTime = 0) + { + uint16_t fileIndex; + char filename[24] = "/FRMATICS"; + + if (SD.exists(filename)) { + for (fileIndex = 1; fileIndex; fileIndex++) { + sprintf(filename + 9, FILE_NAME_FORMAT, fileIndex); + if (!SD.exists(filename)) { + break; + } + } + if (fileIndex == 0) + return 0; + } else { + SD.mkdir(filename); + fileIndex = 1; + sprintf(filename + 9, FILE_NAME_FORMAT, 1); + } + + sdfile = SD.open(filename, FILE_WRITE); + if (!sdfile) { + return 0; + } + +#if LOG_FORMAT == FORMAT_BIN + HEADER hdr = {'UDUS', HEADER_LEN, 1, logType, logFlags, dateTime}; + sdfile.write((uint8_t*)&hdr, sizeof(hdr)); + for (byte i = 0; i < HEADER_LEN - sizeof(hdr); i++) + sdfile.write((uint8_t)0); + dataSize = HEADER_LEN; +#endif + return fileIndex; + } + void closeFile() + { + sdfile.close(); + } + void flushFile() + { + sdfile.flush(); + } +#endif + uint32_t dataTime; + uint32_t dataSize; +private: + static byte getChecksum(char* buffer, byte len) + { + uint8_t checksum = 0; + for (byte i = 0; i < len; i++) { + checksum ^= buffer[i]; + } + return checksum; + } +#if ENABLE_DATA_LOG + File sdfile; +#if LOG_FORMAT == FORMAT_CSV + uint32_t m_lastDataTime; +#endif +#endif +}; diff --git a/nanologger/images.h b/nanologger/images.h new file mode 100644 index 0000000..0a0f0b6 --- /dev/null +++ b/nanologger/images.h @@ -0,0 +1,5 @@ +const PROGMEM uint8_t tick[16 *16 / 8] = +{0x00,0x80,0xC0,0xE0,0xC0,0x80,0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0x78,0x30,0x00,0x00,0x01,0x03,0x07,0x0F,0x1F,0x1F,0x1F,0x0F,0x07,0x03,0x01,0x00,0x00,0x00,0x00}; + +const PROGMEM uint8_t cross[16 *16 / 8] = +{0x00,0x0C,0x1C,0x3C,0x78,0xF0,0xE0,0xC0,0xE0,0xF0,0x78,0x3C,0x1C,0x0C,0x00,0x00,0x00,0x30,0x38,0x3C,0x1E,0x0F,0x07,0x03,0x07,0x0F,0x1E,0x3C,0x38,0x30,0x00,0x00}; diff --git a/nanologger/nanologger.cbp b/nanologger/nanologger.cbp new file mode 100644 index 0000000..b15120b --- /dev/null +++ b/nanologger/nanologger.cbp @@ -0,0 +1,599 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="6" /> + <Project> + <Option title="NanoLogger" /> + <Option pch_mode="2" /> + <Option compiler="avrgcc" /> + <Build> + <Target title="Simulator"> + <Option output="bin/Release/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="-DF_CPU=16000000L" /> + <Add option="-DARDUSIM" /> + <Add option="-D__AVR_ATmega2560__" /> + <Add option="-x c++" /> + <Add directory="$(ARDUINO_DIR)/arduino/cores" /> + <Add directory="$(ARDUINO_DIR)/arduino/variants/standard" /> + <Add directory="$(ARDUINO_DIR)/include" /> + </Compiler> + <Linker> + <Add option="-lardusim" /> + </Linker> + <Environment> + <Variable name="ARDUINO_DIR" value="$(APP_PATH)\ardusim" /> + </Environment> + </Target> + <Target title="Arduino Uno"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-Os" /> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Uno" /> + <Variable name="BOARD_ID" value="uno" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="115200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Leonardo"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega32U4__" /> + <Add option="-DUSB_VID=0x2341" /> + <Add option="-DUSB_PID=0x8036" /> + <Add option="-Os" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/leonardo" /> + </Compiler> + <Linker> + <Add option="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Leonardo" /> + <Variable name="BOARD_ID" value="leonardo" /> + <Variable name="MCU" value="atmega32u4" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Esplora"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega32U4__" /> + <Add option="-DUSB_VID=0x2341" /> + <Add option="-DUSB_PID=0x8037" /> + <Add option="-Os" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/leonardo" /> + </Compiler> + <Linker> + <Add option="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Esplora" /> + <Variable name="BOARD_ID" value="esplora" /> + <Variable name="MCU" value="atmega32u4" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Micro"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega32U4__" /> + <Add option="-DUSB_VID=0x2341" /> + <Add option="-DUSB_PID=0x803C" /> + <Add option="-Os" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/micro" /> + </Compiler> + <Linker> + <Add option="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Micro" /> + <Variable name="BOARD_ID" value="micro" /> + <Variable name="MCU" value="atmega32u4" /> + <Variable name="MCU_CLOCK" value="16" /> + <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}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Duemilanove (328)" /> + <Variable name="BOARD_ID" value="duemilanove328" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Duemilanove (168)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega168__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Duemilanove (168)" /> + <Variable name="BOARD_ID" value="duemilanove168" /> + <Variable name="MCU" value="atmega168" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="19200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Nano (328)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Nano (328)" /> + <Variable name="BOARD_ID" value="nano328" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Nano (168)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega168__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Nano (168)" /> + <Variable name="BOARD_ID" value="nano168" /> + <Variable name="MCU" value="atmega168" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="19200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Mini (328)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Mini (328)" /> + <Variable name="BOARD_ID" value="mini328" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Mini (168)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega168__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Mini (168)" /> + <Variable name="BOARD_ID" value="mini168" /> + <Variable name="MCU" value="atmega168" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="19200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Pro Mini (328)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Pro Mini (328)" /> + <Variable name="BOARD_ID" value="promini328" /> + <Variable name="MCU" value="atmega328p" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Pro Mini (168)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega168__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Pro Mini (168)" /> + <Variable name="BOARD_ID" value="promini168" /> + <Variable name="MCU" value="atmega168" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="19200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Mega 2560/ADK"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-O3" /> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Mega 2560\ADK" /> + <Variable name="BOARD_ID" value="mega2560" /> + <Variable name="MCU" value="atmega2560" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="115200" /> + <Variable name="UPLOAD_PORT" value="COM37" /> + </Environment> + </Target> + <Target title="Arduino Mega 1280"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega1280__" /> + <Add option="-O2" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Mega 1280" /> + <Variable name="BOARD_ID" value="mega1280" /> + <Variable name="MCU" value="atmega1280" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="57600" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Arduino Mega 8"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega328P__" /> + <Add option="-Os" /> + <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="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Arduino Mega 8" /> + <Variable name="BOARD_ID" value="mega8" /> + <Variable name="MCU" value="atmega8" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="19200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + <Target title="Microduino Core+ (644P)"> + <Option output="bin/Release/obdlogger_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" /> + <Option type="1" /> + <Option compiler="avrgcc" /> + <Compiler> + <Add option="-O3" /> + <Add option="-x c++" /> + <Add option="-mmcu=$(MCU)" /> + <Add option="-DF_CPU=16000000L" /> + <Add option="-D__AVR_ATmega644P__" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" /> + <Add directory="$(ARDUINO_DIR)/libraries" /> + <Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/plus" /> + </Compiler> + <Linker> + <Add option="-mmcu=$(MCU)" /> + <Add option='"$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <Add option='"$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + </Linker> + <ExtraCommands> + <Add before='$(TARGET_COMPILER_DIR)ArduinoUploader "$(PROJECT_DIR)." $(BOARD_ID) - $(MCU_CLOCK) 0 "$(PROJECT_DIR)bin/lib_${BOARD_ID}_${MCU_CLOCK}.a" "$(PROJECT_DIR)bin/core_${BOARD_ID}_${MCU_CLOCK}.a"' /> + <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="BOARD" value="Microduino Core+ (644P)" /> + <Variable name="BOARD_ID" value="uduino644p" /> + <Variable name="MCU" value="atmega644p" /> + <Variable name="MCU_CLOCK" value="16" /> + <Variable name="UPLOAD_BAUDRATE" value="115200" /> + <Variable name="UPLOAD_PORT" value="" /> + </Environment> + </Target> + </Build> + <Compiler> + <Add directory="." /> + </Compiler> + <Unit filename="MicroLCD.cpp" /> + <Unit filename="SH1106.cpp" /> + <Unit filename="config.h" /> + <Unit filename="datalogger.h" /> + <Unit filename="images.h" /> + <Unit filename="nanologger.ino"> + <Option compile="1" /> + <Option link="1" /> + </Unit> + <Extensions> + <code_completion /> + <debugger /> + </Extensions> + </Project> +</CodeBlocks_project_file> diff --git a/nanologger/nanologger.ino b/nanologger/nanologger.ino new file mode 100644 index 0000000..48891b9 --- /dev/null +++ b/nanologger/nanologger.ino @@ -0,0 +1,401 @@ +/************************************************************************* +* Arduino GPS/OBD-II/G-Force Data Logger +* Distributed under GPL v2.0 +* Copyright (c) 2013 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <Wire.h> +#include <OBD.h> +#include <SPI.h> +#include <SD.h> +#include <MPU6050.h> +#include "MicroLCD.h" +#include "images.h" +#include "config.h" +#if USE_SOFTSERIAL +#include <SoftwareSerial.h> +#endif +#include "datalogger.h" + +// logger states +#define STATE_SD_READY 0x1 +#define STATE_OBD_READY 0x2 +#define STATE_GPS_FOUND 0x4 +#define STATE_GPS_READY 0x8 +#define STATE_ACC_READY 0x10 +#define STATE_SLEEPING 0x20 + +static uint32_t lastFileSize = 0; +static int lastSpeed = -1; +static int speed = 0; +static uint32_t distance = 0; +static uint16_t fileIndex = 0; +static uint32_t startTime = 0; + +static byte pidTier1[]= {PID_RPM, PID_SPEED, PID_ENGINE_LOAD, PID_THROTTLE}; +static byte pidTier2[] = {PID_INTAKE_MAP, PID_MAF_FLOW, PID_TIMING_ADVANCE}; +static byte pidTier3[] = {PID_COOLANT_TEMP, PID_INTAKE_TEMP, PID_AMBIENT_TEMP, PID_FUEL_LEVEL}; + +#define TIER_NUM1 sizeof(pidTier1) +#define TIER_NUM2 sizeof(pidTier2) +#define TIER_NUM3 sizeof(pidTier3) + +class COBDLogger : public COBD, public CDataLogger +{ +public: + COBDLogger():state(0) {} + void setup() + { +#if USE_GPS + lastGPSDataTime = 0; +#endif + showStates(); + +#if USE_MPU6050 + Wire.begin(); + if (MPU6050_init() == 0) { + state |= STATE_ACC_READY; + showStates(); + } +#endif + + do { + showStates(); + } while (!init()); + + state |= STATE_OBD_READY; + + showStates(); + + uint16_t flags = FLAG_CAR | FLAG_OBD; + if (state & STATE_GPS_FOUND) flags |= FLAG_GPS; + if (state & STATE_ACC_READY) flags |= FLAG_ACC; +#if ENABLE_DATA_LOG + uint16_t index = openFile(LOG_TYPE_DEFAULT, flags); + lcd.setFont(FONT_SIZE_SMALL); + lcd.setCursor(86, 0); + if (index) { + lcd.write('['); + lcd.setFlags(FLAG_PAD_ZERO); + lcd.printInt(index, 5); + lcd.setFlags(0); + lcd.write(']'); + } else { + lcd.print("NO LOG"); + } + delay(100); +#endif + +#ifndef MEMORY_SAVING + //showECUCap(); + //delay(1000); +#endif + +#if ENABLE_DATA_LOG + // open file for logging + if (!(state & STATE_SD_READY)) { + if (checkSD()) { + state |= STATE_SD_READY; + showStates(); + } + } +#endif + + initScreen(); + } + void loop() + { + static byte index = 0; + static byte index2 = 0; + static byte index3 = 0; + + // poll OBD-II PIDs + logOBDData(pidTier1[index++]); + if (index == TIER_NUM1) { + index = 0; + if (index2 == TIER_NUM2) { + index2 = 0; + logOBDData(pidTier3[index3]); + index3 = (index3 + 1) % TIER_NUM3; + } else { + logOBDData(pidTier2[index2++]); + } + } + + // display distance travelled (GPS) + char buf[10]; + sprintf(buf, "%4ukm", (uint16_t)(distance / 1000)); + lcd.setFont(FONT_SIZE_SMALL); + lcd.setCursor(92, 6); + lcd.print(buf); + +#if USE_MPU6050 + if (state & STATE_ACC_READY) { + processAccelerometer(); + } +#endif + +#if ENABLE_DATA_LOG + // flush SD data every 1KB + if (dataSize - lastFileSize >= 1024) { + flushFile(); + lastFileSize = dataSize; + // display logged data size + char buf[7]; + sprintf(buf, "%4uKB", (int)(dataSize >> 10)); + lcd.setFont(FONT_SIZE_SMALL); + lcd.setCursor(92, 7); + lcd.print(buf); + } +#endif + + if (errors >= 2) { + reconnect(); + } + } +#if ENABLE_DATA_LOG + bool checkSD() + { + Sd2Card card; + SdVolume volume; + state &= ~STATE_SD_READY; + pinMode(SS, OUTPUT); + if (card.init(SPI_FULL_SPEED, SD_CS_PIN)) { + const char* type; + + lcd.print("SD"); + switch(card.type()) { + case SD_CARD_TYPE_SD1: + type = "1"; + break; + case SD_CARD_TYPE_SD2: + type = "2"; + break; + case SD_CARD_TYPE_SDHC: + type = "HC"; + break; + default: + type = "x"; + } + + lcd.print(type); + lcd.write(' '); + if (!volume.init(card)) { + lcd.print("No FAT"); + return false; + } + + uint32_t volumesize = volume.blocksPerCluster(); + volumesize >>= 1; // 512 bytes per block + volumesize *= volume.clusterCount(); + volumesize >>= 10; + + lcd.print((int)((volumesize + 511) / 1000)); + lcd.print("GB"); + } else { + lcd.print("SD "); + lcd.draw(cross, 16, 16); + return false; + } + + if (!SD.begin(SD_CS_PIN)) { + lcd.print("Bad"); + return false; + } + + state |= STATE_SD_READY; + return true; + } +#endif + void initScreen() + { + initLoggerScreen(); + } +private: +#if USE_MPU6050 + void processAccelerometer() + { + accel_t_gyro_union data; + MPU6050_readout(&data); + dataTime = millis(); + // log x/y/z of accelerometer + logData(PID_ACC, data.value.x_accel, data.value.y_accel, data.value.z_accel); + //showGForce(data.value.y_accel); + // log x/y/z of gyro meter + logData(PID_GYRO, data.value.x_gyro, data.value.y_gyro, data.value.z_gyro); + } +#endif + void logOBDData(byte pid) + { + int value; + // send a query to OBD adapter for specified OBD-II pid + if (read(pid, value)) { + dataTime = millis(); + showLoggerData(pid, value); + // log data to SD card + logData(0x100 | pid, value); + } + + // if OBD response is very fast, go on processing other data for a while + } +#ifndef MEMORY_SAVING + void showECUCap() + { + char buffer[24]; + byte pidlist[] = {PID_RPM, PID_SPEED, PID_THROTTLE, PID_ENGINE_LOAD, PID_CONTROL_MODULE_VOLTAGE, PID_MAF_FLOW, PID_INTAKE_MAP, PID_FUEL_LEVEL, PID_FUEL_PRESSURE, PID_COOLANT_TEMP, PID_INTAKE_TEMP, PID_AMBIENT_TEMP, PID_TIMING_ADVANCE, PID_BAROMETRIC}; + const char* namelist[] = {"RPM", "SPEED", "THROTTLE", "ENG.LOAD", "CTRL VOLT", "MAF", "MAP", "FUEL LV.", "FUEL PRE.", "COOLANT", "INTAKE","AMBIENT", "IGNITION", "BARO"}; + byte i = 0; + lcd.clear(); + lcd.setFont(FONT_SIZE_SMALL); + for (; i < sizeof(pidlist) / sizeof(pidlist[0]) / 2; i++) { + lcd.setCursor(0, i); + sprintf(buffer, "%s:%c", namelist[i], isValidPID(pidlist[i]) ? 'Y' : 'N'); + lcd.print(buffer); + } + for (byte row = 0; i < sizeof(pidlist) / sizeof(pidlist[0]); i++, row++) { + lcd.setCursor(64, row); + sprintf(buffer, "%s:%c", namelist[i], isValidPID(pidlist[i]) ? 'Y' : 'N'); + lcd.print(buffer); + } + } +#endif + void reconnect() + { +#if ENABLE_DATA_LOG + closeFile(); +#endif + lcd.clear(); + lcd.setFont(FONT_SIZE_MEDIUM); + lcd.print("Reconnecting"); + startTime = millis(); + state &= ~(STATE_OBD_READY | STATE_ACC_READY); + state |= STATE_SLEEPING; + //digitalWrite(SD_CS_PIN, LOW); + for (int i = 0; !init(); i++) { + if (i == 10) lcd.clear(); + } + state &= ~STATE_SLEEPING; + fileIndex++; + write('\r'); + setup(); + } + byte state; + + void showTickCross(bool yes) + { + lcd.draw(yes ? tick : cross, 16, 16); + } + // screen layout related stuff + void showStates() + { + lcd.setFont(FONT_SIZE_MEDIUM); + lcd.setCursor(0, 4); + lcd.print("OBD "); + showTickCross(state & STATE_OBD_READY); + lcd.setCursor(0, 6); + lcd.print("ACC "); + showTickCross(state & STATE_ACC_READY); + } + void showLoggerData(byte pid, int value) + { + char buf[8]; + switch (pid) { + case PID_RPM: + lcd.setCursor(64, 0); + lcd.setFont(FONT_SIZE_XLARGE); + lcd.printInt((unsigned int)value % 10000, 4); + break; + case PID_SPEED: + if (lastSpeed != value) { + lcd.setCursor(0, 0); + lcd.setFont(FONT_SIZE_XLARGE); + lcd.printInt((unsigned int)value % 1000, 3); + lastSpeed = value; + } + break; + case PID_THROTTLE: + lcd.setCursor(24, 5); + lcd.setFont(FONT_SIZE_SMALL); + lcd.printInt(value % 100, 3); + break; + case PID_INTAKE_TEMP: + if (value < 1000) { + lcd.setCursor(102, 5); + lcd.setFont(FONT_SIZE_SMALL); + lcd.printInt(value, 3); + } + break; + } + } +#if USE_MPU6050 + void showGForce(int g) + { + byte n; + /* 0~1.5g -> 0~8 */ + g /= 85 * 25; + lcd.setFont(FONT_SIZE_SMALL); + lcd.setCursor(0, 3); + if (g == 0) { + lcd.clearLine(1); + } else if (g < 0 && g >= -10) { + for (n = 0; n < 10 + g; n++) { + lcd.write(' '); + } + for (; n < 10; n++) { + lcd.write('<'); + } + lcd.print(" "); + } else if (g > 0 && g < 10) { + lcd.print(" "); + for (n = 0; n < g; n++) { + lcd.write('>'); + } + for (; n < 10; n++) { + lcd.write(' '); + } + } + } +#endif + void initLoggerScreen() + { + lcd.clear(); + lcd.backlight(true); + lcd.setFont(FONT_SIZE_SMALL); + lcd.setCursor(24, 3); + lcd.print("km/h"); + lcd.setCursor(110, 3); + lcd.print("rpm"); + lcd.setCursor(0, 5); + lcd.print("THR: %"); + lcd.setCursor(80, 5); + lcd.print("AIR: C"); + } +}; + +static COBDLogger logger; + +void setup() +{ + lcd.begin(); + lcd.setFont(FONT_SIZE_MEDIUM); + lcd.println("NanoLogger"); + + logger.begin(); + logger.initSender(); + +#if ENABLE_DATA_LOG + lcd.setFont(FONT_SIZE_MEDIUM); + lcd.setCursor(0, 2); + logger.checkSD(); +#else + lcd.clear(); +#endif + logger.setup(); +} + +void loop() +{ + logger.loop(); +} |