Add read/write EEPROM unit name and ID functions

Also:
- Changes to show the unit name on the boot logo screen
- Added new example sketch SetNameAndID to allow setting the name and ID
- Updated the LICENSE.txt file and made changes to include it in the
  Doxygen generated documentation
This commit is contained in:
Scott Allen 2017-02-06 13:34:46 -05:00
parent 8125862bfe
commit 2fede9cb86
7 changed files with 960 additions and 12 deletions

View File

@ -1,10 +1,14 @@
/**
\page licenses Software License Agreements
\verbatim
Software License Agreements
-------------------------------------------------------------------------------
Licensed under the BSD 3-clause license:
Arduboy2 library:
Copyright (c) 2016, Scott Allen
Copyright (c) 2017, Scott Allen
All rights reserved.
The Arduboy2 library was forked from the Arduboy library:
@ -19,6 +23,10 @@ https://github.com/adafruit/Adafruit_SSD1306
Copyright (c) 2012, Adafruit Industries
All rights reserved.
SetNameAndID example sketch:
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
@ -44,8 +52,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Licensed under the BSD 2-clause license:
Portions of the Arduboy library, and thus the Arduboy2 library, based on
the Adafruit-GFX library:
Portions of the Arduboy library, and thus portions of the Arduboy2 library,
based on the Adafruit-GFX library:
https://github.com/adafruit/Adafruit-GFX-Library
Copyright (c) 2012 Adafruit Industries
All rights reserved.
@ -127,3 +135,5 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
===============================================================================
\endverbatim
*/

View File

@ -50,7 +50,9 @@ At the start of the sketch, the **ARDUBOY** logo scrolls down from the top of th
The RGB LED lights red then green then blue while the logo is scrolling. (If your Arduboy is one of those that has the RGB LED installed incorrectly, then it will light blue then off then red). This pattern is different than with Arduboy V1.1, which lit and then faded red (or blue if the LED is incorrectly installed).
Once the logo display completes, the sketch continues.
A user settable *unit name* of up to 6 characters can be saved in system EEPROM memory. If set, this name will be briefly displayed at the bottom of the boot logo screen, after the logo stops scrolling down. This feature is only available if the *Arduboy2* class is used, not the *Arduboy2Base* class. This is because it requires the text display functions, which are only available in the *Arduboy2* class.
Once the logo display sequence completes, the sketch continues.
### "Flashlight" mode
@ -188,9 +190,9 @@ void Arduboy2Base::begin()
// check for and handle buttons held during start up for system control
systemButtons();
bootLogo();
audio.begin();
bootLogo();
}
```

View File

@ -0,0 +1,730 @@
// =======================================================================
// 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 1.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 StrSaveQ[] PROGMEM = "SAVE?";
const char StrBtnChangeName[] PROGMEM = "UP:change Unit Name";
const char StrBtnChangeID[] PROGMEM = "DOWN:change Unit ID";
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 StrBtnLogo[] PROGMEM = "LEFT:show 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_LOGO_X 0
#define MENU_BTN_LOGO_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)
// 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(), stateSaveName(), stateSaveID();
void screenMain(), screenName(), screenID(), screenSaveName(), screenSaveID();
Arduboy2 arduboy;
char unitName[ARDUBOY_UNIT_NAME_LEN + 1];
byte nameIndex;
uint16_t unitID;
byte idIndex;
// Assign numbers for each state/screen
enum State : byte {
sMain,
sName,
sID,
sSaveName,
sSaveID,
sMAX = sSaveID
};
byte currentState;
// Function pointer array for button handling
void (*stateFunc[sMAX + 1])() = {
stateMain,
stateName,
stateID,
stateSaveName,
stateSaveID
};
// Function pointer array for screen drawing
void (*screenFunc[sMAX + 1])() = {
screenMain,
screenName,
screenID,
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)) {
arduboy.bootLogo();
delay(1000);
setState(sMain);
}
}
// 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: 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_LOGO_X, MENU_BTN_LOGO_Y, StrBtnLogo);
}
// 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: 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);
}
// --------------------- 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 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 from EEPROM
void readEEPROM() {
memset(unitName, 0, sizeof(unitName));
arduboy.readUnitName(unitName);
unitID = arduboy.readUnitID();
}
// 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();
}
// 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 right justify a string in program memory
int rightStr_P(const char* str) {
return WIDTH - (strlen_P(str) * CHAR_WIDTH) + 1;
}

View File

@ -112,7 +112,7 @@ WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ./src ./README.md
INPUT = ./src ./README.md ./LICENSE.txt
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c *.cc *.cxx *.cpp *.c++ *.h *.hh *.hxx *.hpp *.h++
RECURSIVE = NO

View File

@ -64,6 +64,8 @@ paint8Pixels KEYWORD2
paintScreen KEYWORD2
pollButtons KEYWORD2
pressed KEYWORD2
readUnitID KEYWORD2
readUnitName KEYWORD2
saveOnOff KEYWORD2
setCursor KEYWORD2
setFrameRate KEYWORD2
@ -75,6 +77,8 @@ setTextWrap KEYWORD2
systemButtons KEYWORD2
toggle KEYWORD2
width KEYWORD2
writeUnitID KEYWORD2
writeUnitName KEYWORD2
# Sprites class
drawErase KEYWORD2

View File

@ -42,9 +42,9 @@ void Arduboy2Base::begin()
// check for and handle buttons held during start up for system control
systemButtons();
bootLogo();
audio.begin();
bootLogo();
}
void Arduboy2Base::flashlight()
@ -113,8 +113,13 @@ void Arduboy2Base::bootLogo()
delay(750);
digitalWrite(BLUE_LED, RGB_OFF);
bootLogoExtra();
}
// Virtual function overridden by derived class
void Arduboy2Base::bootLogoExtra() { }
/* Frame management */
void Arduboy2Base::setFrameRate(uint8_t rate)
@ -896,6 +901,58 @@ bool Arduboy2Base::collide(Rect rect1, Rect rect2)
rect2.y + rect2.height <= rect1.y);
}
uint16_t Arduboy2Base::readUnitID()
{
return EEPROM.read(EEPROM_UNIT_ID) |
(((uint16_t)(EEPROM.read(EEPROM_UNIT_ID + 1))) << 8);
}
void Arduboy2Base::writeUnitID(uint16_t id)
{
EEPROM.update(EEPROM_UNIT_ID, (uint8_t)(id & 0xff));
EEPROM.update(EEPROM_UNIT_ID + 1, (uint8_t)(id >> 8));
}
uint8_t Arduboy2Base::readUnitName(char* name)
{
char val;
uint8_t dest;
uint8_t src = EEPROM_UNIT_NAME;
for (dest = 0; dest < ARDUBOY_UNIT_NAME_LEN; dest++)
{
if ((val = EEPROM.read(src)) == 0x00 || (byte)val == 0xFF)
{
break;
}
name[dest] = val;
src++;
}
name[dest] = 0x00;
return dest;
}
void Arduboy2Base::writeUnitName(char* name)
{
bool done = false;
uint8_t dest = EEPROM_UNIT_NAME;
for (uint8_t src = 0; src < ARDUBOY_UNIT_NAME_LEN; src++)
{
if (name[src] != 0x00 && !done)
{
EEPROM.update(dest, name[src]);
}
else
{
done = true;
EEPROM.update(dest, 0x00);
}
dest++;
}
}
void Arduboy2Base::swap(int16_t& a, int16_t& b)
{
int16_t temp = a;
@ -918,6 +975,27 @@ Arduboy2::Arduboy2()
textWrap = 0;
}
void Arduboy2::bootLogoExtra()
{
uint8_t c = EEPROM.read(EEPROM_UNIT_NAME);
if (c != 0xFF && c != 0x00)
{
uint8_t i = EEPROM_UNIT_NAME;
cursor_x = 50;
cursor_y = 56;
do
{
write(c);
c = EEPROM.read(++i);
} while (i < EEPROM_UNIT_NAME + ARDUBOY_UNIT_NAME_LEN);
display();
delay(1500);
}
}
size_t Arduboy2::write(uint8_t c)
{
if (c == '\n')

View File

@ -8,6 +8,7 @@
#define ARDUBOY2_H
#include <Arduino.h>
#include <EEPROM.h>
#include "Arduboy2Core.h"
#include "Sprites.h"
#include <Print.h>
@ -34,9 +35,15 @@
#define ARDUBOY_LIB_VER 30000
// EEPROM settings
#define ARDUBOY_UNIT_NAME_LEN 6 /**< The maximum length of the unit name string. */
#define EEPROM_VERSION 0
#define EEPROM_BRIGHTNESS 1
#define EEPROM_AUDIO_ON_OFF 2
#define EEPROM_UNIT_ID 8 // A uint16_t binary unit ID
#define EEPROM_UNIT_NAME 10 // An up to 6 character unit name. Cannot contain
// 0x00 or 0xFF. Lengths less than 6 are padded
// with 0x00
/** \brief
* Start of EEPROM storage space for sketches.
@ -180,7 +187,7 @@ class Arduboy2Base : public Arduboy2Core
*
* \note
* To free up some code space for use by the sketch, `boot()` can be used
* instead of `begin()` allow the elimination of some of the things that
* instead of `begin()` to allow the elimination of some of the things that
* aren't really required, such as displaying the boot logo.
*
* \see boot()
@ -229,10 +236,18 @@ class Arduboy2Base : public Arduboy2Core
* The Arduboy logo scrolls down from the top of the screen to the center
* while the RGB LEDs light in sequence.
*
* \see begin() boot()
* This function calls `bootLogoExtra()` after the logo stops scrolling down,
* which derived classes can implement to add additional information to the
* logo screen. The `Arduboy2` class uses this to display the unit name.
*
* \see begin() boot() Arduboy2::bootLogoExtra()
*/
void bootLogo();
// Called by bootLogo() to allow derived classes to display additional
// information after the logo stops scrolling down.
virtual void bootLogoExtra();
/** \brief
* Clear the display buffer.
*
@ -807,6 +822,97 @@ class Arduboy2Base : public Arduboy2Core
*/
bool collide(Rect rect1, Rect rect2);
/** \brief
* Read the unit ID from system EEPROM.
*
* \return The value of the unit ID stored in system EEPROM.
*
* \details
* This function reads the unit ID that has been set in system EEPROM.
* The ID can be any value. It is intended to allow different units to be
* uniquely identified.
*
* \see writeUnitID() readUnitName()
*/
uint16_t readUnitID();
/** \brief
* Write a unit ID to system EEPROM.
*
* \param id The value of the unit ID to be stored in system EEPROM.
*
* \details
* This function writes a unit ID to a reserved location in system EEPROM.
* The ID can be any value. It is intended to allow different units to be
* uniquely identified.
*
* \see readUnitID() writeUnitName()
*/
void writeUnitID(uint16_t id);
/** \brief
* Read the unit name from system EEPROM.
*
* \param name A pointer to a string array variable where the unit name will
* be placed. The string will be up to 6 characters and terminated with a
* null (0x00) character, so the provided array must be at least 7 bytes long.
*
* \return The length of the string (0-6).
*
* \details
* This function reads the unit name that has been set in system EEPROM. The
* name is in ASCII and can contain any values except 0xFF and the
* null (0x00) terminator value.
*
* The name can be used for any purpose. It could identify the owner or
* give the unit itself a nickname. A sketch could use it to automatically
* fill in a name or initials in a high score table, or display it as the
* "player" when the opponent is the computer.
*
* \note
* Sketches can use the defined value `ARDUBOY_UNIT_NAME_LEN` instead of
* hard coding a 6 when working with the unit name. For example, to allocate
* a buffer and read the unit name into it:
* \code
* // Buffer for maximum name length plus the terminator
* char unitName[ARDUBOY_UNIT_NAME_LEN + 1];
*
* // The actual name length
* byte unitNameLength;
*
* unitNameLength = arduboy.readUnitName(unitName);
* \endcode
*
* \see writeUnitName() readUnitID() Arduboy2::bootLogoExtra()
*/
uint8_t readUnitName(char* name);
/** \brief
* Write a unit name to system EEPROM.
*
* \param name A pointer to a string array variable containing the unit name
* to be saved. The string can be up to 6 characters and must be terminated
* with a null (0x00) character. It can contain any values except 0xFF.
*
* \details
* This function writes a unit name to a reserved area in system EEPROM.
* The name is in ASCII and can contain any values except 0xFF and the
* null (0x00) terminator value. The newline character (LF, \\n, 0x0A) and
* carriage return character (CR, \\r, 0x0D) should also be avoided.
*
* The name can be used for any purpose. It could identify the owner or
* give the unit itself a nickname. A sketch could use it to automatically
* fill in a name or initials in a high score table, or display it as the
* "player" when the opponent is the computer.
*
* \note
* Sketches can use the defined value `ARDUBOY_UNIT_NAME_LEN` instead of
* hard coding a 6 when working with the unit name.
*
* \see readUnitName() writeUnitID() Arduboy2::bootLogoExtra()
*/
void writeUnitName(char* name);
/** \brief
* A counter which is incremented once per frame.
*
@ -931,6 +1037,24 @@ class Arduboy2 : public Print, public Arduboy2Base
* \see Arduboy2::write()
*/
/** \brief
* Show the unit name at the bottom of the boot logo screen.
*
* \details
* This function is called by the `bootLogo()` function.
*
* If a unit name has been saved in system EEPROM, it will be displayed at
* the bottom of the screen. This function pauses for a short time to allow
* the name to be seen.
*
* \note
* This function would not normally be called directly from within a sketch
* itself.
*
* \see readUnitName() writeUnitName() bootLogo() begin()
*/
virtual void bootLogoExtra();
/** \brief
* Write a single ASCII character at the current text cursor location.
*