diff --git a/docs/source/Plugin/P099.rst b/docs/source/Plugin/P099.rst index 1ccb6edf56..5b8a5f8df8 100644 --- a/docs/source/Plugin/P099.rst +++ b/docs/source/Plugin/P099.rst @@ -30,6 +30,35 @@ Touchscreen support for XPT2046. Some type of TFT screens, particularly the :ref:`P095_page` 65000 color screens, come with a resistive touchscreen using a XPT2046 controller. Usually, a screen pen is included with the purchase. +^^^^^^^^^^^^^^^^^^^ +ESP32 Configuration +^^^^^^^^^^^^^^^^^^^ + +(For ESP8266 configuration, see below) + +Configuration options +^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: P099_DeviceConfiguration_ESP32.png + +Actuator +~~~~~~~~ + +The only connection, besides the standard SPI signals ``MISO``, ``MOSI`` and ``CLK``, is the ``CS`` pin (usually labeled ``TS_CS`` at the connector) that is configured in the device. + +Screen +~~~~~~ + +To allow scaling the touch coordinates to screen coordinates, the screen resolution and (touchscreen) rotation need to be configured, as the 0,0 coordinates of the screen and touchscreen should match, for mapping a touch coordinate to a screen coordinate. The screen dimensions, rotation and color depth of the selected display are retrieved, if the display supports that (most displays do). + + +.. include:: Touch_Configuration.repl + + +^^^^^^^^^^^^^^^^^^^^^ +ESP8266 Configuration +^^^^^^^^^^^^^^^^^^^^^ + Configuration options ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/Plugin/P099_DeviceConfiguration_ESP32.png b/docs/source/Plugin/P099_DeviceConfiguration_ESP32.png new file mode 100644 index 0000000000..e92836a8fe Binary files /dev/null and b/docs/source/Plugin/P099_DeviceConfiguration_ESP32.png differ diff --git a/docs/source/Plugin/Touch_DeviceConfiguration.png b/docs/source/Plugin/Touch_DeviceConfiguration.png index 6b40df6560..d55c76096c 100644 Binary files a/docs/source/Plugin/Touch_DeviceConfiguration.png and b/docs/source/Plugin/Touch_DeviceConfiguration.png differ diff --git a/src/_P099_XPT2046Touch.ino b/src/_P099_XPT2046Touch.ino index a00235c7a1..dc3134b398 100644 --- a/src/_P099_XPT2046Touch.ino +++ b/src/_P099_XPT2046Touch.ino @@ -1,517 +1,781 @@ -#ifdef USES_P099 - -// ####################################################################################################### -// #################################### Plugin 099: XPT2046 TFT Touchscreen ################################# -// ####################################################################################################### - -/** - * Changelog: - * 2025-08-13 tonhuisman: Enable use of secondary SPI bus - * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Touch) - * 2020-11-01 tonhuisman: Solved previous strange rotation settings to be compatible with TFT ILI9341 - * 2020-11-01 tonhuisman: Add option to flip rotation by 180 deg, and command touch,flip,<0|1> - * 2020-11-01 tonhuisman: Add option for the debounce timeout for On/Off buttons - * 2020-11-01 tonhuisman: Some code cleanup and string optimizations - * 2020-09-23/24/25 tonhuisman: Add object disable/enable commands, add On/Off button mode, and inverted, for touchobjects - * 2020-09-10 tonhuisman: Clean up code, testing - * 2020-09-07/08/09 tonhuisman: Fix code issues - * 2020-09-06 tonhuisman: Add object 'layering' so the 'top-most' object only sends an event - * 2020-09-05 tonhuisman: Add touch to touchobject mapping, generate events - * 2020-09-03 tonhuisman: Add touchobject settings - * 2020-08-31 tonhuisman: Add Calibration settings - * 2020-08-30 tonhuisman: Add settings and 2/3 event support - * 2020-08-29 tonhuisman: Initial plugin, based on XPT2046_Touchscreen by Paul Stoffregen from - * https://github.com/PaulStoffregen/XPT2046_Touchscreen - */ - -/** - * Commands supported: - * ------------------- - * touch,rot,<0..3> : Set rotation to 0(0), 90(1), 180(2), 270(3) degrees - * touch,flip,<0|1> : Set rotation normal(0) or flipped by 180 degrees(1) - * touch,enable, : Enables a disabled objectname (removes a leading underscore) - * touch,disable, : Disables an enabled objectname (adds a leading underscore) - */ - -#define PLUGIN_099 -#define PLUGIN_ID_099 99 -#define PLUGIN_NAME_099 "Touch - XPT2046 on a TFT display" -#define PLUGIN_VALUENAME1_099 "X" -#define PLUGIN_VALUENAME2_099 "Y" -#define PLUGIN_VALUENAME3_099 "Z" - -#include "_Plugin_Helper.h" -#include "src/PluginStructs/P099_data_struct.h" - - -boolean Plugin_099(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_099; - dev.Type = DEVICE_TYPE_SPI; - dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; - dev.ValueCount = 3; - dev.SpiBusSelect = true; - break; - } - - case PLUGIN_GET_DEVICENAME: - { - string = F(PLUGIN_NAME_099); - success = true; - break; - } - - case PLUGIN_GET_DEVICEVALUENAMES: - { - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_099)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_099)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_099)); - success = true; - break; - } - - case PLUGIN_GET_DEVICEGPIONAMES: - { - event->String1 = formatGpioName_output(F("TS CS")); - break; - } - - #if FEATURE_MQTT_DISCOVER - case PLUGIN_GET_DISCOVERY_VTYPES: - { - event->Par1 = static_cast(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported - success = true; - break; - } - #endif // if FEATURE_MQTT_DISCOVER - - case PLUGIN_SET_DEFAULTS: - { - // if already configured take it from settings, else use default values - if (P099_CONFIG_STATE != 1) { - P099_CONFIG_CS_PIN = P099_TS_CS; - P099_CONFIG_TRESHOLD = P099_TS_TRESHOLD; - P099_CONFIG_ROTATION = P099_TS_ROTATION; - P099_CONFIG_X_RES = P099_TS_X_RES; - P099_CONFIG_Y_RES = P099_TS_Y_RES; - P099_CONFIG_OBJECTCOUNT = P099_INIT_OBJECTCOUNT; - P099_CONFIG_DEBOUNCE_MS = P099_DEBOUNCE_MILLIS; - - constexpr uint32_t lSettings = 0 - + (P099_TS_SEND_XY ? (1 << P099_FLAGS_SEND_XY) : 0) - + (P099_TS_SEND_Z ? (1 << P099_FLAGS_SEND_Z) : 0) - + (P099_TS_SEND_OBJECTNAME ? (1 << P099_FLAGS_SEND_OBJECTNAME) : 0) - + (P099_TS_USE_CALIBRATION ? (1 << P099_FLAGS_USE_CALIBRATION) : 0) - + (P099_TS_LOG_CALIBRATION ? (1 << P099_FLAGS_LOG_CALIBRATION) : 0) - + (P099_TS_ROTATION_FLIPPED ? (1 << P099_FLAGS_ROTATION_FLIPPED) : 0); - P099_CONFIG_FLAGS = lSettings; - } - success = true; - break; - } - case PLUGIN_WEBFORM_LOAD: - { - addFormSubHeader(F("Screen")); - - uint16_t width_ = P099_CONFIG_X_RES; - - if (width_ == 0) { - width_ = P099_TS_X_RES; // default value - } - addFormNumericBox(F("Screen Width (px) (x)"), F("pwidth"), width_, 1, 65535); - - uint16_t height_ = P099_CONFIG_Y_RES; - - if (height_ == 0) { - height_ = P099_TS_Y_RES; // default value - } - addFormNumericBox(F("Screen Height (px) (y)"), F("pheight"), height_, 1, 65535); - - { - const uint8_t choice2 = P099_CONFIG_ROTATION; - const __FlashStringHelper *options2[] = { F("Normal"), F("+90°"), F("+180°"), F("+270°") }; // Avoid unicode - const int optionValues2[] = { 0, 1, 2, 3 }; // Rotation similar to the - // TFT ILI9341 rotation - constexpr size_t optionCount = NR_ELEMENTS(optionValues2); - const FormSelectorOptions selector(optionCount, options2, optionValues2); - selector.addFormSelector(F("Rotation"), F("protate"), choice2); - } - - const bool bRotationFlipped = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_ROTATION_FLIPPED); - addFormCheckBox(F("Flip rotation 180°"), F("protation_flipped"), bRotationFlipped); - addFormNote(F("Some touchscreens are mounted 180° rotated on the display.")); - - addFormSubHeader(F("Touch configuration")); - - addFormNumericBox(F("Touch minimum pressure"), F("ptreshold"), P099_CONFIG_TRESHOLD, 0, 255); - - uint8_t choice3 = 0; - bitWrite(choice3, P099_FLAGS_SEND_XY, bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_XY)); - bitWrite(choice3, P099_FLAGS_SEND_Z, bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z)); - bitWrite(choice3, P099_FLAGS_SEND_OBJECTNAME, bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_OBJECTNAME)); - { - const __FlashStringHelper *options3[] = - { F("None"), - F("X and Y"), - F("X, Y and Z"), - F("Objectnames only"), - F("Objectnames, X and Y"), - F("Objectnames, X, Y and Z") }; - const int optionValues3[] = { 0, 1, 3, 4, 5, 7 }; // Already used as a bitmap! - constexpr size_t optionCount = NR_ELEMENTS(optionValues3); - const FormSelectorOptions selector(optionCount, options3, optionValues3); - selector.addFormSelector(F("Events"), F("pevents"), choice3); - } - - if (!Settings.UseRules) { - addFormNote(F("Tools / Advanced / Rules must be enabled for events to be fired.")); - } - - { - P099_data_struct *P099_data = new (std::nothrow) P099_data_struct(); - - if (nullptr == P099_data) { - return success; - } - P099_data->loadTouchObjects(event->TaskIndex); - - addFormSubHeader(F("Calibration")); - - const bool tbUseCalibration = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_USE_CALIBRATION); - addFormSelector_YesNo(F("Calibrate to screen resolution"), F("puse_calibration"), tbUseCalibration ? 1 : 0, true); - - if (tbUseCalibration) { - addRowLabel(F("Calibration")); - html_table(EMPTY_STRING, false); // Sub-table - html_table_header(F(""), 100); - html_table_header(F("x"), 70); - html_table_header(F("y"), 70); - html_table_header(F(""), 100); - html_table_header(F("x"), 70); - html_table_header(F("y"), 70); - - html_TR_TD(); - addHtml(F("Top-left")); - html_TD(); - addNumericBox(F("pcal_tl_x"), P099_data->StoredSettings.Calibration.top_left.x, 0, 65535); - html_TD(); - addNumericBox(F("pcal_tl_y"), P099_data->StoredSettings.Calibration.top_left.y, 0, 65535); - html_TD(); - addHtml(F("Bottom-right")); - html_TD(); - addNumericBox(F("pcal_br_x"), P099_data->StoredSettings.Calibration.bottom_right.x, 0, 65535); - html_TD(); - addNumericBox(F("pcal_br_y"), P099_data->StoredSettings.Calibration.bottom_right.y, 0, 65535); - - html_end_table(); - addFormNote(F("All x/y values must be <> 0 to enable calibration.")); - } - const bool bEnableCalibrationLog = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_LOG_CALIBRATION); - addFormCheckBox(F("Enable logging for calibration"), F("plog_calibration"), bEnableCalibrationLog); - - addFormSubHeader(F("Touch objects")); - - { - if (P099_CONFIG_OBJECTCOUNT > P099_MaxObjectCount) { P099_CONFIG_OBJECTCOUNT = P099_MaxObjectCount; } - uint8_t choice5 = P099_CONFIG_OBJECTCOUNT; - - if (choice5 == 0) { // Uninitialized, so use default - choice5 = P099_CONFIG_OBJECTCOUNT = P099_INIT_OBJECTCOUNT; - } - { - const __FlashStringHelper *options5[] = { F("None"), F("8"), F("16"), F("24"), F("32"), F("40") }; - const int optionValues5[] = { -1, 8, 16, 24, 32, 40 }; - constexpr size_t optionCount = NR_ELEMENTS(optionValues5); - FormSelectorOptions selector(optionCount, options5, optionValues5); - selector.reloadonchange = true; - selector.addFormSelector(F("# of objects"), F("pobjectcount"), choice5); - } - } - - if (P099_CONFIG_OBJECTCOUNT > -1) { - addRowLabel(F("Object")); - html_table(EMPTY_STRING, false); // Sub-table - html_table_header(F(" # "), 30); - html_table_header(F("Objectname"), 200); - html_table_header(F("Top-left x"), 120); - html_table_header(F("Top-left y"), 120); - html_table_header(F("Bottom-right x"), 150); - html_table_header(F("Bottom-right y"), 150); - html_table_header(F("On/Off button"), 150); - html_table_header(F("Inverted"), 120); - - for (int objectNr = 0; objectNr < P099_CONFIG_OBJECTCOUNT; ++objectNr) { - html_TR_TD(); - addHtml(F(" ")); - addHtmlInt(objectNr + 1); - html_TD(); - addTextBox(getPluginCustomArgName(objectNr), - String(P099_data->StoredSettings.TouchObjects[objectNr].objectname), - P099_MaxObjectNameLength - 1, - F("")); - html_TD(); - addNumericBox(getPluginCustomArgName(objectNr + 100), P099_data->StoredSettings.TouchObjects[objectNr].top_left.x, 0, 65535); - html_TD(); - addNumericBox(getPluginCustomArgName(objectNr + 200), P099_data->StoredSettings.TouchObjects[objectNr].top_left.y, 0, 65535); - html_TD(); - addNumericBox(getPluginCustomArgName(objectNr + 300), P099_data->StoredSettings.TouchObjects[objectNr].bottom_right.x, 0, 65535); - html_TD(); - addNumericBox(getPluginCustomArgName(objectNr + 400), P099_data->StoredSettings.TouchObjects[objectNr].bottom_right.y, 0, 65535); - html_TD(); - addCheckBox(getPluginCustomArgName(objectNr + 500), - bitRead(P099_data->StoredSettings.TouchObjects[objectNr].flags, P099_FLAGS_ON_OFF_BUTTON), false); - html_TD(); - addCheckBox(getPluginCustomArgName(objectNr + 600), - bitRead(P099_data->StoredSettings.TouchObjects[objectNr].flags, P099_FLAGS_INVERT_BUTTON), false); - } - html_end_table(); - addFormNote(F("Start objectname with '_' to ignore/disable the object (temporarily).")); - - const uint8_t debounce = P099_CONFIG_DEBOUNCE_MS; - addFormNumericBox(F("Debounce delay for On/Off buttons"), F("pdebounce"), debounce, 0, 255); - addUnit(F("0-255 msec.")); - } - delete P099_data; - } - success = true; - break; - } - - case PLUGIN_WEBFORM_SAVE: - { - P099_CONFIG_STATE = 1; // mark config as already saved (next time, will not use default values) - P099_CONFIG_TRESHOLD = getFormItemInt(F("ptreshold")); - P099_CONFIG_ROTATION = getFormItemInt(F("protate")); - P099_CONFIG_X_RES = getFormItemInt(F("pwidth")); - P099_CONFIG_Y_RES = getFormItemInt(F("pheight")); - P099_CONFIG_OBJECTCOUNT = getFormItemInt(F("pobjectcount")); - - if (P099_CONFIG_OBJECTCOUNT > P099_MaxObjectCount) { P099_CONFIG_OBJECTCOUNT = P099_MaxObjectCount; } - - uint32_t lSettings = 0; - bitWrite(lSettings, P099_FLAGS_SEND_XY, bitRead(getFormItemInt(F("pevents")), P099_FLAGS_SEND_XY)); - bitWrite(lSettings, P099_FLAGS_SEND_Z, bitRead(getFormItemInt(F("pevents")), P099_FLAGS_SEND_Z)); - bitWrite(lSettings, P099_FLAGS_SEND_OBJECTNAME, bitRead(getFormItemInt(F("pevents")), P099_FLAGS_SEND_OBJECTNAME)); - bitWrite(lSettings, P099_FLAGS_USE_CALIBRATION, getFormItemInt(F("puse_calibration")) == 1); - bitWrite(lSettings, P099_FLAGS_LOG_CALIBRATION, isFormItemChecked(F("plog_calibration"))); - bitWrite(lSettings, P099_FLAGS_ROTATION_FLIPPED, isFormItemChecked(F("protation_flipped"))); - P099_CONFIG_FLAGS = lSettings; - - P099_data_struct *P099_data = new (std::nothrow) P099_data_struct(); - - if (nullptr == P099_data) { - return success; // Save other settings even though this didn't initialize properly - } - P099_data->StoredSettings.Calibration.top_left.x = getFormItemInt(F("pcal_tl_x")); - P099_data->StoredSettings.Calibration.top_left.y = getFormItemInt(F("pcal_tl_y")); - P099_data->StoredSettings.Calibration.bottom_right.x = getFormItemInt(F("pcal_br_x")); - P099_data->StoredSettings.Calibration.bottom_right.y = getFormItemInt(F("pcal_br_y")); - - String error; - - for (int objectNr = 0; objectNr < P099_CONFIG_OBJECTCOUNT; objectNr++) { - if (!safe_strncpy(P099_data->StoredSettings.TouchObjects[objectNr].objectname, webArg(getPluginCustomArgName(objectNr)), - P099_MaxObjectNameLength)) { - error += getCustomTaskSettingsError(objectNr); - } - P099_data->StoredSettings.TouchObjects[objectNr] - .objectname[P099_MaxObjectNameLength - 1] = 0; // Terminate in case of uninitalized data - - if (!ExtraTaskSettings.checkInvalidCharInNames( - &P099_data->StoredSettings.TouchObjects[objectNr].objectname[0])) { // Check for invalid characters in objectname - error += strformat(F("Invalid character in objectname #%d. " - "Do not use ',-+/*=^%!#[]{}()' or space.\n"), - objectNr + 1); - } - P099_data->StoredSettings.TouchObjects[objectNr].top_left.x = getFormItemIntCustomArgName(objectNr + 100); - P099_data->StoredSettings.TouchObjects[objectNr].top_left.y = getFormItemIntCustomArgName(objectNr + 200); - P099_data->StoredSettings.TouchObjects[objectNr].bottom_right.x = getFormItemIntCustomArgName(objectNr + 300); - P099_data->StoredSettings.TouchObjects[objectNr].bottom_right.y = getFormItemIntCustomArgName(objectNr + 400); - - uint8_t flags = 0; - bitWrite(flags, P099_FLAGS_ON_OFF_BUTTON, isFormItemChecked(getPluginCustomArgName(objectNr + 500))); - bitWrite(flags, P099_FLAGS_INVERT_BUTTON, isFormItemChecked(getPluginCustomArgName(objectNr + 600))); - P099_data->StoredSettings.TouchObjects[objectNr].flags = flags; - } - - if (P099_CONFIG_OBJECTCOUNT > 0) { - P099_CONFIG_DEBOUNCE_MS = getFormItemInt(F("pdebounce")); - } - - if (error.length() > 0) { - addHtmlError(error); - } - #ifdef PLUGIN_099_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, concat(F("P099 data save size: "), sizeof(P099_data->StoredSettings))); - } - #endif // PLUGIN_099_DEBUG - SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast(&(P099_data->StoredSettings)), - sizeof(P099_data->StoredSettings)); - delete P099_data; - - success = true; - break; - } - - case PLUGIN_INIT: - { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P099_data_struct()); - P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); - - success = (nullptr != P099_data) && P099_data->init(event->TaskIndex, - P099_CONFIG_CS_PIN, - P099_CONFIG_ROTATION, - bitRead(P099_CONFIG_FLAGS, P099_FLAGS_ROTATION_FLIPPED), - P099_CONFIG_TRESHOLD, - bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_XY), - bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z), - bitRead(P099_CONFIG_FLAGS, P099_FLAGS_USE_CALIBRATION), - P099_CONFIG_X_RES, - P099_CONFIG_Y_RES); - - break; - } - - case PLUGIN_EXIT: - { - success = true; - break; - } - - // case PLUGIN_READ: // Not implemented on purpose, *only* send out events/values when device is touched, and configured to send events - - case PLUGIN_WRITE: - { - P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); - - if (nullptr != P099_data) { - success = P099_data->plugin_write(event, string); - } - - break; - } - - case PLUGIN_TEN_PER_SECOND: // Should be often/fast enough, as this is user-interaction - { - P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); - - if (nullptr == P099_data) { - return success; - } - - if (P099_data->isInitialized()) { - if (P099_data->touched()) { - uint16_t x, y, rx, ry; - uint8_t z; - P099_data->readData(&x, &y, &z); - - if (!((x >= P099_TOUCH_X_INVALID) || (y >= P099_TOUCH_Y_INVALID) || (z == P099_TOUCH_Z_INVALID) || (z <= P099_CONFIG_TRESHOLD))) { - rx = x; - ry = y; - P099_data->scaleRawToCalibrated(x, y); // Map to screen coordinates if so configured - - P099_SET_VALUE_X(x); - P099_SET_VALUE_Y(y); - P099_SET_VALUE_Z(z); - - bool bEnableCalibrationLog = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_LOG_CALIBRATION); - - if (bEnableCalibrationLog && loglevelActiveFor(LOG_LEVEL_INFO)) { - // REQUIRED for calibration and setting up objects, so do not - // make this optional! - // Space before the logged values was added for readability - addLogMove(LOG_LEVEL_INFO, strformat( - F("Touch calibration rx= %u, ry= %u; z= %u, x= %u, y= %u"), - rx, - ry, - z, // Always log the z value even if not used. - x, - y)); - } - - if (Settings.UseRules) { // No events to handle if rules not - // enabled - if (bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_XY)) { // Send events for each touch - const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(event->TaskIndex); - - if (!bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z) && validDeviceIndex(DeviceIndex)) { // Do NOT send a Z event for each - // touch? - // FIXME TD-er: Should not change anything in the Device vector. - #ifdef ESP8266 - Device[DeviceIndex].VType = Sensor_VType::SENSOR_TYPE_DUAL; - Device[DeviceIndex].ValueCount = 2; - #else // ifdef ESP8266 - Device.getDeviceStructForEdit(DeviceIndex).VType = Sensor_VType::SENSOR_TYPE_DUAL; - Device.getDeviceStructForEdit(DeviceIndex).ValueCount = 2; - #endif // ifdef ESP8266 - } - sendData(event); // Send X/Y(/Z) event - - if (!bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z) && validDeviceIndex(DeviceIndex)) { // Reset device configuration - // FIXME TD-er: Should not change anything in the Device vector. - #ifdef ESP8266 - Device[DeviceIndex].VType = Sensor_VType::SENSOR_TYPE_TRIPLE; - Device[DeviceIndex].ValueCount = 3; - #else // ifdef ESP8266 - Device.getDeviceStructForEdit(DeviceIndex).VType = Sensor_VType::SENSOR_TYPE_TRIPLE; - Device.getDeviceStructForEdit(DeviceIndex).ValueCount = 3; - #endif // ifdef ESP8266 - } - } - - if (bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_OBJECTNAME)) { // Send events for objectname if within reach - String selectedObjectName; - int selectedObjectIndex = -1; - - if (P099_data->isValidAndTouchedTouchObject(x, y, selectedObjectName, selectedObjectIndex, P099_CONFIG_OBJECTCOUNT)) { - if ((selectedObjectIndex > -1) && - bitRead(P099_data->StoredSettings.TouchObjects[selectedObjectIndex].flags, P099_FLAGS_ON_OFF_BUTTON)) { - if ((P099_data->TouchTimers[selectedObjectIndex] == 0) || - (P099_data->TouchTimers[selectedObjectIndex] < millis() - (2 * P099_CONFIG_DEBOUNCE_MS))) { // Not touched yet or - // too long ago - P099_data->TouchTimers[selectedObjectIndex] = millis() + P099_CONFIG_DEBOUNCE_MS; // From now wait the - // debounce time - } else { - if (P099_data->TouchTimers[selectedObjectIndex] <= millis()) { // Debouncing time - // elapsed? - P099_data->TouchStates[selectedObjectIndex] = !P099_data->TouchStates[selectedObjectIndex]; - P099_data->TouchTimers[selectedObjectIndex] = 0; - - bool eventValue = P099_data->TouchStates[selectedObjectIndex]; - - if (bitRead(P099_data->StoredSettings.TouchObjects[selectedObjectIndex].flags, P099_FLAGS_INVERT_BUTTON)) { - eventValue = !eventValue; // Act like an inverted button, 0 = On, 1 = Off - } - eventQueue.add(event->TaskIndex, selectedObjectName, eventValue); - } - } - } else { - // Matching object is found, send # event with x, y and z as %eventvalue1/2/3% - eventQueue.add( - event->TaskIndex, - selectedObjectName, - strformat(F("%u,%u,%u"), x, y, z)); - } - } - } - } - success = true; - } - } - } - break; - } - } // switch(function) - return success; -} // Plugin_099 - -#endif // USES_P099 +#include "_Plugin_Helper.h" + +#ifdef USES_P099 + +// ####################################################################################################### +// #################################### Plugin 099: XPT2046 TFT Touchscreen ################################# +// ####################################################################################################### + +/** + * Changelog: + * 2025-11-19 tonhuisman: Use Rotation flipped setting from ESPEasy_TouchHandler + * 2025-09-28 tonhuisman: Keep previous code for ESP8266, to match the limited binary space available + * 2025-09-27 tonhuisman: Implement ESPEasy_TouchHandler + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus + * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Touch) + * 2020-11-01 tonhuisman: Solved previous strange rotation settings to be compatible with TFT ILI9341 + * 2020-11-01 tonhuisman: Add option to flip rotation by 180 deg, and command touch,flip,<0|1> + * 2020-11-01 tonhuisman: Add option for the debounce timeout for On/Off buttons + * 2020-11-01 tonhuisman: Some code cleanup and string optimizations + * 2020-09-23/24/25 tonhuisman: Add object disable/enable commands, add On/Off button mode, and inverted, for touchobjects + * 2020-09-10 tonhuisman: Clean up code, testing + * 2020-09-07/08/09 tonhuisman: Fix code issues + * 2020-09-06 tonhuisman: Add object 'layering' so the 'top-most' object only sends an event + * 2020-09-05 tonhuisman: Add touch to touchobject mapping, generate events + * 2020-09-03 tonhuisman: Add touchobject settings + * 2020-08-31 tonhuisman: Add Calibration settings + * 2020-08-30 tonhuisman: Add settings and 2/3 event support + * 2020-08-29 tonhuisman: Initial plugin, based on XPT2046_Touchscreen by Paul Stoffregen from + * https://github.com/PaulStoffregen/XPT2046_Touchscreen + */ + +/** + * Commands supported: + * ------------------- + * touch,rot,<0..3> : Set rotation to 0(0), 90(1), 180(2), 270(3) degrees + * touch,flip,<0|1> : Set rotation normal(0) or flipped by 180 degrees(1) + * touch,enable, : Enables a disabled objectname + * touch,disable, : Disables an enabled objectname + */ + +# define PLUGIN_099 +# define PLUGIN_ID_099 99 +# define PLUGIN_NAME_099 "Touch - XPT2046 on a TFT display" +# define PLUGIN_VALUENAME1_099 "X" +# define PLUGIN_VALUENAME2_099 "Y" +# define PLUGIN_VALUENAME3_099 "Z" + +# include "src/PluginStructs/P099_data_struct.h" + + +# ifdef ESP32 + +boolean Plugin_099(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_099; + dev.Type = DEVICE_TYPE_SPI; + dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; + dev.ValueCount = 3; + dev.SpiBusSelect = true; + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_099); + success = true; + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_099)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_099)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_099)); + success = true; + break; + } + + case PLUGIN_GET_DEVICEGPIONAMES: + { + event->String1 = formatGpioName_output(F("TS CS")); + break; + } + + case PLUGIN_GET_DEVICEVALUECOUNT: + { + event->Par1 = P099_GET_CONFIG_VTYPE; + success = true; + break; + } + + # if FEATURE_MQTT_DISCOVER + case PLUGIN_GET_DISCOVERY_VTYPES: + { + event->Par1 = static_cast(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported + success = true; + break; + } + # endif // if FEATURE_MQTT_DISCOVER + + case PLUGIN_SET_DEFAULTS: + { + P099_CONFIG_CS_PIN = -1; + P099_CONFIG_THRESHOLD = P099_TS_TRESHOLD; + P099_CONFIG_ROTATION = P099_TS_ROTATION; + P099_CONFIG_X_RES = P099_TS_X_RES; + P099_CONFIG_Y_RES = P099_TS_Y_RES; + # if P099_ENABLE_OLD_CONFIG + P099_CONFIG_OBJECTCOUNT = P099_INIT_OBJECTCOUNT; + P099_CONFIG_DEBOUNCE_MS = P099_DEBOUNCE_MILLIS; + # endif // if P099_ENABLE_OLD_CONFIG + + constexpr uint32_t lSettings = 0 + + (P099_TS_SEND_XY ? (1 << P099_FLAGS_SEND_XY) : 0) + + (P099_TS_SEND_Z ? (1 << P099_FLAGS_SEND_Z) : 0) + + (P099_TS_SEND_OBJECTNAME ? (1 << P099_FLAGS_SEND_OBJECTNAME) : 0) + + (P099_TS_USE_CALIBRATION ? (1 << P099_FLAGS_USE_CALIBRATION) : 0) + + (P099_TS_LOG_CALIBRATION ? (1 << P099_FLAGS_LOG_CALIBRATION) : 0); + P099_CONFIG_FLAGS = lSettings; + P099_SET_CONFIG_DISPLAY(TASKS_MAX); // Preselect Not Set value + + success = true; + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + addFormSubHeader(F("Screen")); + + { + addRowLabel(F("Display task")); + addTaskSelect(F("dsptask"), P099_GET_CONFIG_DISPLAY); + # ifndef P099_LIMIT_BUILD_SIZE + addFormNote(F("Screen Width, Heigth, Rotation & Color-depth will be fetched from the Display task if possible.")); + # endif // ifndef P099_LIMIT_BUILD_SIZE + } + + uint16_t width_ = P099_CONFIG_X_RES; + uint16_t height_ = P099_CONFIG_Y_RES; + uint16_t rotation_ = P099_CONFIG_ROTATION; + uint16_t colorDepth_ = P099_COLOR_DEPTH; + + if (P099_GET_CONFIG_DISPLAY != static_cast(P099_CONFIG_DISPLAY_PREV)) { // Changed since last saved? + getPluginDisplayParametersFromTaskIndex(P099_GET_CONFIG_DISPLAY, width_, height_, rotation_, colorDepth_); + } + P099_COLOR_DEPTH = colorDepth_; + + if (width_ == 0) { + width_ = P099_TS_X_RES; // default value + } + addFormNumericBox(F("Screen Width (px) (x)"), F("pwidth"), width_, 1, 65535); + + if (height_ == 0) { + height_ = P099_TS_Y_RES; // default value + } + addFormNumericBox(F("Screen Height (px) (y)"), F("pheight"), height_, 1, 65535); + + AdaGFXFormRotation(F("protate"), rotation_); + + AdaGFXFormColorDepth(F("colordepth"), P099_COLOR_DEPTH, (colorDepth_ == 0)); + + addFormSubHeader(F("Touch panel")); + + addFormNumericBox(F("Touch minimum pressure"), F("ptreshold"), P099_CONFIG_THRESHOLD, 0, 255); + + { + P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); + bool deleteP099_data = false; + + if (nullptr == P099_data) { + P099_data = new (std::nothrow) P099_data_struct(); + deleteP099_data = true; + } + + if (nullptr == P099_data) { + return success; + } + + // P099_data->loadTouchObjects(event); + + success = P099_data->plugin_webform_load(event); + + if (deleteP099_data) { + delete P099_data; + } + } + + // success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + P099_CONFIG_VERSION = 2; // Storage layout changed to use ESPEasy_TouchHandler + P099_CONFIG_DISPLAY_PREV = P099_GET_CONFIG_DISPLAY; + P099_CONFIG_THRESHOLD = getFormItemInt(F("ptreshold")); + P099_CONFIG_ROTATION = getFormItemInt(F("protate")); + P099_CONFIG_X_RES = getFormItemInt(F("pwidth")); + P099_CONFIG_Y_RES = getFormItemInt(F("pheight")); + # if P099_ENABLE_OLD_CONFIG + P099_CONFIG_OBJECTCOUNT = getFormItemInt(F("pobjectcount")); + + if (P099_CONFIG_OBJECTCOUNT > P099_MaxObjectCount) { P099_CONFIG_OBJECTCOUNT = P099_MaxObjectCount; } + # endif // if P099_ENABLE_OLD_CONFIG + + uint32_t lSettings = 0; + + bitWrite(lSettings, P099_FLAGS_USE_CALIBRATION, getFormItemInt(F("puse_calibration")) == 1); + bitWrite(lSettings, P099_FLAGS_LOG_CALIBRATION, isFormItemChecked(F("plog_calibration"))); + P099_CONFIG_FLAGS = lSettings; + P099_SET_CONFIG_DISPLAY(getFormItemInt(F("dsptask"))); + + { + P099_data_struct *P099_data = nullptr; // static_cast(getPluginTaskData(event->TaskIndex)); + bool deleteP099_data = false; + + if (nullptr == P099_data) { + P099_data = new (std::nothrow) P099_data_struct(); + deleteP099_data = true; + } + + if (nullptr != P099_data) { + success = P099_data->plugin_webform_save(event); + + if (deleteP099_data) { + delete P099_data; + } + } + } + + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P099_data_struct()); + P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = (nullptr != P099_data) && P099_data->init(event, + P099_CONFIG_CS_PIN, + P099_CONFIG_ROTATION, + false, + P099_CONFIG_THRESHOLD, + P099_CONFIG_X_RES, + P099_CONFIG_Y_RES); + + break; + } + + case PLUGIN_EXIT: + { + success = true; + break; + } + + // case PLUGIN_READ: // Not implemented on purpose, *only* send out events/values when device is touched, and configured to send events + + case PLUGIN_WRITE: + { + P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P099_data) { + success = P099_data->plugin_write(event, string); + } + + break; + } + + case PLUGIN_FIFTY_PER_SECOND: + { + P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P099_data) { + success = P099_data->plugin_fifty_per_second(event); + } + + break; + } + + case PLUGIN_GET_CONFIG_VALUE: + { + P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P099_data) { + success = P099_data->plugin_get_config_value(event, string); + } + break; + } + } // switch(function) + return success; +} // Plugin_099 + +# endif // ifdef ESP32 + +/******************************************************************************************************************************** +* Below code is using the old but smaller implementation for ESP8266, and should only get bugfixes, if needed +********************************************************************************************************************************/ + +# ifdef ESP8266 + +boolean Plugin_099(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_099; + dev.Type = DEVICE_TYPE_SPI; + dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; + dev.ValueCount = 3; + dev.SpiBusSelect = true; + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_099); + success = true; + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_099)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_099)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_099)); + success = true; + break; + } + + case PLUGIN_GET_DEVICEGPIONAMES: + { + event->String1 = formatGpioName_output(F("TS CS")); + break; + } + + # if FEATURE_MQTT_DISCOVER + case PLUGIN_GET_DISCOVERY_VTYPES: + { + event->Par1 = static_cast(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported + success = true; + break; + } + # endif // if FEATURE_MQTT_DISCOVER + + case PLUGIN_SET_DEFAULTS: + { + // if already configured take it from settings, else use default values + if (P099_CONFIG_STATE != 1) { + P099_CONFIG_CS_PIN = P099_TS_CS; + P099_CONFIG_THRESHOLD = P099_TS_TRESHOLD; + P099_CONFIG_ROTATION = P099_TS_ROTATION; + P099_CONFIG_X_RES = P099_TS_X_RES; + P099_CONFIG_Y_RES = P099_TS_Y_RES; + P099_CONFIG_OBJECTCOUNT = P099_INIT_OBJECTCOUNT; + P099_CONFIG_DEBOUNCE_MS = P099_DEBOUNCE_MILLIS; + + constexpr uint32_t lSettings = 0 + + (P099_TS_SEND_XY ? (1 << P099_FLAGS_SEND_XY) : 0) + + (P099_TS_SEND_Z ? (1 << P099_FLAGS_SEND_Z) : 0) + + (P099_TS_SEND_OBJECTNAME ? (1 << P099_FLAGS_SEND_OBJECTNAME) : 0) + + (P099_TS_USE_CALIBRATION ? (1 << P099_FLAGS_USE_CALIBRATION) : 0) + + (P099_TS_LOG_CALIBRATION ? (1 << P099_FLAGS_LOG_CALIBRATION) : 0) + + (P099_TS_ROTATION_FLIPPED ? (1 << P099_FLAGS_ROTATION_FLIPPED) : 0); + P099_CONFIG_FLAGS = lSettings; + } + success = true; + break; + } + case PLUGIN_WEBFORM_LOAD: + { + addFormSubHeader(F("Screen")); + + uint16_t width_ = P099_CONFIG_X_RES; + + if (width_ == 0) { + width_ = P099_TS_X_RES; // default value + } + addFormNumericBox(F("Screen Width (px) (x)"), F("pwidth"), width_, 1, 65535); + + uint16_t height_ = P099_CONFIG_Y_RES; + + if (height_ == 0) { + height_ = P099_TS_Y_RES; // default value + } + addFormNumericBox(F("Screen Height (px) (y)"), F("pheight"), height_, 1, 65535); + + { + const uint8_t choice2 = P099_CONFIG_ROTATION; + const __FlashStringHelper *options2[] = { F("Normal"), F("+90°"), F("+180°"), F("+270°") }; // Avoid unicode + const int optionValues2[] = { 0, 1, 2, 3 }; // Rotation similar to the + // TFT ILI9341 rotation + constexpr size_t optionCount = NR_ELEMENTS(optionValues2); + const FormSelectorOptions selector(optionCount, options2, optionValues2); + selector.addFormSelector(F("Rotation"), F("protate"), choice2); + } + + const bool bRotationFlipped = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_ROTATION_FLIPPED); + addFormCheckBox(F("Flip rotation 180°"), F("protation_flipped"), bRotationFlipped); + addFormNote(F("Some touchscreens are mounted 180° rotated on the display.")); + + addFormSubHeader(F("Touch configuration")); + + addFormNumericBox(F("Touch minimum pressure"), F("ptreshold"), P099_CONFIG_THRESHOLD, 0, 255); + + uint8_t choice3 = 0; + bitWrite(choice3, P099_FLAGS_SEND_XY, bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_XY)); + bitWrite(choice3, P099_FLAGS_SEND_Z, bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z)); + bitWrite(choice3, P099_FLAGS_SEND_OBJECTNAME, bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_OBJECTNAME)); + { + const __FlashStringHelper *options3[] = + { F("None"), + F("X and Y"), + F("X, Y and Z"), + F("Objectnames only"), + F("Objectnames, X and Y"), + F("Objectnames, X, Y and Z") }; + const int optionValues3[] = { 0, 1, 3, 4, 5, 7 }; // Already used as a bitmap! + constexpr size_t optionCount = NR_ELEMENTS(optionValues3); + const FormSelectorOptions selector(optionCount, options3, optionValues3); + selector.addFormSelector(F("Events"), F("pevents"), choice3); + } + + if (!Settings.UseRules) { + addFormNote(F("Tools / Advanced / Rules must be enabled for events to be fired.")); + } + + { + P099_data_struct *P099_data = new (std::nothrow) P099_data_struct(); + + if (nullptr == P099_data) { + return success; + } + P099_data->loadTouchObjects(event->TaskIndex); + + addFormSubHeader(F("Calibration")); + + const bool tbUseCalibration = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_USE_CALIBRATION); + addFormSelector_YesNo(F("Calibrate to screen resolution"), F("puse_calibration"), tbUseCalibration ? 1 : 0, true); + + if (tbUseCalibration) { + addRowLabel(F("Calibration")); + html_table(EMPTY_STRING, false); // Sub-table + html_table_header(F(""), 100); + html_table_header(F("x"), 70); + html_table_header(F("y"), 70); + html_table_header(F(""), 100); + html_table_header(F("x"), 70); + html_table_header(F("y"), 70); + + html_TR_TD(); + addHtml(F("Top-left")); + html_TD(); + addNumericBox(F("pcal_tl_x"), P099_data->StoredSettings.Calibration.top_left.x, 0, 65535); + html_TD(); + addNumericBox(F("pcal_tl_y"), P099_data->StoredSettings.Calibration.top_left.y, 0, 65535); + html_TD(); + addHtml(F("Bottom-right")); + html_TD(); + addNumericBox(F("pcal_br_x"), P099_data->StoredSettings.Calibration.bottom_right.x, 0, 65535); + html_TD(); + addNumericBox(F("pcal_br_y"), P099_data->StoredSettings.Calibration.bottom_right.y, 0, 65535); + + html_end_table(); + addFormNote(F("All x/y values must be <> 0 to enable calibration.")); + } + const bool bEnableCalibrationLog = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_LOG_CALIBRATION); + addFormCheckBox(F("Enable logging for calibration"), F("plog_calibration"), bEnableCalibrationLog); + + addFormSubHeader(F("Touch objects")); + + { + if (P099_CONFIG_OBJECTCOUNT > P099_MaxObjectCount) { P099_CONFIG_OBJECTCOUNT = P099_MaxObjectCount; } + uint8_t choice5 = P099_CONFIG_OBJECTCOUNT; + + if (choice5 == 0) { // Uninitialized, so use default + choice5 = P099_CONFIG_OBJECTCOUNT = P099_INIT_OBJECTCOUNT; + } + { + const __FlashStringHelper *options5[] = { F("None"), F("8"), F("16"), F("24"), F("32"), F("40") }; + const int optionValues5[] = { -1, 8, 16, 24, 32, 40 }; + constexpr size_t optionCount = NR_ELEMENTS(optionValues5); + FormSelectorOptions selector(optionCount, options5, optionValues5); + selector.reloadonchange = true; + selector.addFormSelector(F("# of objects"), F("pobjectcount"), choice5); + } + } + + if (P099_CONFIG_OBJECTCOUNT > -1) { + addRowLabel(F("Object")); + html_table(EMPTY_STRING, false); // Sub-table + html_table_header(F(" # "), 30); + html_table_header(F("Objectname"), 200); + html_table_header(F("Top-left x"), 120); + html_table_header(F("Top-left y"), 120); + html_table_header(F("Bottom-right x"), 150); + html_table_header(F("Bottom-right y"), 150); + html_table_header(F("On/Off button"), 150); + html_table_header(F("Inverted"), 120); + + for (int objectNr = 0; objectNr < P099_CONFIG_OBJECTCOUNT; ++objectNr) { + html_TR_TD(); + addHtml(F(" ")); + addHtmlInt(objectNr + 1); + html_TD(); + addTextBox(getPluginCustomArgName(objectNr), + String(P099_data->StoredSettings.TouchObjects[objectNr].objectname), + P099_MaxObjectNameLength - 1, + F("")); + html_TD(); + addNumericBox(getPluginCustomArgName(objectNr + 100), P099_data->StoredSettings.TouchObjects[objectNr].top_left.x, 0, 65535); + html_TD(); + addNumericBox(getPluginCustomArgName(objectNr + 200), P099_data->StoredSettings.TouchObjects[objectNr].top_left.y, 0, 65535); + html_TD(); + addNumericBox(getPluginCustomArgName(objectNr + 300), P099_data->StoredSettings.TouchObjects[objectNr].bottom_right.x, 0, 65535); + html_TD(); + addNumericBox(getPluginCustomArgName(objectNr + 400), P099_data->StoredSettings.TouchObjects[objectNr].bottom_right.y, 0, 65535); + html_TD(); + addCheckBox(getPluginCustomArgName(objectNr + 500), + bitRead(P099_data->StoredSettings.TouchObjects[objectNr].flags, P099_FLAGS_ON_OFF_BUTTON), false); + html_TD(); + addCheckBox(getPluginCustomArgName(objectNr + 600), + bitRead(P099_data->StoredSettings.TouchObjects[objectNr].flags, P099_FLAGS_INVERT_BUTTON), false); + } + html_end_table(); + addFormNote(F("Start objectname with '_' to ignore/disable the object (temporarily).")); + + const uint8_t debounce = P099_CONFIG_DEBOUNCE_MS; + addFormNumericBox(F("Debounce delay for On/Off buttons"), F("pdebounce"), debounce, 0, 255); + addUnit(F("0-255 msec.")); + } + delete P099_data; + } + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + P099_CONFIG_STATE = 1; // mark config as already saved (next time, will not use default values) + P099_CONFIG_THRESHOLD = getFormItemInt(F("ptreshold")); + P099_CONFIG_ROTATION = getFormItemInt(F("protate")); + P099_CONFIG_X_RES = getFormItemInt(F("pwidth")); + P099_CONFIG_Y_RES = getFormItemInt(F("pheight")); + P099_CONFIG_OBJECTCOUNT = getFormItemInt(F("pobjectcount")); + + if (P099_CONFIG_OBJECTCOUNT > P099_MaxObjectCount) { P099_CONFIG_OBJECTCOUNT = P099_MaxObjectCount; } + + uint32_t lSettings = 0; + bitWrite(lSettings, P099_FLAGS_SEND_XY, bitRead(getFormItemInt(F("pevents")), P099_FLAGS_SEND_XY)); + bitWrite(lSettings, P099_FLAGS_SEND_Z, bitRead(getFormItemInt(F("pevents")), P099_FLAGS_SEND_Z)); + bitWrite(lSettings, P099_FLAGS_SEND_OBJECTNAME, bitRead(getFormItemInt(F("pevents")), P099_FLAGS_SEND_OBJECTNAME)); + bitWrite(lSettings, P099_FLAGS_USE_CALIBRATION, getFormItemInt(F("puse_calibration")) == 1); + bitWrite(lSettings, P099_FLAGS_LOG_CALIBRATION, isFormItemChecked(F("plog_calibration"))); + bitWrite(lSettings, P099_FLAGS_ROTATION_FLIPPED, isFormItemChecked(F("protation_flipped"))); + P099_CONFIG_FLAGS = lSettings; + + P099_data_struct *P099_data = new (std::nothrow) P099_data_struct(); + + if (nullptr == P099_data) { + return success; // Save other settings even though this didn't initialize properly + } + P099_data->StoredSettings.Calibration.top_left.x = getFormItemInt(F("pcal_tl_x")); + P099_data->StoredSettings.Calibration.top_left.y = getFormItemInt(F("pcal_tl_y")); + P099_data->StoredSettings.Calibration.bottom_right.x = getFormItemInt(F("pcal_br_x")); + P099_data->StoredSettings.Calibration.bottom_right.y = getFormItemInt(F("pcal_br_y")); + + String error; + + for (int objectNr = 0; objectNr < P099_CONFIG_OBJECTCOUNT; objectNr++) { + if (!safe_strncpy(P099_data->StoredSettings.TouchObjects[objectNr].objectname, webArg(getPluginCustomArgName(objectNr)), + P099_MaxObjectNameLength)) { + error += getCustomTaskSettingsError(objectNr); + } + P099_data->StoredSettings.TouchObjects[objectNr] + .objectname[P099_MaxObjectNameLength - 1] = 0; // Terminate in case of uninitalized data + + if (!ExtraTaskSettings.checkInvalidCharInNames( + &P099_data->StoredSettings.TouchObjects[objectNr].objectname[0])) { // Check for invalid characters in objectname + error += strformat(F("Invalid character in objectname #%d. " + "Do not use ',-+/*=^%!#[]{}()' or space.\n"), + objectNr + 1); + } + P099_data->StoredSettings.TouchObjects[objectNr].top_left.x = getFormItemIntCustomArgName(objectNr + 100); + P099_data->StoredSettings.TouchObjects[objectNr].top_left.y = getFormItemIntCustomArgName(objectNr + 200); + P099_data->StoredSettings.TouchObjects[objectNr].bottom_right.x = getFormItemIntCustomArgName(objectNr + 300); + P099_data->StoredSettings.TouchObjects[objectNr].bottom_right.y = getFormItemIntCustomArgName(objectNr + 400); + + uint8_t flags = 0; + bitWrite(flags, P099_FLAGS_ON_OFF_BUTTON, isFormItemChecked(getPluginCustomArgName(objectNr + 500))); + bitWrite(flags, P099_FLAGS_INVERT_BUTTON, isFormItemChecked(getPluginCustomArgName(objectNr + 600))); + P099_data->StoredSettings.TouchObjects[objectNr].flags = flags; + } + + if (P099_CONFIG_OBJECTCOUNT > 0) { + P099_CONFIG_DEBOUNCE_MS = getFormItemInt(F("pdebounce")); + } + + if (error.length() > 0) { + addHtmlError(error); + } + # ifdef PLUGIN_099_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLogMove(LOG_LEVEL_INFO, concat(F("P099 data save size: "), sizeof(P099_data->StoredSettings))); + } + # endif // PLUGIN_099_DEBUG + SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast(&(P099_data->StoredSettings)), + sizeof(P099_data->StoredSettings)); + delete P099_data; + + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P099_data_struct()); + P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = (nullptr != P099_data) && P099_data->init(event->TaskIndex, + P099_CONFIG_CS_PIN, + P099_CONFIG_ROTATION, + bitRead(P099_CONFIG_FLAGS, P099_FLAGS_ROTATION_FLIPPED), + P099_CONFIG_THRESHOLD, + bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_XY), + bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z), + bitRead(P099_CONFIG_FLAGS, P099_FLAGS_USE_CALIBRATION), + P099_CONFIG_X_RES, + P099_CONFIG_Y_RES); + + break; + } + + case PLUGIN_EXIT: + { + success = true; + break; + } + + // case PLUGIN_READ: // Not implemented on purpose, *only* send out events/values when device is touched, and configured to send events + + case PLUGIN_WRITE: + { + P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P099_data) { + success = P099_data->plugin_write(event, string); + } + + break; + } + + case PLUGIN_TEN_PER_SECOND: // Should be often/fast enough, as this is user-interaction + { + P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr == P099_data) { + return success; + } + + if (P099_data->isInitialized()) { + if (P099_data->touched()) { + uint16_t x, y, rx, ry; + uint8_t z; + P099_data->readData(&x, &y, &z); + + if (!((x >= P099_TOUCH_X_INVALID) || (y >= P099_TOUCH_Y_INVALID) || (z == P099_TOUCH_Z_INVALID) || (z <= P099_CONFIG_THRESHOLD))) { + rx = x; + ry = y; + P099_data->scaleRawToCalibrated(x, y); // Map to screen coordinates if so configured + + P099_SET_VALUE_X(x); + P099_SET_VALUE_Y(y); + P099_SET_VALUE_Z(z); + + bool bEnableCalibrationLog = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_LOG_CALIBRATION); + + if (bEnableCalibrationLog && loglevelActiveFor(LOG_LEVEL_INFO)) { + // REQUIRED for calibration and setting up objects, so do not + // make this optional! + // Space before the logged values was added for readability + addLogMove(LOG_LEVEL_INFO, strformat( + F("Touch calibration rx= %u, ry= %u; z= %u, x= %u, y= %u"), + rx, + ry, + z, // Always log the z value even if not used. + x, + y)); + } + + if (Settings.UseRules) { // No events to handle if rules not + // enabled + if (bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_XY)) { // Send events for each touch + const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(event->TaskIndex); + + if (!bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z) && validDeviceIndex(DeviceIndex)) { // Do NOT send a Z event for each + // touch? + // FIXME TD-er: Should not change anything in the Device vector. + # ifdef ESP8266 + Device[DeviceIndex].VType = Sensor_VType::SENSOR_TYPE_DUAL; + Device[DeviceIndex].ValueCount = 2; + # else // ifdef ESP8266 + Device.getDeviceStructForEdit(DeviceIndex).VType = Sensor_VType::SENSOR_TYPE_DUAL; + Device.getDeviceStructForEdit(DeviceIndex).ValueCount = 2; + # endif // ifdef ESP8266 + } + sendData(event); // Send X/Y(/Z) event + + if (!bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z) && validDeviceIndex(DeviceIndex)) { // Reset device configuration + // FIXME TD-er: Should not change anything in the Device vector. + # ifdef ESP8266 + Device[DeviceIndex].VType = Sensor_VType::SENSOR_TYPE_TRIPLE; + Device[DeviceIndex].ValueCount = 3; + # else // ifdef ESP8266 + Device.getDeviceStructForEdit(DeviceIndex).VType = Sensor_VType::SENSOR_TYPE_TRIPLE; + Device.getDeviceStructForEdit(DeviceIndex).ValueCount = 3; + # endif // ifdef ESP8266 + } + } + + if (bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_OBJECTNAME)) { // Send events for objectname if within reach + String selectedObjectName; + int selectedObjectIndex = -1; + + if (P099_data->isValidAndTouchedTouchObject(x, y, selectedObjectName, selectedObjectIndex, P099_CONFIG_OBJECTCOUNT)) { + if ((selectedObjectIndex > -1) && + bitRead(P099_data->StoredSettings.TouchObjects[selectedObjectIndex].flags, P099_FLAGS_ON_OFF_BUTTON)) { + if ((P099_data->TouchTimers[selectedObjectIndex] == 0) || + (P099_data->TouchTimers[selectedObjectIndex] < millis() - (2 * P099_CONFIG_DEBOUNCE_MS))) { // Not touched yet or + // too long ago + P099_data->TouchTimers[selectedObjectIndex] = millis() + P099_CONFIG_DEBOUNCE_MS; // From now wait the + // debounce time + } else { + if (P099_data->TouchTimers[selectedObjectIndex] <= millis()) { // Debouncing time + // elapsed? + P099_data->TouchStates[selectedObjectIndex] = !P099_data->TouchStates[selectedObjectIndex]; + P099_data->TouchTimers[selectedObjectIndex] = 0; + + bool eventValue = P099_data->TouchStates[selectedObjectIndex]; + + if (bitRead(P099_data->StoredSettings.TouchObjects[selectedObjectIndex].flags, P099_FLAGS_INVERT_BUTTON)) { + eventValue = !eventValue; // Act like an inverted button, 0 = On, 1 = Off + } + eventQueue.add(event->TaskIndex, selectedObjectName, eventValue); + } + } + } else { + // Matching object is found, send # event with x, y and z as %eventvalue1/2/3% + eventQueue.add( + event->TaskIndex, + selectedObjectName, + strformat(F("%u,%u,%u"), x, y, z)); + } + } + } + } + success = true; + } + } + } + break; + } + } // switch(function) + return success; +} // Plugin_099 + +# endif // ifdef ESP8266 + +#endif // USES_P099 diff --git a/src/_P123_I2CTouch.ino b/src/_P123_I2CTouch.ino index 7a49e469b6..daee4cfda8 100644 --- a/src/_P123_I2CTouch.ino +++ b/src/_P123_I2CTouch.ino @@ -115,7 +115,7 @@ boolean Plugin_123(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_SET_DEFAULTS: { - P123_CONFIG_DISPLAY_TASK = event->TaskIndex; // Preselect current task to avoid pointing to Task 1 by default + P123_CONFIG_DISPLAY_TASK = TASKS_MAX; // Preselect Not Set value P123_CONFIG_THRESHOLD = P123_TS_THRESHOLD; P123_CONFIG_ROTATION = P123_TS_ROTATION; P123_CONFIG_X_RES = P123_TS_X_RES; diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 04748d003d..118063726d 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3070,13 +3070,13 @@ To create/register a plugin, you have to : #define DISABLE_SOFTWARE_SERIAL #endif -#if defined(USES_P095) || defined(USES_P096) || defined(USES_P116) || defined(USES_P131) || defined(USES_P141) || defined(USES_P123) || defined(USES_P165) // Add any plugin that uses AdafruitGFX_Helper +#if defined(USES_P095) || defined(USES_P096) || (defined(USES_P099) && defined(ESP32)) || defined(USES_P116) || defined(USES_P131) || defined(USES_P141) || defined(USES_P123) || defined(USES_P165) // Add any plugin that uses AdafruitGFX_Helper #ifndef PLUGIN_USES_ADAFRUITGFX #define PLUGIN_USES_ADAFRUITGFX // Ensure AdafruitGFX_helper is available for graphics displays (only) #endif #endif -#if defined(USES_P099) || defined(USES_P123) +#if (defined(USES_P099) && defined(ESP32)) || defined(USES_P123) #ifndef PLUGIN_USES_TOUCHHANDLER #define PLUGIN_USES_TOUCHHANDLER #endif diff --git a/src/src/Helpers/ESPEasy_TouchHandler.cpp b/src/src/Helpers/ESPEasy_TouchHandler.cpp index eea5fd5262..1d087148f0 100644 --- a/src/src/Helpers/ESPEasy_TouchHandler.cpp +++ b/src/src/Helpers/ESPEasy_TouchHandler.cpp @@ -8,27 +8,25 @@ tTouchObjects::tTouchObjects() : flags(0u), SurfaceAreas(0u), TouchTimers(0u), -#ifdef ESP32 - top_left({0u, 0u}), - width_height({0u, 0u}), -#endif + # ifdef ESP32 + top_left({ 0u, 0u }), + width_height({ 0u, 0u }), + # endif // ifdef ESP32 TouchStates(0u) # if TOUCH_FEATURE_EXTENDED_TOUCH - , groupFlags (0u) - , colorOn (0u) - , colorOff (0u) - , colorCaption (0u) - , colorBorder (0u) - , colorDisabled (0u) - , colorDisabledCaption (0u) + , groupFlags(0u) + , colorOn(0u) + , colorOff(0u) + , colorCaption(0u) + , colorBorder(0u) + , colorDisabled(0u) + , colorDisabledCaption(0u) # endif // if TOUCH_FEATURE_EXTENDED_TOUCH - { - objectName.clear(); - captionOn.clear(); - captionOff.clear(); - } - - +{ + objectName.clear(); + captionOn.clear(); + captionOff.clear(); +} /**************************************************************************** * toString: Display-value for the touch action @@ -91,7 +89,97 @@ void ESPEasy_TouchHandler::loadTouchObjects(struct EventStruct *event) { # ifdef TOUCH_DEBUG addLog(LOG_LEVEL_INFO, F("TOUCH DEBUG loadTouchObjects")); # endif // TOUCH_DEBUG - LoadCustomTaskSettings(event->TaskIndex, settingsArray, TOUCH_ARRAY_SIZE, 0); + # if defined(USES_P099) && P099_ENABLE_OLD_CONFIG && defined(ESP32) + bool loadStandardConfig = true; + + // Check if old format P099 data is stored, and convert if needed + const pluginID_t taskPlugin = getPluginID_from_TaskIndex(event->TaskIndex); + + if (pluginID_t(99) == taskPlugin) { + if (P099_CONFIG_VERSION == 1) { + addLog(LOG_LEVEL_INFO, F("TOUCH: Converting P099 settings from previous format")); + + // Load, convert settings and set loadStandardConfig = false + loadStandardConfig = false; + tP099_StoredSettings_struct StoredSettings; + LoadCustomTaskSettings(event->TaskIndex, reinterpret_cast(&StoredSettings), sizeof(StoredSettings)); + + String config; + config.reserve(40); + uint32_t lSettings{}; + + set3BitToUL(lSettings, TOUCH_FLAGS_SEND_XY, get3BitFromUL(P099_CONFIG_FLAGS, P099_FLAGS_SEND_XY)); + bitWrite(lSettings, TOUCH_FLAGS_ROTATION_FLIPPED, bitRead(P099_CONFIG_FLAGS, P099_FLAGS_ROTATION_FLIPPED)); + + config += bitRead(P099_CONFIG_FLAGS, P099_FLAGS_USE_CALIBRATION); // First value should not be empty + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(bitRead(P099_CONFIG_FLAGS, P099_FLAGS_LOG_CALIBRATION) ? 1 : 0); + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(StoredSettings.Calibration.top_left.x); + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(StoredSettings.Calibration.top_left.y); + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(StoredSettings.Calibration.bottom_right.x); + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(StoredSettings.Calibration.bottom_right.y); + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(P099_CONFIG_DEBOUNCE_MS); + config += TOUCH_SETTINGS_SEPARATOR; + config += ull2String(lSettings); + + // 'Store' in new format + settingsArray[TOUCH_CALIBRATION_START] = config; + uint8_t obj = TOUCH_OBJECT_INDEX_START; + + for (uint8_t s = 0; s < P099_CONFIG_OBJECTCOUNT; ++s) { + config.clear(); + config += StoredSettings.TouchObjects[s].objectname; // Name + config.trim(); // Remove leading/trailing whitespace from name + + if (!config.isEmpty()) { // Empty name => skip entry + const bool numStart = isdigit(config[0]); // Numeric start? + bool enabled = true; + + if (!ExtraTaskSettings.checkInvalidCharInNames(config.c_str()) || + numStart) { // Check for invalid characters in objectname + enabled = false; + } + + if (config[0] == '_') { // Disabled-marker for P099: prefixed with _ + enabled = false; + } + config += TOUCH_SETTINGS_SEPARATOR; + + // Convert flags + uint32_t flags{}; + bitWrite(flags, TOUCH_OBJECT_FLAG_ENABLED, enabled); // Enabled + bitWrite(flags, TOUCH_OBJECT_FLAG_INVERTED, + bitRead(StoredSettings.TouchObjects[s].flags, P099_FLAGS_INVERT_BUTTON)); // Inverted + bitWrite(flags, TOUCH_OBJECT_FLAG_BUTTON, + bitRead(StoredSettings.TouchObjects[s].flags, P099_FLAGS_ON_OFF_BUTTON)); // On/Off button + + config += ull2String(flags); // Flags + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(StoredSettings.TouchObjects[s].top_left.x); // Top x + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(StoredSettings.TouchObjects[s].top_left.y); // Top y + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(StoredSettings.TouchObjects[s].bottom_right.x); // Bottom x + config += TOUCH_SETTINGS_SEPARATOR; + config += toStringNoZero(StoredSettings.TouchObjects[s].bottom_right.y); // Bottom y + // Store in new format + settingsArray[obj] = config; + ++obj; + } + } + } + } + + if (loadStandardConfig) // false when settings are loaded from old P099 config + # endif // if defined(USES_P099) && P099_ENABLE_OLD_CONFIG && defined(ESP32) + { + LoadCustomTaskSettings(event->TaskIndex, settingsArray, TOUCH_ARRAY_SIZE, 0); + } lastObjectIndex = TOUCH_OBJECT_INDEX_START - 1; // START must be > 0!!! @@ -180,19 +268,19 @@ void ESPEasy_TouchHandler::loadTouchObjects(struct EventStruct *event) { for (uint8_t i = TOUCH_OBJECT_INDEX_START; i <= lastObjectIndex; ++i) { if (!settingsArray[i].isEmpty()) { tTouchObjects touchObject{}; - touchObject.flags = parseStringToInt(settingsArray[i], TOUCH_OBJECT_FLAGS, TOUCH_SETTINGS_SEPARATOR); - String objectName = parseStringKeepCase(settingsArray[i], TOUCH_OBJECT_NAME, TOUCH_SETTINGS_SEPARATOR); + touchObject.flags = parseStringToInt(settingsArray[i], TOUCH_OBJECT_FLAGS, TOUCH_SETTINGS_SEPARATOR); + String objectName = parseStringKeepCase(settingsArray[i], TOUCH_OBJECT_NAME, TOUCH_SETTINGS_SEPARATOR); touchObject.objectName = objectName; touchObject.top_left.x = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COORD_TOP_X, TOUCH_SETTINGS_SEPARATOR); touchObject.top_left.y = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COORD_TOP_Y, TOUCH_SETTINGS_SEPARATOR); touchObject.width_height.x = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COORD_WIDTH, TOUCH_SETTINGS_SEPARATOR); touchObject.width_height.y = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COORD_HEIGHT, TOUCH_SETTINGS_SEPARATOR); # if TOUCH_FEATURE_EXTENDED_TOUCH - touchObject.colorOn = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COLOR_ON, TOUCH_SETTINGS_SEPARATOR); - touchObject.colorOff = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COLOR_OFF, TOUCH_SETTINGS_SEPARATOR); - touchObject.colorCaption = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COLOR_CAPTION, TOUCH_SETTINGS_SEPARATOR); - String captionOn = parseStringKeepCase(settingsArray[i], TOUCH_OBJECT_CAPTION_ON, TOUCH_SETTINGS_SEPARATOR); - String captionOff = parseStringKeepCase(settingsArray[i], TOUCH_OBJECT_CAPTION_OFF, TOUCH_SETTINGS_SEPARATOR); + touchObject.colorOn = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COLOR_ON, TOUCH_SETTINGS_SEPARATOR); + touchObject.colorOff = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COLOR_OFF, TOUCH_SETTINGS_SEPARATOR); + touchObject.colorCaption = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COLOR_CAPTION, TOUCH_SETTINGS_SEPARATOR); + String captionOn = parseStringKeepCase(settingsArray[i], TOUCH_OBJECT_CAPTION_ON, TOUCH_SETTINGS_SEPARATOR); + String captionOff = parseStringKeepCase(settingsArray[i], TOUCH_OBJECT_CAPTION_OFF, TOUCH_SETTINGS_SEPARATOR); touchObject.captionOn = captionOn; touchObject.captionOff = captionOff; touchObject.colorBorder = parseStringToInt(settingsArray[i], TOUCH_OBJECT_COLOR_BORDER, TOUCH_SETTINGS_SEPARATOR); @@ -1764,7 +1852,7 @@ bool ESPEasy_TouchHandler::plugin_fifty_per_second(struct EventStruct *event, if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("Touch Swiped, direction: %s, dx: %d, dy: %d"), - String(toString(swipe)).c_str(), delta_x, delta_y)); + FsP(toString(swipe)), delta_x, delta_y)); } # endif // ifdef TOUCH_DEBUG } @@ -1846,9 +1934,12 @@ bool ESPEasy_TouchHandler::plugin_fifty_per_second(struct EventStruct *event, # if TOUCH_FEATURE_EXTENDED_TOUCH && TOUCH_FEATURE_SWIPE # ifdef TOUCH_DEBUG - addLogMove(LOG_LEVEL_INFO, - strformat(F("Swiped/touched, object: %s:%s"), _lastObjectName.c_str(), - String(toString(swipe)).c_str())); + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, + strformat(F("Swiped/touched, object: %s:%s"), _lastObjectName.c_str(), + FsP(toString(swipe)))); + } # endif // ifdef TOUCH_DEBUG if (swipe != Swipe_action_e::None) { @@ -1915,6 +2006,41 @@ void ESPEasy_TouchHandler::releaseTouch(struct EventStruct *event) { _stillTouching = false; } +const char touchHandler_commands[] PROGMEM = + "enable|disable|" + "on|off|" + "toggle|set|" + # if TOUCH_FEATURE_EXTENDED_TOUCH + # if TOUCH_FEATURE_SWIPE + "swipe|" + # endif // if TOUCH_FEATURE_SWIPE + "setgrp|nextgrp|prevgrp|" + "nextpage|prevpage|" + "updatebutton|" + # endif // if TOUCH_FEATURE_EXTENDED_TOUCH +; + +enum class touchHandler_commands_e : int8_t { + undefined = -1, + enable = 0, + disable = 1, + on = 2, + off = 3, + toggle = 4, + set = 5, + # if TOUCH_FEATURE_EXTENDED_TOUCH + # if TOUCH_FEATURE_SWIPE + swipe = 6, + # endif // if TOUCH_FEATURE_SWIPE + setgrp = 7, + nextgrp = 8, + prevgrp = 9, + nextpage = 10, + prevpage = 11, + updatebutton = 12, + # endif // if TOUCH_FEATURE_EXTENDED_TOUCH +}; + /** * Parse and execute the plugin commands */ @@ -1939,135 +2065,206 @@ bool ESPEasy_TouchHandler::plugin_write(struct EventStruct *event, } # endif // ifdef TOUCH_DEBUG - if (equals(subcommand, F("enable"))) { // touch,enable,[,...] : Enable disabled objectname(s) - arguments = parseString(string, arg); + const int subcommand_i = GetCommandCode(subcommand.c_str(), touchHandler_commands); - while (!arguments.isEmpty()) { - success |= setTouchObjectState(event, arguments, true); - arguments = parseString(string, ++arg); - } - } else if (equals(subcommand, F("disable"))) { // touch,disable,[,...] : Disable enabled objectname(s) - arguments = parseString(string, arg); + if (subcommand_i < 0) { return false; } // Fail fast - while (!arguments.isEmpty()) { - success |= setTouchObjectState(event, arguments, false); - arguments = parseString(string, ++arg); - } - } else if (equals(subcommand, F("on"))) { // touch,on,[,...] : Switch TouchButton(s) on - arguments = parseString(string, arg); + const touchHandler_commands_e subcmd = static_cast(subcommand_i); - while (!arguments.isEmpty()) { - success |= setTouchButtonOnOff(event, arguments, true); - arguments = parseString(string, ++arg); - } - } else if (equals(subcommand, F("off"))) { // touch,off,[,...] : Switch TouchButton(s) off - arguments = parseString(string, arg); + switch (subcmd) { + case touchHandler_commands_e::enable: // touch,enable,[,...] : Enable disabled objectname(s) + arguments = parseString(string, arg); - while (!arguments.isEmpty()) { - success |= setTouchButtonOnOff(event, arguments, false); - arguments = parseString(string, ++arg); - } - } else if (equals(subcommand, F("toggle"))) { // touch,toggle,[,...] : Switch TouchButton(s) to the other state - arguments = parseString(string, arg); + while (!arguments.isEmpty()) { + success |= setTouchObjectState(event, arguments, true); + arguments = parseString(string, ++arg); + } + break; + case touchHandler_commands_e::disable: // touch,disable,[,...] : Disable enabled objectname(s) + arguments = parseString(string, arg); - while (!arguments.isEmpty()) { - const int16_t state = getTouchObjectValue(event, arguments); + while (!arguments.isEmpty()) { + success |= setTouchObjectState(event, arguments, false); + arguments = parseString(string, ++arg); + } + break; + case touchHandler_commands_e::on: // touch,on,[,...] : Switch TouchButton(s) on + arguments = parseString(string, arg); - if (state > -1) { - success |= setTouchButtonOnOff(event, arguments, state == 0); + while (!arguments.isEmpty()) { + success |= setTouchButtonOnOff(event, arguments, true); + arguments = parseString(string, ++arg); } - arguments = parseString(string, ++arg); - } - } else if (equals(subcommand, F("set"))) { // touch,set,, : Set TouchObject value - arguments = parseString(string, arg); - success = setTouchObjectValue(event, arguments, event->Par3); - # if TOUCH_FEATURE_EXTENDED_TOUCH - # if TOUCH_FEATURE_EXTENDED_TOUCH && TOUCH_FEATURE_SWIPE - } else if (equals(subcommand, F("swipe"))) { // touch,swipe, : Switch button group via swipe value - success = handleButtonSwipe(event, event->Par2); - # endif // if TOUCH_FEATURE_EXTENDED_TOUCH && TOUCH_FEATURE_SWIPE - } else if (equals(subcommand, F("setgrp"))) { // touch,setgrp, : Activate button group - success = setButtonGroup(event, event->Par2); - } else if (equals(subcommand, F("nextgrp"))) { // touch,nextgrp : next group and Activate - success = nextButtonGroup(event); - } else if (equals(subcommand, F("prevgrp"))) { // touch,prevgrp : previous group and Activate - success = prevButtonGroup(event); - } else if (equals(subcommand, F("nextpage"))) { // touch,nextpage : next page and Activate - success = nextButtonPage(event); - } else if (equals(subcommand, F("prevpage"))) { // touch,prevpage : previous page and Activate - success = prevButtonPage(event); - } else if (equals(subcommand, F("updatebutton"))) { // touch,updatebutton,[,[,]] : Update a button - arguments = parseString(string, 3); - - // Check for a valid button name or number, returns a 0-based index - const int8_t index = getTouchObjectIndex(event, arguments, true); - - if (index > -1) { - const bool hasPar3 = !parseString(string, 4).isEmpty(); - const bool hasPar4 = !parseString(string, 5).isEmpty(); - - if (hasPar4) { - success = displayButton(event, index, event->Par3, event->Par4); - } else if (hasPar3) { - success = displayButton(event, index, event->Par3); - } else { - success = displayButton(event, index); // Use default argument values + break; + case touchHandler_commands_e::off: // touch,off,[,...] : Switch TouchButton(s) off + arguments = parseString(string, arg); + + while (!arguments.isEmpty()) { + success |= setTouchButtonOnOff(event, arguments, false); + arguments = parseString(string, ++arg); } + break; + case touchHandler_commands_e::toggle: // touch,toggle,[,...] : Switch TouchButton(s) to the other state + arguments = parseString(string, arg); + + while (!arguments.isEmpty()) { + const int16_t state = getTouchObjectValue(event, arguments); + + if (state > -1) { + success |= setTouchButtonOnOff(event, arguments, state == 0); + } + arguments = parseString(string, ++arg); + } + break; + case touchHandler_commands_e::set: // touch,set,, : Set TouchObject value + arguments = parseString(string, arg); + success = setTouchObjectValue(event, arguments, event->Par3); + break; + # if TOUCH_FEATURE_EXTENDED_TOUCH + # if TOUCH_FEATURE_EXTENDED_TOUCH && TOUCH_FEATURE_SWIPE + case touchHandler_commands_e::swipe: // touch,swipe, : Switch button group via swipe value + success = handleButtonSwipe(event, event->Par2); + break; + # endif // if TOUCH_FEATURE_EXTENDED_TOUCH && TOUCH_FEATURE_SWIPE + case touchHandler_commands_e::setgrp: // touch,setgrp, : Activate button group + success = setButtonGroup(event, event->Par2); + break; + case touchHandler_commands_e::nextgrp: // touch,nextgrp : next group and Activate + success = nextButtonGroup(event); + break; + case touchHandler_commands_e::prevgrp: // touch,prevgrp : previous group and Activate + success = prevButtonGroup(event); + break; + case touchHandler_commands_e::nextpage: // touch,nextpage : next page and Activate + success = nextButtonPage(event); + break; + case touchHandler_commands_e::prevpage: // touch,prevpage : previous page and Activate + success = prevButtonPage(event); + break; + case touchHandler_commands_e::updatebutton: // touch,updatebutton,[,[,]] : Update a button + { + arguments = parseString(string, 3); + + // Check for a valid button name or number, returns a 0-based index + const int8_t index = getTouchObjectIndex(event, arguments, true); + + if (index > -1) { + const bool hasPar3 = !parseString(string, 4).isEmpty(); + const bool hasPar4 = !parseString(string, 5).isEmpty(); + + if (hasPar4) { + success = displayButton(event, index, event->Par3, event->Par4); + } else if (hasPar3) { + success = displayButton(event, index, event->Par3); + } else { + success = displayButton(event, index); // Use default argument values + } + } + break; } - # endif // if TOUCH_FEATURE_EXTENDED_TOUCH + # endif // if TOUCH_FEATURE_EXTENDED_TOUCH + case touchHandler_commands_e::undefined: + break; } } return success; } +const char touchHandler_getconfig[] PROGMEM = + "buttongroup|" + "enabled|state|" + # if TOUCH_FEATURE_EXTENDED_TOUCH + "hasgroup|pagemode|" + # if TOUCH_FEATURE_SWIPE + "swipedir|" + # endif // if TOUCH_FEATURE_SWIPE + # endif // if TOUCH_FEATURE_EXTENDED_TOUCH +; + +enum class touchHandler_getconfig_e : int8_t { + undefined = -1, + buttongroup = 0, + enabled = 1, + state = 2, + # if TOUCH_FEATURE_EXTENDED_TOUCH + hasgroup = 3, + pagemode = 4, + # if TOUCH_FEATURE_SWIPE + swipedir = 5, + # endif // if TOUCH_FEATURE_SWIPE + # endif // if TOUCH_FEATURE_EXTENDED_TOUCH +}; + /** * Handle getting config values from plugin/handler */ bool ESPEasy_TouchHandler::plugin_get_config_value(struct EventStruct *event, String & string) { - bool success = false; - const String command = parseString(string, 1); + bool success = false; + const String command = parseString(string, 1); + const int command_i = GetCommandCode(command.c_str(), touchHandler_getconfig); - if (equals(command, F("buttongroup"))) { - string = getButtonGroup(); - success = true; - # if TOUCH_FEATURE_EXTENDED_TOUCH - } else if (equals(command, F("hasgroup"))) { - int32_t group; // We'll be ignoring group 0 if there are multiple button groups + if (command_i < 0) { return false; } // Fail fast - if (validIntFromString(parseString(string, 2), group)) { - string = validButtonGroup(group, true) ? 1 : 0; - success = true; - } else { - string = '0'; // invalid number = false - } - # endif // if TOUCH_FEATURE_EXTENDED_TOUCH - } else if (equals(command, F("enabled"))) { - const int8_t enabled = getTouchObjectState(event, parseStringKeepCase(string, 2)); + const touchHandler_getconfig_e cmd = static_cast(command_i); - if (enabled > -1) { - string = enabled; + switch (cmd) { + case touchHandler_getconfig_e::buttongroup: + string = getButtonGroup(); success = true; + break; + # if TOUCH_FEATURE_EXTENDED_TOUCH + case touchHandler_getconfig_e::hasgroup: + { + int32_t group; // We'll be ignoring group 0 if there are multiple button groups + + if (validIntFromString(parseString(string, 2), group)) { + string = validButtonGroup(group, true) ? 1 : 0; + success = true; + } else { + string = '0'; // invalid number = false + } + break; } - } else if (equals(command, F("state"))) { - const int16_t state = getTouchObjectValue(event, parseStringKeepCase(string, 2)); + # endif // if TOUCH_FEATURE_EXTENDED_TOUCH + case touchHandler_getconfig_e::enabled: + { + const int8_t enabled = getTouchObjectState(event, parseStringKeepCase(string, 2)); - string = state; - success = true; - # if TOUCH_FEATURE_EXTENDED_TOUCH - } else if (equals(command, F("pagemode"))) { - string = bitRead(Touch_Settings.flags, TOUCH_FLAGS_PGUP_BELOW_MENU); - success = true; - # if TOUCH_FEATURE_SWIPE - } else if (equals(command, F("swipedir"))) { - int32_t state; + if (enabled > -1) { + string = enabled; + success = true; + } + break; + } + case touchHandler_getconfig_e::state: + { + const int16_t state = getTouchObjectValue(event, parseStringKeepCase(string, 2)); - if (validIntFromString(parseString(string, 2), state)) { - string = toString(static_cast(state)); + string = state; success = true; + break; } - # endif // if TOUCH_FEATURE_SWIPE - # endif // if TOUCH_FEATURE_EXTENDED_TOUCH + # if TOUCH_FEATURE_EXTENDED_TOUCH + case touchHandler_getconfig_e::pagemode: + string = bitRead(Touch_Settings.flags, TOUCH_FLAGS_PGUP_BELOW_MENU); + success = true; + break; + # if TOUCH_FEATURE_SWIPE + case touchHandler_getconfig_e::swipedir: + { + int32_t state; + + if (validIntFromString(parseString(string, 2), state)) { + string = toString(static_cast(state)); + success = true; + } + break; + } + # endif // if TOUCH_FEATURE_SWIPE + # endif // if TOUCH_FEATURE_EXTENDED_TOUCH + case touchHandler_getconfig_e::undefined: + break; } return success; } diff --git a/src/src/Helpers/ESPEasy_TouchHandler.h b/src/src/Helpers/ESPEasy_TouchHandler.h index dee2e435f8..e38f403963 100644 --- a/src/src/Helpers/ESPEasy_TouchHandler.h +++ b/src/src/Helpers/ESPEasy_TouchHandler.h @@ -9,6 +9,7 @@ /***** * Changelog: + * 2025-09-27 tonhuisman: Add migration of P099 old config to TouchHandler config (that was originally developed from the P099 config) * 2024-03-20 tonhuisman: Change inc/dec* commands to next/prev* commands to more accurately describe their function * 2024-03-13 tonhuisman: Change PageUp/PageDown reversed option to Navigation Left/Right/Up/Down menu reversed, to also swap the behavior * of the left and right navigation buttons, like the Up/Down navigation already had. @@ -76,6 +77,10 @@ * swipedir,directionId : Get the name for the swipe direction provided in numeric form */ +# ifdef USES_P099 +# include "../PluginStructs/P099_data_struct_defs.h" +# endif // ifdef USES_P099 + # define TOUCH_DEBUG // Additional debugging information # define TOUCH_FEATURE_TOOLTIPS 1 // Enable/disable tooltips in UI diff --git a/src/src/PluginStructs/P099_data_struct.cpp b/src/src/PluginStructs/P099_data_struct.cpp index cd278a37b2..62df19a5cb 100644 --- a/src/src/PluginStructs/P099_data_struct.cpp +++ b/src/src/PluginStructs/P099_data_struct.cpp @@ -11,21 +11,30 @@ # include +P099_data_struct::P099_data_struct() { + # ifdef ESP32 + touchHandler = new (std::nothrow) ESPEasy_TouchHandler(); // Temporary object to be able to call loadTouchObjects + # endif // ifdef ESP32 +} + P099_data_struct::~P099_data_struct() { - if (touchscreen != nullptr) { - delete touchscreen; - touchscreen = nullptr; - } + delete touchscreen; + # ifdef ESP32 + delete touchHandler; + # endif // ifdef ESP32 } /** * Proper reset and cleanup. */ void P099_data_struct::reset() { - if (touchscreen != nullptr) { - delete touchscreen; - touchscreen = nullptr; - } + delete touchscreen; + touchscreen = nullptr; + # ifdef ESP32 + delete touchHandler; + touchHandler = nullptr; + # endif // ifdef ESP32 + # ifdef PLUGIN_099_DEBUG addLog(LOG_LEVEL_INFO, F("P099 DEBUG Touchscreen reset.")); # endif // PLUGIN_099_DEBUG @@ -34,6 +43,250 @@ void P099_data_struct::reset() { /** * Initialize data and set up the touchscreen. */ +# ifdef ESP32 + +bool P099_data_struct::init(struct EventStruct *event, + uint8_t cs, + uint8_t rotation, + bool flipped, + uint8_t z_treshold, + uint16_t ts_x_res, + uint16_t ts_y_res) { + reset(); + + _address_ts_cs = cs; + _z_treshold = z_treshold; + _rotation = rotation; + _ts_x_res = ts_x_res; + _ts_y_res = ts_y_res; + + touchHandler = new (std::nothrow) ESPEasy_TouchHandler(static_cast(P099_GET_CONFIG_DISPLAY), + static_cast(P099_COLOR_DEPTH)); + + if (nullptr != touchHandler) { + touchHandler->init(event); + _flipped = touchHandler->_flipped; + + if (touchHandler->touchEnabled()) { + auto spi_ptr = getSPIBusForTask(event->TaskIndex); + if (!spi_ptr) { return false; } + touchscreen = new (std::nothrow) XPT2046_Touchscreen( + _address_ts_cs, + *spi_ptr); + + if (touchscreen != nullptr) { + touchscreen->setRotation(_rotation); + touchscreen->setRotationFlipped(_flipped); + touchscreen->begin(); + } + } + + // loadTouchObjects(event); + # ifdef PLUGIN_099_DEBUG + addLogMove(LOG_LEVEL_INFO, + concat(concat(F("P099 DEBUG Plugin"), nullptr != touchscreen ? F(" & touchscreen") : F("")), F(" initialized."))); + } else { + addLog(LOG_LEVEL_INFO, F("P099 DEBUG Touchscreen initialisation FAILED.")); + # endif // PLUGIN_099_DEBUG + } + return isInitialized(); +} + +/** + * Properly initialized? then true + */ +bool P099_data_struct::isInitialized() const { + return touchHandler != nullptr && (!touchHandler->touchEnabled() || touchscreen != nullptr); +} + +/** + * Load the settings onto the webpage + */ +bool P099_data_struct::plugin_webform_load(struct EventStruct *event) { + if (nullptr != touchHandler) { + return touchHandler->plugin_webform_load(event); + } + return false; +} + +/** + * Save the settings from the web page to flash + */ +bool P099_data_struct::plugin_webform_save(struct EventStruct *event) { + if (nullptr != touchHandler) { + const bool result = touchHandler->plugin_webform_save(event); + P099_SET_CONFIG_VTYPE(touchHandler->get_device_valuecount(event)); // Store 'locally' + return result; + } + return false; +} + +/** + * Every 1/50th second we check if the screen is touched + */ +bool P099_data_struct::plugin_fifty_per_second(struct EventStruct *event) { + if (isInitialized() && touchHandler->touchEnabled()) { + if (touched()) { + int16_t x = 0; + int16_t y = 0; + uint8_t z = 0; + readData(x, y, z); + + if ((z == P099_TOUCH_Z_INVALID) || (z <= _z_treshold)) { // Not past the threshold + return false; + } + + int16_t rx = x; // Keep raw values + int16_t ry = y; + scaleRawToCalibrated(x, y); // Map to screen coordinates if so configured + + return touchHandler->plugin_fifty_per_second(event, x, y, x, y, rx, ry, z); + } else { + touchHandler->releaseTouch(event); + } + } + return false; +} + +/** + * Handle getting config values, delegated to ESPEasy_TouchHandler + */ +bool P099_data_struct::plugin_get_config_value(struct EventStruct *event, + String & string) { + if (nullptr != touchHandler) { + return touchHandler->plugin_get_config_value(event, string); + } + return false; +} + +/** + * Check if the screen is touched. + */ +bool P099_data_struct::touched() { + if (isInitialized()) { + return touchscreen->touched(); + } + return false; +} + +/** + * Read the raw data if the touchscreen is initialized. + */ +void P099_data_struct::readData(int16_t& x, int16_t& y, uint8_t& z) { + if (isInitialized()) { + uint16_t cx = x; + uint16_t cy = y; + touchscreen->readData(&cx, &cy, &z); + x = cx; + y = cy; + # ifdef PLUGIN_099_DEBUG + addLog(LOG_LEVEL_INFO, F("P099 DEBUG readData")); + # endif // PLUGIN_099_DEBUG + } +} + +/** + * Only set rotation if the touchscreen is initialized. + */ +void P099_data_struct::setRotation(uint8_t n) { + if (isInitialized()) { + touchscreen->setRotation(n); + # ifdef PLUGIN_099_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLogMove(LOG_LEVEL_INFO, concat(F("P099 DEBUG Rotation set: "), (int)n)); + } + # endif // PLUGIN_099_DEBUG + } +} + +/** + * Only set rotationFlipped if the touchscreen is initialized. + */ +void P099_data_struct::setRotationFlipped(bool flipped) { + if (isInitialized()) { + touchscreen->setRotationFlipped(flipped); + # ifdef PLUGIN_099_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log = F("P099 DEBUG RotationFlipped set: "); + log += flipped; + addLogMove(LOG_LEVEL_INFO, log); + } + # endif // PLUGIN_099_DEBUG + } +} + +/** + * Scale the provided raw coordinates to screen-resolution coordinates if calibration is enabled/configured + */ +void P099_data_struct::scaleRawToCalibrated(int16_t& x, + int16_t& y) { + if ((nullptr != touchHandler) && touchHandler->isCalibrationActive()) { + int16_t lx = x - touchHandler->Touch_Settings.top_left.x; + + if (lx <= 0) { + x = 0; + } else { + if (lx > touchHandler->Touch_Settings.bottom_right.x) { + lx = touchHandler->Touch_Settings.bottom_right.x; + } + float x_fact = static_cast(touchHandler->Touch_Settings.bottom_right.x - touchHandler->Touch_Settings.top_left.x) / + static_cast(_ts_x_res); + x = static_cast(round(lx / x_fact)); + } + int16_t ly = y - touchHandler->Touch_Settings.top_left.y; + + if (ly <= 0) { + y = 0; + } else { + if (ly > touchHandler->Touch_Settings.bottom_right.y) { + ly = touchHandler->Touch_Settings.bottom_right.y; + } + float y_fact = (touchHandler->Touch_Settings.bottom_right.y - touchHandler->Touch_Settings.top_left.y) / _ts_y_res; + y = static_cast(round(ly / y_fact)); + } + } +} + +/** + * Parse and execute the plugin commands, delegated to ESPEasy_TouchHandler + */ +bool P099_data_struct::plugin_write(struct EventStruct *event, const String& string) { + bool success = false; + const String command = parseString(string, 1); + const String subcommand = parseString(string, 2); + + if (isInitialized() && equals(command, F("touch"))) { + # ifdef PLUGIN_099_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("P099 WRITE arguments Par1: %d, 2: %d, 3: %d, 4: %d"), + event->Par1, event->Par2, event->Par3, event->Par4)); + } + # endif // ifdef PLUGIN_099_DEBUG + + if (equals(subcommand, F("rot"))) { // touch,rot,<0..3> : Set rotation to 0, 90, 180, 270 degrees + setRotation(static_cast(event->Par2 % 4)); + success = true; + } else if (equals(subcommand, F("flip"))) { // touch,flip,<0|1> : Flip rotation by 0 or 180 degrees + setRotationFlipped(event->Par2 > 0); + success = true; + } else { // Rest of the commands handled by ESPEasy_TouchHandler + success = touchHandler->plugin_write(event, string); + } + } + return success; +} + +# endif // ifdef ESP32 + +/******************************************************************************************************************************** +* Below code is using the old but smaller implementation for ESP8266, and should only get bugfixes, if needed +********************************************************************************************************************************/ + +# ifdef ESP8266 + bool P099_data_struct::init(taskIndex_t taskIndex, uint8_t cs, uint8_t rotation, @@ -67,11 +320,11 @@ bool P099_data_struct::init(taskIndex_t taskIndex, touchscreen->setRotationFlipped(_flipped); touchscreen->begin(); loadTouchObjects(taskIndex); - # ifdef PLUGIN_099_DEBUG + # ifdef PLUGIN_099_DEBUG addLog(LOG_LEVEL_INFO, F("P099 DEBUG Plugin & touchscreen initialized.")); } else { addLog(LOG_LEVEL_INFO, F("P099 DEBUG Touchscreen initialisation FAILED.")); - # endif // PLUGIN_099_DEBUG + # endif // PLUGIN_099_DEBUG } return isInitialized(); } @@ -87,12 +340,12 @@ bool P099_data_struct::isInitialized() const { * Load the touch objects from the settings, and initialize then properly where needed. */ void P099_data_struct::loadTouchObjects(taskIndex_t taskIndex) { - # ifdef PLUGIN_099_DEBUG + # ifdef PLUGIN_099_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("P099 DEBUG loadTouchObjects size: "), sizeof(StoredSettings))); } - # endif // PLUGIN_099_DEBUG + # endif // PLUGIN_099_DEBUG LoadCustomTaskSettings(taskIndex, reinterpret_cast(&StoredSettings), sizeof(StoredSettings)); for (int i = 0; i < P099_MaxObjectCount; i++) { @@ -119,9 +372,9 @@ bool P099_data_struct::touched() { void P099_data_struct::readData(uint16_t *x, uint16_t *y, uint8_t *z) { if (isInitialized()) { touchscreen->readData(x, y, z); - # ifdef PLUGIN_099_DEBUG + # ifdef PLUGIN_099_DEBUG addLog(LOG_LEVEL_INFO, F("P099 DEBUG readData")); - # endif // PLUGIN_099_DEBUG + # endif // PLUGIN_099_DEBUG } } @@ -131,12 +384,12 @@ void P099_data_struct::readData(uint16_t *x, uint16_t *y, uint8_t *z) { void P099_data_struct::setRotation(uint8_t n) { if (isInitialized()) { touchscreen->setRotation(n); - # ifdef PLUGIN_099_DEBUG + # ifdef PLUGIN_099_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("P099 DEBUG Rotation set: "), (int)n)); } - # endif // PLUGIN_099_DEBUG + # endif // PLUGIN_099_DEBUG } } @@ -146,14 +399,14 @@ void P099_data_struct::setRotation(uint8_t n) { void P099_data_struct::setRotationFlipped(bool flipped) { if (isInitialized()) { touchscreen->setRotationFlipped(flipped); - # ifdef PLUGIN_099_DEBUG + # ifdef PLUGIN_099_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("P099 DEBUG RotationFlipped set: "); log += flipped; addLogMove(LOG_LEVEL_INFO, log); } - # endif // PLUGIN_099_DEBUG + # endif // PLUGIN_099_DEBUG } } @@ -204,7 +457,7 @@ bool P099_data_struct::isValidAndTouchedTouchObject(uint16_t x, lastObjectArea = SurfaceAreas[objectNr]; selected = true; } - # ifdef PLUGIN_099_DEBUG + # ifdef PLUGIN_099_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("P099 DEBUG Touched: obj: "); @@ -229,7 +482,7 @@ bool P099_data_struct::isValidAndTouchedTouchObject(uint16_t x, log += selectedObjectIndex; addLogMove(LOG_LEVEL_INFO, log); } - # endif // PLUGIN_099_DEBUG + # endif // PLUGIN_099_DEBUG } } return selected; @@ -261,7 +514,7 @@ bool P099_data_struct::setTouchObjectState(const String& touchObject, bool state } } StoredSettings.TouchObjects[objectNr].objectname[P099_MaxObjectNameLength - 1] = 0; // Just to be safe - # ifdef PLUGIN_099_DEBUG + # ifdef PLUGIN_099_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("P099 setTouchObjectState: obj: "); @@ -276,7 +529,7 @@ bool P099_data_struct::setTouchObjectState(const String& touchObject, bool state } addLogMove(LOG_LEVEL_INFO, log); } - # endif // PLUGIN_099_DEBUG + # endif // PLUGIN_099_DEBUG // break; // Only first one found is processed } } @@ -320,6 +573,7 @@ enum class p099_subcommands_e { flip, enable, disable + }; bool P099_data_struct::plugin_write(struct EventStruct *event, const String& string) { @@ -340,7 +594,8 @@ bool P099_data_struct::plugin_write(struct EventStruct *event, const String& str return false; } - switch (static_cast(command_i)) { + switch (static_cast(command_i)) + { case p099_subcommands_e::rot: { // touch,rot,<0..3> : Set rotation to 0, 90, 180, 270 degrees @@ -375,4 +630,6 @@ bool P099_data_struct::plugin_write(struct EventStruct *event, const String& str return false; } +# endif // ifdef ESP8266 + #endif // ifdef USES_P099 diff --git a/src/src/PluginStructs/P099_data_struct.h b/src/src/PluginStructs/P099_data_struct.h index 30ee738f64..8ccdbebed3 100644 --- a/src/src/PluginStructs/P099_data_struct.h +++ b/src/src/PluginStructs/P099_data_struct.h @@ -2,53 +2,39 @@ #define PLUGINSTRUCTS_P099_DATA_STRUCT_H #include "../../_Plugin_Helper.h" -#include "../../ESPEasy_common.h" + +// #include "../../ESPEasy_common.h" #ifdef USES_P099 +# ifdef ESP32 +# include "../Helpers/ESPEasy_TouchHandler.h" +# endif // ifdef ESP32 + # include // #define PLUGIN_099_DEBUG // Additional debugging information +# ifndef LIMIT_BUILD_SIZE +# define PLUGIN_099_DEBUG // Additional debugging information +# else // ifndef LIMIT_BUILD_SIZE +# ifndef P099_LIMIT_BUILD_SIZE // Can be set from elsewhere +# define P099_LIMIT_BUILD_SIZE +# endif // ifndef P099_LIMIT_BUILD_SIZE +# endif // ifndef LIMIT_BUILD_SIZE + +# if defined(BUILD_NO_DEBUG) && defined(PLUGIN_099_DEBUG) +# undef PLUGIN_099_DEBUG +# endif // if defined(BUILD_NO_DEBUG) && defined(PLUGIN_099_DEBUG) + +# include "../PluginStructs/P099_data_struct_defs.h" // Define default values for both ESP32/lolin32 and D1 Mini # ifdef ESP32 # define P099_TS_CS 12 # else // ESP8266/ESP8285 - # define P099_TS_CS 0 // D3 + # define P099_TS_CS 0 // D3 # endif // ESP32 -# define P099_MaxObjectNameLength 15 // 14 character objectnames + terminating 0 -# define P099_MaxObjectCount 40 // This count of touchobjects should be enough, because of limited settings storage, 960 bytes + 8 - // bytes calibration coordinates - -# define P099_FLAGS_ON_OFF_BUTTON 0 // TouchObjects.flags On/Off Button function -# define P099_FLAGS_INVERT_BUTTON 1 // TouchObjects.flags Inverted On/Off Button function - -# define P099_FLAGS_SEND_XY 0 // Set in P099_CONFIG_FLAGS -# define P099_FLAGS_SEND_Z 1 // Set in P099_CONFIG_FLAGS -# define P099_FLAGS_SEND_OBJECTNAME 2 // Set in P099_CONFIG_FLAGS -# define P099_FLAGS_USE_CALIBRATION 3 // Set in P099_CONFIG_FLAGS -# define P099_FLAGS_LOG_CALIBRATION 4 // Set in P099_CONFIG_FLAGS -# define P099_FLAGS_ROTATION_FLIPPED 5 // Set in P099_CONFIG_FLAGS - -# define P099_CONFIG_STATE PCONFIG(0) -# define P099_CONFIG_CS_PIN PIN(0) -# define P099_CONFIG_TRESHOLD PCONFIG(1) -# define P099_CONFIG_ROTATION PCONFIG(2) -# define P099_CONFIG_X_RES PCONFIG(3) -# define P099_CONFIG_Y_RES PCONFIG(4) -# define P099_CONFIG_OBJECTCOUNT PCONFIG(5) -# define P099_CONFIG_DEBOUNCE_MS PCONFIG(6) -# define P099_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags - -# define P099_VALUE_X UserVar[event->BaseVarIndex + 0] -# define P099_VALUE_Y UserVar[event->BaseVarIndex + 1] -# define P099_VALUE_Z UserVar[event->BaseVarIndex + 2] - -# define P099_SET_VALUE_X(v) UserVar.setFloat(event->TaskIndex, 0, v) -# define P099_SET_VALUE_Y(v) UserVar.setFloat(event->TaskIndex, 1, v) -# define P099_SET_VALUE_Z(v) UserVar.setFloat(event->TaskIndex, 2, v) - # define P099_TS_TRESHOLD 15 // Treshold before the value is registered as a proper touch # define P099_TS_ROTATION 2 // Rotation 0-3 = 0/90/180/270 degrees, compatible with TFT ILI9341 # define P099_TS_SEND_XY true // Enable X/Y events @@ -59,8 +45,6 @@ # define P099_TS_ROTATION_FLIPPED false // Enable rotation flipped 180 deg. # define P099_TS_X_RES 240 // Pixels, should match with the screen it is mounted on # define P099_TS_Y_RES 320 -# define P099_INIT_OBJECTCOUNT 8 // Initial setting -# define P099_DEBOUNCE_MILLIS 150 // Debounce delay for On/Off button function # define P099_TOUCH_X_INVALID 4095 // When picking up spurious noise (or an open/not connected TS-CS pin), these are the values that // turn up @@ -71,10 +55,19 @@ // Data structure struct P099_data_struct : public PluginTaskData_base { - P099_data_struct() = default; + P099_data_struct(); virtual ~P099_data_struct(); - void reset(); + # ifdef ESP32 + bool init(struct EventStruct *event, + uint8_t cs, + uint8_t rotation, + bool flipped, + uint8_t z_treshold, + uint16_t ts_x_res, + uint16_t ts_y_res); + # endif // ifdef ESP32 + # ifdef ESP8266 bool init(taskIndex_t taskIndex, uint8_t cs, uint8_t rotation, @@ -85,14 +78,38 @@ struct P099_data_struct : public PluginTaskData_base bool useCalibration, uint16_t ts_x_res, uint16_t ts_y_res); - bool isInitialized() const; void loadTouchObjects(taskIndex_t taskIndex); + # endif // ifdef ESP8266 + bool isInitialized() const; + + bool plugin_webform_load(struct EventStruct *event); + bool plugin_webform_save(struct EventStruct *event); + bool plugin_write(struct EventStruct *event, + const String & string); + # ifdef ESP32 + bool plugin_fifty_per_second(struct EventStruct *event); + bool plugin_get_config_value(struct EventStruct *event, + String & string); + +private: + + # endif // ifdef ESP32 + + void reset(); bool touched(); + void setRotation(uint8_t n); + void setRotationFlipped(bool _flipped); + # ifdef ESP32 + void readData(int16_t& x, + int16_t& y, + uint8_t& z); + void scaleRawToCalibrated(int16_t& x, + int16_t& y); + # endif // ifdef ESP32 + # ifdef ESP8266 void readData(uint16_t *x, uint16_t *y, uint8_t *z); - void setRotation(uint8_t n); - void setRotationFlipped(bool _flipped); bool isCalibrationActive(); bool isValidAndTouchedTouchObject(uint16_t x, uint16_t y, @@ -104,21 +121,23 @@ struct P099_data_struct : public PluginTaskData_base uint8_t checkObjectCount); void scaleRawToCalibrated(uint16_t& x, uint16_t& y); - - bool plugin_write(struct EventStruct *event, - const String & string); + # endif // ifdef ESP8266 // This is initialized by calling init() - XPT2046_Touchscreen *touchscreen = nullptr; - uint8_t _address_ts_cs = 0; - uint8_t _rotation = 0; - bool _flipped = 0; - uint8_t _z_treshold = 0; - bool _send_xy = 0; - bool _send_z = 0; - bool _useCalibration = 0; - uint16_t _ts_x_res = 0; - uint16_t _ts_y_res = 0; + # ifdef ESP32 + ESPEasy_TouchHandler *touchHandler = nullptr; + # endif // ifdef ESP32 + XPT2046_Touchscreen *touchscreen = nullptr; + uint8_t _address_ts_cs = 0; + uint8_t _rotation = 0; + bool _flipped = false; + uint8_t _z_treshold = 0; + uint16_t _ts_x_res = 0; + uint16_t _ts_y_res = 0; + # ifdef ESP8266 + bool _send_xy = 0; + bool _send_z = 0; + bool _useCalibration = 0; // This is filled during checking of a touchobject uint32_t SurfaceAreas[P099_MaxObjectCount] = { 0 }; @@ -127,40 +146,10 @@ struct P099_data_struct : public PluginTaskData_base uint32_t TouchTimers[P099_MaxObjectCount] = { 0 }; bool TouchStates[P099_MaxObjectCount] = { 0 }; - // The settings structures - // Lets define our own coordinate point - struct tP099_Point - { - uint16_t x = 0; - uint16_t y = 0; - }; - - // For touch objects we store a name and 2 coordinates - struct tP099_Touchobjects - { - char objectname[P099_MaxObjectNameLength] = { 0 }; - uint8_t flags = 0; - tP099_Point top_left; - tP099_Point bottom_right; - }; - - // Only 2 coordinates used for calibration (we must assume that the touch panel is mounted straight on to tft) - struct tP099_Calibration - { - tP099_Point top_left; - tP099_Point bottom_right; - }; - - // The stuff we want to save between settings (Calibration coordinates and touchable objects) - struct tP099_StoredSettings_struct - { - tP099_Calibration Calibration; - tP099_Touchobjects TouchObjects[P099_MaxObjectCount]; - }; - // Stored settings data: tP099_StoredSettings_struct StoredSettings; + # endif // ifdef ESP8266 }; -#endif // ifdef USED_P099 +#endif // ifdef USES_P099 #endif // ifndef PLUGINSTRUCTS_P099_DATA_STRUCT_H diff --git a/src/src/PluginStructs/P099_data_struct_defs.h b/src/src/PluginStructs/P099_data_struct_defs.h new file mode 100644 index 0000000000..db5f1d852c --- /dev/null +++ b/src/src/PluginStructs/P099_data_struct_defs.h @@ -0,0 +1,110 @@ +#pragma once + +#include "../../_Plugin_Helper.h" + +#ifdef USES_P099 + +# if !defined(P099_ENABLE_OLD_CONFIG) && defined(ESP32) +# define P099_ENABLE_OLD_CONFIG 1 // When set to 0 will also inhibit the 1-time settings conversion in ESPEasy_TouchHandler +# endif // if !defined(P099_ENABLE_OLD_CONFIG) && defined(ESP32) +# ifdef ESP8266 +# if defined(P099_ENABLE_OLD_CONFIG) +# undef P099_ENABLE_OLD_CONFIG +# endif // if defined(P099_ENABLE_OLD_CONFIG) +# define P099_ENABLE_OLD_CONFIG 0 // Using old implementation, so shouldn't convert anything +# endif // ifdef ESP8266 + + +# if P099_ENABLE_OLD_CONFIG || defined(ESP8266) +# define P099_FLAGS_ON_OFF_BUTTON 0 // TouchObjects.flags On/Off Button function +# define P099_FLAGS_INVERT_BUTTON 1 // TouchObjects.flags Inverted On/Off Button function +# endif // if P099_ENABLE_OLD_CONFIG || defined(ESP8266) + +# define P099_FLAGS_SEND_XY 0 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_Z 1 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_OBJECTNAME 2 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_USE_CALIBRATION 3 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_LOG_CALIBRATION 4 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_ROTATION_FLIPPED 5 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_CONFIG_VTYPE 6 // 4 bits to store the VType +# define P099_FLAGS_CONFIG_DISPLAY 10 // 8 bits to store the Display task + +# ifdef ESP32 +# define P099_CONFIG_VERSION PCONFIG(0) +# endif // ifdef ESP32 +# ifdef ESP8266 +# define P099_CONFIG_STATE PCONFIG(0) +# endif // ifdef ESP8266 +# define P099_CONFIG_CS_PIN PIN(0) +# define P099_CONFIG_THRESHOLD PCONFIG(1) +# define P099_CONFIG_ROTATION PCONFIG(2) +# define P099_CONFIG_X_RES PCONFIG(3) +# define P099_CONFIG_Y_RES PCONFIG(4) +# if P099_ENABLE_OLD_CONFIG || defined(ESP8266) +# define P099_CONFIG_OBJECTCOUNT PCONFIG(5) +# define P099_CONFIG_DEBOUNCE_MS PCONFIG(6) +# endif // if P099_ENABLE_OLD_CONFIG || defined(ESP8266) +# define P099_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags +# ifdef ESP32 +# define P099_CONFIG_DISPLAY_PREV PCONFIG(7) +# define P099_COLOR_DEPTH PCONFIG_LONG(1) + +# define P099_GET_CONFIG_VTYPE get4BitFromUL(P099_CONFIG_FLAGS, P099_FLAGS_CONFIG_VTYPE) +# define P099_SET_CONFIG_VTYPE(v) set4BitToUL(P099_CONFIG_FLAGS, P099_FLAGS_CONFIG_VTYPE, v) +# define P099_GET_CONFIG_DISPLAY get8BitFromUL(P099_CONFIG_FLAGS, P099_FLAGS_CONFIG_DISPLAY) +# define P099_SET_CONFIG_DISPLAY(v) set8BitToUL(P099_CONFIG_FLAGS, P099_FLAGS_CONFIG_DISPLAY, v) +# endif // ifdef ESP32 + +# define P099_VALUE_X UserVar.getFloat(event->TaskIndex, 0) +# define P099_VALUE_Y UserVar.getFloat(event->TaskIndex, 1) +# define P099_VALUE_Z UserVar.getFloat(event->TaskIndex, 2) + +# define P099_SET_VALUE_X(v) UserVar.setFloat(event->TaskIndex, 0, v) +# define P099_SET_VALUE_Y(v) UserVar.setFloat(event->TaskIndex, 1, v) +# define P099_SET_VALUE_Z(v) UserVar.setFloat(event->TaskIndex, 2, v) + +# if P099_ENABLE_OLD_CONFIG || defined(ESP8266) +# define P099_INIT_OBJECTCOUNT 8 // Initial setting +# define P099_DEBOUNCE_MILLIS 150 // Debounce delay for On/Off button function +# endif // if P099_ENABLE_OLD_CONFIG || defined(ESP8266) + + +# if P099_ENABLE_OLD_CONFIG || defined(ESP8266) +# define P099_MaxObjectNameLength 15 // 14 character objectnames + terminating 0 +# define P099_MaxObjectCount 40 // This count of touchobjects should be enough, because of limited settings storage, 960 bytes + 8 + // bytes calibration coordinates + + +// The settings structures +// Lets define our own coordinate point +struct tP099_Point +{ + uint16_t x = 0; + uint16_t y = 0; +}; + +// For touch objects we store a name and 2 coordinates +struct tP099_Touchobjects +{ + char objectname[P099_MaxObjectNameLength] = { 0 }; + uint8_t flags = 0; + tP099_Point top_left; + tP099_Point bottom_right; +}; + +// Only 2 coordinates used for calibration (we must assume that the touch panel is mounted straight on to tft) +struct tP099_Calibration +{ + tP099_Point top_left; + tP099_Point bottom_right; +}; + +// The stuff we want to save between settings (Calibration coordinates and touchable objects) +struct tP099_StoredSettings_struct +{ + tP099_Calibration Calibration; + tP099_Touchobjects TouchObjects[P099_MaxObjectCount]; +}; +# endif // if P099_ENABLE_OLD_CONFIG || defined(ESP8266) + +#endif // ifdef USES_P099 diff --git a/src/src/PluginStructs/P123_data_struct.cpp b/src/src/PluginStructs/P123_data_struct.cpp index f4501d51a2..f82a751b15 100644 --- a/src/src/PluginStructs/P123_data_struct.cpp +++ b/src/src/PluginStructs/P123_data_struct.cpp @@ -28,9 +28,10 @@ bool P123_data_struct::plugin_i2c_has_address(const int Par1) { } uint8_t P123_data_struct::plugin_i2c_address(P123_TouchType_e touchType) { - const int tType = static_cast(touchType); + const int tType = static_cast(touchType); + constexpr int i2csize = NR_ELEMENTS(P123_i2cAddressValues); - if ((tType >= 0) && (tType < NR_ELEMENTS(P123_i2cAddressValues))) { + if ((tType >= 0) && (tType < i2csize)) { return P123_i2cAddressValues[tType]; } return 0u; @@ -214,12 +215,9 @@ bool P123_data_struct::plugin_webform_save(struct EventStruct *event) { */ bool P123_data_struct::plugin_write(struct EventStruct *event, const String & string) { - bool success = false; - String command; - String subcommand; - - command = parseString(string, 1); - subcommand = parseString(string, 2); + bool success = false; + const String command = parseString(string, 1); + const String subcommand = parseString(string, 2); if (isInitialized() && equals(command, F("touch"))) { # ifdef PLUGIN_123_DEBUG