Nesso Mazes : IMU Test

Navigate endless mazes using the built in IMU..

Beats just serial graphing the values.. :stuck_out_tongue:

Was fun digging into the maze creation algo’s..

Based on recursive back trace..

but didn’t expect the recursion to work on these little babies..

so changed it from recursion to iterations..

I started by porting the JoyStickMaze project in my git..

But that was a static maze and while it fit it was also too small..

So I started researching and coding..

This is what I came up with..

/*
  Created 2025.25.11 ~q

  NessoMazes - navigate mazes using IMU..
  The nesso should be flat upon startup as this will
  be used as zero..

  maze creating algos research..
  https://weblog.jamisbuck.org/2010/12/27/maze-generation-recursive-backtracking
  fund some c code in above..
  https://gist.github.com/jamis/754545
  the above gives some ruby code..

  The Zeros allow for it to start and return to a stopped state.

  Finishing the maze drops you right back into a new one..
  improvements 2025.27.11 ~q
   added splash screen.
   added system state machine.
   removed all delays.
   added async back ground music.
   added info screen, can turn music on/off and change song..
   also has battery voltage and charge, currently not reporting properly
   but should be fixed in the future??


  No AI was used or consulted during the creation of this software.. :P
  No warranties, no gaurantees..
  Coded with love..
  be it harm none, do as ye wish..
  have fun, code on.. ~q
  www.github.com/qubits-us/

*/

#include <Arduino.h>
#include <M5GFX.h>
#include <M5Unified.h>
#include <Arduino_BMI270_BMM150.h>
#include <stack>
#include "Muzak.h"


NessoBattery *battery;

#define TONE_PIN 11

#define DIR_STOP 0
#define DIR_UP 1
#define DIR_DWN 2
#define DIR_LEFT 3
#define DIR_RIGHT 4
//max maze size
#define MAX_HEIGHT 15
#define MAX_WIDTH 20
//size of the maze..
int8_t MazeWidth = 15;
int8_t MazeHeight = 8;
//cell size in pixels..
int8_t CellSize = 15;
//player size in pixels..
int8_t PlayerSize = 2;
//data that needs stacked..
struct MazeCell {
  int8_t x;
  int8_t y;
  int8_t Dirs[4];
  int8_t DirStep;
};
//our cell..
MazeCell Cell;
//player position within the maze..
struct PosPlayer {
  int8_t x;
  int8_t y;
};
PosPlayer PlayerPos;
//dir bit fields or'd into maze cells..
enum { N = 1,
       E = 4,
       S = 2,
       W = 8 };
//directions
int8_t directions[4] = { N, E, S, W };
//direction movements..
int8_t DX[9];
int8_t DY[9];
//opposite directions
int8_t OPPS[9];
//maze Grid..
int8_t MAZE[MAX_WIDTH][MAX_HEIGHT];
//our stack of cells..
std::stack<MazeCell> q;
//need a gap to see outer walls..
int gap = CellSize / 2;

//IMU values
float xValue = 0;
float yValue = 0;
//zero might not be zero
float xZero, yZero;
//tilt threshold
const float thresh = 0.10;

//initial starting pos, top right, exit is bottom left..
int playerX = CellSize / 2 + PlayerSize / 2;
int playerY = CellSize / 2 + PlayerSize / 2;

unsigned long now, lastMove;
bool debug = false;
//maze speed, increase to 1000 when debugging..
unsigned long delayMove = 200;
enum statesSystem { state_Splash = 0,
                    state_Maze = 1,
                    state_Info = 2,
                    state_Wait = 3,
                    state_WaitBtn = 4,
                    state_Launch = 5
} stateSystem = state_Splash;

statesSystem nextState = state_Maze;

struct ScreenButton {
  int x;
  int y;
  int w;
  int h;
};

ScreenButton Buttons[2];

unsigned long waitStart;
unsigned long waitFor;

unsigned long lastButton;
unsigned long btnDebounce = 50;
int lastBtnState = HIGH;

unsigned long lastTouch;
unsigned long touchDebounce = 100;




//music player globals
int Music_Tempo = 240;
int Music_WholeNote = (60000 * 4) / Music_Tempo;
int Music_Note;
int Note_Divider = 0;
int CurrNote = -2;
bool NotePlaying = false;
unsigned long NoteStart, NoteDuration;
bool Music_Enabled = false;
int Music_Song = 0;

const int16_t MusicTempos[] = { 150,
                                300,
                                600,
                                400,
                                400,
                                300,
                                300 };



//Creates the maze..
void CreateMaze() {
  //opposites
  OPPS[N] = S;
  OPPS[S] = N;
  OPPS[E] = W;
  OPPS[W] = E;
  //direction steps..
  DX[N] = 0;
  DX[E] = 1;
  DX[S] = 0;
  DX[W] = -1;
  DY[N] = -1;
  DY[E] = 0;
  DY[S] = 1;
  DY[W] = 0;
  //initialize random..
  randomSeed(analogRead(GROVE_IO_0));
  //out with the old..
  memset(&MAZE[0], 0, sizeof(MAZE));
  //shuffle the directions..
  ShuffleDirections(directions, 4);
  //init our first cell..
  Cell.x = 0;
  Cell.y = 0;
  Cell.DirStep = 0;
  Cell.Dirs[0] = directions[0];
  Cell.Dirs[1] = directions[1];
  Cell.Dirs[2] = directions[2];
  Cell.Dirs[3] = directions[3];
  //push the cell to the stack
  q.push(Cell);
  //carve the maze passages..
  CarveMaze();
  //place our player into a maze cell..
  PlayerPos.x = 0;
  PlayerPos.y = 0;
}

//Shuffles a byte array..
void ShuffleDirections(int8_t *dirs, int size) {
  for (int i = 0; i < size; i++) {
    int r = i + (random() % (size - i));
    int8_t temp = dirs[i];
    dirs[i] = dirs[r];
    dirs[r] = temp;
  }
}

//Carves passages into the maze grid..
//a recursive backtracking algorythm..
//uses iteration in place of recursion..
void CarveMaze() {
  int dx, dy, nx, ny, cx, cy;
  int step, i;
  //loop until stack is empty..
  while (not q.empty()) {
    //get top cell..
    Cell = q.top();
    cx = Cell.x;
    cy = Cell.y;
    step = Cell.DirStep;
    for (i = 0; i < 4; i++)
      directions[i] = Cell.Dirs[i];
    //check all 4 directions..
    for (i = step; i < 4; i++) {
      dx = DX[directions[i]];
      dy = DY[directions[i]];
      nx = cx + dx;
      ny = cy + dy;
      //check valid grid cell..
      if (((nx < MazeWidth) & (nx >= 0)) & ((ny < MazeHeight) & (ny >= 0))) {
        //check if it empty..
        if (MAZE[nx][ny] == 0) {
          //valid cell, let's tunnel into it..
          MAZE[cx][cy] = (int8_t)((int8_t)MAZE[cx][cy] | (int8_t)directions[i]);
          MAZE[nx][ny] = (int8_t)((int8_t)MAZE[nx][ny] | (int8_t)OPPS[directions[i]]);
          //replace old cell..
          q.pop();
          //if we really need to come back here..
          if (i < 3) {
            Cell.DirStep = i;
            q.push(Cell);
          }
          //stack the new cell..
          Cell.x = nx;
          Cell.y = ny;
          Cell.DirStep = 0;
          //shuffle directions..
          ShuffleDirections(directions, 4);
          for (int j = 0; j < 4; j++)
            Cell.Dirs[j] = directions[j];
          q.push(Cell);
          //break out and tunnel into new cell..
          break;
        }  //if empty cell
      }    //valid grid pos
    }      // for all directions..
    //pop the stack if we visited all dirs..
    if (i == 4)
      q.pop();
    //don't be a hog!!
    yield();
  }  //while stack not empty..
}

//prints the maze in ascii to the serial monitor..
//don't look too hard, you may go cross eyed.. :)
void SerialPrintMaze() {
  for (int x = 0; x < (MazeWidth * 2); x++)
    Serial.print("_");
  Serial.println();
  for (int y = 0; y < MazeHeight; y++) {
    Serial.print("|");
    for (int x = 0; x < MazeWidth; x++) {
      Serial.print(((MAZE[x][y] & S) != 0) ? " " : "_");
      if ((MAZE[x][y] & E) != 0) {
        Serial.print((((MAZE[x][y] | MAZE[x + 1][y]) & S) != 0) ? " " : "_");
      } else {
        Serial.print("|");
      }
    }
    Serial.println();
  }
}

//draw the maze to the display..
//draws cell by cell..
void DrawMaze(uint16_t color) {
  int x, y, posx, posy, ex, ey, cell;
  x = 0;
  y = 0;
  cell = 0;
  gap = CellSize / 2;
  posx = 1;
  posy = 1;
  ey = 1;
  ex = CellSize;
  M5.Display.fillScreen(TFT_BLACK);
  for (y = 0; y < MazeHeight; y++) {
    for (x = 0; x < MazeWidth; x++) {
      posx = (x * CellSize) + gap;
      posy = (y * CellSize) + gap;
      ey = posy;
      ex = posx + CellSize;
      //top
      if ((MAZE[x][y] & N) == 0)
        M5.Display.drawLine(posx, posy, ex, ey, color);
      ey = posy + CellSize;
      ex = posx;
      //left
      if ((MAZE[x][y] & W) == 0)
        M5.Display.drawLine(posx, posy, ex, ey, color);
      posy = posy + CellSize;
      ex = posx + CellSize;
      ey = posy;
      //bottom
      if ((MAZE[x][y] & S) == 0)
        M5.Display.drawLine(posx, posy, ex, ey, color);
      posx += CellSize;
      ex = posx;
      ey = posy - CellSize;
      //if not the exit..
      if ((x == MazeWidth - 1) && (y == MazeHeight - 1)) {
        if (debug) Serial.println("Door");
      } else {
        //right
        if ((MAZE[x][y] & E) == 0)
          M5.Display.drawLine(posx, posy, ex, ey, color);
      }
    }
  }
}

void LaunchMaze() {
  M5.Display.fillScreen(TFT_BLACK);
  //speed check vars..
  unsigned long start, end;
  //how much ram free before..
  Serial.printf("Starting heap Available: %d\n", ESP.getFreeHeap());
  start = millis();
  CreateMaze();
  end = millis();
  //how long..
  Serial.printf("Maze Generation Completed Millis: %d\n", (end - start));
  //subtract from above, looks like about 356b, stays the same after
  //multiple maze generations..
  Serial.printf("Ending heap Available: %d\n", ESP.getFreeHeap());
  //print it out to the serial monitor..
  SerialPrintMaze();
  //draw it on the screen..
  DrawMaze(YELLOW);

  stateSystem = state_Maze;
}

//step the maze..
void StepMaze() {
  int dir = readIMU();
  drawPlayer(WHITE);

  // Check if the user's next move will intersect with a wall
  bool canMove = dir != DIR_STOP;
  int nextX = PlayerPos.x;
  int nextY = PlayerPos.y;

  //navigate maze..
  switch (dir) {
    case DIR_LEFT:
      nextX -= 1;
      if (nextX < 0) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & W) == 0) canMove = false;
      }
      break;
    case DIR_UP:
      nextY -= 1;
      if (nextY < 0) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & N) == 0) canMove = false;
      }
      break;
    case DIR_RIGHT:
      nextX += 1;
      if (nextX > MazeWidth - 1) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & E) == 0) canMove = false;
      }
      break;
    case DIR_DWN:
      nextY += 1;
      if (nextY > MazeHeight - 1) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & S) == 0) canMove = false;
      }
      break;
  }

  // move the user if allowed
  if (canMove) {
    //erase last pos..
    drawPlayer(BLACK);
    PlayerPos.x = nextX;
    PlayerPos.y = nextY;
    drawPlayer(WHITE);
  }
}



//draw player into a new cell of the maze..
//also checks for completion and creates new maze..
void drawPlayer(uint16_t color) {
  //are we done..
  if (PlayerPos.x == MazeWidth - 1 && PlayerPos.y == MazeHeight - 1) {
    LaunchMaze();
  } else {
    //translate grid to screen cords..
    playerX = (PlayerPos.x * CellSize) + (CellSize / 2) + (PlayerSize / 2) + gap;
    playerY = (PlayerPos.y * CellSize) + (CellSize / 2) + (PlayerSize / 2) + gap;
    //draw the player..
    M5.Display.fillCircle(playerX, playerY, PlayerSize, color);
  }
}


//Read the IMU accelerator and return a direction..
int readIMU() {
  //default to stopped
  int result = DIR_STOP;


  if (IMU.accelerationAvailable()) {
    float zValue;
    IMU.readAcceleration(xValue, yValue, zValue);
    if (debug) {
      Serial.print("X:");
      Serial.print(xValue);
      Serial.print(" Y:");
      Serial.print(yValue);
    }

    if (xValue < (xZero - thresh)) {
      //left
      result = DIR_LEFT;
      if (debug) Serial.print(" Left");

    } else if (xValue > (xZero + thresh)) {
      //right
      result = DIR_RIGHT;
      if (debug) Serial.print(" Right");
    }

    if (result == DIR_STOP) {
      if (yValue < (yZero - thresh)) {
        //down
        result = DIR_DWN;
        if (debug) Serial.print(" Down");
      } else if (yValue > (yZero + thresh)) {
        //up
        result = DIR_UP;
        if (debug) Serial.print(" Up");
      }
    }
  }

  if (debug) {
    if (result == DIR_STOP) Serial.print(" Stop");
    Serial.println();
  }
  return result;
}




void setup() {
  Serial.begin(115200);
  while (!Serial)
    ;

  if (!IMU.begin()) {
    Serial.println("IMU failed!!");
    while (1)
      ;
  }

  delay(2000);  //nap while imu warms up a bit..
  float zValue;
  IMU.readAcceleration(xZero, yZero, zValue);

  Serial.println("**** Nesso Mazes ~q ****");
  //hope nesso is laying flat.. :)
  Serial.print("X zero:");
  Serial.println(xZero);
  Serial.print("Y zero:");
  Serial.println(yZero);

  auto cfg = M5.config();
  M5.begin(cfg);
  M5.Display.init();
  M5.Display.setRotation(1);
  M5.Display.fillScreen(TFT_BLACK);

  //tried a few easy things..
  battery = new NessoBattery();
  //tried unified as well..
  Serial.printf("Batt Level: %d\n", M5.Power.getBatteryLevel());
  Serial.print("Batt Volts: ");
  Serial.println(M5.Power.getBatteryVoltage());
}

void loop() {

  now = millis();
  M5.update();

  lgfx::touch_point_t tp;
  if (M5.Display.getTouch(&tp)) {
    if (now - lastTouch >= touchDebounce) {
      //got a touch..
      lastTouch = now;
      if (stateSystem == state_WaitBtn) {
        //info screen is up, process touches..
        CheckScreenTouch(tp.x, tp.y);
      }
    }
  }

  int b = M5.BtnA.wasClicked();
  if (now - lastButton >= btnDebounce) {
    if (b) {
      //start debounce
      lastButton = now;
      //press and release buttton
      switch (stateSystem) {
        case state_Maze:
          stateSystem = state_Info;
          break;
        case state_WaitBtn:
          DrawMaze(YELLOW);
          stateSystem = state_Maze;
          break;
      }
    }
  }

  if (Music_Enabled) {  //play a tune..
    Music_Tempo = MusicTempos[Music_Song];
    Music_WholeNote = (60000 * 4) / Music_Tempo;
    switch (Music_Song) {
      case 0:
        PlayMusic(furelise, sizeof(furelise) / sizeof(furelise[0]));
        break;
      case 1:
        PlayMusic(minuet, sizeof(minuet) / sizeof(minuet[0]));
        break;
      case 2:
        PlayMusic(cannon, sizeof(cannon) / sizeof(cannon[0]));
        break;
      case 3:
        PlayMusic(odetojoy, sizeof(odetojoy) / sizeof(odetojoy[0]));
        break;
      case 4:
        PlayMusic(brahmslullaby, sizeof(brahmslullaby) / sizeof(brahmslullaby[0]));
        break;
      case 5:
        PlayMusic(starwars, sizeof(starwars) / sizeof(starwars[0]));
        break;
      case 6:
        PlayMusic(imperialmarch, sizeof(imperialmarch) / sizeof(imperialmarch[0]));
        break;
    }
  }

  switch (stateSystem) {
    case state_Maze:
      if (now - lastMove >= delayMove) {
        lastMove = now;
        StepMaze();
      }
      break;
    case state_Splash:
      ScreenSplash();
      break;
    case state_Info:
      ScreenInfo();
      break;
    case state_Wait:
      if (now - waitStart >= waitFor) {
        stateSystem = nextState;
      }
      break;
    case state_Launch:
      LaunchMaze();
      break;
  }
  yield();
}


void ScreenSplash() {
  int tw, th, tx, ty, sh, sw;
  sh = M5.Display.height();
  sw = M5.Display.width();
  M5.Display.fillScreen(TFT_BLACK);
  M5.Display.setTextSize(2);
  th = M5.Display.fontHeight();
  tw = M5.Display.textWidth("Nesso Mazes");
  tx = (M5.Display.width() / 2) - (tw / 2);
  ty = (M5.Display.height() / 2) - (th / 2);
  CreateMaze();
  DrawMaze(YELLOW);
  M5.Display.fillRect(tx - 6, ty - 6, tw + 12, th + 12, BLACK);
  M5.Display.drawRect(tx - 6, ty - 6, tw + 12, th + 12, YELLOW);
  M5.Display.drawString("Nesso Mazes", tx, ty);
  waitStart = now;
  waitFor = 5000;
  stateSystem = state_Wait;
  nextState = state_Launch;
}



void ScreenInfo() {
  int tw, th, tx, ty, gap, sh, sw, nw;

  sh = M5.Display.height();
  sw = M5.Display.width();

  M5.Display.fillScreen(TFT_BLACK);
  M5.Display.setTextSize(2);
  M5.Display.setTextColor(WHITE, BLACK);
  //x,y
  tx = 10;
  ty = 10;
  gap = 2;
  tw = M5.Display.textWidth("Music");
  th = M5.Display.fontHeight();
  M5.Display.drawRect(tx, ty, tw + 12, th + 12, YELLOW);
  M5.Display.drawString("Music", tx + 6, ty + 6);
  ty += th + 12 + gap;
  M5.Display.drawRect(tx, ty, tw + 12, th + 24, YELLOW);
  Buttons[0].x = tx;
  Buttons[0].y = ty;
  Buttons[0].w = tw + 12;
  Buttons[0].h = th + 24;
  if (Music_Enabled) {
    nw = M5.Display.textWidth("On");
    M5.Display.drawString("On", (tx + 6) + (nw / 2), ty + 12);
  } else {
    nw = M5.Display.textWidth("Off");
    M5.Display.drawString("Off", (tx + 6) + (nw / 2), ty + 12);
  }
  tx += tw + 12 + gap;
  ty = 10;
  M5.Display.drawRect(tx, ty, tw + 12, th + 12, YELLOW);
  M5.Display.drawString("Song", tx + 12, ty + 6);
  ty += th + 12 + gap;
  M5.Display.drawRect(tx, ty, tw + 12, th + 24, YELLOW);
  Buttons[1].x = tx;
  Buttons[1].y = ty;
  Buttons[1].w = tw + 12;
  Buttons[1].h = th + 24;
  nw = M5.Display.textWidth("0");

  M5.Display.drawNumber(Music_Song + 1, tx + (nw / 2) + (tw / 2), ty + 12);
  tx += tw + 12 + gap;
  ty = 10;
  M5.Display.drawRect(tx, ty, tw + 12, th + 12, YELLOW);
  M5.Display.drawString("BattV", tx + 6, ty + 6);
  ty += th + 12 + gap;
  M5.Display.drawRect(tx, ty, tw + 12, th + 24, YELLOW);

  nw = M5.Display.textWidth("9.9");
  M5.Display.drawFloat(battery->getVoltage(), 1, (tx) + (nw / 2), ty + 12);

  tx = 10;
  ty += th + 24 + gap;
  M5.Display.drawRect(tx, ty, sw - (tx * 2), (sh - ty) - 10, YELLOW);
  nw = (sw - (tx * 2)) - 4;
  int cl = battery->getChargeLevel();
  if (cl < 100) {
    float multi = cl / 100.0;
    nw = nw * multi;
  }
  M5.Display.fillRect(tx + 2, ty + 2, nw, (sh - ty) - 14, GREEN);
  M5.Display.drawNumber(cl, (sw / 2) - (tw / 2) + 5, ty + 12);
  stateSystem = state_WaitBtn;
}

bool TouchArea(int px, int py, int x, int y, int w, int h) {
  bool result = false;
  int x2 = x + w;
  int y2 = y + h;
  int touch_x = px;
  int touch_y = py;
  if ((touch_x >= x) && (touch_x <= x2) && (touch_y >= y) && (touch_y <= y2)) result = true;
  if (debug) {
    Serial.printf("X: %d , X1: %d, Y: %d, Y1: %d\n", touch_x, x, touch_y, y);
    Serial.printf("Matched :%d", result);
  }
  return result;
}

void CheckScreenTouch(int tx, int ty) {
  int nw;
  //only have 1 screen to watch..

  if (TouchArea(tx, ty, Buttons[0].x, Buttons[0].y, Buttons[0].w, Buttons[0].h)) {
    //touch on button 1 - music on/off toggle
    if (debug)
      Serial.println("Button 1 touched..");

    if (Music_Enabled) {
      Music_Enabled = false;
      noTone(TONE_PIN);
      nw = M5.Display.textWidth("Off");
      M5.Display.setTextColor(BLACK, BLACK);
      M5.Display.drawString(" On", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
      M5.Display.setTextColor(WHITE, BLACK);
      M5.Display.drawString("Off", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
    } else {
      Music_Enabled = true;
      nw = M5.Display.textWidth("On");
      M5.Display.setTextColor(BLACK, BLACK);
      M5.Display.drawString("Off", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
      M5.Display.setTextColor(WHITE, BLACK);
      M5.Display.drawString(" On", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
    }

  } else if (TouchArea(tx, ty, Buttons[1].x, Buttons[1].y, Buttons[1].w, Buttons[1].h)) {
    //touch on button 2 - song button select
    if (debug)
      Serial.println("Button 2 touched..");
    bool isEnabled = Music_Enabled;
    Music_Enabled = false;
    Music_Song++;
    if (Music_Song > (sizeof(MusicTempos) / sizeof(MusicTempos[0])) - 1) Music_Song = 0;
    CurrNote = -2;
    Music_Enabled = isEnabled;
    M5.Display.waitDisplay();
    //remove old text..
    M5.Display.fillRect(Buttons[1].x + 2, Buttons[1].y + 2, Buttons[1].w - 4, Buttons[1].h - 4, BLACK);
    nw = M5.Display.textWidth("0");
    M5.Display.setTextColor(WHITE, BLACK);
    M5.Display.drawNumber(Music_Song + 1, Buttons[1].x + (nw / 2) + ((Buttons[1].w - 12) / 2), Buttons[1].y + 12);
    M5.Display.display();
  }
}

//async tone music player..
void PlayMusic(const int16_t song[], int elements) {
  static bool resting = false;
  if (now - NoteStart >= NoteDuration) {
    if (NotePlaying) {
      if (!resting) {
        noTone(TONE_PIN);
      } else resting = false;
      NotePlaying = false;
      NoteStart = now;
    } else {
      CurrNote += 2;
      if (CurrNote < (elements - 1)) {
        Music_Note = song[CurrNote];
        Note_Divider = song[CurrNote + 1];
        if (Note_Divider > 0) {
          NoteDuration = Music_WholeNote / Note_Divider;
        } else {
          NoteDuration = Music_WholeNote / abs(Note_Divider);
          NoteDuration *= 1.5;
        }
        if (Music_Note != REST) {
          tone(TONE_PIN, Music_Note);
        } else resting = true;
        NoteStart = now;
        NotePlaying = true;
      } else {
        //pause for a sec, then loop..
        noTone(TONE_PIN);
        NoteStart = now;
        NoteDuration = 1000;
        //Music_Song++;
        //if (Music_Song > (sizeof(MusicTempos) / sizeof(MusicTempos[0])) - 1) Music_Song = 0;
        CurrNote = -2;
      }
    }
  }
}


Muzak.h

edit:added music, off by default..

batt does not display properly for now, sorry..

just some fun stuff..

have fun.. ~q

I did have some strange anomalies during this project..

Newly loaded latest version of Arduino..

I think it defaults to 115200 even though it shows 9600??

and somehow, maybe I sat on the keyboard but I lost my menu, like I’m stuck in full screen mode..

strange days.. ~q

The concept of the serial baud rate is only applicable to communication via a UART interface, which is used by the boards that communicate with the computer through a USB to serial bridge chip. The ESP32-C6 microcontroller on the Nesso N1 instead produces a USB CDC serial port via its direct USB connection to the computer.

It does look like you accidentally put the IDE in full screen mode. Press the F11 keyboard shortcut to toggle the mode.

2 Likes

After adding some features..

I can also confirm battery does not initialize properly when using M5GFX, tried some quick things before setting the Core Debug Level to Warn..

Looks like a fight over the wire bus, throws some more errors every call to NessoBattery.

I also had to use M5Unified to get the button working, probably another side affect of M5GFX.

Kind of figuring someone is working it, no worries here..

happy holidays~q

There is a related discussion here:

1 Like

Yes, I’ve been looking into this myself a bit..

Found this on M5 stack..

Following it to the letter..

Under M5 Stacks boards / libs completely..

Buttons, IMU, Touch Screen and buzzer all good..

But I still get nothing on the batt..

Charge level always shows 100 and volt always 0 and yes it was 65.5 the other way..

I have not as of yet fired up vs code and try to use the latest dev release..

time will tell..

~q

If you discover anything, please let us know. I'm sure the community will be glad to learn more on the subject.

Would you mind providing a minimal sketch that can be used to produce the 0 V reading?

Are you referring to the "esp32" boards platform? I did upload the minimal demonstration sketch I provided in the other topic using the development version of the "esp32" boards platform at revision 3239ee2 (which was the tip of the master branch at the time) and I still got the same incorrect return value from NessoBattery::getVoltage.

I am using the latest release version of the M5GFX library 0.2.17, which is also the tip of that repository's master branch (they haven't pushed any commits to the branch since the time of the release). I do see that there is some additional work in the develop branch of the repository:

Nothing jumps out at me as being relevant, but I only took a quick glance at it.

Using mazes I get 0 but I also multiply to get volts instead of milli volts..

With this..

#include "M5Unified.h"

void setup()
{
    auto cfg = M5.config();
    M5.begin(cfg);
    M5.Display.setRotation(1);
    M5.Display.setTextColor(GREEN);
    M5.Display.setTextDatum(middle_center);
    M5.Display.setTextFont(&fonts::FreeSerifBoldItalic18pt7b);
    M5.Display.setTextSize(1);

    M5.Power.setBatteryCharge(true);
    M5.Power.setChargeCurrent(100);
    M5.Power.setChargeVoltage(4200);
}

void loop()
{
    M5.Display.clear();
    int vol = M5.Power.getBatteryVoltage();
    M5.Display.setCursor(10, 30);
    M5.Display.printf("Bat: %dmv", vol);
    int level = M5.Power.getBatteryLevel();
    M5.Display.setCursor(10, 65);
    M5.Display.printf("Level: %d%%", level);
    M5.Display.setCursor(10, 100);
    m5::Power_Class::is_charging_t status = M5.Power.isCharging();
    M5.Display.printf("Charging: %s", status == m5::Power_Class::is_charging_t::is_charging ? "YES" : "NO");
    delay(1000);
} 

I get

Bat: 4mv

Level:100%

Charging:YES

I have installed

Arduino 2.3.6

M5 Boards 3.2.5

M5GFX 2.17

M5Unified 2.11

Maybe the unified is not up to date as m5 mentions using development branch..

Their website is under a heavy load it’s hit or miss last few days..

~q

Simulating the screen in Windows 11..

First need a working tool chain..

Go here msys2..

install msys2-x86_64-xxxxxxx.exe to c:\msys64
open the UCRT64 prompt..
in the terminal enter the following command..

pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-sdl3

accept all and let it download..

now add c:\msys64\ucrt64\bin to your path variable..
win10-11 click start button, type edit environment variables, windows will look it up for you..
click on the Environment variables button, there you will find you Path variable, edit it, add the above to it..

now open a command prompt and verify working tool chain..
gcc --version
g++ --version
cmake --version
ninja --version
should get responses back from all..

LovyanGFX

download the zip of the above git..
extract the contents, go inside its folder and copy the lovyangfx-master folder..
make a new folder in the root of c:
c:\m5 then paste and rename to lovyangfx..
now we have c:\m5\lovyangfx

next we need the sdl2, really old version too..

SDL2-devel-2.0.22-mingw.zip

extract this..
go into its folder and copy the SDL2-2.0.22 folder..
paste this into the root of the c drive and rename it to just SDL2..
now we have a c:\SDL2 folder

pretty sure that be it..
open a command prompt..
cd c:\m5\lovyangfx\examples_for_pc\cmake_sdl
pressing enter should drop you into the above folder..
now do md build
to make a new folder called build, now change into this folder..
cd build
and configure cmake with this following command
cmake ..
then go back one folder
cd ..
and try to build..
cmake --build build
the project should build and give you an exe in the build folder..
you will need to copy the sdl2.dll to you build folder in order to run the app..
copy c:\sdl2\lib\x64\sdl2.dll
run the program..

once you have this running..
i made a copy of the cmake_sdl folder..
pasted it right into the same folder and renamed it to nessomazes..
then i edited the user_code.cpp and put the code i wanted to sim..
use what ever editor you prefer, i'm using vscode with a cmake plugin..

i was also able to sim lvgl with the above setup just used a newer sdl2 lib..

did have to make a few changes to the code, here’s a copy of what I did for mazes..


// If you write this, you can use drawBmpFile / drawJpgFile / drawPngFile
// #include <stdio.h>

// If you write this, you can use drawBmpUrl / drawJpgUrl / drawPngUrl ( for Windows )
// #include <windows.h>
// #include <winhttp.h>
// #pragma comment (lib, "winhttp.lib")


#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <LGFX_AUTODETECT.hpp>
#include <stack>
#include <ctime>

LGFX Display( 135, 240 );


#define HIGH 1
#define LOW  0

#define BLACK 0x0000
#define WHITE 0xFFFF
#define YELLOW 0xFFE0
#define GREEN 0x07E0

#define DIR_STOP 0
#define DIR_UP 1
#define DIR_DWN 2
#define DIR_LEFT 3
#define DIR_RIGHT 4
//max maze size
#define MAX_HEIGHT 20
#define MAX_WIDTH 50

int8_t LastDir = DIR_STOP;
//size of the maze..
int8_t MazeWidth = 15;
int8_t MazeHeight = 8;
//cell size in pixels..
int8_t CellSize = 15;
//player size in pixels..
int8_t PlayerSize = 2;
//data that needs stacked..
struct MazeCell {
  int8_t x;
  int8_t y;
  int8_t Dirs[4];
  int8_t DirStep;
};
//our cell..
MazeCell Cell;
//player position within the maze..
struct PosPlayer {
  int8_t x;
  int8_t y;
};
PosPlayer PlayerPos;
//dir bit fields or'd into maze cells..
enum { N = 1,
       E = 4,
       S = 2,
       W = 8 };
//directions
int8_t directions[4] = { N, E, S, W };
//direction movements..
int8_t DX[9];
int8_t DY[9];
//opposite directions
int8_t OPPS[9];
//maze Grid..
int8_t MAZE[MAX_WIDTH][MAX_HEIGHT];
//our stack of cells..
std::stack<MazeCell> q;
//need a gap to see outer walls..
int gap = CellSize / 2;
int border_x,border_y;

//IMU values
float xValue = 0;
float yValue = 0;
//zero might not be zero
float xZero, yZero;
//tilt threshold
const float thresh = 0.10;

//initial starting pos, top right, exit is bottom left..
int playerX = CellSize / 2 + PlayerSize / 2;
int playerY = CellSize / 2 + PlayerSize / 2;

unsigned long now, lastMove;
bool debug = false;
//maze speed, increase to 1000 when debugging..
unsigned long delayMove = 10;
enum statesSystem { state_Splash = 0,
                    state_Maze = 1,
                    state_Info = 2,
                    state_Wait = 3,
                    state_WaitBtn = 4,
                    state_Launch = 5
};

//current state
statesSystem stateSystem = state_Splash;
//the next state
statesSystem nextState = state_Maze;

struct ScreenButton {
  int x;
  int y;
  int w;
  int h;
};

ScreenButton Buttons[3];

unsigned long waitStart;
unsigned long waitFor;

unsigned long lastButton;
unsigned long btnDebounce = 50;
int lastBtnState = HIGH;

unsigned long lastTouch;
unsigned long touchDebounce = 200;


//music player globals
int Music_Tempo = 240;
int Music_WholeNote = (60000 * 4) / Music_Tempo;
int Music_Note;
int Note_Divider = 0;
int CurrNote = -2;
bool NotePlaying = false;
unsigned long NoteStart, NoteDuration;
bool Music_Enabled = false;
int Music_Song = 0;

const int16_t MusicTempos[] = { 150,
                                300,
                                600,
                                400,
                                400,
                                300,
                                300 };



//forward declarations..
void ShuffleDirections(int8_t *dirs, int size);
void CarveMaze();
void SerialPrintMaze();
void DrawMaze(uint16_t color);
void LaunchMaze();
void StepMaze();
void drawPlayer(uint16_t color);
void ScreenSplash();
void ScreenInfo();
bool TouchArea(int px, int py, int x, int y, int w, int h);
void CheckScreenTouch(int tx, int ty);
unsigned long millis();



unsigned long millis()
{
  clock_t time = clock();
  unsigned long ms = time / CLOCKS_PER_SEC * 1000;
  return ms;
}

//Creates the maze..
void CreateMaze() {
  //opposites
  OPPS[N] = S;
  OPPS[S] = N;
  OPPS[E] = W;
  OPPS[W] = E;
  //direction steps..
  DX[N] = 0;
  DX[E] = 1;
  DX[S] = 0;
  DX[W] = -1;
  DY[N] = -1;
  DY[E] = 0;
  DY[S] = 1;
  DY[W] = 0;
  //initialize random..
  //randomSeed(analogRead(0));
  srand(time(0));
  //out with the old..
  memset(&MAZE[0], 0, sizeof(MAZE));
  //shuffle the directions..
  ShuffleDirections(directions, 4);
  //init our first cell..
  Cell.x = 0;
  Cell.y = 0;
  Cell.DirStep = 0;
  Cell.Dirs[0] = directions[0];
  Cell.Dirs[1] = directions[1];
  Cell.Dirs[2] = directions[2];
  Cell.Dirs[3] = directions[3];
  //push the cell to the stack
  q.push(Cell);
  //carve the maze passages..
  CarveMaze();
  //place our player into a maze cell..
  PlayerPos.x = 0;
  PlayerPos.y = 0;
}

//Shuffles a byte array..
void ShuffleDirections(int8_t *dirs, int size) {
  for (int i = 0; i < size; i++) {
    int r = i + (rand() % (size - i));
    int8_t temp = dirs[i];
    dirs[i] = dirs[r];
    dirs[r] = temp;
  }
}

//Carves passages into the maze grid..
//a recursive backtracking algorythm..
//uses iteration in place of recursion..
void CarveMaze() {
  int dx, dy, nx, ny, cx, cy;
  int step, i;
  //loop until stack is empty..
  while (not q.empty()) {
    //get top cell..
    Cell = q.top();
    cx = Cell.x;
    cy = Cell.y;
    step = Cell.DirStep;
    for (i = 0; i < 4; i++)
      directions[i] = Cell.Dirs[i];
    //check all 4 directions..
    for (i = step; i < 4; i++) {
      dx = DX[directions[i]];
      dy = DY[directions[i]];
      nx = cx + dx;
      ny = cy + dy;
      //check valid grid cell..
      if (((nx < MazeWidth) & (nx >= 0)) & ((ny < MazeHeight) & (ny >= 0))) {
        //check if it empty..
        if (MAZE[nx][ny] == 0) {
          //valid cell, let's tunnel into it..
          MAZE[cx][cy] = (int8_t)((int8_t)MAZE[cx][cy] | (int8_t)directions[i]);
          MAZE[nx][ny] = (int8_t)((int8_t)MAZE[nx][ny] | (int8_t)OPPS[directions[i]]);
          //replace old cell..
          q.pop();
          //if we really need to come back here..
          if (i < 3) {
            Cell.DirStep = i;
            q.push(Cell);
          }
          //stack the new cell..
          Cell.x = nx;
          Cell.y = ny;
          Cell.DirStep = 0;
          //shuffle directions..
          ShuffleDirections(directions, 4);
          for (int j = 0; j < 4; j++)
            Cell.Dirs[j] = directions[j];
          q.push(Cell);
          //break out and tunnel into new cell..
          break;
        }  //if empty cell
      }    //valid grid pos
    }      // for all directions..
    //pop the stack if we visited all dirs..
    if (i == 4)
      q.pop();
    //don't be a hog!!
  //  yield();
  }  //while stack not empty..
}

//prints the maze in ascii to the serial monitor..
//don't look too hard, you may go cross eyed.. :)
void SerialPrintMaze() {
  for (int x = 0; x < (MazeWidth * 2); x++)
      printf("_");
  printf("\n");
  for (int y = 0; y < MazeHeight; y++) {
    printf("|");
    for (int x = 0; x < MazeWidth; x++) {
      printf(((MAZE[x][y] & S) != 0) ? " " : "_");
      if ((MAZE[x][y] & E) != 0) {
        printf((((MAZE[x][y] | MAZE[x + 1][y]) & S) != 0) ? " " : "_");
      } else {
        printf("|");
      }
    }
    printf("\n");
  }
}

//draw the maze to the display..
//draws cell by cell..
void DrawMaze(uint16_t color) {
  int x, y, posx, posy, ex, ey, cell;
  x = 0;
  y = 0;
  cell = 0;
  gap = CellSize / 2;
  border_y =Display.height()-(CellSize*MazeHeight);
  border_x = Display.width()-(CellSize*MazeWidth);
   border_y = border_y /2;
   border_x = border_x /2;
  posx = border_x;
  posy = border_y;
  ey = 1;
  ex = CellSize;
  Display.fillScreen(TFT_BLACK);
  for (y = 0; y < MazeHeight; y++) {
    for (x = 0; x < MazeWidth; x++) {
      posx = (x * CellSize) + border_x;
      posy = (y * CellSize) + border_y;
      ey = posy;
      ex = posx + CellSize;
      //top
      if ((MAZE[x][y] & N) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      ey = posy + CellSize;
      ex = posx;
      //left
      if ((MAZE[x][y] & W) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      posy = posy + CellSize;
      ex = posx + CellSize;
      ey = posy;
      //bottom
      if ((MAZE[x][y] & S) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      posx += CellSize;
      ex = posx;
      ey = posy - CellSize;
      //if not the exit..
      if ((x == MazeWidth - 1) && (y == MazeHeight - 1)) {
        //if (debug) Serial.println("Door");
      } else {
        //right
        if ((MAZE[x][y] & E) == 0)
          Display.drawLine(posx, posy, ex, ey, color);
      }
    }
  }
}

void LaunchMaze() {
  Display.fillScreen(TFT_BLACK);
  //speed check vars..
  unsigned long start, end;
  //how much ram free before..
  //Serial.printf("Starting heap Available: %d\n", ESP.getFreeHeap());
  start = millis();
  CreateMaze();
  end = millis();
  //how long..
  printf("Maze Generation Completed Millis: %d\n", (end - start));
  //subtract from above, looks like about 356b, stays the same after
  //multiple maze generations..
  //Serial.printf("Ending heap Available: %d\n", ESP.getFreeHeap());
  //print it out to the serial monitor..
  SerialPrintMaze();
  //draw it on the screen..
  DrawMaze(YELLOW);

  stateSystem = state_Maze;
}

unsigned bounded_rand(unsigned range)
{
    for (unsigned x, r;;)
        if (x = rand(), r = x % range, x - r <= -range)
            return r;
}

//step the maze..
void StepMaze() {

  //if we haven't stopped then continue
  int dir;
  if (LastDir == DIR_STOP){
    dir = bounded_rand(5);
   // printf("Was Stopped, new DIR:%d\n", dir);
  } else {
    dir = LastDir;
  //  printf("Continue :%d\n",dir);
  }


  drawPlayer(WHITE);

  // Check if the user's next move will intersect with a wall
  bool canMove = dir != DIR_STOP;
  int nextX = PlayerPos.x;
  int nextY = PlayerPos.y;

  //navigate maze..
  switch (dir) {
    case DIR_LEFT:
      nextX -= 1;
      if (nextX < 0) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & W) == 0) canMove = false;
      }
      break;
    case DIR_UP:
      nextY -= 1;
      if (nextY < 0) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & N) == 0) canMove = false;
      }
      break;
    case DIR_RIGHT:
      nextX += 1;
      if (nextX > MazeWidth - 1) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & E) == 0) canMove = false;
      }
      break;
    case DIR_DWN:
      nextY += 1;
      if (nextY > MazeHeight - 1) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & S) == 0) canMove = false;
      }
      break;
  }

  // move the user if allowed
  if (canMove) {
    //erase last pos..
    drawPlayer(BLACK);
    PlayerPos.x = nextX;
    PlayerPos.y = nextY;
    drawPlayer(WHITE);
    LastDir = dir;
  } else LastDir = DIR_STOP;
}



//draw player into a new cell of the maze..
//also checks for completion and creates new maze..
void drawPlayer(uint16_t color) {
  //are we done..
  if (PlayerPos.x == MazeWidth - 1 && PlayerPos.y == MazeHeight - 1) {
    LaunchMaze();
  } else {
    //translate grid to screen cords..
    playerX = (PlayerPos.x * CellSize) + (CellSize / 2) + (PlayerSize / 2) + border_x;
    playerY = (PlayerPos.y * CellSize) + (CellSize / 2) + (PlayerSize / 2) + border_y;
    //draw the player..
    Display.fillCircle(playerX, playerY, PlayerSize, color);
  }
}




void ScreenSplash() {
  int tw, th, tx, ty, sh, sw;
  sh = Display.height();
  sw = Display.width();
  Display.fillScreen(TFT_BLACK);
  Display.setTextSize(2);
  th = Display.fontHeight();
  tw = Display.textWidth("Nesso Mazes");
  tx = (Display.width() / 2) - (tw / 2);
  ty = (Display.height() / 2) - (th / 2);
  CreateMaze();
  DrawMaze(YELLOW);
  Display.fillRect(tx - 6, ty - 6, tw + 12, th + 12, BLACK);
  Display.drawRect(tx - 6, ty - 6, tw + 12, th + 12, YELLOW);
  Display.drawString("Nesso Mazes", tx, ty);
  waitStart = now;
  waitFor = 5000;
  stateSystem = state_Wait;
  nextState = state_Launch;
}



void ScreenInfo() {
  int tw, th, tx, ty, gap,  border,sh, sw, nw,cw,ch;

  sh = Display.height();
  sw = Display.width();
  printf("Screen H: %d W: %d\n",sh,sw);
  border = 2;
  gap = 2;

  Display.fillScreen(TFT_BLACK);
  Display.setTextSize(2);
  Display.setTextColor(WHITE, BLACK);
  //x,y
  tx = border;
  ty = border;
  //3 cols 2 rows..
  cw = (sw-((border*2)+(gap*2)))/3;
  ch = (sh-((border*2)+(gap*2)))/2;
  printf("Cell H: %d W: %d\n",ch,cw);
  
  tw = Display.textWidth("Music");
  th = Display.fontHeight()+gap;
  Display.drawRect(tx, ty, cw, th, YELLOW);
  Display.drawString("Music", tx + 6, ty + 1);
  ty += th  + gap;
  Display.drawRect(tx, ty, cw, ch - (th+gap), YELLOW);
  Buttons[0].x = tx;
  Buttons[0].y = ty;
  Buttons[0].w = cw;
  Buttons[0].h = ch - (th+gap);

  if (Music_Enabled) {
    nw = Display.textWidth("On");
    Display.drawString("On", (tx + 6) + (nw / 2), ty + 12);
  } else {
    nw = Display.textWidth("Off");
    Display.drawString("Off", (tx + 6) + (nw / 2), ty + 12);
  }
  tx += cw + gap;
  ty = border;
  Display.drawRect(tx, ty, cw, th , YELLOW);
  Display.drawString("Song", tx + 6, ty + 1);
  ty += th  + gap;
  Display.drawRect(tx, ty, cw, ch - (th + gap), YELLOW);
  Buttons[1].x = tx;
  Buttons[1].y = ty;
  Buttons[1].w = cw;
  Buttons[1].h = ch - (th+gap);
  nw = Display.textWidth("0");

  Display.drawNumber(Music_Song + 1, tx + (nw / 2) + (tw / 2), ty + 12);
  tx += cw  + gap;
  ty = border;
  Display.drawRect(tx, ty, cw, ch, YELLOW);
  Display.drawString("BattV", tx + 6, ty + 6);
  ty += th  + gap;
  Display.drawRect(tx, ty, cw, ch- (th+gap), YELLOW);

  nw = Display.textWidth("9.9");
  //3800 is milli volts..
  Display.drawFloat(3800 * 0.001, 1, (tx) + (nw / 2), ty + 12);

  tx = border;
  ty = ch  + gap+border;
  Display.drawRect(tx, ty, sw - (tx * 2), ch - gap, YELLOW);
  Buttons[2].x = tx;
  Buttons[2].y = ty;
  Buttons[2].w = sw - (tx * 2);
  Buttons[2].h = (sh - ty) - 10;
  nw = (sw - (tx * 2)) - 4;
  int cl = 96;//simulated batt level
  if (cl < 100) {
    float multi = cl / 100.0;
    nw = nw * multi;
  }
  Display.fillRect(tx + 2, ty + 2, nw, ch - (gap*2)-2, GREEN);
  Display.drawNumber(cl, (sw / 2) - (tw / 2) + 5, ty + 12);
  stateSystem = state_WaitBtn;
}

bool TouchArea(int px, int py, int x, int y, int w, int h) {
  bool result = false;
  int x2 = x + w;
  int y2 = y + h;
  int touch_x = px;
  int touch_y = py;
  if ((touch_x >= x) && (touch_x <= x2) && (touch_y >= y) && (touch_y <= y2)) result = true;
  if (debug) {
    printf("X: %d , X1: %d, Y: %d, Y1: %d\n", touch_x, x, touch_y, y);
    printf("Matched :%d \n", result);
  }
  return result;
}

void CheckScreenTouch(int tx, int ty) {
  int nw;
  //only have 1 screen to watch..
  if (TouchArea(tx, ty, Buttons[0].x, Buttons[0].y, Buttons[0].w, Buttons[0].h)) {
    //touch on button 1 - music on/off toggle
    if (debug)
      printf("Button 1 touched..\n");
    Display.fillRect(Buttons[0].x + 2, Buttons[0].y + 2, Buttons[0].w - 4, Buttons[0].h - 4, BLACK);

    if (Music_Enabled) {
      Music_Enabled = false;
     // noTone(TONE_PIN);
      nw = Display.textWidth("Off");
      Display.setTextColor(WHITE, BLACK);
      Display.drawString("Off", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
    } else {
      Music_Enabled = true;
      nw = Display.textWidth("On");
      Display.setTextColor(WHITE, BLACK);
      Display.drawString(" On", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
    }
  } else if (TouchArea(tx, ty, Buttons[1].x, Buttons[1].y, Buttons[1].w, Buttons[1].h)) {
    //touch on button 2 - song button select
    if (debug)
      printf("Button 2 touched..\n");
    bool isEnabled = Music_Enabled;
    Music_Enabled = false;
    Music_Song++;
    if (Music_Song > (sizeof(MusicTempos) / sizeof(MusicTempos[0])) - 1) Music_Song = 0;
    CurrNote = -2;
    Music_Enabled = isEnabled;
    //remove old text..
    printf("height : %d\n",Buttons[1].h);
    Display.fillRect(Buttons[1].x + 2, Buttons[1].y + 2, Buttons[1].w - 4, Buttons[1].h - 4, BLACK);
    nw = Display.textWidth("0");
    Display.setTextColor(WHITE, BLACK);
    Display.drawNumber(Music_Song + 1, Buttons[1].x + (nw / 2) + ((Buttons[1].w - 12) / 2), Buttons[1].y + 12);
  }else if (TouchArea(tx, ty, Buttons[2].x, Buttons[2].y, Buttons[2].w, Buttons[2].h)) {
    //close the info screen on battery level touch..
    DrawMaze(YELLOW);
    stateSystem = state_Maze;
  }
}








void setup()
{
  //lcd.init();

  Display.init();
  Display.setRotation(1);

  //LaunchMaze();

//  lcd.drawJpgFile("C:\\test.jpg", 0, 0);
//  lcd.drawPngUrl("http://cyberjapandata.gsi.go.jp/xyz/pale/14/14550/6451.png", 0, 0);
}

void loop()
{
 
  now = millis();

 lgfx::touch_point_t tp;
  if (Display.getTouch(&tp)) {
    if (now - lastTouch >= touchDebounce) {
      //got a touch..
      lastTouch = now;
      if (stateSystem == state_WaitBtn) {
        //info screen is up, process touches..
        CheckScreenTouch(tp.x, tp.y);
      } else
      if (stateSystem == state_Maze){
        stateSystem = state_Info;
      }
    }
  }


switch (stateSystem) {
    case state_Maze:
      if (now - lastMove >= delayMove) {
        lastMove = now;
        StepMaze();
      }
      break;
    case state_Splash:
      ScreenSplash();
      break;
    case state_Info:
      ScreenInfo();
      break;
    case state_Wait:
      if (now - waitStart >= waitFor) {
        stateSystem = nextState;
      }
      break;
    case state_Launch:
      LaunchMaze();
      break;
  }




  lgfx::delay(10);

 // lcd2.fillRect(i & 127, i >> 7, 16, 16, i);
}

have fun..~q

2 Likes

don’t like to wait sorry..

i patched my core with the changes in the variant folder..

downloaded the new Arduino_Nesso_N1 lib and manually installed..

removed the includes to m5’s libs and used our new objects..

first gotcha is there’s a conflict between the new lib and the bmi lib for the imu..

i edited the bmi libs source and commented out the offending lines as i deemed them not to be of importance at this time, blink your own leds, stay off mine..

then it compiled but touch wasn’t working, checking into the N1 libs source I see there was a new object added for touch screen too, so added that and got something touching..

touch cords are screwy, probably the screen rotation, I had to flip them y=x and x=y then I also had to invert x as it was backwards, that took a bit..

everything seems to be working, batt voltage is reporting 4.2v, still haven’t told it to start charging as it’s still at 100%..

I also adding a maze solving algo, use button 2 to make it solve the maze for you, fun to watch..

/*
  Created 2025.25.11 ~q

  NessoMazes - navigate mazes using IMU..
  The nesso should be flat upon startup as this will
  be used as zero..

  maze creating algos research..
  https://weblog.jamisbuck.org/2010/12/27/maze-generation-recursive-backtracking
  fund some c code in above..
  https://gist.github.com/jamis/754545
  the above gives some ruby code..

  The Zeros allow for it to start and return to a stopped state.

  Finishing the maze drops you right back into a new one..
  improvements 2025.27.11 ~q
   added splash screen.
   added system state machine.
   removed all delays.
   added async back ground music.
   added info screen, can turn music on/off and change song..
   also has battery voltage and charge, currently not reporting properly
   but should be fixed in the future??
   revision 2025.11.12 ~q
   ported over to the new Arduino_Nesso_N1 lib..
   touch cords were off in lala land, had to compensate..
   the new lib conflics with 1.2.1 of BMI lib, I commented out the error lines..
   added Auto Solver mode, use button 2 to enable/disbale..
   everything seems to work.. :)



  No AI was used or consulted during the creation of this software.. :P
  No warranties, no gaurantees..
  Coded with love..
  be it harm none, do as ye wish..
  have fun, code on.. ~q
  www.github.com/qubits-us/

*/

#include <Arduino_Nesso_N1.h>
#include <Arduino_BMI270_BMM150.h>
#include <stack>
#include "Muzak.h"

NessoBattery battery;
NessoDisplay Display;
NessoTouch Touch;



#define TONE_PIN 11

#define DIR_STOP 0
#define DIR_UP 1
#define DIR_DWN 2
#define DIR_LEFT 3
#define DIR_RIGHT 4
//max maze size
#define MAX_HEIGHT 20
#define MAX_WIDTH 50
//size of the maze..
int8_t MazeWidth = 15;  //24 for Tb5
int8_t MazeHeight = 8;  //12;
//cell size in pixels..
int8_t CellSize = 15;  // 50;
//player size in pixels..
int8_t PlayerSize = 2;  //10;
//data that needs stacked..
struct MazeCell {
  int8_t x;
  int8_t y;
  int8_t Dirs[4];
  int8_t DirStep;
};
//our cell..
MazeCell Cell;
//player position within the maze..
struct PosPlayer {
  int8_t x;
  int8_t y;
};
PosPlayer PlayerPos;
//dir bit fields or'd into maze cells..
enum { N = 1,
       E = 4,
       S = 2,
       W = 8 };
//directions
int8_t directions[4] = { N, E, S, W };
//direction movements..
int8_t DX[9];
int8_t DY[9];
//opposite directions
int8_t OPPS[9];
//maze Grid..
int8_t MAZE[MAX_WIDTH][MAX_HEIGHT];
int8_t SOL[MAX_WIDTH][MAX_HEIGHT];
//our stack of cells..
std::stack<MazeCell> q;
//need a gap to see outer walls..
int gap = CellSize / 2;
int border_x, border_y;

//IMU values
float xValue = 0;
float yValue = 0;
//zero might not be zero
float xZero, yZero;
//tilt threshold
const float thresh = 0.10;

//initial starting pos, top right, exit is bottom left..
int playerX = CellSize / 2 + PlayerSize / 2;
int playerY = CellSize / 2 + PlayerSize / 2;

unsigned long now, lastMove;
bool debug = true;
//maze speed, increase to 1000 when debugging..
unsigned long delayMove = 100;
enum statesSystem { state_Splash = 0,
                    state_Maze = 1,
                    state_Info = 2,
                    state_Wait = 3,
                    state_WaitBtn = 4,
                    state_Launch = 5
} stateSystem = state_Splash;

statesSystem nextState = state_Maze;

struct ScreenButton {
  int x;
  int y;
  int w;
  int h;
};

ScreenButton Buttons[3];

unsigned long waitStart;
unsigned long waitFor;

ExpanderPin btnPins[2] = { KEY1, KEY2 };
unsigned long lastButton[2];
unsigned long btnDebounce = 50;
int lastBtnState[2] = { HIGH, HIGH };



unsigned long lastTouch;
unsigned long touchDebounce = 200;

bool AutoSolve = false;


//music player globals
int Music_Tempo = 240;
int Music_WholeNote = (60000 * 4) / Music_Tempo;
int Music_Note;
int Note_Divider = 0;
int CurrNote = -2;
bool NotePlaying = false;
unsigned long NoteStart, NoteDuration;
bool Music_Enabled = false;
int Music_Song = 0;

const int16_t MusicTempos[] = { 150,
                                300,
                                600,
                                400,
                                400,
                                300,
                                300 };



//Creates the maze..
void CreateMaze() {
  //opposites
  OPPS[N] = S;
  OPPS[S] = N;
  OPPS[E] = W;
  OPPS[W] = E;
  //direction steps..
  DX[N] = 0;
  DX[E] = 1;
  DX[S] = 0;
  DX[W] = -1;
  DY[N] = -1;
  DY[E] = 0;
  DY[S] = 1;
  DY[W] = 0;
  //initialize random..
  randomSeed(analogRead(0));
  //out with the old..
  memset(&MAZE[0], 0, sizeof(MAZE));
  memset(&SOL[0], 0, sizeof(SOL));

  //shuffle the directions..
  ShuffleDirections(directions, 4);
  //init our first cell..
  Cell.x = 0;
  Cell.y = 0;
  Cell.DirStep = 0;
  Cell.Dirs[0] = directions[0];
  Cell.Dirs[1] = directions[1];
  Cell.Dirs[2] = directions[2];
  Cell.Dirs[3] = directions[3];
  //push the cell to the stack
  q.push(Cell);
  //carve the maze passages..
  CarveMaze();
  //place our player into a maze cell..
  PlayerPos.x = 0;
  PlayerPos.y = 0;
}

//Shuffles a byte array..
void ShuffleDirections(int8_t *dirs, int size) {
  for (int i = 0; i < size; i++) {
    int r = i + (random() % (size - i));
    int8_t temp = dirs[i];
    dirs[i] = dirs[r];
    dirs[r] = temp;
  }
}

//Carves passages into the maze grid..
//a recursive backtracking algorythm..
//uses iteration in place of recursion..
void CarveMaze() {
  int dx, dy, nx, ny, cx, cy;
  int step, i;
  //loop until stack is empty..
  while (not q.empty()) {
    //get top cell..
    Cell = q.top();
    cx = Cell.x;
    cy = Cell.y;
    step = Cell.DirStep;
    for (i = 0; i < 4; i++)
      directions[i] = Cell.Dirs[i];
    //check all 4 directions..
    for (i = step; i < 4; i++) {
      dx = DX[directions[i]];
      dy = DY[directions[i]];
      nx = cx + dx;
      ny = cy + dy;
      //check valid grid cell..
      if (((nx < MazeWidth) & (nx >= 0)) & ((ny < MazeHeight) & (ny >= 0))) {
        //check if it empty..
        if (MAZE[nx][ny] == 0) {
          //valid cell, let's tunnel into it..
          MAZE[cx][cy] = (int8_t)((int8_t)MAZE[cx][cy] | (int8_t)directions[i]);
          MAZE[nx][ny] = (int8_t)((int8_t)MAZE[nx][ny] | (int8_t)OPPS[directions[i]]);
          //replace old cell..
          q.pop();
          //if we really need to come back here..
          if (i < 3) {
            Cell.DirStep = i;
            q.push(Cell);
          }
          //stack the new cell..
          Cell.x = nx;
          Cell.y = ny;
          Cell.DirStep = 0;
          //shuffle directions..
          ShuffleDirections(directions, 4);
          for (int j = 0; j < 4; j++)
            Cell.Dirs[j] = directions[j];
          q.push(Cell);
          //break out and tunnel into new cell..
          break;
        }  //if empty cell
      }    //valid grid pos
    }      // for all directions..
    //pop the stack if we visited all dirs..
    if (i == 4)
      q.pop();
    //don't be a hog!!
    yield();
  }  //while stack not empty..
}

//prints the maze in ascii to the serial monitor..
//don't look too hard, you may go cross eyed.. :)
void SerialPrintMaze() {
  for (int x = 0; x < (MazeWidth * 2); x++)
    Serial.print("_");
  Serial.println();
  for (int y = 0; y < MazeHeight; y++) {
    Serial.print("|");
    for (int x = 0; x < MazeWidth; x++) {
      Serial.print(((MAZE[x][y] & S) != 0) ? " " : "_");
      if ((MAZE[x][y] & E) != 0) {
        Serial.print((((MAZE[x][y] | MAZE[x + 1][y]) & S) != 0) ? " " : "_");
      } else {
        Serial.print("|");
      }
    }
    Serial.println();
  }
}

//draw the maze to the display..
//draws cell by cell..
void DrawMaze(uint16_t color) {
  int x, y, posx, posy, ex, ey, cell;
  x = 0;
  y = 0;
  cell = 0;
  gap = CellSize / 2;
  border_y = Display.height() - (CellSize * MazeHeight);
  border_x = Display.width() - (CellSize * MazeWidth);
  border_y = border_y / 2;
  border_x = border_x / 2;
  posx = border_x;
  posy = border_y;
  ey = 1;
  ex = CellSize;
  Display.fillScreen(TFT_BLACK);
  for (y = 0; y < MazeHeight; y++) {
    for (x = 0; x < MazeWidth; x++) {
      posx = (x * CellSize) + border_x;
      posy = (y * CellSize) + border_y;
      ey = posy;
      ex = posx + CellSize;
      //top
      if ((MAZE[x][y] & N) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      ey = posy + CellSize;
      ex = posx;
      //left
      if ((MAZE[x][y] & W) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      posy = posy + CellSize;
      ex = posx + CellSize;
      ey = posy;
      //bottom
      if ((MAZE[x][y] & S) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      posx += CellSize;
      ex = posx;
      ey = posy - CellSize;
      //if not the exit..
      if ((x == MazeWidth - 1) && (y == MazeHeight - 1)) {
        if (debug) Serial.println("Door");
      } else {
        //right
        if ((MAZE[x][y] & E) == 0)
          Display.drawLine(posx, posy, ex, ey, color);
      }
    }
  }
}

void LaunchMaze() {
  Display.fillScreen(TFT_BLACK);
  //speed check vars..
  unsigned long start, end;
  while (!q.empty()) q.pop();
  //how much ram free before..
  Serial.printf("Starting heap Available: %d\n", ESP.getFreeHeap());
  start = millis();
  CreateMaze();
  end = millis();
  //how long..
  Serial.printf("Maze Generation Completed Millis: %d\n", (end - start));
  //subtract from above, looks like about 356b, stays the same after
  //multiple maze generations..
  Serial.printf("Ending heap Available: %d\n", ESP.getFreeHeap());
  //print it out to the serial monitor..
  SerialPrintMaze();
  //draw it on the screen..
  DrawMaze(YELLOW);
  if (AutoSolve) {
    directions[0] = N;
    directions[1] = E;
    directions[2] = S;
    directions[3] = W;
    Cell.x = 0;
    Cell.y = 0;
    Cell.DirStep = 0;
    for (int i = 0; i < 4; i++)
      Cell.Dirs[i] = directions[i];
    //push the cell to the stack
    q.push(Cell);
  }


  stateSystem = state_Maze;
}

bool SolveMazeStep() {
  bool result = false;
  int dx, dy, nx, ny, cx, cy;
  int step, i;
  //until stack is empty..
  if (not q.empty()) {
    //get top cell..
    Cell = q.top();
    cx = Cell.x;
    cy = Cell.y;
    PlayerPos.x = cx;
    PlayerPos.y = cy;
    step = Cell.DirStep;
    //check all 4 directions..
    for (i = step; i < 4; i++) {
      //calc new pos
      dx = DX[directions[i]];
      dy = DY[directions[i]];
      nx = cx + dx;
      ny = cy + dy;
      if (((MAZE[cx][cy] & directions[i]) != 0) && (SOL[nx][ny] == 0)) {
        SOL[nx][ny] = 1;  //mark cell visited..
        //move into new cell..
        q.pop();
        //come back here..
        Cell.DirStep = i;
        q.push(Cell);

        PlayerPos.x = nx;
        PlayerPos.y = ny;
        //is it the exit..
        if (nx == MazeWidth && ny == MazeHeight) {
          //all done.. empty the q..
          while (!q.empty()) q.pop();
          result = true;
          break;
        }
        //stack the new cell..
        Cell.x = nx;
        Cell.y = ny;
        Cell.DirStep = 0;
        q.push(Cell);
        //break out and tunnel into new cell..
        //printf("moving into new cell x: %d y: %d\n",nx,ny);
        break;
      }
    }  // for all directions..
    //pop the stack if we visited all dirs..
    if (i == 4)
      q.pop();
    //don't be a hog!!
    //  yield();
  } else  //if stack not empty..
    result = true;

  return result;
}

//step through maze..
void StepSolution() {
  // move the user if allowed
  //erase last pos..
  playerX = PlayerPos.x;
  playerY = PlayerPos.y;
  SolveMazeStep();
  if (playerX != PlayerPos.x || playerY != PlayerPos.y) {
    int ny = PlayerPos.y;
    int nx = PlayerPos.x;
    PlayerPos.x = playerX;
    PlayerPos.y = playerY;
    drawPlayer(BLACK);
    PlayerPos.x = nx;
    PlayerPos.y = ny;
    drawPlayer(WHITE);
  }
}


//step the maze..
void StepMaze() {
  int dir = readIMU();
  drawPlayer(WHITE);

  // Check if the user's next move will intersect with a wall
  bool canMove = dir != DIR_STOP;
  int nextX = PlayerPos.x;
  int nextY = PlayerPos.y;

  //navigate maze..
  switch (dir) {
    case DIR_LEFT:
      nextX -= 1;
      if (nextX < 0) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & W) == 0) canMove = false;
      }
      break;
    case DIR_UP:
      nextY -= 1;
      if (nextY < 0) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & N) == 0) canMove = false;
      }
      break;
    case DIR_RIGHT:
      nextX += 1;
      if (nextX > MazeWidth - 1) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & E) == 0) canMove = false;
      }
      break;
    case DIR_DWN:
      nextY += 1;
      if (nextY > MazeHeight - 1) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & S) == 0) canMove = false;
      }
      break;
  }

  // move the user if allowed
  if (canMove) {
    //erase last pos..
    drawPlayer(BLACK);
    PlayerPos.x = nextX;
    PlayerPos.y = nextY;
    drawPlayer(WHITE);
  }
}





//draw player into a new cell of the maze..
//also checks for completion and creates new maze..
void drawPlayer(uint16_t color) {
  int g = (CellSize - (PlayerSize * 2)) / 4;

  //are we done..
  if (PlayerPos.x == MazeWidth - 1 && PlayerPos.y == MazeHeight - 1) {
    LaunchMaze();
  } else {
    //translate grid to screen cords..
    playerX = ((PlayerPos.x * CellSize) + (CellSize / 2) + (PlayerSize / 2) + border_x) - g;
    playerY = ((PlayerPos.y * CellSize) + (CellSize / 2) + (PlayerSize / 2) + border_y) - g;
    //draw the player..
    Display.fillCircle(playerX, playerY, PlayerSize, color);
  }
}



//Read the IMU accelerator and return a direction..
int readIMU() {
  //default to stopped
  int result = DIR_STOP;
  if (IMU.accelerationAvailable()) {
    float zValue;
    IMU.readAcceleration(xValue, yValue, zValue);
    if (debug) {
      // Serial.print("X:");
      //  Serial.print(xValue);
      //  Serial.print(" Y:");
      //  Serial.print(yValue);
    }

    if (xValue < (xZero - thresh)) {
      //left
      result = DIR_LEFT;
      if (debug) Serial.print(" Left");

    } else if (xValue > (xZero + thresh)) {
      //right
      result = DIR_RIGHT;
      if (debug) Serial.print(" Right");
    }

    if (result == DIR_STOP) {
      if (yValue < (yZero - thresh)) {
        //down
        result = DIR_DWN;
        if (debug) Serial.print(" Down");
      } else if (yValue > (yZero + thresh)) {
        //up
        result = DIR_UP;
        if (debug) Serial.print(" Up");
      }
    }
  }

  if (debug) {
     if (result == DIR_STOP) Serial.print(" Stop");
     Serial.println();
  }
  return result;
}



void setup() {
  Serial.begin(115200);


  if (!IMU.begin()) {
    Serial.println("IMU Failed!!");
    while (1)
      ;
  }

  delay(2000);  //nap while imu warms up a bit..

  float zValue;
  IMU.readAcceleration(xZero, yZero, zValue);

  Serial.println("**** Nesso Mazes ~q ****");
  //hope nesso is laying flat.. :)
  Serial.print("X zero:");
  Serial.println(xZero);
  Serial.print("Y zero:");
  Serial.println(yZero);

  Display.begin();
  Display.setRotation(1);
  Display.fillScreen(TFT_BLACK);
  if (!Touch.begin()) {
    Serial.println("Touch failed to begin..");
  }

  delay(1000);
  battery.begin();
  //battery.enableCharge();
  //enters/exits info screen
  pinMode(KEY1, INPUT_PULLUP);
  //toggles auto solve
  pinMode(KEY2, INPUT_PULLUP);
}

void loop() {

  now = millis();

  int16_t touchX, touchY;


  if (Touch.isTouched()) {
    if (Touch.read(touchX, touchY)) {
      if (now - lastTouch >= touchDebounce) {
        //got a touch..
        lastTouch = now;
        if (stateSystem == state_WaitBtn) {
          //info screen is up, process touches..
          CheckScreenTouch(touchX, touchY);
        }  //else if (stateSystem == state_Maze) {
           // stateSystem = state_Info;
        //  }
      }
    }
  }

  for (int i = 0; i < 2; i++) {
    if (now - lastButton[i] >= btnDebounce) {
      byte b = digitalRead(btnPins[i]);
      if (b != lastBtnState[i]) {
        //start debounce
        lastButton[i] = now;
        lastBtnState[i] = b;
        if (b == LOW) {
          //press and release buttton
          if (i == 0) {
            switch (stateSystem) {
              case state_Maze:
                stateSystem = state_Info;
                break;
              case state_WaitBtn:
                DrawMaze(YELLOW);
                stateSystem = state_Maze;
                break;
            }
          } else {
            //key2
            if (!AutoSolve) {
              //turn auto solve on..
              AutoSolve = true;
              while (!q.empty()) q.pop();
              memset(&SOL[0], 0, sizeof(SOL));
              directions[0] = N;
              directions[1] = E;
              directions[2] = S;
              directions[3] = W;
              Cell.x = 0;
              Cell.y = 0;
              Cell.DirStep = 0;
              for (int i = 0; i < 4; i++)
                Cell.Dirs[i] = directions[i];
              //push the cell to the stack
              q.push(Cell);
              drawPlayer(BLACK);
            } else {
              //turn auto solve off..
              AutoSolve = false;
              drawPlayer(BLACK);
              PlayerPos.x = 0;
              PlayerPos.y = 0;
              drawPlayer(WHITE);
            }
          }
        }
      }
    }
  }

  if (Music_Enabled) {  //play a tune..
    Music_Tempo = MusicTempos[Music_Song];
    Music_WholeNote = (60000 * 4) / Music_Tempo;
    switch (Music_Song) {
      case 0:
        PlayMusic(furelise, sizeof(furelise) / sizeof(furelise[0]));
        break;
      case 1:
        PlayMusic(minuet, sizeof(minuet) / sizeof(minuet[0]));
        break;
      case 2:
        PlayMusic(cannon, sizeof(cannon) / sizeof(cannon[0]));
        break;
      case 3:
        PlayMusic(odetojoy, sizeof(odetojoy) / sizeof(odetojoy[0]));
        break;
      case 4:
        PlayMusic(brahmslullaby, sizeof(brahmslullaby) / sizeof(brahmslullaby[0]));
        break;
      case 5:
        PlayMusic(starwars, sizeof(starwars) / sizeof(starwars[0]));
        break;
      case 6:
        PlayMusic(imperialmarch, sizeof(imperialmarch) / sizeof(imperialmarch[0]));
        break;
    }
  }

  switch (stateSystem) {
    case state_Maze:
      if (now - lastMove >= delayMove) {
        lastMove = now;
        if (!AutoSolve){
        StepMaze();} else {
          StepSolution();
        }
      }
      break;
    case state_Splash:
      ScreenSplash();
      break;
    case state_Info:
      ScreenInfo();
      break;
    case state_Wait:
      if (now - waitStart >= waitFor) {
        stateSystem = nextState;
      }
      break;
    case state_Launch:
      LaunchMaze();
      break;
  }
  yield();
}


void ScreenSplash() {
  int tw, th, tx, ty, sh, sw;
  sh = Display.height();
  sw = Display.width();
  Display.fillScreen(TFT_BLACK);
  Display.setTextSize(2);
  th = Display.fontHeight();
  tw = Display.textWidth("Nesso Mazes");
  tx = (Display.width() / 2) - (tw / 2);
  ty = (Display.height() / 2) - (th / 2);
  CreateMaze();
  DrawMaze(YELLOW);
  Display.fillRect(tx - 6, ty - 6, tw + 12, th + 12, BLACK);
  Display.drawRect(tx - 6, ty - 6, tw + 12, th + 12, YELLOW);
  Display.drawString("Nesso Mazes", tx, ty);
  waitStart = now;
  waitFor = 5000;
  stateSystem = state_Wait;
  nextState = state_Launch;
}



void ScreenInfo() {
  int tw, th, tx, ty, gap, border, sh, sw, nw, cw, ch;

  sh = Display.height();
  sw = Display.width();
  //Serial.printf("Screen H: %d W: %d\n", sh, sw);
  border = 10;
  gap = 2;

  Display.fillScreen(TFT_BLACK);
  Display.setTextSize(2);
  Display.setTextColor(WHITE, BLACK);
  //x,y
  tx = 10;
  ty = 10;
  //3 cols 2 rows..
  cw = (sw - ((border * 2) + (gap * 2))) / 3;
  ch = (sh - ((border * 2) + (gap * 2))) / 2;
  // Serial.printf("Cell H: %d W: %d\n", ch, cw);

  tw = Display.textWidth("Music");
  th = Display.fontHeight() + gap;
  Display.drawRect(tx, ty, cw, th, YELLOW);
  Display.drawString("Music", tx + 6, ty + 1);
  ty += th + gap;
  Display.drawRect(tx, ty, cw, ch - (th + gap), YELLOW);
  Buttons[0].x = tx;
  Buttons[0].y = ty;
  Buttons[0].w = cw;
  Buttons[0].h = ch - (th + gap);

  if (Music_Enabled) {
    nw = Display.textWidth("On");
    Display.drawString("On", (tx + 6) + (nw / 2), ty + 12);
  } else {
    nw = Display.textWidth("Off");
    Display.drawString("Off", (tx + 6) + (nw / 2), ty + 12);
  }
  tx += cw + gap;
  ty = 10;
  Display.drawRect(tx, ty, cw, th, YELLOW);
  Display.drawString("Song", tx + 6, ty + 1);
  ty += th + gap;
  Display.drawRect(tx, ty, cw, ch - (th + gap), YELLOW);
  Buttons[1].x = tx;
  Buttons[1].y = ty;
  Buttons[1].w = cw;
  Buttons[1].h = ch - (th + gap);
  nw = Display.textWidth("0");

  Display.drawNumber(Music_Song + 1, tx + (nw / 2) + (tw / 2), ty + 12);
  tx += cw + gap;
  ty = 10;
  Display.drawRect(tx, ty, cw, ch, YELLOW);
  Display.drawString("BattV", tx + 6, ty + 6);
  ty += th + gap;
  Display.drawRect(tx, ty, cw, ch - (th + gap), YELLOW);

  nw = Display.textWidth("9.9");
  Display.drawFloat(battery.getVoltage(), 1, (tx) + (nw / 2), ty + 12);
  // Serial.printf("Voltage: %4.2f",battery.getVoltage());

  tx = 10;
  ty += ch + gap;
  Display.drawRect(tx, ty, sw - (tx * 2), ch - gap, YELLOW);
  Buttons[2].x = tx;
  Buttons[2].y = ty;
  Buttons[2].w = sw - (tx * 2);
  Buttons[2].h = (sh - ty) - 10;
  nw = (sw - (tx * 2)) - 4;
  int cl = battery.getChargeLevel();
  if (cl < 100) {
    float multi = cl / 100.0;
    nw = nw * multi;
  }
  Display.fillRect(tx + 2, ty + 2, nw, ch - (gap * 2), GREEN);
  Display.drawNumber(cl, (sw / 2) - (tw / 2) + 5, ty + 12);
  stateSystem = state_WaitBtn;
}

bool TouchArea(int px, int py, int x, int y, int w, int h) {
  bool result = false;
  int x2 = x + w;
  int y2 = y + h;
  //flip the points around and invert x..
  // Serial.printf("B X: %d  Y: %d\n",px,py);
  int touch_x = py;          //(135 - px);
  int touch_y = (135 - px);  // (240 - py);
                             /// Serial.printf("F X: %d  Y: %d\n",touch_x,touch_y);

  if ((touch_x >= x) && (touch_x <= x2) && (touch_y >= y) && (touch_y <= y2)) result = true;
  if (debug) {
    Serial.printf("X: %d , X1: %d, Y: %d, Y1: %d\n", touch_x, x, touch_y, y);
    Serial.printf("Matched :%d\n", result);
  }
  return result;
}

void CheckScreenTouch(int tx, int ty) {
  int nw;
  //only have 1 screen to watch..
  // Serial.printf("Button 0 x: %d y: %d w: %d h: %d\n",Buttons[0].x, Buttons[0].y, Buttons[0].w, Buttons[0].h);
  //Serial.printf("Button 1 x: %d y: %d w: %d h: %d\n",Buttons[1].x, Buttons[1].y, Buttons[1].w, Buttons[1].h);

  if (TouchArea(tx, ty, Buttons[0].x, Buttons[0].y, Buttons[0].w, Buttons[0].h)) {
    //touch on button 1 - music on/off toggle
    if (debug)
      Serial.println("Button 1 touched..");
    Display.fillRect(Buttons[0].x + 2, Buttons[0].y + 2, Buttons[0].w - 4, Buttons[0].h - 4, BLACK);
    if (Music_Enabled) {
      Music_Enabled = false;
      noTone(TONE_PIN);
      nw = Display.textWidth("Off");
       Display.setTextColor(WHITE, BLACK);
      Display.drawString("Off", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
    } else {
      Music_Enabled = true;
      nw = Display.textWidth("On");
      Display.setTextColor(WHITE, BLACK);
      Display.drawString(" On", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
    }

  } else if (TouchArea(tx, ty, Buttons[1].x, Buttons[1].y, Buttons[1].w, Buttons[1].h)) {
    //touch on button 2 - song button select
    if (debug)
      Serial.println("Button 2 touched..");
    bool isEnabled = Music_Enabled;
    Music_Enabled = false;
    Music_Song++;
    if (Music_Song > (sizeof(MusicTempos) / sizeof(MusicTempos[0])) - 1) Music_Song = 0;
    CurrNote = -2;
    Music_Enabled = isEnabled;
    Display.waitDisplay();
    //remove old text..
    Display.fillRect(Buttons[1].x + 2, Buttons[1].y + 2, Buttons[1].w - 4, Buttons[1].h - 4, BLACK);
    nw = Display.textWidth("0");
    Display.setTextColor(WHITE, BLACK);
    Display.drawNumber(Music_Song + 1, Buttons[1].x + (nw / 2) + ((Buttons[1].w - 12) / 2), Buttons[1].y + 12);
    Display.display();
  }  // else if (TouchArea(tx, ty, Buttons[2].x, Buttons[2].y, Buttons[2].w, Buttons[2].h)) {
     //close the info screen..
  //  DrawMaze(YELLOW);
  // stateSystem = state_Maze;
  //}
}

//async tone music player..
void PlayMusic(const int16_t song[], int elements) {
  static bool resting = false;
  if (now - NoteStart >= NoteDuration) {
    if (NotePlaying) {
      if (!resting) {
        noTone(TONE_PIN);
      } else resting = false;
      NotePlaying = false;
      NoteStart = now;
    } else {
      CurrNote += 2;
      if (CurrNote < (elements - 1)) {
        Music_Note = song[CurrNote];
        Note_Divider = song[CurrNote + 1];
        if (Note_Divider > 0) {
          NoteDuration = Music_WholeNote / Note_Divider;
        } else {
          NoteDuration = Music_WholeNote / abs(Note_Divider);
          NoteDuration *= 1.5;
        }
        if (Music_Note != REST) {
          tone(TONE_PIN, Music_Note);
        } else resting = true;
        NoteStart = now;
        NotePlaying = true;
      } else {
        //pause for a sec, then loop..
        noTone(TONE_PIN);
        NoteStart = now;
        NoteDuration = 1000;
        //Music_Song++;
        //if (Music_Song > (sizeof(MusicTempos) / sizeof(MusicTempos[0])) - 1) Music_Song = 0;
        CurrNote = -2;
      }
    }
  }
}

thanks for the hard work Arduino!!

enjoy.. ~q

2 Likes

Updated Arduino to 2.3.7

Updated esp32 core, Arduino_Nesso_N1 and the BMI IMU Libs..

All seems good, IMU, Touch, buttons, buzzer and battery..

A nice holiday present, great work everyone, thank you!!

~q

2 Likes

This (above) might be useful to be another Topic...

I find the Arduino IDE randomly starts without a toolbar (File... Edit... Sketch... Tools... Help), and sometimes all five of those headers are stacked atop each other in the upper-right of the IDE toolbar. I do not have a screen capture now because I always reboot when I see this because editing and saving (I have not tested compiling and uploading) are also effected.

I’ve seen that in the past before too, that’s something else, but maybe switching to full screen mode and back out might clear it up..

Funny, happened again to me last night, this time it was vscode not arduino, it’s F11 for vscode too..

~q

Sorry, I did not mention that I did the full-screen... restore... full-screen... but the issue persisted.

The Arduino IDE developers are tracking this bug here:

1 Like

@qubits-us - Having fun with mazes...

Regarding Muzak.h file in mazes... Do you know the meaning of the negative duration values (-2, -4, -6, -8, and I presume, -16, -32, -64... if that is a playable note), for example, the following "-8"

NOTE_A4, -8

Is it a dotted 8th (half, quarter, eighth...)?

I am "coding" a song I will add to muzak.h songs... and will post it here when done.

I used pitches.h and changed the notation (easier for me to read), from:

#define NOTE_A4  440
#define NOTE_AS4 466

To (whole list, commented-out unused notes)

#define CN3 131
#define CS3 139
#define DN3 147
#define DS3 156
#define EN3 165
#define FN3 175
#define FS3 185
#define GN3 196
#define GS3 208
#define AN3 220
#define AS3 233
#define BN3 247
#define CN4 262 // middle C
#define CS4 277
#define DN4 294
#define DS4 311
#define EN4 330
#define FN4 349
#define FS4 370
#define GN4 392
#define GS4 415
#define AN4 440
#define AS4 466
#define BN4 494
#define CN5 523
#define CS5 554
#define DN5 587
#define DS5 622
#define EN5 659
#define FN5 698
#define FS5 740
// #define GN5 784
// #define GS5 831
// #define AN5 880
// #define ST1 913.8 // SIT (Frequency Hz) Cadence (S): (913.8) 0.274 ON (1370.6) 0.274 ON (1776.7) 0.38 ON 1.0 OFF.
// #define AS5 932
// #define BN5 988
// #define CN6 1047
// #define CS6 1109
// #define DN6 1175
// #define DS6 1245
// #define EN6 1319
// #define ST2 1370.6 // SIT (Frequency Hz) Cadence (S): (913.8) 0.274 ON (1370.6) 0.274 ON (1776.7) 0.38 ON 1.0 OFF.
// #define FN6 1397
// #define FS6 1480
// #define GN6 1568
// #define GS6 1661
// #define AN6 1760
// #define ST3 1776.7 // SIT (Frequency Hz) Cadence (S): (913.8) 0.274 ON (1370.6) 0.274 ON (1776.7) 0.38 ON 1.0 OFF.
// #define AS6 1865
// #define BN6 1976
// #define CN7 2093
// #define CS7 2217
// #define DN7 2349
// #define DS7 2489
// #define EN7 2637
// #define FN7 2794
// #define FS7 2960
// #define GN7 3136
// #define GS7 3322
// #define AN7 3520
// #define AS7 3729
// #define BN7 3951
// #define CN8 4186
// #define CS8 4435
// #define DN8 4699
// #define DS8 4978

An example is of using this notation is this twelve scales sketch (for Nesso N1)...

// passive buzzer musical scales
// keyboard scale:  https://i.pinimg.com/originals/cf/72/9a/cf729ae2213a849fe6b8cc527a0e7471.png

#include <Arduino_Nesso_N1.h>
#include "freqs.h"  // adapted from pitches.h:  https://gist.github.com/mikeputnam/2820675

int duration, dur = 200;
const int notes = 10;
const int scales = 12;

const int scale[scales][notes] = {
  // N = natural S = sharp
  // O-   W    W    H    W    W    H    W    W    S      // Octave lower, Whole-step, Half-step
  { CN3, CN4, DN4, EN4, FN4, GN4, FN4, EN4, DN4, CN4 },  // C
  { CS3, CS4, DS4, FN4, FS4, GS4, FS4, FN4, DS4, CS4 },  // C#
  { DN3, DN4, EN4, FS4, GN4, AN4, GN4, FS4, EN4, DN4 },  // D
  { DS3, DS4, FN4, GN4, GS4, AS4, GS4, GN4, FN4, DS4 },  // D#
  { EN3, EN4, FS4, GS4, AN4, BN4, AN4, GS4, FS4, EN4 },  // E
  { FN3, FN4, GN4, AN4, AS4, CN5, AS4, AN4, GN4, FN4 },  // F
  { FS3, FS4, GS4, AS4, BN4, CS5, BN4, AS4, GS4, FS4 },  // F#
  { GN3, GN4, AN4, BN4, CN5, DN5, CN5, BN4, AN4, GN4 },  // G
  { GS3, GS4, AS4, CN5, CS5, DS5, CS5, CN5, AS4, GS4 },  // G#
  { AN3, AN4, BN4, CS5, DN5, EN5, DN5, CS5, BN4, AN4 },  // A
  { AS3, AS4, CN5, DN5, DS5, FN5, DS5, DN5, CN5, AS4 },  // A#
  { BN3, BN4, CS5, DS5, EN5, FS5, EN5, DS5, CS5, BN4 }   // B
};

void setup() {
  Serial.begin(115200);
  delay(3000);  // Nesso needs a delay before outputing to Serial Monitor
  Serial.println();
  pinMode(KEY1, INPUT_PULLUP);

  Serial.println("Press KEY1");
  while (digitalRead(KEY1)) {};  // wait for KEY1 press
  delay(500);                    // slight delay

  for (byte j = 0; j < scales; j++) {
    if (j + 1 < 10)
      Serial.print(" ");  // alignment
    Serial.print(j + 1);  // counting numbers
    Serial.print(" of ");
    Serial.print(scales);
    Serial.print(" scales in fifths: ");

    for (byte i = 0; i < notes; i++) {
      Serial.print(i);
      if (i == 0 || i == 9)  // first and last note in scale
        duration = dur * 2;  // double note duration
      else
        duration = dur;
      tone(BEEP_PIN, scale[j][i], duration);
      delay(duration);
    }
    noTone(BEEP_PIN);  // end tone
    Serial.println();
  }
}

void loop() {}
1 Like

yes, it is exactly that dotted notes..

see the comments from the originals..

furelise.ino

fun stuff.. ~q

Thank you. On we go to code the song.

I think this comment in furelise.ino should read "eighth"

a quarter plus an eighteenth!!

Dotted notes are1.5 * duration. duration += duration * 0.5;

Click here for...

The new song, transcribed, as an array...
static constexpr const int16_t stillalive[] = {

// set tempo = 300

// https://forum.arduino.cc/t/nesso-mazes-imu-test/1416402/17
// Still Alive by Jonathan Coultrain
// https://en.wikipedia.org/wiki/Jonathan_Coulton
// https://en.wikipedia.org/wiki/Still_Alive

  // bar 1
  NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 8, 
  NOTE_FS4, 2, REST    , 2, 
  REST    ,-4, NOTE_A3 , 8, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 4, 
  NOTE_FS4, -4, NOTE_D4 , 4, NOTE_E4 , 8, NOTE_A3 , 8, 

  // bar 5
  REST    , 8, NOTE_A3 , 8, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 8, NOTE_CS4,  4, 
  NOTE_D4 ,-4, NOTE_E4 , 4, NOTE_A3 , 8, NOTE_A3 , 4, 
  NOTE_FS4, 8, 

  // bar 9
  REST    , 2, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 8, 
  NOTE_FS4, 2, REST    , 2, 
  REST    ,-4, NOTE_A3 , 8, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 ,-4, 
  NOTE_FS4, 8, NOTE_D4 ,-4, NOTE_E4 , 8, NOTE_A3 ,-4, 

  // bar 13
  REST    , 1, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 8, NOTE_CS4, -4, 
  NOTE_D4 , 8, NOTE_E4 , 4, NOTE_A3 , 8, NOTE_D4 , 8, NOTE_E4 , 8, 
  NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_C4 , 8, REST    , 4, NOTE_A3 , 8, NOTE_AS3, 8, 

  // bar 17
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_C4 , 8, NOTE_C4 , 4, NOTE_C4 , 4, NOTE_A3 , 8, NOTE_AS3, 8,   
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, 
  NOTE_D4 , 8, NOTE_E4 , 8, NOTE_F4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, 

  // bar 21
  NOTE_AS4, 8, NOTE_AS4, 8, NOTE_A4 , 4, NOTE_G4 , 4, NOTE_F4 , 8, NOTE_G4 , 8, 
  NOTE_A4 , 8, NOTE_A4 , 8, NOTE_G4 , 4, NOTE_F4 , 4, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_F4 , 8, NOTE_F4 , 8, NOTE_E4 , 4, NOTE_E4 , 8, NOTE_FS4, 8, NOTE_FS4, 8, 
  REST    , 2, REST    , 2, 

  // bar 25
  REST    , 2, REST    , 2, 
  REST    , 2, REST    , 2, 
  REST    ,-4, NOTE_A3 , 8, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 8, 
  NOTE_FS4, 8, REST    , 2, 

  // bar 29
  REST    , 2, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 ,-4, 
  NOTE_FS4, 8, NOTE_D4 , 4, NOTE_E4 , 4, NOTE_A3 ,-4, 
  REST    , 8, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 4, 

  // bar 33
  NOTE_CS4,  4, NOTE_D4 , 8, NOTE_E4 ,-4, NOTE_A3 , 8, NOTE_A3 , 4, 
  NOTE_FS4, 8, REST    , 4, 
  REST    ,-4, NOTE_A3 , 8, NOTE_B4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 , 8, 
  NOTE_A4 , 4, REST    ,-2,

  // bar 37
  REST    ,-4, NOTE_A3 , 8, NOTE_B4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 ,-4, 
  NOTE_A4 , 8, NOTE_FS4, -4, NOTE_G4 , 8, NOTE_D4 ,-4, 
  REST    ,-2,
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 4, 

  // bar 41
  NOTE_CS4,  4, NOTE_D4 , 8, NOTE_E4 , 4, NOTE_A3 , 8, NOTE_D4 , 8, NOTE_E4 , 8, 
  NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_C4 ,-4, NOTE_A3 , 8, NOTE_AS3, 8, 
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_C4 , 8, NOTE_C4 , 4, NOTE_C4 , 4, NOTE_A3 , 8, NOTE_AS3, 8, 

  // bar 45
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, 
  NOTE_D4 , 8, NOTE_E4 , 8, NOTE_F4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, 
  NOTE_AS4, 8, NOTE_AS4, 8, NOTE_A4 , 4, NOTE_G4 , 4, NOTE_F4 , 8, NOTE_G4 , 8, 
  NOTE_A4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_F4 , 4, NOTE_D4 , 8, NOTE_C4 , 8, 

  // bar 49
  NOTE_D4 , 8, NOTE_F4 , 8, NOTE_F4 , 8, NOTE_E4 , 4, NOTE_E4 , 8, NOTE_FS4, 8, NOTE_FS4, 8, 
  REST    , 2, 
  REST    , 2, REST    , 2, 
  REST    , 2, REST    , 2, 

  // bar 53
  REST    , 2, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 4, 
  NOTE_FS4, -4, REST    , 2, 
  REST    ,-4, NOTE_A3 , 8, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 ,-4, 
  NOTE_FS4, 8, NOTE_D4 ,-4, NOTE_E4 , 8, NOTE_A3 , 8, 

  // bar 57
  REST    , 2, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 4, 
  NOTE_CS4,  4, NOTE_D4 , 8, NOTE_E4 , 4, REST    , 8, NOTE_A3 , 8, NOTE_A3 , 4, 
  NOTE_FS4, 8, REST    , 4, 

  // bar 61
  REST    , 4, REST    , 4, NOTE_B4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 , 4, 
  NOTE_A4 , 4, REST    , 8, 
  REST    , 2, NOTE_B4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 ,-4, 
  NOTE_A4 , 8, NOTE_FS4, -4, NOTE_G4 , 8, NOTE_D4 ,-4, 

  // bar 65
  REST    ,-2, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 4, 
  NOTE_CS4,  4, NOTE_D4 , 8, NOTE_E4 , 4, NOTE_A3 , 8, NOTE_D4 , 8, NOTE_E4 , 8, 
  NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_C4 ,-4, NOTE_A3 , 8, NOTE_AS3, 8, 

  // bar 69
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_C4 , 8, NOTE_C4 , 4, NOTE_C4 , 4, NOTE_A3 , 8, NOTE_AS3, 8, 
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, 
  NOTE_D4 , 8, NOTE_E4 , 8, NOTE_F4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, 

  // bar 73
  NOTE_AS4, 8, NOTE_AS4, 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 , 4, NOTE_F4 , 8, NOTE_G4 , 8, 
  NOTE_A4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_F4 , 4, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_F4 , 8, NOTE_F4 , 8, NOTE_E4 , 4, NOTE_E4 , 8, NOTE_FS4, 8, NOTE_FS4, 8, 
  REST    , 8, REST    , 8, NOTE_A4 , 8, NOTE_A4 , 8, 

  // bar 77
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_E4 , 8, NOTE_FS4, 8, NOTE_A4 ,-4, 
  REST    , 8, REST    , 4, NOTE_A4 , 8, NOTE_A4 , 8, NOTE_A4 , 8, 
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    , 8,   REST    , 4, NOTE_A4 , 8, NOTE_A4 , 8, NOTE_A4 , 8, 

  // bar 81
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    , 4, REST    , 4, NOTE_A4 , 8, NOTE_A4 , 8, 
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    , 8, REST    , 4, NOTE_A4 , 8, NOTE_A4 , 8, NOTE_A4 , 8, 

  // bar 85
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    , 8, REST    , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    ,-4, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_FS4, 8, 
  REST    , 2
};

static constexpr const int16_t furelise[] = {
  // Fur Elise - Ludwig van Beethovem
  // Score available at https://musescore.com/user/28149610/scores/5281944

  //starts from 1 ending on 9
  NOTE_E5, 16, NOTE_DS5, 16, //1
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,
  NOTE_C5, 8,  REST, 16, NOTE_E4, 16, NOTE_E5, 16,  NOTE_DS5, 16,
  
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,//6
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, 
  NOTE_B4, -8, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16, 
  NOTE_A4 , 4, REST, 8, //9 - 1st ending

  //repaets from 1 ending on 10
  NOTE_E5, 16, NOTE_DS5, 16, //1
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,
  NOTE_C5, 8,  REST, 16, NOTE_E4, 16, NOTE_E5, 16,  NOTE_DS5, 16,
  
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,//6
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, 
  NOTE_B4, -8, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16, 
  NOTE_A4, 8, REST, 16, NOTE_B4, 16, NOTE_C5, 16, NOTE_D5, 16, //10 - 2nd ending
  //continues from 11
  NOTE_E5, -8, NOTE_G4, 16, NOTE_F5, 16, NOTE_E5, 16, 
  NOTE_D5, -8, NOTE_F4, 16, NOTE_E5, 16, NOTE_D5, 16, //12
  
  NOTE_C5, -8, NOTE_E4, 16, NOTE_D5, 16, NOTE_C5, 16, //13
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, REST, 16,
  REST, 16, NOTE_E5, 16, NOTE_E6, 16, REST, 16, REST, 16, NOTE_DS5, 16,
  NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16, //19
  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16,  NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4, 8, REST, 16, NOTE_B4, 16, NOTE_C5, 16, NOTE_D5, 16, //24 (1st ending)
  
  //repeats from 11
  NOTE_E5, -8, NOTE_G4, 16, NOTE_F5, 16, NOTE_E5, 16, 
  NOTE_D5, -8, NOTE_F4, 16, NOTE_E5, 16, NOTE_D5, 16, //12
  
  NOTE_C5, -8, NOTE_E4, 16, NOTE_D5, 16, NOTE_C5, 16, //13
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, REST, 16,
  REST, 16, NOTE_E5, 16, NOTE_E6, 16, REST, 16, REST, 16, NOTE_DS5, 16,
  NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16, //19
  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16,  NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4, 8, REST, 16, NOTE_C5, 16, NOTE_C5, 16, NOTE_C5, 16, //25 - 2nd ending

  //continues from 26
  NOTE_C5 , 4, NOTE_F5, -16, NOTE_E5, 32, //26
  NOTE_E5, 8, NOTE_D5, 8, NOTE_AS5, -16, NOTE_A5, 32,
  NOTE_A5, 16, NOTE_G5, 16, NOTE_F5, 16, NOTE_E5, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_AS4, 8, NOTE_A4, 8, NOTE_A4, 32, NOTE_G4, 32, NOTE_A4, 32, NOTE_B4, 32,
  NOTE_C5 , 4, NOTE_D5, 16, NOTE_DS5, 16,
  NOTE_E5, -8, NOTE_E5, 16, NOTE_F5, 16, NOTE_A4, 16,
  NOTE_C5 , 4,  NOTE_D5, -16, NOTE_B4, 32,
 
  
  NOTE_C5, 32, NOTE_G5, 32, NOTE_G4, 32, NOTE_G5, 32, NOTE_A4, 32, NOTE_G5, 32, NOTE_B4, 32, NOTE_G5, 32, NOTE_C5, 32, NOTE_G5, 32, NOTE_D5, 32, NOTE_G5, 32, //33
  NOTE_E5, 32, NOTE_G5, 32, NOTE_C6, 32, NOTE_B5, 32, NOTE_A5, 32, NOTE_G5, 32, NOTE_F5, 32, NOTE_E5, 32, NOTE_D5, 32, NOTE_G5, 32, NOTE_F5, 32, NOTE_D5, 32,
  NOTE_C5, 32, NOTE_G5, 32, NOTE_G4, 32, NOTE_G5, 32, NOTE_A4, 32, NOTE_G5, 32, NOTE_B4, 32, NOTE_G5, 32, NOTE_C5, 32, NOTE_G5, 32, NOTE_D5, 32, NOTE_G5, 32,

  NOTE_E5, 32, NOTE_G5, 32, NOTE_C6, 32, NOTE_B5, 32, NOTE_A5, 32, NOTE_G5, 32, NOTE_F5, 32, NOTE_E5, 32, NOTE_D5, 32, NOTE_G5, 32, NOTE_F5, 32, NOTE_D5, 32, //36
  NOTE_E5, 32, NOTE_F5, 32, NOTE_E5, 32, NOTE_DS5, 32, NOTE_E5, 32, NOTE_B4, 32, NOTE_E5, 32, NOTE_DS5, 32, NOTE_E5, 32, NOTE_B4, 32, NOTE_E5, 32, NOTE_DS5, 32,
  NOTE_E5, -8, NOTE_B4, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, -8, NOTE_B4, 16, NOTE_E5, 16, REST, 16,

  REST, 16, NOTE_DS5, 16, NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, //40
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,
  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,

  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, //46
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4, 8, REST, 16, NOTE_B4, 16, NOTE_C5, 16, NOTE_D5, 16,
  NOTE_E5, -8, NOTE_G4, 16, NOTE_F5, 16, NOTE_E5, 16,
  NOTE_D5, -8, NOTE_F4, 16, NOTE_E5, 16, NOTE_D5, 16,
  NOTE_C5, -8, NOTE_E4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, REST, 16,
  REST, 16, NOTE_E5, 16, NOTE_E6, 16, REST, 16, REST, 16, NOTE_DS5, 16,

  NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_D5, 16, //54
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,
  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, //60
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4, 8, REST, 16, REST, 16, REST, 8, 
  NOTE_CS5 , -4, 
  NOTE_D5 , 4, NOTE_E5, 16, NOTE_F5, 16,
  NOTE_F5 , 4, NOTE_F5, 8, 
  NOTE_E5 , -4,
  NOTE_D5 , 4, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4 , 4, NOTE_A4, 8,
  NOTE_A4, 8, NOTE_C5, 8, NOTE_B4, 8,
  NOTE_A4 , -4,
  NOTE_CS5 , -4,

  NOTE_D5 , 4, NOTE_E5, 16, NOTE_F5, 16, //72
  NOTE_F5 , 4, NOTE_F5, 8,
  NOTE_F5 , -4,
  NOTE_DS5 , 4, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_AS4 , 4, NOTE_A4, 8,
  NOTE_GS4 , 4, NOTE_G4, 8,
  NOTE_A4 , -4,
  NOTE_B4 , 4, REST, 8,
  NOTE_A3, -32, NOTE_C4, -32, NOTE_E4, -32, NOTE_A4, -32, NOTE_C5, -32, NOTE_E5, -32, NOTE_D5, -32, NOTE_C5, -32, NOTE_B4, -32,

  NOTE_A4, -32, NOTE_C5, -32, NOTE_E5, -32, NOTE_A5, -32, NOTE_C6, -32, NOTE_E6, -32, NOTE_D6, -32, NOTE_C6, -32, NOTE_B5, -32, //80
  NOTE_A4, -32, NOTE_C5, -32, NOTE_E5, -32, NOTE_A5, -32, NOTE_C6, -32, NOTE_E6, -32, NOTE_D6, -32, NOTE_C6, -32, NOTE_B5, -32,
  NOTE_AS5, -32, NOTE_A5, -32, NOTE_GS5, -32, NOTE_G5, -32, NOTE_FS5, -32, NOTE_F5, -32, NOTE_E5, -32, NOTE_DS5, -32, NOTE_D5, -32,

  NOTE_CS5, -32, NOTE_C5, -32, NOTE_B4, -32, NOTE_AS4, -32, NOTE_A4, -32, NOTE_GS4, -32, NOTE_G4, -32, NOTE_FS4, -32, NOTE_F4, -32, //84
  NOTE_E4, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,

  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, NOTE_DS5, 16, //88
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16, 
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, 
  NOTE_B4, -8, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16, 
  NOTE_A4, -8, REST, -8,
  REST, -8, NOTE_G4, 16, NOTE_F5, 16, NOTE_E5, 16,
  NOTE_D5 , 4, REST, 8,
  REST, -8, NOTE_E4, 16, NOTE_D5, 16, NOTE_C5, 16,
  
  NOTE_B4, -8, NOTE_E4, 16, NOTE_E5, 8, //96
  NOTE_E5, 8, NOTE_E6, -8, NOTE_DS5, 16,
  NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,

  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, NOTE_DS5, 16, //102
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4 , -4,
};
Your `mazes.ino` code, new song title added, song tempo added
/*
  Created 2025.25.11 ~q

  NessoMazes - navigate mazes using IMU..
  The nesso should be flat upon startup as this will
  be used as zero..

  maze creating algos research..
  https://weblog.jamisbuck.org/2010/12/27/maze-generation-recursive-backtracking
  fund some c code in above..
  https://gist.github.com/jamis/754545
  the above gives some ruby code..

  The Zeros allow for it to start and return to a stopped state.

  Finishing the maze drops you right back into a new one..
  improvements 2025.27.11 ~q
   added splash screen.
   added system state machine.
   removed all delays.
   added async back ground music.
   added info screen, can turn music on/off and change song..
   also has battery voltage and charge, currently not reporting properly
   but should be fixed in the future??
   revision 2025.11.12 ~q
   ported over to the new Arduino_Nesso_N1 lib..
   touch cords were off in lala land, had to compensate..
   the new lib conflics with 1.2.1 of BMI lib, I commented out the error lines..
   added Auto Solver mode, use button 2 to enable/disbale..
   everything seems to work.. :)
  No AI was used or consulted during the creation of this software.. :P
  No warranties, no gaurantees..
  Coded with love..
  be it harm none, do as ye wish..
  have fun, code on.. ~q
  www.github.com/qubits-us/

*/

#include <Arduino_Nesso_N1.h>
#include <Arduino_BMI270_BMM150.h>
#include <stack>
#include "Muzak.h"

NessoBattery battery;
NessoDisplay Display;
NessoTouch Touch;
#define TONE_PIN 11

#define DIR_STOP 0
#define DIR_UP 1
#define DIR_DWN 2
#define DIR_LEFT 3
#define DIR_RIGHT 4
//max maze size
#define MAX_HEIGHT 20
#define MAX_WIDTH 50
//size of the maze..
int8_t MazeWidth = 15;  //24 for Tb5
int8_t MazeHeight = 8;  //12;
//cell size in pixels..
int8_t CellSize = 15;  // 50;
//player size in pixels..
int8_t PlayerSize = 2;  //10;
//data that needs stacked..
struct MazeCell {
  int8_t x;
  int8_t y;
  int8_t Dirs[4];
  int8_t DirStep;
};
//our cell..
MazeCell Cell;
//player position within the maze..
struct PosPlayer {
  int8_t x;
  int8_t y;
};
PosPlayer PlayerPos;
//dir bit fields or'd into maze cells..
enum { N = 1,
       E = 4,
       S = 2,
       W = 8 };
//directions
int8_t directions[4] = { N, E, S, W };
//direction movements..
int8_t DX[9];
int8_t DY[9];
//opposite directions
int8_t OPPS[9];
//maze Grid..
int8_t MAZE[MAX_WIDTH][MAX_HEIGHT];
int8_t SOL[MAX_WIDTH][MAX_HEIGHT];
//our stack of cells..
std::stack<MazeCell> q;
//need a gap to see outer walls..
int gap = CellSize / 2;
int border_x, border_y;

//IMU values
float xValue = 0;
float yValue = 0;
//zero might not be zero
float xZero, yZero;
//tilt threshold
const float thresh = 0.10;

//initial starting pos, top right, exit is bottom left..
int playerX = CellSize / 2 + PlayerSize / 2;
int playerY = CellSize / 2 + PlayerSize / 2;

unsigned long now, lastMove;
bool debug = true;
//maze speed, increase to 1000 when debugging..
unsigned long delayMove = 100;
enum statesSystem { state_Splash = 0,
                    state_Maze = 1,
                    state_Info = 2,
                    state_Wait = 3,
                    state_WaitBtn = 4,
                    state_Launch = 5
} stateSystem = state_Splash;

statesSystem nextState = state_Maze;

struct ScreenButton {
  int x;
  int y;
  int w;
  int h;
};

ScreenButton Buttons[3];

unsigned long waitStart;
unsigned long waitFor;

ExpanderPin btnPins[2] = { KEY1, KEY2 };
unsigned long lastButton[2];
unsigned long btnDebounce = 50;
int lastBtnState[2] = { HIGH, HIGH };
unsigned long lastTouch;
unsigned long touchDebounce = 200;

bool AutoSolve = false;
//music player globals
int Music_Tempo = 240;
int Music_WholeNote = (60000 * 4) / Music_Tempo;
int Music_Note;
int Note_Divider = 0;
int CurrNote = -2;
bool NotePlaying = false;
unsigned long NoteStart, NoteDuration;
bool Music_Enabled = false;
int Music_Song = 0;

const int16_t MusicTempos[] = { 150, // furelise
                                300, // minuet
                                600, // cannon
                                400, // odetojoy
                                400, // brahmlullaby
                                300, // starwars
                                300, // imperialmarch
                                300  // stillalive
                                };
//Creates the maze..
void CreateMaze() {
  //opposites
  OPPS[N] = S;
  OPPS[S] = N;
  OPPS[E] = W;
  OPPS[W] = E;
  //direction steps..
  DX[N] = 0;
  DX[E] = 1;
  DX[S] = 0;
  DX[W] = -1;
  DY[N] = -1;
  DY[E] = 0;
  DY[S] = 1;
  DY[W] = 0;
  //initialize random..
  randomSeed(analogRead(0));
  //out with the old..
  memset(&MAZE[0], 0, sizeof(MAZE));
  memset(&SOL[0], 0, sizeof(SOL));

  //shuffle the directions..
  ShuffleDirections(directions, 4);
  //init our first cell..
  Cell.x = 0;
  Cell.y = 0;
  Cell.DirStep = 0;
  Cell.Dirs[0] = directions[0];
  Cell.Dirs[1] = directions[1];
  Cell.Dirs[2] = directions[2];
  Cell.Dirs[3] = directions[3];
  //push the cell to the stack
  q.push(Cell);
  //carve the maze passages..
  CarveMaze();
  //place our player into a maze cell..
  PlayerPos.x = 0;
  PlayerPos.y = 0;
}

//Shuffles a byte array..
void ShuffleDirections(int8_t *dirs, int size) {
  for (int i = 0; i < size; i++) {
    int r = i + (random() % (size - i));
    int8_t temp = dirs[i];
    dirs[i] = dirs[r];
    dirs[r] = temp;
  }
}

//Carves passages into the maze grid..
//a recursive backtracking algorythm..
//uses iteration in place of recursion..
void CarveMaze() {
  int dx, dy, nx, ny, cx, cy;
  int step, i;
  //loop until stack is empty..
  while (not q.empty()) {
    //get top cell..
    Cell = q.top();
    cx = Cell.x;
    cy = Cell.y;
    step = Cell.DirStep;
    for (i = 0; i < 4; i++)
      directions[i] = Cell.Dirs[i];
    //check all 4 directions..
    for (i = step; i < 4; i++) {
      dx = DX[directions[i]];
      dy = DY[directions[i]];
      nx = cx + dx;
      ny = cy + dy;
      //check valid grid cell..
      if (((nx < MazeWidth) & (nx >= 0)) & ((ny < MazeHeight) & (ny >= 0))) {
        //check if it empty..
        if (MAZE[nx][ny] == 0) {
          //valid cell, let's tunnel into it..
          MAZE[cx][cy] = (int8_t)((int8_t)MAZE[cx][cy] | (int8_t)directions[i]);
          MAZE[nx][ny] = (int8_t)((int8_t)MAZE[nx][ny] | (int8_t)OPPS[directions[i]]);
          //replace old cell..
          q.pop();
          //if we really need to come back here..
          if (i < 3) {
            Cell.DirStep = i;
            q.push(Cell);
          }
          //stack the new cell..
          Cell.x = nx;
          Cell.y = ny;
          Cell.DirStep = 0;
          //shuffle directions..
          ShuffleDirections(directions, 4);
          for (int j = 0; j < 4; j++)
            Cell.Dirs[j] = directions[j];
          q.push(Cell);
          //break out and tunnel into new cell..
          break;
        }  //if empty cell
      }    //valid grid pos
    }      // for all directions..
    //pop the stack if we visited all dirs..
    if (i == 4)
      q.pop();
    //don't be a hog!!
    yield();
  }  //while stack not empty..
}

//prints the maze in ascii to the serial monitor..
//don't look too hard, you may go cross eyed.. :)
void SerialPrintMaze() {
  for (int x = 0; x < (MazeWidth * 2); x++)
    Serial.print("_");
  Serial.println();
  for (int y = 0; y < MazeHeight; y++) {
    Serial.print("|");
    for (int x = 0; x < MazeWidth; x++) {
      Serial.print(((MAZE[x][y] & S) != 0) ? " " : "_");
      if ((MAZE[x][y] & E) != 0) {
        Serial.print((((MAZE[x][y] | MAZE[x + 1][y]) & S) != 0) ? " " : "_");
      } else {
        Serial.print("|");
      }
    }
    Serial.println();
  }
}

//draw the maze to the display..
//draws cell by cell..
void DrawMaze(uint16_t color) {
  int x, y, posx, posy, ex, ey, cell;
  x = 0;
  y = 0;
  cell = 0;
  gap = CellSize / 2;
  border_y = Display.height() - (CellSize * MazeHeight);
  border_x = Display.width() - (CellSize * MazeWidth);
  border_y = border_y / 2;
  border_x = border_x / 2;
  posx = border_x;
  posy = border_y;
  ey = 1;
  ex = CellSize;
  Display.fillScreen(TFT_BLACK);
  for (y = 0; y < MazeHeight; y++) {
    for (x = 0; x < MazeWidth; x++) {
      posx = (x * CellSize) + border_x;
      posy = (y * CellSize) + border_y;
      ey = posy;
      ex = posx + CellSize;
      //top
      if ((MAZE[x][y] & N) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      ey = posy + CellSize;
      ex = posx;
      //left
      if ((MAZE[x][y] & W) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      posy = posy + CellSize;
      ex = posx + CellSize;
      ey = posy;
      //bottom
      if ((MAZE[x][y] & S) == 0)
        Display.drawLine(posx, posy, ex, ey, color);
      posx += CellSize;
      ex = posx;
      ey = posy - CellSize;
      //if not the exit..
      if ((x == MazeWidth - 1) && (y == MazeHeight - 1)) {
        if (debug) Serial.println("Door");
      } else {
        //right
        if ((MAZE[x][y] & E) == 0)
          Display.drawLine(posx, posy, ex, ey, color);
      }
    }
  }
}

void LaunchMaze() {
  Display.fillScreen(TFT_BLACK);
  //speed check vars..
  unsigned long start, end;
  while (!q.empty()) q.pop();
  //how much ram free before..
  Serial.printf("Starting heap Available: %d\n", ESP.getFreeHeap());
  start = millis();
  CreateMaze();
  end = millis();
  //how long..
  Serial.printf("Maze Generation Completed Millis: %d\n", (end - start));
  //subtract from above, looks like about 356b, stays the same after
  //multiple maze generations..
  Serial.printf("Ending heap Available: %d\n", ESP.getFreeHeap());
  //print it out to the serial monitor..
  SerialPrintMaze();
  //draw it on the screen..
  DrawMaze(YELLOW);
  if (AutoSolve) {
    directions[0] = N;
    directions[1] = E;
    directions[2] = S;
    directions[3] = W;
    Cell.x = 0;
    Cell.y = 0;
    Cell.DirStep = 0;
    for (int i = 0; i < 4; i++)
      Cell.Dirs[i] = directions[i];
    //push the cell to the stack
    q.push(Cell);
  }
  stateSystem = state_Maze;
}

bool SolveMazeStep() {
  bool result = false;
  int dx, dy, nx, ny, cx, cy;
  int step, i;
  //until stack is empty..
  if (not q.empty()) {
    //get top cell..
    Cell = q.top();
    cx = Cell.x;
    cy = Cell.y;
    PlayerPos.x = cx;
    PlayerPos.y = cy;
    step = Cell.DirStep;
    //check all 4 directions..
    for (i = step; i < 4; i++) {
      //calc new pos
      dx = DX[directions[i]];
      dy = DY[directions[i]];
      nx = cx + dx;
      ny = cy + dy;
      if (((MAZE[cx][cy] & directions[i]) != 0) && (SOL[nx][ny] == 0)) {
        SOL[nx][ny] = 1;  //mark cell visited..
        //move into new cell..
        q.pop();
        //come back here..
        Cell.DirStep = i;
        q.push(Cell);

        PlayerPos.x = nx;
        PlayerPos.y = ny;
        //is it the exit..
        if (nx == MazeWidth && ny == MazeHeight) {
          //all done.. empty the q..
          while (!q.empty()) q.pop();
          result = true;
          break;
        }
        //stack the new cell..
        Cell.x = nx;
        Cell.y = ny;
        Cell.DirStep = 0;
        q.push(Cell);
        //break out and tunnel into new cell..
        //printf("moving into new cell x: %d y: %d\n",nx,ny);
        break;
      }
    }  // for all directions..
    //pop the stack if we visited all dirs..
    if (i == 4)
      q.pop();
    //don't be a hog!!
    //  yield();
  } else  //if stack not empty..
    result = true;

  return result;
}

//step through maze..
void StepSolution() {
  // move the user if allowed
  //erase last pos..
  playerX = PlayerPos.x;
  playerY = PlayerPos.y;
  SolveMazeStep();
  if (playerX != PlayerPos.x || playerY != PlayerPos.y) {
    int ny = PlayerPos.y;
    int nx = PlayerPos.x;
    PlayerPos.x = playerX;
    PlayerPos.y = playerY;
    drawPlayer(BLACK);
    PlayerPos.x = nx;
    PlayerPos.y = ny;
    drawPlayer(WHITE);
  }
}
//step the maze..
void StepMaze() {
  int dir = readIMU();
  drawPlayer(WHITE);

  // Check if the user's next move will intersect with a wall
  bool canMove = dir != DIR_STOP;
  int nextX = PlayerPos.x;
  int nextY = PlayerPos.y;

  //navigate maze..
  switch (dir) {
    case DIR_LEFT:
      nextX -= 1;
      if (nextX < 0) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & W) == 0) canMove = false;
      }
      break;
    case DIR_UP:
      nextY -= 1;
      if (nextY < 0) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & N) == 0) canMove = false;
      }
      break;
    case DIR_RIGHT:
      nextX += 1;
      if (nextX > MazeWidth - 1) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & E) == 0) canMove = false;
      }
      break;
    case DIR_DWN:
      nextY += 1;
      if (nextY > MazeHeight - 1) {
        canMove = false;
      } else {
        if ((MAZE[PlayerPos.x][PlayerPos.y] & S) == 0) canMove = false;
      }
      break;
  }

  // move the user if allowed
  if (canMove) {
    //erase last pos..
    drawPlayer(BLACK);
    PlayerPos.x = nextX;
    PlayerPos.y = nextY;
    drawPlayer(WHITE);
  }
}
//draw player into a new cell of the maze..
//also checks for completion and creates new maze..
void drawPlayer(uint16_t color) {
  int g = (CellSize - (PlayerSize * 2)) / 4;

  //are we done..
  if (PlayerPos.x == MazeWidth - 1 && PlayerPos.y == MazeHeight - 1) {
    LaunchMaze();
  } else {
    //translate grid to screen cords..
    playerX = ((PlayerPos.x * CellSize) + (CellSize / 2) + (PlayerSize / 2) + border_x) - g;
    playerY = ((PlayerPos.y * CellSize) + (CellSize / 2) + (PlayerSize / 2) + border_y) - g;
    //draw the player..
    Display.fillCircle(playerX, playerY, PlayerSize, color);
  }
}
//Read the IMU accelerator and return a direction..
int readIMU() {
  //default to stopped
  int result = DIR_STOP;
  if (IMU.accelerationAvailable()) {
    float zValue;
    IMU.readAcceleration(xValue, yValue, zValue);
    if (debug) {
      // Serial.print("X:");
      //  Serial.print(xValue);
      //  Serial.print(" Y:");
      //  Serial.print(yValue);
    }

    if (xValue < (xZero - thresh)) {
      //left
      result = DIR_LEFT;
      if (debug) Serial.print(" Left");

    } else if (xValue > (xZero + thresh)) {
      //right
      result = DIR_RIGHT;
      if (debug) Serial.print(" Right");
    }

    if (result == DIR_STOP) {
      if (yValue < (yZero - thresh)) {
        //down
        result = DIR_DWN;
        if (debug) Serial.print(" Down");
      } else if (yValue > (yZero + thresh)) {
        //up
        result = DIR_UP;
        if (debug) Serial.print(" Up");
      }
    }
  }

  if (debug) {
    if (result == DIR_STOP) Serial.print(" Stop");
    Serial.println();
  }
  return result;
}
void setup() {
  Serial.begin(115200);
  if (!IMU.begin()) {
    Serial.println("IMU Failed!!");
    while (1)
      ;
  }

  delay(2000);  //nap while imu warms up a bit..

  float zValue;
  IMU.readAcceleration(xZero, yZero, zValue);

  Serial.println("**** Nesso Mazes ~q ****");
  //hope nesso is laying flat.. :)
  Serial.print("X zero:");
  Serial.println(xZero);
  Serial.print("Y zero:");
  Serial.println(yZero);

  Display.begin();
  Display.setRotation(1);
  Display.fillScreen(TFT_BLACK);
  if (!Touch.begin()) {
    Serial.println("Touch failed to begin..");
  }

  delay(1000);
  battery.begin();
  //battery.enableCharge();
  //enters/exits info screen
  pinMode(KEY1, INPUT_PULLUP);
  //toggles auto solve
  pinMode(KEY2, INPUT_PULLUP);
}

void loop() {

  now = millis();

  int16_t touchX, touchY;
  if (Touch.isTouched()) {
    if (Touch.read(touchX, touchY)) {
      if (now - lastTouch >= touchDebounce) {
        //got a touch..
        lastTouch = now;
        if (stateSystem == state_WaitBtn) {
          //info screen is up, process touches..
          CheckScreenTouch(touchX, touchY);
        }  //else if (stateSystem == state_Maze) {
           // stateSystem = state_Info;
        //  }
      }
    }
  }

  for (int i = 0; i < 2; i++) {
    if (now - lastButton[i] >= btnDebounce) {
      byte b = digitalRead(btnPins[i]);
      if (b != lastBtnState[i]) {
        //start debounce
        lastButton[i] = now;
        lastBtnState[i] = b;
        if (b == LOW) {
          //press and release buttton
          if (i == 0) {
            switch (stateSystem) {
              case state_Maze:
                stateSystem = state_Info;
                break;
              case state_WaitBtn:
                DrawMaze(YELLOW);
                stateSystem = state_Maze;
                break;
            }
          } else {
            //key2
            if (!AutoSolve) {
              //turn auto solve on..
              AutoSolve = true;
              while (!q.empty()) q.pop();
              memset(&SOL[0], 0, sizeof(SOL));
              directions[0] = N;
              directions[1] = E;
              directions[2] = S;
              directions[3] = W;
              Cell.x = 0;
              Cell.y = 0;
              Cell.DirStep = 0;
              for (int i = 0; i < 4; i++)
                Cell.Dirs[i] = directions[i];
              //push the cell to the stack
              q.push(Cell);
              drawPlayer(BLACK);
            } else {
              //turn auto solve off..
              AutoSolve = false;
              drawPlayer(BLACK);
              PlayerPos.x = 0;
              PlayerPos.y = 0;
              drawPlayer(WHITE);
            }
          }
        }
      }
    }
  }

  if (Music_Enabled) {  //play a tune..
    Music_Tempo = MusicTempos[Music_Song];
    Music_WholeNote = (60000 * 4) / Music_Tempo;
    switch (Music_Song) {
      case 0:
        PlayMusic(furelise, sizeof(furelise) / sizeof(furelise[0]));
        break;
      case 1:
        PlayMusic(minuet, sizeof(minuet) / sizeof(minuet[0]));
        break;
      case 2:
        PlayMusic(cannon, sizeof(cannon) / sizeof(cannon[0]));
        break;
      case 3:
        PlayMusic(odetojoy, sizeof(odetojoy) / sizeof(odetojoy[0]));
        break;
      case 4:
        PlayMusic(brahmslullaby, sizeof(brahmslullaby) / sizeof(brahmslullaby[0]));
        break;
      case 5:
        PlayMusic(starwars, sizeof(starwars) / sizeof(starwars[0]));
        break;
      case 6:
        PlayMusic(imperialmarch, sizeof(imperialmarch) / sizeof(imperialmarch[0]));
        break;
      case 7:
        PlayMusic(stillalive, sizeof(stillalive) / sizeof(stillalive[0]));
        break;
    }
  }

  switch (stateSystem) {
    case state_Maze:
      if (now - lastMove >= delayMove) {
        lastMove = now;
        if (!AutoSolve) {
          StepMaze();
        } else {
          StepSolution();
        }
      }
      break;
    case state_Splash:
      ScreenSplash();
      break;
    case state_Info:
      ScreenInfo();
      break;
    case state_Wait:
      if (now - waitStart >= waitFor) {
        stateSystem = nextState;
      }
      break;
    case state_Launch:
      LaunchMaze();
      break;
  }
  yield();
}
void ScreenSplash() {
  int tw, th, tx, ty, sh, sw;
  sh = Display.height();
  sw = Display.width();
  Display.fillScreen(TFT_BLACK);
  Display.setTextSize(2);
  th = Display.fontHeight();
  tw = Display.textWidth("Nesso Mazes");
  tx = (Display.width() / 2) - (tw / 2);
  ty = (Display.height() / 2) - (th / 2);
  CreateMaze();
  DrawMaze(YELLOW);
  Display.fillRect(tx - 6, ty - 6, tw + 12, th + 12, BLACK);
  Display.drawRect(tx - 6, ty - 6, tw + 12, th + 12, YELLOW);
  Display.drawString("Nesso Mazes", tx, ty);
  waitStart = now;
  waitFor = 5000;
  stateSystem = state_Wait;
  nextState = state_Launch;
}
void ScreenInfo() {
  int tw, th, tx, ty, gap, border, sh, sw, nw, cw, ch;

  sh = Display.height();
  sw = Display.width();
  //Serial.printf("Screen H: %d W: %d\n", sh, sw);
  border = 10;
  gap = 2;

  Display.fillScreen(TFT_BLACK);
  Display.setTextSize(2);
  Display.setTextColor(WHITE, BLACK);
  //x,y
  tx = 10;
  ty = 10;
  //3 cols 2 rows..
  cw = (sw - ((border * 2) + (gap * 2))) / 3;
  ch = (sh - ((border * 2) + (gap * 2))) / 2;
  // Serial.printf("Cell H: %d W: %d\n", ch, cw);

  tw = Display.textWidth("Music");
  th = Display.fontHeight() + gap;
  Display.drawRect(tx, ty, cw, th, YELLOW);
  Display.drawString("Music", tx + 6, ty + 1);
  ty += th + gap;
  Display.drawRect(tx, ty, cw, ch - (th + gap), YELLOW);
  Buttons[0].x = tx;
  Buttons[0].y = ty;
  Buttons[0].w = cw;
  Buttons[0].h = ch - (th + gap);

  if (Music_Enabled) {
    nw = Display.textWidth("On");
    Display.drawString("On", (tx + 6) + (nw / 2), ty + 12);
  } else {
    nw = Display.textWidth("Off");
    Display.drawString("Off", (tx + 6) + (nw / 2), ty + 12);
  }
  tx += cw + gap;
  ty = 10;
  Display.drawRect(tx, ty, cw, th, YELLOW);
  Display.drawString("Song", tx + 6, ty + 1);
  ty += th + gap;
  Display.drawRect(tx, ty, cw, ch - (th + gap), YELLOW);
  Buttons[1].x = tx;
  Buttons[1].y = ty;
  Buttons[1].w = cw;
  Buttons[1].h = ch - (th + gap);
  nw = Display.textWidth("0");

  Display.drawNumber(Music_Song + 1, tx + (nw / 2) + (tw / 2), ty + 12);
  tx += cw + gap;
  ty = 10;
  Display.drawRect(tx, ty, cw, ch, YELLOW);
  Display.drawString("BattV", tx + 6, ty + 6);
  ty += th + gap;
  Display.drawRect(tx, ty, cw, ch - (th + gap), YELLOW);

  nw = Display.textWidth("9.9");
  Display.drawFloat(battery.getVoltage(), 1, (tx) + (nw / 2), ty + 12);
  // Serial.printf("Voltage: %4.2f",battery.getVoltage());

  tx = 10;
  ty += ch + gap;
  Display.drawRect(tx, ty, sw - (tx * 2), ch - gap, YELLOW);
  Buttons[2].x = tx;
  Buttons[2].y = ty;
  Buttons[2].w = sw - (tx * 2);
  Buttons[2].h = (sh - ty) - 10;
  nw = (sw - (tx * 2)) - 4;
  int cl = battery.getChargeLevel();
  if (cl < 100) {
    float multi = cl / 100.0;
    nw = nw * multi;
  }
  Display.fillRect(tx + 2, ty + 2, nw, ch - (gap * 2), GREEN);
  Display.drawNumber(cl, (sw / 2) - (tw / 2) + 5, ty + 12);
  stateSystem = state_WaitBtn;
}

bool TouchArea(int px, int py, int x, int y, int w, int h) {
  bool result = false;
  int x2 = x + w;
  int y2 = y + h;
  //flip the points around and invert x..
  // Serial.printf("B X: %d  Y: %d\n",px,py);
  int touch_x = py;          //(135 - px);
  int touch_y = (135 - px);  // (240 - py);
                             /// Serial.printf("F X: %d  Y: %d\n",touch_x,touch_y);

  if ((touch_x >= x) && (touch_x <= x2) && (touch_y >= y) && (touch_y <= y2)) result = true;
  if (debug) {
    Serial.printf("X: %d , X1: %d, Y: %d, Y1: %d\n", touch_x, x, touch_y, y);
    Serial.printf("Matched :%d\n", result);
  }
  return result;
}

void CheckScreenTouch(int tx, int ty) {
  int nw;
  //only have 1 screen to watch..
  // Serial.printf("Button 0 x: %d y: %d w: %d h: %d\n",Buttons[0].x, Buttons[0].y, Buttons[0].w, Buttons[0].h);
  //Serial.printf("Button 1 x: %d y: %d w: %d h: %d\n",Buttons[1].x, Buttons[1].y, Buttons[1].w, Buttons[1].h);

  if (TouchArea(tx, ty, Buttons[0].x, Buttons[0].y, Buttons[0].w, Buttons[0].h)) {
    //touch on button 1 - music on/off toggle
    if (debug)
      Serial.println("Button 1 touched..");
    Display.fillRect(Buttons[0].x + 2, Buttons[0].y + 2, Buttons[0].w - 4, Buttons[0].h - 4, BLACK);
    if (Music_Enabled) {
      Music_Enabled = false;
      noTone(TONE_PIN);
      nw = Display.textWidth("Off");
      Display.setTextColor(WHITE, BLACK);
      Display.drawString("Off", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
    } else {
      Music_Enabled = true;
      nw = Display.textWidth("On");
      Display.setTextColor(WHITE, BLACK);
      Display.drawString(" On", Buttons[0].x + 6 + (nw / 2), Buttons[0].y + 12);
    }

  } else if (TouchArea(tx, ty, Buttons[1].x, Buttons[1].y, Buttons[1].w, Buttons[1].h)) {
    //touch on button 2 - song button select
    if (debug)
      Serial.println("Button 2 touched..");
    bool isEnabled = Music_Enabled;
    Music_Enabled = false;
    Music_Song++;
    if (Music_Song > (sizeof(MusicTempos) / sizeof(MusicTempos[0])) - 1) Music_Song = 0;
    CurrNote = -2;
    Music_Enabled = isEnabled;
    Display.waitDisplay();
    //remove old text..
    Display.fillRect(Buttons[1].x + 2, Buttons[1].y + 2, Buttons[1].w - 4, Buttons[1].h - 4, BLACK);
    nw = Display.textWidth("0");
    Display.setTextColor(WHITE, BLACK);
    Display.drawNumber(Music_Song + 1, Buttons[1].x + (nw / 2) + ((Buttons[1].w - 12) / 2), Buttons[1].y + 12);
    Display.display();
  }  // else if (TouchArea(tx, ty, Buttons[2].x, Buttons[2].y, Buttons[2].w, Buttons[2].h)) {
     //close the info screen..
  //  DrawMaze(YELLOW);
  // stateSystem = state_Maze;
  //}
}

//async tone music player..
void PlayMusic(const int16_t song[], int elements) {
  static bool resting = false;
  if (now - NoteStart >= NoteDuration) {
    if (NotePlaying) {
      if (!resting) {
        noTone(TONE_PIN);
      } else resting = false;
      NotePlaying = false;
      NoteStart = now;
    } else {
      CurrNote += 2;
      if (CurrNote < (elements - 1)) {
        Music_Note = song[CurrNote];
        Note_Divider = song[CurrNote + 1];
        if (Note_Divider > 0) {
          NoteDuration = Music_WholeNote / Note_Divider;
        } else {
          NoteDuration = Music_WholeNote / abs(Note_Divider);
          NoteDuration *= 1.5;
        }
        if (Music_Note != REST) {
          tone(TONE_PIN, Music_Note);
        } else resting = true;
        NoteStart = now;
        NotePlaying = true;
      } else {
        //pause for a sec, then loop..
        noTone(TONE_PIN);
        NoteStart = now;
        NoteDuration = 1000;
        //Music_Song++;
        //if (Music_Song > (sizeof(MusicTempos) / sizeof(MusicTempos[0])) - 1) Music_Song = 0;
        CurrNote = -2;
      }
    }
  }
}
Your `Muzak.h`, new song array added
/*

Muzak.h

Contains musical score arrays..
for back ground music player..

Major Credits for all the musical scores goes too..
https://github.com/robsoncouto/arduino-songs/
Has many more there as well..
Nice work, thanks for sharing!!

2025.28.11 ~q

*/

// pitches.h
#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
#define REST     0

static constexpr const int16_t stillalive[] = {

// set tempo = 300

// https://forum.arduino.cc/t/nesso-mazes-imu-test/1416402/17
// Still Alive by Jonathan Coultrain
// https://en.wikipedia.org/wiki/Jonathan_Coulton
// https://en.wikipedia.org/wiki/Still_Alive

  // bar 1
  NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 8, 
  NOTE_FS4, 2, REST    , 2, 
  REST    ,-4, NOTE_A3 , 8, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 4, 
  NOTE_FS4, -4, NOTE_D4 , 4, NOTE_E4 , 8, NOTE_A3 , 8, 

  // bar 5
  REST    , 8, NOTE_A3 , 8, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 8, NOTE_CS4,  4, 
  NOTE_D4 ,-4, NOTE_E4 , 4, NOTE_A3 , 8, NOTE_A3 , 4, 
  NOTE_FS4, 8, 

  // bar 9
  REST    , 2, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 8, 
  NOTE_FS4, 2, REST    , 2, 
  REST    ,-4, NOTE_A3 , 8, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 ,-4, 
  NOTE_FS4, 8, NOTE_D4 ,-4, NOTE_E4 , 8, NOTE_A3 ,-4, 

  // bar 13
  REST    , 1, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 8, NOTE_CS4, -4, 
  NOTE_D4 , 8, NOTE_E4 , 4, NOTE_A3 , 8, NOTE_D4 , 8, NOTE_E4 , 8, 
  NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_C4 , 8, REST    , 4, NOTE_A3 , 8, NOTE_AS3, 8, 

  // bar 17
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_C4 , 8, NOTE_C4 , 4, NOTE_C4 , 4, NOTE_A3 , 8, NOTE_AS3, 8,   
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, 
  NOTE_D4 , 8, NOTE_E4 , 8, NOTE_F4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, 

  // bar 21
  NOTE_AS4, 8, NOTE_AS4, 8, NOTE_A4 , 4, NOTE_G4 , 4, NOTE_F4 , 8, NOTE_G4 , 8, 
  NOTE_A4 , 8, NOTE_A4 , 8, NOTE_G4 , 4, NOTE_F4 , 4, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_F4 , 8, NOTE_F4 , 8, NOTE_E4 , 4, NOTE_E4 , 8, NOTE_FS4, 8, NOTE_FS4, 8, 
  REST    , 2, REST    , 2, 

  // bar 25
  REST    , 2, REST    , 2, 
  REST    , 2, REST    , 2, 
  REST    ,-4, NOTE_A3 , 8, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 8, 
  NOTE_FS4, 8, REST    , 2, 

  // bar 29
  REST    , 2, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 ,-4, 
  NOTE_FS4, 8, NOTE_D4 , 4, NOTE_E4 , 4, NOTE_A3 ,-4, 
  REST    , 8, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 4, 

  // bar 33
  NOTE_CS4,  4, NOTE_D4 , 8, NOTE_E4 ,-4, NOTE_A3 , 8, NOTE_A3 , 4, 
  NOTE_FS4, 8, REST    , 4, 
  REST    ,-4, NOTE_A3 , 8, NOTE_B4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 , 8, 
  NOTE_A4 , 4, REST    ,-2,

  // bar 37
  REST    ,-4, NOTE_A3 , 8, NOTE_B4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 ,-4, 
  NOTE_A4 , 8, NOTE_FS4, -4, NOTE_G4 , 8, NOTE_D4 ,-4, 
  REST    ,-2,
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 4, 

  // bar 41
  NOTE_CS4,  4, NOTE_D4 , 8, NOTE_E4 , 4, NOTE_A3 , 8, NOTE_D4 , 8, NOTE_E4 , 8, 
  NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_C4 ,-4, NOTE_A3 , 8, NOTE_AS3, 8, 
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_C4 , 8, NOTE_C4 , 4, NOTE_C4 , 4, NOTE_A3 , 8, NOTE_AS3, 8, 

  // bar 45
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, 
  NOTE_D4 , 8, NOTE_E4 , 8, NOTE_F4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, 
  NOTE_AS4, 8, NOTE_AS4, 8, NOTE_A4 , 4, NOTE_G4 , 4, NOTE_F4 , 8, NOTE_G4 , 8, 
  NOTE_A4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_F4 , 4, NOTE_D4 , 8, NOTE_C4 , 8, 

  // bar 49
  NOTE_D4 , 8, NOTE_F4 , 8, NOTE_F4 , 8, NOTE_E4 , 4, NOTE_E4 , 8, NOTE_FS4, 8, NOTE_FS4, 8, 
  REST    , 2, 
  REST    , 2, REST    , 2, 
  REST    , 2, REST    , 2, 

  // bar 53
  REST    , 2, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 , 4, 
  NOTE_FS4, -4, REST    , 2, 
  REST    ,-4, NOTE_A3 , 8, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_E4 , 8, NOTE_E4 ,-4, 
  NOTE_FS4, 8, NOTE_D4 ,-4, NOTE_E4 , 8, NOTE_A3 , 8, 

  // bar 57
  REST    , 2, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 4, 
  NOTE_CS4,  4, NOTE_D4 , 8, NOTE_E4 , 4, REST    , 8, NOTE_A3 , 8, NOTE_A3 , 4, 
  NOTE_FS4, 8, REST    , 4, 

  // bar 61
  REST    , 4, REST    , 4, NOTE_B4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 , 4, 
  NOTE_A4 , 4, REST    , 8, 
  REST    , 2, NOTE_B4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 ,-4, 
  NOTE_A4 , 8, NOTE_FS4, -4, NOTE_G4 , 8, NOTE_D4 ,-4, 

  // bar 65
  REST    ,-2, 
  NOTE_E4 , 4, NOTE_FS4, 8, NOTE_G4 ,-4, NOTE_E4 , 4, 
  NOTE_CS4,  4, NOTE_D4 , 8, NOTE_E4 , 4, NOTE_A3 , 8, NOTE_D4 , 8, NOTE_E4 , 8, 
  NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_C4 ,-4, NOTE_A3 , 8, NOTE_AS3, 8, 

  // bar 69
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_E4 , 8, NOTE_D4 , 8, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_C4 , 8, NOTE_C4 , 4, NOTE_C4 , 4, NOTE_A3 , 8, NOTE_AS3, 8, 
  NOTE_C4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_E4 , 8, NOTE_D4 , 8, 
  NOTE_D4 , 8, NOTE_E4 , 8, NOTE_F4 , 4, NOTE_F4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, 

  // bar 73
  NOTE_AS4, 8, NOTE_AS4, 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_G4 , 4, NOTE_F4 , 8, NOTE_G4 , 8, 
  NOTE_A4 , 8, NOTE_A4 , 8, NOTE_G4 , 8, NOTE_F4 , 8, NOTE_F4 , 4, NOTE_D4 , 8, NOTE_C4 , 8, 
  NOTE_D4 , 8, NOTE_F4 , 8, NOTE_F4 , 8, NOTE_E4 , 4, NOTE_E4 , 8, NOTE_FS4, 8, NOTE_FS4, 8, 
  REST    , 8, REST    , 8, NOTE_A4 , 8, NOTE_A4 , 8, 

  // bar 77
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_E4 , 8, NOTE_FS4, 8, NOTE_A4 ,-4, 
  REST    , 8, REST    , 4, NOTE_A4 , 8, NOTE_A4 , 8, NOTE_A4 , 8, 
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    , 8,   REST    , 4, NOTE_A4 , 8, NOTE_A4 , 8, NOTE_A4 , 8, 

  // bar 81
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    , 4, REST    , 4, NOTE_A4 , 8, NOTE_A4 , 8, 
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    , 8, REST    , 4, NOTE_A4 , 8, NOTE_A4 , 8, NOTE_A4 , 8, 

  // bar 85
  NOTE_B4 , 8, NOTE_A4 , 8, NOTE_FS4, 8, NOTE_D4 , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    , 8, REST    , 4, NOTE_G4 , 8, NOTE_A4 , 8, NOTE_A4 ,-4, 
  REST    ,-4, NOTE_G4 , 8, NOTE_FS4, 8, NOTE_FS4, 8, 
  REST    , 2
};

static constexpr const int16_t furelise[] = {
  // Fur Elise - Ludwig van Beethovem
  // Score available at https://musescore.com/user/28149610/scores/5281944

  //starts from 1 ending on 9
  NOTE_E5, 16, NOTE_DS5, 16, //1
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,
  NOTE_C5, 8,  REST, 16, NOTE_E4, 16, NOTE_E5, 16,  NOTE_DS5, 16,
  
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,//6
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, 
  NOTE_B4, -8, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16, 
  NOTE_A4 , 4, REST, 8, //9 - 1st ending

  //repaets from 1 ending on 10
  NOTE_E5, 16, NOTE_DS5, 16, //1
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,
  NOTE_C5, 8,  REST, 16, NOTE_E4, 16, NOTE_E5, 16,  NOTE_DS5, 16,
  
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,//6
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, 
  NOTE_B4, -8, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16, 
  NOTE_A4, 8, REST, 16, NOTE_B4, 16, NOTE_C5, 16, NOTE_D5, 16, //10 - 2nd ending
  //continues from 11
  NOTE_E5, -8, NOTE_G4, 16, NOTE_F5, 16, NOTE_E5, 16, 
  NOTE_D5, -8, NOTE_F4, 16, NOTE_E5, 16, NOTE_D5, 16, //12
  
  NOTE_C5, -8, NOTE_E4, 16, NOTE_D5, 16, NOTE_C5, 16, //13
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, REST, 16,
  REST, 16, NOTE_E5, 16, NOTE_E6, 16, REST, 16, REST, 16, NOTE_DS5, 16,
  NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16, //19
  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16,  NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4, 8, REST, 16, NOTE_B4, 16, NOTE_C5, 16, NOTE_D5, 16, //24 (1st ending)
  
  //repeats from 11
  NOTE_E5, -8, NOTE_G4, 16, NOTE_F5, 16, NOTE_E5, 16, 
  NOTE_D5, -8, NOTE_F4, 16, NOTE_E5, 16, NOTE_D5, 16, //12
  
  NOTE_C5, -8, NOTE_E4, 16, NOTE_D5, 16, NOTE_C5, 16, //13
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, REST, 16,
  REST, 16, NOTE_E5, 16, NOTE_E6, 16, REST, 16, REST, 16, NOTE_DS5, 16,
  NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16, //19
  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16,  NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4, 8, REST, 16, NOTE_C5, 16, NOTE_C5, 16, NOTE_C5, 16, //25 - 2nd ending

  //continues from 26
  NOTE_C5 , 4, NOTE_F5, -16, NOTE_E5, 32, //26
  NOTE_E5, 8, NOTE_D5, 8, NOTE_AS5, -16, NOTE_A5, 32,
  NOTE_A5, 16, NOTE_G5, 16, NOTE_F5, 16, NOTE_E5, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_AS4, 8, NOTE_A4, 8, NOTE_A4, 32, NOTE_G4, 32, NOTE_A4, 32, NOTE_B4, 32,
  NOTE_C5 , 4, NOTE_D5, 16, NOTE_DS5, 16,
  NOTE_E5, -8, NOTE_E5, 16, NOTE_F5, 16, NOTE_A4, 16,
  NOTE_C5 , 4,  NOTE_D5, -16, NOTE_B4, 32,
 
  
  NOTE_C5, 32, NOTE_G5, 32, NOTE_G4, 32, NOTE_G5, 32, NOTE_A4, 32, NOTE_G5, 32, NOTE_B4, 32, NOTE_G5, 32, NOTE_C5, 32, NOTE_G5, 32, NOTE_D5, 32, NOTE_G5, 32, //33
  NOTE_E5, 32, NOTE_G5, 32, NOTE_C6, 32, NOTE_B5, 32, NOTE_A5, 32, NOTE_G5, 32, NOTE_F5, 32, NOTE_E5, 32, NOTE_D5, 32, NOTE_G5, 32, NOTE_F5, 32, NOTE_D5, 32,
  NOTE_C5, 32, NOTE_G5, 32, NOTE_G4, 32, NOTE_G5, 32, NOTE_A4, 32, NOTE_G5, 32, NOTE_B4, 32, NOTE_G5, 32, NOTE_C5, 32, NOTE_G5, 32, NOTE_D5, 32, NOTE_G5, 32,

  NOTE_E5, 32, NOTE_G5, 32, NOTE_C6, 32, NOTE_B5, 32, NOTE_A5, 32, NOTE_G5, 32, NOTE_F5, 32, NOTE_E5, 32, NOTE_D5, 32, NOTE_G5, 32, NOTE_F5, 32, NOTE_D5, 32, //36
  NOTE_E5, 32, NOTE_F5, 32, NOTE_E5, 32, NOTE_DS5, 32, NOTE_E5, 32, NOTE_B4, 32, NOTE_E5, 32, NOTE_DS5, 32, NOTE_E5, 32, NOTE_B4, 32, NOTE_E5, 32, NOTE_DS5, 32,
  NOTE_E5, -8, NOTE_B4, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, -8, NOTE_B4, 16, NOTE_E5, 16, REST, 16,

  REST, 16, NOTE_DS5, 16, NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, //40
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,
  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,

  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, //46
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4, 8, REST, 16, NOTE_B4, 16, NOTE_C5, 16, NOTE_D5, 16,
  NOTE_E5, -8, NOTE_G4, 16, NOTE_F5, 16, NOTE_E5, 16,
  NOTE_D5, -8, NOTE_F4, 16, NOTE_E5, 16, NOTE_D5, 16,
  NOTE_C5, -8, NOTE_E4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, REST, 16,
  REST, 16, NOTE_E5, 16, NOTE_E6, 16, REST, 16, REST, 16, NOTE_DS5, 16,

  NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_D5, 16, //54
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,
  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  
  NOTE_A4, 8, REST, 16, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, //60
  NOTE_B4, 8, REST, 16, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4, 8, REST, 16, REST, 16, REST, 8, 
  NOTE_CS5 , -4, 
  NOTE_D5 , 4, NOTE_E5, 16, NOTE_F5, 16,
  NOTE_F5 , 4, NOTE_F5, 8, 
  NOTE_E5 , -4,
  NOTE_D5 , 4, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4 , 4, NOTE_A4, 8,
  NOTE_A4, 8, NOTE_C5, 8, NOTE_B4, 8,
  NOTE_A4 , -4,
  NOTE_CS5 , -4,

  NOTE_D5 , 4, NOTE_E5, 16, NOTE_F5, 16, //72
  NOTE_F5 , 4, NOTE_F5, 8,
  NOTE_F5 , -4,
  NOTE_DS5 , 4, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_AS4 , 4, NOTE_A4, 8,
  NOTE_GS4 , 4, NOTE_G4, 8,
  NOTE_A4 , -4,
  NOTE_B4 , 4, REST, 8,
  NOTE_A3, -32, NOTE_C4, -32, NOTE_E4, -32, NOTE_A4, -32, NOTE_C5, -32, NOTE_E5, -32, NOTE_D5, -32, NOTE_C5, -32, NOTE_B4, -32,

  NOTE_A4, -32, NOTE_C5, -32, NOTE_E5, -32, NOTE_A5, -32, NOTE_C6, -32, NOTE_E6, -32, NOTE_D6, -32, NOTE_C6, -32, NOTE_B5, -32, //80
  NOTE_A4, -32, NOTE_C5, -32, NOTE_E5, -32, NOTE_A5, -32, NOTE_C6, -32, NOTE_E6, -32, NOTE_D6, -32, NOTE_C6, -32, NOTE_B5, -32,
  NOTE_AS5, -32, NOTE_A5, -32, NOTE_GS5, -32, NOTE_G5, -32, NOTE_FS5, -32, NOTE_F5, -32, NOTE_E5, -32, NOTE_DS5, -32, NOTE_D5, -32,

  NOTE_CS5, -32, NOTE_C5, -32, NOTE_B4, -32, NOTE_AS4, -32, NOTE_A4, -32, NOTE_GS4, -32, NOTE_G4, -32, NOTE_FS4, -32, NOTE_F4, -32, //84
  NOTE_E4, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,

  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, NOTE_DS5, 16, //88
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16, 
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16, 
  NOTE_B4, -8, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16, 
  NOTE_A4, -8, REST, -8,
  REST, -8, NOTE_G4, 16, NOTE_F5, 16, NOTE_E5, 16,
  NOTE_D5 , 4, REST, 8,
  REST, -8, NOTE_E4, 16, NOTE_D5, 16, NOTE_C5, 16,
  
  NOTE_B4, -8, NOTE_E4, 16, NOTE_E5, 8, //96
  NOTE_E5, 8, NOTE_E6, -8, NOTE_DS5, 16,
  NOTE_E5, 16, REST, 16, REST, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_DS5, 16,
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_GS4, 16, NOTE_B4, 16,

  NOTE_C5, 8, REST, 16, NOTE_E4, 16, NOTE_E5, 16, NOTE_DS5, 16, //102
  NOTE_E5, 16, NOTE_DS5, 16, NOTE_E5, 16, NOTE_B4, 16, NOTE_D5, 16, NOTE_C5, 16,
  NOTE_A4, -8, NOTE_C4, 16, NOTE_E4, 16, NOTE_A4, 16,
  NOTE_B4, -8, NOTE_E4, 16, NOTE_C5, 16, NOTE_B4, 16,
  NOTE_A4 , -4,
};

static constexpr  const int16_t starwars[] = {
  
  // Star Wars Main Theme 
  
  NOTE_AS4,8, NOTE_AS4,8, NOTE_AS4,8,//1
  NOTE_F5,2, NOTE_C6,2,
  NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F6,2, NOTE_C6,4,  
  NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F6,2, NOTE_C6,4,  
  NOTE_AS5,8, NOTE_A5,8, NOTE_AS5,8, NOTE_G5,2, NOTE_C5,8, NOTE_C5,8, NOTE_C5,8,
  NOTE_F5,2, NOTE_C6,2,
  NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F6,2, NOTE_C6,4,  
  
  NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F6,2, NOTE_C6,4, //8  
  NOTE_AS5,8, NOTE_A5,8, NOTE_AS5,8, NOTE_G5,2, NOTE_C5,-8, NOTE_C5,16, 
  NOTE_D5,-4, NOTE_D5,8, NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F5,8,
  NOTE_F5,8, NOTE_G5,8, NOTE_A5,8, NOTE_G5,4, NOTE_D5,8, NOTE_E5,4,NOTE_C5,-8, NOTE_C5,16,
  NOTE_D5,-4, NOTE_D5,8, NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F5,8,
  
  NOTE_C6,-8, NOTE_G5,16, NOTE_G5,2, REST,8, NOTE_C5,8,//13
  NOTE_D5,-4, NOTE_D5,8, NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F5,8,
  NOTE_F5,8, NOTE_G5,8, NOTE_A5,8, NOTE_G5,4, NOTE_D5,8, NOTE_E5,4,NOTE_C6,-8, NOTE_C6,16,
  NOTE_F6,4, NOTE_DS6,8, NOTE_CS6,4, NOTE_C6,8, NOTE_AS5,4, NOTE_GS5,8, NOTE_G5,4, NOTE_F5,8,
  NOTE_C6,1
  
};


static constexpr const int16_t brahmslullaby[] = {

  // Wiegenlied (Brahms' Lullaby)
  // Score available at https://www.flutetunes.com/tunes.php?id=54

  NOTE_G4, 4, NOTE_G4, 4, //1
  NOTE_AS4, -4, NOTE_G4, 8, NOTE_G4, 4,
  NOTE_AS4, 4, REST, 4, NOTE_G4, 8, NOTE_AS4, 8,
  NOTE_DS5, 4, NOTE_D5, -4, NOTE_C5, 8,
  NOTE_C5, 4, NOTE_AS4, 4, NOTE_F4, 8, NOTE_G4, 8,
  NOTE_GS4, 4, NOTE_F4, 4, NOTE_F4, 8, NOTE_G4, 8,
  NOTE_GS4, 4, REST, 4, NOTE_F4, 8, NOTE_GS4, 8,
  NOTE_D5, 8, NOTE_C5, 8, NOTE_AS4, 4, NOTE_D5, 4,

  NOTE_DS5, 4, REST, 4, NOTE_DS4, 8, NOTE_DS4, 8, //8
  NOTE_DS5, 2, NOTE_C5, 8, NOTE_GS4, 8,
  NOTE_AS4, 2, NOTE_G4, 8, NOTE_DS4, 8,
  NOTE_GS4, 4, NOTE_AS4, 4, NOTE_C5, 4,
  NOTE_AS4, 2, NOTE_DS4, 8, NOTE_DS4, 8,
  NOTE_DS5, 2, NOTE_C5, 8, NOTE_GS4, 8,
  NOTE_AS4, 2, NOTE_G4, 8, NOTE_DS4, 8,
  NOTE_AS4, 4, NOTE_G4, 4, NOTE_DS4, 4,
  NOTE_DS4, 2

};


static constexpr const int16_t imperialmarch[] = {
  
  // Dart Vader theme (Imperial March) - Star wars 
  // Score available at https://musescore.com/user/202909/scores/1141521
  // The tenor saxophone part was used
  
  NOTE_A4,-4, NOTE_A4,-4, NOTE_A4,16, NOTE_A4,16, NOTE_A4,16, NOTE_A4,16, NOTE_F4,8, REST,8,
  NOTE_A4,-4, NOTE_A4,-4, NOTE_A4,16, NOTE_A4,16, NOTE_A4,16, NOTE_A4,16, NOTE_F4,8, REST,8,
  NOTE_A4,4, NOTE_A4,4, NOTE_A4,4, NOTE_F4,-8, NOTE_C5,16,

  NOTE_A4,4, NOTE_F4,-8, NOTE_C5,16, NOTE_A4,2,//4
  NOTE_E5,4, NOTE_E5,4, NOTE_E5,4, NOTE_F5,-8, NOTE_C5,16,
  NOTE_A4,4, NOTE_F4,-8, NOTE_C5,16, NOTE_A4,2,
  
  NOTE_A5,4, NOTE_A4,-8, NOTE_A4,16, NOTE_A5,4, NOTE_GS5,-8, NOTE_G5,16, //7 
  NOTE_DS5,16, NOTE_D5,16, NOTE_DS5,8, REST,8, NOTE_A4,8, NOTE_DS5,4, NOTE_D5,-8, NOTE_CS5,16,

  NOTE_C5,16, NOTE_B4,16, NOTE_C5,16, REST,8, NOTE_F4,8, NOTE_GS4,4, NOTE_F4,-8, NOTE_A4,-16,//9
  NOTE_C5,4, NOTE_A4,-8, NOTE_C5,16, NOTE_E5,2,

  NOTE_A5,4, NOTE_A4,-8, NOTE_A4,16, NOTE_A5,4, NOTE_GS5,-8, NOTE_G5,16, //7 
  NOTE_DS5,16, NOTE_D5,16, NOTE_DS5,8, REST,8, NOTE_A4,8, NOTE_DS5,4, NOTE_D5,-8, NOTE_CS5,16,

  NOTE_C5,16, NOTE_B4,16, NOTE_C5,16, REST,8, NOTE_F4,8, NOTE_GS4,4, NOTE_F4,-8, NOTE_A4,-16,//9
  NOTE_A4,4, NOTE_F4,-8, NOTE_C5,16, NOTE_A4,2,
  
};

static constexpr const int16_t minuet[] = {

  // Minuet in G - Petzold
  // Score available at https://musescore.com/user/3402766/scores/1456391
  NOTE_D5,4, NOTE_G4,8, NOTE_A4,8, NOTE_B4,8, NOTE_C5,8, //1
  NOTE_D5,4, NOTE_G4,4, NOTE_G4,4,
  NOTE_E5,4, NOTE_C5,8, NOTE_D5,8, NOTE_E5,8, NOTE_FS5,8,
  NOTE_G5,4, NOTE_G4,4, NOTE_G4,4,
  NOTE_C5,4, NOTE_D5,8, NOTE_C5,8, NOTE_B4,8, NOTE_A4,8,
  
  NOTE_B4,4, NOTE_C5,8, NOTE_B4,8, NOTE_A4,8, NOTE_G4,8,//6
  NOTE_FS4,4, NOTE_G4,8, NOTE_A4,8, NOTE_B4,8, NOTE_G4,8,
  NOTE_A4,-2,
  NOTE_D5,4, NOTE_G4,8, NOTE_A4,8, NOTE_B4,8, NOTE_C5,8, 
  NOTE_D5,4, NOTE_G4,4, NOTE_G4,4,
  NOTE_E5,4, NOTE_C5,8, NOTE_D5,8, NOTE_E5,8, NOTE_FS5,8,
  
  NOTE_G5,4, NOTE_G4,4, NOTE_G4,4,
  NOTE_C5,4, NOTE_D5,8, NOTE_C5,8, NOTE_B4,8, NOTE_A4,8, //12
  NOTE_B4,4, NOTE_C5,8, NOTE_B4,8, NOTE_A4,8, NOTE_G4,8,
  NOTE_A4,4, NOTE_B4,8, NOTE_A4,8, NOTE_G4,8, NOTE_FS4,8,
  NOTE_G4,-2,

  //repeats from 1

  NOTE_D5,4, NOTE_G4,8, NOTE_A4,8, NOTE_B4,8, NOTE_C5,8, //1
  NOTE_D5,4, NOTE_G4,4, NOTE_G4,4,
  NOTE_E5,4, NOTE_C5,8, NOTE_D5,8, NOTE_E5,8, NOTE_FS5,8,
  NOTE_G5,4, NOTE_G4,4, NOTE_G4,4,
  NOTE_C5,4, NOTE_D5,8, NOTE_C5,8, NOTE_B4,8, NOTE_A4,8,
  
  NOTE_B4,4, NOTE_C5,8, NOTE_B4,8, NOTE_A4,8, NOTE_G4,8,//6
  NOTE_FS4,4, NOTE_G4,8, NOTE_A4,8, NOTE_B4,8, NOTE_G4,8,
  NOTE_A4,-2,
  NOTE_D5,4, NOTE_G4,8, NOTE_A4,8, NOTE_B4,8, NOTE_C5,8, 
  NOTE_D5,4, NOTE_G4,4, NOTE_G4,4,
  NOTE_E5,4, NOTE_C5,8, NOTE_D5,8, NOTE_E5,8, NOTE_FS5,8,
  
  NOTE_G5,4, NOTE_G4,4, NOTE_G4,4,
  NOTE_C5,4, NOTE_D5,8, NOTE_C5,8, NOTE_B4,8, NOTE_A4,8, //12
  NOTE_B4,4, NOTE_C5,8, NOTE_B4,8, NOTE_A4,8, NOTE_G4,8,
  NOTE_A4,4, NOTE_B4,8, NOTE_A4,8, NOTE_G4,8, NOTE_FS4,8,
  NOTE_G4,-2,

  //continues from 17

  NOTE_B5,4, NOTE_G5,8, NOTE_A5,8, NOTE_B5,8, NOTE_G5,8,//17
  NOTE_A5,4, NOTE_D5,8, NOTE_E5,8, NOTE_FS5,8, NOTE_D5,8,
  NOTE_G5,4, NOTE_E5,8, NOTE_FS5,8, NOTE_G5,8, NOTE_D5,8,
  NOTE_CS5,4, NOTE_B4,8, NOTE_CS5,8, NOTE_A4,4,
  NOTE_A4,8, NOTE_B4,8, NOTE_CS5,8, NOTE_D5,8, NOTE_E5,8, NOTE_FS5,8,

  NOTE_G5,4, NOTE_FS5,4, NOTE_E5,4, //22
  NOTE_FS5,4, NOTE_A4,4, NOTE_CS5,4,
  NOTE_D5,-2,
  NOTE_D5,4, NOTE_G4,8, NOTE_FS5,8, NOTE_G4,4,
  NOTE_E5,4,  NOTE_G4,8, NOTE_FS4,8, NOTE_G4,4,
  NOTE_D5,4, NOTE_C5,4, NOTE_B4,4,

  NOTE_A4,8, NOTE_G4,8, NOTE_FS4,8, NOTE_G4,8, NOTE_A4,4, //28
  NOTE_D4,8, NOTE_E4,8, NOTE_FS4,8, NOTE_G4,8, NOTE_A4,8, NOTE_B4,8,
  NOTE_C5,4, NOTE_B4,4, NOTE_A4,4,
  NOTE_B4,8, NOTE_D5,8, NOTE_G4,4, NOTE_FS4,4,
  NOTE_G4,-2,
 
};

static constexpr const int16_t cannon[] = {

  // Cannon in D - Pachelbel
  // Score available at https://musescore.com/user/4710311/scores/1975521
  // C F
  NOTE_FS4,2, NOTE_E4,2,
  NOTE_D4,2, NOTE_CS4,2,
  NOTE_B3,2, NOTE_A3,2,
  NOTE_B3,2, NOTE_CS4,2,
  NOTE_FS4,2, NOTE_E4,2,
  NOTE_D4,2, NOTE_CS4,2,
  NOTE_B3,2, NOTE_A3,2,
  NOTE_B3,2, NOTE_CS4,2,
  NOTE_D4,2, NOTE_CS4,2,
  NOTE_B3,2, NOTE_A3,2,
  NOTE_G3,2, NOTE_FS3,2,
  NOTE_G3,2, NOTE_A3,2,

  NOTE_D4,4, NOTE_FS4,8, NOTE_G4,8, NOTE_A4,4, NOTE_FS4,8, NOTE_G4,8, 
  NOTE_A4,4, NOTE_B3,8, NOTE_CS4,8, NOTE_D4,8, NOTE_E4,8, NOTE_FS4,8, NOTE_G4,8, 
  NOTE_FS4,4, NOTE_D4,8, NOTE_E4,8, NOTE_FS4,4, NOTE_FS3,8, NOTE_G3,8,
  NOTE_A3,8, NOTE_G3,8, NOTE_FS3,8, NOTE_G3,8, NOTE_A3,2,
  NOTE_G3,4, NOTE_B3,8, NOTE_A3,8, NOTE_G3,4, NOTE_FS3,8, NOTE_E3,8, 
  NOTE_FS3,4, NOTE_D3,8, NOTE_E3,8, NOTE_FS3,8, NOTE_G3,8, NOTE_A3,8, NOTE_B3,8,

  NOTE_G3,4, NOTE_B3,8, NOTE_A3,8, NOTE_B3,4, NOTE_CS4,8, NOTE_D4,8,
  NOTE_A3,8, NOTE_B3,8, NOTE_CS4,8, NOTE_D4,8, NOTE_E4,8, NOTE_FS4,8, NOTE_G4,8, NOTE_A4,2,
  NOTE_A4,4, NOTE_FS4,8, NOTE_G4,8, NOTE_A4,4,
  NOTE_FS4,8, NOTE_G4,8, NOTE_A4,8, NOTE_A3,8, NOTE_B3,8, NOTE_CS4,8,
  NOTE_D4,8, NOTE_E4,8, NOTE_FS4,8, NOTE_G4,8, NOTE_FS4,4, NOTE_D4,8, NOTE_E4,8,
  NOTE_FS4,8, NOTE_CS4,8, NOTE_A3,8, NOTE_A3,8,

  NOTE_CS4,4, NOTE_B3,4, NOTE_D4,8, NOTE_CS4,8, NOTE_B3,4,
  NOTE_A3,8, NOTE_G3,8, NOTE_A3,4, NOTE_D3,8, NOTE_E3,8, NOTE_FS3,8, NOTE_G3,8,
  NOTE_A3,8, NOTE_B3,4, NOTE_G3,4, NOTE_B3,8, NOTE_A3,8, NOTE_B3,4,
  NOTE_CS4,8, NOTE_D4,8, NOTE_A3,8, NOTE_B3,8, NOTE_CS4,8, NOTE_D4,8, NOTE_E4,8,
  NOTE_FS4,8, NOTE_G4,8, NOTE_A4,2,  
   
  
};

static constexpr const int16_t odetojoy[] = {

/* 
  Ode to Joy - Beethoven's Symphony No. 9 
  More songs available at https://github.com/robsoncouto/arduino-songs                                            
*/

  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//1
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_E4,-4, NOTE_D4,8,  NOTE_D4,2,

  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//4
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_D4,-4,  NOTE_C4,8,  NOTE_C4,2,

  NOTE_D4,4,  NOTE_D4,4,  NOTE_E4,4,  NOTE_C4,4,//8
  NOTE_D4,4,  NOTE_E4,8,  NOTE_F4,8,  NOTE_E4,4, NOTE_C4,4,
  NOTE_D4,4,  NOTE_E4,8,  NOTE_F4,8,  NOTE_E4,4, NOTE_D4,4,
  NOTE_C4,4,  NOTE_D4,4,  NOTE_G3,2,

  NOTE_E4,4,  NOTE_E4,4,  NOTE_F4,4,  NOTE_G4,4,//12
  NOTE_G4,4,  NOTE_F4,4,  NOTE_E4,4,  NOTE_D4,4,
  NOTE_C4,4,  NOTE_C4,4,  NOTE_D4,4,  NOTE_E4,4,
  NOTE_D4,-4,  NOTE_C4,8,  NOTE_C4,2
  
};

I made many errors hand-scribing the song from sheet music (I do not read music). Then I made many errors transcribing into "pitches" notation. Then I got rests wrong. I did what I could to clean it up, and think it is recognizable, but I can still hear many errors (in this third try). I still can't "read" music. I'm going to work on my "tone sequencer" that will let me write the score on a clef, and have it spit out "pitches" notation. It will imitate my "DIO sequencer"

1 Like

me neither, i do play a mean guitar though..

i was curious if there was software that would do the job..

nice job.. ~q