/*J import javax.swing.*; import java.applet.Applet; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; public class BikeComp extends JApplet implements MouseListener, Runnable { private static final Color PIXEL_BRIGHT = new Color(0xE1, 0xE2, 0xA0); private static final Color PIXEL_DARK = new Color(0x14, 0x15, 0x00); private static final byte[] frameBuffer = new byte[84 * 48 / 8]; private static final int frameBufferLen = frameBuffer.length; private static final int LCD_X = 84, LCD_Y = 48; // private static final double PERSPECTIVE_CORRECTION = 1.28333; // Spacer does not work! private static final double PERSPECTIVE_CORRECTION = 1.0; private static final char CONFIG_VERSION[] = "bc5\0".toCharArray(); private static final int PIN_ANALOG_FUELTANK = 0; // Analog input! private static final int PIN_LED = 13; // LED... private static final boolean HIGH = true; private static final boolean LOW = false; static final int CONFIG_START = 450; // The first seven bytes are used for the clock! private static final byte NVB_STOREMARKER = 8; // There is valid data to retrieve, 1 byte private static final byte NVB_ANALOG_FUEL_SENSOR = 9; // State of fuel tank, 1 byte private static final byte NVL_DISTANCE_SINCE_REFUELING_STOP = 10; // Distance in ticks since last refueling stop, 4 bytes private static final byte NVL_DISTANCE_TODAY = 14; // Distance in ticks today, 4 bytes private static final byte NVL_DISTANCE_TRIP = 18; // Distance in ticks on the actual trip, 4 bytes private static final byte NVB_LAST_SCAN_DAY = 22; // The day this Arduino was last switched on, 1 byte private static final byte NVB_LAST_SCAN_MONTH = 23; // The month this Arduino was last switched on, 1 byte private static final byte NVB_LAST_SCAN_YEAR = 24; // The year this Arduino was last switched on, 1 byte private static final byte NVL_FUEL_SINCE_REFUELING_STOP = 25; // Ticks since last refueling stop, 4 bytes private static final byte NVI_CLOCK_LAST_SET_DATE = 29; // Date where the clock has last been set, 2 bytes private static final byte NVL_CLOCK_LAST_SET_TIME = 31; // Time where the clock has last been set, 4 bytes private static final byte NVL_DISTANCE_TOTAL = 35; // Distance in ticks total, 4 bytes private static final byte SLOT_EMPTY = 0; private static final byte SLOT_SPEED = 1; private static final byte SLOT_TIME = 2; private static final byte SLOT_RPM = 3; private static final byte SLOT_TANKCONTENT = 4; private static final byte SLOT_CONSUMED = 5; private static final byte SLOT_CONSUMPTION = 6; private static final byte SLOT_CONSUMPTION_LONG = 7; private static final byte SLOT_DIST_LEFT = 8; private static final byte SLOT_DIST_LEFT_LONG = 9; private static final byte SLOT_DIST_TOUR = 10; private static final byte SLOT_DIST_TODAY = 11; private static final byte SLOT_DIST_REFUELINGSTOP = 12; private static final byte SLOT_WHEEL_TOTAL = 13; private static final String MODE_BASE = "base"; private static final String MODE_INPUTGEN = "inputGen"; private boolean setupDone; private long initialTime; private Graphics g; private Graphics bufferGraphics; private Image bufferImage; private int gearDivider = 35; private Method fuelGaugeSetValueMethod; private Applet fuelGaugeApplet; private StoreStruct storage = new StoreStruct(); class StoreStruct { char version[] = CONFIG_VERSION; int capacityOfFuelTank_MulTen = 220; int circumferenceOfWheel = 1884; int clockOffset_MulThousand = 0; byte xGraphDivider = 3; byte refuellingStopRecognitionAmount = 50; byte slot[] = {SLOT_TANKCONTENT, SLOT_CONSUMED, SLOT_DIST_LEFT_LONG, SLOT_DIST_REFUELINGSTOP, SLOT_DIST_TODAY, SLOT_DIST_TOUR}; boolean extendedInfos = true; boolean mirror = false; boolean aboutOnStart = true; boolean summertimeSwitch = true; long ticksPerFuelUnit = 10000; float gearTransmissionRatio[] = {(float) 109.7, (float) 78.4, (float) 60.9, (float) 52.3, (float) 45.7, (float) 42.2}; byte physicalFuelGaugeE = 28; byte physicalFuelGaugeF = 72; public String toString() { final StringBuffer retVal = new StringBuffer(); retVal.append(" char version[] = ").append(showContentOfArray(version)).append('\n'); retVal.append(" int capacityOfFuelTank_MulTen = ").append(capacityOfFuelTank_MulTen).append('\n'); retVal.append(" int circumferenceOfWheel = ").append(circumferenceOfWheel).append('\n'); retVal.append(" int clockOffset_MulThousand = ").append(clockOffset_MulThousand).append('\n'); retVal.append(" byte xGraphDivider = ").append(xGraphDivider).append('\n'); retVal.append(" byte refuellingStopRecognitionAmount = ").append(refuellingStopRecognitionAmount).append('\n'); retVal.append(" byte slot[] = ").append(showContentOfArray(slot)).append('\n'); retVal.append(" boolean extendedInfos = ").append(extendedInfos).append('\n'); retVal.append(" boolean mirror = ").append(mirror).append('\n'); retVal.append(" boolean aboutOnStart = ").append(aboutOnStart).append('\n'); retVal.append(" boolean summertimeSwitch = ").append(summertimeSwitch).append('\n'); retVal.append(" long ticksPerFuelUnit = ").append(ticksPerFuelUnit).append('\n'); retVal.append(" float gearTransmissionRatio[] = ").append(showContentOfArray(gearTransmissionRatio)).append('\n'); retVal.append(" byte physicalFuelGaugeE = ").append(physicalFuelGaugeE).append('\n'); retVal.append(" byte physicalFuelGaugeF = ").append(physicalFuelGaugeF).append('\n'); return retVal.toString(); } } public String showContentOfArray(final byte[] data) { final StringBuffer buffer = new StringBuffer("{"); for (final byte o : data) { if (buffer.length() > 1) buffer.append(", "); buffer.append(o); } buffer.append("}"); return buffer.toString(); } public String showContentOfArray(final char[] data) { return new String(data).substring(0, strlen(data)); } public String showContentOfArray(final float[] data) { final StringBuffer buffer = new StringBuffer("{"); for (final float o : data) { if (buffer.length() > 1) buffer.append(", "); buffer.append(o); } buffer.append("}"); return buffer.toString(); } public void init() { addMouseListener(this); new Thread(this, MODE_BASE).start(); fetchFuelGaugeApplet(); } private void fetchFuelGaugeApplet() { fuelGaugeApplet = null; // Reset them variables... fuelGaugeSetValueMethod = null; // Reset them variables... final Enumeration applets = getAppletContext().getApplets(); while (applets.hasMoreElements()) { final Applet whateverApplet = applets.nextElement(); if ("FuelGauge".equals(whateverApplet.getClass().getName())) // Take only the last one; maybe there's more than one active, from a page-reload or so... fuelGaugeApplet = whateverApplet; } if (fuelGaugeApplet != null) { try { fuelGaugeSetValueMethod = fuelGaugeApplet.getClass().getMethod("setValue", Integer.TYPE); } catch (NoSuchMethodException e) { e.printStackTrace(); } if (fuelGaugeSetValueMethod == null) { System.err.println("Method 'setValue' not found!"); } } } private void prepareInputsAndInterrupts() { new Thread(this, MODE_INPUTGEN).start(); } private void lcdInitialise() { repaint(); // For to see the showState()-bar... } public void run() { if (MODE_BASE.equals(Thread.currentThread().getName())) { while (g == null) { // Wait 'till applet shakes a leg delay(30); } setup(); // ### Arduino setup()-function setupDone = true; } else if (MODE_INPUTGEN.equals(Thread.currentThread().getName())) { long counter = 0; //noinspection InfiniteLoopStatement while (true) { if (counter % 3 == 0) irqMotorTicks(); // Motor ticks (~66,7/s at 100km/h) if (counter % 14 == 0) irqFuelTicks(); // Fuel (~15,3/s at 100km/h or ~3,8/s at 100km/h) int theoreticalGear = (int) (3.5 - Math.cos(millis() / 8000.0) * 3); pinStateOfNeutral = theoreticalGear == 0; // N if (theoreticalGear == 1) gearDivider = 35; // 1 if (theoreticalGear == 2) gearDivider = 25; // 2 if (theoreticalGear == 3) gearDivider = 20; // 3 if (theoreticalGear == 4) gearDivider = 17; // 4 if (theoreticalGear == 5) gearDivider = 15; // 5 if (theoreticalGear == 6) gearDivider = 14; // 6 if (counter % gearDivider == 0) irqWheelTicks(); // Wheel ticks (~14,7/s at 100km/h in 6th gear) counter++; try { Thread.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); } repaint(); } } } private void drawFrameBuffer() { int multiplier = Math.min(getWidth() / LCD_X, (int) (getHeight() / (LCD_Y * PERSPECTIVE_CORRECTION))); int spacer = 0; if (multiplier > 4) { spacer = 1; --multiplier; } for (int y = 0; y < LCD_Y * PERSPECTIVE_CORRECTION; ++y) for (int x = 0; x < LCD_X; ++x) { g.setColor(((frameBuffer[x + (int) (y / PERSPECTIVE_CORRECTION) / 8 * LCD_X] & 1 << (int) (y / PERSPECTIVE_CORRECTION) % 8) != 0) ? PIXEL_DARK : PIXEL_BRIGHT); g.fillRect(x * (multiplier + spacer), y * (multiplier + spacer), multiplier, multiplier); } } public void paint(final Graphics g) { this.g = g; if (setupDone) { loop(); // ### Arduino loop()-function } } public void update(final Graphics g) { if (bufferImage == null || bufferImage.getWidth(this) != getWidth() || bufferImage.getHeight(this) != getHeight()) { bufferImage = createImage(getWidth(), getHeight()); bufferGraphics = bufferImage.getGraphics(); } paint(bufferGraphics); g.drawImage(bufferImage, 0, 0, this); } public void mouseClicked(MouseEvent e) { } public void mousePressed(MouseEvent e) { pinStateOfButton = false; } public void mouseReleased(MouseEvent e) { pinStateOfButton = true; } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } private long millis() { if (initialTime == 0) initialTime = new Date().getTime(); return new Date().getTime() - initialTime; } @SuppressWarnings({"UnusedDeclaration"}) private double pow(int base, int exponent) { throw new RuntimeException("Arduino's pow() is buggy, don't use it!!"); } private int strlen(char[] value) { if (new String(value).indexOf('\0') != -1) return new String(value).indexOf('\0'); else return value.length; } private void delay(final long delayInMillis) { try { Thread.sleep(delayInMillis); } catch (InterruptedException ignored) { } } int lastAnalogTankValue = 1024; private int analogRead(int pin) { int value = (int) (1024 - millis() / (300 / (pin + 1)) % 1024); if (lastAnalogTankValue < value) { fuelTicksSinceRefueling = 0; wheelTicksSinceRefueling = 0; } lastAnalogTankValue = value; return value; } private char readCharFromEeprom(final char[] array, final int pos) { return array[pos]; } private void readToPrintBufferFromEeprom(String data) { int t = 0; while (t < data.length()) printBuffer[t] = data.charAt(t++); printBuffer[t] = '\0'; // End of String } private void readToPrintBufferFromEepromArray(String[] array, int pos) { int t = 0; while (t < array[pos].length()) printBuffer[t] = array[pos].charAt(t++); printBuffer[t] = '\0'; // End of String } private int readIntFromEeprom(final int[] array, final int pos) { return array[pos]; } byte[] parseByteArray(final String text) { byte retVal[] = new byte[0]; if (text.startsWith("[") && text.endsWith("]")) { final String[] separate = text.substring(1, text.length() - 1).split(", "); retVal = new byte[separate.length]; for (int t = 0; t < separate.length; t++) retVal[t] = Byte.parseByte(separate[t]); } return retVal; } float[] parseFloatArray(final String text) { float retVal[] = new float[0]; if (text.startsWith("[") && text.endsWith("]")) { final String[] separate = text.substring(1, text.length() - 1).split(", "); retVal = new float[separate.length]; for (int t = 0; t < separate.length; t++) retVal[t] = Float.parseFloat(separate[t]); } return retVal; } double max(final double a, final double b) { return a > b ? a : b; } private void digitalWrite(int pin, boolean value) { System.out.println(pin == PIN_LED ? (value ? "LED: on" : "LED: off") : "Unknown pin: " + pin); } J*/ // ####################################################################### //*C // --- Input --- #define PIN_ANALOG_FUELTANK 0 // Analog; no need to switch that to 'input'-mode! // #define PIN_IRQ_MOTORTICKS 2 #define PIN_IRQ_WHEELTICKS 3 #define PIN_IRQ_FUELTICKS 4 #define PIN_IRQ_NEUTRAL 5 #define PIN_OUT_FUELCLOCK 6 #define PIN_IRQ_BUTTON 7 // --- // --- Pinbelegung, Displayrueckseite, Leiste oben, von links // VDD #define PIN_SCLK 8 #define PIN_SDIN 9 #define PIN_DC 10 #define PIN_SCE 11 // GND // VOUT #define PIN_RESET 12 // --- // --- Output --- #define PIN_LED 13 // --- // --- Physical --- #define LCD_C LOW #define LCD_D HIGH #define LCD_X 84 #define LCD_Y 48 #define LCD_CMD 0 // --- // --- EEPROM --- #include #define CONFIG_VERSION "bc5" #define CONFIG_START 450 // --- // --- PROGMEM --- #include // --- // --- DS1307 --- #include #define DS1307 0x68 // The first seven bytes are used for the clock! #define NVB_STOREMARKER 8 // There is valid data to retrieve, 1 byte #define NVB_ANALOG_FUEL_SENSOR 9 // State of fuel tank, 1 byte #define NVL_DISTANCE_SINCE_REFUELING_STOP 10 // Distance in ticks since last refueling stop, 4 bytes #define NVL_DISTANCE_TODAY 14 // Distance in ticks today, 4 bytes #define NVL_DISTANCE_TRIP 18 // Distance in ticks on the actual trip, 4 bytes #define NVB_LAST_SCAN_DAY 22 // The day this Arduino was last switched on, 1 byte #define NVB_LAST_SCAN_MONTH 23 // The month this Arduino was last switched on, 1 byte #define NVB_LAST_SCAN_YEAR 24 // The year this Arduino was last switched on, 1 byte #define NVL_FUEL_SINCE_REFUELING_STOP 25 // Ticks since last refueling stop, 4 bytes #define NVI_CLOCK_LAST_SET_DATE 29 // Date where the clock has last been set, 2 bytes #define NVL_CLOCK_LAST_SET_TIME 31 // Date where the clock has last been set, 4 bytes #define NVL_DISTANCE_TOTAL 35 // Distance in ticks total, 4 bytes #define SLOT_EMPTY 0 #define SLOT_SPEED 1 #define SLOT_TIME 2 #define SLOT_RPM 3 #define SLOT_TANKCONTENT 4 #define SLOT_CONSUMED 5 #define SLOT_CONSUMPTION 6 #define SLOT_CONSUMPTION_LONG 7 #define SLOT_DIST_LEFT 8 #define SLOT_DIST_LEFT_LONG 9 #define SLOT_DIST_TOUR 10 #define SLOT_DIST_TODAY 11 #define SLOT_DIST_REFUELINGSTOP 12 #define SLOT_WHEEL_TOTAL 13 #define INVERT_ALL 254 #define INVERT_NONE 255 #define ALIGN_LEFT 0 #define ALIGN_CENTER 1 #define ALIGN_RIGHT 2 #define PAGE_MAIN 0 #define PAGE_CLOCK 1 #define PAGE_CONFIG_MAIN 2 #define PAGE_CONFIG_SLOTS 3 #define PAGE_CONFIG_PORTS 4 #define PAGE_CONFIG_FUEL_TANK 5 #define PAGE_CONFIG_CLOCK 6 #define PAGE_CONFIG_CLOCK_OFFSET 7 #define PAGE_CONFIG_EXTENDED 8 #define PAGE_CONFIG_ODOMETER 9 #define PAGE_SLOTS 10 #define PAGE_TRANSMISSION 11 #define PAGE_TRANSMISSION_CONFIG 12 #define PAGE_INFO_TICKS_OVERVIEW 13 #define PAGE_INFO_TICKS_HISTORY 14 #define PAGE_ABOUT 15 #define QUEUE_SIZE_MOTOR 64 // Size of tickqueue, needs to be 2^x!! #define QUEUE_SIZE_WHEEL 32 // Size of tickqueue, needs to be 2^x!! #define QUEUE_SIZE_FUEL 32 // Size of tickqueue, needs to be 2^x!! #define PRINTBUFFER_SIZE 22 // As much as needed at max #define HISTORY_WIDTH 50 // Width of history graph #define HISTORY_HEIGHT 32 // Width of history graph, make it something that is dividable by 8 //C*/ /*J final int DS1307 = 0x68; final byte INVERT_ALL = (byte) 254; final byte INVERT_NONE = (byte) 255; final int ALIGN_LEFT = 0; final int ALIGN_CENTER = 1; final int ALIGN_RIGHT = 2; final int PAGE_MAIN = 0; final int PAGE_CLOCK = 1; final int PAGE_CONFIG_MAIN = 2; final int PAGE_CONFIG_SLOTS = 3; final int PAGE_CONFIG_PORTS = 4; final int PAGE_CONFIG_FUEL_TANK = 5; final int PAGE_CONFIG_CLOCK = 6; final int PAGE_CONFIG_CLOCK_OFFSET = 7; final int PAGE_CONFIG_EXTENDED = 8; final int PAGE_CONFIG_ODOMETER = 9; final int PAGE_SLOTS = 10; final int PAGE_TRANSMISSION = 11; final int PAGE_TRANSMISSION_CONFIG = 12; final int PAGE_INFO_TICKS_OVERVIEW = 13; final int PAGE_INFO_TICKS_HISTORY = 14; final int PAGE_ABOUT = 15; private static final int QUEUE_SIZE_MOTOR = 64; // Size of tickqueue, needs to be 2^x!! private static final int QUEUE_SIZE_WHEEL = 32; // Size of tickqueue, needs to be 2^x!! private static final int QUEUE_SIZE_FUEL = 32; // Size of tickqueue, needs to be 2^x!! private static final int PRINTBUFFER_SIZE = 22; // As much as needed at max private static final int HISTORY_WIDTH = 50; // Width of history graph private static final int HISTORY_HEIGHT = 32; // Width of history graph, make it something that is dividable by 8 J*/ // ####################################################################### // --- PROGMEM-stuff --- /*C*/ PROGMEM prog_char WEEKDAY_MO[] //*J*/ String WEEKDAY_MO = "montag"; /*C*/ PROGMEM prog_char WEEKDAY_TU[] //*J*/ String WEEKDAY_TU = "dienstag"; /*C*/ PROGMEM prog_char WEEKDAY_WE[] //*J*/ String WEEKDAY_WE = "mittwoch"; /*C*/ PROGMEM prog_char WEEKDAY_TH[] //*J*/ String WEEKDAY_TH = "donnerstag"; /*C*/ PROGMEM prog_char WEEKDAY_FR[] //*J*/ String WEEKDAY_FR = "freitag"; /*C*/ PROGMEM prog_char WEEKDAY_SA[] //*J*/ String WEEKDAY_SA = "samstag"; /*C*/ PROGMEM prog_char WEEKDAY_SU[] //*J*/ String WEEKDAY_SU = "sonntag"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_EMPTY[] //*J*/ String SLOTOPTION_LABEL_EMPTY = "leer"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_SPEED[] //*J*/ String SLOTOPTION_LABEL_SPEED = "geschwindgk."; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_TIME[] //*J*/ String SLOTOPTION_LABEL_TIME = "uhrzeit"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_RPM[] //*J*/ String SLOTOPTION_LABEL_RPM = "drehzahl"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_TANKCONTENT[] //*J*/ String SLOTOPTION_LABEL_TANKCONTENT = "tankinhalt"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_CONSUMED[] //*J*/ String SLOTOPTION_LABEL_CONSUMED = "verbraucht"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_CONSUMPTION[] //*J*/ String SLOTOPTION_LABEL_CONSUMPTION = "verbrauch"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_CONSUMPTION_LONG[] //*J*/ String SLOTOPTION_LABEL_CONSUMPTION_LONG = "verbr. lang."; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_DIST_LEFT[] //*J*/ String SLOTOPTION_LABEL_DIST_LEFT = "restdistanz"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_DIST_LEFT_LONG[] //*J*/ String SLOTOPTION_LABEL_DIST_LEFT_LONG = "restdist. lang."; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_WHEEL_TOTAL[] //*J*/ String SLOTOPTION_LABEL_WHEEL_TOTAL = "reifen dist."; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_DIST_TOUR[] //*J*/ String SLOTOPTION_LABEL_DIST_TOUR = "distanz tour"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_DIST_TODAY[] //*J*/ String SLOTOPTION_LABEL_DIST_TODAY = "distanz heute"; /*C*/ PROGMEM prog_char SLOTOPTION_LABEL_DIST_REFUELINGSTOP[] //*J*/ String SLOTOPTION_LABEL_DIST_REFUELINGSTOP = "dist. tankst."; /*C*/ #define CONFIGPAGE_TOTAL 14 //*J*/ final int CONFIGPAGE_TOTAL = 14; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_SLOTS[] //*J*/ String CONFIGPAGE_TITLE_SLOTS = "alle slots anzeigen"; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_TRANSMISSION[] //*J*/ String CONFIGPAGE_TITLE_TRANSMISSION = "untersetzung anz."; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_INFO_TICKS_OVERVIEW[] //*J*/ String CONFIGPAGE_TITLE_INFO_TICKS_OVERVIEW = "eingaenge zaehler"; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_INFO_TICKS_HISTORY[] //*J*/ String CONFIGPAGE_TITLE_INFO_TICKS_HISTORY = "eingaenge graphen"; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_CONFIG_SLOTS[] //*J*/ String CONFIGPAGE_TITLE_CONFIG_SLOTS = "slots einstellen"; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_TRANSMISSION_CONFIG[] //*J*/ String CONFIGPAGE_TITLE_TRANSMISSION_CONFIG = "untersetzung einst."; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_CONFIG_PORTS[] //*J*/ String CONFIGPAGE_TITLE_CONFIG_PORTS = "eingaenge einst."; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_CONFIG_FUEL_TANK[] //*J*/ String CONFIGPAGE_TITLE_CONFIG_FUEL_TANK = "benzintank einst."; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_CONFIG_EXTENDED[] //*J*/ String CONFIGPAGE_TITLE_CONFIG_EXTENDED = "erweitertes einst."; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_CONFIG_ODOMETER[] //*J*/ String CONFIGPAGE_TITLE_CONFIG_ODOMETER = "kilometerzaehler"; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_CONFIG_CLOCK[] //*J*/ String CONFIGPAGE_TITLE_CONFIG_CLOCK = "uhr einstellen"; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_CONFIG_CLOCK_OFFSET[] //*J*/ String CONFIGPAGE_TITLE_CONFIG_CLOCK_OFFSET = "uhr offset einst."; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_ABOUT[] //*J*/ String CONFIGPAGE_TITLE_ABOUT = "ueber"; /*C*/ PROGMEM prog_char CONFIGPAGE_TITLE_MAIN[] //*J*/ String CONFIGPAGE_TITLE_MAIN = "zurueck"; /*C*/ PROGMEM prog_char CONST_YES[] //*J*/ String CONST_YES = "ja"; /*C*/ PROGMEM prog_char CONST_NO[] //*J*/ String CONST_NO = "nein"; /*C*/ PROGMEM prog_char CONST_RESET[] //*J*/ String CONST_RESET = "reset"; /*C*/ PROGMEM prog_char CONST_SETTINGS[] //*J*/ String CONST_SETTINGS = "einstellungen"; /*C*/ PROGMEM prog_char CONST_ERROR[] //*J*/ String CONST_ERROR = "error"; /*C*/ PROGMEM prog_char CONST_TANK[] //*J*/ String CONST_TANK = "tank"; /*C*/ PROGMEM prog_char CONST_FUEL[] //*J*/ String CONST_FUEL = "benzin"; /*C*/ PROGMEM prog_char CONST_MOTOR[] //*J*/ String CONST_MOTOR = "motor"; /*C*/ PROGMEM prog_char CONST_WHEEL[] //*J*/ String CONST_WHEEL = "rad"; /*C*/ PROGMEM prog_char CONST_ACTIVE[] //*J*/ String CONST_ACTIVE = "aktiv"; /*C*/ PROGMEM prog_char CONST_GEAR[] //*J*/ String CONST_GEAR = "gang"; /*C*/ PROGMEM prog_char CONST_TIME[] //*J*/ String CONST_TIME = "uhrzeit"; /*C*/ PROGMEM prog_char CONST_DATE[] //*J*/ String CONST_DATE = "datum"; /*C*/ PROGMEM prog_char CONST_OFFSET_PER_DAY[] //*J*/ String CONST_OFFSET_PER_DAY = "offset/tag"; /*C*/ PROGMEM prog_char CONST_HEADFIRST[] //*J*/ String CONST_HEADFIRST = "kopfueber"; /*C*/ PROGMEM prog_char CONST_SPLASHSCREEN[] //*J*/ String CONST_SPLASHSCREEN = "splashscreen"; /*C*/ PROGMEM prog_char CONST_AUTOMATIC_DAYLIGHT_SAVING_TIME[] //*J*/ String CONST_AUTOMATIC_DAYLIGHT_SAVING_TIME = "autom. sommerz."; /*C*/ PROGMEM prog_char CONST_GEAR_X_AXIS_DIVIDER[] //*J*/ String CONST_GEAR_X_AXIS_DIVIDER = "gangxachsenteiler"; /*C*/ PROGMEM prog_char CONST_REFUELLINGSTOP_MIN_DIFFERENCE[] //*J*/ String CONST_REFUELLINGSTOP_MIN_DIFFERENCE = "tankstoppdiff."; /*C*/ PROGMEM prog_char CONST_MORE_INFOS[] //*J*/ String CONST_MORE_INFOS = "mehr infos"; /*C*/ PROGMEM prog_char CONST_FUEL_IMPULSES[] //*J*/ String CONST_FUEL_IMPULSES = "tankimpulse"; /*C*/ PROGMEM prog_char CONST_WHEEL_CIRCUMFERENCE[] //*J*/ String CONST_WHEEL_CIRCUMFERENCE = "radumfang"; /*C*/ PROGMEM prog_char CONST_TANK_VOLUME[] //*J*/ String CONST_TANK_VOLUME = "tankvolumen"; /*C*/ PROGMEM prog_char CONST_PHYSICAL_FUEL_GAUGE_E[] //*J*/ String CONST_PHYSICAL_FUEL_GAUGE_E = "tankuhr leer"; /*C*/ PROGMEM prog_char CONST_PHYSICAL_FUEL_GAUGE_F[] //*J*/ String CONST_PHYSICAL_FUEL_GAUGE_F = "tankuhr voll"; // ####################################################################### /*C*/ PROGMEM prog_char ABOUT[] = { //*J*/ char ABOUT[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x04, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x05, 0x09, 0x0A, 0x08, 0x14, 0x18, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0xD0, 0x70, 0x20, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x40, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0xCE, 0x51, 0x5F, 0x50, 0x51, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xC8, 0x84, 0x7A, 0x49, 0x85, 0x86, 0x82, 0x82, 0x02, 0x02, 0x06, 0x08, 0x08, 0x10, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0C, 0x04, 0x04, 0x06, 0x02, 0x01, 0x01, 0x01, 0x03, 0x83, 0x83, 0x83, 0x83, 0x43, 0x43, 0x40, 0xE1, 0xA1, 0x31, 0x50, 0x70, 0x29, 0x19, 0x17, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x83, 0x84, 0x84, 0x84, 0x04, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x31, 0x61, 0xC2, 0x02, 0x02, 0x01, 0x05, 0x05, 0x05, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x02, 0x02, 0x02, 0x05, 0x05, 0x05, 0x01, 0x01, 0x01, 0x02, 0x02, 0x12, 0x12, 0x11, 0x11, 0x01, 0x88, 0xC8, 0x48, 0x40, 0xFC, 0xE4, 0x04, 0xFE, 0x03, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x17, 0x11, 0x11, 0x11, 0x11, 0x11, 0xE1, 0x00, 0x80, 0x00, 0x00, 0xA4, 0x24, 0x2A, 0x2A, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1E, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x67, 0x7E, 0x00, 0x18, 0x3C, 0x44, 0x4C, 0x78, 0x00, 0x1C, 0xF4, 0xA4, 0xBC, 0xFC, 0x01, 0x3F, 0x36, 0x02, 0x1E, 0x1C, 0x03, 0x1F, 0xD8, 0xC9, 0xCF, 0xE0, 0xE7, 0xC7, 0xC0, 0x80, 0x01, 0x03, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x45, 0x05, 0x09, 0x11, 0x21, 0x41, 0x7C, 0x00, 0x00, 0x01, 0x01, 0x19, 0x25, 0x11, 0x89, 0x25, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0x7E, 0x3F, 0x3C, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x8F, 0x83, 0x83, 0x40, 0x20, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x99, 0xA1, 0x99, 0x42, 0x3C, 0x00, 0x01, 0x03, 0x03, 0xE5, 0x25, 0x29, 0x2F, 0x21, 0xE1, 0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0C, 0x10, 0x00, 0x20, 0x20, 0x00, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x40, 0x60, 0x70, 0x71, 0x71, 0x20, 0x20, 0x10, 0x0C, 0x06, 0x07, 0x04, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x79, 0x8A, 0x8A, 0x8A, 0x8A, 0x79, 0x00, 0x03, 0x00, 0x00, 0x00 }; /*C*/ #define GEAR_COL 39 // Bytes per row //*J*/ final int GEAR_COL = 39; // Bytes per row /*C*/ PROGMEM prog_char GEAR[] = { //*J*/ char GEAR[] = { // N 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xF0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x07, 0x1F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF0, 0xE0, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x1F, // 1 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xE0, 0xF0, 0xF0, 0xF8, 0xF8, 0xFC, 0xFC, 0xFE, 0xFF, 0xFF, 0x7F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE, 0x7F, 0x7F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x00, // 3 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF0, 0xF8, 0xF8, 0xFC, 0xFC, 0xFC, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0xFE, 0xFE, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3F, 0x7F, 0x7F, 0x3F, 0x3F, 0x0F, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xFC, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xCF, 0x87, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x07, 0x0F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0x7E, 0xFE, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFE, 0xFE, 0x7F, 0x7F, 0x7F, 0x3F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, // 4 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xC7, 0xC3, 0xC1, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x0F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 5 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7C, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, 0xFC, 0xF8, 0xF8, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07, 0x07, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFC, 0xF0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x0F, 0x0F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0xFE, 0xFE, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFE, 0x7F, 0x7F, 0x7F, 0x3F, 0x3F, 0x1F, 0x1F, 0x0F, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // 6 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xF8, 0xFC, 0xFE, 0xFE, 0xFE, 0x7F, 0x7F, 0x3F, 0x3F, 0x3F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, 0xFC, 0xF8, 0xF8, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xE0, 0xF0, 0xF8, 0xFC, 0xFC, 0x7C, 0x7E, 0x7E, 0x7E, 0x7E, 0xFE, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0xFE, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFE, 0xFE, 0x7F, 0x7F, 0x7F, 0x3F, 0x3F, 0x1F, 0x0F, 0x0F, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00 }; /*C*/ #define FUEL_GAUGE_LEN 16 // The number of bytes per row //*J*/ final int FUEL_GAUGE_LEN = 16; // The number of bytes per row /*C*/ PROGMEM prog_char FUEL_GAUGE[] = { //*J*/ char FUEL_GAUGE[] = { 0x33, 0x33, 0x33, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0xFF, 0xFF, 0xFC, 0xFC, 0x03, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0x0F, 0x0F, 0xFF, 0xFF, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0x3F, 0x3F, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xCC, 0xCC, 0xCC }; /*C*/ PROGMEM prog_int16_t CLOCK_NUMBER_POS[] = { //*J*/ int CLOCK_NUMBER_POS[] = { // The characters for the small numerical font 0, 72, 144, 216, 288, 360, 432, 504, 576, 648, 720, 736 // 10*18*4([0-9]) + 1*4*4(":") }; /*C*/ PROGMEM prog_char CLOCK_NUMBER[] = { // The characters for the small numerical font //*J*/ char CLOCK_NUMBER[] = { // The characters for the small numerical font // 0 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0xF8, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1E, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1F, 0x0F, 0x07, 0x01, 0x00, 0x00, // 1 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xFC, 0xFC, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3E, 0x3E, 0x1F, 0x0F, 0x07, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, // 2 0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xF8, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0xFC, 0xF8, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xFF, 0xFF, 0x7F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF8, 0xFC, 0x7E, 0x3E, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x3F, 0x3F, 0x3F, 0x3F, 0x3D, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, // 3 0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xF8, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xF8, 0xF8, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF8, 0xFF, 0xFF, 0x9F, 0x0F, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0xE0, 0xC0, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, 0xFF, 0xFF, 0xFE, 0xFC, 0x00, 0x00, 0x03, 0x07, 0x0F, 0x1F, 0x1F, 0x3E, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1F, 0x1F, 0x0F, 0x07, 0x00, 0x00, // 4 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xFC, 0xFC, 0xFC, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xF8, 0xFC, 0x3F, 0x1F, 0x07, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x78, 0xFE, 0xFF, 0xFF, 0xF7, 0xF1, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x3F, 0x1F, 0x00, 0x00, 0x00, // 5 0x00, 0x00, 0x00, 0xC0, 0xF8, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x78, 0x78, 0x78, 0x78, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x00, 0x80, 0xE1, 0xE3, 0xE3, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x03, 0x0F, 0x0F, 0x1F, 0x3F, 0x3E, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1F, 0x0F, 0x0F, 0x03, 0x00, // 6 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xF8, 0x7C, 0x3C, 0x3C, 0x3C, 0x7C, 0xF8, 0xF8, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xC3, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE1, 0xE3, 0xC3, 0x83, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1E, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1F, 0x1F, 0x0F, 0x03, 0x00, // 7 0x00, 0x1C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0xFC, 0xFF, 0x3F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFC, 0xFF, 0xFF, 0xFF, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x3F, 0x1F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 0x00, 0x00, 0xC0, 0xF0, 0xF8, 0xF8, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0xF8, 0xF8, 0xF0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0xFF, 0xFF, 0xF0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xFF, 0xFF, 0x3F, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xFF, 0xFF, 0x07, 0x03, 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, 0xFF, 0xFF, 0xFE, 0xFC, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x3E, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1F, 0x1F, 0x0F, 0x07, 0x01, 0x00, // 9 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0x78, 0xF8, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x81, 0x87, 0x8F, 0x8F, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x0F, 0x0F, 0x87, 0xFF, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x07, 0x0F, 0x1F, 0x1F, 0x3E, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1E, 0x1F, 0x0F, 0x03, 0x00, 0x00, 0x00, // colon 0x80, 0xC0, 0xC0, 0x80, 0x03, 0x07, 0x07, 0x03, 0xC0, 0xE0, 0xE0, 0xC0, 0x01, 0x03, 0x03, 0x01, }; /*C*/ PROGMEM prog_char SMALL_NUMBER_AND_SPECIAL_POS[] = { // The characters for the small numerical font //*J*/ char SMALL_NUMBER_AND_SPECIAL_POS[] = { // The characters for the small numerical font 0, 2, 5, 7, 10, 11, 14, 20, 23, 29, 35, 41, 47, 53, 59, 65, 71, 73, 76 }; /*C*/ PROGMEM prog_char SMALL_NUMBER_AND_SPECIAL[] = { // The characters for the small numerical font //*J*/ char SMALL_NUMBER_AND_SPECIAL[] = { // The characters for the small numerical font 0x30, 0x30, // * 0x2A - HACK: big "." for dates 0x08, 0x1C, 0x08, // + 0x2B 0x70, 0x70, // , 0x2C 0x08, 0x08, 0x08, // - 0x2D 0x20, // . 0x2E 0x30, 0x08, 0x06, // / 0x2F 0x1C, 0x3E, 0x22, 0x22, 0x3E, 0x1C, // 0 0x30 0x02, 0x3E, 0x3E, // 1 0x32, 0x3A, 0x2A, 0x2A, 0x2E, 0x24, // 2 0x22, 0x2A, 0x2A, 0x2A, 0x3E, 0x14, // 3 0x18, 0x1C, 0x16, 0x3E, 0x3E, 0x10, // 4 0x2E, 0x2E, 0x2A, 0x2A, 0x3A, 0x12, // 5 0x1C, 0x3E, 0x2A, 0x2A, 0x3A, 0x10, // 6 0x02, 0x22, 0x32, 0x1A, 0x0E, 0x06, // 7 0x14, 0x3E, 0x2A, 0x2A, 0x3E, 0x14, // 8 0x04, 0x2E, 0x2A, 0x2A, 0x3E, 0x1C, // 9 0x39 0x36, 0x36, // : 0x3A 0x32, 0x08, 0x26 // ; 0x3B - HACK: '%' has unnecessary distance to '*' }; /*C*/ PROGMEM prog_char SMALL_LETTER_POS[] = { // The number of used bytes per character //*J*/ char SMALL_LETTER_POS[] = { // The number of used bytes per character 0, 4, 8, 11, 15, 19, 22, 26, 30, 31, 33, 37, 38, 43, 47, 51, 55, 59, 62, 66, 69, 73, 77, 82, 85, 89, 93 }; /*C*/ PROGMEM prog_char SMALL_LETTER[] = { //*J*/ char SMALL_LETTER[] = { 0x18, 0x24, 0x24, 0x3C, // a 0x61 0x3E, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x18, 0x24, 0x24, 0x3E, 0x18, 0x34, 0x2C, 0x08, 0x08, 0x3C, 0x0A, 0x18, 0xA4, 0xA4, 0x7C, 0x3E, 0x04, 0x04, 0x38, 0x3A, 0x80, 0x7A, 0x3E, 0x10, 0x18, 0x24, 0x3E, 0x3C, 0x04, 0x3C, 0x04, 0x38, 0x3C, 0x04, 0x04, 0x38, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0xFC, 0x3C, 0x08, 0x04, 0x28, 0x2C, 0x34, 0x14, 0x04, 0x1E, 0x24, 0x1C, 0x20, 0x20, 0x3C, 0x1C, 0x20, 0x10, 0x0C, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x24, 0x18, 0x24, 0x1C, 0xA0, 0xA0, 0x7C, 0x24, 0x34, 0x2C, 0x24 // z 0x7A }; /*C*/ PROGMEM prog_int16_t DAYS_IN_YEAR[] = { //*J*/ int DAYS_IN_YEAR[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; /*C*/ #define SLOT_OPTION_AMOUNT 14 // Number of slots //*J*/ public static final byte SLOT_OPTION_AMOUNT = 14; // Number of slots /*C*/ PROGMEM const char* SLOTOPTION_LABEL[] = { //*J*/ String SLOTOPTION_LABEL[] = { SLOTOPTION_LABEL_EMPTY, SLOTOPTION_LABEL_SPEED, SLOTOPTION_LABEL_TIME, SLOTOPTION_LABEL_RPM, SLOTOPTION_LABEL_TANKCONTENT, SLOTOPTION_LABEL_CONSUMED, SLOTOPTION_LABEL_CONSUMPTION, SLOTOPTION_LABEL_CONSUMPTION_LONG, SLOTOPTION_LABEL_DIST_LEFT, SLOTOPTION_LABEL_DIST_LEFT_LONG, SLOTOPTION_LABEL_DIST_TOUR, SLOTOPTION_LABEL_DIST_TODAY, SLOTOPTION_LABEL_DIST_REFUELINGSTOP, SLOTOPTION_LABEL_WHEEL_TOTAL }; /*C*/ PROGMEM const char * //*J*/ String WEEKDAY[] = {WEEKDAY_MO, WEEKDAY_TU, WEEKDAY_WE, WEEKDAY_TH, WEEKDAY_FR, WEEKDAY_SA, WEEKDAY_SU}; /*C*/ PROGMEM const char* CONFIGPAGE_TITLE[] = { //*J*/ String CONFIGPAGE_TITLE[] = { CONFIGPAGE_TITLE_SLOTS, CONFIGPAGE_TITLE_TRANSMISSION, CONFIGPAGE_TITLE_INFO_TICKS_OVERVIEW, CONFIGPAGE_TITLE_INFO_TICKS_HISTORY, CONFIGPAGE_TITLE_CONFIG_SLOTS, CONFIGPAGE_TITLE_TRANSMISSION_CONFIG, CONFIGPAGE_TITLE_CONFIG_PORTS, CONFIGPAGE_TITLE_CONFIG_FUEL_TANK, CONFIGPAGE_TITLE_CONFIG_ODOMETER, CONFIGPAGE_TITLE_CONFIG_EXTENDED, CONFIGPAGE_TITLE_CONFIG_CLOCK, CONFIGPAGE_TITLE_CONFIG_CLOCK_OFFSET, CONFIGPAGE_TITLE_ABOUT, CONFIGPAGE_TITLE_MAIN }; /*C*/ PROGMEM prog_char CONFIGPAGE_ID[] = { //*J*/ char CONFIGPAGE_ID[] = { PAGE_SLOTS, PAGE_TRANSMISSION, PAGE_INFO_TICKS_OVERVIEW, PAGE_INFO_TICKS_HISTORY, PAGE_CONFIG_SLOTS, PAGE_TRANSMISSION_CONFIG, PAGE_CONFIG_PORTS, PAGE_CONFIG_FUEL_TANK, PAGE_CONFIG_ODOMETER, PAGE_CONFIG_EXTENDED, PAGE_CONFIG_CLOCK, PAGE_CONFIG_CLOCK_OFFSET, PAGE_ABOUT, PAGE_MAIN }; // ####################################################################### // --- Variables --- //*C struct StoreStruct { char version[4]; int capacityOfFuelTank_MulTen; // Liters/Gallons that fit into the fuel tank IN 10th OF THAT UNIT!!! int circumferenceOfWheel; // Circumference of the wheel where the distance sensor is in millimeters or 1/1000th of a mile int clockOffset_MulThousand; // Offset of DS1307 RTC in seconds per day byte xGraphDivider; // Divider for the x-dimension of the graph on the transmission-pages byte refuellingStopRecognitionAmount; // What's the minimum amount needed to recognize a refuelling stop? 0-255 byte slot[6]; // What to display in the slots on the main page boolean extendedInfos; // Show extended help and infos boolean mirror; // Upside down? boolean aboutOnStart; boolean summertimeSwitch; long ticksPerFuelUnit; // Ticks of the sensor per liter/gallon of fuel float gearTransmissionRatio[6]; byte physicalFuelGaugeE; byte physicalFuelGaugeF; } storage = { CONFIG_VERSION, 220, 1884, 0, 3, 50, {SLOT_TANKCONTENT, SLOT_CONSUMED, SLOT_DIST_LEFT_LONG, SLOT_DIST_REFUELINGSTOP, SLOT_DIST_TODAY, SLOT_DIST_TOUR}, true, false, true, true, 10000, {(float) 109.7, (float) 78.4, (float) 60.9, (float) 52.3, (float) 45.7, (float) 42.2}, 28, 72 }; //C*/ char line // Don't use 'byte', that'll go negative in Java... :( //*J*/ [] = new char [LCD_X]; double gearRatioAvg; int gear, configGear; // 0 == 'N' boolean oldButtonState, shortClick, longClick, selected, summertime; byte menu, menuOld, specialOperationValue, subPage; int configMenuCursorPosition, cursorPosition, errorValue, lastFrameBufferChecksum; long buttonPressed, buttonReleased, specialOperationTime, lastTurn = millis(), lastReset = millis(); double slotSpeed, slotFuelConsumed, slotFuelLeft, slotFuelConsumption, slotFuelConsumptionLongTerm; int slotRPM, slotDistanceLeft, slotDistanceLeftLongTerm, slotDistanceSinceRefueling, slotDistanceToday, slotDistanceTrip; char clockHou, clockMin, clockSec, clockWee, clockDay, clockMon, clockYea; int lastSetDate; long lastSetTime, slotDistanceTotal; char printBuffer //*J*/ [] = new char [PRINTBUFFER_SIZE]; double gearRatio; char showHistory = 0; long motorTickSensorMillisDuration, wheelTickSensorMillisDuration, fuelFlowSensorMillisDuration; byte historyPosCounter; byte historyLines // 'char' won't go negative //*J*/ [][] = new byte [4][HISTORY_WIDTH]; long motorTicksSinceSwitchingOn; long fuelTicksSinceSwitchingOn, fuelTicksSinceRefueling; long wheelTicksSinceSwitchingOn, wheelTicksSinceRefueling, wheelTicksToday, wheelTicksTrip, wheelTicksTotal; long lastStored; // --- 'volatile' is for all the stuff that is going to be modified by an interrupt... /*C*/ volatile byte lastStateOfMotorTicksPin, lastStateOfWheelTicksPin, lastStateOfFuelTicksPin; volatile byte motorTicksIrqFiredPointer, wheelTicksIrqFiredPointer, fuelTicksIrqFiredPointer; volatile byte motorTicksIrqProcessedPointer, wheelTicksIrqProcessedPointer, fuelTicksIrqProcessedPointer; volatile boolean pinStateOfButton = true, pinStateOfNeutral; volatile long motorTicksIrqFired //*J*/ [] = new long [QUEUE_SIZE_MOTOR]; volatile long wheelTicksIrqFired //*J*/ [] = new long [QUEUE_SIZE_WHEEL]; volatile long fuelTicksIrqFired //*J*/ [] = new long [QUEUE_SIZE_FUEL]; // --- 'volatile' is for all the stuff that is going to be modified by an interrupt... void loadNVRamData() { if (getNVRamChar(NVB_STOREMARKER) == CONFIG_VERSION[2]) { // valid data //*J*/ System.out.println("loadNVRamData() -> Found data, loading..."); // Check if we have to reset one or more of the distance tick counters... syncClock(false); char day = getNVRamChar(NVB_LAST_SCAN_DAY); char month = getNVRamChar(NVB_LAST_SCAN_MONTH); char year = getNVRamChar(NVB_LAST_SCAN_YEAR); // Check if the last switching-on was on another day -> "0" if (day == clockDay && month == clockMon && year == clockYea) // Only today is ok wheelTicksToday = getNVRamLong(NVL_DISTANCE_TODAY); // Check if the last switching-on was before yesterday -> "0" if (getDaysInCentury(clockDay, clockMon, clockYea) - getDaysInCentury(day, month, year) <= 1) // Today or yesterday is ok wheelTicksTrip = getNVRamLong(NVL_DISTANCE_TRIP); wheelTicksTotal = getNVRamLong(NVL_DISTANCE_TOTAL); // Check if the fuel tank is a lot fuller than when this Arduino was last shut down int actualFuelTankValue = analogRead(PIN_ANALOG_FUELTANK) / 4; // -> 0..255 if (actualFuelTankValue <= getNVRamChar(NVB_ANALOG_FUEL_SENSOR) + storage.refuellingStopRecognitionAmount * 2.58) { // 99 -> 255 wheelTicksSinceRefueling = getNVRamLong(NVL_DISTANCE_SINCE_REFUELING_STOP); fuelTicksSinceRefueling = getNVRamLong(NVL_FUEL_SINCE_REFUELING_STOP); } lastSetDate = getNVRamInt(NVI_CLOCK_LAST_SET_DATE); lastSetTime = getNVRamLong(NVL_CLOCK_LAST_SET_TIME); //*J*/ System.out.println("wheelTicksToday: " + wheelTicksToday); //*J*/ System.out.println("wheelTicksTrip: " + wheelTicksTrip); //*J*/ System.out.println("wheelTicksTotal: " + wheelTicksTotal); //*J*/ System.out.println("wheelTicksSinceRefueling: " + wheelTicksSinceRefueling); //*J*/ System.out.println("fuelTicksSinceRefueling: " + fuelTicksSinceRefueling); } else { //*J*/ System.out.println("loadNVRamData() -> No data found"); digitalWrite(PIN_LED, HIGH); delay(1000); // One long blink for "No config found!" or "Wrong config version" digitalWrite(PIN_LED, LOW); delay(100); } } void saveNVRamData() { //*J*/ System.out.println("saveNVRamData()"); digitalWrite(PIN_LED, HIGH); setNVRamChar(NVB_STOREMARKER, CONFIG_VERSION[2]); setNVRamChar(NVB_ANALOG_FUEL_SENSOR, (char) (analogRead(PIN_ANALOG_FUELTANK) / 4)); syncClock(false); setNVRamChar(NVB_LAST_SCAN_DAY, clockDay); setNVRamChar(NVB_LAST_SCAN_MONTH, clockMon); setNVRamChar(NVB_LAST_SCAN_YEAR, clockYea); setNVRamLong(NVL_DISTANCE_TODAY, wheelTicksToday); setNVRamLong(NVL_DISTANCE_TRIP, wheelTicksTrip); setNVRamLong(NVL_DISTANCE_TOTAL, wheelTicksTotal); setNVRamLong(NVL_DISTANCE_SINCE_REFUELING_STOP, wheelTicksSinceRefueling); setNVRamLong(NVL_FUEL_SINCE_REFUELING_STOP, fuelTicksSinceRefueling); setNVRamInt(NVI_CLOCK_LAST_SET_DATE, lastSetDate); setNVRamLong(NVL_CLOCK_LAST_SET_TIME, lastSetTime); digitalWrite(PIN_LED, LOW); } void loadEEPRomData() { if (EEPROM.read(CONFIG_START) == CONFIG_VERSION[0] && EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] && EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2]) { /*J System.out.println("loadEEPRomData() -> Found data, loading..."); storage.capacityOfFuelTank_MulTen = Integer.parseInt(EEPROM.getProperty("EEPRom.capacityOfFuelTank_MulTen")); storage.circumferenceOfWheel = Integer.parseInt(EEPROM.getProperty("EEPRom.circumferenceOfWheel")); storage.clockOffset_MulThousand = Integer.parseInt(EEPROM.getProperty("EEPRom.clockOffset_MulThousand")); storage.xGraphDivider = Byte.parseByte(EEPROM.getProperty("EEPRom.xGraphDivider")); storage.refuellingStopRecognitionAmount = Byte.parseByte(EEPROM.getProperty("EEPRom.refuellingStopRecognitionAmount")); storage.slot = parseByteArray(EEPROM.getProperty("EEPRom.slot")); storage.extendedInfos = Boolean.parseBoolean(EEPROM.getProperty("EEPRom.extendedInfos")); storage.mirror = Boolean.parseBoolean(EEPROM.getProperty("EEPRom.mirror")); storage.aboutOnStart = Boolean.parseBoolean(EEPROM.getProperty("EEPRom.aboutOnStart")); storage.summertimeSwitch = Boolean.parseBoolean(EEPROM.getProperty("EEPRom.summertimeSwitch")); storage.ticksPerFuelUnit = Long.parseLong(EEPROM.getProperty("EEPRom.ticksPerFuelUnit")); storage.gearTransmissionRatio = parseFloatArray(EEPROM.getProperty("EEPRom.gearTransmissionRatio")); storage.physicalFuelGaugeE = Byte.parseByte(EEPROM.getProperty("EEPRom.physicalFuelGaugeE")); storage.physicalFuelGaugeF = Byte.parseByte(EEPROM.getProperty("EEPRom.physicalFuelGaugeF")); J*/ //*C for (unsigned int t=0; t No data found"); digitalWrite(PIN_LED, HIGH); delay(1000); // One long blink for "No config found!" or "Wrong config version" digitalWrite(PIN_LED, LOW); delay(100); } } void saveEEPRomData() { digitalWrite(PIN_LED, HIGH); /*J System.out.println("saveEEPRomData() = "); System.out.println(storage); EEPROM.setProperty("EEPRom.configVersion", "" + CONFIG_VERSION[2]); // Mark that we have stored data... EEPROM.setProperty("EEPRom.capacityOfFuelTank_MulTen", "" + storage.capacityOfFuelTank_MulTen); EEPROM.setProperty("EEPRom.circumferenceOfWheel", "" + storage.circumferenceOfWheel); EEPROM.setProperty("EEPRom.clockOffset_MulThousand", "" + storage.clockOffset_MulThousand); EEPROM.setProperty("EEPRom.xGraphDivider", "" + storage.xGraphDivider); EEPROM.setProperty("EEPRom.refuellingStopRecognitionAmount", "" + storage.refuellingStopRecognitionAmount); EEPROM.setProperty("EEPRom.slot", Arrays.toString(storage.slot)); EEPROM.setProperty("EEPRom.extendedInfos", "" + storage.extendedInfos); EEPROM.setProperty("EEPRom.mirror", "" + storage.mirror); EEPROM.setProperty("EEPRom.aboutOnStart", "" + storage.aboutOnStart); EEPROM.setProperty("EEPRom.summertimeSwitch", "" + storage.summertimeSwitch); EEPROM.setProperty("EEPRom.ticksPerFuelUnit", "" + storage.ticksPerFuelUnit); EEPROM.setProperty("EEPRom.gearTransmissionRatio", Arrays.toString(storage.gearTransmissionRatio)); EEPROM.setProperty("EEPRom.physicalFuelGaugeE", "" + storage.physicalFuelGaugeE); EEPROM.setProperty("EEPRom.physicalFuelGaugeF", "" + storage.physicalFuelGaugeF); EEPROM.write(CONFIG_START, CONFIG_VERSION[0]); EEPROM.write(CONFIG_START + 1, CONFIG_VERSION[1]); EEPROM.write(CONFIG_START + 2, CONFIG_VERSION[2]); J*/ //*C for (unsigned int t=0; t> IRQ0 << ISR(INT0_vect) { int tmp = digitalRead(PIN_IRQ_MOTORTICKS); if (lastStateOfMotorTicksPin != tmp) { // check for a rising flank, to use only complete amplitudes! if (tmp) irqMotorTicks(); lastStateOfMotorTicksPin = tmp; } } // Aka >> IRQ1 << ISR(INT1_vect) { int tmp = digitalRead(PIN_IRQ_WHEELTICKS); if (lastStateOfWheelTicksPin != tmp) { // check for a rising flank, to use only complete amplitudes! if (tmp) irqWheelTicks(); lastStateOfWheelTicksPin = tmp; } } // This one is called through the PCINTs, we need it because we only have two 'regular' IRQs... :( ISR(PCINT2_vect) { int tmp = digitalRead(PIN_IRQ_FUELTICKS); if (lastStateOfFuelTicksPin != tmp) { // check for a rising flank, to use only complete amplitudes! if (tmp) irqFuelTicks(); lastStateOfFuelTicksPin = tmp; } pinStateOfNeutral = digitalRead(PIN_IRQ_NEUTRAL); // With these we take every flank pinStateOfButton = digitalRead(PIN_IRQ_BUTTON); // With these we take every flank } void prepareInputsAndInterrupts() { // Switch some ports to Input pinMode(PIN_IRQ_MOTORTICKS, INPUT); pinMode(PIN_IRQ_WHEELTICKS, INPUT); pinMode(PIN_IRQ_FUELTICKS, INPUT); pinMode(PIN_IRQ_NEUTRAL, INPUT); pinMode(PIN_IRQ_BUTTON, INPUT); pinMode(PIN_OUT_FUELCLOCK, OUTPUT); // Activate the 20k pullup-resistor -> connect button to GND! digitalWrite(PIN_IRQ_MOTORTICKS, HIGH); digitalWrite(PIN_IRQ_WHEELTICKS, HIGH); digitalWrite(PIN_IRQ_FUELTICKS, HIGH); digitalWrite(PIN_IRQ_NEUTRAL, HIGH); digitalWrite(PIN_IRQ_BUTTON, HIGH); // Connect Interrupts // first: INT0 and INT1 EIMSK = (1 << INT1) | (1 << INT0); // External Interrupt Mask: Both IRQs // There's a lot of electrical jitter there and it looks like I can't make sure // only the rising flank will trigger it, so we'll have to do it using software. EICRA = (1 << ISC10) | (1 << ISC00); // and also: the other pins, using 'ordinary' PCINTs PCICR |= 1 << PCIE2; // IRQ for some of D0-D7 PCMSK2 |= 1 << PCINT20; // IRQ for D4 PCMSK2 |= 1 << PCINT21; // IRQ for D5 PCMSK2 |= 1 << PCINT22; // IRQ for D6 // Enable IRQs sei(); } //C*/ char getNVRamChar(byte what) { Wire.beginTransmission(DS1307); Wire.send(what); // Go to address - free memory starts at 8! (7 is status register) Wire.endTransmission(); Wire.requestFrom(DS1307, 1); // This function reads just one byte! return (char) (Wire.receive() & 0xff); } void setNVRamChar(byte what, char value) { Wire.beginTransmission(DS1307); Wire.send(what); // Go to address - free memory starts at 8! (7 is status register) Wire.send((byte) value); Wire.endTransmission(); } int getNVRamInt(byte what) { Wire.beginTransmission(DS1307); Wire.send(what); // Go to address - free memory starts at 8! (7 is status register) Wire.endTransmission(); Wire.requestFrom(DS1307, 2); // This function reads a word, two bytes! return (Wire.receive() << 8 & 0xff00) | (Wire.receive() & 0xff); } void setNVRamInt(byte what, int value) { Wire.beginTransmission(DS1307); Wire.send(what); // Go to address - free memory starts at 8! (7 is status register) Wire.send((byte) (value >> 8)); Wire.send((byte) value); Wire.endTransmission(); } long getNVRamLong(byte what) { Wire.beginTransmission(DS1307); Wire.send(what); // Go to address - free memory starts at 8! (7 is status register) Wire.endTransmission(); Wire.requestFrom(DS1307, 4); // This function reads a long, four bytes! return (Wire.receive() << 24 & 0xff000000) | (Wire.receive() << 16 & 0xff0000) | (Wire.receive() << 8 & 0xff00) | (Wire.receive() & 0xff); } void setNVRamLong(byte what, long value) { Wire.beginTransmission(DS1307); Wire.send(what); // Go to address - free memory starts at 8! (7 is status register) Wire.send((byte) (value >> 24)); Wire.send((byte) (value >> 16)); Wire.send((byte) (value >> 8)); Wire.send((byte) value); Wire.endTransmission(); } byte bcd2Dec(byte val) { return (byte) (val / 16 * 10 + val % 16); } char dec2Bcd(byte val) { return (char) (val / 10 * 16 + val % 10); } long getClockSeconds() { Wire.beginTransmission(DS1307); Wire.send((byte) 0); // Go to address Wire.endTransmission(); Wire.requestFrom(DS1307, 3); // second, minute, hour return bcd2Dec((byte) (Wire.receive() & 0x7f)) + bcd2Dec(Wire.receive()) * 60 + bcd2Dec(Wire.receive()) * 3600L; } int getClockDays() { Wire.beginTransmission(DS1307); Wire.send((byte) 4); // Go to address Wire.endTransmission(); Wire.requestFrom(DS1307, 3); // day, month, year return getDaysInCentury((char) bcd2Dec(Wire.receive()), (char) bcd2Dec(Wire.receive()), (char) (bcd2Dec(Wire.receive()) % 100)); } void setClockHou(byte val) { if (summertime && storage.summertimeSwitch) val--; // When we're on summertime we display one more hour, so we need to subtract it here! setNVRamChar((byte) 2, dec2Bcd(val)); } void setClockMin(byte val) { setNVRamChar((byte) 1, dec2Bcd(val)); } void setClockSec(byte val) { setNVRamChar((byte) 0, dec2Bcd(val)); } void setClockDay(byte val) { setNVRamChar((byte) 4, dec2Bcd(val)); } void setClockMon(byte val) { setNVRamChar((byte) 5, dec2Bcd(val)); } void setClockYea(byte val) { setNVRamChar((byte) 6, dec2Bcd(val)); } void fillPrintBufferWithClock() { printBuffer[0] = (char) (clockDay / 10 + '0'); printBuffer[1] = (char) (clockDay % 10 + '0'); printBuffer[2] = '*'; printBuffer[3] = (char) (clockMon / 10 + '0'); printBuffer[4] = (char) (clockMon % 10 + '0'); printBuffer[5] = '*'; printBuffer[6] = (char) (clockYea / 10 + '0'); printBuffer[7] = (char) (clockYea % 10 + '0'); printBuffer[8] = '\0'; } // Supports ONLY ONE digit after the comma! int fillPrintBufferWithDouble(double value, int start, byte minLength) { if (value < 0) { value = -value; printBuffer[start++] = '-'; } byte length = 0; long valueBuffer = (long) (value * 10 + .5); while (valueBuffer > 0) { valueBuffer /= 10; length++; } if (length < minLength) length = minLength; printBuffer[length + 1 + start] = '\0'; valueBuffer = (long) (value * 10 + .5); for (byte t = (byte) (length - 1); t >= 0 && t < 127; t--) { // Watch out, 'byte' has NO negative on Arduino! printBuffer[t + start] = (char) ('0' + valueBuffer % 10); valueBuffer /= 10; } printBuffer[length + start] = printBuffer[length - 1 + start]; // Move 'last' digit to really last position. :) printBuffer[length - 1 + start] = ','; // Replace old 'last' digit with "," return length + 1 + start; } // Supports ONLY ONE digit after the comma! void printDouble(int row, int align, double value, byte inverted, boolean fullInverted, byte minLength) { fillPrintBufferWithDouble(value, 0, minLength); printChars(row, align, printBuffer, inverted, fullInverted); } int fillPrintBufferWithLong(long value, int start, byte minLength) { if (value < 0) { value = -value; printBuffer[start++] = '-'; } byte length = 0; long valueBuffer = value; do { valueBuffer /= 10; length++; } while (valueBuffer > 0); if (length < minLength) length = minLength; printBuffer[length + start] = '\0'; for (byte t = (byte) (length - 1); t >= 0 && t < 127; t--) { // Watch out, 'byte' has NO negative on Arduino! printBuffer[t + start] = (char) ('0' + value % 10); value /= 10; } return length + start; } void printLong(int row, int align, long value, byte inverted, boolean fullInverted, byte minLength) { fillPrintBufferWithLong(value, 0, minLength); printChars(row, align, printBuffer, inverted, fullInverted); } void printChars(int row, int align, //*J*/ char[] /*C*/ const char* value, byte inverted, boolean fullInverted) { int pos = 0; // Position for first char in row if (align == ALIGN_CENTER || align == ALIGN_RIGHT) pos = LCD_X - getCharArrayLen(value); // Align right... if (align == ALIGN_CENTER) pos /= 2; // Align center for (byte c = 0; c < strlen(value); c++) { boolean invert = c == inverted || inverted == INVERT_ALL; pos += 1 + printChar(pos, row, value[c], invert, fullInverted); // plus one empty pixel between chars... } } int printChar(int col, int row, char c, boolean inverted, boolean fullInverted) { byte len = getCharLen(c); if (c == ' ') { // Special case in case of space! ;) if (inverted) frameBuffer[col + row * LCD_X] |= fullInverted ? 0xff : 0x80; } else if (c >= '*' && c <= ';') { // *+,-./0123456789:; c -= '*'; for (byte x = 0; x < len; x++) { int innerVal = readCharFromEeprom(SMALL_NUMBER_AND_SPECIAL_POS, c) + x; int value = readCharFromEeprom(SMALL_NUMBER_AND_SPECIAL, innerVal); frameBuffer[col + row * LCD_X + x] ^= inverted ? value ^ (fullInverted ? 0xff : 0x80) : value; } } else if (c >= 'a' && c <= 'z') { // 'a'-'z' c -= 'a'; for (byte x = 0; x < len; x++) { int innerVal = readCharFromEeprom(SMALL_LETTER_POS, c) + x; int value = readCharFromEeprom(SMALL_LETTER, innerVal); frameBuffer[col + row * LCD_X + x] ^= inverted ? value ^ (fullInverted ? 0xff : 0x80) : value; } } if (inverted && col > 0) frameBuffer[col - 1 + row * LCD_X] |= fullInverted ? 0xff : 0x80; // left border if (inverted && fullInverted && col < LCD_X - len) frameBuffer[col + len + row * LCD_X] |= 0xff; // right border return len; } int getCharArrayLen( //*J*/ char[] /*C*/ const char* value) { int pos = 0; for (byte c = 0; c < strlen(value); c++) { pos += getCharLen(value[c]); if (c > 0) pos++; // Space between chars... } return pos; } byte getCharLen(char c) { if (c >= '*' && c <= ';') { // *+,-./0123456789:; c -= '*'; return (byte) (readCharFromEeprom(SMALL_NUMBER_AND_SPECIAL_POS, c + 1) - readCharFromEeprom(SMALL_NUMBER_AND_SPECIAL_POS, c)); } else if (c >= 'a' && c <= 'z') { // 'a'-'z' c -= 'a'; return (byte) (readCharFromEeprom(SMALL_LETTER_POS, c + 1) - readCharFromEeprom(SMALL_LETTER_POS, c)); } else return 1; // ' ', plus right and left border -> effectively 3 } void printBigGear() { for (byte x = 0; x < GEAR_COL; x++) for (byte y = 0; y < LCD_Y / 8; y++) // "21" is the x-offset frameBuffer[21 + x + y * LCD_X] |= readCharFromEeprom(GEAR, gear * GEAR_COL * 6 + x + y * GEAR_COL); } void printBigClock() { printBigClockDigit(1, 1, clockHou / 10); printBigClockDigit(20, 1, clockHou % 10); printBigClockDigit(40, 1, 10); // ':' printBigClockDigit(46, 1, clockMin / 10); printBigClockDigit(65, 1, clockMin % 10); } void printBigClockDigit(int col, int row, int value) { byte digitWidth = (byte) ((readIntFromEeprom(CLOCK_NUMBER_POS, value + 1) - readIntFromEeprom(CLOCK_NUMBER_POS, value)) / 4); for (byte y = 0; y < 4; y++) // Clock digit height is '4'! for (byte x = 0; x < digitWidth; x++) frameBuffer[col + x + (y + row) * LCD_X] |= readCharFromEeprom(CLOCK_NUMBER, readIntFromEeprom(CLOCK_NUMBER_POS, value) + x + y * digitWidth); } // Not row but pixel, top and left is '0'!! void drawHorizontalLine(byte x1, byte x2, byte y) { for (byte col = x1; col <= x2; col++) frameBuffer[col + (y / 8) * LCD_X] ^= 1 << (y % 8); } // Not row but pixel, top and left is '0'!! void drawVerticalLine(byte x, byte y1, byte y2) { for (byte row = (byte) (y1 / 8); row <= y2 / 8; row++) { byte data = (byte) 0xff; if (row == y1 / 8) data &= 0xff << y1 % 8; if (row == y2 / 8) data &= 0xff >> 7 - y2 % 8; frameBuffer[x + row * LCD_X] ^= data; } } void drawPixel(int x, int y) { frameBuffer[x + (y / 8) * LCD_X] |= 1 << (y % 8); } void drawVerticalBarWithXGraphDivider(int row, double col) { frameBuffer[(int) (col / (storage.xGraphDivider / 2.0) + .5) + row * LCD_X] ^= 0xfe; } void calculateCharsForClock(long time) { printBuffer[0] = (char) (time / 36000 + '0'); printBuffer[1] = (char) (time / 3600 % 10 + '0'); printBuffer[2] = ':'; printBuffer[3] = (char) (time / 600 % 6 + '0'); printBuffer[4] = (char) (time / 60 % 10 + '0'); printBuffer[5] = ':'; printBuffer[6] = (char) (time / 10 % 6 + '0'); printBuffer[7] = (char) (time % 10 + '0'); printBuffer[8] = '\0'; } void error(int value) { if (value != -1) errorValue |= value; } void printSlotOptionValue(int row, int option) { if (option == SLOT_EMPTY) return; // Print nothing in that case! if (option == SLOT_SPEED) printDouble(row, ALIGN_RIGHT, slotSpeed, INVERT_NONE, false, (byte) 2); if (option == SLOT_TIME) { syncClock(true); // Fetch actual date and time from RTC... printBuffer[0] = (char) (clockHou / 10 + '0'); printBuffer[1] = (char) (clockHou % 10 + '0'); printBuffer[2] = ':'; printBuffer[3] = (char) (clockMin / 10 + '0'); printBuffer[4] = (char) (clockMin % 10 + '0'); printBuffer[5] = '\0'; printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); } if (option == SLOT_RPM) printLong(row, ALIGN_RIGHT, slotRPM, INVERT_NONE, false, (byte) 1); if (option == SLOT_TANKCONTENT) printDouble(row, ALIGN_RIGHT, slotFuelLeft, INVERT_NONE, false, (byte) 2); if (option == SLOT_CONSUMED) printDouble(row, ALIGN_RIGHT, slotFuelConsumed, INVERT_NONE, false, (byte) 2); if (option == SLOT_CONSUMPTION) printDouble(row, ALIGN_RIGHT, slotFuelConsumption, INVERT_NONE, false, (byte) 2); if (option == SLOT_CONSUMPTION_LONG) printDouble(row, ALIGN_RIGHT, slotFuelConsumptionLongTerm, INVERT_NONE, false, (byte) 2); if (option == SLOT_DIST_LEFT) printLong(row, ALIGN_RIGHT, slotDistanceLeft, INVERT_NONE, false, (byte) 1); if (option == SLOT_DIST_LEFT_LONG) printLong(row, ALIGN_RIGHT, slotDistanceLeftLongTerm, INVERT_NONE, false, (byte) 1); if (option == SLOT_DIST_TOUR) printLong(row, ALIGN_RIGHT, slotDistanceTrip, INVERT_NONE, false, (byte) 1); if (option == SLOT_DIST_TODAY) printLong(row, ALIGN_RIGHT, slotDistanceToday, INVERT_NONE, false, (byte) 1); if (option == SLOT_DIST_REFUELINGSTOP) printLong(row, ALIGN_RIGHT, slotDistanceSinceRefueling, INVERT_NONE, false, (byte) 1); if (option == SLOT_WHEEL_TOTAL) printLong(row, ALIGN_RIGHT, slotDistanceTotal, INVERT_NONE, false, (byte) 1); } // Mirror a byte byte mirror(byte n) { n = (byte) (((n >> 1) & 0x55) | ((n << 1) & 0xaa)); n = (byte) (((n >> 2) & 0x33) | ((n << 2) & 0xcc)); n = (byte) (((n >> 4) & 0x0f) | ((n << 4) & 0xf0)); return n; } // Mirror the framebuffer void mirrorFrameBuffer() { byte b; for (int i = 0; i < LCD_X * LCD_Y / 8 / 2; i++) { b = frameBuffer[i]; frameBuffer[i] = mirror(frameBuffer[LCD_X * LCD_Y / 8 - i - 1]); frameBuffer[LCD_X * LCD_Y / 8 - i - 1] = mirror(b); } } // 0..99, 1..12, 1..(28,29,30,31) -> 1..36519 int getDaysInCentury(char day, char month, char year) { if (month == 0 || month > 12) // Prevent problems in case nothing is stored in NVRam yet... return 0; return (int) (year * 365.25 + .75) + // years readIntFromEeprom(DAYS_IN_YEAR, month - 1) + // months (month > 2 && year % 4 == 0 ? 1 : 0) +// leap year? If yes, add one day after february day; // days } // 1..366 -> 0, 367..731 -> 1, 732..1096 -> 2 ... char getYearInCentury(int days) { return (char) ((days - 1) / 365.25); } char getMonthInCentury(int days) { char year = getYearInCentury(days); days -= (int) (year * 365.25 + .75); // Day in year char nearly1 = (char) (days / 30); // approximately... if (nearly1 < 1) // Prevent out-of-bounds...! nearly1 = 1; if (nearly1 > 11) // Prevent out-of-bounds...! nearly1 = 11; char nearly2 = (char) (nearly1 + 1); int days2 = readIntFromEeprom(DAYS_IN_YEAR, nearly1) + (nearly2 > 2 && year % 4 == 0 ? 1 : 0); if (days <= days2) return nearly1; else return nearly2; } char getMonthDayInCentury(int days) { char year = getYearInCentury(days); char month = getMonthInCentury(days); int daysTillMonth = (int) (year * 365.25 + .75) + // years readIntFromEeprom(DAYS_IN_YEAR, month - 1) + // months (month > 2 && year % 4 == 0 ? 1 : 0); // leap year? If yes, add one day after february return (char) (days - daysTillMonth); } void syncClock(boolean corrected) { long clockSeconds = getClockSeconds(); int clockDays = getClockDays(); // Correct the time... if (corrected && lastSetDate != 0 && lastSetTime != 0) { // if we have valid data clockSeconds += (clockDays - lastSetDate + (clockSeconds - lastSetTime) / 86400.0) * (storage.clockOffset_MulThousand / 1000.0); } // In case of need add one hour for Daylight Savings Time if (summertime && storage.summertimeSwitch) clockSeconds += 3600; // Check seconds and fix them and the date... while (clockSeconds >= 86400) { clockSeconds -= 86400; clockDays++; } while (clockSeconds < 0) { clockSeconds += 86400; clockDays--; } clockYea = getYearInCentury(clockDays); clockMon = getMonthInCentury(clockDays); clockDay = getMonthDayInCentury(clockDays); clockHou = (char) (clockSeconds / 3600); clockMin = (char) (clockSeconds / 60 % 60); clockSec = (char) (clockSeconds % 60); clockWee = (char) ((getDaysInCentury(clockDay, clockMon, clockYea) + 4) % 7); if (storage.summertimeSwitch) { // Daylight Savings Time goes from last Sunday in March to last Sunday in October if (clockMon > 3 && clockMon < 10) summertime = true; // If we have April to September, we're definitely using Daylight Savings Time! else if (clockMon < 3 || clockMon > 10) summertime = false; // If we have November to February, we're definitely not using Daylight Savings Time! else { // March or October char dayOfLastSunday = (char) (31 - ((getDaysInCentury((char) 31, clockMon, clockYea) + 5) % 7)); if (clockMon == 3) { // March summertime = clockDay > dayOfLastSunday || clockDay == dayOfLastSunday && clockHou >= 2; } else if (clockMon == 10) { // October summertime = clockDay < dayOfLastSunday || clockDay == dayOfLastSunday && clockHou - (summertime ? 1 : 0) < 2; } } } } void storeClockChangeTime() { lastSetDate = getClockDays(); lastSetTime = getClockSeconds(); } void specialOperation(byte specOps, int delayMillis) { specialOperationValue = specOps; specialOperationTime = millis() + delayMillis; } void calculateGear(double gearRatio) { if (pinStateOfNeutral) { gear = 0; // "N" return; } else if (gearRatio >= storage.gearTransmissionRatio[5] - (storage.gearTransmissionRatio[4] - storage.gearTransmissionRatio[5]) / 2 && gearRatio <= storage.gearTransmissionRatio[5] + (storage.gearTransmissionRatio[4] - storage.gearTransmissionRatio[5]) / 2) { gear = 6; return; } else if (gearRatio >= storage.gearTransmissionRatio[4] - (storage.gearTransmissionRatio[3] - storage.gearTransmissionRatio[4]) / 2 && gearRatio <= storage.gearTransmissionRatio[4] + (storage.gearTransmissionRatio[4] - storage.gearTransmissionRatio[5]) / 2) { gear = 5; return; } else if (gearRatio >= storage.gearTransmissionRatio[3] - (storage.gearTransmissionRatio[2] - storage.gearTransmissionRatio[3]) / 2 && gearRatio <= storage.gearTransmissionRatio[3] + (storage.gearTransmissionRatio[3] - storage.gearTransmissionRatio[4]) / 2) { gear = 4; return; } else if (gearRatio >= storage.gearTransmissionRatio[2] - (storage.gearTransmissionRatio[1] - storage.gearTransmissionRatio[2]) / 2 && gearRatio <= storage.gearTransmissionRatio[2] + (storage.gearTransmissionRatio[2] - storage.gearTransmissionRatio[3]) / 2) { gear = 3; return; } else if (gearRatio >= storage.gearTransmissionRatio[1] - (storage.gearTransmissionRatio[0] - storage.gearTransmissionRatio[1]) / 2 && gearRatio <= storage.gearTransmissionRatio[1] + (storage.gearTransmissionRatio[1] - storage.gearTransmissionRatio[2]) / 2) { gear = 2; return; } else if (gearRatio >= storage.gearTransmissionRatio[0] - (storage.gearTransmissionRatio[0] - storage.gearTransmissionRatio[1]) / 2 && gearRatio <= storage.gearTransmissionRatio[0] + (storage.gearTransmissionRatio[0] - storage.gearTransmissionRatio[1]) / 2) { gear = 1; return; } gear = -1; // "" } void calculateSlotsData() { if (wheelTickSensorMillisDuration != 0) slotSpeed = storage.circumferenceOfWheel / wheelTickSensorMillisDuration * 3.6; if (motorTickSensorMillisDuration != 0) slotRPM = (int) (60000 / motorTickSensorMillisDuration); if (slotSpeed != 0) gearRatio = slotRPM / slotSpeed; slotFuelConsumed = fuelTicksSinceRefueling / (double) storage.ticksPerFuelUnit; slotFuelLeft = (storage.capacityOfFuelTank_MulTen / 10.0) - slotFuelConsumed; if (slotSpeed != 0 && fuelFlowSensorMillisDuration != 0) slotFuelConsumption = 360000000L / (slotSpeed * storage.ticksPerFuelUnit * fuelFlowSensorMillisDuration); if (wheelTicksSinceRefueling != 0) slotFuelConsumptionLongTerm = slotFuelConsumed * 100000000L / storage.circumferenceOfWheel / wheelTicksSinceRefueling; if (slotFuelConsumption != 0) slotDistanceLeft = (int) ((storage.capacityOfFuelTank_MulTen / 10.0) / slotFuelConsumption * 100 + .5); slotDistanceSinceRefueling = (int) (storage.circumferenceOfWheel * wheelTicksSinceRefueling / 1000000.0 + .5); slotDistanceToday = (int) (storage.circumferenceOfWheel * wheelTicksToday / 1000000.0 + .5); slotDistanceTrip = (int) (storage.circumferenceOfWheel * wheelTicksTrip / 1000000.0 + .5); slotDistanceTotal = (long) (storage.circumferenceOfWheel * wheelTicksTotal / 1000000.0 + .5); if (slotFuelConsumptionLongTerm != 0) slotDistanceLeftLongTerm = (int) ((storage.capacityOfFuelTank_MulTen / 10.0) / slotFuelConsumptionLongTerm * 100 + .5) - slotDistanceSinceRefueling; } void processIrqs() { while (motorTicksIrqFiredPointer != motorTicksIrqProcessedPointer) { long lastValue = motorTicksIrqFired[motorTicksIrqProcessedPointer++]; motorTicksIrqProcessedPointer &= (QUEUE_SIZE_MOTOR - 1); long tmp = motorTicksIrqFired[motorTicksIrqProcessedPointer] - lastValue; motorTickSensorMillisDuration = (long) (motorTickSensorMillisDuration * .8 + tmp * .2); motorTicksSinceSwitchingOn++; // Statistics... historyLines[2][historyPosCounter] += historyLines[2][historyPosCounter] < 255 ? 1 : 0; // Prohibit vertical overflow! } while (wheelTicksIrqFiredPointer != wheelTicksIrqProcessedPointer) { long lastValue = wheelTicksIrqFired[wheelTicksIrqProcessedPointer++]; wheelTicksIrqProcessedPointer &= (QUEUE_SIZE_WHEEL - 1); long tmp = wheelTicksIrqFired[wheelTicksIrqProcessedPointer] - lastValue; wheelTickSensorMillisDuration = (long) (wheelTickSensorMillisDuration * .5 + tmp * .5); wheelTicksSinceRefueling++; wheelTicksToday++; wheelTicksTrip++; wheelTicksTotal++; wheelTicksSinceSwitchingOn++; // Statistics... historyLines[3][historyPosCounter] += historyLines[3][historyPosCounter] < 255 ? 1 : 0; // Prohibit vertical overflow! } while (fuelTicksIrqFiredPointer != fuelTicksIrqProcessedPointer) { long lastValue = fuelTicksIrqFired[fuelTicksIrqProcessedPointer++]; fuelTicksIrqProcessedPointer &= (QUEUE_SIZE_FUEL - 1); long tmp = fuelTicksIrqFired[fuelTicksIrqProcessedPointer] - lastValue; fuelFlowSensorMillisDuration = (long) (fuelFlowSensorMillisDuration * .75 + tmp * .25); fuelTicksSinceRefueling++; fuelTicksSinceSwitchingOn++; // Statistics... historyLines[1][historyPosCounter] += historyLines[1][historyPosCounter] < 255 ? 1 : 0; // Prohibit vertical overflow! } } // Motor ticks (~66,7/s at 100km/h), Button, Neutral void irqMotorTicks() { // -> Pin 2 motorTicksIrqFiredPointer = (byte) ((motorTicksIrqFiredPointer + 1) & (QUEUE_SIZE_MOTOR - 1)); motorTicksIrqFired[motorTicksIrqFiredPointer] = millis(); if (motorTicksIrqFiredPointer == motorTicksIrqProcessedPointer) // Overflow?!!! error(1); } // Wheel ticks (~14,7/s at 100km/h) void irqWheelTicks() { // -> Pin 3 wheelTicksIrqFiredPointer = (byte) ((wheelTicksIrqFiredPointer + 1) & (QUEUE_SIZE_WHEEL - 1)); wheelTicksIrqFired[wheelTicksIrqFiredPointer] = millis(); if (wheelTicksIrqFiredPointer == wheelTicksIrqProcessedPointer) // Overflow?!!! error(2); } // Fuel (~15,3/s at 100km/h or ~3,8/s at 100km/h) void irqFuelTicks() { // -> Pin 4 fuelTicksIrqFiredPointer = (byte) ((fuelTicksIrqFiredPointer + 1) & (QUEUE_SIZE_FUEL - 1)); fuelTicksIrqFired[fuelTicksIrqFiredPointer] = millis(); if (fuelTicksIrqFiredPointer == fuelTicksIrqProcessedPointer) // Overflow?!!! error(4); } void setup() { lcdInitialise(); showState((byte) 0, (byte) 4); Wire.begin(); // RTC DS1307 showState((byte) 1, (byte) 4); loadEEPRomData(); // Load motorcycle-depending stuff that can be configured using the button on the bike showState((byte) 2, (byte) 4); loadNVRamData(); showState((byte) 3, (byte) 4); prepareInputsAndInterrupts(); showState((byte) 4, (byte) 4); menu = (byte) (storage.aboutOnStart ? PAGE_ABOUT : PAGE_MAIN); menuOld = (byte) 255; // Make sure the 'about'-screen will disappear after restart (java only?) after some seconds if enabled... } void showState(byte state, byte max) { for (byte a = 0; a < max; a++) for (byte b = 0; b < 8; b++) { byte pos = (byte) (LCD_X / 2 - 9 * max / 2 + a * 9 + b); boolean full = a < state || b == 0 || b == 7; frameBuffer[LCD_X * 2 + pos] |= (full ? 0xf0 : 0x10); frameBuffer[LCD_X * 3 + pos] |= (full ? 0x0f : 0x08); } drawFrameBuffer(); // No need to check checksum in this case... //*J*/ repaint(); //*J*/ delay(20); //*J*/ repaint(); //*J*/ Thread.yield(); } void loop() { processIrqs(); calculateSlotsData(); if (millis() - lastStored > 20000) { // Store NVRam-stuff every 20 seconds... saveNVRamData(); lastStored = millis(); } // Delayed stuff... if (specialOperationTime != 0 && millis() > specialOperationTime) { if (specialOperationValue == 1) { if (++configGear == 7) // After accepting and saving the gear ratio wait a little so the user recognizes that, then proceed configGear = 1; gearRatioAvg = gearRatio; // Using asymptotic approximation when shifting takes too long... } if (specialOperationValue == 2) { menu = PAGE_MAIN; // Display About for 2000ms after start } specialOperationTime = 0; specialOperationValue = 0; } // One cell per second if (millis() - lastTurn > 1000) { // The go to next memory cell historyPosCounter = (byte) ((historyPosCounter + 1) % HISTORY_WIDTH); historyLines[0][historyPosCounter] = (byte) (analogRead(PIN_ANALOG_FUELTANK) / 4); // 0..1023 -> 0..255 historyLines[1][historyPosCounter] = 0; historyLines[2][historyPosCounter] = 0; historyLines[3][historyPosCounter] = 0; lastTurn = millis(); } // Button: Debounce and short-/long-push-recognition... if (!oldButtonState && !pinStateOfButton) buttonPressed = millis(); if (oldButtonState && pinStateOfButton) buttonReleased = millis(); oldButtonState = !pinStateOfButton; if (buttonPressed != 0) { if (buttonReleased != 0) { /*C*/ if (buttonReleased - buttonPressed > 25) // 25ms seems to be a lot slower in Java...? And since we don't need debounce there anyway... shortClick = true; buttonPressed = 0; buttonReleased = 0; } else if (millis() - buttonPressed > 700) { longClick = true; buttonPressed = 0; } } else buttonReleased = 0; boolean configChanged = false; byte menuBuffered = menu; // "menu" can become modified from within the show*Page-functions! if (errorValue > 0) { // In case of an error display that over the "normal" background! showErrorMessage(); if (longClick) errorValue = 0; } else if (menu == PAGE_MAIN) { showMainPage(); if (shortClick) menu = PAGE_CLOCK; if (longClick) { configMenuCursorPosition = 0; menu = PAGE_CONFIG_MAIN; } } else if (menu == PAGE_CLOCK) { showClockPage(); if (shortClick) menu = PAGE_MAIN; if (longClick) { configMenuCursorPosition = 0; menu = PAGE_CONFIG_MAIN; } } else if (menu == PAGE_CONFIG_MAIN) { showConfigMainPage(); if (shortClick) configMenuCursorPosition = (configMenuCursorPosition + 1) % CONFIGPAGE_TOTAL; if (longClick) menu = (byte) readCharFromEeprom(CONFIGPAGE_ID, configMenuCursorPosition); } else if (menu == PAGE_CONFIG_SLOTS || menu == PAGE_CONFIG_PORTS || menu == PAGE_CONFIG_FUEL_TANK || menu == PAGE_CONFIG_CLOCK || menu == PAGE_CONFIG_CLOCK_OFFSET || menu == PAGE_CONFIG_ODOMETER || menu == PAGE_CONFIG_EXTENDED) { configChanged = showConfigPage(configChanged, menu); } else if (menu == PAGE_SLOTS) { showSlotsPage(); } else if (menu == PAGE_TRANSMISSION) { showTransmissionPage(); if (longClick) for (int x = 0; x < LCD_X; x++) line[x] = 0; if (shortClick) menu = PAGE_CONFIG_MAIN; } else if (menu == PAGE_TRANSMISSION_CONFIG) { showTransmissionConfigPage(); if (longClick) { storage.gearTransmissionRatio[configGear - 1] = (float) gearRatioAvg; // "1"-"6" are valid gears, "0" is "N". specialOperation((byte) 1, 700); // Show the value for some millis then proceed with next gear configChanged = true; // Save the config! } if (shortClick) menu = PAGE_CONFIG_MAIN; } else if (menu == PAGE_INFO_TICKS_OVERVIEW) { showTicksOverviewPage(); if (longClick) { // Reset counters... lastReset = millis(); motorTicksSinceSwitchingOn = 0; wheelTicksSinceSwitchingOn = 0; fuelTicksSinceSwitchingOn = 0; } if (shortClick) menu = PAGE_CONFIG_MAIN; } else if (menu == PAGE_INFO_TICKS_HISTORY) { showTicksHistoryPage(); if (longClick) showHistory = (char) ((showHistory + 1) % 4); // Next display... if (shortClick) menu = PAGE_CONFIG_MAIN; } else if (menu == PAGE_ABOUT) { showAboutPage(); if (shortClick) menu = PAGE_CONFIG_MAIN; } menuOld = menuBuffered; // "menu" could have been modified in the show*Page-functions! setPhysicalFuelGauge(); // Do this after the fuel-gauge configuration stuff or it won't simulate full or empty! shortClick = false; longClick = false; if (configChanged) saveEEPRomData(); if (storage.mirror) mirrorFrameBuffer(); // Only draw framebuffer if necessary to save transfer time int frameBufferChecksum = 0; for (int index = 0; index < LCD_X * LCD_Y / 8; index++) frameBufferChecksum = (((frameBufferChecksum << 1) & 0xfffe) | ((frameBufferChecksum >> 15) & 0x0001)) ^ frameBuffer[index]; if (lastFrameBufferChecksum != frameBufferChecksum) { drawFrameBuffer(); lastFrameBufferChecksum = frameBufferChecksum; } } /* Displays the menu with the titles of the secondary pages */ void showConfigMainPage() { // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; readToPrintBufferFromEeprom(CONST_SETTINGS); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); drawHorizontalLine((byte) 0, (byte) (LCD_X - 1), (byte) 7); int firstPos = configMenuCursorPosition < 2 ? 0 : (configMenuCursorPosition > CONFIGPAGE_TOTAL - 3 ? CONFIGPAGE_TOTAL - 5 : configMenuCursorPosition - 2); for (byte t = 0; t < 5; t++) { readToPrintBufferFromEepromArray(CONFIGPAGE_TITLE, firstPos + t); printChars(t + 1, ALIGN_LEFT, printBuffer, configMenuCursorPosition == firstPos + t ? INVERT_ALL : INVERT_NONE, false); } drawScrollbar(CONFIGPAGE_TOTAL, firstPos); } void drawScrollbar(int total, int position) { frameBuffer[LCD_X - 1 + (LCD_Y / 8 - 1) * LCD_X] ^= 0x80; int sbs = 9 + (LCD_Y - 11) * position / total; int sbe = 8 + (LCD_Y - 11) * (position + 5) / total; drawVerticalLine((byte) (LCD_X - 1), (byte) sbs, (byte) sbe); } /* Displays an error if there was one... */ void showErrorMessage() { // Flush error-message background clean, let the rest stay there! for (byte t = 0; t < 49; t++) frameBuffer[t] = (byte) 0x7f; frameBuffer[49] = (byte) 0x00; readToPrintBufferFromEeprom(CONST_ERROR); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); printLong(0, ALIGN_CENTER, errorValue, INVERT_NONE, false, (byte) 1); } /* Display: * Fuel in tank left * Actual gear * Kilometers/miles driven * Fuel consumption per kilometer * Kilometers/miles that can be driven at the actual consumption rate 'till the tank is empty */ void showMainPage() { // Show info on page entry if enabled if (storage.extendedInfos && menuOld != menu) specialOperationTime = millis() + 3000; // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; if (storage.extendedInfos && specialOperationTime != 0 && millis() < specialOperationTime) { for (byte b = 0; b < 6; b++) { readToPrintBufferFromEepromArray(SLOTOPTION_LABEL, storage.slot[b]); printChars(b, ALIGN_LEFT, printBuffer, INVERT_NONE, false); } } else { // Calculate gear from gear ratio only if needed calculateGear(gearRatio); drawDigitalFuelGauge(); // Draw the BIG gear-number if (gear >= 0) // Don't draw the big gear when "-1" printBigGear(); } // Draw slots - special case for distance-slots: Show an odometer only if the distance differs to the one above for (byte t = 0; t < 6; t++) { boolean same = false; if (t > 0 && (storage.slot[t] == SLOT_DIST_REFUELINGSTOP || storage.slot[t] == SLOT_DIST_TODAY || storage.slot[t] == SLOT_WHEEL_TOTAL || storage.slot[t] == SLOT_DIST_TOUR) && (storage.slot[t - 1] == SLOT_DIST_REFUELINGSTOP || storage.slot[t - 1] == SLOT_DIST_TODAY || storage.slot[t - 1] == SLOT_WHEEL_TOTAL || storage.slot[t - 1] == SLOT_DIST_TOUR) ) { long actualValue = 0; if (storage.slot[t] == SLOT_DIST_TOUR) actualValue = slotDistanceTrip; if (storage.slot[t] == SLOT_DIST_TODAY) actualValue = slotDistanceToday; if (storage.slot[t] == SLOT_DIST_REFUELINGSTOP) actualValue = slotDistanceSinceRefueling; if (storage.slot[t] == SLOT_WHEEL_TOTAL) actualValue = slotDistanceTotal; long lastValue = 0; if (storage.slot[t - 1] == SLOT_DIST_TOUR) lastValue = slotDistanceTrip; if (storage.slot[t - 1] == SLOT_DIST_TODAY) lastValue = slotDistanceToday; if (storage.slot[t - 1] == SLOT_DIST_REFUELINGSTOP) lastValue = slotDistanceSinceRefueling; if (storage.slot[t - 1] == SLOT_WHEEL_TOTAL) lastValue = slotDistanceTotal; if (actualValue == lastValue) same = true; } if (!same) printSlotOptionValue(t, storage.slot[t]); } } void drawDigitalFuelGauge() { int tmpBarVol = (int) (LCD_Y - slotFuelLeft * 2); if (tmpBarVol < 0) tmpBarVol = 0; // If more fuel than LCD_Y/2 for (byte y = (byte) (tmpBarVol / 8); y < LCD_Y / 8; y++) { for (byte x = 0; x < FUEL_GAUGE_LEN; x++) { byte shorter = (byte) 0xff; if (y == tmpBarVol / 8) shorter = (byte) (0xff << tmpBarVol % 8); frameBuffer[x + y * LCD_X] |= readCharFromEeprom(FUEL_GAUGE, x + y * FUEL_GAUGE_LEN) & shorter; } } } void setPhysicalFuelGauge() { int calculatedValue = (int) (storage.physicalFuelGaugeE * 2.58 + ((storage.physicalFuelGaugeF - storage.physicalFuelGaugeE) * 2.58 * slotFuelLeft / (storage.capacityOfFuelTank_MulTen / 10.0)) + .5); /*J if (fuelGaugeSetValueMethod == null) fetchFuelGaugeApplet(); else { try { fuelGaugeSetValueMethod.invoke(fuelGaugeApplet, calculatedValue); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } J*/ /*C*/ analogWrite(PIN_OUT_FUELCLOCK, calculatedValue); } /* Display logo, copyright, year and website plus config version number */ void showAboutPage() { // Show about on startup if (storage.aboutOnStart && menuOld == (byte) 255) // Startup specialOperation((byte) 2, 2000); // Init // Flush background with "About"-decal for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = (byte) readCharFromEeprom(ABOUT, t); for (byte b = 0; b < CONFIG_VERSION[2] - '0'; b++) drawPixel(0, LCD_Y - b * 2 - 1); } /* Display direct input history: * 0 = Analog fuel sensor value * 1 = Digital fuel flow sensor ticks * 2 = Motor ticks * 3 = Wheel ticks */ void showTicksHistoryPage() { // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; // Draw the line... if (showHistory == 0) { readToPrintBufferFromEeprom(CONST_TANK); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); } else if (showHistory == 1) { readToPrintBufferFromEeprom(CONST_FUEL); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); } else if (showHistory == 2) { readToPrintBufferFromEeprom(CONST_MOTOR); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); } else if (showHistory == 3) { readToPrintBufferFromEeprom(CONST_WHEEL); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); } for (char x = 0; x < HISTORY_WIDTH; x++) { int pos = historyLines[showHistory][x]; //*J*/ if (pos < 0) // On Arduino "byte" goes from 0 to 255, in Java it goes from -128 to 127. :( //*J*/ pos = 256 + pos; if (showHistory == 0) { // Analog tank pos = (byte) (pos * LCD_Y / 256); pos = pos > LCD_Y - 1 ? LCD_Y - 1 : pos; byte yMaj = (byte) (5 - pos / 8); byte yMin = (byte) (7 - pos % 8); frameBuffer[LCD_X - 1 - ((HISTORY_WIDTH + historyPosCounter - x) % HISTORY_WIDTH) + yMaj * LCD_X] ^= 1 << yMin; } else { if (showHistory == 2) // Motor ticks pos /= 5; pos = pos > HISTORY_HEIGHT - 1 ? HISTORY_HEIGHT - 1 : pos; byte yMaj = (byte) (((HISTORY_HEIGHT - 1) / 8) - pos / 8); byte yMin = (byte) (((HISTORY_HEIGHT - 1) % 8) - pos % 8); frameBuffer[LCD_X - 1 - ((HISTORY_WIDTH + historyPosCounter - x) % HISTORY_WIDTH) + yMaj * LCD_X] ^= 1 << yMin; } } if (showHistory == 0) { // Analog tank int end = fillPrintBufferWithLong((int) ((historyLines[showHistory][historyPosCounter] & 0xff) / 2.55 + .5), 0, (byte) 1); printBuffer[end] = ';'; printBuffer[end + 1] = '\0'; } else { // Motor, Wheel & Fuel ticks -- Can't use "historyPosCounter" because that one's always counting up... int end = fillPrintBufferWithLong(historyLines[showHistory][(historyPosCounter + HISTORY_WIDTH - 1) % HISTORY_WIDTH] & 0xff, 0, (byte) 1); printBuffer[end] = '/'; printBuffer[end + 1] = 's'; printBuffer[end + 2] = '\0'; } printChars(1, ALIGN_LEFT, printBuffer, INVERT_NONE, false); int queueSize[] = {QUEUE_SIZE_FUEL, QUEUE_SIZE_MOTOR, QUEUE_SIZE_WHEEL}; byte irqFiredPointer[] = {fuelTicksIrqFiredPointer, motorTicksIrqFiredPointer, wheelTicksIrqFiredPointer}; /*C*/ volatile long *ticksIrqFired //*J*/ long ticksIrqFired[] [] = {fuelTicksIrqFired, motorTicksIrqFired, wheelTicksIrqFired}; if (showHistory >= 1 && showHistory <= 3) { long minVal = 2100000000, maxVal = -2100000000; char pos = (char) (showHistory - 1); for (int t = 1; t < queueSize[pos]; t++) { long dist = ticksIrqFired[pos][(irqFiredPointer[pos] + t + 1) % queueSize[pos]] - ticksIrqFired[pos][(irqFiredPointer[pos] + t) % queueSize[pos]]; if (minVal > dist && dist >=0) // Define boundaries 0-999ms minVal = dist; if (maxVal < dist && dist <=999) // Define boundaries 0-999ms maxVal = dist; } long minMax = maxVal - minVal < max((maxVal + minVal) / 20, LCD_Y - HISTORY_HEIGHT - 2) ? (long) max((maxVal + minVal) / 20, LCD_Y - HISTORY_HEIGHT - 2) : maxVal - minVal; for (int t = 1; t < queueSize[pos]; t++) { long dist = ticksIrqFired[pos][(irqFiredPointer[pos] + t + 1) % queueSize[pos]] - ticksIrqFired[pos][(irqFiredPointer[pos] + t) % queueSize[pos]]; int x = LCD_X - HISTORY_WIDTH + HISTORY_WIDTH * t / queueSize[pos]; int y = (int) (HISTORY_HEIGHT + (LCD_Y - HISTORY_HEIGHT) / 2 - (dist - (minVal + maxVal) / 2) * (LCD_Y - HISTORY_HEIGHT - 2) / minMax); if (y > HISTORY_HEIGHT && y < LCD_Y) drawPixel(x, y); } int end = fillPrintBufferWithLong(maxVal, 0, (byte) 1); printBuffer[end] = 'm'; printBuffer[end + 1] = 's'; printBuffer[end + 2] = '\0'; printChars(4, ALIGN_LEFT, printBuffer, INVERT_NONE, false); end = fillPrintBufferWithLong(minVal, 0, (byte) 1); printBuffer[end] = 'm'; printBuffer[end + 1] = 's'; printBuffer[end + 2] = '\0'; printChars(5, ALIGN_LEFT, printBuffer, INVERT_NONE, false); } } /* Display raw inputs: * Seconds since boot-time * Seconds since last reset of this counters * Motor ticks * Wheel ticks * Fuel sensor ticks */ void showTicksOverviewPage() { // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; readToPrintBufferFromEeprom(CONST_ACTIVE); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); calculateCharsForClock(millis() / 1000); printChars(0, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); readToPrintBufferFromEeprom(CONST_ACTIVE); printChars(2, ALIGN_LEFT, printBuffer, INVERT_NONE, false); calculateCharsForClock((millis() - lastReset) / 1000); printChars(2, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); readToPrintBufferFromEeprom(CONST_MOTOR); printChars(3, ALIGN_LEFT, printBuffer, INVERT_NONE, false); printLong(3, ALIGN_RIGHT, motorTicksSinceSwitchingOn, INVERT_NONE, false, (byte) 1); readToPrintBufferFromEeprom(CONST_WHEEL); printChars(4, ALIGN_LEFT, printBuffer, INVERT_NONE, false); printLong(4, ALIGN_RIGHT, wheelTicksSinceSwitchingOn, INVERT_NONE, false, (byte) 1); readToPrintBufferFromEeprom(CONST_FUEL); printChars(5, ALIGN_LEFT, printBuffer, INVERT_NONE, false); printLong(5, ALIGN_RIGHT, fuelTicksSinceSwitchingOn, INVERT_NONE, false, (byte) 1); } // Gear transmission ratio void showTransmissionConfigPage() { // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; // First-time reset if (menuOld != menu) { configGear = 1; // Prepare for change in gear transmission ratio configuration... gearRatioAvg = gearRatio; // Using asymptotic approximation when shifting takes too long... } if (gearRatio <= 1) // Make sure we do not touch the negative-border! gearRatio = 0; gearRatioAvg = gearRatioAvg * .98 + gearRatio * .02; // Using asymptotic approximation; no time nor RAM for shifting arrays... // Gear configuration display readToPrintBufferFromEeprom(CONST_GEAR); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); printLong(0, ALIGN_RIGHT, configGear, INVERT_NONE, false, (byte) 1); drawVerticalBarWithXGraphDivider(1, storage.gearTransmissionRatio[configGear - 1]); printDouble(2, ALIGN_RIGHT, gearRatioAvg, INVERT_NONE, false, (byte) 2); drawVerticalBarWithXGraphDivider(2, gearRatioAvg); drawTransmissionLine(); } // Actual transmission ratio and the ratios of the configured gears void showTransmissionPage() { // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; // Calculate gear from gear ratio only if needed calculateGear(gearRatio); if (gearRatio <= 1) // Make sure we do not touch the negative-border! gearRatio = 0; // Normal "averages" display if (gear >= 1 && gear <= 6) { readToPrintBufferFromEeprom(CONST_GEAR); printChars(0, ALIGN_LEFT, printBuffer, INVERT_NONE, false); printLong(0, ALIGN_RIGHT, gear, INVERT_NONE, false, (byte) 1); } for (int t = 0; t < 6; t++) drawVerticalBarWithXGraphDivider(1, storage.gearTransmissionRatio[t]); drawTransmissionLine(); } void drawTransmissionLine() { gearRatio /= storage.xGraphDivider / 2.0; // For displaying and storing purposes... if (gearRatio >= LCD_X - 2) // Make sure the graph does not touch the positive border. gearRatio = LCD_X - 1; if (menuOld != menu) // Flush on entry for (int t = 0; t < LCD_X; t++) // Flush line line[t] = 0; // Draw a line... for (int x = 0; x < LCD_X; x++) { int yMaj = 5 - line[x] / 8; int yMin = 7 - line[x] % 8; frameBuffer[x + yMaj * LCD_X] ^= 1 << yMin; } line[(int) (gearRatio + .5)] += line[(int) (gearRatio + .5)] < LCD_Y - 1 ? 1 : -1; } // Display clock and date void showClockPage() { // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; syncClock(true); // Fetch actual date and time from RTC... fillPrintBufferWithClock(); printChars(0, ALIGN_CENTER, printBuffer, INVERT_NONE, false); printBigClock(); // Print the clock... readToPrintBufferFromEepromArray(WEEKDAY, clockWee); printChars(5, ALIGN_LEFT, printBuffer, INVERT_NONE, false); printBuffer[0] = ':'; printBuffer[1] = (char) (clockSec / 10 + '0'); printBuffer[2] = (char) (clockSec % 10 + '0'); printBuffer[3] = '\0'; printChars(5, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); } void showSlotsPage() { // First-time reset if (menuOld != menu) subPage = 0; // Start at first page! // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; for (int t = subPage * 6; t < (subPage + 1) * 6 && t < SLOT_OPTION_AMOUNT - 1; t++) { readToPrintBufferFromEepromArray(SLOTOPTION_LABEL, t + 1); printChars(t % 6, ALIGN_LEFT, printBuffer, INVERT_NONE, false); printSlotOptionValue(t % 6, t + 1); } if (shortClick) if (++subPage == (SLOT_OPTION_AMOUNT + 4) / 6) // Go to next page (or to menu if last (-"empty"!)) menu = PAGE_CONFIG_MAIN; } //*J*/ @SuppressWarnings({"UnusedAssignment"}) // for the last "row++" in the cases boolean showConfigPage(boolean configChanged, byte configSubPage) { // First-time reset if (menuOld != menu) cursorPosition = 0; // Flush background really clean for (int t = 0; t < frameBufferLen; t++) frameBuffer[t] = 0; int cPos = cursorPosition; int row = 0; switch (configSubPage) { case PAGE_CONFIG_SLOTS: for (byte t = 0; t < 6; t++) cPos = configSlotDisplaySlot(row++, cPos, t); break; case PAGE_CONFIG_PORTS: cPos = configSlotTankTicksPerVolume(row++, cPos); cPos = configSlotRefuellingStopRecognitionAmount(row++, cPos); cPos = configSlotWheelCircumference(row++, cPos); break; case PAGE_CONFIG_FUEL_TANK: cPos = configSlotTankVolume(row++, cPos); cPos = configSlotPhysicalFuelGaugeE(row++, cPos); cPos = configSlotPhysicalFuelGaugeF(row++, cPos); break; case PAGE_CONFIG_CLOCK: cPos = configSlotClockDate(row++, cPos); cPos = configSlotClockTime(row++, cPos); cPos = configSlotSummertimeAutoswitch(row++, cPos); break; case PAGE_CONFIG_CLOCK_OFFSET: cPos = showSlotClockDate(row++, cPos); cPos = showSlotClockTime(row++, cPos); cPos = configSlotClockOffset(row++, cPos); break; case PAGE_CONFIG_ODOMETER: cPos = configSlotSetTotalDistance(row++, cPos); cPos = configSlotResetTotalDistance(row++, cPos); break; case PAGE_CONFIG_EXTENDED: cPos = configSlotTooltips(row++, cPos); cPos = configSlotTransmissionXDivider(row++, cPos); cPos = configSlotUpsideDown(row++, cPos); cPos = configSlotAboutOnStart(row++, cPos); break; } if (cPos == 0) menu = PAGE_CONFIG_MAIN; if (!selected && shortClick) cursorPosition++; // Go to next position (or exit if last) if (longClick) { selected = !selected; // Select/Deselect for editing... if (!selected) configChanged = true; // Save the config! } return configChanged; } // Slots for displaying data on the right side of the main page int configSlotDisplaySlot(int row, int cPos, int slot) { int usedCursorSlots = 1; // This function consumes x cursor slots printBuffer[0] = 's'; printBuffer[1] = 'l'; printBuffer[2] = 'o'; printBuffer[3] = 't'; printBuffer[4] = ' '; printBuffer[5] = (char) ('1' + slot); printBuffer[6] = '\0'; printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) storage.slot[slot] = (byte) ((storage.slot[slot] + 1) % SLOT_OPTION_AMOUNT); readToPrintBufferFromEepromArray(SLOTOPTION_LABEL, storage.slot[slot]); printChars(row, ALIGN_RIGHT, printBuffer, INVERT_ALL, selected); } else { readToPrintBufferFromEepromArray(SLOTOPTION_LABEL, storage.slot[slot]); printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, selected); } return cPos - usedCursorSlots; } // Time int configSlotClockTime(int row, int cPos) { int usedCursorSlots = 6; // This function consumes x cursor slots readToPrintBufferFromEeprom(CONST_TIME); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); syncClock(false); // Fetch actual date and time from RTC... calculateCharsForClock(clockHou * 3600L + clockMin * 60 + clockSec); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) { if (cPos == 0) setClockHou((byte) ((clockHou + 10) % 100)); else if (cPos == 1) setClockHou((byte) (clockHou / 10 * 10 + ((clockHou + 1) % 10))); else if (cPos == 2) setClockMin((byte) ((clockMin + 10) % 100)); else if (cPos == 3) setClockMin((byte) (clockMin / 10 * 10 + ((clockMin + 1) % 10))); else if (cPos == 4) setClockSec((byte) ((clockSec + 10) % 100)); else if (cPos == 5) setClockSec((byte) (clockSec / 10 * 10 + ((clockSec + 1) % 10))); storeClockChangeTime(); } byte cursorPos = (byte) (cPos > 1 ? (cPos > 3 ? cPos + 2 : cPos + 1) : cPos); printChars(row, ALIGN_RIGHT, printBuffer, cursorPos, selected); } else printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); return cPos - usedCursorSlots; } // Date int configSlotClockDate(int row, int cPos) { int usedCursorSlots = 6; // This function consumes x cursor slots readToPrintBufferFromEeprom(CONST_DATE); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); syncClock(false); // Fetch actual date and time from RTC... fillPrintBufferWithClock(); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) { if (cPos == 0) setClockDay((byte) ((clockDay + 10) % 100)); else if (cPos == 1) setClockDay((byte) (clockDay / 10 * 10 + ((clockDay + 1) % 10))); else if (cPos == 2) setClockMon((byte) ((clockMon + 10) % 100)); else if (cPos == 3) setClockMon((byte) (clockMon / 10 * 10 + ((clockMon + 1) % 10))); else if (cPos == 4) setClockYea((byte) ((clockYea + 10) % 100)); else if (cPos == 5) setClockYea((byte) (clockYea / 10 * 10 + ((clockYea + 1) % 10))); storeClockChangeTime(); } byte cursorPos = (byte) (cPos > 1 ? (cPos > 3 ? cPos + 2 : cPos + 1) : cPos); printChars(row, ALIGN_RIGHT, printBuffer, cursorPos, selected); } else printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); return cPos - usedCursorSlots; } // Clock offset int configSlotClockOffset(int row, int cPos) { int usedCursorSlots = 5; // This function consumes x cursor slots readToPrintBufferFromEeprom(CONST_OFFSET_PER_DAY); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); int value = storage.clockOffset_MulThousand; boolean negative = false; if (value < 0) { negative = true; value = -value; } printBuffer[0] = negative ? '-' : '+'; printBuffer[1] = (char) (value / 1000 % 10 + '0'); printBuffer[2] = ','; printBuffer[3] = (char) (value / 100 % 10 + '0'); printBuffer[4] = (char) (value / 10 % 10 + '0'); printBuffer[5] = (char) (value % 10 + '0'); printBuffer[6] = '\0'; if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) { if (cPos == 0) value = -value; else if (cPos == 1) { int oldPosVal = value / 1000 % 10 * 1000; int newPosVal = (value / 1000 + 1) % 10 * 1000; value = value - oldPosVal + newPosVal; } else if (cPos == 2) { int oldPosVal = value / 100 % 10 * 100; int newPosVal = (value / 100 + 1) % 10 * 100; value = value - oldPosVal + newPosVal; } else if (cPos == 3) { byte oldPosVal = (byte) (value / 10 % 10 * 10); byte newPosVal = (byte) ((value / 10 + 1) % 10 * 10); value = value - oldPosVal + newPosVal; } else if (cPos == 4) { byte oldPosVal = (byte) (value % 10); byte newPosVal = (byte) ((value + 1) % 10); value = value - oldPosVal + newPosVal; } if (negative) // Restore algebraic sign! value = -value; storage.clockOffset_MulThousand = value; } byte cursorPos = (byte) (cPos > 1 ? cPos + 1 : cPos); printChars(row, ALIGN_RIGHT, printBuffer, cursorPos, selected); } else printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); return cPos - usedCursorSlots; } // Time int showSlotClockTime(int row, int cPos) { readToPrintBufferFromEeprom(CONST_TIME); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); syncClock(true); // Fetch actual date and time from RTC... calculateCharsForClock(clockHou * 3600L + clockMin * 60 + clockSec); printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); return cPos; } // Date int showSlotClockDate(int row, int cPos) { readToPrintBufferFromEeprom(CONST_DATE); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); syncClock(true); // Fetch actual date and time from RTC... fillPrintBufferWithClock(); printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); return cPos; } // Reset total distance counter int configSlotResetTotalDistance(int row, int cPos) { int usedCursorSlots = 1; // This function consumes x cursor slots readToPrintBufferFromEeprom(SLOTOPTION_LABEL_WHEEL_TOTAL); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) wheelTicksTotal = 0; readToPrintBufferFromEeprom(CONST_RESET); printChars(row, ALIGN_RIGHT, printBuffer, INVERT_ALL, selected); } else { readToPrintBufferFromEeprom(CONST_RESET); printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, selected); } return cPos - usedCursorSlots; } // Divider for x-dimension on transmission-pages int configSlotTransmissionXDivider(int row, int cPos) { int usedCursorSlots = 1; // This function consumes x cursor slots readToPrintBufferFromEeprom(CONST_GEAR_X_AXIS_DIVIDER); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) storage.xGraphDivider = (byte) ((storage.xGraphDivider + 1) % 10); printLong(row, ALIGN_RIGHT, storage.xGraphDivider, (byte) cPos, selected, (byte) usedCursorSlots); } else printLong(row, ALIGN_RIGHT, storage.xGraphDivider, INVERT_NONE, false, (byte) 1); return cPos - usedCursorSlots; } // Minimum amount of "more fuel" on startup to positively recognize a refuelling stop int configSlotRefuellingStopRecognitionAmount(int row, int cPos) { int usedCursorSlots = 2; // This function consumes x cursor slots readToPrintBufferFromEeprom(CONST_REFUELLINGSTOP_MIN_DIFFERENCE); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) { long pos = 1; // Using "pow" instead does not work properly because that one makes calculation mistakes (10^2 -> 99)... :( for (int t = cPos; t < usedCursorSlots - 1; t++) pos *= 10; long oldPosVal = storage.refuellingStopRecognitionAmount / pos % 10 * pos; long newPosVal = (storage.refuellingStopRecognitionAmount / pos + 1) % 10 * pos; storage.refuellingStopRecognitionAmount = (byte) (storage.refuellingStopRecognitionAmount - oldPosVal + newPosVal); } int end = fillPrintBufferWithLong(storage.refuellingStopRecognitionAmount & 0xff, 0, (byte) usedCursorSlots); printBuffer[end] = ';'; printBuffer[end + 1] = '\0'; printChars(row, ALIGN_RIGHT, printBuffer, (byte) cPos, selected); } else { int end = fillPrintBufferWithLong(storage.refuellingStopRecognitionAmount & 0xff, 0, (byte) 1); printBuffer[end] = ';'; printBuffer[end + 1] = '\0'; printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, false); } return cPos - usedCursorSlots; } // Set total distance counter int configSlotSetTotalDistance(int row, int cPos) { int usedCursorSlots = 5; // This function consumes x cursor slots readToPrintBufferFromEeprom(SLOTOPTION_LABEL_WHEEL_TOTAL); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) { long pos = 1; // Using "pow" instead does not work properly because that one makes calculation mistakes (10^2 -> 99)... :( for (int t = cPos; t < usedCursorSlots - 1; t++) pos *= 10; long oldPosVal = slotDistanceTotal / pos % 10 * pos; long newPosVal = (slotDistanceTotal / pos + 1) % 10 * pos; slotDistanceTotal = slotDistanceTotal - oldPosVal + newPosVal; wheelTicksTotal = (slotDistanceTotal * 1000000) / storage.circumferenceOfWheel; } printLong(row, ALIGN_RIGHT, slotDistanceTotal, (byte) cPos, selected, (byte) usedCursorSlots); } else printLong(row, ALIGN_RIGHT, slotDistanceTotal, INVERT_NONE, false, (byte) 1); return cPos - usedCursorSlots; } // What fits into the fuel tank int configSlotTankVolume(int row, int cPos) { int usedCursorSlots = 3; // This function consumes x cursor slots readToPrintBufferFromEeprom(CONST_TANK_VOLUME); printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) { int pos = 1; // Using "pow" instead does not work properly because that one makes calculation mistakes (10^2 -> 99)... :( for (int t = cPos; t < usedCursorSlots - 1; t++) pos *= 10; int oldPosVal = storage.capacityOfFuelTank_MulTen / pos % 10 * pos; int newPosVal = (storage.capacityOfFuelTank_MulTen / pos + 1) % 10 * pos; storage.capacityOfFuelTank_MulTen = storage.capacityOfFuelTank_MulTen - oldPosVal + newPosVal; } byte cursorPos = (byte) (cPos < usedCursorSlots - 1 ? cPos : cPos + 1); printDouble(row, ALIGN_RIGHT, storage.capacityOfFuelTank_MulTen / 10.0, cursorPos, selected, (byte) usedCursorSlots); } else printDouble(row, ALIGN_RIGHT, storage.capacityOfFuelTank_MulTen / 10.0, INVERT_NONE, false, (byte) 2); return cPos - usedCursorSlots; } // Display or edit a ConfigSlot long configSlotValueLong(int row, int cPos, long value, byte usedCursorSlots) { printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); if (cPos >= 0 && cPos < usedCursorSlots) { if (selected && shortClick) { long pos = 1; // Using "pow" instead does not work properly because that one makes calculation mistakes (10^2 -> 99)... :( for (int t = cPos; t < usedCursorSlots - 1; t++) pos *= 10; long oldPosVal = value / pos % 10 * pos; long newPosVal = (value / pos + 1) % 10 * pos; value = value - oldPosVal + newPosVal; } printLong(row, ALIGN_RIGHT, value, (byte) cPos, selected, usedCursorSlots); } else printLong(row, ALIGN_RIGHT, value, INVERT_NONE, false, (byte) 1); return value; } // Value that brings the an analog fuel gauge to "E" int configSlotPhysicalFuelGaugeE(int row, int cPos) { byte usedCursorSlots = 2; // This function consumes x cursor slots if (cPos >= 0 && cPos < usedCursorSlots && selected) // Simulate an empty fuel tank slotFuelLeft = 0; readToPrintBufferFromEeprom(CONST_PHYSICAL_FUEL_GAUGE_E); storage.physicalFuelGaugeE = (byte) configSlotValueLong(row, cPos, storage.physicalFuelGaugeE, usedCursorSlots); return cPos - usedCursorSlots; } // Value that brings the an analog fuel gauge to "F" int configSlotPhysicalFuelGaugeF(int row, int cPos) { byte usedCursorSlots = 2; // This function consumes x cursor slots if (cPos >= 0 && cPos < usedCursorSlots && selected) // Simulate a full fuel tank slotFuelLeft = storage.capacityOfFuelTank_MulTen / 10.0; readToPrintBufferFromEeprom(CONST_PHYSICAL_FUEL_GAUGE_F); storage.physicalFuelGaugeF = (byte) configSlotValueLong(row, cPos, storage.physicalFuelGaugeF, usedCursorSlots); return cPos - usedCursorSlots; } // Ticks of fuel sensor per liter/gallon int configSlotTankTicksPerVolume(int row, int cPos) { byte usedCursorSlots = 5; // This function consumes x cursor slots readToPrintBufferFromEeprom(CONST_FUEL_IMPULSES); storage.ticksPerFuelUnit = configSlotValueLong(row, cPos, storage.ticksPerFuelUnit, usedCursorSlots); return cPos - usedCursorSlots; } // Circumference of the measured wheel in mm or 1/1000 of a mile int configSlotWheelCircumference(int row, int cPos) { byte usedCursorSlots = 4; // This function consumes x cursor slots readToPrintBufferFromEeprom(CONST_WHEEL_CIRCUMFERENCE); storage.circumferenceOfWheel = (int) configSlotValueLong(row, cPos, storage.circumferenceOfWheel, usedCursorSlots); return cPos - usedCursorSlots; } // Display or edit a ConfigSlot boolean configSlotValueBoolean(int row, int cPos, boolean value) { printChars(row, ALIGN_LEFT, printBuffer, INVERT_NONE, false); if (cPos >= 0 && cPos < 1) { // This function consumes x cursor slots if (selected && shortClick) value = !value; readToPrintBufferFromEeprom(value ? CONST_YES : CONST_NO); printChars(row, ALIGN_RIGHT, printBuffer, INVERT_ALL, selected); } else { readToPrintBufferFromEeprom(value ? CONST_YES : CONST_NO); printChars(row, ALIGN_RIGHT, printBuffer, INVERT_NONE, selected); } return value; } // Upside-down int configSlotUpsideDown(int row, int cPos) { readToPrintBufferFromEeprom(CONST_HEADFIRST); storage.mirror = configSlotValueBoolean(row, cPos, storage.mirror); return cPos - 1; // This function consumes x cursor slots } // Show splash-screen on startup int configSlotAboutOnStart(int row, int cPos) { readToPrintBufferFromEeprom(CONST_SPLASHSCREEN); storage.aboutOnStart = configSlotValueBoolean(row, cPos, storage.aboutOnStart); return cPos - 1; // This function consumes x cursor slots } // Automatically switch to daylight savings time int configSlotSummertimeAutoswitch(int row, int cPos) { readToPrintBufferFromEeprom(CONST_AUTOMATIC_DAYLIGHT_SAVING_TIME); storage.summertimeSwitch = configSlotValueBoolean(row, cPos, storage.summertimeSwitch); return cPos - 1; // This function consumes x cursor slots } // Extended infos int configSlotTooltips(int row, int cPos) { readToPrintBufferFromEeprom(CONST_MORE_INFOS); storage.extendedInfos = configSlotValueBoolean(row, cPos, storage.extendedInfos); return cPos - 1; // This function consumes x cursor slots } //*J*/ }