MessagePanel.ino
1 // SPDX-FileCopyrightText: 2019 Melissa LeBlanc-Williams for Adafruit Industries 2 // 3 // SPDX-License-Identifier: MIT 4 5 // Message Panel 6 // Reads an Adafruit IO Feed, then formats and displays the message 7 // Author: Melissa LeBlanc-Williams 8 9 #include <RGBmatrixPanel.h> 10 #include <SPI.h> 11 12 #define BASE_CHAR_WIDTH 6 // 5 pixels + 1 space 13 #define BASE_CHAR_HEIGHT 8 // 7 pixels + 1 space 14 15 // Most of the signal pins are configurable, but the CLK pin has some 16 // special constraints. On 8-bit AVR boards it must be on PORTB... 17 // Pin 8 works on the Arduino Uno & compatibles (e.g. Adafruit Metro), 18 // Pin 11 works on the Arduino Mega. On 32-bit SAMD boards it must be 19 // on the same PORT as the RGB data pins (D2-D7)... 20 // Pin 8 works on the Adafruit Metro M0 or Arduino Zero, 21 // Pin A4 works on the Adafruit Metro M4 (if using the Adafruit RGB 22 // Matrix Shield, cut trace between CLK pads and run a wire to A4). 23 24 //#define CLK 8 // USE THIS ON ADAFRUIT METRO M0 or adapting to use Airlift, etc. 25 #define CLK A4 // USE THIS ON METRO M4 (not M0) 26 #define OE 9 27 #define LAT 10 28 #define A A0 29 #define B A1 30 #define C A2 31 #define D A3 32 33 RGBmatrixPanel matrix(A, B, C, D, CLK, LAT, OE, false, 64); 34 35 #include "config.h" 36 37 // set up the 'messagepanel' feed 38 AdafruitIO_Feed *counter = io.feed("messagepanel"); 39 40 void drawText(const char *text, bool resetPosition = true, uint16_t color = 0xffff, uint16_t textSize = 1) { 41 matrix.setTextSize(textSize); // size 1 == 8 pixels high 42 if (resetPosition) { 43 matrix.setCursor(0, 0); // start at top left, with 8 pixel of spacing 44 } 45 matrix.setTextColor(color); 46 matrix.print(text); 47 } 48 49 // This function is called whenever a 'messagepanel' message 50 // is received from Adafruit IO. it was attached to 51 // the counter feed in the setup() function above. 52 void handleMessage(AdafruitIO_Data *data) { 53 String message = data->toString(); 54 String plainText = data->toString(); 55 uint16_t color = matrix.Color333(7, 7, 7); 56 uint16_t textSize = 1; 57 uint16_t colorStartIndex = 0, colorEndIndex = 0; 58 uint16_t sizeStartIndex = 0, sizeEndIndex = 0; 59 uint16_t strpos = 0; 60 byte lineLengths[] = {0, 0, 0, 0}; 61 byte lineNum = 0; 62 byte messageHeight = 0; 63 byte lineHeight = 0; 64 // Calculate line lengths 65 boolean paramRead = false; 66 boolean newLine = false; 67 68 matrix.setCursor(0, 0); 69 matrix.fillScreen(matrix.Color333(0, 0, 0)); 70 71 // Strip out all color data first 72 while(strpos < plainText.length()) { 73 colorStartIndex = plainText.indexOf('{'); 74 colorEndIndex = plainText.indexOf('}'); 75 plainText.remove(colorStartIndex, colorEndIndex - colorStartIndex + 1); 76 strpos++; 77 } 78 79 // Calculate the line lengths in pixels for fixed width text 80 strpos = 0; 81 while(strpos < plainText.length()) { 82 sizeStartIndex = plainText.indexOf('<'); 83 sizeEndIndex = plainText.indexOf('>'); 84 85 if (strpos == sizeStartIndex) { 86 textSize = atoi(plainText.substring(sizeStartIndex + 1, sizeEndIndex).c_str()); 87 plainText.remove(sizeStartIndex, sizeEndIndex - sizeStartIndex + 1); 88 } 89 90 if (plainText.charAt(strpos) != '\n') { 91 lineLengths[lineNum] += textSize * BASE_CHAR_WIDTH; 92 if (textSize * BASE_CHAR_HEIGHT > lineHeight) { 93 lineHeight = textSize * BASE_CHAR_HEIGHT; 94 } 95 } 96 97 // We want to keep adding up the characters * textSize until we hit a newline character 98 // or we reach the width of the message panel. Then we go down to the next line 99 if (plainText.charAt(strpos) == '\n' || lineLengths[lineNum] >= matrix.width()) { 100 messageHeight += lineHeight; 101 lineHeight = 0; 102 lineNum++; 103 } 104 105 strpos++; 106 } 107 108 // Add the last line 109 messageHeight += lineHeight; 110 111 textSize = 1; 112 lineNum = 0; 113 for(uint16_t i=0; i<message.length(); i++) { 114 if (message.charAt(i) == '{') { 115 paramRead = true; 116 colorStartIndex = i + 1; 117 } else if (message.charAt(i) == '}') { 118 paramRead = false; 119 int wheelPos = atoi(message.substring(colorStartIndex, i).c_str()); 120 if (wheelPos < 24) { 121 color = Wheel(wheelPos); 122 } else { 123 color = matrix.Color333(7, 7, 7); 124 } 125 } else if (message.charAt(i) == '<') { 126 paramRead = true; 127 sizeStartIndex = i + 1; 128 } else if (message.charAt(i) == '>') { 129 paramRead = false; 130 textSize = atoi(message.substring(sizeStartIndex, i).c_str()); 131 } else { 132 if (paramRead) continue; 133 134 if (matrix.getCursorX() == 0 && matrix.getCursorY() == 0) { 135 matrix.setCursor(floor((matrix.width() / 2) - (lineLengths[lineNum] / 2)), matrix.height() / 2 - messageHeight / 2); 136 } else if (newLine) { 137 matrix.setCursor(floor((matrix.width() / 2) - (lineLengths[++lineNum] / 2)), matrix.getCursorY()); 138 newLine = false; 139 } 140 drawText(message.substring(i, i+1).c_str(), false, color, textSize); 141 if (message.charAt(i) == '\n' || matrix.getCursorX() >= matrix.width()) { 142 newLine = true; 143 } 144 } 145 } 146 } 147 148 void setup() { 149 matrix.begin(); 150 151 // fill the screen with 'black' 152 matrix.fillScreen(matrix.Color333(0, 0, 0)); 153 // draw some text! 154 matrix.setTextWrap(true); 155 drawText("Connecting..."); 156 Serial.begin(115200); 157 io.connect(); 158 159 counter->onMessage(handleMessage); 160 161 while(io.mqttStatus() < AIO_CONNECTED) { 162 drawText("."); 163 delay(500); 164 } 165 166 counter->get(); 167 168 Serial.println(); 169 Serial.println(io.statusText()); 170 } 171 172 void loop() { 173 io.run(); 174 } 175 176 // Input a value 0 to 23 to get a color value. 177 // The colours are a transition r - g - b - back to r. 178 uint16_t Wheel(byte WheelPos) { 179 if(WheelPos < 8) { 180 return matrix.Color333(7 - WheelPos, WheelPos, 0); 181 } else if(WheelPos < 16) { 182 WheelPos -= 8; 183 return matrix.Color333(0, 7 - WheelPos, WheelPos); 184 } else { 185 WheelPos -= 16; 186 return matrix.Color333(WheelPos, 0, 7 - WheelPos); 187 } 188 }