mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-10-26 22:10:45 +00:00
393 lines
13 KiB
Arduino
393 lines
13 KiB
Arduino
#include "_Plugin_Helper.h"
|
|
#ifdef USES_P062
|
|
|
|
// #######################################################################################################
|
|
// #################################### Plugin 062: MPR121 KeyPad ########################################
|
|
// #######################################################################################################
|
|
|
|
// ESPEasy Plugin to scan a 12 key touch pad chip MPR121
|
|
// written by Jochen Krapf (jk@nerd2nerd.org)
|
|
|
|
/** Changelog:
|
|
* 2025-06-14 tonhuisman: Add support for Custom Value Type per task value
|
|
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Keypad)
|
|
* Update changelog
|
|
* 2021-12-29 tonhuisman: Add setting for panel sensitivity, as requested in https://github.com/letscontrolit/ESPEasy/issues/3828
|
|
* Reformat source using Uncrustify
|
|
* 2020-10-14 tonhuisman: Added settings for global and per-sensor sensitivity
|
|
* and getting 'calibration' touch pressure data (current, min, max)
|
|
*/
|
|
|
|
// ScanCode;
|
|
// Value 1...12 for the key number
|
|
// No key - the code 0
|
|
// If more than one key is pressed, the scan code is the code with the lowest value
|
|
|
|
// If ScanCode is unchecked the value is the KeyMap 1.Key=1, 2.Key=2, 3.Key=4, 4.Key=8 ... 1.Key=2048
|
|
// If more than one key is pressed, the value is sum of all KeyMap-values
|
|
|
|
|
|
# define PLUGIN_062
|
|
# define PLUGIN_ID_062 62
|
|
# define PLUGIN_NAME_062 "Keypad - MPR121 Touch"
|
|
# define PLUGIN_VALUENAME1_062 "ScanCode"
|
|
|
|
|
|
# include "src/PluginStructs/P062_data_struct.h"
|
|
|
|
# define P062_FLAGS_USE_CALIBRATION 0 // Set in P062_CONFIG_FLAGS
|
|
|
|
# define P062_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags
|
|
|
|
# define P062_DEFAULT_TOUCH_TRESHOLD 12 // Defaults got from MPR_121 source
|
|
# define P062_DEFAULT_RELEASE_TRESHOLD 6
|
|
|
|
boolean Plugin_062(uint8_t function, struct EventStruct *event, String& string)
|
|
{
|
|
boolean success = false;
|
|
|
|
switch (function)
|
|
{
|
|
case PLUGIN_DEVICE_ADD:
|
|
{
|
|
auto& dev = Device[++deviceCount];
|
|
dev.Number = PLUGIN_ID_062;
|
|
dev.Type = DEVICE_TYPE_I2C;
|
|
dev.VType = Sensor_VType::SENSOR_TYPE_SWITCH;
|
|
dev.ValueCount = 1;
|
|
dev.SendDataOption = true;
|
|
dev.TimerOption = true;
|
|
dev.TimerOptional = true;
|
|
dev.ExitTaskBeforeSave = false;
|
|
dev.CustomVTypeVar = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_DEVICENAME:
|
|
{
|
|
string = F(PLUGIN_NAME_062);
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_GET_DEVICEVALUENAMES:
|
|
{
|
|
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_062));
|
|
break;
|
|
}
|
|
|
|
# if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
|
|
case PLUGIN_GET_DISCOVERY_VTYPES:
|
|
{
|
|
# if FEATURE_CUSTOM_TASKVAR_VTYPE
|
|
|
|
for (uint8_t i = 0; i < event->Par5; ++i) {
|
|
event->ParN[i] = ExtraTaskSettings.getTaskVarCustomVType(i); // Custom/User selection
|
|
}
|
|
# else // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
|
event->Par1 = static_cast<int>(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported
|
|
# endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
|
|
success = true;
|
|
break;
|
|
}
|
|
# endif // if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
|
|
|
|
case PLUGIN_I2C_HAS_ADDRESS:
|
|
case PLUGIN_WEBFORM_SHOW_I2C_PARAMS:
|
|
{
|
|
const uint8_t i2cAddressValues[] = { 0x5A, 0x5B, 0x5C, 0x5D };
|
|
|
|
if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) {
|
|
addFormSelectorI2C(F("i2c_addr"), 4, i2cAddressValues, PCONFIG(0));
|
|
} else {
|
|
success = intArrayContains(4, i2cAddressValues, event->Par1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
# if FEATURE_I2C_GET_ADDRESS
|
|
case PLUGIN_I2C_GET_ADDRESS:
|
|
{
|
|
event->Par1 = PCONFIG(0);
|
|
success = true;
|
|
break;
|
|
}
|
|
# endif // if FEATURE_I2C_GET_ADDRESS
|
|
|
|
case PLUGIN_WEBFORM_LOAD:
|
|
{
|
|
addFormCheckBox(F("ScanCode"), F("scancode"), PCONFIG(1));
|
|
|
|
addFormSubHeader(F("Sensitivity"));
|
|
{
|
|
uint8_t touch_treshold = PCONFIG(2);
|
|
|
|
if (touch_treshold == 0) {
|
|
touch_treshold = P062_DEFAULT_TOUCH_TRESHOLD; // default value
|
|
}
|
|
addFormNumericBox(F("Touch treshold (1..255)"), F("ttreshold"), touch_treshold, 0, 255);
|
|
addUnit(concat(F("Default: "), P062_DEFAULT_TOUCH_TRESHOLD));
|
|
}
|
|
|
|
{
|
|
uint8_t release_treshold = PCONFIG(3);
|
|
|
|
if (release_treshold == 0) {
|
|
release_treshold = P062_DEFAULT_RELEASE_TRESHOLD; // default value
|
|
}
|
|
addFormNumericBox(F("Release treshold (1..255)"), F("rtreshold"), release_treshold, 0, 255);
|
|
addUnit(concat(F("Default: "), P062_DEFAULT_RELEASE_TRESHOLD));
|
|
}
|
|
{
|
|
const __FlashStringHelper *sensitivityOptions[] = {
|
|
F("Normal"),
|
|
F("Extra sensitive (behind 4-6mm glass cover)")
|
|
};
|
|
const int sensitivityValues[] = {
|
|
MPR212_NORMAL_SENSITIVITY,
|
|
MPR212_EXTRA_SENSITIVITY
|
|
};
|
|
constexpr size_t optionCount = NR_ELEMENTS(sensitivityValues);
|
|
const FormSelectorOptions selector(optionCount, sensitivityOptions, sensitivityValues);
|
|
selector.addFormSelector(F("Panel sensitivity"), F("psens"), PCONFIG(4));
|
|
}
|
|
{
|
|
bool canCalibrate = true;
|
|
bool tbUseCalibration = bitRead(P062_CONFIG_FLAGS, P062_FLAGS_USE_CALIBRATION);
|
|
|
|
P062_data_struct *P062_data = static_cast<P062_data_struct *>(getPluginTaskData(event->TaskIndex));
|
|
|
|
if (nullptr == P062_data) {
|
|
P062_data = new (std::nothrow) P062_data_struct();
|
|
canCalibrate = false;
|
|
|
|
if (P062_data == nullptr) {
|
|
return success;
|
|
}
|
|
}
|
|
P062_data->loadTouchObjects(event->TaskIndex);
|
|
|
|
addRowLabel(F("Object"));
|
|
html_table(EMPTY_STRING, false); // Sub-table
|
|
html_table_header(F(" # "));
|
|
html_table_header(F("Touch (0..255)"));
|
|
html_table_header(F("Release (0..255)"));
|
|
|
|
if (tbUseCalibration && canCalibrate) {
|
|
html_table_header(F("Current"));
|
|
html_table_header(F("Min"));
|
|
html_table_header(F("Max"));
|
|
}
|
|
|
|
for (int objectNr = 0; objectNr < P062_MaxTouchObjects; ++objectNr) {
|
|
html_TR_TD();
|
|
addHtml(F(" "));
|
|
addHtmlInt(objectNr + 1);
|
|
html_TD();
|
|
addNumericBox(getPluginCustomArgName(objectNr + 100), P062_data->StoredSettings.TouchObjects[objectNr].touch, 0, 255);
|
|
html_TD();
|
|
addNumericBox(getPluginCustomArgName(objectNr + 200), P062_data->StoredSettings.TouchObjects[objectNr].release, 0, 255);
|
|
|
|
if (tbUseCalibration && canCalibrate) {
|
|
uint16_t current = 0;
|
|
uint16_t min = 0;
|
|
uint16_t max = 0;
|
|
P062_data->getCalibrationData(objectNr, ¤t, &min, &max);
|
|
html_TD();
|
|
addHtmlInt(current);
|
|
html_TD();
|
|
addHtmlInt(min);
|
|
html_TD();
|
|
addHtmlInt(max);
|
|
}
|
|
}
|
|
html_end_table();
|
|
|
|
if (canCalibrate) {
|
|
const int choice1 = tbUseCalibration ? 1 : 0;
|
|
addFormSelector_YesNo(F("Enable Calibration"), F("ucal"), choice1, true);
|
|
|
|
if (tbUseCalibration) {
|
|
addFormCheckBox(F("Clear calibrationdata"), F("clr_cal"), false);
|
|
}
|
|
}
|
|
delete P062_data;
|
|
}
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_WEBFORM_SAVE:
|
|
{
|
|
PCONFIG(0) = getFormItemInt(F("i2c_addr"));
|
|
|
|
PCONFIG(1) = isFormItemChecked(F("scancode"));
|
|
|
|
PCONFIG(2) = getFormItemInt(F("ttreshold"));
|
|
PCONFIG(3) = getFormItemInt(F("rtreshold"));
|
|
PCONFIG(4) = getFormItemInt(F("psens"));
|
|
|
|
uint32_t lSettings = 0;
|
|
bitWrite(lSettings, P062_FLAGS_USE_CALIBRATION, getFormItemInt(F("ucal")) == 1);
|
|
P062_CONFIG_FLAGS = lSettings;
|
|
|
|
{
|
|
bool canCalibrate = true;
|
|
P062_data_struct *P062_data = static_cast<P062_data_struct *>(getPluginTaskData(event->TaskIndex));
|
|
|
|
if (nullptr == P062_data) {
|
|
P062_data = new (std::nothrow) P062_data_struct();
|
|
canCalibrate = false;
|
|
|
|
if (P062_data == nullptr) {
|
|
return success; // Save other settings even though this didn't initialize properly
|
|
}
|
|
}
|
|
P062_data->loadTouchObjects(event->TaskIndex);
|
|
|
|
for (int objectNr = 0; objectNr < P062_MaxTouchObjects; ++objectNr) {
|
|
P062_data->StoredSettings.TouchObjects[objectNr].touch = getFormItemIntCustomArgName(objectNr + 100);
|
|
P062_data->StoredSettings.TouchObjects[objectNr].release = getFormItemIntCustomArgName(objectNr + 200);
|
|
}
|
|
# ifdef PLUGIN_062_DEBUG
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
addLogMove(LOG_LEVEL_INFO, strformat(F("P062_data save size: %u"), sizeof(P062_data->StoredSettings)));
|
|
}
|
|
# endif // PLUGIN_062_DEBUG
|
|
SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast<const uint8_t *>(&(P062_data->StoredSettings)),
|
|
sizeof(P062_data->StoredSettings));
|
|
|
|
if (!canCalibrate) {
|
|
delete P062_data;
|
|
P062_data = nullptr;
|
|
} else {
|
|
const bool clearCalibration = isFormItemChecked(F("clr_cal"));
|
|
|
|
if (clearCalibration) {
|
|
P062_data->clearCalibrationData();
|
|
# ifdef PLUGIN_062_DEBUG
|
|
addLog(LOG_LEVEL_INFO, F("p062 clear calibration"));
|
|
# endif // PLUGIN_062_DEBUG
|
|
}
|
|
}
|
|
}
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_INIT:
|
|
{
|
|
bool tbUseCalibration = bitRead(P062_CONFIG_FLAGS, P062_FLAGS_USE_CALIBRATION);
|
|
|
|
initPluginTaskData(event->TaskIndex, new (std::nothrow) P062_data_struct());
|
|
P062_data_struct *P062_data = static_cast<P062_data_struct *>(getPluginTaskData(event->TaskIndex));
|
|
|
|
if (nullptr != P062_data) {
|
|
if (!P062_data->init(event->TaskIndex, PCONFIG(0), PCONFIG(1), tbUseCalibration, PCONFIG(4))) {
|
|
clearPluginTaskData(event->TaskIndex);
|
|
P062_data = nullptr;
|
|
} else {
|
|
success = true;
|
|
|
|
uint8_t touch_treshold = PCONFIG(2);
|
|
|
|
if (touch_treshold == 0) {
|
|
touch_treshold = P062_DEFAULT_TOUCH_TRESHOLD; // default value
|
|
}
|
|
uint8_t release_treshold = PCONFIG(3);
|
|
|
|
if (release_treshold == 0) {
|
|
release_treshold = P062_DEFAULT_RELEASE_TRESHOLD; // default value
|
|
}
|
|
|
|
if ((touch_treshold != P062_DEFAULT_TOUCH_TRESHOLD) && (release_treshold != P062_DEFAULT_RELEASE_TRESHOLD)) {
|
|
P062_data->setThresholds(touch_treshold, release_treshold); // Set custom tresholds, ignore default values
|
|
}
|
|
|
|
for (uint8_t objectNr = 0; objectNr < P062_MaxTouchObjects; ++objectNr) {
|
|
if ((P062_data->StoredSettings.TouchObjects[objectNr].touch != 0) &&
|
|
(P062_data->StoredSettings.TouchObjects[objectNr].release != 0)) {
|
|
P062_data->setThreshold(objectNr,
|
|
P062_data->StoredSettings.TouchObjects[objectNr].touch,
|
|
P062_data->StoredSettings.TouchObjects[objectNr].release);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_TEN_PER_SECOND:
|
|
{
|
|
P062_data_struct *P062_data =
|
|
static_cast<P062_data_struct *>(getPluginTaskData(event->TaskIndex));
|
|
|
|
if (nullptr == P062_data) {
|
|
return success;
|
|
} else {
|
|
uint16_t key;
|
|
|
|
if (P062_data->readKey(key))
|
|
{
|
|
UserVar.setFloat(event->TaskIndex, 0, key);
|
|
event->sensorType = Sensor_VType::SENSOR_TYPE_SWITCH;
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
String log = F("Tkey : ");
|
|
log.reserve(22);
|
|
|
|
if (PCONFIG(1)) {
|
|
log += F("ScanCode=");
|
|
}
|
|
else {
|
|
log += F("KeyMap=");
|
|
}
|
|
log += formatToHex(key);
|
|
addLogMove(LOG_LEVEL_INFO, log);
|
|
|
|
bool tbUseCalibration = bitRead(P062_CONFIG_FLAGS, P062_FLAGS_USE_CALIBRATION);
|
|
|
|
if (tbUseCalibration) {
|
|
uint16_t colMask = 0x01;
|
|
|
|
for (uint8_t col = 0; col < P062_MaxTouchObjects; ++col)
|
|
{
|
|
if (key & colMask) // this key pressed?
|
|
{
|
|
uint16_t current = 0;
|
|
uint16_t min = 0;
|
|
uint16_t max = 0;
|
|
P062_data->getCalibrationData(col, ¤t, &min, &max);
|
|
#ifndef BUILD_NO_DEBUG
|
|
addLog(LOG_LEVEL_INFO,
|
|
strformat(F("P062 touch #%d current: %d min: %d max: %d"),
|
|
col, current, min, max));
|
|
#endif
|
|
if (!PCONFIG(1)) {
|
|
break;
|
|
}
|
|
}
|
|
colMask <<= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
sendData(event);
|
|
}
|
|
}
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
case PLUGIN_READ:
|
|
{
|
|
success = true;
|
|
break;
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
#endif // USES_P062
|