Guest User

8x8 LED Gear Shift Indicator

a guest
Mar 5th, 2021
19
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* necessary libraries, ensure they are
  2.   installed in Arduino IDE before uploading */
  3. #include <MD_Parola.h>
  4. #include <MD_MAX72xx.h>
  5. #include <SPI.h>
  6. #include <CircularBuffer.h>
  7.  
  8. #define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW                                       // change if using PAROLA_HW or other LED hardware, incorrect setting can cause orientation issues
  9.  
  10. /* settings: */
  11. const byte    MAX_DEVICES                   = 1;                                   // number of LED modules, should always be one in this setup - unsure if more would work with no issues
  12. const byte    CLK_PIN                       = 13;                                  // LED's CLK pin
  13. const byte    DATA_PIN                      = 11;                                  // LED's DATA pin
  14. const byte    CS_PIN                        = 10;                                  // LED's CS pin
  15. const byte    NUM_GEARS                     = 7;                                   // how many gears are used - must match the number of gear characters in GearChars array
  16. const uint8_t BUFFER_SIZE                   = 4;                                   // set number of stored previous gear states - 4 used here used to detect 'sequence' such as 1-2-1-2, which can then be acted upon
  17. const char    GearChars[NUM_GEARS]          = {'P', 'R', 'N', 'D', '3', '2', '1'}; // layout here from left to right should match gear order on vehicle from top to bottom/start to finish
  18. const uint8_t Hall[]                        = {      3,   4,   5,   6,   7,   8 }; // sensor pins in the same order as GearChars array - 'Park' position is assumed to not have a sensor and so first pin is "R"
  19. const char    StartupText[]                 = {"Startup Text"};                    // text scrolled upon boot
  20. const char    ANIM_SEQUENCE[BUFFER_SIZE]    = {'D', 'N', 'D', 'N'};                // sequence of gears necessary to activate animation (i.e. D-N-D-N), must be same length as BUFFER_SIZE
  21. //const char    DEBUG_SEQUENCE                = {'1', '2', '1', '2'};                // sequence of gears necessary to activate DEBUG mode (i.e. 1-2-1-2), must be same length as BUFFER_SIZE
  22. const uint8_t SCROLL_SPEED                  = 75;                                  // speed that StartupText is scrolled, number is milliseconds between frame updates
  23. const byte    BRIGHTNESS                    = 4;                                   // set brightness of LEDs (0-15), can replace with potentiometer-derived value
  24. const uint8_t DEBUG_MODE                    = 0;                                   // set to 1 to enable debugging via Serial (baud)
  25. const uint8_t DEBUG_READS                   = BUFFER_SIZE;                         // number of readings in debug mode, recommended to match buffer size or larger
  26. const int     DEBUG_DELAY                   = 3000;                                // delay between debug readings in milliseconds (3 seconds by default)
  27. const int     BAUD_SPEED                    = 9600;                                // set baud rate here for Serial communication
  28. const int8_t  NUM_LOOPS                     = NUM_GEARS - 1;                       // used for loop counting etc when starting with 0
  29.  
  30. MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);    // creates display instance using given settings
  31.  
  32. /* Sprite definitions - stored in RAM/PROGMEM to save memory */
  33. const uint8_t F_PMAN = 6;
  34. const uint8_t W_PMAN = 8;
  35. const uint8_t PROGMEM pacman[F_PMAN * W_PMAN] =                                    // gobbling pacman animation
  36. {
  37.   0x00, 0x81, 0xc3, 0xe7, 0xff, 0x7e, 0x7e, 0x3c,
  38.   0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c,
  39.   0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  40.   0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  41.   0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  42.   0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c,
  43. };
  44.  
  45. const uint8_t F_PMANGHOST = 6;
  46. const uint8_t W_PMANGHOST = 18;
  47. static const uint8_t PROGMEM pacmanghost[F_PMANGHOST * W_PMANGHOST] =              // ghost pursued by pacman
  48. {
  49.   0x00, 0x81, 0xc3, 0xe7, 0xff, 0x7e, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  50.   0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  51.   0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  52.   0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  53.   0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  54.   0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  55. };
  56.  
  57. const uint8_t F_SAILBOAT = 1;
  58. const uint8_t W_SAILBOAT = 11;
  59. const uint8_t PROGMEM sailboat[F_SAILBOAT * W_SAILBOAT] =                          // sail boat
  60. {
  61.   0x10, 0x30, 0x58, 0x94, 0x92, 0x9f, 0x92, 0x94, 0x98, 0x50, 0x30,
  62. };
  63.  
  64. const uint8_t F_STEAMBOAT = 2;
  65. const uint8_t W_STEAMBOAT = 11;
  66. const uint8_t PROGMEM steamboat[F_STEAMBOAT * W_STEAMBOAT] =                       // steam boat
  67. {
  68.   0x10, 0x30, 0x50, 0x9c, 0x9e, 0x90, 0x91, 0x9c, 0x9d, 0x90, 0x71,
  69.   0x10, 0x30, 0x50, 0x9c, 0x9c, 0x91, 0x90, 0x9d, 0x9e, 0x91, 0x70,
  70. };
  71.  
  72. const uint8_t F_HEART = 5;
  73. const uint8_t W_HEART = 9;
  74. const uint8_t PROGMEM beatingheart[F_HEART * W_HEART] =                            // beating heart
  75. {
  76.   0x0e, 0x11, 0x21, 0x42, 0x84, 0x42, 0x21, 0x11, 0x0e,
  77.   0x0e, 0x1f, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x1f, 0x0e,
  78.   0x0e, 0x1f, 0x3f, 0x7e, 0xfc, 0x7e, 0x3f, 0x1f, 0x0e,
  79.   0x0e, 0x1f, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x1f, 0x0e,
  80.   0x0e, 0x11, 0x21, 0x42, 0x84, 0x42, 0x21, 0x11, 0x0e,
  81. };
  82.  
  83. const uint8_t F_INVADER = 2;
  84. const uint8_t W_INVADER = 10;
  85. const uint8_t PROGMEM spaceinvader[F_INVADER * W_INVADER] =                        // space invader
  86. {
  87.   0x0e, 0x98, 0x7d, 0x36, 0x3c, 0x3c, 0x36, 0x7d, 0x98, 0x0e,
  88.   0x70, 0x18, 0x7d, 0xb6, 0x3c, 0x3c, 0xb6, 0x7d, 0x18, 0x70,
  89. };
  90.  
  91. const uint8_t F_FIRE = 2;
  92. const uint8_t W_FIRE = 11;
  93. const uint8_t PROGMEM fire[F_FIRE * W_FIRE] =                                      // fire
  94. {
  95.   0x7e, 0xab, 0x54, 0x28, 0x52, 0x24, 0x40, 0x18, 0x04, 0x10, 0x08,
  96.   0x7e, 0xd5, 0x2a, 0x14, 0x24, 0x0a, 0x30, 0x04, 0x28, 0x08, 0x10,
  97. };
  98.  
  99. const uint8_t F_WALKER = 5;
  100. const uint8_t W_WALKER = 7;
  101. const uint8_t PROGMEM walker[F_WALKER * W_WALKER] =                                // walking stick figure
  102. {
  103.   0x00, 0x48, 0x77, 0x1f, 0x1c, 0x94, 0x68,
  104.   0x00, 0x90, 0xee, 0x3e, 0x38, 0x28, 0xd0,
  105.   0x00, 0x00, 0xae, 0xfe, 0x38, 0x28, 0x40,
  106.   0x00, 0x00, 0x2e, 0xbe, 0xf8, 0x00, 0x00,
  107.   0x00, 0x10, 0x6e, 0x3e, 0xb8, 0xe8, 0x00,
  108. };
  109.  
  110. /* Struct used for sprite settings. */
  111. struct
  112. {
  113.   const uint8_t *data;
  114.   uint8_t width;
  115.   uint8_t frames;
  116. }
  117. sprite[] =
  118. {
  119.   { fire, W_FIRE, F_FIRE },
  120.   { pacman, W_PMAN, F_PMAN },
  121.   { walker, W_WALKER, F_WALKER },
  122.   { beatingheart, W_HEART, F_HEART },
  123.   { sailboat, W_SAILBOAT, F_SAILBOAT },
  124.   { spaceinvader, W_INVADER, F_INVADER },
  125.   { steamboat, W_STEAMBOAT, F_STEAMBOAT },
  126.   { pacmanghost, W_PMANGHOST, F_PMANGHOST }
  127. };
  128.  
  129. /* variables will change: */
  130. int8_t currentGear;
  131. CircularBuffer<byte, BUFFER_SIZE> previousGears;                                   // a buffer to store previous gear positions
  132.  
  133. void setup() {
  134.   hallSetup();                                                                     // initialise sensors
  135.   displaySetup();                                                                  // initialise display
  136.   currentGear = 0;                                                                 // set current gear to 'Parked' position until first sensor read to establish known state
  137.   previousGears.push(currentGear);                                                 // push 'Park' {"P"} position to buffer also, which is translated to *char via GearChars[0]
  138.   if (DEBUG_MODE == 1) {                                                           // check if DEBUG_MODE is enabled, and runs debugFunction() if TRUE
  139.     Serial.begin(BAUD_SPEED);
  140.     debugFunction();
  141.   }
  142. }
  143.  
  144. void loop() {
  145.   currentGear = getGear();                                                         // read hall effect sensors and calculate current gear position
  146.   displayGear(currentGear);                                                        // display the current gear, with appropriate animation if different from previous gear
  147.   checkHistory();                                                                  // checks gear history for defined sequences and calls relevant functions
  148. }
  149.  
  150. /* writes 4 (or DEBUG_READS) lots of readings from
  151.   all sensors to Serial, with a delay to allow changing
  152.   gear, and fills & prints buffer, for debugging purposes */
  153. void debugFunction() {
  154.   String buf = "";
  155.   String bufChars = "";
  156.   for (int8_t i = 0; i < DEBUG_READS; i++) {
  157.     delay(DEBUG_DELAY);                                                            // wait to allow gear changing/hall sensor/magnet position changes
  158.     for (int8_t x = 0; x < NUM_LOOPS; x++) {                                       // loop through all sensors and print values to Serial
  159.       Serial.println(
  160.         String(x + 1) +
  161.         "| Digital: " +
  162.         String(digitalRead(Hall[x])) +
  163.         " Analogue: " +
  164.         String(analogRead(Hall[x]))
  165.       );
  166.     }
  167.     previousGears.push(GearChars[random(NUM_LOOPS)]);                              // push pseudorandom GearChar values to buffer
  168.     buf = buf + previousGears.last();                                              // add current gear in numeric form to a string for printing to Serial
  169.     bufChars = bufChars + GearChars[previousGears.last()];                         // add current gear in char form to a string for printing to Serial
  170.   }
  171.   Serial.println("Buffer contents: " + buf);                                       // ...print buffer contents, to Serial...
  172.   while (true);                                                                    // puts arduino into an infinite loop, reboot to start again
  173. }
  174.  
  175. /* initialize the hall effect sensor pins as inputs: */
  176. void hallSetup() {
  177.   for (int8_t i = 0; i < NUM_LOOPS; i++) {
  178.     pinMode(Hall[i], INPUT);
  179.   }
  180. }
  181.  
  182. /* setup LED display */
  183. void displaySetup() {
  184.   P.begin();                                                                       // initialise display
  185.   P.setIntensity(BRIGHTNESS);                                                      // set display intensity/brightness
  186.   P.displayClear();
  187.   P.displayScroll(StartupText, PA_LEFT, PA_SCROLL_LEFT, SCROLL_SPEED);             // display message on startup
  188.   while (!P.displayAnimate())                                                      // play animation once until complete
  189.     ;
  190.   P.displayReset();
  191.   P.displayClear();
  192. }
  193.  
  194. /* loop through sensors until LOW reading detected */
  195. int8_t getGear() {
  196.   int8_t gear = NUM_LOOPS;
  197.   while ((gear) && (digitalRead(Hall[gear - 1]))) {
  198.     gear--;
  199.   }
  200.   return gear;
  201. }
  202.  
  203. /* displays current gear on LED, checks if animations
  204.   should be used depending on previous gear value */
  205. void displayGear(int8_t gearValue) {
  206.   char curGearChar[2] = {GearChars[gearValue]};                                    // convert gearValue to c-string character for display purposes by pulling from null terminated array using pointers
  207.   if (gearValue == previousGears.last()) {                                         // if current gear is same as previous, simply print
  208.     P.displayText(curGearChar, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);           // set display settings
  209.     P.displayAnimate();                                                            // display appropriate character
  210.   }
  211.   else if ((previousGears.last() < gearValue)) {                                   // if the previous gear is situated to the left of current gear (in char array) then scroll down
  212.     P.displayText(
  213.       curGearChar, PA_CENTER, SCROLL_SPEED, 1, PA_SCROLL_DOWN, PA_NO_EFFECT        // set scrolling text settings
  214.     );
  215.     while (!P.displayAnimate())                                                    // play once animation until complete
  216.       ;
  217.     previousGears.push(gearValue);                                                 // push current gear to buffer as it is different
  218.   } else {                                                                         // if the previous gear is not situated left (i.e. is to the right of current gear in char array) then scroll up
  219.     P.displayText(
  220.       curGearChar, PA_CENTER, SCROLL_SPEED, 1, PA_SCROLL_UP, PA_NO_EFFECT
  221.     );
  222.     while (!P.displayAnimate())
  223.       ;
  224.     previousGears.push(gearValue);                                                 // push current gear to buffer as it is different
  225.   }
  226. }
  227.  
  228. /* WIP */
  229.  
  230. /* checks for given sequence of gear changes and calls other functions as required */
  231. void checkHistory() {
  232.   if (previousGears.isFull()) {
  233.     char gearHistory[BUFFER_SIZE];                                                 // create new char array from history for comparison
  234.     for (int8_t i = 0; i < BUFFER_SIZE; i++) {                                     // loop to populate array with char equivalents
  235.       gearHistory[i] = GearChars[previousGears[i]];
  236.     }
  237.     if (checkArrays(gearHistory, ANIM_SEQUENCE, BUFFER_SIZE) == true) {            // compares the two arrays; if buffer history matches ANIM_SEQUENCE, then display animation
  238.       displayAnimation(random(ARRAY_SIZE(sprite) - 1));                            // selects and displays random animation from struct array
  239.     }
  240.   }
  241. }
  242.  
  243. /* compares 2 char arrays and returns boolean result */
  244. boolean checkArrays(char arrayA[], char arrayB[], long numItems) {
  245.   boolean match = true;
  246.   long i = 0;
  247.   while (i < numItems && match) {
  248.     match = (arrayA[i] == arrayB[i]);
  249.     i++;
  250.   }
  251.   return match;
  252. }
  253.  
  254. /* displays selected animation */
  255. void displayAnimation(byte selection) {
  256.   char curGearChar[2] = {GearChars[previousGears.last()]};
  257.   P.displayReset();
  258.   P.displayClear();
  259.   P.setSpriteData(
  260.     sprite[selection].data, sprite[selection].width, sprite[selection].frames,   // entry sprite
  261.     sprite[selection].data, sprite[selection].width, sprite[selection].frames    // exit sprite
  262.   );
  263.   P.displayText(
  264.     curGearChar, PA_CENTER, SCROLL_SPEED, 1, PA_SPRITE, PA_SPRITE                  // set animation settings
  265.   );
  266.   while (!P.displayAnimate())                                                      // play animation once until complete
  267.     ;
  268. }
RAW Paste Data