diff --git a/lib/lib_display/LedControl/LICENSE b/lib/lib_display/LedControl/LICENSE new file mode 100644 index 000000000..8d59812aa --- /dev/null +++ b/lib/lib_display/LedControl/LICENSE @@ -0,0 +1,23 @@ +LedControl.h - A library for controling Leds with a MAX7219/MAX7221 +Copyright (c) 2007-2015 Eberhard Fahle + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +This permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/lib_display/LedControl/README.md b/lib/lib_display/LedControl/README.md new file mode 100644 index 000000000..8c832d654 --- /dev/null +++ b/lib/lib_display/LedControl/README.md @@ -0,0 +1,25 @@ +LedControl +========== +LedControl is an [Arduino](http://arduino.cc) library for MAX7219 and MAX7221 Led display drivers. +The code also works with the [Teensy (3.1)](https://www.pjrc.com/teensy/) + +Documentation +------------- +Documentation for the library is on the [Github Project Pages](http://wayoda.github.io/LedControl/) + +Download +-------- +The lastest binary version of the Library is always available from the +[LedControl Release Page](https://github.com/wayoda/LedControl/releases) + + +Install +------- +The library can be installed using the [standard Arduino library install procedure](http://arduino.cc/en/Guide/Libraries#.UwxndHX5PtY) + + + + + + + diff --git a/lib/lib_display/LedControl/keywords.txt b/lib/lib_display/LedControl/keywords.txt new file mode 100644 index 000000000..9d2a94f1f --- /dev/null +++ b/lib/lib_display/LedControl/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring Map For LedControl +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LedControl KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +shutdown KEYWORD2 +setScanLimit KEYWORD2 +setIntensity KEYWORD2 +clearDisplay KEYWORD2 +setLed KEYWORD2 +setRow KEYWORD2 +setColumn KEYWORD2 +setDigit KEYWORD2 +setChar KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/lib/lib_display/LedControl/library.properties b/lib/lib_display/LedControl/library.properties new file mode 100644 index 000000000..66baced18 --- /dev/null +++ b/lib/lib_display/LedControl/library.properties @@ -0,0 +1,10 @@ +name=LedControl +version=1.0.6 +author=Eberhard Fahle +maintainer=Eberhard Fahle +sentence=A library for the MAX7219 and the MAX7221 Led display drivers. +paragraph=The library supports multiple daisychained drivers and supports Led-Matrix displays as well as 7-Segment displays. +category=Display +url=http://wayoda.github.io/LedControl/ +architectures=* + diff --git a/lib/lib_display/LedControl/src/LedControl.cpp b/lib/lib_display/LedControl/src/LedControl.cpp new file mode 100644 index 000000000..e43211fd8 --- /dev/null +++ b/lib/lib_display/LedControl/src/LedControl.cpp @@ -0,0 +1,211 @@ +/* + * LedControl.cpp - A library for controling Leds with a MAX7219/MAX7221 + * Copyright (c) 2007 Eberhard Fahle + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * This permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "LedControl.h" + +//the opcodes for the MAX7221 and MAX7219 +#define OP_NOOP 0 +#define OP_DIGIT0 1 +#define OP_DIGIT1 2 +#define OP_DIGIT2 3 +#define OP_DIGIT3 4 +#define OP_DIGIT4 5 +#define OP_DIGIT5 6 +#define OP_DIGIT6 7 +#define OP_DIGIT7 8 +#define OP_DECODEMODE 9 +#define OP_INTENSITY 10 +#define OP_SCANLIMIT 11 +#define OP_SHUTDOWN 12 +#define OP_DISPLAYTEST 15 + +LedControl::LedControl(int dataPin, int clkPin, int csPin, int numDevices) { + SPI_MOSI=dataPin; + SPI_CLK=clkPin; + SPI_CS=csPin; + if(numDevices<=0 || numDevices>8 ) + numDevices=8; + maxDevices=numDevices; + pinMode(SPI_MOSI,OUTPUT); + pinMode(SPI_CLK,OUTPUT); + pinMode(SPI_CS,OUTPUT); + digitalWrite(SPI_CS,HIGH); + SPI_MOSI=dataPin; + for(int i=0;i<64;i++) + status[i]=0x00; + for(int i=0;i=maxDevices) + return; + if(b) + spiTransfer(addr, OP_SHUTDOWN,0); + else + spiTransfer(addr, OP_SHUTDOWN,1); +} + +void LedControl::setScanLimit(int addr, int limit) { + if(addr<0 || addr>=maxDevices) + return; + if(limit>=0 && limit<8) + spiTransfer(addr, OP_SCANLIMIT,limit); +} + +void LedControl::setIntensity(int addr, int intensity) { + if(addr<0 || addr>=maxDevices) + return; + if(intensity>=0 && intensity<16) + spiTransfer(addr, OP_INTENSITY,intensity); +} + +void LedControl::clearDisplay(int addr) { + int offset; + + if(addr<0 || addr>=maxDevices) + return; + offset=addr*8; + for(int i=0;i<8;i++) { + status[offset+i]=0; + spiTransfer(addr, i+1,status[offset+i]); + } +} + +void LedControl::setLed(int addr, int row, int column, boolean state) { + int offset; + byte val=0x00; + + if(addr<0 || addr>=maxDevices) + return; + if(row<0 || row>7 || column<0 || column>7) + return; + offset=addr*8; + val=B10000000 >> column; + if(state) + status[offset+row]=status[offset+row]|val; + else { + val=~val; + status[offset+row]=status[offset+row]&val; + } + spiTransfer(addr, row+1,status[offset+row]); +} + +void LedControl::setRow(int addr, int row, byte value) { + int offset; + if(addr<0 || addr>=maxDevices) + return; + if(row<0 || row>7) + return; + offset=addr*8; + status[offset+row]=value; + spiTransfer(addr, row+1,status[offset+row]); +} + +void LedControl::setColumn(int addr, int col, byte value) { + byte val; + + if(addr<0 || addr>=maxDevices) + return; + if(col<0 || col>7) + return; + for(int row=0;row<8;row++) { + val=value >> (7-row); + val=val & 0x01; + setLed(addr,row,col,val); + } +} + +void LedControl::setDigit(int addr, int digit, byte value, boolean dp) { + int offset; + byte v; + + if(addr<0 || addr>=maxDevices) + return; + if(digit<0 || digit>7 || value>15) + return; + offset=addr*8; + v=pgm_read_byte_near(charTable + value); + if(dp) + v|=B10000000; + status[offset+digit]=v; + spiTransfer(addr, digit+1,v); +} + +void LedControl::setChar(int addr, int digit, char value, boolean dp) { + int offset; + byte index,v; + + if(addr<0 || addr>=maxDevices) + return; + if(digit<0 || digit>7) + return; + offset=addr*8; + index=(byte)value; + if(index >127) { + //no defined beyond index 127, so we use the space char + index=32; + } + v=pgm_read_byte_near(charTable + index); + if(dp) + v|=B10000000; + status[offset+digit]=v; + spiTransfer(addr, digit+1,v); +} + +void LedControl::spiTransfer(int addr, volatile byte opcode, volatile byte data) { + //Create an array with the data to shift out + int offset=addr*2; + int maxbytes=maxDevices*2; + + for(int i=0;i0;i--) + shiftOut(SPI_MOSI,SPI_CLK,MSBFIRST,spidata[i-1]); + //latch the data onto the display + digitalWrite(SPI_CS,HIGH); +} + + diff --git a/lib/lib_display/LedControl/src/LedControl.h b/lib/lib_display/LedControl/src/LedControl.h new file mode 100644 index 000000000..cdfaa1f5a --- /dev/null +++ b/lib/lib_display/LedControl/src/LedControl.h @@ -0,0 +1,190 @@ +/* + * LedControl.h - A library for controling Leds with a MAX7219/MAX7221 + * Copyright (c) 2007 Eberhard Fahle + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * This permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LedControl_h +#define LedControl_h + +#include + +#if (ARDUINO >= 100) +#include +#else +#include +#endif + +/* + * Segments to be switched on for characters and digits on + * 7-Segment Displays + */ +const static byte charTable [] PROGMEM = { + B01111110,B00110000,B01101101,B01111001,B00110011,B01011011,B01011111,B01110000, + B01111111,B01111011,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B10000000,B00000001,B10000000,B00000000, + B01111110,B00110000,B01101101,B01111001,B00110011,B01011011,B01011111,B01110000, + B01111111,B01111011,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111,B00000000, + B00110111,B00000000,B00000000,B00000000,B00001110,B00000000,B00000000,B00000000, + B01100111,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00001000, + B00000000,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111,B00000000, + B00110111,B00000000,B00000000,B00000000,B00001110,B00000000,B00010101,B00011101, + B01100111,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000, + B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000 +}; + +class LedControl { + private : + /* The array for shifting the data to the devices */ + byte spidata[16]; + /* Send out a single command to the device */ + void spiTransfer(int addr, byte opcode, byte data); + + /* We keep track of the led-status for all 8 devices in this array */ + byte status[64]; + /* Data is shifted out of this pin*/ + int SPI_MOSI; + /* The clock is signaled on this pin */ + int SPI_CLK; + /* This one is driven LOW for chip selectzion */ + int SPI_CS; + /* The maximum number of devices we use */ + int maxDevices; + + public: + /* + * Create a new controler + * Params : + * dataPin pin on the Arduino where data gets shifted out + * clockPin pin for the clock + * csPin pin for selecting the device + * numDevices maximum number of devices that can be controled + */ + LedControl(int dataPin, int clkPin, int csPin, int numDevices=1); + + /* + * Gets the number of devices attached to this LedControl. + * Returns : + * int the number of devices on this LedControl + */ + int getDeviceCount(); + + /* + * Set the shutdown (power saving) mode for the device + * Params : + * addr The address of the display to control + * status If true the device goes into power-down mode. Set to false + * for normal operation. + */ + void shutdown(int addr, bool status); + + /* + * Set the number of digits (or rows) to be displayed. + * See datasheet for sideeffects of the scanlimit on the brightness + * of the display. + * Params : + * addr address of the display to control + * limit number of digits to be displayed (1..8) + */ + void setScanLimit(int addr, int limit); + + /* + * Set the brightness of the display. + * Params: + * addr the address of the display to control + * intensity the brightness of the display. (0..15) + */ + void setIntensity(int addr, int intensity); + + /* + * Switch all Leds on the display off. + * Params: + * addr address of the display to control + */ + void clearDisplay(int addr); + + /* + * Set the status of a single Led. + * Params : + * addr address of the display + * row the row of the Led (0..7) + * col the column of the Led (0..7) + * state If true the led is switched on, + * if false it is switched off + */ + void setLed(int addr, int row, int col, boolean state); + + /* + * Set all 8 Led's in a row to a new state + * Params: + * addr address of the display + * row row which is to be set (0..7) + * value each bit set to 1 will light up the + * corresponding Led. + */ + void setRow(int addr, int row, byte value); + + /* + * Set all 8 Led's in a column to a new state + * Params: + * addr address of the display + * col column which is to be set (0..7) + * value each bit set to 1 will light up the + * corresponding Led. + */ + void setColumn(int addr, int col, byte value); + + /* + * Display a hexadecimal digit on a 7-Segment Display + * Params: + * addr address of the display + * digit the position of the digit on the display (0..7) + * value the value to be displayed. (0x00..0x0F) + * dp sets the decimal point. + */ + void setDigit(int addr, int digit, byte value, boolean dp); + + /* + * Display a character on a 7-Segment display. + * There are only a few characters that make sense here : + * '0','1','2','3','4','5','6','7','8','9','0', + * 'A','b','c','d','E','F','H','L','P', + * '.','-','_',' ' + * Params: + * addr address of the display + * digit the position of the character on the display (0..7) + * value the character to be displayed. + * dp sets the decimal point. + */ + void setChar(int addr, int digit, char value, boolean dp); +}; + +#endif //LedControl.h + + + diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index b61c82c6e..7693ec42c 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index 2061eb460..96d1783c1 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -642,6 +642,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index be329c3e2..9359c0c3a 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index 9eaff2441..e042317bd 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index ca14da7c1..677ea5c1d 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 12a476a32..a2afce3f0 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index 93967a818..4316797d7 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 980b380f7..c31f97859 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index 8b166dd94..ca6688f57 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 8bf5e5e97..0b4374f5c 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 239239348..5c4b3a150 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index b8a7d828b..c449f9362 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 - CLK" #define D_SENSOR_TM1638_DIO "TM1638 - DIO" #define D_SENSOR_TM1638_STB "TM1638 - STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 - DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 - CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 - CLK" #define D_SENSOR_HX711_SCK "HX711 - SCK" #define D_SENSOR_HX711_DAT "HX711 - DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index 964ab46a7..442aaa4cb 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index bc855e4a8..87ad91eae 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 78710996f..da1151789 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index a2929b529..138799cbd 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index c5b524875..c448703bd 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index c8182eb75..9329d95ef 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index 6d8d0b932..ac7a9479a 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index b0fbc5c3f..1342d88ae 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index 5ccbc77fe..1300e6295 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index e6429fed1..bcf01e1ea 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 286b851dc..27527d226 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index c04e43010..5b9b2c9c2 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index a7e1847ca..9f99d4cf8 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 13cc6e858..cfeed1ff7 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -643,6 +643,9 @@ #define D_SENSOR_TM1638_CLK "TM1638 CLK" #define D_SENSOR_TM1638_DIO "TM1638 DIO" #define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" #define D_SENSOR_HX711_SCK "HX711 SCK" #define D_SENSOR_HX711_DAT "HX711 DAT" #define D_SENSOR_FTC532 "FTC532" diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index c4956bdbd..fa25e67f0 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -306,6 +306,7 @@ #define USE_DISPLAY // Add Display Support (+2k code) #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 module + #define USE_DISPLAY_MAX7219 // [DisplayModel 16] Enable MAX7219 7-segment module #define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 197596b7b..4bdeee62d 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -155,6 +155,7 @@ enum UserSelectablePins { GPIO_XPT2046_CS, // XPT2046 SPI Chip Select GPIO_CSE7761_TX, GPIO_CSE7761_RX, // CSE7761 Serial interface (Dual R3) GPIO_VL53L0X_XSHUT1, // VL53L0X_XSHUT (the max number of sensors is VL53L0X_MAX_SENSORS)- Used when connecting multiple VL53L0X + GPIO_MAX7219CLK, GPIO_MAX7219DIN, GPIO_MAX7219CS, // MAX7219 interface GPIO_SENSOR_END }; enum ProgramSelectablePins { @@ -330,6 +331,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_XPT2046_CS "|" D_SENSOR_CSE7761_TX "|" D_SENSOR_CSE7761_RX "|" D_SENSOR_VL53L0X_XSHUT "|" + D_SENSOR_MAX7219_CLK "|" D_SENSOR_MAX7219_DIN "|" D_SENSOR_MAX7219_CS "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -789,8 +791,13 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif #ifdef USE_VL53L0X AGPIO(GPIO_VL53L0X_XSHUT1) + VL53L0X_MAX_SENSORS, // When using multiple VL53L0X. -#endif +#endif +#ifdef USE_DISPLAY_MAX7219 + AGPIO(GPIO_MAX7219CLK), + AGPIO(GPIO_MAX7219DIN), + AGPIO(GPIO_MAX7219CS), +#endif // USE_DISPLAY_MAX7219 /*-------------------------------------------------------------------------------------------*\ * ESP32 specifics \*-------------------------------------------------------------------------------------------*/ diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 35e4e05ee..fd2c3964d 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -42,7 +42,7 @@ uint16_t bg_color = 0; uint8_t color_type = COLOR_BW; uint8_t auto_draw = 1; -const uint8_t DISPLAY_MAX_DRIVERS = 16; // Max number of display drivers/models supported by xdsp_interface.ino +const uint8_t DISPLAY_MAX_DRIVERS = 17; // Max number of display drivers/models supported by xdsp_interface.ino const uint8_t DISPLAY_MAX_COLS = 64; // Max number of columns allowed with command DisplayCols const uint8_t DISPLAY_MAX_ROWS = 64; // Max number of lines allowed with command DisplayRows @@ -1873,15 +1873,9 @@ void CmndDisplayScrollText(void) void CmndDisplaySize(void) { -#ifdef USE_DISPLAY_TM1637 - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 6)) { -#else - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { -#endif - Settings.display_size = XdrvMailbox.payload; - if (renderer) renderer->setTextSize(Settings.display_size); - //else DisplaySetSize(Settings.display_size); - } + Settings.display_size = XdrvMailbox.payload; + if (renderer) renderer->setTextSize(Settings.display_size); + //else DisplaySetSize(Settings.display_size); ResponseCmndNumber(Settings.display_size); } @@ -1945,7 +1939,7 @@ void CmndDisplayText(void) #ifndef USE_DISPLAY_MODES1TO5 DisplayText(); #else - if(Settings.display_model == 15) { + if(Settings.display_model == 15 || Settings.display_model == 16) { XdspCall(FUNC_DISPLAY_SEVENSEG_TEXT); } else if (!Settings.display_mode) { DisplayText(); diff --git a/tasmota/xdsp_16_max7219.ino b/tasmota/xdsp_16_max7219.ino new file mode 100644 index 000000000..c8a39b280 --- /dev/null +++ b/tasmota/xdsp_16_max7219.ino @@ -0,0 +1,878 @@ +/* + xdsp_16_max7219.ino - Support for MAX7219- based seven-segment displays for Tasmota + + Copyright (C) 2021 Ajith Vasudevan + + 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 . +*/ + +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_MAX7219 +/*********************************************************************************************\ + This driver enables the display of numbers (both integers and floats) and basic text + on the inexpensive MAX7219-based seven-segment modules. + + Raw segments can also be displayed. + + In addition, it is also possible to set brightness (8 levels), clear the display, scroll text, + display a rudimentary bar graph, and a Clock (12 hr and 24 hr). + + To use, compile Tasmota with USE_DISPLAY and USE_DISPLAY_MAX7219, or build the tasmota-display env. + + Connect the MAX7219 display module's pins to any free GPIOs of the ESP8266 module + and 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 16 + using the command "DisplayModel 16" + + After the ESP8266/ESP32 module restarts again, the following "Display" commands can be used: + + + DisplayClear + + Clears the display, command: "DisplayClear" + + + DisplayNumber num [,position {0-(MAX7219Data.num_digits-1))} [,leading_zeros {0|1} [,length {1 to MAX7219Data.num_digits}]]] + + Clears and then displays number without decimal. command e.g., "DisplayNumber 1234" + Control 'leading zeros', 'length' and 'position' with "DisplayNumber 1234, , , " + 'leading zeros' can be 1 or 0 (default), 'length' can be 1 to MAX7219Data.num_digits, 'position' can be 0 (left-most) to MAX7219Data.num_digits (right-most). + See function description below for more details. + + DisplayNumberNC num [,position {0-(MAX7219Data.num_digits-1))} [,leading_zeros {0|1} [,length {1 to MAX7219Data.num_digits}]]] + + Display integer number as above, but without clearing first. e.g., "DisplayNumberNC 1234". Usage is same as above. + + + + DisplayFloat num [,position {0-(MAX7219Data.num_digits-1)} [,precision {0-MAX7219Data.num_digits} [,length {1 to MAX7219Data.num_digits}]]] + + Clears and then displays float (with decimal point) command e.g., "DisplayFloat 12.34" + See function description below for more details. + + + + DisplayFloatNC num [,position {0-(MAX7219Data.num_digits-1)} [,precision {0-MAX7219Data.num_digits} [,length {1 to MAX7219Data.num_digits}]]] + + Displays float (with decimal point) as above, but without clearing first. command e.g., "DisplayFloatNC 12.34" + See function description below for more details. + + + + DisplayBrightness num {1-8} + + Set brightness (1 to 8) command e.g., "DisplayBrightness 2" + + + + DisplayRaw position {0-(MAX7219Data.num_digits-1)},length {1 to MAX7219Data.num_digits}, num1 [, num2[, num3[, num4[, ...upto MAX7219Data.num_digits numbers]]]]] + + Takes upto MAX7219Data.num_digits comma-separated integers (0-255) and displays raw segments. Each number represents a + 7-segment digit. Each 8-bit number represents individual segments of a digit. + For example, the command "DisplayRaw 0, 4, 255, 255, 255, 255" would display "[8.8.8.8.]" + + + + DisplayText text [, position {0-(MAX7219Data.num_digits-1)} [,length {1 to MAX7219Data.num_digits}]] + + Clears and then displays basic text. command e.g., "DisplayText ajith vasudevan" + Control 'length' and 'position' with "DisplayText , , " + 'length' can be 1 to MAX7219Data.num_digits, 'position' can be 0 (left-most) to MAX7219Data.num_digits-1 (right-most) + A caret(^) symbol in the text input is dispayed as the degrees(°) symbol. This is useful for displaying Temperature! + For example, the command "DisplayText 22.5^" will display "22.5°". + + + DisplayTextNC text [, position {0-MAX7219Data.num_digits-1} [,length {1 to MAX7219Data.num_digits}]] + + Clears first, then displays text. Usage is same as above. + + + + DisplayScrollText text + + Displays scrolling text. + + + + DisplayScrollDelay delay {0-15} // default = 4 + + Sets the speed of text scroll. Smaller delay = faster scrolling. + + + + DisplayLevel num {0-100} + + Display a horizontal bar graph (0-100) command e.g., "DisplayLevel 50" will display [|||| ] + + + + DisplayClock 1|2|0 + + Displays a clock. + Commands "DisplayClock 1" // 12 hr format + "DisplayClock 2" // 24 hr format + "DisplayClock 0" // turn off clock + + + +\*********************************************************************************************/ + +#define XDSP_16 16 + +#define BRIGHTNESS_MIN 1 +#define BRIGHTNESS_MAX 8 +#define CMD_MAX_LEN 55 +#define LEVEL_MIN 0 +#define LEVEL_MAX 100 +#define SCROLL_MAX_LEN 50 +#define POSITION_MIN 0 +#define POSITION_MAX 8 +#define LED_MIN 0 +#define LED_MAX 255 +#define MAX7219_ADDR 0 + +#include + +LedControl *max7219display; + +struct +{ + char scroll_text[CMD_MAX_LEN]; + char msg[60]; + char model_name[8]; + uint8_t num_digits = 4; + uint8_t scroll_delay = 4; + uint8_t scroll_index = 0; + uint8_t iteration = 0; + uint8_t brightness = 5; + uint8_t prev_buttons; + + bool init_done = false; + bool scroll = false; + bool show_clock = false; + bool clock_24 = false; +} MAX7219Data; + +/*********************************************************************************************\ +* Init function +\*********************************************************************************************/ +void MAXDriverInit(void) +{ + + if (!(PinUsed(GPIO_MAX7219DIN) && PinUsed(GPIO_MAX7219CLK) && PinUsed(GPIO_MAX7219CS))) + return; + + Settings.display_model == XDSP_16; + MAX7219Data.num_digits = 8; + + strcpy(MAX7219Data.model_name, "MAX7219"); + max7219display = new LedControl(Pin(GPIO_MAX7219DIN), Pin(GPIO_MAX7219CLK), Pin(GPIO_MAX7219CS), 1); + max7219display->shutdown(MAX7219_ADDR, false); + + maxClearDisplay(); + MAX7219Data.brightness = (Settings.display_dimmer ? Settings.display_dimmer : MAX7219Data.brightness); + maxSetBrightness(MAX7219Data.brightness); + MAX7219Data.init_done = true; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: %s display driver initialized with %d digits"), MAX7219Data.model_name, MAX7219Data.num_digits); +} + +// Function to display specified ascii char at specified position for MAX7219 +void displayMAX7219ASCII(uint8_t pos, char c) +{ + pos = 7 - pos; + max7219display->setChar(MAX7219_ADDR, pos, c, false); +} + +// Function to display specified ascii char with dot at specified position for MAX7219 +void displayMAX7219ASCIIwDot(uint8_t pos, char c) +{ + pos = 7 - pos; + max7219display->setChar(MAX7219_ADDR, pos, c, true); +} + +// Function to display raw segments at specified position for MAX7219 +void displayMAX72197Seg(uint8_t pos, uint8_t seg) +{ + bool dec_bit = seg & 128; + seg = seg << 1; + seg = seg | dec_bit; + uint8_t NO_OF_BITS = 8; + uint8_t reverse_num = 0; + for (uint8_t i = 0; i < NO_OF_BITS; i++) + { + if ((seg & (1 << i))) + reverse_num |= 1 << ((NO_OF_BITS - 1) - i); + } + seg = reverse_num; + + pos = 7 - pos; + max7219display->setRow(MAX7219_ADDR, pos, seg); +} + +/*********************************************************************************************\ +* Displays number without decimal, with/without leading zeros, specifying start-position +* and length, optionally skipping clearing display before displaying the number. +* commands: DisplayNumber num [,position {0-(MAX7219Data.num_digits-1)} [,leading_zeros {0|1} [,length {1 to MAX7219Data.num_digits}]]] +* DisplayNumberNC num [,position {0-(MAX7219Data.num_digits-1)} [,leading_zeros {0|1} [,length {1 to MAX7219Data.num_digits}]]] // "NC" --> "No Clear" +\*********************************************************************************************/ +bool MAXCmndNumber(bool clear) +{ + char sNum[CMD_MAX_LEN]; + char sLeadingzeros[CMD_MAX_LEN]; + char sPosition[CMD_MAX_LEN]; + char sLength[CMD_MAX_LEN]; + uint8_t length = 0; + bool leadingzeros = false; + uint8_t position = 0; + + uint32_t num = 0; + + switch (ArgC()) + { + case 4: + subStr(sLength, XdrvMailbox.data, ",", 4); + length = atoi(sLength); + case 3: + subStr(sLeadingzeros, XdrvMailbox.data, ",", 3); + leadingzeros = atoi(sLeadingzeros); + case 2: + subStr(sPosition, XdrvMailbox.data, ",", 2); + position = atoi(sPosition); + case 1: + subStr(sNum, XdrvMailbox.data, ",", 1); + num = atof(sNum); + } + + if ((position < 0) || (position > (MAX7219Data.num_digits - 1))) + position = 0; + + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: num %d, pos %d, lead %d, len %d"), num, position, leadingzeros, length); + + if (clear) + maxClearDisplay(); + + char txt[30]; + snprintf_P(txt, sizeof(txt), PSTR("%d"), num); + if (!length) + length = strlen(txt); + if ((length < 0) || (length > MAX7219Data.num_digits)) + length = MAX7219Data.num_digits; + + char pad = (leadingzeros ? '0' : ' '); + uint32_t i = position; + uint8_t rawBytes[1]; + + for (; i < position + (length - strlen(txt)); i++) + { + if (i > MAX7219Data.num_digits) + break; + displayMAX7219ASCII(i, pad); + } + + for (uint32_t j = 0; i < position + length; i++, j++) + { + if (i > MAX7219Data.num_digits) + break; + if (txt[j] == 0) + break; + displayMAX7219ASCII(i, txt[j]); + } + + return true; +} + +/*********************************************************************************************\ +* Displays number with decimal, specifying position, precision and length, +* optionally skipping clearing display before displaying the number. +* commands: DisplayFloat num [,position {0-(MAX7219Data.num_digits-1)} [,precision {0-MAX7219Data.num_digits} [,length {1 to MAX7219Data.num_digits}]]] +* DisplayFloatNC num [,position {0-(MAX7219Data.num_digits-1)} [,precision {0-MAX7219Data.num_digits} [,length {1 to MAX7219Data.num_digits}]]] // "NC" --> "No Clear" +\*********************************************************************************************/ +bool MAXCmndFloat(bool clear) +{ + + char sNum[CMD_MAX_LEN]; + char sPrecision[CMD_MAX_LEN]; + char sPosition[CMD_MAX_LEN]; + char sLength[CMD_MAX_LEN]; + uint8_t length = 0; + uint8_t precision = MAX7219Data.num_digits; + uint8_t position = 0; + + float fnum = 0.0f; + + switch (ArgC()) + { + case 4: + subStr(sLength, XdrvMailbox.data, ",", 4); + length = atoi(sLength); + case 3: + subStr(sPrecision, XdrvMailbox.data, ",", 3); + precision = atoi(sPrecision); + case 2: + subStr(sPosition, XdrvMailbox.data, ",", 2); + position = atoi(sPosition); + case 1: + subStr(sNum, XdrvMailbox.data, ",", 1); + fnum = atof(sNum); + } + + if ((position < 0) || (position > (MAX7219Data.num_digits - 1))) + position = 0; + if ((precision < 0) || (precision > MAX7219Data.num_digits)) + precision = MAX7219Data.num_digits; + + if (clear) + maxClearDisplay(); + + char txt[30]; + ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), precision, &fnum); + + if (!length) + length = strlen(txt); + if ((length <= 0) || (length > MAX7219Data.num_digits)) + length = MAX7219Data.num_digits; + + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: num %4_f, prec %d, len %d"), &fnum, precision, length); + + for (uint32_t i = 0, j = 0; i < length; i++, j++) + { + if ((j + position) > 7) + break; + if (txt[i] == 0) + break; + if (txt[i + 1] == '.') + { + displayMAX7219ASCIIwDot(j + position, txt[i]); + i++; + length++; + } + else + displayMAX7219ASCII(j + position, txt[i]); + } + return true; +} + +// /*********************************************************************************************\ +// * Clears the display +// * Command: DisplayClear +// \*********************************************************************************************/ +bool MAXCmndClear(void) +{ + maxClearDisplay(); + sprintf(MAX7219Data.msg, PSTR("Cleared")); + XdrvMailbox.data = MAX7219Data.msg; + return true; +} + +// /*********************************************************************************************\ +// * Clears the display +// \*********************************************************************************************/ +void maxClearDisplay(void) +{ + max7219display->clearDisplay(MAX7219_ADDR); +} + +/*********************************************************************************************\ +* Display scrolling text +* Command: DisplayMAX7219Data.scroll_text text +\*********************************************************************************************/ +bool MAXCmndScrollText(void) +{ + + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: Text %s"), XdrvMailbox.data); + + if (XdrvMailbox.data_len > SCROLL_MAX_LEN) + { + snprintf(MAX7219Data.msg, sizeof(MAX7219Data.msg), PSTR("Text too long. Length should be less than %d"), SCROLL_MAX_LEN); + XdrvMailbox.data = MAX7219Data.msg; + return false; + } + else + { + snprintf(MAX7219Data.scroll_text, sizeof(MAX7219Data.scroll_text), PSTR(" ")); + snprintf(MAX7219Data.scroll_text, sizeof(MAX7219Data.scroll_text), PSTR("%s"), XdrvMailbox.data); + MAX7219Data.scroll_text[XdrvMailbox.data_len] = 0; + MAX7219Data.scroll_index = 0; + MAX7219Data.scroll = true; + return true; + } +} + +/*********************************************************************************************\ +* Sets the scroll delay for scrolling text. +* Command: DisplayMAX7219Data.scroll_delay delay {0-15} // default = 4 +\*********************************************************************************************/ +bool MAXCmndScrollDelay(void) +{ + if (ArgC() == 0) + { + XdrvMailbox.payload = MAX7219Data.scroll_delay; + return true; + } + if (MAX7219Data.scroll_delay < 0) + MAX7219Data.scroll_delay = 0; + MAX7219Data.scroll_delay = XdrvMailbox.payload; + return true; +} + +/*********************************************************************************************\ +* Scrolls a given string. Called every 50ms +\*********************************************************************************************/ +void maxScrollText(void) +{ + MAX7219Data.iteration++; + if (MAX7219Data.scroll_delay) + MAX7219Data.iteration = MAX7219Data.iteration % MAX7219Data.scroll_delay; + else + MAX7219Data.iteration = 0; + if (MAX7219Data.iteration) + return; + + if (MAX7219Data.scroll_index > strlen(MAX7219Data.scroll_text)) + { + MAX7219Data.scroll = false; + MAX7219Data.scroll_index = 0; + return; + } + uint8_t rawBytes[1]; + for (uint32_t i = 0, j = MAX7219Data.scroll_index; i < 1 + strlen(MAX7219Data.scroll_text); i++, j++) + { + if (i > (MAX7219Data.num_digits - 1)) + { + break; + } + rawBytes[0] = tm1637display->encode(MAX7219Data.scroll_text[j]); + bool dotSkipped = false; + if (MAX7219Data.scroll_text[j + 1] == '.') + { + dotSkipped = true; + rawBytes[0] = rawBytes[0] | 128; + j++; + } + else if (MAX7219Data.scroll_text[j] == '^') + { + rawBytes[0] = 1 | 2 | 32 | 64; + } + if (!dotSkipped && MAX7219Data.scroll_text[j] == '.') + { + j++; + MAX7219Data.scroll_index++; + rawBytes[0] = tm1637display->encode(MAX7219Data.scroll_text[j]); + } + if (MAX7219Data.scroll_text[j + 1] == '.') + { + rawBytes[0] = rawBytes[0] | 128; + } + displayMAX72197Seg(i, rawBytes[0]); + } + MAX7219Data.scroll_index++; +} + +/*********************************************************************************************\ +* Displays a horizontal bar graph. Takes a percentage number (0-100) as input +* Command: DisplayLevel level {0-100} +\*********************************************************************************************/ +bool MAXCmndLevel(void) +{ + uint16_t val = XdrvMailbox.payload; + if ((val < LEVEL_MIN) || (val > LEVEL_MAX)) + { + Response_P(PSTR("{\"Error\":\"Level should be a number in the range [%d, %d]\"}"), LEVEL_MIN, LEVEL_MAX); + return false; + } + + uint8_t totalBars = 2 * MAX7219Data.num_digits; + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: MAX7219Data.model_name %s MAXCmndLevel totalBars=%d"), MAX7219Data.model_name, totalBars); + float barsToDisplay = totalBars * val / 100.0f; + char txt[5]; + ext_snprintf_P(txt, sizeof(txt), PSTR("%*_f"), 1, &barsToDisplay); + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: MAX7219Data.model_name %s MAXCmndLevel barsToDisplay=%s"), MAX7219Data.model_name, txt); + char s[4]; + ext_snprintf_P(s, sizeof(s), PSTR("%0_f"), &barsToDisplay); + uint8_t numBars = atoi(s); + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: CmndTM1637Level numBars %d"), numBars); + + maxClearDisplay(); + uint8_t rawBytes[1]; + for (int i = 1; i <= numBars; i++) + { + uint8_t digit = (i - 1) / 2; + uint8_t value = (((i % 2) == 0) ? 54 : 48); + displayMAX72197Seg(digit, value); + } + return true; +} + +/*********************************************************************************************\ +* Display arbitrary data on the display module +* Command: DisplayRaw position {0-(MAX7219Data.num_digits-1)},length {1 to MAX7219Data.num_digits}, a [, b[, c[, d[...upto MAX7219Data.num_digits]]]] +* where a,b,c,d... are upto MAX7219Data.num_digits numbers in the range 0-255, each number (byte) +* corresponding to a single 7-segment digit. Within each byte, bit 0 is segment A, +* bit 1 is segment B etc. The function may either set the entire display +* or any desired part using the length and position parameters. +\*********************************************************************************************/ +bool MAXCmndRaw(void) +{ + uint8_t DATA[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + char as[CMD_MAX_LEN]; + char bs[CMD_MAX_LEN]; + char cs[CMD_MAX_LEN]; + char ds[CMD_MAX_LEN]; + char es[CMD_MAX_LEN]; + char fs[CMD_MAX_LEN]; + char gs[CMD_MAX_LEN]; + char hs[CMD_MAX_LEN]; + + char sLength[CMD_MAX_LEN]; + char sPos[CMD_MAX_LEN]; + + uint32_t position = 0; + uint32_t length = 0; + + switch (ArgC()) + { + case 10: + subStr(hs, XdrvMailbox.data, ",", 10); + DATA[7] = atoi(hs); + case 9: + subStr(gs, XdrvMailbox.data, ",", 9); + DATA[6] = atoi(gs); + case 8: + subStr(fs, XdrvMailbox.data, ",", 8); + DATA[5] = atoi(fs); + case 7: + subStr(es, XdrvMailbox.data, ",", 7); + DATA[4] = atoi(es); + case 6: + subStr(ds, XdrvMailbox.data, ",", 6); + DATA[3] = atoi(ds); + case 5: + subStr(cs, XdrvMailbox.data, ",", 5); + DATA[2] = atoi(cs); + case 4: + subStr(bs, XdrvMailbox.data, ",", 4); + DATA[1] = atoi(bs); + case 3: + subStr(as, XdrvMailbox.data, ",", 3); + DATA[0] = atoi(as); + case 2: + subStr(sLength, XdrvMailbox.data, ",", 2); + length = atoi(sLength); + case 1: + subStr(sPos, XdrvMailbox.data, ",", 1); + position = atoi(sPos); + } + + if (!length) + length = ArgC() - 2; + if (length < 0 || length > MAX7219Data.num_digits) + length = MAX7219Data.num_digits; + if (position < 0 || position > (MAX7219Data.num_digits - 1)) + position = 0; + + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: a %d, b %d, c %d, d %d, e %d, f %d, g %d, h %d, len %d, pos %d"), + DATA[0], DATA[1], DATA[2], DATA[3], DATA[4], DATA[5], DATA[6], DATA[7], length, position); + + for (uint32_t i = position; i < position + length; i++) + { + if (i > 7) + break; + displayMAX72197Seg(i, DATA[i - position]); + } + + return true; +} + +/*********************************************************************************************\ +* Display a given string. +* Text can be placed at arbitrary location on the display using the length and +* position parameters without affecting the rest of the display. +* Command: DisplayText text [, position {0-(MAX7219Data.num_digits-1)} [,length {1 to MAX7219Data.num_digits}]] +\*********************************************************************************************/ +bool MAXCmndText(bool clear) +{ + char sString[CMD_MAX_LEN + 1]; + char sPosition[CMD_MAX_LEN]; + char sLength[CMD_MAX_LEN]; + uint8_t length = 0; + uint8_t position = 0; + + switch (ArgC()) + { + case 3: + subStr(sLength, XdrvMailbox.data, ",", 3); + length = atoi(sLength); + case 2: + subStr(sPosition, XdrvMailbox.data, ",", 2); + position = atoi(sPosition); + case 1: + subStr(sString, XdrvMailbox.data, ",", 1); + } + + if ((position < 0) || (position > (MAX7219Data.num_digits - 1))) + position = 0; + + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: sString %s, pos %d, len %d"), sString, position, length); + + if (clear) + maxClearDisplay(); + + if (!length) + length = strlen(sString); + if ((length < 0) || (length > MAX7219Data.num_digits)) + length = MAX7219Data.num_digits; + + uint32_t i = position; + uint8_t rawBytes[1]; + for (uint32_t j = 0; i < position + length; i++, j++) + { + if (i > (MAX7219Data.num_digits - 1)) + break; + if (sString[j] == 0) + break; + rawBytes[0] = tm1637display->encode(sString[j]); + bool dotSkipped = false; + if (sString[j + 1] == '.') + { + dotSkipped = true; + rawBytes[0] = rawBytes[0] | 128; + j++; + } + else if (sString[j] == '^') + { + rawBytes[0] = 1 | 2 | 32 | 64; + } + if (!dotSkipped && sString[j] == '.') + rawBytes[0] = 128; + displayMAX72197Seg(i, rawBytes[0]); + } + + return true; +} + +/*********************************************************************************************\ +* Sets brightness of the display. +* Command: DisplayBrightness {1-8} +\*********************************************************************************************/ +bool MAXCmndBrightness(void) +{ + + uint16_t val = XdrvMailbox.payload; + if (ArgC() == 0) + { + XdrvMailbox.payload = MAX7219Data.brightness; + return true; + } + + if ((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) + { + Response_P(PSTR("{\"Error\":\"Brightness should be a number in the range [%d, %d]\"}"), BRIGHTNESS_MIN, BRIGHTNESS_MAX); + return false; + } + MAX7219Data.brightness = val; + maxSetBrightness(MAX7219Data.brightness); + return true; +} + +void maxSetBrightness(uint8_t val) +{ + if ((val < BRIGHTNESS_MIN) || (val > BRIGHTNESS_MAX)) + val = 5; + Settings.display_dimmer = val; + max7219display->setIntensity(MAX7219_ADDR, val - 1); +} + +/*********************************************************************************************\ +* Displays a clock. +* Command: DisplayClock 1 // 12-hour format +* DisplayClock 2 // 24-hour format +* DisplayClock 0 // turn off clock and clear +\*********************************************************************************************/ +bool MAXCmndClock(void) +{ + + MAX7219Data.show_clock = XdrvMailbox.payload; + + if (ArgC() == 0) + XdrvMailbox.payload = 1; + if (XdrvMailbox.payload > 1) + MAX7219Data.clock_24 = true; + else if (XdrvMailbox.payload == 1) + MAX7219Data.clock_24 = false; + + AddLog(LOG_LEVEL_DEBUG, PSTR("MAX: MAX7219Data.show_clock %d, MAX7219Data.clock_24 %d"), MAX7219Data.show_clock, MAX7219Data.clock_24); + + maxClearDisplay(); + return true; +} + +/*********************************************************************************************\ +* refreshes the time if clock is displayed +\*********************************************************************************************/ +void maxShowTime() +{ + uint8_t hr = RtcTime.hour; + uint8_t mn = RtcTime.minute; + // uint8_t hr = 1; + // uint8_t mn = 0; + char z = ' '; + if (MAX7219Data.clock_24) + { + z = '0'; + } + else + { + if (hr > 12) + hr -= 12; + if (hr == 0) + hr = 12; + } + + char tm[5]; + if (hr < 10) + { + if (mn < 10) + snprintf(tm, sizeof(tm), PSTR("%c%d0%d"), z, hr, mn); + else + snprintf(tm, sizeof(tm), PSTR("%c%d%d"), z, hr, mn); + } + else + { + if (mn < 10) + snprintf(tm, sizeof(tm), PSTR("%d0%d"), hr, mn); + else + snprintf(tm, sizeof(tm), PSTR("%d%d"), hr, mn); + } + + for (uint32_t i = 0; i < 4; i++) + { + if ((millis() % 1000) > 500 && (i == 1)) + displayMAX7219ASCIIwDot(i, tm[i]); + else + displayMAX7219ASCII(i, tm[i]); + } +} + +/*********************************************************************************************\ +* This function is called for all Display functions. +\*********************************************************************************************/ +bool MAXMainFunc(uint8_t fn) +{ + bool result = false; + + if (XdrvMailbox.data_len > CMD_MAX_LEN) + { + Response_P(PSTR("{\"Error\":\"Command text too long. Please limit it to %d characters\"}"), CMD_MAX_LEN); + return false; + } + + switch (fn) + { + case FUNC_DISPLAY_CLEAR: + result = MAXCmndClear(); + break; + case FUNC_DISPLAY_NUMBER: + result = MAXCmndNumber(true); + break; + case FUNC_DISPLAY_NUMBERNC: + result = MAXCmndNumber(false); + break; + case FUNC_DISPLAY_FLOAT: + result = MAXCmndFloat(true); + break; + case FUNC_DISPLAY_FLOATNC: + result = MAXCmndFloat(false); + break; + case FUNC_DISPLAY_BRIGHTNESS: + result = MAXCmndBrightness(); + break; + case FUNC_DISPLAY_RAW: + result = MAXCmndRaw(); + break; + case FUNC_DISPLAY_SEVENSEG_TEXT: + result = MAXCmndText(true); + break; + case FUNC_DISPLAY_SEVENSEG_TEXTNC: + result = MAXCmndText(false); + break; + case FUNC_DISPLAY_LEVEL: + result = MAXCmndLevel(); + break; + case FUNC_DISPLAY_SCROLLTEXT: + result = MAXCmndScrollText(); + break; + case FUNC_DISPLAY_SCROLLDELAY: + result = MAXCmndScrollDelay(); + break; + case FUNC_DISPLAY_CLOCK: + result = MAXCmndClock(); + break; + } + + return result; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ +bool Xdsp16(uint8_t function) +{ + bool result = false; + + if (Settings.display_model == XDSP_16) + { + switch (function) + { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT_DRIVER: + MAXDriverInit(); // init + break; + case FUNC_DISPLAY_SEVENSEG_TEXT: + case FUNC_DISPLAY_CLEAR: + case FUNC_DISPLAY_NUMBER: + case FUNC_DISPLAY_FLOAT: + case FUNC_DISPLAY_NUMBERNC: + case FUNC_DISPLAY_FLOATNC: + case FUNC_DISPLAY_RAW: + case FUNC_DISPLAY_LEVEL: + case FUNC_DISPLAY_SEVENSEG_TEXTNC: + case FUNC_DISPLAY_SCROLLTEXT: + case FUNC_DISPLAY_SCROLLDELAY: + case FUNC_DISPLAY_CLOCK: + MAX7219Data.show_clock = false; + case FUNC_DISPLAY_BRIGHTNESS: + result = MAXMainFunc(function); + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: + if (MAX7219Data.scroll) + maxScrollText(); + if (MAX7219Data.show_clock) + maxShowTime(); + break; + } + } + return result; +} + +#endif // USE_DISPLAY_MAX7219 +#endif // USE_DISPLAY