mirror of https://github.com/MLXXXp/Arduboy2.git
836 lines
24 KiB
C++
836 lines
24 KiB
C++
// =======================================================================
|
|
// 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 <Arduboy2.h>
|
|
|
|
// 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;
|
|
}
|
|
|