mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-12 09:36:59 +00:00
403 lines
12 KiB
Arduino
403 lines
12 KiB
Arduino
#include "_Plugin_Helper.h"
|
|
#ifdef USES_P057
|
|
|
|
// #######################################################################################################
|
|
// #################################### Plugin 057: HT16K33 LED ##########################################
|
|
// #######################################################################################################
|
|
|
|
// ESPEasy Plugin to control a 16x8 LED matrix or 8 7-segment displays with chip HT16K33
|
|
// written by Jochen Krapf (jk@nerd2nerd.org)
|
|
|
|
// List of commands:
|
|
// (1) M,<param>,<param>,<param>, ... with decimal values
|
|
// (2) MX,<param>,<param>,<param>, ... with hexadecimal values
|
|
// (3) MNUM,<param>,<param>,<param>, ... with decimal values for 7-segment displays
|
|
// (4) MPRINT,<text> with decimal values for 7-segment displays
|
|
// (5) MBR,<0-15> set display brightness, between 0 and 15
|
|
|
|
// List of M* params:
|
|
// (a) <value>
|
|
// Writes a decimal / hexadecimal (0...0xFFFF) values to actual segment starting with 0
|
|
// (b) <seg>=<value>
|
|
// Writes a decimal / hexadecimal (0...0xFFFF) values to given segment (0...7)
|
|
// (c) "CLEAR"
|
|
// Set all LEDs to 0.
|
|
// (d) "TEST"
|
|
// Set test pattern to LED buffer.
|
|
// (e) "LOG"
|
|
// Print LED buffer to log output.
|
|
|
|
// Examples:
|
|
// MX,AA,55,AA,55,AA,55,AA,55 Set chess pattern to LED buffer
|
|
// MNUM,CLEAR,1,0 Clear the LED buffer and then set 0x06 to 1st segment and 0x3F to 2nd segment
|
|
|
|
// Connecting LEDs to HT16K33-board:
|
|
// Cathode for Column 0 = C0
|
|
// Cathode for Column 1 = C1
|
|
// ...
|
|
// Cathode for Column 7 = C7
|
|
//
|
|
// Anode for bit 0x0001 = A0
|
|
// Anode for bit 0x0002 = A1
|
|
// ...
|
|
// Anode for bit 0x0080 = A7
|
|
// ...
|
|
// Anode for bit 0x8000 = A15
|
|
|
|
// Note: The HT16K33-LED-plugin and the HT16K33-key-plugin can be used at the same time with the same I2C address
|
|
|
|
// Clock Display:
|
|
// This plugin also allows a "clock" mode. In clock mode the display will show
|
|
// the current system time. The "7-Seg. Clock" needs to be configured for this
|
|
// mode to work. Each segment number (0..5) needs to be set based on your
|
|
// display.
|
|
//
|
|
// For my _Adafruit 0.56" 4-Digit 7-Segment FeatherWing Display_ these
|
|
// settings are as follows:
|
|
// Xx:xx = 0, xX:xx = 1,
|
|
// xx:Xx = 3, xx:xX = 4
|
|
// Seg. for Colon is 2 with a value of 2
|
|
//
|
|
// Any other data written to the display will show and be replaced at the next
|
|
// clock cycle, e.g. when the plugin received 'PLUGIN_CLOCK_IN'.
|
|
//
|
|
// NOTE: The system time is set via NTP as part of the Core ESPEasy firmware.
|
|
// There is no configuration here to set or manipulate the time, only to
|
|
// display it.
|
|
|
|
#define PLUGIN_057
|
|
#define PLUGIN_ID_057 57
|
|
#define PLUGIN_NAME_057 "Display - HT16K33 [TESTING]"
|
|
|
|
|
|
#include "src/PluginStructs/P057_data_struct.h"
|
|
|
|
boolean Plugin_057(uint8_t function, struct EventStruct *event, String& string)
|
|
{
|
|
boolean success = false;
|
|
|
|
switch (function)
|
|
{
|
|
case PLUGIN_DEVICE_ADD:
|
|
{
|
|
Device[++deviceCount].Number = PLUGIN_ID_057;
|
|
Device[deviceCount].Type = DEVICE_TYPE_I2C;
|
|
Device[deviceCount].Ports = 0;
|
|
Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE;
|
|
Device[deviceCount].PullUpOption = false;
|
|
Device[deviceCount].InverseLogicOption = false;
|
|
Device[deviceCount].FormulaOption = false;
|
|
Device[deviceCount].ValueCount = 0;
|
|
Device[deviceCount].SendDataOption = false;
|
|
Device[deviceCount].TimerOption = false;
|
|
Device[deviceCount].TimerOptional = false;
|
|
Device[deviceCount].GlobalSyncOption = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_DEVICENAME:
|
|
{
|
|
string = F(PLUGIN_NAME_057);
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_WEBFORM_SHOW_I2C_PARAMS:
|
|
{
|
|
uint8_t addr = PCONFIG(0);
|
|
|
|
int optionValues[8] = { 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77 };
|
|
addFormSelectorI2C(F("i2c_addr"), 8, optionValues, addr);
|
|
break;
|
|
}
|
|
|
|
|
|
case PLUGIN_WEBFORM_LOAD:
|
|
{
|
|
addFormSubHeader(F("7-Seg. Clock"));
|
|
|
|
{
|
|
int16_t choice = PCONFIG(1);
|
|
const __FlashStringHelper * options[3] = { F("none"), F("7-Seg. HH:MM (24 hour)"), F("7-Seg. HH:MM (12 hour)") };
|
|
addFormSelector(F("Clock Type"), F("clocktype"), 3, options, NULL, choice);
|
|
}
|
|
|
|
addFormNumericBox(F("Seg. for <b>X</b>x:xx"), F("clocksegh10"), PCONFIG(2), 0, 7);
|
|
addFormNumericBox(F("Seg. for x<b>X</b>:xx"), F("clocksegh1"), PCONFIG(3), 0, 7);
|
|
addFormNumericBox(F("Seg. for xx:<b>X</b>x"), F("clocksegm10"), PCONFIG(4), 0, 7);
|
|
addFormNumericBox(F("Seg. for xx:x<b>X</b>"), F("clocksegm1"), PCONFIG(5), 0, 7);
|
|
|
|
addFormNumericBox(F("Seg. for Colon"), F("clocksegcol"), PCONFIG(6), -1, 7);
|
|
addHtml(F(" Value "));
|
|
addNumericBox(F("clocksegcolval"), PCONFIG(7), 0, 255);
|
|
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_WEBFORM_SAVE:
|
|
{
|
|
PCONFIG(0) = getFormItemInt(F("i2c_addr"));
|
|
|
|
PCONFIG(1) = getFormItemInt(F("clocktype"));
|
|
|
|
PCONFIG(2) = getFormItemInt(F("clocksegh10"));
|
|
PCONFIG(3) = getFormItemInt(F("clocksegh1"));
|
|
PCONFIG(4) = getFormItemInt(F("clocksegm10"));
|
|
PCONFIG(5) = getFormItemInt(F("clocksegm1"));
|
|
PCONFIG(6) = getFormItemInt(F("clocksegcol"));
|
|
PCONFIG(7) = getFormItemInt(F("clocksegcolval"));
|
|
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_INIT:
|
|
{
|
|
uint8_t address = PCONFIG(0);
|
|
|
|
initPluginTaskData(event->TaskIndex, new (std::nothrow) P057_data_struct(address));
|
|
P057_data_struct *P057_data =
|
|
static_cast<P057_data_struct *>(getPluginTaskData(event->TaskIndex));
|
|
|
|
if (nullptr != P057_data) {
|
|
success = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_WRITE:
|
|
{
|
|
P057_data_struct *P057_data =
|
|
static_cast<P057_data_struct *>(getPluginTaskData(event->TaskIndex));
|
|
|
|
if (nullptr == P057_data) {
|
|
return false;
|
|
}
|
|
|
|
String command = parseString(string, 1);
|
|
|
|
if (command == F("mprint"))
|
|
{
|
|
String text = parseStringToEnd(string, 2);
|
|
|
|
if (!text.isEmpty()) {
|
|
uint8_t seg = 0;
|
|
uint8_t txt = 0; // Separate indexers for text and segments
|
|
bool setDot;
|
|
|
|
P057_data->ledMatrix.ClearRowBuffer();
|
|
|
|
while (txt < text.length() && text[txt] && seg < 8)
|
|
{
|
|
setDot = (txt < text.length() - 1 && text[txt + 1] == '.');
|
|
char c = text[txt];
|
|
P057_data->ledMatrix.SetDigit(seg, c, setDot);
|
|
seg++;
|
|
txt++;
|
|
if (setDot) { txt++; } // extra increment to skip past the dot
|
|
}
|
|
P057_data->ledMatrix.TransmitRowBuffer();
|
|
success = true;
|
|
}
|
|
}
|
|
else if (command == F("mbr")) {
|
|
String param = parseString(string, 2);
|
|
int brightness;
|
|
|
|
if (validIntFromString(param, brightness)) {
|
|
if ((brightness >= 0) && (brightness <= 255)) {
|
|
P057_data->ledMatrix.SetBrightness(brightness);
|
|
}
|
|
}
|
|
success = true;
|
|
}
|
|
else if ((command == F("m")) || (command == F("mx")) || (command == F("mnum")))
|
|
{
|
|
String param;
|
|
String paramKey;
|
|
String paramVal;
|
|
uint8_t paramIdx = 2;
|
|
uint8_t seg = 0;
|
|
uint16_t value = 0;
|
|
|
|
String lowerString = string;
|
|
lowerString.toLowerCase();
|
|
lowerString.replace(F(" "), " ");
|
|
lowerString.replace(F(" ="), "=");
|
|
lowerString.replace(F("= "), "=");
|
|
|
|
param = parseString(lowerString, paramIdx++);
|
|
|
|
if (param.length())
|
|
{
|
|
while (param.length())
|
|
{
|
|
addLog(LOG_LEVEL_DEBUG_MORE, param);
|
|
|
|
if (param == F("log"))
|
|
{
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
String log = F("MX : ");
|
|
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
{
|
|
log += String(P057_data->ledMatrix.GetRow(i), 16);
|
|
log += F("h, ");
|
|
}
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
}
|
|
success = true;
|
|
}
|
|
|
|
else if (param == F("test"))
|
|
{
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
P057_data->ledMatrix.SetRow(i, 1 << i);
|
|
}
|
|
success = true;
|
|
}
|
|
|
|
else if (param == F("clear"))
|
|
{
|
|
P057_data->ledMatrix.ClearRowBuffer();
|
|
success = true;
|
|
}
|
|
|
|
else
|
|
{
|
|
int index = param.indexOf('=');
|
|
|
|
if (index > 0) // syntax: "<seg>=<value>"
|
|
{
|
|
paramKey = param.substring(0, index);
|
|
paramVal = param.substring(index + 1);
|
|
seg = paramKey.toInt();
|
|
}
|
|
else // syntax: "<value>"
|
|
{
|
|
paramVal = param;
|
|
}
|
|
|
|
if (command == F("mnum"))
|
|
{
|
|
value = paramVal.toInt();
|
|
|
|
if (value < 16) {
|
|
P057_data->ledMatrix.SetDigit(seg, value);
|
|
}
|
|
else {
|
|
P057_data->ledMatrix.SetRow(seg, value);
|
|
}
|
|
}
|
|
else if (command == F("mx"))
|
|
{
|
|
char *ep;
|
|
value = strtol(paramVal.c_str(), &ep, 16);
|
|
P057_data->ledMatrix.SetRow(seg, value);
|
|
}
|
|
else
|
|
{
|
|
value = paramVal.toInt();
|
|
P057_data->ledMatrix.SetRow(seg, value);
|
|
}
|
|
|
|
success = true;
|
|
seg++;
|
|
}
|
|
|
|
param = parseString(lowerString, paramIdx++);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ??? no params
|
|
}
|
|
|
|
if (success) {
|
|
P057_data->ledMatrix.TransmitRowBuffer();
|
|
}
|
|
success = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_CLOCK_IN:
|
|
{
|
|
P057_data_struct *P057_data =
|
|
static_cast<P057_data_struct *>(getPluginTaskData(event->TaskIndex));
|
|
|
|
if (nullptr == P057_data || (PCONFIG(1) == 0)) {
|
|
break;
|
|
}
|
|
|
|
uint8_t hours = node_time.hour();
|
|
uint8_t minutes = node_time.minute();
|
|
|
|
// P057_data->ledMatrix.ClearRowBuffer();
|
|
P057_data->ledMatrix.SetDigit(PCONFIG(5), minutes % 10);
|
|
P057_data->ledMatrix.SetDigit(PCONFIG(4), minutes / 10);
|
|
|
|
if (PCONFIG(1) == 1) { // 24-hour clock
|
|
// 24-hour clock shows leading zero
|
|
P057_data->ledMatrix.SetDigit(PCONFIG(2), hours / 10);
|
|
P057_data->ledMatrix.SetDigit(PCONFIG(3), hours % 10);
|
|
} else if (PCONFIG(1) == 2) { // 12-hour clock
|
|
if (hours < 12) {
|
|
// to set AM marker, get buffer and add decimal to it.
|
|
P057_data->ledMatrix.SetRow(PCONFIG(5), (P057_data->ledMatrix.GetRow(PCONFIG(5)) | 0x80));
|
|
}
|
|
|
|
hours = hours % 12;
|
|
|
|
if (hours == 0) {
|
|
hours = 12;
|
|
}
|
|
|
|
P057_data->ledMatrix.SetDigit(PCONFIG(3), hours % 10);
|
|
|
|
if (hours < 10) {
|
|
// 12-hour clock will show empty segment when hours < 10
|
|
P057_data->ledMatrix.SetRow(PCONFIG(2), 0);
|
|
} else {
|
|
P057_data->ledMatrix.SetDigit(PCONFIG(2), hours / 10);
|
|
}
|
|
}
|
|
|
|
// if (PCONFIG(6) >= 0)
|
|
// P057_data->ledMatrix.SetRow(PCONFIG(6), PCONFIG(7));
|
|
P057_data->ledMatrix.TransmitRowBuffer();
|
|
|
|
success = true;
|
|
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_TEN_PER_SECOND:
|
|
{
|
|
P057_data_struct *P057_data =
|
|
static_cast<P057_data_struct *>(getPluginTaskData(event->TaskIndex));
|
|
|
|
if (nullptr == P057_data || (PCONFIG(1) == 0)) { // clock enabled?
|
|
break;
|
|
}
|
|
|
|
if (PCONFIG(6) >= 0) // colon used?
|
|
{
|
|
uint8_t act = ((uint16_t)millis() >> 9) & 1; // blink with about 2 Hz
|
|
static uint8_t last = 0;
|
|
|
|
if (act != last)
|
|
{
|
|
last = act;
|
|
P057_data->ledMatrix.SetRow(PCONFIG(6), (act) ? PCONFIG(7) : 0);
|
|
P057_data->ledMatrix.TransmitRowBuffer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
#endif // USES_P057
|