diff --git a/README.md b/README.md index b8cbced..2e13c2b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The Arduboy2 library is maintained in a git repository hosted on [GitHub](https: https://github.com/MLXXXp/Arduboy2 -The **Arduboy2** library is a fork of the [Arduboy library](https://github.com/Arduboy/Arduboy), which provides a standard *application programming interface* (API) to the display, buttons and other hardware of the Arduino based [Arduboy miniature game system](https://www.arduboy.com/). +The **Arduboy2** library is a fork of the [Arduboy library](https://github.com/Arduboy/Arduboy), which provides a standard *application programming interface* (API) to the display, buttons and other hardware of the Arduino based [Arduboy miniature game system](https://www.arduboy.com/). The original *Arduboy* library is no longer being maintained. The name *Arduboy2* doesn't indicate that it's for a new "next generation" of the Arduboy hardware. The name was changed so it can coexist in the Arduino IDE with the current *Arduboy* library, without conflict. This way, existing sketches can continue to use the *Arduboy* library and class, without changes, while new sketches can be written (or old ones modified) to use and take advantage of the capabilities of the *Arduboy2* class and library. @@ -47,9 +47,10 @@ A user settable *unit name* can be saved in system EEPROM memory. If set, this n Once the logo display sequence completes, the sketch continues. -For developers who wish to quickly begin testing, or impatient users who want to go strait to playing their game, the boot logo sequence can be bypassed by holding the *RIGHT* button while powering up, and then releasing it. Alternatively, the *RIGHT* button can be pressed while the logo is scrolling down. +**Note:** -For users who wish to always disable the displaying of the boot logo sequence on boot up, a flag in system EEPROM is available for this. The included *SetSystemEEPROM* example sketch can be used to set this flag. +- For developers who wish to quickly begin testing, or impatient users who want to go strait to playing their game, the boot logo sequence can be bypassed by holding the *RIGHT* button while powering up, and then releasing it. Alternatively, the *RIGHT* button can be pressed while the logo is scrolling down. +- For users who wish to always disable the displaying of the boot logo sequence on boot up, a flag in system EEPROM is available for this. The included *SetSystemEEPROM* example sketch can be used to set this flag. ### "Flashlight" mode @@ -165,63 +166,99 @@ with Arduboy2Base arduboy; ``` -#### Remove boot up features +#### Substitute or remove boot up features -As previously described, the *begin()* function includes features that are intended to be available to all sketches during boot up. However, if you're looking to gain some code space, you can call *boot()* instead of *begin()*. This will initialize the system but not include any of the extra boot up features. If desired, you can then add back in any of these features by calling the functions that perform them. You will have to trade off between the desirability of having a feature and how much memory you can recover by not including it. +As previously described in the _Start up features_ section, the *begin()* function includes features that are intended to be available to all sketches during boot up. However, if you're looking to gain some code space, you can call *boot()* instead of *begin()*. This will initialize the system but not include any of the extra boot up features. You can then add back in any of these features by calling the functions that perform them. You will have to trade off between the desirability of having a feature and how much memory you can recover by not including it. -A good way to use *boot()* instead of *begin()* is to copy the code from the body of the *begin()* function, in file *Arduboy2.cpp*, into your sketch and then edit it to retain the *boot()* call and any feature calls desired. +You should at least call either *flashlight()* or *safeMode()* as a safeguard to allow uploading a new sketch when the bootloader "magic key" problem is an issue. -As of this writing, the begin function is: +Here is a template that provides the equivalent of *begin()* ```cpp -void Arduboy2Base::begin() +void setup() { - boot(); // raw hardware + // Required to initialize the hardware. + arduboy.boot(); - display(); // blank the display (sBuffer is global, so cleared automatically) + // This clears the display. (The screen buffer will be all zeros) + // It may not be needed if something clears the display later on but + // "garbage" will be displayed if systemButtons() is used without it. + arduboy.display(); - flashlight(); // light the RGB LED and screen if UP button is being held. + // flashlight() or safeMode() should always be included to provide + // a method of recovering from the bootloader "magic key" problem. + arduboy.flashlight(); +// arduboy.safeMode(); - // check for and handle buttons held during start up for system control - systemButtons(); + // This allows sound to be turned on or muted. If the sketch provides + // its own way of toggling sound, or doesn't produce any sound, this + // function may not be required. + arduboy.systemButtons(); - audio.begin(); + // This is required to initialize the speaker. It's not needed if + // the sketch doesn't produce any sounds. + arduboy.audio.begin(); - bootLogo(); + // This displays the boot logo sequence but note that the logo can + // be suppressed by the user, by pressing the RIGHT button or using + // a system EEPROM setting. If not removed entirely, an alternative + // bootLogo...() function may save some memory. + arduboy.bootLogo(); +// arduboy.bootLogoCompressed(); +// arduboy.bootLogoSpritesSelfMasked(); +// arduboy.bootLogoSpritesOverwrite(); +// arduboy.bootLogoSpritesBSelfMasked(); +// arduboy.bootLogoSpritesBOverwrite(); +// arduboy.bootLogoText(); + + // Wait for all buttons to be released, in case a pressed one might + // cause problems by being acted upon when the actual sketch code + // starts. If neither systemButtons() nor bootLogo() is kept, this + // function isn't required. + arduboy.waitNoButtons(); + +// Additional setup code... - waitNoButtons(); // wait for all buttons to be released } ``` -To incorporate it into your sketch just keep *boot()* and whatever feature calls are desired, if any. Comment out or delete the rest. Remember to add the class object name in front of each function call, since they're now being called from outside the class itself. If your sketch uses sound, it's a good idea to keep the call to *audio.begin()*. - -For example: Let's say a sketch has its own code to enable, disable and save the *audio on/off* setting, and wants to keep the *flashlight* function. In *setup()* it could replace *begin()* with: - -```cpp - arduboy.boot(); // raw hardware - -// *** This particular sketch clears the display soon, so it doesn't need this: -// display(); // blank the display (sBuffer is global, so cleared automatically) - - arduboy.flashlight(); // light the RGB LED and screen if UP button is being held. - - // check for and handle buttons held during start up for system control -// systemButtons(); - - arduboy.audio.begin(); - -// bootLogo(); - -// waitNoButtons(); // wait for all buttons to be released -``` - -This saves whatever code *display()*, *systemButtons()*, *bootLogo()* and *waitNoButtons()* would use. - There are a few functions provided that are roughly equivalent to the standard functions used by *begin()* but which use less code space. -- *bootLogoCompressed()*, *bootLogoSpritesSelfMasked()*, *bootLogoSpritesOverwrite()*, *bootLogoSpritesBSelfMasked()* and *bootLogoSpritesBOverwrite()* will do the same as *bootLogo()* but will use *drawCompressed()*, or *Sprites* / *SpritesB* class *drawSelfMasked()* or *drawOverwrite()* functions respectively, instead of *drawBitmask()*, to render the logo. If the sketch uses one of these functions, then using the boot logo function that also uses it may reduce code size. It's best to try each of them to see which one produces the smallest size. +- *bootLogoCompressed()*, *bootLogoSpritesSelfMasked()*, *bootLogoSpritesOverwrite()*, *bootLogoSpritesBSelfMasked()* and *bootLogoSpritesBOverwrite()* will do the same as *bootLogo()* but will use *drawCompressed()*, or *Sprites* / *SpritesB* class *drawSelfMasked()* or *drawOverwrite()* functions respectively, instead of *drawBitmap()*, to render the logo. If the sketch uses one of these functions, then using the boot logo function that also uses it may reduce code size. It's best to try each of them to see which one produces the smallest size. - *bootLogoText()* can be used in place *bootLogo()* in the case where the sketch uses text functions. It renders the logo as text instead of as a bitmap (so doesn't look as good). -- *safeMode()* can be used in place of *flashlight()* for cases where it's needed to allow uploading a new sketch when the bootloader "magic key" problem is an issue. It only lights the red RGB LED, so you don't get the bright light that is the primary purpose of *flashlight()*. +- *safeMode()* can be used in place of *flashlight()* as a safeguard to allow uploading a new sketch when the bootloader "magic key" problem is an issue. It only lights the red RGB LED, so you don't get the bright light that is the primary purpose of *flashlight()*. + +It is also possible to replace the boot logo drawing function with one that uses a different bitmap rendering function used elsewhere in your sketch. This may save memory by using this bitmap function for the logo, instead of the logo using a separate function that only ends up being used once. For example, if you use the *ArdBitmap* library's *drawCompressed()* function in your sketch, you could convert the **ARDUBOY** logo to *Ardbitmap* compressed format, and create *drawLogoArdCompressed()* and *bootLogoArdCompressed()* functions: + +```cpp +void drawLogoArdCompressed(int16_t y) +{ + ardbitmap.drawCompressed(20, y, arduboy_logo_ardbitmap, + WHITE, ALIGN_CENTER, MIRROR_NONE); +} + +void bootLogoArdCompressed() +{ + if (arduboy.bootLogoShell(drawLogoArdCompressed)) + { + arduboy.bootLogoExtra(); + } +} + +void setup() +{ + arduboy.beginDoFirst(); + bootLogoArdCompressed(); + arduboy.waitNoButtons(); + +// Additional setup code... + +} +``` + +The **ARDUBOY** logo, in PNG format, is included in the library repository as file: + +`extras/assets/arduboy_logo.png` #### Use the SpritesB class instead of Sprites @@ -246,6 +283,8 @@ The *ARDUBOY_NO_USB* macro is used to eliminate the USB code. The *exitToBootloa ## What's different from Arduboy library V1.1 +(These notes apply to when the *Arduboy2* library was first released. There will have been many additional changes, enhancements and features added to *Arduboy2* since then.) + A main goal of Arduboy2 is to provide ways in which more code space can be freed for use by large sketches. Another goal is to allow methods other than the *tunes* functions to be used to produce sounds. Arduboy2 remains substantially compatible with [Arduboy library V1.1](https://github.com/Arduboy/Arduboy/releases/tag/v1.1), which was the latest stable release at the time of the fork. Arduboy2 is based on the code targeted for Arduboy library V1.2, which was still in development and unreleased at the time it was forked. Main differences between Arduboy2 and Arduboy V1.1 are: @@ -424,13 +463,13 @@ The benefit of using *ArduboyTones* would be reduced code size and possibly easi ### Sketch uses the beginNoLogo() function instead of begin() -The *beginNoLogo()* function has been removed. Instead, *boot()* can be used with additional functions following it to add back in desired boot functionality. See the information above, under the heading *Remove boot up features*, for more details. Assuming the object is named *arduboy*, a direct replacement for *beginNoLogo()* would be: +The *beginNoLogo()* function has been removed. *beginNoLogo()* can be replaced with *begin()*, since users can choose to suppress the logo sequence using the *RIGHT* button or by setting a flag in system EEPROM. + +If using *begin()* results in the sketch program memory size being too large, *beginDoFirst()* or *boot()* can be used with additional functions following it to add back in desired boot functionality. See the information above, under the heading *Substitute or remove boot up features*, for more details. Assuming the object is named *arduboy*, an equivalent replacement for *beginNoLogo()* would be: ```cpp - arduboy.boot(); - arduboy.display(); - arduboy.flashlight(); - arduboy.audio.begin(); + arduboy.beginDoFirst(); + arduboy.waitNoButtons(); ``` ---------- diff --git a/keywords.txt b/keywords.txt index 9c06e7f..2dd1770 100644 --- a/keywords.txt +++ b/keywords.txt @@ -26,6 +26,7 @@ blank KEYWORD2 boot KEYWORD2 bootLogo KEYWORD2 bootLogoCompressed KEYWORD2 +bootLogoExtra KEYWORD2 bootLogoShell KEYWORD2 bootLogoSpritesBOverwrite KEYWORD2 bootLogoSpritesBSelfMasked KEYWORD2 diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index 1093558..69be0dc 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -12,20 +12,40 @@ uint8_t Arduboy2Base::sBuffer[]; -Arduboy2Base::Arduboy2Base() -{ - currentButtonState = 0; - previousButtonState = 0; - // frame management - setFrameDuration(16); - frameCount = 0; - justRendered = false; -} +uint16_t Arduboy2Base::frameCount = 0; + +uint8_t Arduboy2Base::eachFrameMillis = 16; +uint8_t Arduboy2Base::thisFrameStart; +uint8_t Arduboy2Base::lastFrameDurationMs; +bool Arduboy2Base::justRendered = false; + +uint8_t Arduboy2Base::currentButtonState = 0; +uint8_t Arduboy2Base::previousButtonState = 0; // functions called here should be public so users can create their // own init functions if they need different behavior than `begin` -// provides by default +// provides by default. +// +// This code and it's documentation should be kept in sync with +// Aruduboy2::begin() void Arduboy2Base::begin() +{ + beginDoFirst(); + + bootLogo(); + // alternative logo functions. Work the same as bootLogo() but may reduce + // memory size if the sketch uses the same bitmap drawing function or + // `Sprites`/`SpritesB` class +// bootLogoCompressed(); +// bootLogoSpritesSelfMasked(); +// bootLogoSpritesOverwrite(); +// bootLogoSpritesBSelfMasked(); +// bootLogoSpritesBOverwrite(); + + waitNoButtons(); // wait for all buttons to be released +} + +void Arduboy2Base::beginDoFirst() { boot(); // raw hardware @@ -37,17 +57,6 @@ void Arduboy2Base::begin() systemButtons(); audio.begin(); - - bootLogo(); - // alternative logo functions. Work the same as bootLogo() but may reduce - // memory size if the sketch uses the same bitmap drawing function -// bootLogoCompressed(); -// bootLogoSpritesSelfMasked(); -// bootLogoSpritesOverwrite(); -// bootLogoSpritesBSelfMasked(); -// bootLogoSpritesBOverwrite(); - - waitNoButtons(); // wait for all buttons to be released } void Arduboy2Base::flashlight() @@ -159,12 +168,12 @@ void Arduboy2Base::drawLogoSpritesBOverwrite(int16_t y) // bootLogoText() should be kept in sync with bootLogoShell() // if changes are made to one, equivalent changes should be made to the other -void Arduboy2Base::bootLogoShell(void (*drawLogo)(int16_t)) +bool Arduboy2Base::bootLogoShell(void (&drawLogo)(int16_t)) { bool showLEDs = readShowBootLogoLEDsFlag(); if (!readShowBootLogoFlag()) { - return; + return false; } if (showLEDs) { @@ -174,7 +183,7 @@ void Arduboy2Base::bootLogoShell(void (*drawLogo)(int16_t)) for (int16_t y = -15; y <= 24; y++) { if (pressed(RIGHT_BUTTON)) { digitalWriteRGB(RGB_OFF, RGB_OFF, RGB_OFF); // all LEDs off - return; + return false; } if (showLEDs && y == 4) { @@ -197,14 +206,12 @@ void Arduboy2Base::bootLogoShell(void (*drawLogo)(int16_t)) delayShort(400); digitalWriteRGB(BLUE_LED, RGB_OFF); - bootLogoExtra(); + return true; } -// Virtual function overridden by derived class -void Arduboy2Base::bootLogoExtra() { } - // wait for all buttons to be released -void Arduboy2Base::waitNoButtons() { +void Arduboy2Base::waitNoButtons() +{ do { delayShort(50); // simple button debounce } while (buttonsState()); @@ -1146,15 +1153,83 @@ void Arduboy2Base::swapInt16(int16_t& a, int16_t& b) //========== class Arduboy2 ========== //==================================== -Arduboy2::Arduboy2() +int16_t Arduboy2::cursor_x = 0; +int16_t Arduboy2::cursor_y = 0; +uint8_t Arduboy2::textColor = WHITE; +uint8_t Arduboy2::textBackground = BLACK; +uint8_t Arduboy2::textSize = 1; +bool Arduboy2::textWrap = false; +bool Arduboy2::textRaw = false; + +// functions called here should be public so users can create their +// own init functions if they need different behavior than `begin` +// provides by default. +// +// This code and it's documentation should be kept in sync with +// Aruduboy2Base::begin() +void Arduboy2::begin() { - cursor_x = 0; - cursor_y = 0; - textColor = 1; - textBackground = 0; - textSize = 1; - textWrap = 0; - textRaw = 0; + beginDoFirst(); + + bootLogo(); + // alternative logo functions. Work the same as bootLogo() but may reduce + // memory size if the sketch uses the same bitmap drawing function or + // `Sprites`/`SpritesB` class +// bootLogoCompressed(); +// bootLogoSpritesSelfMasked(); +// bootLogoSpritesOverwrite(); +// bootLogoSpritesBSelfMasked(); +// bootLogoSpritesBOverwrite(); + + waitNoButtons(); +} + +void Arduboy2::bootLogo() +{ + if (bootLogoShell(drawLogoBitmap)) + { + bootLogoExtra(); + } +} + +void Arduboy2::bootLogoCompressed() +{ + if (bootLogoShell(drawLogoCompressed)) + { + bootLogoExtra(); + } +} + +void Arduboy2::bootLogoSpritesSelfMasked() +{ + if (bootLogoShell(drawLogoSpritesSelfMasked)) + { + bootLogoExtra(); + } +} + +void Arduboy2::bootLogoSpritesOverwrite() +{ + if (bootLogoShell(drawLogoSpritesOverwrite)) + { + bootLogoExtra(); + } +} + +void Arduboy2::bootLogoSpritesBSelfMasked() +{ + if (bootLogoShell(drawLogoSpritesBSelfMasked)) + { + bootLogoExtra(); + } +} + +void Arduboy2::bootLogoSpritesBOverwrite() +{ + if (bootLogoShell(drawLogoSpritesBOverwrite)) + { + bootLogoExtra(); + } } // bootLogoText() should be kept in sync with bootLogoShell() @@ -1171,7 +1246,7 @@ void Arduboy2::bootLogoText() digitalWriteRGB(RED_LED, RGB_ON); } - for (int16_t y = -16; y <= 24; y++) { + for (int16_t y = -15; y <= 24; y++) { if (pressed(RIGHT_BUTTON)) { digitalWriteRGB(RGB_OFF, RGB_OFF, RGB_OFF); // all LEDs off return; diff --git a/src/Arduboy2.h b/src/Arduboy2.h index 80a7029..cb77966 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -214,7 +214,6 @@ class Arduboy2Base : public Arduboy2Core friend class Arduboy2Audio; public: - Arduboy2Base(); /** \brief * An object created to provide audio control functions within this class. @@ -225,7 +224,7 @@ class Arduboy2Base : public Arduboy2Core * * \see Arduboy2Audio */ - Arduboy2Audio audio; + static Arduboy2Audio audio; /** \brief * Initialize the hardware, display the boot logo, provide boot utilities, etc. @@ -237,13 +236,42 @@ class Arduboy2Base : public Arduboy2Core * and system control features and initializes audio control. * * \note - * To free up some code space for use by the sketch, `boot()` can be used - * instead of `begin()` to allow the elimination of some of the things that - * aren't really required, such as displaying the boot logo. + * \parblock + * If it becomes necessary to free up some code space for use by the sketch, + * `boot()` can be used instead of `begin()` to allow the elimination of + * some of the things that aren't absolutely required. + * + * See the README file or main page, in section + * _Substitute or remove boot up features_, for more details. + * \endparblock * * \see boot() */ - void begin(); + static void begin(); + + /** \brief + * Helper function that calls the inital functions used by `begin()` + * + * \details + * This function calls all the functions used by `begin()` up to the point of + * calling `bootLogo()`. It could be called by a sketch to make it easy to + * use one of the alternative `bootLogo...()` functions or a user provided + * one. + * + * For example, if a sketch uses `Sprites` class functions but doesn't use + * `drawBitmap()`, some program space may be saved by using the following in + * place of `begin()`: + * + * \code{.cpp} + * arduboy.beginDoFirst(); + * arduboy.bootLogoSpritesSelfMasked(); // or: + * //arduboy.bootLogoSpritesOverwrite(); // (whatever saves more memory) + * arduboy.waitNoButtons(); + * \endcode + * + * \see begin() boot() + */ + static void beginDoFirst(); /** \brief * Turn the RGB LED and display fully on to act as a small flashlight/torch. @@ -253,8 +281,8 @@ class Arduboy2Base : public Arduboy2Core * display pixels fully on. If the UP button is detected, this function * does not exit. The Arduboy must be restarted after flashlight mode is used. * - * This function is called by `begin()` and can be called by a sketch - * after `boot()`. + * This function is called by `begin()` and should be called by a sketch + * after `boot()` unless `safeMode()` is called instead. * * \note * \parblock @@ -263,15 +291,14 @@ class Arduboy2Base : public Arduboy2Core * This problem occurs with certain sketches that use large amounts of RAM. * Being in flashlight mode when uploading a new sketch can fix this problem. * - * Therefore, for sketches that potentially could cause this problem, and use - * `boot()` instead of `begin()`, it is recommended that a call to - * `flashlight()` be included after calling `boot()`. If program space is - * limited, `safeMode()` can be used instead of `flashlight()`. + * Therefore, for sketches that use `boot()` instead of `begin()`, a call to + * `flashlight()` should be included after calling `boot()`. If program space + * is limited, `safeMode()` can be used instead of `flashlight()`. * \endparblock * * \see begin() boot() safeMode() */ - void flashlight(); + static void flashlight(); /** \brief * Handle buttons held on startup for system control. @@ -289,7 +316,1271 @@ class Arduboy2Base : public Arduboy2Core * * \see begin() boot() */ - void systemButtons(); + static void systemButtons(); + + /** \brief + * Display the boot logo sequence using `drawBitmap()`. + * + * \details + * This function is called by `begin()` and can be called by a sketch + * after `boot()`. + * + * The Arduboy logo scrolls down from the top of the screen to the center + * while the RGB LEDs light in sequence. + * + * The `bootLogoShell()` helper function is used to perform the actual + * sequence. The documentation for `bootLogoShell()` provides details on how + * it operates. + * + * \see begin() boot() bootLogoShell() Arduboy2::bootLogoText() + */ + static void bootLogo(); + + /** \brief + * Display the boot logo sequence using `drawCompressed()`. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative to + * `bootLogo()`. This may reduce code size if the sketch itself uses + * `drawCompressed()`. + * + * \see bootLogo() begin() boot() + */ + static void bootLogoCompressed(); + + /** \brief + * Display the boot logo sequence using `Sprites::drawSelfMasked()`. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative to + * `bootLogo()`. This may reduce code size if the sketch itself uses + * `Sprites` class functions. + * + * \see bootLogo() begin() boot() Sprites + */ + static void bootLogoSpritesSelfMasked(); + + /** \brief + * Display the boot logo sequence using `Sprites::drawOverwrite()`. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative to + * `bootLogo()`. This may reduce code size if the sketch itself uses + * `Sprites` class functions. + * + * \see bootLogo() begin() boot() Sprites + */ + static void bootLogoSpritesOverwrite(); + + /** \brief + * Display the boot logo sequence using `SpritesB::drawSelfMasked()`. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative to + * `bootLogo()`. This may reduce code size if the sketch itself uses + * `SpritesB` class functions. + * + * \see bootLogo() begin() boot() SpritesB + */ + static void bootLogoSpritesBSelfMasked(); + + /** \brief + * Display the boot logo sequence using `SpritesB::drawOverwrite()`. + * + * \details + * This function can be called by a sketch after `boot()` as an alternative to + * `bootLogo()`. This may reduce code size if the sketch itself uses + * `SpritesB` class functions. + * + * \see bootLogo() begin() boot() SpritesB + */ + static void bootLogoSpritesBOverwrite(); + + /** \brief + * Display the boot logo sequence using the provided function. + * + * \param drawLogo A reference to a function which will draw the boot logo + * at the given Y position. + * + * \return `true` if the sequence runs to completion. `false` if the sequence + * is aborted or bypassed. + * + * \details + * This common function executes the sequence to display the boot logo. + * It is called by `bootLogo()` and other similar functions which provide it + * with a reference to a function which will do the actual drawing of the + * logo. + * + * 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. + * + * If the RIGHT button is pressed while the logo is scrolling down, + * the boot logo sequence will be aborted. This can be useful for + * developers who wish to quickly start testing, or anyone else who is + * impatient and wants to go straight to the actual sketch. + * + * If the "Show LEDs with boot logo" flag in system EEPROM is cleared, + * the RGB LEDs will not be flashed during the logo display sequence. + * + * If the "Show Boot Logo" flag in system EEPROM is cleared, this function + * will return without executing the logo display sequence. + * + * The prototype for the function provided to draw the logo is: + * + * \code{.cpp} + * void drawLogo(int16_t y); + * \endcode + * + * The y parameter is the Y offset for the top of the logo. It is expected + * that the logo will be 16 pixels high and centered horizontally. This will + * result in the logo stopping in the middle of the screen at the end of the + * sequence. If the logo height is not 16 pixels, the Y value can be adjusted + * to compensate. + * + * \see bootLogo() boot() Arduboy2::bootLogoExtra() + */ + static bool bootLogoShell(void (&drawLogo)(int16_t)); + + /** \brief + * Wait until all buttons have been released. + * + * \details + * This function is called by `begin()` and can be called by a sketch + * after `boot()`. + * + * It won't return unless no buttons are being pressed. A short delay is + * performed each time before testing the state of the buttons to do a + * simple button debounce. + * + * This function is called at the end of `begin()` to make sure no buttons + * used to perform system start up actions are still being pressed, to + * prevent them from erroneously being detected by the sketch code itself. + * + * \see begin() boot() + */ + static void waitNoButtons(); + + /** \brief + * Clear the display buffer. + * + * \details + * The entire contents of the screen buffer are cleared to BLACK. + * + * \see display(bool) fillScreen() + */ + static void clear(); + + /** \brief + * Fill the screen buffer with the specified color. + * + * \param color The fill color (optional; defaults to WHITE). + * + * \see clear() + */ + static void fillScreen(uint8_t color = WHITE); + + /** \brief + * Copy the contents of the display buffer to the display. + * + * \details + * The contents of the display buffer in RAM are copied to the display and + * will appear on the screen. + * + * \see display(bool) + */ + static void display(); + + /** \brief + * Copy the contents of the display buffer to the display. The display buffer + * can optionally be cleared. + * + * \param clear If `true` the display buffer will be cleared to zero. + * The defined value `CLEAR_BUFFER` should be used instead of `true` to make + * it more meaningful. + * + * \details + * Operation is the same as calling `display()` without parameters except + * additionally the display buffer will be cleared if the parameter evaluates + * to `true`. (The defined value `CLEAR_BUFFER` can be used for this) + * + * Using `display(CLEAR_BUFFER)` is faster and produces less code than + * calling `display()` followed by `clear()`. + * + * \see display() clear() + */ + static void display(bool clear); + + /** \brief + * Set a single pixel in the display buffer to the specified color. + * + * \param x The X coordinate of the pixel. + * \param y The Y coordinate of the pixel. + * \param color The color of the pixel (optional; defaults to WHITE). + * + * \details + * The single pixel specified location in the display buffer is set to the + * specified color. The values WHITE or BLACK can be used for the color. + * If the `color` parameter isn't included, the pixel will be set to WHITE. + */ + static void drawPixel(int16_t x, int16_t y, uint8_t color = WHITE); + + /** \brief + * Returns the state of the given pixel in the screen buffer. + * + * \param x The X coordinate of the pixel. + * \param y The Y coordinate of the pixel. + * + * \return WHITE if the pixel is on or BLACK if the pixel is off. + */ + static uint8_t getPixel(uint8_t x, uint8_t y); + + /** \brief + * Draw a circle of a given radius. + * + * \param x0 The X coordinate of the circle's center. + * \param y0 The Y coordinate of the circle's center. + * \param r The radius of the circle in pixels. + * \param color The circle's color (optional; defaults to WHITE). + * + * \see fillCircle() + */ + static void drawCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color = WHITE); + + /** \brief + * Draw a filled-in circle of a given radius. + * + * \param x0 The X coordinate of the circle's center. + * \param y0 The Y coordinate of the circle's center. + * \param r The radius of the circle in pixels. + * \param color The circle's color (optional; defaults to WHITE). + * + * \see drawCircle() + */ + static void fillCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color = WHITE); + + /** \brief + * Draw a line between two specified points. + * + * \param x0,x1 The X coordinates of the line ends. + * \param y0,y1 The Y coordinates of the line ends. + * \param color The line's color (optional; defaults to WHITE). + * + * \details + * Draw a line from the start point to the end point using + * Bresenham's algorithm. + * The start and end points can be at any location with respect to the other. + * + * \see drawFastHLine() drawFastVLine() + */ + static void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color = WHITE); + + /** \brief + * Draw a vertical line. + * + * \param x The X coordinate of the upper start point. + * \param y The Y coordinate of the upper start point. + * \param h The height of the line. + * \param color The color of the line (optional; defaults to WHITE). + * + * \see drawFastHLine() drawLine() + */ + static void drawFastVLine(int16_t x, int16_t y, uint8_t h, uint8_t color = WHITE); + + /** \brief + * Draw a horizontal line. + * + * \param x The X coordinate of the left start point. + * \param y The Y coordinate of the left start point. + * \param w The width of the line. + * \param color The color of the line (optional; defaults to WHITE). + * + * \see drawFastVLine() drawLine() + */ + static void drawFastHLine(int16_t x, int16_t y, uint8_t w, uint8_t color = WHITE); + + /** \brief + * Draw a rectangle of a specified width and height. + * + * \param x The X coordinate of the upper left corner. + * \param y The Y coordinate of the upper left corner. + * \param w The width of the rectangle. + * \param h The height of the rectangle. + * \param color The color of the pixel (optional; defaults to WHITE). + * + * \see fillRect() drawRoundRect() fillRoundRect() + */ + static void drawRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color = WHITE); + + /** \brief + * Draw a filled-in rectangle of a specified width and height. + * + * \param x The X coordinate of the upper left corner. + * \param y The Y coordinate of the upper left corner. + * \param w The width of the rectangle. + * \param h The height of the rectangle. + * \param color The color of the pixel (optional; defaults to WHITE). + * + * \see drawRect() drawRoundRect() fillRoundRect() + */ + static void fillRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color = WHITE); + + /** \brief + * Draw a rectangle with rounded corners. + * + * \param x The X coordinate of the left edge. + * \param y The Y coordinate of the top edge. + * \param w The width of the rectangle. + * \param h The height of the rectangle. + * \param r The radius of the semicircles forming the corners. + * \param color The color of the rectangle (optional; defaults to WHITE). + * + * \see fillRoundRect() drawRect() fillRect() + */ + static void drawRoundRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color = WHITE); + + /** \brief + * Draw a filled-in rectangle with rounded corners. + * + * \param x The X coordinate of the left edge. + * \param y The Y coordinate of the top edge. + * \param w The width of the rectangle. + * \param h The height of the rectangle. + * \param r The radius of the semicircles forming the corners. + * \param color The color of the rectangle (optional; defaults to WHITE). + * + * \see drawRoundRect() drawRect() fillRect() + */ + static void fillRoundRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color = WHITE); + + /** \brief + * Draw a triangle given the coordinates of each corner. + * + * \param x0,x1,x2 The X coordinates of the corners. + * \param y0,y1,y2 The Y coordinates of the corners. + * \param color The triangle's color (optional; defaults to WHITE). + * + * \details + * A triangle is drawn by specifying each of the three corner locations. + * The corners can be at any position with respect to the others. + * + * \see fillTriangle() + */ + static void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color = WHITE); + + /** \brief + * Draw a filled-in triangle given the coordinates of each corner. + * + * \param x0,x1,x2 The X coordinates of the corners. + * \param y0,y1,y2 The Y coordinates of the corners. + * \param color The triangle's color (optional; defaults to WHITE). + * + * \details + * A triangle is drawn by specifying each of the three corner locations. + * The corners can be at any position with respect to the others. + * + * \see drawTriangle() + */ + static void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color = WHITE); + + /** \brief + * Draw a bitmap from an array in program memory. + * + * \param x The X coordinate of the top left pixel affected by the bitmap. + * \param y The Y coordinate of the top left pixel affected by the bitmap. + * \param bitmap A pointer to the bitmap array in program memory. + * \param w The width of the bitmap in pixels. + * \param h The height of the bitmap in pixels. + * \param color The color of pixels for bits set to 1 in the bitmap. + * If the value is INVERT, bits set to 1 will invert the + * corresponding pixel. (optional; defaults to WHITE). + * + * \details + * Bits set to 1 in the provided bitmap array will have their corresponding + * pixel set to the specified color. For bits set to 0 in the array, the + * corresponding pixel will be left unchanged. + * + * Each byte in the array specifies a vertical column of 8 pixels, with the + * least significant bit at the top. + * + * The array must be located in program memory by using the PROGMEM modifier. + * + * \see drawCompressed() drawSlowXYBitmap() Sprites + */ + static void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE); + + /** \brief + * Draw a bitmap from a horizontally oriented array in program memory. + * + * \param x The X coordinate of the top left pixel affected by the bitmap. + * \param y The Y coordinate of the top left pixel affected by the bitmap. + * \param bitmap A pointer to the bitmap array in program memory. + * \param w The width of the bitmap in pixels. + * \param h The height of the bitmap in pixels. + * \param color The color of pixels for bits set to 1 in the bitmap. + * (optional; defaults to WHITE). + * + * \details + * Bits set to 1 in the provided bitmap array will have their corresponding + * pixel set to the specified color. For bits set to 0 in the array, the + * corresponding pixel will be left unchanged. + * + * Each byte in the array specifies a horizontal row of 8 pixels, with the + * most significant bit at the left end of the row. + * + * The array must be located in program memory by using the PROGMEM modifier. + * + * \note + * This function requires a lot of additional CPU power and will draw images + * slower than `drawBitmap()`, which uses bitmaps that are stored in a format + * that allows them to be directly written to the screen. It is recommended + * you use `drawBitmap()` when possible. + * + * \see drawBitmap() drawCompressed() + */ + static void drawSlowXYBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE); + + /** \brief + * Draw a bitmap from an array of compressed data. + * + * \param sx The X coordinate of the top left pixel affected by the bitmap. + * \param sy The Y coordinate of the top left pixel affected by the bitmap. + * \param bitmap A pointer to the compressed bitmap array in program memory. + * \param color The color of pixels for bits set to 1 in the bitmap. + * (optional; defaults to WHITE). + * + * \details + * Draw a bitmap starting at the given coordinates using an array that has + * been compressed using an RLE algorthm implemented by Team A.R.G. + * + * Bits set to 1 in the provided bitmap array (after decoding) will have + * their corresponding pixel set to the specified color. For bits set to 0 + * in the array, the corresponding pixel will be left unchanged. + * + * The array must be located in program memory by using the PROGMEM modifier. + * + * \note + * C source code for a command line program named `Cabi`, which can convert + * a PNG bitmap image file to source code suitable for use with + * `drawCompressed()`, is included in the `extras` directory of the library. + * + * \see drawBitmap() drawSlowXYBitmap() + */ + static void drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color = WHITE); + + /** \brief + * Get a pointer to the display buffer in RAM. + * + * \return A pointer to the display buffer array in RAM. + * + * \details + * The location of the display buffer in RAM, which is displayed using + * `display()`, can be gotten using this function. The buffer can then be + * read and directly manipulated. + * + * \note + * The display buffer array, `sBuffer`, is public. A sketch can access it + * directly. Doing so may be more efficient than accessing it via the + * pointer returned by `getBuffer()`. + * + * \see sBuffer + */ + static uint8_t* getBuffer(); + + /** \brief + * Seed the random number generator with a random value. + * + * \details + * The Arduino pseudorandom number generator is seeded with the random value + * returned from a call to `generateRandomSeed()`. + * + * \note + * This function will be more effective if called after a semi-random time, + * such as after waiting for the user to press a button to start a game, or + * another event that takes a variable amount of time after boot. + * + * \see generateRandomSeed() + */ + static void initRandomSeed(); + + /** \brief + * Set the frame rate used by the frame control functions. + * + * \param rate The desired frame rate in frames per second. + * + * \details + * Set the frame rate, in frames per second, used by `nextFrame()` to update + * frames at a given rate. If this function or `setFrameDuration()` + * isn't used, the default rate will be 60 (actually 62.5; see note below). + * + * Normally, the frame rate would be set to the desired value once, at the + * start of the game, but it can be changed at any time to alter the frame + * update rate. + * + * \note + * \parblock + * The given rate is internally converted to a frame duration in milliseconds, + * rounded down to the nearest integer. Therefore, the actual rate will be + * equal to or higher than the rate given. + + * For example, 60 FPS would be 16.67ms per frame. This will be rounded down + * to 16ms, giving an actual frame rate of 62.5 FPS. + * \endparblock + * + * \see nextFrame() setFrameDuration() + */ + static void setFrameRate(uint8_t rate); + + /** \brief + * Set the frame rate, used by the frame control functions, by giving + * the duration of each frame. + * + * \param duration The desired duration of each frame in milliseconds. + * + * \details + * Set the frame rate by specifying the duration of each frame in + * milliseconds. This is used by `nextFrame()` to update frames at a + * given rate. If this function or `setFrameRate()` isn't used, + * the default will be 16ms per frame. + * + * Normally, the frame rate would be set to the desired value once, at the + * start of the game, but it can be changed at any time to alter the frame + * update rate. + * + * \see nextFrame() setFrameRate() + */ + static void setFrameDuration(uint8_t duration); + + /** \brief + * Indicate that it's time to render the next frame. + * + * \return `true` if it's time for the next frame. + * + * \details + * When this function returns `true`, the amount of time has elapsed to + * display the next frame, as specified by `setFrameRate()` or + * `setFrameDuration()`. + * + * This function will normally be called at the start of the rendering loop + * which would wait for `true` to be returned before rendering and + * displaying the next frame. + * + * example: + * \code{.cpp} + * void loop() { + * if (!arduboy.nextFrame()) { + * return; // go back to the start of the loop + * } + * // render and display the next frame + * } + * \endcode + * + * \see setFrameRate() setFrameDuration() nextFrameDEV() + */ + static bool nextFrame(); + + /** \brief + * Indicate that it's time to render the next frame, and visually indicate + * if the code is running slower than the desired frame rate. + * **FOR USE DURING DEVELOPMENT** + * + * \return `true` if it's time for the next frame. + * + * \details + * This function is intended to be used in place of `nextFrame()` during the + * development of a sketch. It does the same thing as `nextFrame()` but + * additionally will light the yellow TX LED (at the bottom, to the left + * of the USB connector) whenever a frame takes longer to generate than the + * time allotted per frame, as determined by the `setFrameRate()` or + * `setFrameDuration()` function. + * + * Therefore, whenever the TX LED comes on (while not communicating over + * USB), it indicates that the sketch is running slower than the desired + * rate set by `setFrameRate()` or `setFrameDuration()`. In this case the + * developer may wish to set a slower frame rate, or reduce or optimize the + * code for such frames. + * + * \note + * Once a sketch is ready for release, it would be expected that + * `nextFrameDEV()` calls be restored to `nextFrame()`. + * + * \see nextFrame() cpuLoad() setFrameRate() setFrameDuration() + */ + static bool nextFrameDEV(); + + /** \brief + * Indicate if the specified number of frames has elapsed. + * + * \param frames The desired number of elapsed frames. + * + * \return `true` if the specified number of frames has elapsed. + * + * \details + * This function should be called with the same value each time for a given + * event. It will return `true` if the given number of frames has elapsed + * since the previous frame in which it returned `true`. + * + * For example, if you wanted to fire a shot every 5 frames while the A button + * is being held down: + * + * \code{.cpp} + * if (arduboy.everyXFrames(5)) { + * if arduboy.pressed(A_BUTTON) { + * fireShot(); + * } + * } + * \endcode + * + * \see setFrameRate() setFrameDuration() nextFrame() + */ + static bool everyXFrames(uint8_t frames); + + /** \brief + * Return the load on the CPU as a percentage. + * + * \return The load on the CPU as a percentage of the total frame time. + * + * \details + * The returned value gives the time spent processing a frame as a percentage + * the total time allotted for a frame, as determined by the frame rate. + * + * This function normally wouldn't be used in the final program. It is + * intended for use during program development as an aid in helping with + * frame timing. + * + * \note + * The percentage returned can be higher than 100 if more time is spent + * processing a frame than the time allotted per frame. This would indicate + * that the frame rate should be made slower or the frame processing code + * should be optimized to run faster. + * + * \see nextFrameDEV() setFrameRate() setFrameDuration() nextFrame() + */ + static int cpuLoad(); + + /** \brief + * Test if the all of the specified buttons are pressed. + * + * \param buttons A bit mask indicating which buttons to test. + * (Can be a single button) + * + * \return `true` if *all* buttons in the provided mask are currently pressed. + * + * \details + * Read the state of the buttons and return `true` if all of the buttons in + * the specified mask are being pressed. + * + * Example: `if (pressed(LEFT_BUTTON | A_BUTTON))` + * + * \note + * This function does not perform any button debouncing. + * + * \see anyPressed() notPressed() + */ + static bool pressed(uint8_t buttons); + + /** \brief + * Test if any of the specified buttons are pressed. + * + * \param buttons A bit mask indicating which buttons to test. + * (Can be a single button) + * + * \return `true` if *one or more* of the buttons in the provided mask are + * currently pressed. + * + * \details + * Read the state of the buttons and return `true` if one or more of the + * buttons in the specified mask are being pressed. + * + * Example: `if (anyPressed(RIGHT_BUTTON | LEFT_BUTTON))` + * + * \note + * This function does not perform any button debouncing. + * + * \see pressed() notPressed() + */ + static bool anyPressed(uint8_t buttons); + + /** \brief + * Test if the specified buttons are not pressed. + * + * \param buttons A bit mask indicating which buttons to test. + * (Can be a single button) + * + * \return `true` if *all* buttons in the provided mask are currently + * released. + * + * \details + * Read the state of the buttons and return `true` if all the buttons in the + * specified mask are currently released. + * + * Example: `if (notPressed(UP_BUTTON))` + * + * \note + * This function does not perform any button debouncing. + * + * \see pressed() anyPressed() + */ + static bool notPressed(uint8_t buttons); + + /** \brief + * Poll the buttons and track their state over time. + * + * \details + * Read and save the current state of the buttons and also keep track of the + * button state when this function was previously called. These states are + * used by the `justPressed()` and `justReleased()` functions to determine + * if a button has changed state between now and the previous call to + * `pollButtons()`. + * + * This function should be called once at the start of each new frame. + * + * The `justPressed()` and `justReleased()` functions rely on this function. + * + * example: + * \code{.cpp} + * void loop() { + * if (!arduboy.nextFrame()) { + * return; + * } + * arduboy.pollButtons(); + * + * // use justPressed() as necessary to determine if a button was just pressed + * \endcode + * + * \note + * As long as the elapsed time between calls to this function is long + * enough, buttons will be naturally debounced. Calling it once per frame at + * a frame rate of 60 or lower (or possibly somewhat higher), should be + * sufficient. + * + * \see justPressed() justReleased() + */ + static void pollButtons(); + + /** \brief + * Check if a button has just been pressed. + * + * \param button The button to test for. Only one button should be specified. + * + * \return `true` if the specified button has just been pressed. + * + * \details + * Return `true` if the given button was pressed between the latest + * call to `pollButtons()` and previous call to `pollButtons()`. + * If the button has been held down over multiple polls, this function will + * return `false`. + * + * There is no need to check for the release of the button since it must have + * been released for this function to return `true` when pressed again. + * + * This function should only be used to test a single button. + * + * \see pollButtons() justReleased() + */ + static bool justPressed(uint8_t button); + + /** \brief + * Check if a button has just been released. + * + * \param button The button to test for. Only one button should be specified. + * + * \return `true` if the specified button has just been released. + * + * \details + * Return `true` if the given button, having previously been pressed, + * was released between the latest call to `pollButtons()` and previous call + * to `pollButtons()`. If the button has remained released over multiple + * polls, this function will return `false`. + * + * There is no need to check for the button having been pressed since it must + * have been previously pressed for this function to return `true` upon + * release. + * + * This function should only be used to test a single button. + * + * \note + * There aren't many cases where this function would be needed. Wanting to + * know if a button has been released, without knowing when it was pressed, + * is uncommon. + * + * \see pollButtons() justPressed() + */ + static bool justReleased(uint8_t button); + + /** \brief + * Test if a point falls within a rectangle. + * + * \param point A structure describing the location of the point. + * \param rect A structure describing the location and size of the rectangle. + * + * \return `true` if the specified point is within the specified rectangle. + * + * \details + * This function is intended to detemine if an object, whose boundaries are + * are defined by the given rectangle, is in contact with the given point. + * + * \see Point Rect + */ + static bool collide(Point point, Rect rect); + + /** \brief + * Test if a rectangle is intersecting with another rectangle. + * + * \param rect1,rect2 Structures describing the size and locations of the + * rectangles. + * + * \return `true` if the first rectangle is intersecting the second. + * + * \details + * This function is intended to detemine if an object, whose boundaries are + * are defined by the given rectangle, is in contact with another rectangular + * object. + * + * \see Rect + */ + static 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() + */ + static 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() + */ + static void writeUnitID(uint16_t id); + + /** \brief + * Read the unit name from system EEPROM. + * + * \param name A pointer to the first element of a `char` array in which the + * unit name will be written. The name will be up to `ARDUBOY_UNIT_NAME_LEN` + * characters in length and additionally terminated with a null (0x00) + * character, so **the provided array MUST be at least + * `ARDUBOY_UNIT_NAME_BUFFER_SIZE` characters long**. + * Using `ARDUBOY_UNIT_NAME_BUFFER_SIZE` to specify the array length is the + * proper way to do this, although any array larger than + * `ARDUBOY_UNIT_NAME_BUFFER_SIZE` is also acceptable. + * + * \return The length of the string (between 0 and + * `ARDUBOY_UNIT_NAME_LEN` *inclusive*). + * + * \details + * This function reads the unit name that has been set in system EEPROM. + * The name represents characters in the library's `font5x7` font. It can + * contain any values except 0xFF and the null (0x00) terminator value, plus + * the ASCII newline/line feed character (`\n`, 0x0A, inverse white circle) + * and ASCII carriage return character (`\r`, 0x0D, musical eighth note). + * + * 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 + * The defined value `ARDUBOY_UNIT_NAME_BUFFER_SIZE` should be used to + * allocate an array to hold the unit name string, instead of using a + * hard coded value for the size. + * For example, to allocate a buffer and read the unit name into it: + * \code{.cpp} + * // Buffer large enough to hold the unit name and a null terminator + * char unitName[ARDUBOY_UNIT_NAME_BUFFER_SIZE]; + * + * // After the call, unitNameLength will contain the actual name length, + * // not including the null terminator. + * uint8_t unitNameLength = arduboy.readUnitName(unitName); + * \endcode + * + * \see writeUnitName() readUnitID() Arduboy2::bootLogoExtra() + * ARDUBOY_UNIT_NAME_BUFFER_SIZE ARDUBOY_UNIT_NAME_LEN Arduboy2::font5x7 + */ + static uint8_t readUnitName(char* name); + + /** \brief + * Write a unit name to system EEPROM. + * + * \param name A pointer to the first element of a C-style null-terminated + * string containing the unit name to be saved. The name can be up to + * `ARDUBOY_UNIT_NAME_LEN` characters long and must be terminated with a + * null (0x00) character. + * + * \details + * This function writes a unit name to a reserved area in system EEPROM. + * The name represents characters in the library's `font5x7` font. It can + * contain any values except 0xFF and the null (0x00) terminator value, plus + * the ASCII newline/line feed character (`\n`, 0x0A, inverse white circle) + * and ASCII carriage return character (`\r`, 0x0D, musical eighth note) + * because of their special use by the library's text handling functions. + * + * 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 + * The defined value `ARDUBOY_UNIT_NAME_BUFFER_SIZE` should be used to + * allocate an array to hold the unit name string, instead of using a + * hard coded value for the size. + * + * \see readUnitName() writeUnitID() Arduboy2::bootLogoExtra() + * ARDUBOY_UNIT_NAME_BUFFER_SIZE ARDUBOY_UNIT_NAME_LEN Arduboy2::font5x7 + */ + static void writeUnitName(const char* name); + + /** \brief + * Read the "Show Boot Logo" flag in system EEPROM. + * + * \return `true` if the flag is set to indicate that the boot logo sequence + * should be displayed. `false` if the flag is set to not display the + * boot logo sequence. + * + * \details + * The "Show Boot Logo" flag is used to determine whether the system + * boot logo sequence is to be displayed when the system boots up. + * This function returns the value of this flag. + * + * \see writeShowBootLogoFlag() bootLogo() + */ + static bool readShowBootLogoFlag(); + + /** \brief + * Write the "Show Boot Logo" flag in system EEPROM. + * + * \param val If `true` the flag is set to indicate that the boot logo + * sequence should be displayed. If `false` the flag is set to not display + * the boot logo sequence. + * + * \details + * The "Show Boot Logo" flag is used to determine whether the system + * boot logo sequence is to be displayed when the system boots up. + * This function allows the flag to be saved with the desired value. + * + * \see readShowBootLogoFlag() bootLogo() + */ + static void writeShowBootLogoFlag(bool val); + + /** \brief + * Read the "Show Unit Name" flag in system EEPROM. + * + * \return `true` if the flag is set to indicate that the unit name should + * be displayed. `false` if the flag is set to not display the unit name. + * + * \details + * The "Show Unit Name" flag is used to determine whether the system + * unit name is to be displayed at the end of the boot logo sequence. + * This function returns the value of this flag. + * + * \see writeShowUnitNameFlag() writeUnitName() readUnitName() + * Arduboy2::bootLogoExtra() + */ + static bool readShowUnitNameFlag(); + + /** \brief + * Write the "Show Unit Name" flag in system EEPROM. + * + * \param val If `true` the flag is set to indicate that the unit name should + * be displayed. If `false` the flag is set to not display the unit name. + * + * \details + * The "Show Unit Name" flag is used to determine whether the system + * unit name is to be displayed at the end of the boot logo sequence. + * This function allows the flag to be saved with the desired value. + * + * \see readShowUnitNameFlag() writeUnitName() readUnitName() + * Arduboy2::bootLogoExtra() + */ + static void writeShowUnitNameFlag(bool val); + + /** \brief + * Read the "Show LEDs with boot logo" flag in system EEPROM. + * + * \return `true` if the flag is set to indicate that the RGB LEDs should be + * flashed. `false` if the flag is set to leave the LEDs off. + * + * \details + * The "Show LEDs with boot logo" flag is used to determine whether the + * RGB LEDs should be flashed in sequence while the boot logo is being + * displayed. This function returns the value of this flag. + * + * \see writeShowBootLogoLEDsFlag() + */ + static bool readShowBootLogoLEDsFlag(); + + /** \brief + * Write the "Show LEDs with boot logo" flag in system EEPROM. + * + * \param val If `true` the flag is set to indicate that the RGB LEDs should + * be flashed. If `false` the flag is set to leave the LEDs off. + * + * \details + * The "Show LEDs with boot logo" flag is used to determine whether the + * RGB LEDs should be flashed in sequence while the boot logo is being + * displayed. This function allows the flag to be saved with the desired + * value. + * + * \see readShowBootLogoLEDsFlag() + */ + static void writeShowBootLogoLEDsFlag(bool val); + + /** \brief + * A counter which is incremented once per frame. + * + * \details + * This counter is incremented once per frame when using the `nextFrame()` + * function. It will wrap to zero when it reaches its maximum value. + * + * It could be used to have an event occur for a given number of frames, or + * a given number of frames later, in a way that wouldn't be quantized the + * way that using `everyXFrames()` might. + * + * example: + * \code{.cpp} + * // move for 10 frames when right button is pressed, if not already moving + * if (!moving) { + * if (arduboy.justPressed(RIGHT_BUTTON)) { + * endMoving = arduboy.frameCount + 10; + * moving = true; + * } + * } else { + * movePlayer(); + * if (arduboy.frameCount == endMoving) { + * moving = false; + * } + * } + * \endcode + * + * This counter could also be used to determine the number of frames that + * have elapsed between events but the possibility of the counter wrapping + * would have to be accounted for. + * + * \see nextFrame() everyXFrames() + */ + static uint16_t frameCount; + + /** \brief + * The display buffer array in RAM. + * + * \details + * The display buffer (also known as the screen buffer) contains an + * image bitmap of the desired contents of the display, which is written + * to the display using the `display()` function. The drawing functions of + * this library manipulate the contents of the display buffer. A sketch can + * also access the display buffer directly. + * + * \see getBuffer() + */ + static uint8_t sBuffer[(HEIGHT*WIDTH)/8]; + + /** \brief + * The bitmap for the ARDUBOY logo in `drawBitmap()` format. + * + * \see bootLogo() drawBitmap() + */ + static const PROGMEM uint8_t arduboy_logo[]; + + /** \brief + * The bitmap for the ARDUBOY logo in `drawCompressed()` format. + * + * \see bootLogoCompressed() drawCompressed() + */ + static const PROGMEM uint8_t arduboy_logo_compressed[]; + + /** \brief + * The bitmap for the ARDUBOY logo in `Sprites` class + * `drawSelfMasked()` or `drawOverwrite()` format. + * + * \see bootLogoSpritesSelfMasked() bootLogoSpritesOverwrite() + * bootLogoSpritesBSelfMasked() bootLogoSpritesBOverwrite() + */ + static const PROGMEM uint8_t arduboy_logo_sprite[]; + + protected: + // helper function for sound enable/disable system control + static void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal); + + // functions passed to bootLogoShell() to draw the logo + static void drawLogoBitmap(int16_t y); + static void drawLogoCompressed(int16_t y); + static void drawLogoSpritesSelfMasked(int16_t y); + static void drawLogoSpritesOverwrite(int16_t y); + static void drawLogoSpritesBSelfMasked(int16_t y); + static void drawLogoSpritesBOverwrite(int16_t y); + + // draw one or more "corners" of a circle + static void drawCircleHelper(int16_t x0, int16_t y0, uint8_t r, uint8_t corners, + uint8_t color = WHITE); + + // draw one or both vertical halves of a filled-in circle or + // rounded rectangle edge + static void fillCircleHelper(int16_t x0, int16_t y0, uint8_t r, + uint8_t sides, int16_t delta, uint8_t color = WHITE); + + // helper for drawCompressed() + class BitStreamReader; + + // swap the values of two int16_t variables passed by reference + static void swapInt16(int16_t& a, int16_t& b); + + // For button handling + static uint8_t currentButtonState; + static uint8_t previousButtonState; + + // For frame functions + static uint8_t eachFrameMillis; + static uint8_t thisFrameStart; + static uint8_t lastFrameDurationMs; + static bool justRendered; + + // ----- Map of EEPROM addresses for system use----- + + // EEPROM address 0 is reserved for bootloader use + // This library will not touch it + + // Control flags + static constexpr uint16_t eepromSysFlags = 1; + // Audio mute control. 0 = audio off, non-zero = audio on + static constexpr uint16_t eepromAudioOnOff = 2; + // -- Addresses 3-7 are currently reserved for future use -- + // A uint16_t binary unit ID + static constexpr uint16_t eepromUnitID = 8; // A uint16_t binary unit ID + // An up to 6 character unit name + // The name cannot contain 0x00, 0xFF, 0x0A, 0x0D + // Lengths less than 6 are padded with 0x00 + static constexpr uint16_t eepromUnitName = 10; + // -- User EEPROM space starts at address 16 -- + + // --- Map of the bits in the eepromSysFlags byte -- + // Display the unit name on the logo screen + static constexpr uint8_t sysFlagUnameBit = 0; + static constexpr uint8_t sysFlagUnameMask = _BV(sysFlagUnameBit); + // Show the logo sequence during boot up + static constexpr uint8_t sysFlagShowLogoBit = 1; + static constexpr uint8_t sysFlagShowLogoMask = _BV(sysFlagShowLogoBit); + // Flash the RGB led during the boot logo + static constexpr uint8_t sysFlagShowLogoLEDsBit = 2; + static constexpr uint8_t sysFlagShowLogoLEDsMask = _BV(sysFlagShowLogoLEDsBit); +}; + + +//============================== +//========== Arduboy2 ========== +//============================== + +/** \brief + * The main functions provided for writing sketches for the Arduboy, + * _including_ text output. + * + * \details + * This class is derived from Arduboy2Base. It provides text output functions + * in addition to all the functions inherited from Arduboy2Base. + * + * \note + * A friend class named _Arduboy2Ex_ is declared by this class. The intention + * is to allow a sketch to create an _Arduboy2Ex_ class which would have access + * to the private and protected members of the Arduboy2 class. It is hoped + * that this may eliminate the need to create an entire local copy of the + * library, in order to extend the functionality, in most circumstances. + * + * \see Arduboy2Base + */ +class Arduboy2 : public Print, public Arduboy2Base +{ + friend class Arduboy2Ex; + + public: + + /** \class Print + * \brief + * The Arduino `Print` class is available for writing text to the screen + * buffer. + * + * \details + * For an `Arduboy2` class object, functions provided by the Arduino `Print` + * class can be used to write text to the screen buffer, in the same manner + * as the Arduino `Serial.print()`, etc., functions. + * + * Print will use the `write()` function to actually draw each character + * in the screen buffer, using the library's `font5x7` font. + * Two character values are handled specially: + * + * - ASCII newline/line feed (`\n`, 0x0A, inverse white circle). + * This will move the text cursor position to the start of the next line, + * based on the current text size. + * - ASCII carriage return (`\r`, 0x0D, musical eighth note). + * This character will be ignored. + * + * To override the special handling of the above values, to allow the + * characters they represent to be printed, text _raw_ mode can be selected + * using the `setTextRawMode()` function. + * + * See: + * https://www.arduino.cc/reference/en/language/functions/communication/serial/print/ + * + * Example: + * \code{.cpp} + * int value = 42; + * + * arduboy.println("Hello World"); // Prints "Hello World" and then sets the + * // text cursor to the start of the next line + * arduboy.print(value); // Prints "42" + * arduboy.print('\n'); // Sets the text cursor to the start of the next line + * arduboy.print(78, HEX); // Prints "4E" (78 in hexadecimal) + * arduboy.print("\x03\xEA"); // Prints a heart symbol and a Greek uppercase omega + * + * arduboy.setTextRawMode(true); // Set text "raw" mode + * arduboy.print("\r\n") // Prints a "musical eighth note" + * // followed by an "inverse white circle" + * // because we're in "raw" mode + * arduboy.setTextRawMode(false); // Exit text "raw" mode + * \endcode + * + * \see Arduboy2::setTextSize() Arduboy2::setTextColor() + * Arduboy2::setTextBackground() Arduboy2::setTextWrap() + * Arduboy2::setTextRawMode() Arduboy2::write() Arduboy2::font5x7 + */ + using Print::write; + + /** \brief + * Initialize the hardware, display the boot logo, provide boot utilities, etc. + * + * \details + * This function should be called once near the start of the sketch, + * usually in `setup()`, before using any other functions in this class. + * It initializes the display, displays the boot logo, provides "flashlight" + * and system control features and initializes audio control. + * + * \note + * \parblock + * If it becomes necessary to free up some code space for use by the sketch, + * `boot()` can be used instead of `begin()` to allow the elimination of + * some of the things that aren't absolutely required. + * + * See the README file or main page, in section + * _Substitute or remove boot up features_, for more details. + * \endparblock + * + * \see boot() + */ + void begin(); /** \brief * Display the boot logo sequence using `drawBitmap()`. @@ -369,1171 +1660,6 @@ class Arduboy2Base : public Arduboy2Core */ void bootLogoSpritesBOverwrite(); - /** \brief - * Display the boot logo sequence using the provided function - * - * \param drawLogo A reference to a function which will draw the boot logo - * at the given Y position. - * - * \details - * This common function executes the sequence to display the boot logo. - * It is called by `bootLogo()` and other similar functions which provide it - * with a reference to a function which will do the actual drawing of the - * logo. - * - * 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. - * - * If the RIGHT button is pressed while the logo is scrolling down, - * the boot logo sequence will be aborted. This can be useful for - * developers who wish to quickly start testing, or anyone else who is - * impatient and wants to go straight to the actual sketch. - * - * If the "Show LEDs with boot logo" flag in system EEPROM is cleared, - * the RGB LEDs will not be flashed during the logo display sequence. - * - * If the "Show Boot Logo" flag in system EEPROM is cleared, this function - * will return without executing the logo display sequence. - * - * The prototype for the function provided to draw the logo is: - * - * \code{.cpp} - * void drawLogo(int16_t y); - * \endcode - * - * The y parameter is the Y offset for the top of the logo. It is expected - * that the logo will be 16 pixels high and centered horizontally. This will - * result in the logo stopping in the middle of the screen at the end of the - * sequence. If the logo height is not 16 pixels, the Y value can be adjusted - * to compensate. - * - * \see bootLogo() boot() Arduboy2::bootLogoExtra() - */ - void bootLogoShell(void (*drawLogo)(int16_t)); - - // Called by bootLogoShell() to allow derived classes to display additional - // information after the logo stops scrolling down. - virtual void bootLogoExtra(); - - /** \brief - * Wait until all buttons have been released. - * - * \details - * This function is called by `begin()` and can be called by a sketch - * after `boot()`. - * - * It won't return unless no buttons are being pressed. A short delay is - * performed each time before testing the state of the buttons to do a - * simple button debounce. - * - * This function is called at the end of `begin()` to make sure no buttons - * used to perform system start up actions are still being pressed, to - * prevent them from erroneously being detected by the sketch code itself. - * - * \see begin() boot() - */ - void waitNoButtons(); - - /** \brief - * Clear the display buffer. - * - * \details - * The entire contents of the screen buffer are cleared to BLACK. - * - * \see display(bool) fillScreen() - */ - void clear(); - - /** \brief - * Fill the screen buffer with the specified color. - * - * \param color The fill color (optional; defaults to WHITE). - * - * \see clear() - */ - void fillScreen(uint8_t color = WHITE); - - /** \brief - * Copy the contents of the display buffer to the display. - * - * \details - * The contents of the display buffer in RAM are copied to the display and - * will appear on the screen. - * - * \see display(bool) - */ - void display(); - - /** \brief - * Copy the contents of the display buffer to the display. The display buffer - * can optionally be cleared. - * - * \param clear If `true` the display buffer will be cleared to zero. - * The defined value `CLEAR_BUFFER` should be used instead of `true` to make - * it more meaningful. - * - * \details - * Operation is the same as calling `display()` without parameters except - * additionally the display buffer will be cleared if the parameter evaluates - * to `true`. (The defined value `CLEAR_BUFFER` can be used for this) - * - * Using `display(CLEAR_BUFFER)` is faster and produces less code than - * calling `display()` followed by `clear()`. - * - * \see display() clear() - */ - void display(bool clear); - - /** \brief - * Set a single pixel in the display buffer to the specified color. - * - * \param x The X coordinate of the pixel. - * \param y The Y coordinate of the pixel. - * \param color The color of the pixel (optional; defaults to WHITE). - * - * \details - * The single pixel specified location in the display buffer is set to the - * specified color. The values WHITE or BLACK can be used for the color. - * If the `color` parameter isn't included, the pixel will be set to WHITE. - */ - static void drawPixel(int16_t x, int16_t y, uint8_t color = WHITE); - - /** \brief - * Returns the state of the given pixel in the screen buffer. - * - * \param x The X coordinate of the pixel. - * \param y The Y coordinate of the pixel. - * - * \return WHITE if the pixel is on or BLACK if the pixel is off. - */ - uint8_t getPixel(uint8_t x, uint8_t y); - - /** \brief - * Draw a circle of a given radius. - * - * \param x0 The X coordinate of the circle's center. - * \param y0 The Y coordinate of the circle's center. - * \param r The radius of the circle in pixels. - * \param color The circle's color (optional; defaults to WHITE). - * - * \see fillCircle() - */ - void drawCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color = WHITE); - - /** \brief - * Draw a filled-in circle of a given radius. - * - * \param x0 The X coordinate of the circle's center. - * \param y0 The Y coordinate of the circle's center. - * \param r The radius of the circle in pixels. - * \param color The circle's color (optional; defaults to WHITE). - * - * \see drawCircle() - */ - void fillCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color = WHITE); - - /** \brief - * Draw a line between two specified points. - * - * \param x0,x1 The X coordinates of the line ends. - * \param y0,y1 The Y coordinates of the line ends. - * \param color The line's color (optional; defaults to WHITE). - * - * \details - * Draw a line from the start point to the end point using - * Bresenham's algorithm. - * The start and end points can be at any location with respect to the other. - * - * \see drawFastHLine() drawFastVLine() - */ - void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color = WHITE); - - /** \brief - * Draw a vertical line. - * - * \param x The X coordinate of the upper start point. - * \param y The Y coordinate of the upper start point. - * \param h The height of the line. - * \param color The color of the line (optional; defaults to WHITE). - * - * \see drawFastHLine() drawLine() - */ - void drawFastVLine(int16_t x, int16_t y, uint8_t h, uint8_t color = WHITE); - - /** \brief - * Draw a horizontal line. - * - * \param x The X coordinate of the left start point. - * \param y The Y coordinate of the left start point. - * \param w The width of the line. - * \param color The color of the line (optional; defaults to WHITE). - * - * \see drawFastVLine() drawLine() - */ - void drawFastHLine(int16_t x, int16_t y, uint8_t w, uint8_t color = WHITE); - - /** \brief - * Draw a rectangle of a specified width and height. - * - * \param x The X coordinate of the upper left corner. - * \param y The Y coordinate of the upper left corner. - * \param w The width of the rectangle. - * \param h The height of the rectangle. - * \param color The color of the pixel (optional; defaults to WHITE). - * - * \see fillRect() drawRoundRect() fillRoundRect() - */ - void drawRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color = WHITE); - - /** \brief - * Draw a filled-in rectangle of a specified width and height. - * - * \param x The X coordinate of the upper left corner. - * \param y The Y coordinate of the upper left corner. - * \param w The width of the rectangle. - * \param h The height of the rectangle. - * \param color The color of the pixel (optional; defaults to WHITE). - * - * \see drawRect() drawRoundRect() fillRoundRect() - */ - void fillRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color = WHITE); - - /** \brief - * Draw a rectangle with rounded corners. - * - * \param x The X coordinate of the left edge. - * \param y The Y coordinate of the top edge. - * \param w The width of the rectangle. - * \param h The height of the rectangle. - * \param r The radius of the semicircles forming the corners. - * \param color The color of the rectangle (optional; defaults to WHITE). - * - * \see fillRoundRect() drawRect() fillRect() - */ - void drawRoundRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color = WHITE); - - /** \brief - * Draw a filled-in rectangle with rounded corners. - * - * \param x The X coordinate of the left edge. - * \param y The Y coordinate of the top edge. - * \param w The width of the rectangle. - * \param h The height of the rectangle. - * \param r The radius of the semicircles forming the corners. - * \param color The color of the rectangle (optional; defaults to WHITE). - * - * \see drawRoundRect() drawRect() fillRect() - */ - void fillRoundRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color = WHITE); - - /** \brief - * Draw a triangle given the coordinates of each corner. - * - * \param x0,x1,x2 The X coordinates of the corners. - * \param y0,y1,y2 The Y coordinates of the corners. - * \param color The triangle's color (optional; defaults to WHITE). - * - * \details - * A triangle is drawn by specifying each of the three corner locations. - * The corners can be at any position with respect to the others. - * - * \see fillTriangle() - */ - void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color = WHITE); - - /** \brief - * Draw a filled-in triangle given the coordinates of each corner. - * - * \param x0,x1,x2 The X coordinates of the corners. - * \param y0,y1,y2 The Y coordinates of the corners. - * \param color The triangle's color (optional; defaults to WHITE). - * - * \details - * A triangle is drawn by specifying each of the three corner locations. - * The corners can be at any position with respect to the others. - * - * \see drawTriangle() - */ - void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color = WHITE); - - /** \brief - * Draw a bitmap from an array in program memory. - * - * \param x The X coordinate of the top left pixel affected by the bitmap. - * \param y The Y coordinate of the top left pixel affected by the bitmap. - * \param bitmap A pointer to the bitmap array in program memory. - * \param w The width of the bitmap in pixels. - * \param h The height of the bitmap in pixels. - * \param color The color of pixels for bits set to 1 in the bitmap. - * If the value is INVERT, bits set to 1 will invert the - * corresponding pixel. (optional; defaults to WHITE). - * - * \details - * Bits set to 1 in the provided bitmap array will have their corresponding - * pixel set to the specified color. For bits set to 0 in the array, the - * corresponding pixel will be left unchanged. - * - * Each byte in the array specifies a vertical column of 8 pixels, with the - * least significant bit at the top. - * - * The array must be located in program memory by using the PROGMEM modifier. - * - * \see drawCompressed() drawSlowXYBitmap() Sprites - */ - static void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE); - - /** \brief - * Draw a bitmap from a horizontally oriented array in program memory. - * - * \param x The X coordinate of the top left pixel affected by the bitmap. - * \param y The Y coordinate of the top left pixel affected by the bitmap. - * \param bitmap A pointer to the bitmap array in program memory. - * \param w The width of the bitmap in pixels. - * \param h The height of the bitmap in pixels. - * \param color The color of pixels for bits set to 1 in the bitmap. - * (optional; defaults to WHITE). - * - * \details - * Bits set to 1 in the provided bitmap array will have their corresponding - * pixel set to the specified color. For bits set to 0 in the array, the - * corresponding pixel will be left unchanged. - * - * Each byte in the array specifies a horizontal row of 8 pixels, with the - * most significant bit at the left end of the row. - * - * The array must be located in program memory by using the PROGMEM modifier. - * - * \note - * This function requires a lot of additional CPU power and will draw images - * slower than `drawBitmap()`, which uses bitmaps that are stored in a format - * that allows them to be directly written to the screen. It is recommended - * you use `drawBitmap()` when possible. - * - * \see drawBitmap() drawCompressed() - */ - void drawSlowXYBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color = WHITE); - - /** \brief - * Draw a bitmap from an array of compressed data. - * - * \param sx The X coordinate of the top left pixel affected by the bitmap. - * \param sy The Y coordinate of the top left pixel affected by the bitmap. - * \param bitmap A pointer to the compressed bitmap array in program memory. - * \param color The color of pixels for bits set to 1 in the bitmap. - * (optional; defaults to WHITE). - * - * \details - * Draw a bitmap starting at the given coordinates using an array that has - * been compressed using an RLE algorthm implemented by Team A.R.G. - * - * Bits set to 1 in the provided bitmap array (after decoding) will have - * their corresponding pixel set to the specified color. For bits set to 0 - * in the array, the corresponding pixel will be left unchanged. - * - * The array must be located in program memory by using the PROGMEM modifier. - * - * \note - * C source code for a command line program named `Cabi`, which can convert - * a PNG bitmap image file to source code suitable for use with - * `drawCompressed()`, is included in the `extras` directory of the library. - * - * \see drawBitmap() drawSlowXYBitmap() - */ - static void drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color = WHITE); - - /** \brief - * Get a pointer to the display buffer in RAM. - * - * \return A pointer to the display buffer array in RAM. - * - * \details - * The location of the display buffer in RAM, which is displayed using - * `display()`, can be gotten using this function. The buffer can then be - * read and directly manipulated. - * - * \note - * The display buffer array, `sBuffer`, is public. A sketch can access it - * directly. Doing so may be more efficient than accessing it via the - * pointer returned by `getBuffer()`. - * - * \see sBuffer - */ - uint8_t* getBuffer(); - - /** \brief - * Seed the random number generator with a random value. - * - * \details - * The Arduino pseudorandom number generator is seeded with the random value - * returned from a call to `generateRandomSeed()`. - * - * \note - * This function will be more effective if called after a semi-random time, - * such as after waiting for the user to press a button to start a game, or - * another event that takes a variable amount of time after boot. - * - * \see generateRandomSeed() - */ - void initRandomSeed(); - - /** \brief - * Set the frame rate used by the frame control functions. - * - * \param rate The desired frame rate in frames per second. - * - * \details - * Set the frame rate, in frames per second, used by `nextFrame()` to update - * frames at a given rate. If this function or `setFrameDuration()` - * isn't used, the default rate will be 60 (actually 62.5; see note below). - * - * Normally, the frame rate would be set to the desired value once, at the - * start of the game, but it can be changed at any time to alter the frame - * update rate. - * - * \note - * \parblock - * The given rate is internally converted to a frame duration in milliseconds, - * rounded down to the nearest integer. Therefore, the actual rate will be - * equal to or higher than the rate given. - - * For example, 60 FPS would be 16.67ms per frame. This will be rounded down - * to 16ms, giving an actual frame rate of 62.5 FPS. - * \endparblock - * - * \see nextFrame() setFrameDuration() - */ - void setFrameRate(uint8_t rate); - - /** \brief - * Set the frame rate, used by the frame control functions, by giving - * the duration of each frame. - * - * \param duration The desired duration of each frame in milliseconds. - * - * \details - * Set the frame rate by specifying the duration of each frame in - * milliseconds. This is used by `nextFrame()` to update frames at a - * given rate. If this function or `setFrameRate()` isn't used, - * the default will be 16ms per frame. - * - * Normally, the frame rate would be set to the desired value once, at the - * start of the game, but it can be changed at any time to alter the frame - * update rate. - * - * \see nextFrame() setFrameRate() - */ - void setFrameDuration(uint8_t duration); - - /** \brief - * Indicate that it's time to render the next frame. - * - * \return `true` if it's time for the next frame. - * - * \details - * When this function returns `true`, the amount of time has elapsed to - * display the next frame, as specified by `setFrameRate()` or - * `setFrameDuration()`. - * - * This function will normally be called at the start of the rendering loop - * which would wait for `true` to be returned before rendering and - * displaying the next frame. - * - * example: - * \code{.cpp} - * void loop() { - * if (!arduboy.nextFrame()) { - * return; // go back to the start of the loop - * } - * // render and display the next frame - * } - * \endcode - * - * \see setFrameRate() setFrameDuration() nextFrameDEV() - */ - bool nextFrame(); - - /** \brief - * Indicate that it's time to render the next frame, and visually indicate - * if the code is running slower than the desired frame rate. - * **FOR USE DURING DEVELOPMENT** - * - * \return `true` if it's time for the next frame. - * - * \details - * This function is intended to be used in place of `nextFrame()` during the - * development of a sketch. It does the same thing as `nextFrame()` but - * additionally will light the yellow TX LED (at the bottom, to the left - * of the USB connector) whenever a frame takes longer to generate than the - * time allotted per frame, as determined by the `setFrameRate()` or - * `setFrameDuration()` function. - * - * Therefore, whenever the TX LED comes on (while not communicating over - * USB), it indicates that the sketch is running slower than the desired - * rate set by `setFrameRate()` or `setFrameDuration()`. In this case the - * developer may wish to set a slower frame rate, or reduce or optimize the - * code for such frames. - * - * \note - * Once a sketch is ready for release, it would be expected that - * `nextFrameDEV()` calls be restored to `nextFrame()`. - * - * \see nextFrame() cpuLoad() setFrameRate() setFrameDuration() - */ - bool nextFrameDEV(); - - /** \brief - * Indicate if the specified number of frames has elapsed. - * - * \param frames The desired number of elapsed frames. - * - * \return `true` if the specified number of frames has elapsed. - * - * \details - * This function should be called with the same value each time for a given - * event. It will return `true` if the given number of frames has elapsed - * since the previous frame in which it returned `true`. - * - * For example, if you wanted to fire a shot every 5 frames while the A button - * is being held down: - * - * \code{.cpp} - * if (arduboy.everyXFrames(5)) { - * if arduboy.pressed(A_BUTTON) { - * fireShot(); - * } - * } - * \endcode - * - * \see setFrameRate() setFrameDuration() nextFrame() - */ - bool everyXFrames(uint8_t frames); - - /** \brief - * Return the load on the CPU as a percentage. - * - * \return The load on the CPU as a percentage of the total frame time. - * - * \details - * The returned value gives the time spent processing a frame as a percentage - * the total time allotted for a frame, as determined by the frame rate. - * - * This function normally wouldn't be used in the final program. It is - * intended for use during program development as an aid in helping with - * frame timing. - * - * \note - * The percentage returned can be higher than 100 if more time is spent - * processing a frame than the time allotted per frame. This would indicate - * that the frame rate should be made slower or the frame processing code - * should be optimized to run faster. - * - * \see nextFrameDEV() setFrameRate() setFrameDuration() nextFrame() - */ - int cpuLoad(); - - /** \brief - * Test if the all of the specified buttons are pressed. - * - * \param buttons A bit mask indicating which buttons to test. - * (Can be a single button) - * - * \return `true` if *all* buttons in the provided mask are currently pressed. - * - * \details - * Read the state of the buttons and return `true` if all of the buttons in - * the specified mask are being pressed. - * - * Example: `if (pressed(LEFT_BUTTON | A_BUTTON))` - * - * \note - * This function does not perform any button debouncing. - * - * \see anyPressed() notPressed() - */ - bool pressed(uint8_t buttons); - - /** \brief - * Test if any of the specified buttons are pressed. - * - * \param buttons A bit mask indicating which buttons to test. - * (Can be a single button) - * - * \return `true` if *one or more* of the buttons in the provided mask are - * currently pressed. - * - * \details - * Read the state of the buttons and return `true` if one or more of the - * buttons in the specified mask are being pressed. - * - * Example: `if (anyPressed(RIGHT_BUTTON | LEFT_BUTTON))` - * - * \note - * This function does not perform any button debouncing. - * - * \see pressed() notPressed() - */ - bool anyPressed(uint8_t buttons); - - /** \brief - * Test if the specified buttons are not pressed. - * - * \param buttons A bit mask indicating which buttons to test. - * (Can be a single button) - * - * \return `true` if *all* buttons in the provided mask are currently - * released. - * - * \details - * Read the state of the buttons and return `true` if all the buttons in the - * specified mask are currently released. - * - * Example: `if (notPressed(UP_BUTTON))` - * - * \note - * This function does not perform any button debouncing. - * - * \see pressed() anyPressed() - */ - bool notPressed(uint8_t buttons); - - /** \brief - * Poll the buttons and track their state over time. - * - * \details - * Read and save the current state of the buttons and also keep track of the - * button state when this function was previously called. These states are - * used by the `justPressed()` and `justReleased()` functions to determine - * if a button has changed state between now and the previous call to - * `pollButtons()`. - * - * This function should be called once at the start of each new frame. - * - * The `justPressed()` and `justReleased()` functions rely on this function. - * - * example: - * \code{.cpp} - * void loop() { - * if (!arduboy.nextFrame()) { - * return; - * } - * arduboy.pollButtons(); - * - * // use justPressed() as necessary to determine if a button was just pressed - * \endcode - * - * \note - * As long as the elapsed time between calls to this function is long - * enough, buttons will be naturally debounced. Calling it once per frame at - * a frame rate of 60 or lower (or possibly somewhat higher), should be - * sufficient. - * - * \see justPressed() justReleased() - */ - void pollButtons(); - - /** \brief - * Check if a button has just been pressed. - * - * \param button The button to test for. Only one button should be specified. - * - * \return `true` if the specified button has just been pressed. - * - * \details - * Return `true` if the given button was pressed between the latest - * call to `pollButtons()` and previous call to `pollButtons()`. - * If the button has been held down over multiple polls, this function will - * return `false`. - * - * There is no need to check for the release of the button since it must have - * been released for this function to return `true` when pressed again. - * - * This function should only be used to test a single button. - * - * \see pollButtons() justReleased() - */ - bool justPressed(uint8_t button); - - /** \brief - * Check if a button has just been released. - * - * \param button The button to test for. Only one button should be specified. - * - * \return `true` if the specified button has just been released. - * - * \details - * Return `true` if the given button, having previously been pressed, - * was released between the latest call to `pollButtons()` and previous call - * to `pollButtons()`. If the button has remained released over multiple - * polls, this function will return `false`. - * - * There is no need to check for the button having been pressed since it must - * have been previously pressed for this function to return `true` upon - * release. - * - * This function should only be used to test a single button. - * - * \note - * There aren't many cases where this function would be needed. Wanting to - * know if a button has been released, without knowing when it was pressed, - * is uncommon. - * - * \see pollButtons() justPressed() - */ - bool justReleased(uint8_t button); - - /** \brief - * Test if a point falls within a rectangle. - * - * \param point A structure describing the location of the point. - * \param rect A structure describing the location and size of the rectangle. - * - * \return `true` if the specified point is within the specified rectangle. - * - * \details - * This function is intended to detemine if an object, whose boundaries are - * are defined by the given rectangle, is in contact with the given point. - * - * \see Point Rect - */ - static bool collide(Point point, Rect rect); - - /** \brief - * Test if a rectangle is intersecting with another rectangle. - * - * \param rect1,rect2 Structures describing the size and locations of the - * rectangles. - * - * \return `true` if the first rectangle is intersecting the second. - * - * \details - * This function is intended to detemine if an object, whose boundaries are - * are defined by the given rectangle, is in contact with another rectangular - * object. - * - * \see Rect - */ - static 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 the first element of a `char` array in which the - * unit name will be written. The name will be up to `ARDUBOY_UNIT_NAME_LEN` - * characters in length and additionally terminated with a null (0x00) - * character, so **the provided array MUST be at least - * `ARDUBOY_UNIT_NAME_BUFFER_SIZE` characters long**. - * Using `ARDUBOY_UNIT_NAME_BUFFER_SIZE` to specify the array length is the - * proper way to do this, although any array larger than - * `ARDUBOY_UNIT_NAME_BUFFER_SIZE` is also acceptable. - * - * \return The length of the string (between 0 and - * `ARDUBOY_UNIT_NAME_LEN` *inclusive*). - * - * \details - * This function reads the unit name that has been set in system EEPROM. - * The name represents characters in the library's `font5x7` font. It can - * contain any values except 0xFF and the null (0x00) terminator value, plus - * the ASCII newline/line feed character (`\n`, 0x0A, inverse white circle) - * and ASCII carriage return character (`\r`, 0x0D, musical eighth note). - * - * 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 - * The defined value `ARDUBOY_UNIT_NAME_BUFFER_SIZE` should be used to - * allocate an array to hold the unit name string, instead of using a - * hard coded value for the size. - * For example, to allocate a buffer and read the unit name into it: - * \code{.cpp} - * // Buffer large enough to hold the unit name and a null terminator - * char unitName[ARDUBOY_UNIT_NAME_BUFFER_SIZE]; - * - * // After the call, unitNameLength will contain the actual name length, - * // not including the null terminator. - * uint8_t unitNameLength = arduboy.readUnitName(unitName); - * \endcode - * - * \see writeUnitName() readUnitID() Arduboy2::bootLogoExtra() - * ARDUBOY_UNIT_NAME_BUFFER_SIZE ARDUBOY_UNIT_NAME_LEN Arduboy2::font5x7 - */ - uint8_t readUnitName(char* name); - - /** \brief - * Write a unit name to system EEPROM. - * - * \param name A pointer to the first element of a C-style null-terminated - * string containing the unit name to be saved. The name can be up to - * `ARDUBOY_UNIT_NAME_LEN` characters long and must be terminated with a - * null (0x00) character. - * - * \details - * This function writes a unit name to a reserved area in system EEPROM. - * The name represents characters in the library's `font5x7` font. It can - * contain any values except 0xFF and the null (0x00) terminator value, plus - * the ASCII newline/line feed character (`\n`, 0x0A, inverse white circle) - * and ASCII carriage return character (`\r`, 0x0D, musical eighth note) - * because of their special use by the library's text handling functions. - * - * 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 - * The defined value `ARDUBOY_UNIT_NAME_BUFFER_SIZE` should be used to - * allocate an array to hold the unit name string, instead of using a - * hard coded value for the size. - * - * \see readUnitName() writeUnitID() Arduboy2::bootLogoExtra() - * ARDUBOY_UNIT_NAME_BUFFER_SIZE ARDUBOY_UNIT_NAME_LEN Arduboy2::font5x7 - */ - void writeUnitName(const char* name); - - /** \brief - * Read the "Show Boot Logo" flag in system EEPROM. - * - * \return `true` if the flag is set to indicate that the boot logo sequence - * should be displayed. `false` if the flag is set to not display the - * boot logo sequence. - * - * \details - * The "Show Boot Logo" flag is used to determine whether the system - * boot logo sequence is to be displayed when the system boots up. - * This function returns the value of this flag. - * - * \see writeShowBootLogoFlag() bootLogo() - */ - bool readShowBootLogoFlag(); - - /** \brief - * Write the "Show Boot Logo" flag in system EEPROM. - * - * \param val If `true` the flag is set to indicate that the boot logo - * sequence should be displayed. If `false` the flag is set to not display - * the boot logo sequence. - * - * \details - * The "Show Boot Logo" flag is used to determine whether the system - * boot logo sequence is to be displayed when the system boots up. - * This function allows the flag to be saved with the desired value. - * - * \see readShowBootLogoFlag() bootLogo() - */ - void writeShowBootLogoFlag(bool val); - - /** \brief - * Read the "Show Unit Name" flag in system EEPROM. - * - * \return `true` if the flag is set to indicate that the unit name should - * be displayed. `false` if the flag is set to not display the unit name. - * - * \details - * The "Show Unit Name" flag is used to determine whether the system - * unit name is to be displayed at the end of the boot logo sequence. - * This function returns the value of this flag. - * - * \see writeShowUnitNameFlag() writeUnitName() readUnitName() - * Arduboy2::bootLogoExtra() - */ - bool readShowUnitNameFlag(); - - /** \brief - * Write the "Show Unit Name" flag in system EEPROM. - * - * \param val If `true` the flag is set to indicate that the unit name should - * be displayed. If `false` the flag is set to not display the unit name. - * - * \details - * The "Show Unit Name" flag is used to determine whether the system - * unit name is to be displayed at the end of the boot logo sequence. - * This function allows the flag to be saved with the desired value. - * - * \see readShowUnitNameFlag() writeUnitName() readUnitName() - * Arduboy2::bootLogoExtra() - */ - void writeShowUnitNameFlag(bool val); - - /** \brief - * Read the "Show LEDs with boot logo" flag in system EEPROM. - * - * \return `true` if the flag is set to indicate that the RGB LEDs should be - * flashed. `false` if the flag is set to leave the LEDs off. - * - * \details - * The "Show LEDs with boot logo" flag is used to determine whether the - * RGB LEDs should be flashed in sequence while the boot logo is being - * displayed. This function returns the value of this flag. - * - * \see writeShowBootLogoLEDsFlag() - */ - bool readShowBootLogoLEDsFlag(); - - /** \brief - * Write the "Show LEDs with boot logo" flag in system EEPROM. - * - * \param val If `true` the flag is set to indicate that the RGB LEDs should - * be flashed. If `false` the flag is set to leave the LEDs off. - * - * \details - * The "Show LEDs with boot logo" flag is used to determine whether the - * RGB LEDs should be flashed in sequence while the boot logo is being - * displayed. This function allows the flag to be saved with the desired - * value. - * - * \see readShowBootLogoLEDsFlag() - */ - void writeShowBootLogoLEDsFlag(bool val); - - /** \brief - * A counter which is incremented once per frame. - * - * \details - * This counter is incremented once per frame when using the `nextFrame()` - * function. It will wrap to zero when it reaches its maximum value. - * - * It could be used to have an event occur for a given number of frames, or - * a given number of frames later, in a way that wouldn't be quantized the - * way that using `everyXFrames()` might. - * - * example: - * \code{.cpp} - * // move for 10 frames when right button is pressed, if not already moving - * if (!moving) { - * if (arduboy.justPressed(RIGHT_BUTTON)) { - * endMoving = arduboy.frameCount + 10; - * moving = true; - * } - * } else { - * movePlayer(); - * if (arduboy.frameCount == endMoving) { - * moving = false; - * } - * } - * \endcode - * - * This counter could also be used to determine the number of frames that - * have elapsed between events but the possibility of the counter wrapping - * would have to be accounted for. - * - * \see nextFrame() everyXFrames() - */ - uint16_t frameCount; - - /** \brief - * The display buffer array in RAM. - * - * \details - * The display buffer (also known as the screen buffer) contains an - * image bitmap of the desired contents of the display, which is written - * to the display using the `display()` function. The drawing functions of - * this library manipulate the contents of the display buffer. A sketch can - * also access the display buffer directly. - * - * \see getBuffer() - */ - static uint8_t sBuffer[(HEIGHT*WIDTH)/8]; - - /** \brief - * The bitmap for the ARDUBOY logo in `drawBitmap()` format. - * - * \see bootLogo() drawBitmap() - */ - static const PROGMEM uint8_t arduboy_logo[]; - - /** \brief - * The bitmap for the ARDUBOY logo in `drawCompressed()` format. - * - * \see bootLogoCompressed() drawCompressed() - */ - static const PROGMEM uint8_t arduboy_logo_compressed[]; - - /** \brief - * The bitmap for the ARDUBOY logo in `Sprites` class - * `drawSelfMasked()` or `drawOverwrite()` format. - * - * \see bootLogoSpritesSelfMasked() bootLogoSpritesOverwrite() - * bootLogoSpritesBSelfMasked() bootLogoSpritesBOverwrite() - */ - static const PROGMEM uint8_t arduboy_logo_sprite[]; - - protected: - // helper function for sound enable/disable system control - void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal); - - // functions passed to bootLogoShell() to draw the logo - static void drawLogoBitmap(int16_t y); - static void drawLogoCompressed(int16_t y); - static void drawLogoSpritesSelfMasked(int16_t y); - static void drawLogoSpritesOverwrite(int16_t y); - static void drawLogoSpritesBSelfMasked(int16_t y); - static void drawLogoSpritesBOverwrite(int16_t y); - - // draw one or more "corners" of a circle - void drawCircleHelper(int16_t x0, int16_t y0, uint8_t r, uint8_t corners, - uint8_t color = WHITE); - - // draw one or both vertical halves of a filled-in circle or - // rounded rectangle edge - void fillCircleHelper(int16_t x0, int16_t y0, uint8_t r, - uint8_t sides, int16_t delta, uint8_t color = WHITE); - - // helper for drawCompressed() - class BitStreamReader; - - // swap the values of two int16_t variables passed by reference - void swapInt16(int16_t& a, int16_t& b); - - // For button handling - uint8_t currentButtonState; - uint8_t previousButtonState; - - // For frame functions - uint8_t eachFrameMillis; - uint8_t thisFrameStart; - bool justRendered; - uint8_t lastFrameDurationMs; - - // ----- Map of EEPROM addresses for system use----- - - // EEPROM address 0 is reserved for bootloader use - // This library will not touch it - - // Control flags - static constexpr uint16_t eepromSysFlags = 1; - // Audio mute control. 0 = audio off, non-zero = audio on - static constexpr uint16_t eepromAudioOnOff = 2; - // -- Addresses 3-7 are currently reserved for future use -- - // A uint16_t binary unit ID - static constexpr uint16_t eepromUnitID = 8; // A uint16_t binary unit ID - // An up to 6 character unit name - // The name cannot contain 0x00, 0xFF, 0x0A, 0x0D - // Lengths less than 6 are padded with 0x00 - static constexpr uint16_t eepromUnitName = 10; - // -- User EEPROM space starts at address 16 -- - - // --- Map of the bits in the eepromSysFlags byte -- - // Display the unit name on the logo screen - static constexpr uint8_t sysFlagUnameBit = 0; - static constexpr uint8_t sysFlagUnameMask = _BV(sysFlagUnameBit); - // Show the logo sequence during boot up - static constexpr uint8_t sysFlagShowLogoBit = 1; - static constexpr uint8_t sysFlagShowLogoMask = _BV(sysFlagShowLogoBit); - // Flash the RGB led during the boot logo - static constexpr uint8_t sysFlagShowLogoLEDsBit = 2; - static constexpr uint8_t sysFlagShowLogoLEDsMask = _BV(sysFlagShowLogoLEDsBit); -}; - - -//============================== -//========== Arduboy2 ========== -//============================== - -/** \brief - * The main functions provided for writing sketches for the Arduboy, - * _including_ text output. - * - * \details - * This class is derived from Arduboy2Base. It provides text output functions - * in addition to all the functions inherited from Arduboy2Base. - * - * \note - * A friend class named _Arduboy2Ex_ is declared by this class. The intention - * is to allow a sketch to create an _Arduboy2Ex_ class which would have access - * to the private and protected members of the Arduboy2 class. It is hoped - * that this may eliminate the need to create an entire local copy of the - * library, in order to extend the functionality, in most circumstances. - * - * \see Arduboy2Base - */ -class Arduboy2 : public Print, public Arduboy2Base -{ - friend class Arduboy2Ex; - - public: - Arduboy2(); - - /** \class Print - * \brief - * The Arduino `Print` class is available for writing text to the screen - * buffer. - * - * \details - * For an `Arduboy2` class object, functions provided by the Arduino `Print` - * class can be used to write text to the screen buffer, in the same manner - * as the Arduino `Serial.print()`, etc., functions. - * - * Print will use the `write()` function to actually draw each character - * in the screen buffer, using the library's `font5x7` font. - * Two character values are handled specially: - * - * - ASCII newline/line feed (`\n`, 0x0A, inverse white circle). - * This will move the text cursor position to the start of the next line, - * based on the current text size. - * - ASCII carriage return (`\r`, 0x0D, musical eighth note). - * This character will be ignored. - * - * To override the special handling of the above values, to allow the - * characters they represent to be printed, text _raw_ mode can be selected - * using the `setTextRawMode()` function. - * - * See: - * https://www.arduino.cc/reference/en/language/functions/communication/serial/print/ - * - * Example: - * \code{.cpp} - * int value = 42; - * - * arduboy.println("Hello World"); // Prints "Hello World" and then sets the - * // text cursor to the start of the next line - * arduboy.print(value); // Prints "42" - * arduboy.print('\n'); // Sets the text cursor to the start of the next line - * arduboy.print(78, HEX); // Prints "4E" (78 in hexadecimal) - * arduboy.print("\x03\xEA"); // Prints a heart symbol and a Greek uppercase omega - * - * arduboy.setTextRawMode(true); // Set text "raw" mode - * arduboy.print("\r\n") // Prints a "musical eighth note" - * // followed by an "inverse white circle" - * // because we're in "raw" mode - * arduboy.setTextRawMode(false); // Exit text "raw" mode - * \endcode - * - * \see Arduboy2::setTextSize() Arduboy2::setTextColor() - * Arduboy2::setTextBackground() Arduboy2::setTextWrap() - * Arduboy2::setTextRawMode() Arduboy2::write() Arduboy2::font5x7 - */ - using Print::write; - /** \brief * Display the boot logo sequence using printed text instead of a bitmap. * @@ -1569,7 +1695,8 @@ class Arduboy2 : public Print, public Arduboy2Base * Show the unit name at the bottom of the boot logo screen. * * \details - * This function is called by `bootLogoShell()` and `bootLogoText()`. + * This function is called by the `bootLogo...()` functions if the logo + * sequence runs to completion. * * 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 @@ -1578,14 +1705,16 @@ class Arduboy2 : public Print, public Arduboy2Base * If the "Show Unit Name" flag in system EEPROM is cleared, this function * will return without showing the unit name or pausing. * - * \note * This function would not normally be called directly from within a sketch - * itself. + * except from a sketch provided `bootlogo...()` function that renders the + * boot logo using a function that's not part of the Arduboy2 library. + * The README file or main page describes how this would be done, at the + * end of the _Substitute or remove boot up features_ section. * * \see readUnitName() writeUnitName() bootLogo() bootLogoShell() * bootLogoText() writeShowUnitNameFlag() begin() */ - virtual void bootLogoExtra(); + void bootLogoExtra(); /** \brief * Write a single character at the current text cursor position. @@ -1650,7 +1779,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see Print write() font5x7 */ - void drawChar(int16_t x, int16_t y, uint8_t c, uint8_t color, uint8_t bg, uint8_t size); + static void drawChar(int16_t x, int16_t y, uint8_t c, uint8_t color, uint8_t bg, uint8_t size); /** \brief * Set the location of the text cursor. @@ -1670,7 +1799,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setCursorX() setCursorY() getCursorX() getCursorY() */ - void setCursor(int16_t x, int16_t y); + static void setCursor(int16_t x, int16_t y); /** \brief * Set the X coordinate of the text cursor location. @@ -1685,7 +1814,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setCursor() setCursorY() getCursorX() getCursorY() */ - void setCursorX(int16_t x); + static void setCursorX(int16_t x); /** \brief * Set the Y coordinate of the text cursor location. @@ -1700,7 +1829,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setCursor() setCursorX() getCursorX() getCursorY() */ - void setCursorY(int16_t y); + static void setCursorY(int16_t y); /** \brief * Get the X coordinate of the current text cursor position. @@ -1713,7 +1842,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see getCursorY() setCursor() setCursorX() setCursorY() */ - int16_t getCursorX(); + static int16_t getCursorX(); /** \brief * Get the Y coordinate of the current text cursor position. @@ -1726,7 +1855,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see getCursorX() setCursor() setCursorX() setCursorY() */ - int16_t getCursorY(); + static int16_t getCursorY(); /** \brief * Set the text foreground color. @@ -1736,7 +1865,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setTextBackground() getTextColor() */ - void setTextColor(uint8_t color); + static void setTextColor(uint8_t color); /** \brief * Get the currently set text foreground color. @@ -1745,7 +1874,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setTextColor() getTextBackground() */ - uint8_t getTextColor(); + static uint8_t getTextColor(); /** \brief * Set the text background color. @@ -1763,7 +1892,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setTextColor() getTextBackground() */ - void setTextBackground(uint8_t bg); + static void setTextBackground(uint8_t bg); /** \brief * Get the currently set text background color. @@ -1772,7 +1901,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setTextBackground() getTextColor() */ - uint8_t getTextBackground(); + static uint8_t getTextBackground(); /** \brief * Set the text character size. @@ -1788,7 +1917,7 @@ class Arduboy2 : public Print, public Arduboy2Base * \see getTextSize() getCharacterWidth() getCharacterHeight() * getCharacterSpacing() getLineSpacing() font5x7 */ - void setTextSize(uint8_t s); + static void setTextSize(uint8_t s); /** \brief * Get the currently set text size. @@ -1798,7 +1927,7 @@ class Arduboy2 : public Print, public Arduboy2Base * \see setTextSize() getCharacterWidth() getCharacterHeight() * getCharacterSpacing() getLineSpacing() font5x7 */ - uint8_t getTextSize(); + static uint8_t getTextSize(); /** \brief * Set or disable text wrap mode. @@ -1818,7 +1947,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see getTextWrap() */ - void setTextWrap(bool w); + static void setTextWrap(bool w); /** \brief * Get the currently set text wrap mode. @@ -1827,7 +1956,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setTextWrap() */ - bool getTextWrap(); + static bool getTextWrap(); /** \brief * Set or disable text raw mode, allowing special characters to be displayed. @@ -1846,7 +1975,7 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see getTextRawMode() Print */ - void setTextRawMode(bool raw); + static void setTextRawMode(bool raw); /** \brief * Get the current state of text raw mode. @@ -1855,12 +1984,12 @@ class Arduboy2 : public Print, public Arduboy2Base * * \see setTextRawMode() */ - bool getTextRawMode(); + static bool getTextRawMode(); /** \brief * Clear the display buffer and set the text cursor to location 0, 0. */ - void clear(); + static void clear(); /** \brief * Get the width, in pixels, of a character in the library's font. @@ -2009,13 +2138,13 @@ class Arduboy2 : public Print, public Arduboy2Base static const PROGMEM uint8_t font5x7[]; protected: - int16_t cursor_x; - int16_t cursor_y; - uint8_t textColor; - uint8_t textBackground; - uint8_t textSize; - bool textWrap; - bool textRaw; + static int16_t cursor_x; + static int16_t cursor_y; + static uint8_t textColor; + static uint8_t textBackground; + static uint8_t textSize; + static bool textWrap; + static bool textRaw; // Width and height of a font5x7 character // (not including inter-character spacing) diff --git a/src/Arduboy2Audio.h b/src/Arduboy2Audio.h index c7c8035..d359831 100644 --- a/src/Arduboy2Audio.h +++ b/src/Arduboy2Audio.h @@ -80,11 +80,8 @@ class Arduboy2Audio * * \details * The speaker is initialized based on the current mute setting saved in - * system EEPROM. This function is called by `Arduboy2Base::begin()` so it - * isn't normally required to call it within a sketch. However, if - * `Arduboy2Core::boot()` is used instead of `Arduboy2Base::begin()` and the - * sketch includes sound, then this function should be called after `boot()`. - */ + * system EEPROM. + */ static void begin(); /** \brief diff --git a/src/Arduboy2Core.cpp b/src/Arduboy2Core.cpp index 953560a..c475fbc 100644 --- a/src/Arduboy2Core.cpp +++ b/src/Arduboy2Core.cpp @@ -13,8 +13,6 @@ //========== class Arduboy2Core ========== //======================================== -Arduboy2Core::Arduboy2Core() { } - // Commands sent to the OLED display to initialize it const PROGMEM uint8_t Arduboy2Core::lcdBootProgram[] = { // boot defaults are commented out but left here in case they diff --git a/src/Arduboy2Core.h b/src/Arduboy2Core.h index 0170f73..084dee3 100644 --- a/src/Arduboy2Core.h +++ b/src/Arduboy2Core.h @@ -337,7 +337,6 @@ class Arduboy2Core : public Arduboy2NoUSB friend class Arduboy2Ex; public: - Arduboy2Core(); /** \brief * Idle the CPU to save power. @@ -797,14 +796,21 @@ class Arduboy2Core : public Arduboy2NoUSB * \details * This function initializes the display, buttons, etc. * - * This function is called by begin() so isn't normally called within a + * This function is called by `begin()` so isn't normally called within a * sketch. However, in order to free up some code space, by eliminating * some of the start up features, it can be called in place of begin(). - * The functions that begin() would call after boot() can then be called - * to add back in some of the start up features, if desired. - * See the README file or documentation on the main page for more details. + * The functions that `begin()` would call after `boot()` can then be + * called to add back in some of the start up features as space permits. * - * \see Arduboy2Base::begin() + * See the README file or main page, in section + * _Substitute or remove boot up features_, for more details. + * + * \warning + * If this function is used, it is recommended that at least `flashlight()` + * or `safeMode()` be called after it to provide a means to upload a new + * sketch if the bootloader "magic number" problem is encountered. + * + * \see Arduboy2::begin() Arduboy2Base::flashlight() safeMode() */ static void boot(); @@ -818,8 +824,8 @@ class Arduboy2Core : public Arduboy2NoUSB * sketch, for sketches that interfere with the bootloader "magic number". * The problem occurs with certain sketches that use large amounts of RAM. * - * This function should be called after `boot()` in sketches that - * potentially could cause the problem. + * This function should be called after `boot()` in sketches that don't + * call `flashlight()`. * * It is intended to replace the `flashlight()` function when more * program space is required. If possible, it is more desirable to use