Add support for TM1640 based IoTTimer by Stefan Oskamp (#21376)

This commit is contained in:
Theo Arends 2024-11-20 14:31:21 +01:00
parent 3778f22d7b
commit ae7cba2f13
5 changed files with 790 additions and 7 deletions

View File

@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
## [14.3.0.7]
### Added
- Support for TM1640 based IoTTimer by Stefan Oskamp (#21376)
### Breaking Changed

View File

@ -130,6 +130,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
- Support for HLK-LD2410S 24GHz smart wave motion sensor [#22253](https://github.com/arendst/Tasmota/issues/22253)
- Support for US AQI and EPA AQI in PMS5003x sensors [#22294](https://github.com/arendst/Tasmota/issues/22294)
- Support for MS5837 pressure and temperature sensor [#22376](https://github.com/arendst/Tasmota/issues/22376)
- Support for TM1640 based IoTTimer by Stefan Oskamp [#21376](https://github.com/arendst/Tasmota/issues/21376)
- HLK-LD2410 Engineering mode [#21880](https://github.com/arendst/Tasmota/issues/21880)
- Mitsubishi Electric HVAC Operation time for MiElHVAC [#22334](https://github.com/arendst/Tasmota/issues/22334)
- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC [#22345](https://github.com/arendst/Tasmota/issues/22345)

View File

@ -775,7 +775,7 @@
#define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module
#define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module
#define USE_DISPLAY_SEVENSEG // [DisplayModel 11] [I2cDriver47] Enable sevenseg display (I2C 0x70-0x77) (<+11k code)
// #define USE_DISPLAY_SEVENSEG_COMMON_ANODE // Enable support for common anode sevenseg displays
// #define USE_DISPLAY_SEVENSEG_COMMON_ANODE // Enable support for common anode sevenseg displays
// Multiple sevenseg displays are logically arranged vertically with MTX_ADDRESS1 at y=0,
// MTX_ADDRESS2 at y=1, up to MTX_ADDRESS8 at y=7
// Command: DisplayText [yn]8888
@ -783,7 +783,7 @@
// Each segment may be address Command: DisplayText [xn]m
// where n is 0..4 (4 digits and middle :) and m is decimal for bitmap of which segment to turn on.
// Reference: https://cdn-learn.adafruit.com/downloads/pdf/adafruit-led-backpack.pdf
// #define SEVENSEG_ADDRESS1 0x70 // No longer used. Use MTX_ADDRESS1 - MTX_ADDRESS8 instead to specify I2C address of sevenseg displays
// #define SEVENSEG_ADDRESS1 0x70 // No longer used. Use MTX_ADDRESS1 - MTX_ADDRESS8 instead to specify I2C address of sevenseg displays
// #define USE_DISPLAY_SH1106 // [DisplayModel 7] [I2cDriver6] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D)
// #define USE_DISPLAY_TM1650 // [DisplayModel 20] [I2cDriver74] Enable TM1650 display (I2C addresses 0x24 - 0x27 and 0x34 - 0x37)
// #define USE_DT_VARS // Display variables that are exposed in JSON MQTT strings e.g. in TelePeriod messages.
@ -793,10 +793,12 @@
#endif // USE_I2C
// #define USE_DISPLAY // Add I2C/TM1637/MAX7219 Display Support (+2k code)
// #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 Seven Segment Display Module (4-6 digits)
// #define USE_DISPLAY_MAX7219 // [DisplayModel 15] Enable MAX7219 Seven Segment Display Module (8 digits)
// #define USE_DISPLAY_MAX7219_MATRIX // [DisplayModel 19] Enable MAX7219 8x8 Matrix Display
//#define USE_DISPLAY // Add I2C/TM1637/MAX7219 Display Support (+2k code)
// #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 Seven Segment Display Module (4-6 digits)
// #define USE_DISPLAY_MAX7219 // [DisplayModel 15] Enable MAX7219 Seven Segment Display Module (8 digits)
// #define USE_DISPLAY_MAX7219_MATRIX // [DisplayModel 19] Enable MAX7219 8x8 Matrix Display
// #define USE_DISPLAY_TM1640 // [DisplayModel 13] Enable TM1640 module Seven Segment Display Module (stub)
// #define USE_IOTTIMER // Enable TM1640 based IotTimer
// -- Universal Display Driver ---------------------------------
// #define USE_UNIVERSAL_DISPLAY // New universal display driver for both I2C and SPI

View File

@ -2169,7 +2169,7 @@ void CmndDisplayText(void) {
void CmndDisplayClear(void) {
DisplayClear();
ResponseCmndChar(XdrvMailbox.data);
ResponseCmndDone();
}
void CmndDisplayNumber(void) {

View File

@ -0,0 +1,779 @@
/*
xdsp_13_tm1640.ino - TM1640B LED display controller support for Tasmota
Copyright (C) 2024 Stefan Oskamp
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_TM1640
/*********************************************************************************************\
This driver enables the display of the current time, numbers (both integers and floats) and
basic text on the IoTTimer clock.
Template {"NAME":"IoTTimer Lo","GPIO":[32,33,0,34,3872,1312,0,0,10944,10912,640,480,608,4768],"FLAG":0,"BASE":18}
In addition, it is possible to set brightness (seven levels plus off) and clear the display.
To use, compile Tasmota with USE_DISPLAY, USE_DISPLAY_TM1640 and USE_IOTTIMER, or build the tasmota-display
firmware.
Either use following template:
Template {"NAME":"IoTTimer Lo","GPIO":[32,33,0,34,3872,1312,0,0,10944,10912,640,480,608,4768],"FLAG":0,"BASE":18}
or configure manually:
For the IoTTimer clock assign the pins as follows from Tasmota's GUI:
GPIO12 --> "TM1640 DIN"
GPIO13 --> "TM1640 CLK"
Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, set the Display
Model to 13 and Display Mode to 0 using the command "Backlog DisplayModel 13 ; DisplayMode 0;"
Before using it, set the Display Type to 1 (for IOTTIMER) using the "DisplayType 1" command.
After the ESP8266 restarts again, turn ON the display with the command "Power 1"
Now, the following "Display" commands can be used:
DisplayClear
Clears the display, command: "DisplayClear"
DisplayFloat num
Clears and then displays float (with decimal point) command e.g., "DisplayFloat 12.3"
See function description below for more details.
DisplayFloatNC num
Same as DisplayFloatNC
DisplayClock 1|2|3|4
Displays a clock.
Commands "DisplayClock 1" // 12 hr format
"DisplayClock 2" // 24 hr format
"DisplayClock 3" // 12-hour without seconds
"DisplayClock 4" // 24-hour without seconds
In addition, if you compile using USE_DISPLAY_MODES1TO5, setting DisplayMode to 1 shows the time,
setting it to 2 shows the date and setting it to 3 alternates between time and date (using "DisplayRefresh [1..7]"
for the time and seconds you want to show the time before displaying the date)
\*********************************************************************************************/
#define XDSP_13 13
#define TM1640_CMD_DATA_AUTO 0x40
#define TM1640_CMD_DATA_FIXED 0x44
#define TM1640_CMD_DISPLAY 0x80
#define TM1640_CMD_ADDRESS 0xC0
#define TM1640_CLOCK_DELAY 1 // uSec
#define LEVEL_MIN 0
#define LEVEL_MAX 100
enum tm1640_display_options_types {
TM1640_DEFAULT,
TM1640_IOTTIMER // IOTTIMER WiFi clock
};
typedef struct Tm1640_t {
int8_t clock_pin;
int8_t data_pin;
bool show_clock;
bool clock_24;
bool clock_seconds;
} Tm1640_t;
Tm1640_t* Tm1640 = nullptr;
/*********************************************************************************************\
* TM1640 low level functions
\*********************************************************************************************/
void TM1640Start (void) {
digitalWrite(Tm1640->data_pin, LOW);
digitalWrite(Tm1640->clock_pin, LOW);
delayMicroseconds(TM1640_CLOCK_DELAY);
}
void TM1640Stop (void) {
digitalWrite(Tm1640->clock_pin, HIGH);
digitalWrite(Tm1640->data_pin, HIGH);
delayMicroseconds(TM1640_CLOCK_DELAY);
}
void TM1640Send(uint8_t data) {
for (uint32_t i = 0; i < 8; i++) { // 8 bits
digitalWrite(Tm1640->data_pin, data & 1 ? HIGH : LOW);
delayMicroseconds(TM1640_CLOCK_DELAY);
data >>= 1;
digitalWrite(Tm1640->clock_pin, HIGH);
delayMicroseconds(TM1640_CLOCK_DELAY);
digitalWrite(Tm1640->clock_pin, LOW);
delayMicroseconds(TM1640_CLOCK_DELAY);
}
digitalWrite(Tm1640->data_pin, LOW);
delayMicroseconds(TM1640_CLOCK_DELAY);
}
void TM1640SendData(uint8_t address, uint8_t data) {
// First, send data command using FIXED addressing:
TM1640Start();
TM1640Send(TM1640_CMD_DATA_FIXED);
TM1640Stop();
// Then, send address and one data byte:
TM1640Start();
TM1640Send(TM1640_CMD_ADDRESS | address);
TM1640Send(data);
TM1640Stop();
}
void TM1640SendDataArray(uint8_t address, uint8_t *data, uint8_t count) {
// First, send data command using AUTO addressing:
TM1640Start();
TM1640Send(TM1640_CMD_DATA_AUTO);
TM1640Stop();
// Then, send address and all data bytes:
TM1640Start();
TM1640Send(TM1640_CMD_ADDRESS | address);
while (count-- > 0) {
TM1640Send(*data++);
}
TM1640Stop();
}
void TM1640SetBrightness(uint8_t level) {
// level can be 0 to 8.
// 0 means off
//
// Other levels are mapped to TM1640 levels 0 ... 7
// The mapping to the PWM level is non-linear, according to the data sheet
// level | TM1640 | PWM
// 1 | 0 | 1/16
// 2 | 1 | 2/16
// 3 | 2 | 4/16
// 4 | 3 | 10/16
// 5 | 4 | 11/16
// 6 | 5 | 12/16
// 7 | 6 | 13/16
// 8 | 7 | 14/16
uint8_t cmd = TM1640_CMD_DISPLAY | (level > 0 ? 0x8 : 0) | ((level - 1) % 8);
TM1640Start();
TM1640Send (cmd);
TM1640Stop();
}
/*********************************************************************************************\
* Init function
\*********************************************************************************************/
void TM1640Init(void) {
if (PinUsed(GPIO_TM1640CLK) && PinUsed(GPIO_TM1640DIN)) {
Tm1640 = (Tm1640_t*)calloc(sizeof(Tm1640_t), 1); // Need calloc to reset registers to 0/false
if (nullptr == Tm1640) { return; }
Tm1640->clock_pin = Pin(GPIO_TM1640CLK);
Tm1640->data_pin = Pin(GPIO_TM1640DIN);
pinMode(Tm1640->data_pin, OUTPUT);
pinMode(Tm1640->clock_pin, OUTPUT);
digitalWrite(Tm1640->clock_pin, HIGH);
digitalWrite(Tm1640->data_pin, HIGH);
Settings->display_model = XDSP_13;
#ifdef USE_IOTTIMER
Settings->display_options.type = TM1640_IOTTIMER;
Settings->display_cols[0] = 9; // 4 (left) + 2 (lower right) + 3 (upper right).
Settings->display_rows = 1;
Settings->display_width = Settings->display_cols[0];
Settings->display_height = Settings->display_rows;
Tm1640->clock_24 = true;
Tm1640->clock_seconds = true;
IoTTimerDim();
IoTTimerClearDisplay();
#endif // USE_IOTTIMER
AddLog(LOG_LEVEL_INFO, PSTR("DSP: TM1640 with %d digits (type %d)"), Settings->display_width, Settings->display_options.type);
}
}
/*********************************************************************************************\
* IotTimer
\*********************************************************************************************/
#ifdef USE_IOTTIMER
/*
(specifically for its use in the IOTTIMER WiFi clock)
The WiFi LED clock called IOTTIMER has the following characteristics:
- Controlled by an ESP-12F
- Display with four 35 mm (1 12/32 in), two 21 mm (26/32 in), and three 12 mm (~1/2 in),
seven-segment LED digits, plus special symbols (alarm, AM/PM)
- TM1640B LED controller
- R8010 RTC with CR1220 battery
- Temperature sensor M1601
- Ambient light sensor (analog voltage)
- Buzzer
- Three buttons on the backside
- USB C port for power supply (only)
The TM1640B chip is a controller for a sixteen-digit seven-segment (plus dot) LED display.
It is also sometimes used to control a 16 x 8 LED matrix. The controller is controlled
through a proprietary two-wire serial interface bearing some similarities with I2C. The
two wires are called CLK and DIN. We use two GPIO pins and one-microsecond sleeps to
implement the required timing.
The wiring of the LEDs in the IOTTIMER clock has been optimized for a simple routing of the
traces on the display board. The enumeration of the digit segments is non-standard, but
consistent across all digits. The bigger digits have two LEDs per segment, controlled by
separate digit lines of the LED controller. From the software perspective, they appear as
two layers of four digits each.
The brightness of the LEDs can be controlled in seven steps (plus off). In theory, the
brightness of the segments with two LEDs could be set in fifteen levels (plus off).
To keep things simple and to avoid brightness gradients within segments, both LEDs of a
segment will always be set to the same level.
The intention of this display driver (together with the drivers for the other components)
is to be able to use the IOTTIMER as an alarm clock that can be fully integrated in your
home automation using Tasmota and rules.
This driver is not a generic TM1640B driver as use cases of the TM1640B in different
devices will differ significantly.
*/
#define IOTTIMER_DIGITS 16
#define IOTTIMER_DOT_BIT 2
static unsigned char IoTTimerDisplay[IOTTIMER_DIGITS];
// Wiring of the LEDs (per digit):
//
// Seg# Bit Hex
// 07 06 40
// 08 01 07 00 80 01
// 02 01 02
// 06 04 05 03 20 08
// 05 03 04 02 10 04
//
// Font as per wiring:
static const byte IoTTimerFont[128] {
//0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10
// SP ! " # $ % & ' ( ) * + , - . /
0x00, 0xA0, 0x81, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x59, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // 0x20
//0 1 2 3 4 5 6 7 8 9 : ; < = > ?
0xF9, 0x09, 0x73, 0x5B, 0x8B, 0xDA, 0xFA, 0x49, 0xFB, 0xDB, 0x00, 0x00, 0x00, 0x12, 0x00, 0x63, // 0x30
// @ A B C D E F G H I J K L M N O
0x00, 0xEB, 0xBA, 0xF0, 0x3B, 0xF2, 0xE2, 0xFA, 0xAB, 0x09, 0x19, 0x00, 0xB0, 0x00, 0xE9, 0xF9, // 0x40
// P Q R S T U V W X Y Z [ \ ] ^(°) _
0xE3, 0xAB, 0x22, 0xDA, 0xB2, 0xB9, 0x00, 0x00, 0x00, 0x4B, 0x00, 0xF0, 0x00, 0x59, 0xC3, 0x10, // 0x50
// `=° a b c d e f g h i j k l m n o
0x01, 0x7B, 0xBA, 0x32, 0x3B, 0xF3, 0xE2, 0xDB, 0xAA, 0x08, 0x19, 0x00, 0x09, 0x00, 0x2A, 0x3A, // 0x60
// p q r s t u v w x y z { | } ~ DEL
0xE3, 0xAB, 0x22, 0xDA, 0xB2, 0x38, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x0B, 0x09, 0xA2, 0x00, 0x00 // 0x70
};
void IoTTimerDim(void)
{
TM1640SetBrightness (changeUIntScale(GetDisplayDimmer(), 0, 100, 0, 8));
}
void IoTTimerDisplayOn (void)
{
IoTTimerDim();
}
void IoTTimerDisplayOff (void)
{
TM1640SetBrightness (0);
}
void IoTTimerDisplayOnOff(void)
{
if (disp_power) {
IoTTimerDisplayOn();
}
else {
IoTTimerDisplayOff();
}
}
void IoTTimerClearDisplay (void)
{
for (int i = 0; i < IOTTIMER_DIGITS; i++) {
IoTTimerDisplay[i] = 0;
}
TM1640SendDataArray(0, IoTTimerDisplay, IOTTIMER_DIGITS);
}
/*********************************************************************************************\
* Init function
\*********************************************************************************************/
void IoTTimerInit(uint8_t mode)
{
switch(mode) {
case DISPLAY_INIT_MODE:
IoTTimerDim();
IoTTimerClearDisplay();
break;
case DISPLAY_INIT_PARTIAL:
case DISPLAY_INIT_FULL:
IoTTimerDim();
IoTTimerClearDisplay();
break;
}
}
void IoTTimerDrawStringAt(uint32_t x, uint32_t y, const char *str, uint32_t color = 0, uint32_t flag = 0);
void IoTTimerDrawStringAt(uint32_t x, uint32_t y, const char *str, uint32_t color, uint32_t flag) {
// displaytext [x] = 0123456789
// Considers display as 1111223334 where 1111 is large white display,
// 22 is small white display,
// 333 is green display,
// 4 is "1" = pm, "2" = alarm, "3" = both
// Following also works - notice scattered dot(.), colon(:), minus(-) or plus(+)
// displaytext 12:34:56.7.8.9ab - Show all lights including "pm" (=a) and "alarm" (=b)
// displaytext 12:34:56 - Show time
// displaytext 05-11-24 - Show date
// displaytext [x6]12.3 - Show value in green leds
// displaytext [ztS] - Clear display and show current time with seconds
bool alarm = false;
bool pm = false;
bool dot_left_up = false;
bool dot_left_dn = false;
bool dot_right_dn = false;
bool dot_right_up_left = false;
bool dot_right_up_right = false;
bool dash_left = false;
bool dash_right = false;
char chr;
uint32_t idx = x;
const char *pos = str;
while (*pos) {
chr = *pos & 0x7F; // We only support 0 to 127
switch (idx) {
case 0:
IoTTimerDisplay[12] = IoTTimerDisplay[13] = IoTTimerFont[chr]; // col 0
break;
case 1:
IoTTimerDisplay[14] = IoTTimerDisplay[15] = IoTTimerFont[chr]; // col 1
break;
case 2:
if (('.' == chr) || (':' == chr) || ('-' == chr) || ('+' == chr)) {
if ('.' == chr) {
dot_left_dn = true;
}
else if (':' == chr) {
dot_left_up = true;
dot_left_dn = true;
}
else if ('-' == chr) {
dash_left = true;
dash_right = true;
}
else if ('+' == chr) {
dot_left_up = true;
dot_left_dn = true;
dash_left = true;
dash_right = true;
}
idx--;
} else {
IoTTimerDisplay[4] = IoTTimerDisplay[5] = IoTTimerFont[chr]; // col 2
}
break;
case 3:
IoTTimerDisplay[11] = IoTTimerDisplay[1] = IoTTimerFont[chr]; // col 3
break;
case 4:
if (('.' == chr) || (':' == chr) || ('-' == chr)) {
idx--; // Skip
} else {
IoTTimerDisplay[6] = IoTTimerFont[chr]; // col 4
}
break;
case 5:
IoTTimerDisplay[7] = IoTTimerFont[chr]; // col 5
break;
case 6:
if ('.' == chr) {
dot_right_dn = true;
idx--;
} else {
// Upper right (green)
IoTTimerDisplay[9] = IoTTimerFont[chr]; // col 6
}
break;
case 7:
if ('.' == chr) {
dot_right_up_left = true;
idx--;
} else {
IoTTimerDisplay[10] = IoTTimerFont[chr]; // col 7
}
break;
case 8:
if ('.' == chr) {
dot_right_up_right = true;
idx--;
} else {
IoTTimerDisplay[8] = IoTTimerFont[chr]; // col 8
}
break;
case 9: // col 9
if (chr & 0x01) { // 1, A, a
pm = true;
}
if (chr & 0x02) { // 2, B, b
alarm = true;
}
break;
}
idx++;
pos++;
}
// Dots and dash
if (alarm) {
IoTTimerDisplay[12] |= 1 << IOTTIMER_DOT_BIT; // Alarm symbol
}
if (pm) {
IoTTimerDisplay[13] |= 1 << IOTTIMER_DOT_BIT; // PM
}
if (dot_left_up) {
IoTTimerDisplay[14] |= 1 << IOTTIMER_DOT_BIT; // Upper dot
}
if (dot_left_dn) {
IoTTimerDisplay[4] |= 1 << IOTTIMER_DOT_BIT; // Lower dot
}
if (dot_right_dn) {
IoTTimerDisplay[7] |= 1 << IOTTIMER_DOT_BIT; // Blue dot
}
if (dot_right_up_left) {
IoTTimerDisplay[10] |= 1 << IOTTIMER_DOT_BIT; // Green dot left
}
if (dot_right_up_right) {
IoTTimerDisplay[8] |= 1 << IOTTIMER_DOT_BIT; // Green dot right
}
if (dash_left) {
IoTTimerDisplay[5] |= 1 << IOTTIMER_DOT_BIT;
}
if (dash_right) {
IoTTimerDisplay[15] |= 1 << IOTTIMER_DOT_BIT;
}
TM1640SendDataArray(0, IoTTimerDisplay, IOTTIMER_DIGITS);
}
/*********************************************************************************************\
* Displays floating point number in the upper right sub-display of the IOTTIMER.
* Format is always "[n]n[.]n" (negative number is "-n[.]n")
\*********************************************************************************************/
void IoTTimerShowFloat(float f) {
/*
char buffer[16];
ext_snprintf_P(buffer, sizeof(buffer),PSTR("%1_f"), &f);
IoTTimerDrawStringAt(6, 0, buffer);
*/
bool negative = false;
float threshold = 99.95;
if (f < 0.0) {
f = -f;
negative = true;
threshold = 9.95;
}
uint8_t precision = 0;
if (f < threshold) {
f *= 10.0;
precision++;
}
uint32_t n = (uint32_t) (f + 0.5);
char buffer[5] = { 0 };
if (negative) {
if (n > 99) {
n = 99;
}
buffer[0] = '-';
} else {
if (n > 999) {
n = 999;
}
if (n / 100 != 0) {
buffer[0] = '0' + n / 100;
}
}
buffer[1] = '0' + n % 100 / 10;
uint32_t idx = 2;
if (precision == 1) {
buffer[2] = '.';
idx++;
}
buffer[idx] = '0' + n % 10;
IoTTimerDrawStringAt(6, 0, buffer);
}
/*********************************************************************************************\
* Update the temperature in the upper right corner.
\*********************************************************************************************/
void IoTTimerUpdateTemperature(void) {
IoTTimerShowFloat(ConvertTempToFahrenheit(TasmotaGlobal.temperature_celsius));
}
/*********************************************************************************************\
* Adjust the brightness based on the photo diode voltage.
\*********************************************************************************************/
void IoTTimerAdjustBrightness(void) {
// Max ADC value is 3400 [lx], but that is only reached in direct sun light.
// 20 is already quite bright.
// Illuminance value of 0 should map to level 1 (level 0 is off)
static float filteredLevel = 1.0;
float level = (float) AdcGetLux(0) / (20.0 / 7.0) + 1.0;
if (level > 8.0) level = 8.0;
if (level < 1.0) level = 1.0;
filteredLevel = 0.9 * filteredLevel + 0.1 * (float) level;
TM1640SetBrightness ((int) (filteredLevel + 0.5));
}
#ifdef USE_DISPLAY_MODES1TO5
/*********************************************************************************************\
* Show the current time
\*********************************************************************************************/
void IoTTimerShowTime(void) {
uint8_t hour = RtcTime.hour;
uint8_t min = RtcTime.minute;
uint8_t sec = RtcTime.second;
uint8_t symbol = 0;
if (!Tm1640->clock_24) {
if (hour > 12) {
hour -= 12;
}
if (hour == 0) {
hour = 12;
}
symbol |= 1;
}
char buffer[16];
snprintf_P(buffer, sizeof(buffer), PSTR("%2d:%02d"), hour, min);
if (Tm1640->clock_seconds) {
snprintf_P(buffer, sizeof(buffer), PSTR("%s:%02d"), buffer, sec);
}
IoTTimerDrawStringAt(0, 0, buffer);
if (Settings->timer[0].arm) {
symbol |= 2;
}
snprintf_P(buffer, sizeof(buffer), PSTR("%d"), symbol);
IoTTimerDrawStringAt(9, 0, buffer);
}
/*********************************************************************************************\
* Show the current date
\*********************************************************************************************/
void IoTTimerShowDate(void)
{
uint8_t left = RtcTime.day_of_month;
uint8_t middle = RtcTime.month;
uint8_t right = RtcTime.year % 100;
if (!Tm1640->clock_24) {
// Use U.S. date format.
left = RtcTime.month;
middle = RtcTime.day_of_month;
}
char buffer[16];
snprintf_P(buffer, sizeof(buffer), PSTR("%02d-%02d-%02d"), left, middle, right);
IoTTimerDrawStringAt(0, 0, buffer);
}
void IoTTimerRefresh(void) { // Every second
// Update temperature display content:
IoTTimerUpdateTemperature();
// Auto-adjust brightness:
IoTTimerAdjustBrightness();
if (Settings->display_mode) { // Mode 0 is User text
switch (Settings->display_mode) {
case 1: // Time
IoTTimerShowTime();
break;
case 2: // Date
IoTTimerShowDate();
break;
case 3: // Time/Date
if (TasmotaGlobal.uptime % Settings->display_refresh)
{
IoTTimerShowTime();
}
else
{
IoTTimerShowDate();
}
break;
case 4:
case 5:
// not in use
break;
}
}
}
#endif // USE_DISPLAY_MODES1TO5
/*********************************************************************************************\
* Displays a clock.
* Command: DisplayClock 1 // 12-hour format
* DisplayClock 2 // 24-hour format
* DisplayClock 3 // 12-hour without seconds
* DisplayClock 4 // 24-hour without seconds
\*********************************************************************************************/
void CmndIoTTimerClock(void) {
uint16_t val = XdrvMailbox.payload;
if (ArgC() == 0)
val = 0;
if ((val < 1) || (val > 4))
return;
if (val == 1) {
Tm1640->show_clock = true;
Tm1640->clock_24 = false;
Tm1640->clock_seconds = true;
}
else if (val == 2) {
Tm1640->show_clock = true;
Tm1640->clock_24 = true;
Tm1640->clock_seconds = true;
}
else if (val == 3) {
Tm1640->show_clock = true;
Tm1640->clock_24 = false;
Tm1640->clock_seconds = false;
}
else if (val == 4) {
Tm1640->show_clock = true;
Tm1640->clock_24 = true;
Tm1640->clock_seconds = false;
} else {
Tm1640->show_clock = false;
Tm1640->clock_24 = false;
}
IoTTimerClearDisplay();
}
#endif // USE_IOTTIMER
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdsp13(uint32_t function) {
bool result = false;
if (FUNC_DISPLAY_INIT_DRIVER == function) {
TM1640Init();
}
else if (Tm1640 && (XDSP_13 == Settings->display_model)) {
#ifdef USE_IOTTIMER
switch (function) {
case FUNC_DISPLAY_INIT:
IoTTimerInit(dsp_init);
break;
#ifdef USE_DISPLAY_MODES1TO5
case FUNC_DISPLAY_EVERY_SECOND:
IoTTimerRefresh();
break;
#endif // USE_DISPLAY_MODES1TO5
case FUNC_DISPLAY_MODEL:
result = true;
break;
case FUNC_DISPLAY_CLOCK:
CmndIoTTimerClock();
break;
case FUNC_DISPLAY_CLEAR:
IoTTimerClearDisplay();
break;
case FUNC_DISPLAY_NUMBER:
case FUNC_DISPLAY_NUMBERNC:
case FUNC_DISPLAY_FLOAT:
case FUNC_DISPLAY_FLOATNC:
IoTTimerShowFloat(CharToFloat(XdrvMailbox.data));
break;
case FUNC_DISPLAY_DIM:
IoTTimerDim();
break;
case FUNC_DISPLAY_POWER:
IoTTimerDisplayOnOff();
break;
case FUNC_DISPLAY_DRAW_STRING:
IoTTimerDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag);
break;
}
#endif // USE_IOTTIMER
}
return result;
}
#endif // USE_DISPLAY_TM1640
#endif // USE_DISPLAY