One of the best things about the boards from M5Stack is their API, which handles display, button debounce, and other hardware interfaces. Documents and source code for the ATOM's API can be found here: https://github.com/m5stack/M5Atom
To include the API in a sketch, use the following:
#include "M5Atom.h"
This will define a class called M5Atom, and create an instance of that class called M5.
Here are the most common API methods we'll be using:
To initialize the ATOM, use this:
M5.begin(bool SerialEnable , bool I2CEnable , bool DisplayEnable)
The first argument should be set to true when we want to do serial outputThe second argument enables I2C, so would be set to true when using some Grove sensors
The third argument, when set to true, enables the 5x5 RGB LED display.
We will be using four methods from that API for displaying data:
M5.dis.setBrightness(uint8_t brightness)
M5.dis.drawpix(uint8_t xpos, uint8_t ypos, CRGB Color)
M5.dis.drawpix(uint8_t Number, CRGB Color)
M5.dis.clear()
There are other display-related methods that we'll not be using.
Finally, use this method to poll for button clicks:
M5.update()
Activating a Single Pixel
To introduce the M5Atom API, here is a simple sketch that causes the RGB LED in the top-left corner to blink. Thrilling, no?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /* * SinglePixel.ino * * By: Mike Klepper * Date: 16 May 2020 * * Introduces the M5Atom API */ #include "M5Atom.h" void setup() { M5.begin(true, false, true); delay(20); } void loop() { M5.dis.drawpix(0, 0, 0xffffff); Serial.println(F("Pixel is on")); delay(250); M5.dis.clear(); Serial.println(F("Pixel is off")); delay(250); M5.update(); } |
Line | Comment |
---|---|
10 | Include the M5Atom API, which creates an object called M5 |
14 | Initialize the M5 object, enabling serial output, disabling I2C, and enabling the display |
15 | This delay is not needed, but apps that use the IMU seem to require it |
20 | Set the pixel at (0, 0) to be white - so we know where the origin is! |
23 | Clear the display |
26 | Call update method, which polls for button events (unnecessary for this particular sketch) |
Before uploading this program, choose the proper serial port from the Tools > Port menu, and set the board to be "M5Stick-C".
Something to notice is what is missing from this program:
Serial.begin(115200)
This line is included in the M5.begin method when the first argument is set to true.
Color Order
When you attempt to change the color of the pixel's color in line 20, you'll get unexpected results! For example, if you change the line to read:
M5.dis.drawpix(0, 0, 0xff0000);
instead of getting blinking red, you'll get a green blinking pixel!
As it goes, the color order that these LEDs use isn't the RRGGBB found in HTML, but GGRRBB. To demonstrate this (and to demonstrate the axis orientation), this program will plot a red pixel in the top-right corner, a green pixel at bottom-left, and a blue pixel at the bottom-right corner.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /* * FourPixels.ino * * By: Mike Klepper * Date: 16 May 2020 * * Demonstrates GRB color order for the LEDs as well as axis orientation */ #include "M5Atom.h" void setup() { M5.begin(true, false, true); delay(20); } void loop() { M5.dis.drawpix(0, 0, 0xffffff); // White M5.dis.drawpix(4, 0, 0x00ff00); // Red M5.dis.drawpix(0, 4, 0xff0000); // Green M5.dis.drawpix(4, 4, 0x0000ff); // Blue Serial.println(F("Pixels are on")); delay(250); M5.dis.clear(); Serial.println(F("Pixels are off")); delay(250); M5.update(); } |
So we see that the pixels are numbered 0 to 4, inclusive, along each axis, and that the y-axis points down.
Pixel Order
The programs so far has used the drawpix(x, y, c) method. We will have a need for the other drawpix method in the API, the drawpix(n, c). This method counts from the top-left (n = 0) to the bottom-right (n = 24), but are the pixels in the middle ordered like words in an English paragraph (line-by-line, top-to-bottom) or in a zig-zag manner? Let's find out!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /** * LED-Order.ino * * By: Mike Klepper * Date: 26 April 2020 * * Demonstrates order that pixels are addressed * * See blog post on patriot-geek.blogspot.com * for instructions. */ #include "M5Atom.h" int GRB_COLOR_WHITE = 0xffffff; int GRB_COLOR_BLACK = 0x000000; int delayAmt = 1000; void setup() { M5.begin(true, false, true); delay(50); } void loop() { for(int i = 0; i < 25; i++) { M5.dis.drawpix(i, GRB_COLOR_WHITE); delay(50); } delay(delayAmt); for(int i = 0; i < 25; i++) { M5.dis.drawpix(i, GRB_COLOR_BLACK); delay(50); } delay(delayAmt); M5.update(); } |
Thus the pixel order with the drawpix(n, c) method is the same as the reading direction in English.
Controlling Brightness
Something unexpected is that changing the overall brightness of the display does NOT require a redraw! Here's a demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /* * BrightnessDemo.ino * * By: Mike Klepper * Date: 16 May 2020 * * Demonstrates that changing brightness does NOT require a redraw! */ #include "M5Atom.h" void setup() { M5.begin(true, false, true); delay(20); for(int i = 0; i < 25; i++) { M5.dis.drawpix(i, 0xffffff); } } void loop() { for(int i = 60; i >= 0; i--) { M5.dis.setBrightness(i); delay(50); } for (int i = 0; i < 60; i++) { M5.dis.setBrightness(i); delay(50); } M5.update(); } |
The documents for the ATOM Matrix state that the maximum brightness for the pixels should be 60, hence the limits on the for-loops in lines 25 and 31.
Displaying Single Characters
In future parts of this series, we will frequently need to display single characters or other patterns. This can be accomplished by specifying the desired colors of the pixels in a 1-dimensional array, and looping over that array. The last program in this post displays a Morse code message, thus demonstrating how to display dash and dot characters. As it goes, this program mostly shows how to handle character arrays and strings in C.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | /** * MorseCode.ino * * By: Mike Klepper * Date: 26 April 2020 * * Converts string stored in message into Morse code * * See blog post on patriot-geek.blogspot.com * for instructions. */ #include "M5Atom.h" int GRB_COLOR_WHITE = 0xffffff; int GRB_COLOR_BLACK = 0x000000; int GRB_COLOR_RED = 0x00ff00; int GRB_COLOR_ORANGE = 0xa5ff00; int GRB_COLOR_YELLOW = 0xffff00; int GRB_COLOR_GREEN = 0xff0000; int GRB_COLOR_BLUE = 0x0000ff; int GRB_COLOR_PURPLE = 0x008080; int activeColor = GRB_COLOR_RED; int colorList[] = {GRB_COLOR_BLACK, activeColor}; // Constants used in Farnsworth timing // See https://morsecode.world/international/timing.html for details int scale = 200; int dotDuration = 1 * scale; int dashDuration = 3 * scale; int timeBetweenDotsAndDashes = 1 * scale; int timeBetweenCharacters = 3 * scale; int spaceDuration = 7 * scale; String message = F("Trump 2020 Keep America Great"); int dot[25] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,0, 0,0,1,1,0, 0,0,0,0,0 }; int dash[25] = { 0,0,0,0,0, 0,0,0,0,0, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,0 }; char* letters[] = { ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", // A-I ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", // J-R "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.." // S-Z }; char* digits[] = { "-----", ".----", "..---", "...--", "....-", ".....", // 0-5 "-....", "--...", "---..", "----." // 6-9 }; void setup() { M5.begin(true, false, true); delay(20); } void loop() { displayMessageInMorseCode(message); finalAnimation(); delay(2000); } void displayMessageInMorseCode(String message) { message.toUpperCase(); int msgLength = message.length(); Serial.println(""); for(int i = 0; i < msgLength; i++) { char currentChar = message.charAt(i); String morseCodeForCurrentChar; Serial.print(currentChar); Serial.print(" "); if(isAlpha(currentChar)) { morseCodeForCurrentChar = letters[currentChar - 65]; Serial.print(letters[currentChar - 65]); Serial.println(" - Alpha"); displaySingleMorseCodeCharacter(morseCodeForCurrentChar); } else if(isDigit(currentChar)) { morseCodeForCurrentChar = digits[currentChar - 48]; Serial.print(digits[currentChar - 48]); Serial.println(" - Digit"); displaySingleMorseCodeCharacter(morseCodeForCurrentChar); } else if(isSpace(currentChar)) { Serial.println(" - Space"); delay(spaceDuration); } } } void displaySingleMorseCodeCharacter(String morseCodeCharacter) { int morseCodeLength = morseCodeCharacter.length(); for(int i = 0; i < morseCodeLength; i++) { char currentDotOrDash = morseCodeCharacter.charAt(i); Serial.println(currentDotOrDash); if(currentDotOrDash == '.') { M5.dis.clear(); drawArray(dot, colorList); delay(dotDuration); } else { M5.dis.clear(); drawArray(dash, colorList); delay(dashDuration); } M5.dis.clear(); delay(timeBetweenDotsAndDashes); } delay(timeBetweenCharacters); Serial.println("---------------------"); } void drawArray(int arr[], int colors[]) { for(int i = 0; i < 25; i++) { M5.dis.drawpix(i, colors[arr[i]]); } } void fillDisplay(int fillColor) { for(int i = 0; i < 25; i++) { M5.dis.drawpix(i, fillColor); } } void finalAnimation() { delay(2000); M5.dis.clear(); fillDisplay(GRB_COLOR_RED); delay(2000); fillDisplay(GRB_COLOR_WHITE); delay(2000); fillDisplay(GRB_COLOR_BLUE); delay(2000); M5.dis.clear(); } |
It is easier to understand the code starting from the bottom...
Line | Comment |
---|---|
156 - 162 | Draws an array using the colors specified in the array of colors |
164 - 170 | Fills the display with a single color |
172 - 184 | Animation that is displayed at the end of the Morse code message |
15 - 22 | Handy color constants! |
24 | Color used to display the Morse code message |
26 | Colors used when displaying a character (both off and on colors) |
28 - 35 | Constants that control the Morse code speed |
37 | The message string that will be displayed - MAGA! |
41 - 48 | Array describing the dot character |
50 - 57 | Array for the dash character |
60 - 69 | The Morse code for letters and numbers |
72 - 76 | Usual setup function |
79 - 84 | The loop function - display the message, do the animation, pause for two seconds |
86 - 124 | This function looks-up each character in lines 60 - 69 by ASCII code |
94 | Loop over all characters in the message |
96 | Get current character |
101 | If the character is a letter... |
103 | Look up character in the letters array, offsetting the ASCII code by 65 (the ASCII code for 'A') |
107 | Display the single letter in Morse code |
109 | Else if the character is a digit... | 111 | Look up character in the digits array, offsetting ASCII code by 48 (the ASCII code for '0') |
115 | Display the single digit in Morse code |
117 - 121 | A space is just a pause in Morse code |
125 - 149 | Function for displaying the Morse code equivalent of a single character |
129 | Loop over the dots and dashes |
131 | Get current dot or dash |
134 - 139 | If it is a dot, show the dot character, and delay |
140 - 145 | Same for dash |
151 | Pause between characters |
156 - 162 | Display a character by looping over its array representation |
158 | Loop over the given array |
160 | Look up the color in the colorList and plot it! |
Not very memory efficient code, but there it is.
The drawArray function is quite flexible! As used here, it displays characters using only one color for all pixels (and the other pixels are black). In later posts in this series, we will use that same function to show characters or icons using more colors!
Click here to go to the table of contents for this series.
Great article, well structered and extremly understandable. Highly recommanded to all all who would like to use M5Stack Atom Matrix
ReplyDeleteThanks, glad you found it helpful!
DeleteThanks for sharing this useful content. STONE Tech is a manufacturer of HMI display module Intelligent TFT LCD.
ReplyDeletetft color lcd display