// ======================================================================= // Manage an Arduboy's unit name and ID stored in the system EEPROM area // ======================================================================= /* ---------------------------------------------------------------------------- The area in EEPROM reserved by the library for global system use includes space to store a Unit Name and Unit ID. These can be read by sketches, using the readUnitName() and readUnitID() functions. Also, the Arduboy2 class will display the Unit Name at the end of the boot logo sequence, if possible, during start up. This sketch allows the Unit Name and Unit ID to be set or changed and saved. ---------------------------------------------------------------------------- */ // Version 2.0 /* ------------------------------------------------------------------------------ Copyright (c) 2017, Scott Allen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ */ #include // The frame rate determines the button auto-repeat rate for unit name entry #define FRAME_RATE 10 // The unit ID auto-repeat rate is slowed, compared to the unit name rate, by // repeating only once per the defined number of frames #define ID_REPEAT_FRAMES 3 // Delay time before button auto-repeat starts, in milliseconds #define REPEAT_DELAY 700 // All the constant stings const char StrName[] PROGMEM = "NAME"; const char StrID[] PROGMEM = "ID"; const char StrYes[] PROGMEM = "YES"; const char StrNo[] PROGMEM = "NO"; const char StrSaveQ[] PROGMEM = "SAVE?"; const char StrSaved[] PROGMEM = "SAVED"; const char StrShowNameQ1[] PROGMEM = "Show Unit Name"; const char StrShowNameQ2[] PROGMEM = "on logo screen?"; const char StrBtnChangeName[] PROGMEM = "UP:change Unit Name"; const char StrBtnChangeID[] PROGMEM = "DOWN:change Unit ID"; const char StrBtnShowName[] PROGMEM = "LEFT:set \"show name\""; const char StrBtnMenu[] PROGMEM = "A:menu"; const char StrBtnSave[] PROGMEM = "B:save"; const char StrBtnYes[] PROGMEM = "A:yes"; const char StrBtnNo[] PROGMEM = "B:no"; const char StrBtnTestLogo[] PROGMEM = "DOWN:test boot logo"; const char StrHex[] PROGMEM = "hex"; const char StrDecimal[] PROGMEM = "decimal"; #define CHAR_WIDTH 6 #define CHAR_HEIGHT 8 #define SMALL_SPACE 4 // The number of pixels for a small space between groups // Defines for text and field locations #define MENU_BTN_CHANGE_NAME_X 0 #define MENU_BTN_CHANGE_NAME_Y 0 #define MENU_NAME_X centerStrLen(ARDUBOY_UNIT_NAME_LEN) #define MENU_NAME_Y (MENU_BTN_CHANGE_NAME_Y + CHAR_HEIGHT + 3) #define MENU_BTN_CHANGE_ID_X 0 #define MENU_BTN_CHANGE_ID_Y 26 #define MENU_HEADING_HEX_X (centerStr_P(StrHex) - (WIDTH / 4)) #define MENU_HEADING_DECIMAL_X (centerStr_P(StrDecimal) + (WIDTH / 4)) #define MENU_HEADINGS_Y (MENU_BTN_CHANGE_ID_Y + CHAR_HEIGHT + 1) #define MENU_ID_HEX_X (centerStrLen(5) - (WIDTH / 4)) #define MENU_ID_DECIMAL_X (centerStrLen(5) + (WIDTH / 4)) #define MENU_ID_Y (MENU_HEADINGS_Y + CHAR_HEIGHT + 1) #define MENU_BTN_SHOW_NAME_X 0 #define MENU_BTN_SHOW_NAME_Y 56 #define NAME_TITLE_X centerStr_P(StrName) #define NAME_TITLE_Y 0 #define NAME_BTN_MENU_X 0 #define NAME_BTN_MENU_Y 0 #define NAME_BTN_SAVE_X rightStr_P(StrBtnSave) #define NAME_BTN_SAVE_Y NAME_BTN_MENU_Y #define NAME_LARGE_X centerStrLen(ARDUBOY_UNIT_NAME_LEN * 2) #define NAME_LARGE_Y 11 #define NAME_HEX_X 0 #define NAME_HEX_Y 40 #define NAME_DECIMAL_X 0 #define NAME_DECIMAL_Y 54 #define NAME_BTN_YES_X 0 #define NAME_BTN_YES_Y 0 #define NAME_BTN_NO_X rightStr_P(StrBtnNo) #define NAME_BTN_NO_Y NAME_BTN_YES_Y #define NAME_SAVE_Q_X (centerStr_P(StrSaveQ) - ((6 * CHAR_WIDTH) + (CHAR_WIDTH / 2))) #define NAME_SAVE_Q_Y (NAME_LARGE_Y + (CHAR_HEIGHT / 2) + 3) #define NAME_SAVE_X (NAME_SAVE_Q_X + ((strlen_P(StrSaveQ) * CHAR_WIDTH) + CHAR_WIDTH)) #define NAME_SAVE_Y (NAME_LARGE_Y + 3) #define ID_TITLE_X centerStr_P(StrID) #define ID_TITLE_Y 0 #define ID_BTN_MENU_X 0 #define ID_BTN_MENU_Y 0 #define ID_BTN_SAVE_X rightStr_P(StrBtnSave) #define ID_BTN_SAVE_Y ID_BTN_MENU_Y #define ID_LARGE_X centerStrLen(5 * 2) #define ID_LARGE_Y 13 #define ID_2_DECIMAL_X 12 #define ID_2_DECIMAL_Y 38 #define ID_DECIMAL_X (WIDTH - (CHAR_WIDTH * 5 + 12)) #define ID_DECIMAL_Y ID_2_DECIMAL_Y #define ID_BINARY_X 0 #define ID_BINARY_Y 54 #define ID_BTN_YES_X 0 #define ID_BTN_YES_Y 0 #define ID_BTN_NO_X rightStr_P(StrBtnNo) #define ID_BTN_NO_Y ID_BTN_YES_Y #define ID_SAVE_Q_X (centerStr_P(StrSaveQ) - ((5 * CHAR_WIDTH) + (CHAR_WIDTH / 2))) #define ID_SAVE_Q_Y (ID_LARGE_Y + (CHAR_HEIGHT / 2) + 1) #define ID_SAVE_X (ID_SAVE_Q_X + ((strlen_P(StrSaveQ) * CHAR_WIDTH) + CHAR_WIDTH)) #define ID_SAVE_Y (ID_LARGE_Y + 1) #define SHOW_NAME_BTN_MENU_X 0 #define SHOW_NAME_BTN_MENU_Y 0 #define SHOW_NAME_BTN_SAVE_X rightStr_P(StrBtnSave) #define SHOW_NAME_BTN_SAVE_Y SHOW_NAME_BTN_MENU_Y #define SHOW_NAME_Q_1_X centerStr_P(StrShowNameQ1) #define SHOW_NAME_Q_1_Y 12 #define SHOW_NAME_Q_2_X centerStr_P(StrShowNameQ2) #define SHOW_NAME_Q_2_Y (SHOW_NAME_Q_1_Y + 8) #define SHOW_NAME_YES_X ((WIDTH / 2) - ((strlen_P(StrYes) + 1) * CHAR_WIDTH * 2)) #define SHOW_NAME_YES_Y 34 #define SHOW_NAME_NO_X ((WIDTH / 2) + (CHAR_WIDTH * 2)) #define SHOW_NAME_NO_Y SHOW_NAME_YES_Y #define SHOW_NAME_TEST_X 0 #define SHOW_NAME_TEST_Y 56 #define SHOW_NAME_SAVED_X centerStr2_P(StrSaved) #define SHOW_NAME_SAVED_Y ((HEIGHT / 2) - CHAR_HEIGHT) // Calculation of the number of frames to wait before button auto-repeat starts #define DELAY_FRAMES (REPEAT_DELAY / (1000 / FRAME_RATE)) // The Arduino "magic" has trouble creating prototypes for functions called // by pointers, so they're declared here manually void stateMain(), stateName(), stateID(), stateShowName(); void stateSaveName(), stateSaveID(); void screenMain(), screenName(), screenID(), screenShowName(); void screenSaveName(), screenSaveID(); Arduboy2 arduboy; char unitName[ARDUBOY_UNIT_NAME_LEN + 1]; byte nameIndex; uint16_t unitID; byte idIndex; boolean showNameFlag; // Assign numbers for each state/screen enum State : byte { sMain, sName, sID, sShowName, sSaveName, sSaveID, sMAX = sSaveID }; byte currentState; // Function pointer array for button handling void (*stateFunc[sMAX + 1])() = { stateMain, stateName, stateID, stateShowName, stateSaveName, stateSaveID }; // Function pointer array for screen drawing void (*screenFunc[sMAX + 1])() = { screenMain, screenName, screenID, screenShowName, screenSaveName, screenSaveID }; unsigned int delayCount = 0; boolean repeating = false; // ============================= SETUP =================================== void setup() { arduboy.begin(); arduboy.setFrameRate(FRAME_RATE); setState(sMain); } // ======================================================================= // =========================== MAIN LOOP ================================= void loop() { if (!arduboy.nextFrame()) { return; } arduboy.pollButtons(); (*stateFunc[currentState])(); if ((delayCount != 0) && (--delayCount == 0)) { repeating = true; } } // ======================================================================= // ------------------------- Program States ------------------------------ // Set to the given state and display the screen for that state // Can be called with the current state to update the current screen void setState(byte newState) { currentState = newState; stopButtonRepeat(); drawScreen(); } // STATE: Main selection screen void stateMain() { if (arduboy.justPressed(UP_BUTTON)) { setState(sName); } else if (arduboy.justPressed(DOWN_BUTTON)) { setState(sID); } else if (arduboy.justPressed(LEFT_BUTTON)) { setState(sShowName); } } // STATE: Change unit name void stateName() { if (arduboy.justPressed(UP_BUTTON)) { nameCharInc(); startButtonDelay(); } else if (arduboy.justPressed(DOWN_BUTTON)) { nameCharDec(); startButtonDelay(); } else if (repeating && arduboy.pressed(UP_BUTTON)) { nameCharInc(); } else if (repeating && arduboy.pressed(DOWN_BUTTON)) { nameCharDec(); } else if (arduboy.justPressed(RIGHT_BUTTON)) { nameCursorRight(); } else if (arduboy.justPressed(LEFT_BUTTON)) { nameCursorLeft(); } else if (arduboy.justPressed(A_BUTTON)) { setState(sMain); } else if (arduboy.justPressed(B_BUTTON)) { setState(sSaveName); } else if (repeating) { stopButtonRepeat(); } } // STATE: Change unit ID void stateID() { if (arduboy.justPressed(UP_BUTTON)) { idDigitInc(); startButtonDelay(); } else if (arduboy.justPressed(DOWN_BUTTON)) { idDigitDec(); startButtonDelay(); } else if (repeating && arduboy.pressed(UP_BUTTON)) { if (arduboy.everyXFrames(ID_REPEAT_FRAMES)) { idDigitInc(); } } else if (repeating && arduboy.pressed(DOWN_BUTTON)) { if (arduboy.everyXFrames(ID_REPEAT_FRAMES)) { idDigitDec(); } } else if (arduboy.justPressed(RIGHT_BUTTON)) { idCursorRight(); } else if (arduboy.justPressed(LEFT_BUTTON)) { idCursorLeft(); } else if (arduboy.justPressed(A_BUTTON)) { setState(sMain); } else if (arduboy.justPressed(B_BUTTON)) { setState(sSaveID); } else if (repeating) { stopButtonRepeat(); } } // STATE: Set "Show Unit Name" flag void stateShowName() { if (arduboy.justPressed(RIGHT_BUTTON)) { showNameToggle(); } else if (arduboy.justPressed(LEFT_BUTTON)) { showNameToggle(); } else if (arduboy.justPressed(A_BUTTON)) { setState(sMain); } else if (arduboy.justPressed(B_BUTTON)) { saveShowName(); setState(sShowName); } else if (arduboy.justPressed(DOWN_BUTTON)) { showNameFlag = arduboy.readShowUnitNameFlag(); arduboy.bootLogo(); delay(1000); setState(sShowName); } } // STATE: Prompt to save the unit name void stateSaveName() { if (arduboy.justPressed(A_BUTTON)) { arduboy.writeUnitName(unitName); setState(sMain); } else if (arduboy.justPressed(B_BUTTON)) { setState(sName); } } // STATE: Prompt to save the unit ID void stateSaveID() { if (arduboy.justPressed(A_BUTTON)) { arduboy.writeUnitID(unitID); setState(sMain); } else if (arduboy.justPressed(B_BUTTON)) { setState(sID); } } // ------------------------- Screen Display ------------------------------ // Display the screen for the current state void drawScreen() { arduboy.clear(); (*screenFunc[currentState])(); arduboy.display(); } // DISPLAY: Main screen void screenMain() { readEEPROM(); nameIndex = idIndex = 0; printStr_P(MENU_BTN_CHANGE_NAME_X, MENU_BTN_CHANGE_NAME_Y, StrBtnChangeName); printName(MENU_NAME_X, MENU_NAME_Y); printStr_P(MENU_BTN_CHANGE_ID_X, MENU_BTN_CHANGE_ID_Y, StrBtnChangeID); printStr_P(MENU_HEADING_HEX_X, MENU_HEADINGS_Y, StrHex); printStr_P(MENU_HEADING_DECIMAL_X, MENU_HEADINGS_Y, StrDecimal); printIDHex(MENU_ID_HEX_X, MENU_ID_Y); printIDDecimal(MENU_ID_DECIMAL_X, MENU_ID_Y); printStr_P(MENU_BTN_SHOW_NAME_X, MENU_BTN_SHOW_NAME_Y, StrBtnShowName); } // DISPLAY: Change unit name void screenName() { printNameScreenCommon(); printStr_P(NAME_BTN_MENU_X, NAME_BTN_MENU_Y, StrBtnMenu); printStr_P(NAME_BTN_SAVE_X, NAME_BTN_SAVE_Y, StrBtnSave); printNameLarge(NAME_LARGE_X, NAME_LARGE_Y); printNameUnderline(NAME_LARGE_X, NAME_LARGE_Y); printNameCursors(); } // DISPLAY: Change unit ID void screenID() { printIDScreenCommon(); printStr_P(ID_BTN_MENU_X, ID_BTN_MENU_Y, StrBtnMenu); printStr_P(ID_BTN_SAVE_X, ID_BTN_SAVE_Y, StrBtnSave); printIDLarge(ID_LARGE_X, ID_LARGE_Y); printIDCursors(); } // DISPLAY: Set "Show Unit Name" flag void screenShowName() { printStr_P(SHOW_NAME_BTN_MENU_X, SHOW_NAME_BTN_MENU_Y, StrBtnMenu); printStr_P(SHOW_NAME_BTN_SAVE_X, SHOW_NAME_BTN_SAVE_Y, StrBtnSave); printStr_P(SHOW_NAME_Q_1_X, SHOW_NAME_Q_1_Y, StrShowNameQ1); printStr_P(SHOW_NAME_Q_2_X, SHOW_NAME_Q_2_Y, StrShowNameQ2); arduboy.setTextSize(2); printStr_P(SHOW_NAME_YES_X, SHOW_NAME_YES_Y, StrYes); printStr_P(SHOW_NAME_NO_X, SHOW_NAME_NO_Y, StrNo); arduboy.setTextSize(1); printShowNameCursor(); printStr_P(SHOW_NAME_TEST_X, SHOW_NAME_TEST_Y, StrBtnTestLogo); } // DISPLAY: Prompt to save the unit name void screenSaveName() { printNameScreenCommon(); printStr_P(NAME_BTN_YES_X, NAME_BTN_YES_Y, StrBtnYes); printStr_P(NAME_BTN_NO_X, NAME_BTN_NO_Y, StrBtnNo); printSavePrompt(NAME_SAVE_Q_X, NAME_SAVE_Q_Y); printNameLarge(NAME_SAVE_X, NAME_SAVE_Y); } // DISPLAY: Prompt to save the unit ID void screenSaveID() { printIDScreenCommon(); printStr_P(ID_BTN_YES_X, ID_BTN_YES_Y, StrBtnYes); printStr_P(ID_BTN_NO_X, ID_BTN_NO_Y, StrBtnNo); printSavePrompt(ID_SAVE_Q_X, ID_SAVE_Q_Y); printIDLarge(ID_SAVE_X, ID_SAVE_Y); } // Save the "Show Unit Name" flag and overlay the "SAVED" message on the screen void saveShowName() { arduboy.writeShowUnitNameFlag(showNameFlag); arduboy.fillRect(SHOW_NAME_SAVED_X - 4, SHOW_NAME_SAVED_Y - 4, strlen_P(StrSaved) * CHAR_WIDTH * 2 + 6, CHAR_HEIGHT * 2 + 6); arduboy.setTextColor(BLACK); arduboy.setTextBackground(WHITE); arduboy.setTextSize(2); printStr_P(SHOW_NAME_SAVED_X, SHOW_NAME_SAVED_Y, StrSaved); arduboy.setTextSize(1); arduboy.setTextColor(WHITE); arduboy.setTextBackground(BLACK); arduboy.display(); delay(1000); } // --------------------- Printing Functions ------------------------------ // Print the name entry screen common information void printNameScreenCommon() { printStr_P(NAME_TITLE_X, NAME_TITLE_Y, StrName); printNameHex(NAME_HEX_X, NAME_HEX_Y); printNameDecimal(NAME_DECIMAL_X, NAME_DECIMAL_Y); } // Print the name entry screen common information void printIDScreenCommon() { printStr_P(ID_TITLE_X, ID_TITLE_Y, StrID); printIDDecimalBytes(ID_2_DECIMAL_X, ID_2_DECIMAL_Y); printIDDecimal(ID_DECIMAL_X, ID_DECIMAL_Y); printIDBinary(ID_BINARY_X, ID_BINARY_Y); } // Print the name screen cursors void printNameCursors() { arduboy.fillRect(NAME_LARGE_X + (nameIndex * CHAR_WIDTH * 2), NAME_LARGE_Y + (CHAR_HEIGHT * 2) + 2, (CHAR_WIDTH * 2) - 2, 2); arduboy.drawFastHLine(NAME_HEX_X + (nameIndex * (CHAR_WIDTH * 3 + SMALL_SPACE)), NAME_HEX_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 3 - 1); arduboy.drawFastHLine(NAME_DECIMAL_X + (nameIndex * (CHAR_WIDTH * 3 + SMALL_SPACE)), NAME_DECIMAL_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 3 - 1); } // Print the ID screen cursors void printIDCursors() { arduboy.fillRect(ID_LARGE_X + ((idIndex + 1) * (CHAR_WIDTH * 2)), ID_LARGE_Y + (CHAR_HEIGHT * 2), (CHAR_WIDTH * 2) - 2, 2); arduboy.drawFastHLine(ID_2_DECIMAL_X + ((idIndex / 2) * (CHAR_WIDTH * 3 + SMALL_SPACE)), ID_2_DECIMAL_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 3 - 1); arduboy.drawFastHLine(ID_DECIMAL_X, ID_DECIMAL_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 5 - 1); arduboy.drawFastHLine((ID_BINARY_X + CHAR_WIDTH + SMALL_SPACE) + (idIndex * (CHAR_WIDTH * 4 + SMALL_SPACE)), ID_BINARY_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 4 - 1); } // Print the current "Show Unit Name" cursor void printShowNameCursor() { if (showNameFlag) { arduboy.fillRect(SHOW_NAME_YES_X, SHOW_NAME_YES_Y + (CHAR_HEIGHT * 2), (strlen_P(StrYes) * CHAR_WIDTH - 1) * 2, 2); } else { arduboy.fillRect(SHOW_NAME_NO_X, SHOW_NAME_NO_Y + (CHAR_HEIGHT * 2), (strlen_P(StrNo) * CHAR_WIDTH - 1) * 2, 2); } } // Print the unit name in normal size including an extent underline // at the given location void printName(int x, int y) { printStr(x, y, unitName); y += (CHAR_HEIGHT + 1); for (byte i = 0; i < ARDUBOY_UNIT_NAME_LEN; i++, x += CHAR_WIDTH) { arduboy.drawFastHLine(x, y, CHAR_WIDTH - 1); } } // Print the unit name in large size at the given location void printNameLarge(int x, int y) { arduboy.setTextSize(2); printStr(x, y, unitName); arduboy.setTextSize(1); } // Add a line below the large name showing the current length // Coordinates are for the name itself void printNameUnderline(int x, int y) { int lWidth; if (unitName[0] != 0) { x -= 1; y += ((CHAR_HEIGHT * 2) + 6); lWidth = (strlen(unitName) * (CHAR_WIDTH * 2)); arduboy.drawPixel(x, y); arduboy.drawPixel(x + lWidth - 1, y); arduboy.drawFastHLine(x, y + 1, lWidth); } } // Print the unit name in hex at the given location void printNameHex(int x, int y) { for (byte i = 0; i < ARDUBOY_UNIT_NAME_LEN; i++) { printHex8(x, y, unitName[i]); x += CHAR_WIDTH * 3 + SMALL_SPACE; } } // Print the unit name in decimal at the given location void printNameDecimal(int x, int y) { for (byte i = 0; i < ARDUBOY_UNIT_NAME_LEN; i++) { printDecimal8(x, y, unitName[i]); x += CHAR_WIDTH * 3 + SMALL_SPACE; } } // Print the unit ID in large size at the given location void printIDLarge(int x, int y) { arduboy.setTextSize(2); printIDHex(x, y); arduboy.setTextSize(1); } // Print the unit ID in normal size at the given location void printIDHex(int x, int y) { printHex16(x, y, unitID); } // Print the unit ID as 2 decimal bytes at the given location void printIDDecimalBytes(int x, int y) { printDecimal8(x, y, unitID >> 8); printDecimal8(x + CHAR_WIDTH * 3 + SMALL_SPACE, y, unitID & 0x00FF); } // Print the unit ID in decimal at the given location void printIDDecimal(int x, int y) { printDecimal16(x, y, unitID); } // print the unit ID in binary at the given location as 4 nybbles // with a leading 'b' void printIDBinary(int x, int y) { arduboy.setCursor(x, y); arduboy.print('b'); x += CHAR_WIDTH + SMALL_SPACE; for (char i = 3 * 4; i >= 0; i -= 4) { printBinaryNybble(x, y, (byte)(unitID >> i)); x += CHAR_WIDTH * 4 + SMALL_SPACE; } } // Print the save prompt in reverse at the given location void printSavePrompt(int x, int y) { arduboy.fillRect(x - 2, y - 2, strlen_P(StrSaveQ) * CHAR_WIDTH + 3, CHAR_HEIGHT + 3); arduboy.setTextColor(BLACK); arduboy.setTextBackground(WHITE); printStr_P(x, y, StrSaveQ); arduboy.setTextColor(WHITE); arduboy.setTextBackground(BLACK); } // Print a string at the given location void printStr(int x, int y, char* str) { arduboy.setCursor(x, y); arduboy.print(str); } // Print a string in program memory at the given location void printStr_P(int x, int y, const char* str) { arduboy.setCursor(x, y); arduboy.print((__FlashStringHelper*)(str)); } // Print an 8 bit number in decimal, right justified with leading spaces void printDecimal8(int x, int y, byte val) { printDecimalHelper(x, y, 2, 100, val); } // Print a 16 bit number in decimal, right justified with leading spaces void printDecimal16(int x, int y, unsigned int val) { printDecimalHelper(x, y, 4, 10000, val); } // Print a right justified decimal number, given width-1 and (width-1)^10 void printDecimalHelper(int x, int y, byte width, unsigned int pwr10, unsigned int val) { arduboy.setCursor(x, y); while (width > 0) { if (val >= pwr10) { break; } arduboy.print(' '); pwr10 /= 10; width--; } arduboy.print(val); } // Print an 8 bit hex number with leading x and zeros void printHex8(int x, int y, byte val) { arduboy.setCursor(x, y); arduboy.print('x'); if (val < 16) { arduboy.print('0'); } arduboy.print(val, HEX); } // Print a 16 bit hex number with leading x and zeros void printHex16(int x, int y, unsigned int val) { arduboy.setCursor(x, y); arduboy.print('x'); for (char i = 3 * 4; i >= 0; i -= 4) { arduboy.print((val >> i) & 0x000F, HEX); } } // Print a nybble in binary from the lowest 4 bits of the provided byte void printBinaryNybble(int x, int y, byte val) { arduboy.setCursor(x, y); for (char i = 3; i >= 0; i--) { arduboy.print((val >> i) & 0x01); } } // ---------------- Control and Utility Functions ------------------------ // Get the current unit name and ID, and the "Show Unit Name" flag, from EEPROM void readEEPROM() { memset(unitName, 0, sizeof(unitName)); arduboy.readUnitName(unitName); unitID = arduboy.readUnitID(); showNameFlag = arduboy.readShowUnitNameFlag(); } // Increment the name character at the cursor position void nameCharInc() { while (invalidChar(++unitName[nameIndex])) { } drawScreen(); } // Decrement the name character at the cursor position void nameCharDec() { while (invalidChar(--unitName[nameIndex])) { } drawScreen(); } // Return true if the given character is not allowed boolean invalidChar(char c) { return (c == '\n') || (c == '\r') || ((byte)c == 0xFF); } // Move the name cursor right void nameCursorRight() { if (++nameIndex == ARDUBOY_UNIT_NAME_LEN) { nameIndex = 0; } drawScreen(); } // Move the name cursor left void nameCursorLeft() { if (nameIndex == 0) { nameIndex = ARDUBOY_UNIT_NAME_LEN - 1; } else { nameIndex--; } drawScreen(); } // Increment the ID digit at the cursor position void idDigitInc() { uint16_t mask = 0xF000 >> (idIndex * 4); uint16_t val = 0x1000 >> (idIndex * 4); unitID = (unitID & ~mask) | ((unitID + val) & mask); drawScreen(); } // Decrement the ID digit at the cursor position void idDigitDec() { uint16_t mask = 0xF000 >> (idIndex * 4); uint16_t val = 0x1000 >> (idIndex * 4); unitID = (unitID & ~mask) | ((unitID - val) & mask); drawScreen(); } // Move the ID cursor right void idCursorRight() { if (++idIndex == sizeof(unitID) * 2) { idIndex = 0; } drawScreen(); } // Move the ID cursor left void idCursorLeft() { if (idIndex == 0) { idIndex = sizeof(unitID) * 2 - 1; } else { idIndex--; } drawScreen(); } // Toggle the "Show Unit Name" selection void showNameToggle() { showNameFlag = !showNameFlag; drawScreen(); } // Start the button auto-repeat delay void startButtonDelay() { delayCount = DELAY_FRAMES; repeating = false; } // Stop the button auto-repeat or delay void stopButtonRepeat() { delayCount = 0; repeating = false; } // Calculate the X coordinate to center a string of the given length int centerStrLen(unsigned int len) { return (WIDTH / 2) - (len * CHAR_WIDTH / 2); } // Calculate the X coordinate to center a string located in program memory int centerStr_P(const char* str) { return (WIDTH / 2) - (strlen_P(str) * CHAR_WIDTH / 2); } // Calculate the X coordinate to center a size 2 string located in // program memory int centerStr2_P(const char* str) { return (WIDTH / 2) - (strlen_P(str) * CHAR_WIDTH); } // Calculate the X coordinate to right justify a string in program memory int rightStr_P(const char* str) { return WIDTH - (strlen_P(str) * CHAR_WIDTH) + 1; }