Programming

Want to play now?

Download one of the following UF2 files for your particular device:

Double click the reset button to get to the UF2 screen. Go to the appropriate flash drive that appears on your computer - PYBADGEBOOT or PYGAMERBOOT and copy over the correct UF2 file from above and the device should reboot and start the game.

Compiling the Game in Arduino

The code uses the Adafruit Arcada library. Arcada is a full featured set of game creation and display functions. It can be thought of the older Adafruit GFX library on super steroids.

In the Arduino IDE, you will need to use the Library manager to load a BUNCH of helper libraries. Please take the time to get the latest versions to ensure you have the latest code tweaks.

The code also requires a recent Adafruit SAMD boards file. Version 1.5 or higher. Go to the boards manager via the steps listed below.

Type "samd" into the search. The first couple of entries will probably not be the Adafruit package. You want to install the package called Adafruit SAMD Boards by Adafruit and be sure the version is 1.5 or higher.

This will give you the support for the Adafruit PyGamer and PyBadge definitions for the game.

Once you have the latest board definitions, you can select the Adafruit PyGamer or the Adafruit PyBadge for the game depending on your board. The PyBadge LC will not work as it does not have an accelerometer to provide the game interactivity.

Code

If you click the Download: Zip link, you should get both the bounce.ino and logo.h files. Save them in your Arduino projects directory in a subdirectory named bounce.

// Adafruit Arcada based Level Bounce Game
//
// Mike Barela, June 3, 2019 for Adafruit Industries
//
// Mod of game by R0D0T posted on http://r0d0t.tumblr.com/post/29641975900 and 
// Hackaday http://hackaday.com/2012/10/01/fantastic-programming-makes-this-arduino-gaming-device-something-special/ 
// which was published for Arduino Esplora by Mike Barela 2013

#include <Adafruit_Arcada.h>
#include <Adafruit_Arcada_Def.h>
#include <Adafruit_NeoPixel.h>
#include "logo.h"

Adafruit_Arcada arcada; // Set up the Arcada code
Adafruit_NeoPixel strip(5, 8, NEO_GRB + NEO_KHZ800);  // 5 NeoPixels are on D8

// SCREEN
#define s_width   128
#define s_height  139
#define textline1 (s_height +  3)
#define textline2 (s_height + 13) 
unsigned int background = ARCADA_BLACK;

long lastMillis;

// SOUND
#define sound_pin A0      // Direct sound 
uint8_t sound_on = 1;     // start with sound on
char speaker_icon = 0xEC; // icon to show sound off

// PLAYER
float p_X = s_width/2;
float p_Y = s_height/2;
float p_lastX, p_vX;
float p_lastY, p_vY;
//#define p_vmaxY 6
//#define p_vmaxX 6
#define gravity_default 0.2
float gravity = 0.2;
#define p_bounceSpeed_default 4
byte p_bounceSpeed = p_bounceSpeed_default;
#define p_width_default 4
byte p_width = p_width_default;
#define p_height_default 4
byte p_height = p_height_default;
#define p_color_default ARCADA_WHITE
#define p_color_dead    ARCADA_RED
int p_color = p_color_default;

// SCORE & BONUS
unsigned int score = 0;
unsigned int highscore = 0;
byte nextBonus;
boolean b_pendingPlatform = false;
boolean b_used;
byte b_platformID;
byte b_pendingID;
byte b_onUseID;
byte b_remaining = 0;
byte b_max;
#define b_number 4
int b_colors[b_number] = {
  ARCADA_ORANGE, 0x0F00, 0xF000, 0x00FF};
byte b_lives = 0;
byte num_lives = 5;

// WORLD
#define w_size 8
byte world[w_size][6];         // contains platforms data
int  w_color = ARCADA_YELLOW;  // Platform default color
byte w_speed = 1;
byte w_widthMin = 4;
byte w_widthMax = 8;

// ACCELEROMETER 
int acc_avgX, acc_avgY, acc_avgZ;
sensors_event_t event;
#define acc_pinX 1  
#define acc_pinY 2  
#define acc_pinZ 3 

// NeoPixels
#define RED      0xFF0000
#define YELLOW   0xFFFF00
#define GREEN    0x00FF00
#define BLACK    0x000000

void setup() {
  Serial.begin(9600);
  // initialize 

  Serial.println("Hello! Arcada version of game");
  if (!arcada.begin()) {
    Serial.print("Failed to begin");
    strip.fill(RED);
    strip.show();
    while (1);
  }
  if( !arcada.hasAccel() ) {
    strip.fill(YELLOW);
    strip.show();
    arcada.haltBox("An accelerometer is required for this gamne");
  }
  arcada.displayBegin();         // Initialize display code
  arcada.setBacklight(0);        // Initial display off
  arcada.setRotation(0);         // Rotate to portrait
  arcada.fillScreen(background); // Clear screen if necessary
  arcada.setTextColor(ARCADA_GREEN, ARCADA_BLACK);
  arcada.setTextSize(2);
  arcada.println(" ");
  arcada.println("  Bounce!");
  // Set up the logo bitmap
  int logo_origin_x = (128 - 2*LOGO_WIDTH ) / 2;
  int logo_origin_y = (160 - 2*LOGO_HEIGHT) / 2;
  for(int y=0; y<LOGO_HEIGHT; y++) {
    for(int x=0; x<LOGO_WIDTH; x++) {
      uint8_t c = logo_mask[y][x / 8];
      if(c & (0x80 >> (x & 7))) {
        int xx = logo_origin_x+2*x;
        int yy = logo_origin_y+2*y;
        arcada.drawPixel(xx, yy, ARCADA_WHITE);
        arcada.drawPixel(xx+1, yy, ARCADA_WHITE);
        arcada.drawPixel(xx, yy+1, ARCADA_WHITE);
        arcada.drawPixel(xx+1, yy+1, ARCADA_WHITE);
      }
    }
  }
  arcada.println(" ");
  arcada.println(" ");
  arcada.println(" ");
  arcada.println(" ");
  arcada.println(" ");
  arcada.setTextColor(ARCADA_ORANGE, ARCADA_BLACK);
  arcada.setTextSize(1);
  arcada.println(" ");
  arcada.println("  Adafruit");
  arcada.println("   Industries");
  for (int i=0; i<220; i++) {    // Display initial text
    arcada.setBacklight(i);
    delay(14);
  }
  arcada.setBacklight(250);
  arcada.setTextColor(ST7735_WHITE, ST7735_BLACK);
  arcada.println(" ");
  arcada.println(" ");
  arcada.println("  Press Start");
  while(!(arcada.readButtons() & ARCADA_BUTTONMASK_START)) ; // wait for start button
  arcada.fillScreen(background);                             // Clear screen

  arcada.enableSpeaker(1);   // Enable the speaker and play opening tones
  pinMode(sound_pin, OUTPUT);
  beep(sound_pin,  880, 100);
  delay(100);
  beep(sound_pin, 1060, 120);
  delay(100);
  beep(sound_pin, 1800, 100);
  
  // NeoPixel setup
  strip.begin();
  strip.setBrightness(40);  // not too bright
  strip.show();

  // Game setup
  randomSeed(millis());  
  FillWorld();

  // Get average accelerations
  acc_avgX = acc_avg(acc_pinX);
  acc_avgY = acc_avg(acc_pinY);
  acc_avgZ = acc_avg(acc_pinZ);

  ScoreSetup();
}

void loop() {
  if(millis()-lastMillis > 20){
    lastMillis = millis();
    CheckButtons();
    MovePlayer();
    ScrollWorld();
    CollideBorders();
    CollideWorld();
    DrawWorld();
    DrawPlayer();
  }
}

int acc_avg(int pin) {
  int avg = 0;
  arcada.accel.getEvent(&event);
  switch (pin) {
    case acc_pinX:
       for(int i = 0; i < 50; i++) {
         avg += event.acceleration.x;
         // delay(10);
        }
       break;
     case acc_pinY:
       for(int i = 0; i < 50; i++) {
         avg += event.acceleration.y;
         // delay(10);
        }
       break;
     case acc_pinZ:
         for(int i = 0; i < 50; i++) {
         avg += event.acceleration.z;
         // delay(10);
        }
       break;
     }
  avg /= 50;
  return avg;
}

int acc_readX() {
  int val;
  arcada.accel.getEvent(&event);
  val = -1 * constrain((event.acceleration.y - acc_avgY)*7, -64, 64); // use Y for orientation
  // Serial.print("Acceleration: ");                                  // joystick is at top
  // Serial.println(val);
  return(val);
}


void CheckBonus(byte thisPlatform){
  if(b_pendingPlatform && (b_platformID == thisPlatform)){
    b_pendingPlatform = false;
  }
  if(nextBonus ){
    nextBonus--;
  } 
  else {
    nextBonus = random(30, 60);
    b_pendingID = random(0, b_number);
    b_pendingPlatform = true;
    b_platformID = thisPlatform;
  }
}

void BonusReset(){
  nextBonus = random(30, 60);
  b_pendingPlatform = false;
  b_remaining = 0;
  arcada.fillRect(0, s_height+2, 96, 10, background);
  arcada.fillRect(0, s_height+1, 96, 1, w_color);
  EndUseBonus();
}

void ClearBonus(){
  EndUseBonus();
  b_remaining = 0;
  p_color = p_color_default;
  arcada.fillRect(0, s_height+2, 96, 10, background);
  arcada.fillRect(0, s_height+1, s_width-1,  1, w_color);
  b_used = false;
}

void GetBonus(){
  ClearBonus();
  b_onUseID = b_pendingID;
  b_pendingPlatform = false;
  beep(sound_pin, 3500, 50);  // SOUND
  p_color = b_colors[b_onUseID];
  arcada.fillRect(0, s_height+2, 96, 10, background);

  switch (b_onUseID){
  case 0:
    drawString(1, textline1, "Jet pack", b_colors[b_onUseID], 1);
    b_max = 100;
    b_remaining = b_max;
    break;
  case 1:
    drawString(1, textline1, "Frog legs", b_colors[b_onUseID], 1);
    b_max = 100;
    b_remaining = b_max;
    break;
  case 2:
    drawString(1, textline1, "Boost", b_colors[b_onUseID], 1);
    b_max = 1;
    b_remaining = b_max;
    break;
  case 3:
    drawString(1, textline1, "Low gravity", b_colors[b_onUseID], 1);
    b_max = 255;
    b_remaining = b_max;
    break;
  }
  if(b_remaining > 1) {
    arcada.fillRect(95, s_height+2, 1, 10, b_colors[b_onUseID]);
    arcada.fillRect( 0, s_height+1, 95, 1, b_colors[b_onUseID]);
  }
}

void UseBonus(){
  if(b_remaining){
    b_used = true;
    arcada.fillRect(b_remaining*95/b_max, s_height+2, 1, 10, background);
    arcada.drawPixel(b_remaining*95/b_max, s_height+1, w_color);
    b_remaining --;
    arcada.fillRect(b_remaining*95/b_max, s_height+2, 1, 10, b_colors[b_onUseID]);
    arcada.drawPixel(b_remaining*95/b_max, s_height+1, b_colors[b_onUseID]);

    switch (b_onUseID){
    case 0:
      if(p_vY > -1){
        p_vY -= 0.3;
      }
      beep(sound_pin, 100, 3 + abs(10 * p_vY)); // SOUND
      break;
    case 1:
      p_bounceSpeed = p_bounceSpeed_default * 1.5;
      break;
    case 2:
      p_width = min(p_width+1, p_width_default+12);
      p_height = min(p_height+1, p_height_default+12);
      b_lives = min(b_lives+1, 12);
      beep(sound_pin, 50+150*b_lives, 100);  // SOUND
      break;
    case 3:
      gravity = gravity_default * 0.3;
      break;
    }

    if(!b_remaining){
      ClearBonus();
    }
  }
}

void EndUseBonus(){
  b_used = false;

  switch (b_onUseID){
  case 0:
    break;
  case 1:
    p_bounceSpeed = p_bounceSpeed_default;
    break;
  case 2:
    break;
  case 3:
    gravity = gravity_default;
    break;
  }
}

void CheckButtons(){
  uint32_t pressed_buttons;
  pressed_buttons = arcada.readButtons();
  
  if(pressed_buttons & ARCADA_BUTTONMASK_START) {  // pause
    Serial.print("START");
    delay(500);
    while(!(arcada.readButtons())) {  // wait until any button is pressed again
       strip.setPixelColor(0, GREEN);
       strip.show();
       delay(100);
       strip.setPixelColor(0, BLACK);
       strip.show();
       delay(100);
    }
    pressed_buttons = 0;
    return;
  }
  if(pressed_buttons & ARCADA_BUTTONMASK_B) {  // use bonus
    Serial.print("B");
    if(b_used = true) {
      EndUseBonus();
    } else {
      UseBonus();
    }
    return;
  }
  if(pressed_buttons & ARCADA_BUTTONMASK_SELECT) {  // SELECT toggles sound on/off
    Serial.print("SELECT");
    if(sound_on) {
       sound_on = 0;
       drawChar(s_width-6, textline2, speaker_icon, ARCADA_RED);  // Put character in lower right corner to indicate speaker off
    } else {
       sound_on = 1;
       arcada.fillRect( s_width-6, textline2, s_width-1, textline2+9, background); // Blank lower right character = sound on
    }
  }
}

void DrawPlayer(){
  CheckButtons();  // check more frequently
  arcada.fillRect(p_lastX, p_lastY, p_width, p_height, background); // erase previous pos
  if(p_lastX > (s_width-1 - p_width)) // if across the edge of the screen
    arcada.fillRect(0, p_lastY, p_lastX + p_width - s_width, p_height, background);

  arcada.fillRect(p_X, p_Y, p_width, p_height, p_color); // draw new pos
  if(p_X > (s_width-1 - p_width)) // if across the edge of the screen
    arcada.fillRect(0, p_Y, p_X + p_width - s_width, p_height, p_color);
}

void MovePlayer(){
  p_lastX = p_X;
  p_lastY = p_Y;

  p_vX = (p_vX + acc_readX()) / 20;
  p_vY = (p_vY + gravity);

  p_X = p_X + p_vX;
  if(p_X > s_width){
    p_X = 0;
  }
  if(p_X < 0){
    p_X = s_width - p_X;
  }
  p_Y = p_Y + p_vY;

  if((p_vY < -4) && (p_color != p_color_dead)){
    beep(sound_pin, 600+p_vY*100, 25);  // SOUND
  }
  if(b_used && (b_onUseID == 3)) { // if low gravity is on use
    beep(sound_pin, 300-abs(p_vY*100), 25);  // SOUND
  }
}

void CollideBorders() {
  if(p_Y > s_height - p_height){ // touch the floor
    p_vY = -p_bounceSpeed;
    p_Y = s_height - p_height;
    if(!b_lives){
      if(p_color != p_color_dead){
        BonusReset();
        ScoreReset();
      }
    } 
    else {
      arcada.fillRect(p_lastX, p_lastY, p_width, p_height, background);
      p_width--;
      p_height--;
      b_lives--;
      beep(sound_pin, 100+150*b_lives, 100);  // SOUND
    }
  }
}

void ScoreSetup(){
  arcada.fillRect(0, s_height+2, 128, 10, background);
  arcada.fillRect(0, s_height+1, 128, 1, w_color);
  // highscore = EEPROM.read(0);
  // highscore += (EEPROM.read(1)<<8);
  if(highscore > 64000){
    highscore = 0;
  }
  drawString(1, textline2, "High score:", w_color, 1);  
  drawInt(highscore, 66, textline2, w_color, background);
}

void ScoreReset(){
  if(p_color != p_color_dead) {
    p_color = p_color_dead;
    beep(sound_pin, 50, 500); // SOUND
    if(score > highscore){
      if(score < 64000) {
        // EEPROM.write(0, score);
        // EEPROM.write(1, score>>8);
      }
      else { // bad value, zero out
        // EEPROM.write(0,0);
        // EEPROM.write(1,0);
      }       
      highscore = score; 
      flashMessage();
      // drawInt(score, 66, textline2, p_color_default, background);
    } 
    else {
      drawString(1, textline1, "Last score:", w_color, 1);
      drawInt(score, 66, s_height+3, w_color, background);
    }
    score = 0;
    drawInt(score, 97, textline1, w_color, background);
  }
}

void flashMessage() {
   uint8_t i;
   arcada.fillRect( 0, textline2, s_width-10, textline2+9, background);
   for(i=0; i<5; i++) {
     drawString(1, textline2, "HIGHSCORE!", p_color_default, 1);
     delay(400);
     arcada.fillRect( 0, textline2, s_width-10, textline2+9, background);
     delay(400);
   }
   drawString(1, textline2, "High score:", w_color, 1);  
   drawInt(highscore, 66, textline2, w_color, background);
}

void ScoreAdd(){ // add to the current score lower left
  score++;
  beep(sound_pin, 3000, 50);  // SOUND
  if(score > highscore) {
    drawInt(score, 97, textline1, p_color_default, background);
  } 
  else {
    drawInt(score, 97, textline1, w_color, background);
  }
  w_widthMin = max(1, 4 - score/50);
  w_widthMax = max(2, 8 - score/50);
  if(score == 1) {
    p_color = p_color_default;
  }
}

// transform from int to string and display it
void drawInt(unsigned int num, byte nx, byte ny, unsigned int color, unsigned int color2) {
  arcada.fillRect(nx, ny, 29, 7, color2);
  drawChar(nx+24, ny, 48+(num%10), color);

  if(num > 9) {
    drawChar(nx+18, ny, 48+(num%100)/10, color);

    if(num>99) {
      drawChar(nx+12, ny, 48+(num%1000)/100, color);

      if(num>999) {
        drawChar(nx+6, ny, 48+(num%10000)/1000, color);

        if(num>9999) {
          drawChar(nx, ny, 48+(num%100000)/10000, color);
        }
      }
    }
  }
}

/*
world[a][b] ->
 a ->  platform id
 b ->  0 = x
 1 = y
 2 = last x
 3 = last y
 4 = width
 4 = last width
 */

void FillWorld(){
  for(byte i=0; i<w_size; i++) {
    world[i][0] = (s_width * (w_size-i-1) / w_size)/2;
    world[i][1] = 1 + i * (s_height / w_size);
    world[i][2] = world[i][0];
    world[i][3] = world[i][1];
    world[i][4] = s_width * (i+1)/w_size;
    world[i][5] = world[i][4];
  }
}

void ScrollWorld(){
  byte fastScrolling = 0;
  if(p_Y < s_height/4) { // if the player reaches the top of the screen
    fastScrolling ++;
    if(p_Y < s_height/8) { // if the player reaches the top of the screen
      fastScrolling ++;
    }
  }
  p_Y += fastScrolling;
  for(byte i=0; i<w_size; i++) {  // for each platform
    world[i][3] = world[i][1];    // update last y
    world[i][2] = world[i][0];    // update last x
    world[i][5] = world[i][4];    // update last width
    world[i][1] += w_speed ;      // move the platform downward
    world[i][1] += fastScrolling;
    if(world[i][1] > s_height){    // if the platform reaches the bottom of the screen
      //world[i][2] = world[i][0]; // update last x
      world[i][1] = world[i][1] - s_height + 1; // put the platform on the top of the screen
      world[i][4] = min(random(w_widthMin, w_widthMax)*p_width_default, s_width-world[i][0]); //pick a random width
      world[i][0] = random(0, s_width-world[i][4]); // pick a random x
      CheckBonus(i); // check if the new platform will have a bonus
    }
  }
}

void CollideWorld() {
  for(byte i=0; i<w_size; i++) {
    if(p_vY > 0){ // if player falling
      if((p_Y+p_width <= world[i][1]) && (world[i][1] < (p_Y+p_vY+p_height)) ) { // and at the height of the platform
        if(((p_X+p_width) >= world[i][0]) && (world[i][0]+world[i][4] > (p_X)) ) {     // and does not miss it
          p_vY = -p_bounceSpeed ;
          p_Y = world[i][1] - p_height;
          ScoreAdd();
          if(b_pendingPlatform && (b_platformID == i)) {
            GetBonus();
          }
        }
      }
    }
  }
}

void DrawWorld() {
  for(byte i=0; i<w_size; i++) {
    arcada.fillRect(world[i][2], world[i][3], world[i][5], 1, background);
    arcada.fillRect(world[i][0], world[i][1], world[i][4], 1, w_color);
  }
  if(b_pendingPlatform){
    arcada.fillRect(world[b_platformID][0], world[b_platformID][1], world[b_platformID][4], 1, b_colors[b_pendingID]);
  }
}


void drawString(byte x, byte y, char *text, uint16_t color, bool wrap) { // replicate tft.drawString
  arcada.setCursor(x,y);
  arcada.setTextColor(color);
  arcada.setTextWrap(wrap);
  arcada.print(text);
}

void drawChar(byte x, byte y, char text, uint16_t color) { // replicate tft.drawChar
  arcada.setCursor(x,y);
  arcada.setTextColor(color);  
  arcada.print(text);
  Serial.println(text);
}

// A sound-producing function
void beep(unsigned char speakerPin, int frequencyInHertz, long timeInMilliseconds) {
  // http://web.media.mit.edu/~leah/LilyPad/07_sound_code.html
  int  x;
  if( sound_on ) {     // Only play if the sound_on flag is 1
    long delayAmount = (long)(1000000 / frequencyInHertz);
    long loopTime = (long)((timeInMilliseconds * 1000) / (delayAmount * 2));
    for (x = 0; x < loopTime; x++) {
      digitalWrite(speakerPin, HIGH);
      delayMicroseconds(delayAmount);
      digitalWrite(speakerPin, LOW);
      delayMicroseconds(delayAmount);
    }
  }
}
/*!
 * @file logo.h
 *
 * Header file to accompany the demo-3-logo example.
 * A 40x40 pixel Adafruit logo in grayscale (for rendering on LED matrix)
 * and corresponding bitmap (for PixelDust collision detection).
 * Latter has the 'seeds' filled so randomly-placed grains don't
 * end up trapped in there.
 *
 */

#define LOGO_WIDTH  40
#define LOGO_HEIGHT 40

const uint8_t logo_gray[LOGO_HEIGHT][LOGO_WIDTH] = {
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X16, 0XD2, 0XEB, 0X39,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01,
  0XB7, 0XFF, 0XFF, 0XB1, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X71, 0XFF, 0XFF, 0XFF, 0XF7, 0X0D, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X30, 0XF8, 0XFF, 0XFF, 0XFF, 0XFF,
  0X57, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0A, 0XD6, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XA9, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X98, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF2, 0X08, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X51, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0X4D, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0D, 0XE8, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X9F, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X72,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XEC, 0X02, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0XCB, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0X25, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X22, 0X70, 0X78, 0X77, 0X76, 0X75, 0X74, 0X73,
  0X72, 0X6C, 0X4D, 0X15, 0X00, 0X00, 0X04, 0XFB, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X3F, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XDA, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0XB3, 0X2D, 0X0C, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X3C, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0XDD, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XF8, 0X52, 0XEB, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFE, 0X17, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X46, 0XFD, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF6, 0XC9, 0XFF, 0XFF, 0XFF, 0XC8,
  0X9F, 0XFF, 0XFF, 0XFF, 0XFF, 0XC4, 0X00, 0X17, 0X3D, 0X41, 0X27, 0X03,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X89, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFA,
  0XFF, 0XFF, 0XFD, 0X22, 0X0F, 0XFF, 0XFF, 0XFF, 0XFC, 0X76, 0XC4, 0XFE,
  0XFF, 0XFF, 0XFF, 0XEE, 0XA2, 0X50, 0X0A, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X05, 0XC7, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XC9, 0X00, 0X00, 0XFA, 0XFF, 0XFF,
  0XE6, 0XFC, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF4, 0XAD,
  0X5C, 0X10, 0X00, 0X00, 0X00, 0X00, 0X21, 0XEF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XA0, 0X00,
  0X16, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF9, 0XB7, 0X3D, 0X00, 0X00, 0X00, 0X55,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X68, 0X2B, 0X6A, 0XD9,
  0XFF, 0XFF, 0X9F, 0X00, 0X58, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XEC,
  0X00, 0X00, 0X00, 0X00, 0X98, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0X94, 0X00, 0X00, 0X06, 0X91, 0XFF, 0XDC, 0X19, 0XD2, 0XFF, 0XFF, 0XFF,
  0XFC, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XCF, 0X00, 0X00, 0X00, 0X00, 0X09, 0XC6, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XA9, 0X1D, 0X00, 0X00, 0XC6, 0XFF, 0XFF,
  0XFF, 0XD5, 0X5C, 0X18, 0X00, 0X10, 0X9D, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XB4, 0X14, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X0F, 0XB9, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFB, 0XBC,
  0X9E, 0XF0, 0XFF, 0XFF, 0XFF, 0X1D, 0X00, 0X00, 0X00, 0X21, 0XC7, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF7, 0X6D, 0X01, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X02, 0X65, 0XD7, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XDC, 0X9E, 0X9E,
  0XC7, 0XFC, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XD4,
  0X2E, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X39, 0XAF, 0XEF, 0XFF, 0XFF, 0XFF, 0XE5, 0X64, 0X6C, 0XFF, 0XF2,
  0XC7, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0X97, 0X09, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X69, 0XF8, 0XFF, 0XFF, 0XFF, 0XFF, 0XD3, 0X18,
  0X00, 0X5E, 0XFF, 0XA2, 0X00, 0X8E, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XE7, 0X50, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X64, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XF3, 0X20, 0X00, 0X13, 0XE1, 0XFF, 0XC0, 0X00, 0X05, 0XD5, 0XFF,
  0XFF, 0XFB, 0XC9, 0XEA, 0XFF, 0XFB, 0XCB, 0X72, 0X0D, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X21, 0XF5,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X97, 0X00, 0X1A, 0XCC, 0XFF, 0XFF, 0XFC,
  0X20, 0X00, 0X65, 0XFF, 0XFF, 0XFF, 0XF7, 0X55, 0X0C, 0X04, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X9C, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XCE, 0X8F, 0XEF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XAD, 0X00, 0X25, 0XFF, 0XFF, 0XFF, 0XFF, 0XF9,
  0X31, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X0A, 0XF4, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X96, 0X60, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XB9, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X52, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0X1A, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0XA6, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XB1, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0X54, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X08, 0XF2, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFB, 0X3A, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X74, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X4F, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X76, 0X00, 0XD1, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X7A, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0XA3, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF9, 0X71,
  0X00, 0X00, 0X5F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0X7B, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X07, 0XF0, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XF9, 0XA8, 0X2A, 0X00, 0X00, 0X00, 0X01, 0XB5, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X7D, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X42, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0XF8, 0XB3, 0X5F, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0D,
  0XC3, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X7E, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X31,
  0XFC, 0XFF, 0XF8, 0XB1, 0X5D, 0X0F, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X08, 0X94, 0XFE, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
  0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X37, 0X4F, 0X0E, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X50, 0XED,
  0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X81, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X1E, 0XC3, 0XFF, 0XFF, 0XFF, 0XFF, 0X82, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X83, 0XFC, 0XFF,
  0XFF, 0X7C, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X40, 0XD9, 0XDF, 0X29, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
  0X00, 0X00, 0X00, 0X00 };
const uint8_t logo_mask[LOGO_HEIGHT][(LOGO_WIDTH + 7) / 8] = {
  0X00, 0X00, 0X06, 0X00, 0X00, 0X00, 0X00, 0X0F, 0X00, 0X00, 0X00, 0X00,
  0X0F, 0X00, 0X00, 0X00, 0X00, 0X1F, 0X00, 0X00, 0X00, 0X00, 0X3F, 0X80,
  0X00, 0X00, 0X00, 0X7F, 0X80, 0X00, 0X00, 0X00, 0X7F, 0X80, 0X00, 0X00,
  0X00, 0XFF, 0XC0, 0X00, 0X00, 0X00, 0XFF, 0XC0, 0X00, 0X00, 0X01, 0XFF,
  0XC0, 0X00, 0X00, 0X01, 0XFF, 0XC0, 0X00, 0XFF, 0XF9, 0XFF, 0XC0, 0X00,
  0XFF, 0XFD, 0XFF, 0XC0, 0X00, 0X7F, 0XFF, 0XFF, 0XC0, 0X00, 0X7F, 0XFF,
  0XFF, 0XFF, 0X80, 0X3F, 0XFF, 0XFF, 0XFF, 0XF0, 0X1F, 0XFF, 0XFF, 0XFF,
  0XFE, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0X0F, 0XFF, 0XFF, 0XFF, 0XFF, 0X07,
  0XFF, 0XFF, 0XFF, 0XFE, 0X03, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0XFF, 0XFF,
  0XFF, 0XF0, 0X00, 0X3F, 0XFF, 0XFF, 0XE0, 0X00, 0X7F, 0XFF, 0XFF, 0X80,
  0X00, 0XFF, 0XFF, 0XFE, 0X00, 0X01, 0XFF, 0XFF, 0XE0, 0X00, 0X03, 0XFF,
  0XFF, 0XF0, 0X00, 0X03, 0XFF, 0XFF, 0XF8, 0X00, 0X03, 0XFF, 0XFF, 0XF8,
  0X00, 0X07, 0XFF, 0XFF, 0XF8, 0X00, 0X07, 0XFF, 0XBF, 0XF8, 0X00, 0X07,
  0XFF, 0X3F, 0XF8, 0X00, 0X0F, 0XFE, 0X1F, 0XF8, 0X00, 0X0F, 0XFC, 0X1F,
  0XF8, 0X00, 0X0F, 0XE0, 0X0F, 0XF8, 0X00, 0X0F, 0X00, 0X07, 0XFC, 0X00,
  0X00, 0X00, 0X01, 0XFC, 0X00, 0X00, 0X00, 0X00, 0XFC, 0X00, 0X00, 0X00,
  0X00, 0X78, 0X00, 0X00, 0X00, 0X00, 0X18, 0X00 };
This guide was first published on Jun 26, 2019. It was last updated on Jun 26, 2019. This page (Programming) was last updated on Oct 07, 2019.