mirror of https://github.com/arendst/Tasmota.git
377 lines
12 KiB
C++
377 lines
12 KiB
C++
/*
|
|
xdsp_19_max7219_matrix.ino.ino - Support for MAX7219 based 8x8 dot matrix displays for Tasmota
|
|
|
|
Copyright (C) 2021 Michael Beuss
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef USE_DISPLAY
|
|
#ifdef USE_DISPLAY_MAX7219_MATRIX
|
|
/*********************************************************************************************\
|
|
This driver enables the display of ascii text on MAX7219 based 8x8 LED dot matrix modules (1088AS).
|
|
|
|
Connect the MAX7219 display module's pins to any free GPIOs of the ESP8266 or ESP32 module.
|
|
VCC should be 5V. Depending on the number of used modules and the brightness, the used current can be more than 500 mA.
|
|
|
|
Connect the 5 outgoing pins (VCC, GND, DI, CS, CLK) of the first module to the next one.
|
|
With this you can connect up to 32 modules.
|
|
To extend the display hieght, multiple rows are supported. Each module row starts from left to right.
|
|
|
|
Assign the pins as follows from Tasmota's GUI:
|
|
|
|
DIN hardware pin --> "MAX7219 DIN"
|
|
CS hardware pin --> "MAX7219 CS"
|
|
CLK hardware pin --> "MAX7219 CLK"
|
|
|
|
|
|
Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts,
|
|
set the Display Model to 19 and Display Mode to 0
|
|
|
|
Depending on order of the wired 8x8 matrix modules you have got a display of size pixel_width x pixel_height.
|
|
The size has to be set with the commands "DisplayWidth <pixel_width>" and "DisplayHeight <pixel_height>"
|
|
|
|
After the ESP8266/ESP32 module restarts again, turn ON the display with the command "Power 1"
|
|
|
|
|
|
Now, the following "Display" commands can be used:
|
|
|
|
DisplayText text
|
|
Sends the text to the display.
|
|
If the text fits into the display, it is shown in the center.
|
|
Otherwise it scrolls to the left and repeats as long it is cleared or new "DisplayText" overwrites it.
|
|
|
|
DisplayDimmer [0..100]
|
|
Sets the intensity of the display.
|
|
|
|
DisplayBlinkrate [0..3]
|
|
0: not blinking
|
|
1: slow, 2: medium 3: fast blinking
|
|
|
|
Power [ON|OFF]
|
|
Sitches the display on or off. When "off", the display buffer is not cleared and will be shown again when after "Power ON".
|
|
Other display commands are still active when off.
|
|
|
|
DisplayClear
|
|
Clears the display
|
|
|
|
DisplayScrollDelay [0..15] // default = 0
|
|
Sets the speed of text scroll. Smaller delay = faster scrolling.
|
|
The maximum scroll speed is 50ms per pixel on DisplayScrollDelay 0.
|
|
|
|
DisplayWidth [8..256]
|
|
Sets the pixel width of the display (8x number of modules in a row)
|
|
|
|
DisplayHeight [8..256]
|
|
Sets the pixel height of the display (8x number of module rows)
|
|
|
|
DisplayRotate [0|2]
|
|
0: normal orientation; devide 0 starts at top left
|
|
2: upside down; device 0 starts at bottom right
|
|
|
|
DisplayClock [0|1|2]
|
|
Displays a clock.
|
|
Commands "DisplayClock 1" // 12 hr format
|
|
"DisplayClock 2" // 24 hr format
|
|
"DisplayClock 0" // turn off clock; please use additional cammand: DisplayMode 0
|
|
|
|
If you would like to use the UTF8 latin1 character set, it cam be added by copile option:
|
|
#define USE_UTF8_LATIN1
|
|
|
|
\*********************************************************************************************/
|
|
|
|
#define XDSP_19 19
|
|
|
|
#include <LedMatrix.h>
|
|
|
|
LedMatrix *max7219_Matrix = nullptr;
|
|
bool max2791Matrix_initDriver_done = false;
|
|
struct
|
|
{
|
|
byte modulesPerRow = 4;
|
|
byte modulesPerCol = 1;
|
|
byte scroll_delay = 0;
|
|
byte scroll_iteration = 0;
|
|
bool show_clock = false;
|
|
bool timeFormat24 = true;
|
|
byte blink_delay = 0; // 0: not blinking
|
|
byte blink_iteration = 0;
|
|
|
|
} LedMatrix_settings;
|
|
|
|
// FUNC_DISPLAY_INIT_DRIVER
|
|
bool MAX7291Matrix_initDriver(void)
|
|
{
|
|
if (!PinUsed(GPIO_MAX7219DIN) || !PinUsed(GPIO_MAX7219CLK) || !PinUsed(GPIO_MAX7219CS))
|
|
{
|
|
AddLog(LOG_LEVEL_INFO, PSTR("DSP: MAX7291Matrix_init GPIO pins missing DIN:%d, CLK:%d, CS:%d"), Pin(GPIO_MAX7219DIN), Pin(GPIO_MAX7219CLK), Pin(GPIO_MAX7219CS) );
|
|
return false; // ensure necessariy pins are configurated
|
|
}
|
|
|
|
Settings->display_model = XDSP_19;
|
|
renderer = nullptr; // renderer not yet used
|
|
if (Settings->display_width) // [pixel]
|
|
{
|
|
LedMatrix_settings.modulesPerRow = (Settings->display_width - 1) / 8 + 1;
|
|
}
|
|
Settings->display_width = 8 * LedMatrix_settings.modulesPerRow;
|
|
Settings->display_cols[0] = LedMatrix_settings.modulesPerRow;
|
|
if (Settings->display_height) // [pixel]
|
|
{
|
|
LedMatrix_settings.modulesPerCol = (Settings->display_height - 1) / 8 + 1;
|
|
}
|
|
Settings->display_height = 8 * LedMatrix_settings.modulesPerCol;
|
|
Settings->display_rows = LedMatrix_settings.modulesPerCol;
|
|
Settings->display_cols[1] = LedMatrix_settings.modulesPerCol;
|
|
max7219_Matrix = new LedMatrix(Pin(GPIO_MAX7219DIN), Pin(GPIO_MAX7219CLK), Pin(GPIO_MAX7219CS), LedMatrix_settings.modulesPerRow, LedMatrix_settings.modulesPerCol);
|
|
max2791Matrix_initDriver_done = true;
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("MTX: MAX7291Matrix_initDriver DIN:%d CLK:%d CS:%d size(%dx%d)"), Pin(GPIO_MAX7219DIN), Pin(GPIO_MAX7219CLK), Pin(GPIO_MAX7219CS), LedMatrix_settings.modulesPerRow, LedMatrix_settings.modulesPerCol);
|
|
return MAX7291Matrix_init();
|
|
}
|
|
|
|
// FUNC_DISPLAY_INIT
|
|
bool MAX7291Matrix_init(void)
|
|
{
|
|
Settings->display_mode = 0; // text mode
|
|
LedMatrix_settings.show_clock = 0; // no
|
|
LedMatrix_settings.blink_delay = 0; // no blinking
|
|
|
|
int intensity = GetDisplayDimmer16(); // 0..15
|
|
max7219_Matrix->setIntensity(intensity);
|
|
|
|
max7219_Matrix->power(true); // power on
|
|
if(Settings->display_rotate <= 3)
|
|
{
|
|
max7219_Matrix->setOrientation((LedMatrix::ModuleOrientation)Settings->display_rotate );
|
|
}
|
|
else
|
|
{
|
|
// default for most 32x8 modules
|
|
Settings->display_rotate = LedMatrix::ORIENTATION_UPSIDE_DOWN;
|
|
max7219_Matrix->setOrientation( LedMatrix::ORIENTATION_UPSIDE_DOWN );
|
|
}
|
|
AddLog(LOG_LEVEL_INFO, PSTR("MTX: MAX7291Matrix_init orientation: %d, intensity: %d"), Settings->display_rotate, intensity);
|
|
return true;
|
|
}
|
|
|
|
bool MAX7291Matrix_setText(bool clearBefore=true)
|
|
{
|
|
if(Settings->display_mode != 0) MAX7291Matrix_init();
|
|
LedMatrix_settings.blink_delay = 0; // no blinking
|
|
return max7219_Matrix->drawText(XdrvMailbox.data, clearBefore);
|
|
}
|
|
|
|
// FUNC_DISPLAY_SCROLLDELAY
|
|
bool MAX7291Matrix_scrollDelay(void)
|
|
{
|
|
if (ArgC() == 0)
|
|
{
|
|
XdrvMailbox.payload = LedMatrix_settings.scroll_delay;
|
|
return true;
|
|
}
|
|
if (LedMatrix_settings.scroll_delay < 0)
|
|
LedMatrix_settings.scroll_delay = 0;
|
|
LedMatrix_settings.scroll_delay = XdrvMailbox.payload;
|
|
return true;
|
|
}
|
|
|
|
// FUNC_DISPLAY_EVERY_50_MSECOND
|
|
bool MAX7291Matrix_scrollText(void)
|
|
{
|
|
// This function is called every 50 ms.
|
|
// scroll_delay defines the number of cycles to be ignored until the display scrolls by one pixel to the left.
|
|
// e.g. scrall_delay = 4 causes a scroll each 200 ms.
|
|
|
|
if(!max7219_Matrix->isPowerOn()) return false; // do not scroll on power off
|
|
LedMatrix_settings.scroll_iteration++;
|
|
if (LedMatrix_settings.scroll_delay)
|
|
LedMatrix_settings.scroll_iteration = LedMatrix_settings.scroll_iteration % LedMatrix_settings.scroll_delay;
|
|
else
|
|
LedMatrix_settings.scroll_iteration = 0;
|
|
if (LedMatrix_settings.scroll_iteration)
|
|
return false;
|
|
|
|
return max7219_Matrix->scrollText();
|
|
}
|
|
|
|
bool MAX7291Matrix_blink(void)
|
|
{
|
|
// This function is called every 50 ms.
|
|
// blink_delay defines the number of cycles to be ignored until the blinkstate changes.
|
|
if(LedMatrix_settings.blink_delay == 0) return false;
|
|
|
|
LedMatrix_settings.blink_iteration++;
|
|
if(LedMatrix_settings.blink_iteration == LedMatrix_settings.blink_delay)
|
|
{
|
|
max7219_Matrix->power(false);
|
|
}
|
|
else if(LedMatrix_settings.blink_iteration == 2* LedMatrix_settings.blink_delay )
|
|
{
|
|
LedMatrix_settings.blink_iteration = 0;
|
|
max7219_Matrix->power(true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MAX7291Matrix_setBlinkRate()
|
|
{
|
|
LedMatrix_settings.blink_iteration = 0;
|
|
max7219_Matrix->power(true);
|
|
if (ArgC() == 0)
|
|
{
|
|
XdrvMailbox.payload = 0;
|
|
}
|
|
if (XdrvMailbox.payload)
|
|
LedMatrix_settings.blink_delay = 20 / XdrvMailbox.payload; // 1: once per second; 2: twice per second; 3: three times per second
|
|
else
|
|
LedMatrix_settings.blink_delay = 0; // do not blink
|
|
return true;
|
|
}
|
|
|
|
#ifdef USE_DISPLAY_MODES1TO5
|
|
// FUNC_DISPLAY_CLOCK
|
|
bool MAX7291Matrix_clock(void)
|
|
{
|
|
LedMatrix_settings.show_clock = XdrvMailbox.payload;
|
|
if (ArgC() == 0)
|
|
XdrvMailbox.payload = 2;
|
|
switch(XdrvMailbox.payload)
|
|
{
|
|
case 0:
|
|
// no clock, switch to text mode
|
|
Settings->display_mode = 0;
|
|
return true;
|
|
case 1:
|
|
// 12 h clock
|
|
LedMatrix_settings.timeFormat24 = false;
|
|
Settings->display_mode = 1;
|
|
break;
|
|
case 2:
|
|
// 24 h clock
|
|
LedMatrix_settings.timeFormat24 = true;
|
|
Settings->display_mode = 1;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("MTX: LedMatrix_settings.show_clock %d, 24h: %b"), LedMatrix_settings.show_clock, LedMatrix_settings.timeFormat24);
|
|
|
|
max7219_Matrix->clearDisplay();
|
|
MAX7291Matrix_showTime();
|
|
return true;
|
|
}
|
|
|
|
// FUNC_DISPLAY_EVERY_SECOND
|
|
bool MAX7291Matrix_showTime()
|
|
{
|
|
if(!LedMatrix_settings.show_clock) return false;
|
|
|
|
uint8_t hr = RtcTime.hour;
|
|
uint8_t mn = RtcTime.minute;
|
|
uint8_t sc = RtcTime.second;
|
|
char timeStr[10];
|
|
if(!LedMatrix_settings.timeFormat24)
|
|
{
|
|
if(hr == 0) hr = 12;
|
|
if(hr > 12 ) hr -= 12;
|
|
}
|
|
|
|
if(LedMatrix_settings.modulesPerRow >= 6)
|
|
{
|
|
snprintf(timeStr, 10, "%02d:%02d:%02d", hr , mn, sc);
|
|
}
|
|
else
|
|
{
|
|
snprintf(timeStr, 10, "%02d:%02d", hr , mn);
|
|
}
|
|
max7219_Matrix->drawText(timeStr, false); // false: do not clear desplay on update to prevent flicker
|
|
return true;
|
|
}
|
|
|
|
#endif // USE_DISPLAY_MODES1TO5
|
|
|
|
|
|
bool Xdsp19(uint32_t function)
|
|
{
|
|
bool result = false;
|
|
|
|
if (FUNC_DISPLAY_INIT_DRIVER == function && !max2791Matrix_initDriver_done )
|
|
{
|
|
result = MAX7291Matrix_initDriver();
|
|
}
|
|
else if (max2791Matrix_initDriver_done && max7219_Matrix && (XDSP_19 == Settings->display_model))
|
|
{
|
|
switch (function)
|
|
{
|
|
case FUNC_DISPLAY_INIT:
|
|
result = MAX7291Matrix_init();
|
|
break;
|
|
case FUNC_DISPLAY_POWER:
|
|
max7219_Matrix->power(disp_power!=0);
|
|
break;
|
|
case FUNC_DISPLAY_MODEL:
|
|
result = true;
|
|
break;
|
|
case FUNC_DISPLAY_CLEAR:
|
|
result = max7219_Matrix->clearDisplay();
|
|
break;
|
|
case FUNC_DISPLAY_DIM:
|
|
result = max7219_Matrix->setIntensity(GetDisplayDimmer16());
|
|
break;
|
|
case FUNC_DISPLAY_DRAW_STRING:
|
|
case FUNC_DISPLAY_SCROLLTEXT:
|
|
case FUNC_DISPLAY_SEVENSEG_TEXT:
|
|
result = MAX7291Matrix_setText(true); // true: clears display before drawing text
|
|
break;
|
|
case FUNC_DISPLAY_SEVENSEG_TEXTNC:
|
|
result = MAX7291Matrix_setText(false); // false: does not clear display before drawing text
|
|
break;
|
|
case FUNC_DISPLAY_SCROLLDELAY:
|
|
result = MAX7291Matrix_scrollDelay();
|
|
break;
|
|
case FUNC_DISPLAY_BLINKRATE:
|
|
{
|
|
result = MAX7291Matrix_setBlinkRate();
|
|
break;
|
|
}
|
|
case FUNC_DISPLAY_EVERY_50_MSECOND:
|
|
MAX7291Matrix_blink();
|
|
result = MAX7291Matrix_scrollText();
|
|
break;
|
|
|
|
#ifdef USE_DISPLAY_MODES1TO5
|
|
case FUNC_DISPLAY_CLOCK:
|
|
result = MAX7291Matrix_clock();
|
|
break;
|
|
case FUNC_DISPLAY_EVERY_SECOND:
|
|
if (LedMatrix_settings.show_clock)
|
|
{
|
|
result = MAX7291Matrix_showTime();
|
|
}
|
|
break;
|
|
#endif // USE_DISPLAY_MODES1TO5
|
|
|
|
default:
|
|
result = false;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif // USE_DISPLAY_MAX7219_MATRIX
|
|
#endif // USE_DISPLAY
|