Merge branch 'mega' into feature/build_core_3_0_0

This commit is contained in:
TD-er
2022-01-27 23:37:02 +01:00
committed by GitHub
31 changed files with 1287 additions and 211 deletions
+33 -54
View File
@@ -182,6 +182,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = false;
Device[deviceCount].TimerOptional = true;
break;
}
@@ -315,14 +316,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string)
2000);
{
// For load and save of the display lines, we must not rely on the data in memory.
// This data in memory can be altered through write commands.
// Therefore we must read the lines from flash in a temporary object.
P016_data_struct *P016_data = new (std::nothrow) P016_data_struct();
if (nullptr != P016_data) {
P016_data->loadCommandLines(event); // load saved codes and commands
{
addFormSubHeader(F("Code - command map"));
int size = static_cast<int>(decode_type_t::kLastDecodeType) + 1;
@@ -366,6 +360,9 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string)
int rowCnt = 0;
for (uint8_t varNr = 0; varNr < P16_Nlines; varNr++) {
tCommandLinesV2 line;
P016_data_struct::loadCommandLine(event, line, varNr);
html_TR_TD();
if (varNr < 9) {
@@ -375,30 +372,30 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string)
html_TD();
{ // Decode type
addSelector(getPluginCustomArgName(rowCnt + 0), size, &decodeTypes[0], &decodeTypeOptions[0], NULL,
static_cast<int>(P016_data->CommandLines[varNr].CodeDecodeType), false, true, EMPTY_STRING);
static_cast<int>(line.CodeDecodeType), false, true, EMPTY_STRING);
}
html_TD();
addCheckBox(getPluginCustomArgName(rowCnt + 1), bitRead(P016_data->CommandLines[varNr].CodeFlags, P16_FLAGS_REPEAT));
addCheckBox(getPluginCustomArgName(rowCnt + 1), bitRead(line.CodeFlags, P16_FLAGS_REPEAT));
html_TD();
strCode = EMPTY_STRING;
if (P016_data->CommandLines[varNr].Code > 0) {
strCode = uint64ToString(P016_data->CommandLines[varNr].Code, 16); // convert code to hex for display
if (line.Code > 0) {
strCode = uint64ToString(line.Code, 16); // convert code to hex for display
}
addTextBox(getPluginCustomArgName(rowCnt + 2), strCode, P16_Cchars - 1, false, false, P016_HEX_INPUT_PATTERN, EMPTY_STRING);
html_TD();
{
addSelector(getPluginCustomArgName(rowCnt + 3), size, &decodeTypes[0], &decodeTypeOptions[0], NULL,
static_cast<int>(P016_data->CommandLines[varNr].AlternativeCodeDecodeType), false, true, EMPTY_STRING);
static_cast<int>(line.AlternativeCodeDecodeType), false, true, EMPTY_STRING);
}
html_TD();
addCheckBox(getPluginCustomArgName(rowCnt + 4), bitRead(P016_data->CommandLines[varNr].AlternativeCodeFlags, P16_FLAGS_REPEAT));
addCheckBox(getPluginCustomArgName(rowCnt + 4), bitRead(line.AlternativeCodeFlags, P16_FLAGS_REPEAT));
html_TD();
strCode = EMPTY_STRING;
if (P016_data->CommandLines[varNr].AlternativeCode > 0) {
strCode = uint64ToString(P016_data->CommandLines[varNr].AlternativeCode, 16); // convert code to hex for display
if (line.AlternativeCode > 0) {
strCode = uint64ToString(line.AlternativeCode, 16); // convert code to hex for display
}
addTextBox(getPluginCustomArgName(rowCnt + 5), strCode, P16_Cchars - 1, false, false, P016_HEX_INPUT_PATTERN, EMPTY_STRING);
@@ -409,15 +406,12 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string)
addHtmlInt(varNr + 1);
addHtml(':');
addHtml(F("<TD colspan=\"5\">")); // Use as much of available width (though limited to 500px by css)
addTextBox(getPluginCustomArgName(rowCnt + 6), String(P016_data->CommandLines[varNr].Command), P16_Nchars - 1);
addTextBox(getPluginCustomArgName(rowCnt + 6), String(line.Command), P16_Nchars - 1);
rowCnt += 7;
delay(0);
}
html_end_table();
// Need to delete the allocated object here
delete P016_data;
}
if (P016_SETTINGS_VERSION != P16_SETTINGS_LATEST) {
@@ -454,65 +448,58 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string)
PCONFIG_LONG(0) = lSettings;
P016_CMDINHIBIT = getFormItemInt(F("p016_cmdinhibit"));
{
// For load and save of the display lines, we must not rely on the data in memory.
// This data in memory can be altered through write commands.
// Therefore we must use a temporary version to store the settings.
P016_data_struct *P016_data = new (std::nothrow) P016_data_struct();
# ifdef PLUGIN_016_DEBUG
P016_infoLogMemory(F("before save"));
# endif // ifdef PLUGIN_016_DEBUG
if (nullptr != P016_data) {
char strCode[P16_Cchars];
{
String strError;
strError.reserve(30); // Length of expected string, needed for strings > 11 chars
String strID;
uint64_t iCode;
int rowCnt = 0;
P016_data->CommandLines.clear(); // Start fresh
for (uint8_t varNr = 0; varNr < P16_Nlines; varNr++) {
P016_data->CommandLines.push_back(tCommandLinesV2());
tCommandLinesV2 line;
strError = EMPTY_STRING;
// Normal Code & flags
P016_data->CommandLines[varNr].CodeDecodeType = static_cast<decode_type_t>(getFormItemInt(getPluginCustomArgName(rowCnt + 0)));
bitWrite(P016_data->CommandLines[varNr].CodeFlags, P16_FLAGS_REPEAT, isFormItemChecked(getPluginCustomArgName(rowCnt + 1)));
iCode = 0;
line.CodeDecodeType = static_cast<decode_type_t>(getFormItemInt(getPluginCustomArgName(rowCnt + 0)));
bitWrite(line.CodeFlags, P16_FLAGS_REPEAT, isFormItemChecked(getPluginCustomArgName(rowCnt + 1)));
line.Code = 0;
char strCode[P16_Cchars] = {0};
if (!safe_strncpy(strCode, webArg(getPluginCustomArgName(rowCnt + 2)), P16_Cchars)) {
strError += F("Code ");
strError += (varNr + 1);
strError += ' ';
} else {
iCode = hexToULL(strCode); // convert string with hexnumbers to uint64_t
line.Code = hexToULL(strCode); // convert string with hexnumbers to uint64_t
}
P016_data->CommandLines[varNr].Code = iCode;
delay(0);
// Alternate Code & flags
P016_data->CommandLines[varNr].AlternativeCodeDecodeType =
line.AlternativeCodeDecodeType =
static_cast<decode_type_t>(getFormItemInt(getPluginCustomArgName(rowCnt + 3)));
bitWrite(P016_data->CommandLines[varNr].AlternativeCodeFlags, P16_FLAGS_REPEAT,
bitWrite(line.AlternativeCodeFlags, P16_FLAGS_REPEAT,
isFormItemChecked(getPluginCustomArgName(rowCnt + 4)));
iCode = 0;
line.AlternativeCode = 0;
if (!safe_strncpy(strCode, webArg(getPluginCustomArgName(rowCnt + 5)), P16_Cchars)) {
strError += F("Alt.Code ");
strError += (varNr + 1);
strError += ' ';
} else {
iCode = hexToULL(strCode); // convert string with hexnumbers to uint64_t
line.AlternativeCode = hexToULL(strCode); // convert string with hexnumbers to uint64_t
}
P016_data->CommandLines[varNr].AlternativeCode = iCode;
// Command
if (!safe_strncpy(P016_data->CommandLines[varNr].Command, webArg(getPluginCustomArgName(rowCnt + 6)), P16_Nchars)) {
if (!safe_strncpy(line.Command, webArg(getPluginCustomArgName(rowCnt + 6)), P16_Nchars)) {
strError += F("Command ");
strError += (varNr + 1);
}
P016_data->CommandLines[varNr].Command[P16_Nchars - 1] = 0; // Terminate string
line.Command[P16_Nchars - 1] = 0; // Terminate string
if (!strError.isEmpty()) {
addHtmlError(strError);
@@ -520,21 +507,13 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string)
rowCnt += 7;
delay(0);
P016_data_struct::saveCommandLine(event, line, varNr);
}
# ifdef PLUGIN_016_DEBUG
P016_infoLogMemory(F("before save"));
# endif // ifdef PLUGIN_016_DEBUG
P016_data->saveCommandLines(event);
# ifdef PLUGIN_016_DEBUG
P016_infoLogMemory(F("after save"));
# endif // ifdef PLUGIN_016_DEBUG
// Need to delete the allocated object here
delete P016_data;
}
}
# ifdef PLUGIN_016_DEBUG
+1
View File
@@ -52,6 +52,7 @@ boolean Plugin_017(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = false;
Device[deviceCount].TimerOptional = true;
Device[deviceCount].GlobalSyncOption = true;
break;
}
+1
View File
@@ -63,6 +63,7 @@ boolean Plugin_022(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].ValueCount = 0;
Device[deviceCount].Custom = true;
Device[deviceCount].TimerOption = false;
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
-11
View File
@@ -461,17 +461,6 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string)
}
}
P036_data_struct *P036_data =
static_cast<P036_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr != P036_data) {
// After saving, make sure the active lines are updated.
P036_data->frameCounter = 0;
P036_data->MaxFramesToDisplay = 0xFF;
P036_data->disp_resolution = static_cast<p036_resolution>(P036_RESOLUTION);
P036_data->loadDisplayLines(event->TaskIndex, 1);
}
#ifdef PLUGIN_036_DEBUG
addLog(LOG_LEVEL_INFO, F("P036_PLUGIN_WEBFORM_SAVE Done"));
#endif // PLUGIN_036_DEBUG
+2
View File
@@ -158,6 +158,8 @@ boolean Plugin_052(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
Device[deviceCount].OutputDataType = Output_Data_type_t::Simple;
// FIXME TD-er: Seems to use task data, but not sure if really needed.
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+1
View File
@@ -56,6 +56,7 @@ boolean Plugin_062(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].TimerOption = true;
Device[deviceCount].TimerOptional = true;
Device[deviceCount].GlobalSyncOption = true;
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+2
View File
@@ -166,6 +166,8 @@ boolean Plugin_070(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].SendDataOption = false;
Device[deviceCount].TimerOption = false;
Device[deviceCount].GlobalSyncOption = false;
// FIXME TD-er: Not sure if access to any existing task data is needed when saving
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+2
View File
@@ -704,6 +704,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].TimerOption = false;
Device[deviceCount].TimerOptional = false;
Device[deviceCount].GlobalSyncOption = true;
// FIXME TD-er: Not sure if access to any existing task data is needed when saving
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+3
View File
@@ -121,6 +121,9 @@ boolean Plugin_075(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].TimerOption = true;
Device[deviceCount].TimerOptional = true; // Allow user to disable interval function.
Device[deviceCount].GlobalSyncOption = true;
// FIXME TD-er: Not sure if access to any existing task data is needed when saving
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+1
View File
@@ -105,6 +105,7 @@ boolean Plugin_085(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+2
View File
@@ -65,6 +65,8 @@ boolean Plugin_087(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = false;
// FIXME TD-er: Not sure if access to any existing task data is needed when saving
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+2
View File
@@ -73,6 +73,8 @@ boolean Plugin_094(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = false;
// Device[deviceCount].DuplicateDetection = true;
// FIXME TD-er: Not sure if access to any existing task data is needed when saving
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+1
View File
@@ -56,6 +56,7 @@ boolean Plugin_097(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].SendDataOption = true;
Device[deviceCount].DecimalsOnly = false;
Device[deviceCount].TimerOption = false;
Device[deviceCount].TimerOptional = true;
Device[deviceCount].GlobalSyncOption = true;
break;
}
+433
View File
@@ -0,0 +1,433 @@
#include "_Plugin_Helper.h"
#ifdef USES_P098
// #######################################################################################################
// ######################## Plugin 098 PWM Motor I2C Barometric Pressure Sensor ########################
// #######################################################################################################
# include "src/PluginStructs/P098_data_struct.h"
# define PLUGIN_098
# define PLUGIN_ID_098 98
# define PLUGIN_NAME_098 "Motor - PWM Motor"
# define PLUGIN_VALUENAME1_098 "Position"
# define PLUGIN_VALUENAME2_098 "LimitA"
# define PLUGIN_VALUENAME3_098 "LimitB"
# define PLUGIN_VALUENAME4_098 "LimitApos"
# define P098_PWM_FREQ PCONFIG_LONG(0)
# define P098_FLAGS PCONFIG_LONG(1)
# define P098_LIMIT_SWA_GPIO PCONFIG(0)
# define P098_LIMIT_SWB_GPIO PCONFIG(1)
# define P098_MOTOR_CONTROL PCONFIG(2)
# ifdef ESP32
# define P098_ANALOG_GPIO PCONFIG(3)
# endif // ifdef ESP32
# define P098_LIMIT_SWA_DEBOUNCE PCONFIG(4)
# define P098_LIMIT_SWB_DEBOUNCE PCONFIG(5)
# define P098_ENC_TIMEOUT PCONFIG(6)
# define P098_PWM_DUTY PCONFIG(7)
# define P098_FLAGBIT_LIM_A_PULLUP 0
# define P098_FLAGBIT_LIM_B_PULLUP 1
# define P098_FLAGBIT_ENC_IN_PULLUP 2
# define P098_FLAGBIT_LIM_A_INVERTED 3
# define P098_FLAGBIT_LIM_B_INVERTED 4
# define P098_FLAGBIT_MOTOR_FWD_INVERTED 5
# define P098_FLAGBIT_MOTOR_REV_INVERTED 6
# define P098_FLAGBIT_PWM_SOFT_STARTSTOP 7
boolean Plugin_098(uint8_t function, struct EventStruct *event, String& string)
{
boolean success = false;
switch (function)
{
case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_098;
Device[deviceCount].Type = DEVICE_TYPE_CUSTOM0;
Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = false;
Device[deviceCount].ValueCount = 4;
Device[deviceCount].SendDataOption = false;
Device[deviceCount].TimerOption = false;
Device[deviceCount].TimerOptional = true;
break;
}
case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_098);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_098));
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_098));
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_098));
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_098));
break;
}
case PLUGIN_WEBFORM_SHOW_GPIO_DESCR:
{
addHtml(F("M Fwd:"));
addHtml(F("&nbsp;"));
addHtml(formatGpioLabel(Settings.TaskDevicePin1[event->TaskIndex], true));
addHtml(event->String1);
addHtml(F("M Rev:"));
addHtml(F("&nbsp;"));
addHtml(formatGpioLabel(Settings.TaskDevicePin2[event->TaskIndex], true));
addHtml(event->String1);
addHtml(F("Enc:"));
addHtml(F("&nbsp;"));
addHtml(formatGpioLabel(Settings.TaskDevicePin3[event->TaskIndex], true));
addHtml(event->String1);
# ifdef ESP32
addHtml(F("Analog:"));
addHtml(F("&nbsp;"));
addHtml(formatGpioLabel(P098_ANALOG_GPIO, true));
addHtml(event->String1);
# endif // ifdef ESP32
addHtml(F("Lim A:"));
addHtml(F("&nbsp;"));
addHtml(formatGpioLabel(P098_LIMIT_SWA_GPIO, true));
addHtml(event->String1);
addHtml(F("Lim B:"));
addHtml(F("&nbsp;"));
addHtml(formatGpioLabel(P098_LIMIT_SWB_GPIO, true));
success = true;
break;
}
/*
case PLUGIN_WEBFORM_SHOW_VALUES:
{
P098_data_struct *P098_data =
static_cast<P098_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr != P098_data) {
byte varNr = VARS_PER_TASK;
int limitApos, limitBpos;
P098_data->getLimitSwitchPositions(limitApos, limitBpos);
pluginWebformShowValue(event->TaskIndex, varNr++, F("LimitBpos"), String(limitBpos));
pluginWebformShowValue(event->TaskIndex, varNr++, F("State"), String(static_cast<int>(P098_data->state)), true);
// success = true;
}
break;
}
*/
case PLUGIN_SET_DEFAULTS:
{
P098_FLAGS = 0;
P098_LIMIT_SWA_GPIO = -1;
P098_LIMIT_SWB_GPIO = -1;
P098_LIMIT_SWA_DEBOUNCE = 100;
P098_LIMIT_SWB_DEBOUNCE = 100;
P098_ENC_TIMEOUT = 100;
P098_MOTOR_CONTROL = 0; // No PWM
P098_PWM_FREQ = 1000;
P098_PWM_DUTY = 1023;
# ifdef ESP32
P098_ANALOG_GPIO = -1; // Analog feedback
# endif // ifdef ESP32
break;
}
case PLUGIN_WEBFORM_LOAD:
{
addFormSubHeader(F("Motor Control"));
// We load/save the TaskDevicePin ourselves to allow to combine the pin specific configuration be shown along with the pin selection.
addFormPinSelect(PinSelectPurpose::Generic_output,
formatGpioName_output(F("Motor Fwd")),
F("taskdevicepin1"),
Settings.TaskDevicePin1[event->TaskIndex]);
addFormCheckBox(F("Motor Fwd Inverted"), F("mot_fwd_inv"), bitRead(P098_FLAGS, P098_FLAGBIT_MOTOR_FWD_INVERTED));
addFormSeparator(2);
addFormPinSelect(PinSelectPurpose::Generic_output,
formatGpioName_output(F("Motor Rev")),
F("taskdevicepin2"),
Settings.TaskDevicePin2[event->TaskIndex]);
addFormCheckBox(F("Motor Rev Inverted"), F("mot_rev_inv"), bitRead(P098_FLAGS, P098_FLAGBIT_MOTOR_REV_INVERTED));
addFormSeparator(2);
{
# define P098_PWM_MODE_TYPES static_cast<int>(P098_config_struct::PWM_mode_type::MAX_TYPE)
String options[P098_PWM_MODE_TYPES];
int optionValues[P098_PWM_MODE_TYPES];
for (int i = 0; i < P098_PWM_MODE_TYPES; ++i) {
options[i] = P098_config_struct::toString(static_cast<P098_config_struct::PWM_mode_type>(i));
optionValues[i] = i;
}
addFormSelector(F("Motor Control"), F("p098_motor_contr"), P098_PWM_MODE_TYPES, options, optionValues, P098_MOTOR_CONTROL);
}
addFormNumericBox(F("PWM Frequency"), F("p098_pwm_freq"), P098_PWM_FREQ, 50, 100000);
addUnit(F("Hz"));
addFormNumericBox(F("PWM Duty Cycle"), F("p098_pwm_duty"), P098_PWM_DUTY, 0, 1023);
addFormCheckBox(F("PWM Soft Start/Stop"), F("pwm_soft_st"), bitRead(P098_FLAGS, P098_FLAGBIT_PWM_SOFT_STARTSTOP));
addFormSubHeader(F("Feedback"));
addFormPinSelect(PinSelectPurpose::Generic_input,
formatGpioName_input_optional(F("Encoder")),
F("taskdevicepin3"),
Settings.TaskDevicePin3[event->TaskIndex]);
addFormCheckBox(F("Encoder Pull-Up"), F("enc_pu"), bitRead(P098_FLAGS, P098_FLAGBIT_ENC_IN_PULLUP));
addFormNumericBox(F("Encoder Timeout"), F("enc_timeout"), P098_ENC_TIMEOUT, 0, 1000);
addUnit(F("ms"));
# ifdef ESP32
{
addRowLabel(formatGpioName_input_optional(F("Analog Feedback")));
addADC_PinSelect(AdcPinSelectPurpose::ADC_Touch_Optional, F("analogpin"), P098_ANALOG_GPIO);
}
# endif // ifdef ESP32
addFormSubHeader(F("Limit Switches"));
addFormPinSelect(PinSelectPurpose::Generic_input,
formatGpioName_input_optional(F("Limit A")),
F("limit_a"),
P098_LIMIT_SWA_GPIO);
addFormCheckBox(F("Limit A Pull-Up"), F("limit_a_pu"), bitRead(P098_FLAGS, P098_FLAGBIT_LIM_A_PULLUP));
addFormCheckBox(F("Limit A Inverted"), F("limit_a_inv"), bitRead(P098_FLAGS, P098_FLAGBIT_LIM_A_INVERTED));
addFormNumericBox(F("Limit A Debounce"), F("limit_a_debounce"), P098_LIMIT_SWA_DEBOUNCE, 0, 1000);
addUnit(F("ms"));
addFormSeparator(2);
addFormPinSelect(PinSelectPurpose::Generic_input,
formatGpioName_input_optional(F("Limit B")),
F("limit_b"),
P098_LIMIT_SWB_GPIO);
addFormCheckBox(F("Limit B Pull-Up"), F("limit_b_pu"), bitRead(P098_FLAGS, P098_FLAGBIT_LIM_B_PULLUP));
addFormCheckBox(F("Limit B Inverted"), F("limit_b_inv"), bitRead(P098_FLAGS, P098_FLAGBIT_LIM_B_INVERTED));
addFormNumericBox(F("Limit B Debounce"), F("limit_b_debounce"), P098_LIMIT_SWB_DEBOUNCE, 0, 1000);
addUnit(F("ms"));
success = true;
break;
}
case PLUGIN_WEBFORM_SAVE:
{
// We load/save the TaskDevicePin ourselves to allow to combine the pin specific configuration be shown along with the pin selection.
Settings.TaskDevicePin1[event->TaskIndex] = getFormItemInt(F("taskdevicepin1"));
Settings.TaskDevicePin2[event->TaskIndex] = getFormItemInt(F("taskdevicepin2"));
Settings.TaskDevicePin3[event->TaskIndex] = getFormItemInt(F("taskdevicepin3"));
P098_ENC_TIMEOUT = getFormItemInt(F("enc_timeout"));
P098_LIMIT_SWA_GPIO = getFormItemInt(F("limit_a"));
P098_LIMIT_SWB_GPIO = getFormItemInt(F("limit_b"));
P098_LIMIT_SWA_DEBOUNCE = getFormItemInt(F("limit_a_debounce"));
P098_LIMIT_SWB_DEBOUNCE = getFormItemInt(F("limit_b_debounce"));
P098_MOTOR_CONTROL = getFormItemInt(F("p098_motor_contr"));
P098_PWM_FREQ = getFormItemInt(F("p098_pwm_freq"));
P098_PWM_DUTY = getFormItemInt(F("p098_pwm_duty"));
# ifdef ESP32
P098_ANALOG_GPIO = getFormItemInt(F("analogpin"));
# endif // ifdef ESP32
P098_FLAGS = 0;
if (isFormItemChecked(F("mot_fwd_inv"))) { bitSet(P098_FLAGS, P098_FLAGBIT_MOTOR_FWD_INVERTED); }
if (isFormItemChecked(F("mot_rev_inv"))) { bitSet(P098_FLAGS, P098_FLAGBIT_MOTOR_REV_INVERTED); }
if (isFormItemChecked(F("pwm_soft_st"))) { bitSet(P098_FLAGS, P098_FLAGBIT_PWM_SOFT_STARTSTOP); }
if (isFormItemChecked(F("enc_pu"))) { bitSet(P098_FLAGS, P098_FLAGBIT_ENC_IN_PULLUP); }
if (isFormItemChecked(F("limit_a_pu"))) { bitSet(P098_FLAGS, P098_FLAGBIT_LIM_A_PULLUP); }
if (isFormItemChecked(F("limit_b_pu"))) { bitSet(P098_FLAGS, P098_FLAGBIT_LIM_B_PULLUP); }
if (isFormItemChecked(F("limit_a_inv"))) { bitSet(P098_FLAGS, P098_FLAGBIT_LIM_A_INVERTED); }
if (isFormItemChecked(F("limit_b_inv"))) { bitSet(P098_FLAGS, P098_FLAGBIT_LIM_B_INVERTED); }
success = true;
break;
}
case PLUGIN_INIT:
{
P098_config_struct config;
config.motorFwd.gpio = CONFIG_PIN1;
config.motorRev.gpio = CONFIG_PIN2;
config.encoder.gpio = CONFIG_PIN3;
config.encoder.timer_us = P098_ENC_TIMEOUT * 1000;
config.limitA.gpio = P098_LIMIT_SWA_GPIO;
config.limitB.gpio = P098_LIMIT_SWB_GPIO;
config.limitA.timer_us = P098_LIMIT_SWA_DEBOUNCE * 1000;
config.limitB.timer_us = P098_LIMIT_SWB_DEBOUNCE * 1000;
# ifdef ESP32
config.gpio_analogIn = P098_ANALOG_GPIO;
# endif // ifdef ESP32
config.motorFwd.inverted = bitRead(P098_FLAGS, P098_FLAGBIT_MOTOR_FWD_INVERTED);
config.motorRev.inverted = bitRead(P098_FLAGS, P098_FLAGBIT_MOTOR_REV_INVERTED);
config.limitA.inverted = bitRead(P098_FLAGS, P098_FLAGBIT_LIM_A_INVERTED);
config.limitB.inverted = bitRead(P098_FLAGS, P098_FLAGBIT_LIM_B_INVERTED);
config.limitA.pullUp = bitRead(P098_FLAGS, P098_FLAGBIT_LIM_A_PULLUP);
config.limitB.pullUp = bitRead(P098_FLAGS, P098_FLAGBIT_LIM_B_PULLUP);
config.encoder.pullUp = bitRead(P098_FLAGS, P098_FLAGBIT_ENC_IN_PULLUP);
config.PWM_mode = static_cast<P098_config_struct::PWM_mode_type>(P098_MOTOR_CONTROL);
config.pwm_soft_startstop = bitRead(P098_FLAGS, P098_FLAGBIT_PWM_SOFT_STARTSTOP);
config.pwm_duty_cycle = P098_PWM_DUTY;
initPluginTaskData(event->TaskIndex, new (std::nothrow) P098_data_struct(config));
P098_data_struct *P098_data =
static_cast<P098_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr != P098_data) {
// Restore the last known values on warm boot.
P098_data->begin(
UserVar[event->BaseVarIndex + 0], // Position
UserVar[event->BaseVarIndex + 3], // limitApos
0); // limitBpos is not stored in RTC
success = true;
}
break;
}
case PLUGIN_FIFTY_PER_SECOND:
{
P098_data_struct *P098_data =
static_cast<P098_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr != P098_data) {
bool limitA_triggered, limitB_triggered = false;
P098_data->getLimitSwitchStates(limitA_triggered, limitB_triggered);
if (!P098_data->loop()) {}
switch (P098_data->state) {
case P098_data_struct::State::Idle:
case P098_data_struct::State::RunFwd:
case P098_data_struct::State::RunRev:
break;
case P098_data_struct::State::StopLimitSw:
case P098_data_struct::State::StopPosReached:
case P098_data_struct::State::StopEncoderTimeout:
{
if (Settings.UseRules) {
String RuleEvent = getTaskDeviceName(event->TaskIndex);
RuleEvent += '#';
if (limitA_triggered) {
eventQueue.addMove(String(RuleEvent + F("limitA")));
}
if (limitB_triggered) {
eventQueue.addMove(String(RuleEvent + F("limitB")));
}
if (P098_data->state == P098_data_struct::State::StopPosReached) {
eventQueue.addMove(String(RuleEvent + F("positionReached")));
}
if (P098_data->state == P098_data_struct::State::StopEncoderTimeout) {
eventQueue.addMove(String(RuleEvent + F("encoderTimeout")));
}
}
P098_data->state = P098_data_struct::State::Idle;
break;
}
}
// }
int limitApos, limitBpos;
P098_data->getLimitSwitchPositions(limitApos, limitBpos);
UserVar[event->BaseVarIndex + 0] = P098_data->getPosition();
UserVar[event->BaseVarIndex + 1] = limitA_triggered ? 1 : 0;
UserVar[event->BaseVarIndex + 2] = limitB_triggered ? 1 : 0;
UserVar[event->BaseVarIndex + 3] = limitApos;
}
break;
}
case PLUGIN_READ:
{
P098_data_struct *P098_data =
static_cast<P098_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr != P098_data) {
// What to do here?
}
break;
}
case PLUGIN_WRITE:
{
P098_data_struct *P098_data =
static_cast<P098_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr != P098_data) {
const String command = parseString(string, 1);
if (command.startsWith(F("pwmmotor"))) {
if (command == F("pwmmotorhome")) {
// Run the motor in reverse till limit A switch is reached
P098_data->findHome();
success = true;
} else if (command == F("pwmmotorend")) {
// Run the motor forward till limit B switch is reached
P098_data->moveForward(-1);
success = true;
} else if (command == F("pwmmotorforward")) {
// Run the motor N steps forward
// N <= 0: Move till limit B switch is reached
P098_data->moveForward(event->Par1);
success = true;
} else if (command == F("pwmmotorreverse")) {
// Run the motor N steps in reverse
P098_data->moveReverse(event->Par1);
success = true;
} else if (command == F("pwmmotorstop")) {
// Run the motor N steps in reverse
P098_data->stop();
success = true;
} else if (command == F("pwmmotormovetopos")) {
// Run the motor in the required direction to position N
// What to do when position is unknown?
if (!P098_data->moveToPos(event->Par1)) {
if (!P098_data->homePosSet()) {
addLog(LOG_LEVEL_ERROR, F("PWM motor: Home position unknown"));
} else {
addLog(LOG_LEVEL_ERROR, F("PWM motor: Cannot move to position"));
}
}
success = true;
}
}
}
break;
}
}
return success;
}
#endif // USES_P098
+1
View File
@@ -131,6 +131,7 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].TimerOption = false;
Device[deviceCount].TimerOptional = false;
Device[deviceCount].GlobalSyncOption = true;
Device[deviceCount].ExitTaskBeforeSave = false;
break;
}
+12 -1
View File
@@ -1141,6 +1141,7 @@ To create/register a plugin, you have to :
//#define USES_P096 // eInk (Needs lib_deps = Adafruit GFX Library, LOLIN_EPD )
#define USES_P097 // Touch (ESP32)
//#define USES_P099 // XPT2046 Touchscreen
#define USES_P098 // PWM motor (relies on iRAM, cannot be combined with all other plugins)
#define USES_P105 // AHT10/20/21
#endif
@@ -1172,6 +1173,9 @@ To create/register a plugin, you have to :
#ifdef PLUGIN_SET_TESTING_D
#define USES_P093 // Mitsubishi Heat Pump
#define USES_P094 // CUL Reader
#ifndef USES_P098
#define USES_P098 // PWM motor
#endif
#define USES_P114 // VEML6075 UVA/UVB sensor
#define USES_P115 // Fuel Gauge MAX1704x
#define USES_P117 // SCD30
@@ -1415,7 +1419,7 @@ To create/register a plugin, you have to :
#define USES_P096 // eInk (Needs lib_deps = Adafruit GFX Library, LOLIN_EPD )
#endif
#ifndef USES_P098
// #define USES_P098 // ESPEasy-NOW Receiver
#define USES_P098 // PWM motor
#endif
#ifndef USES_P099
#define USES_P099 // XPT2046 Touchscreen
@@ -1775,6 +1779,13 @@ To create/register a plugin, you have to :
#endif
// P098 PWM motor needs P003 pulse
#if defined(USES_P098)
#ifndef USES_P003
#define USES_P003
#endif
#endif
#ifdef USES_MQTT
// MQTT_MAX_PACKET_SIZE : Maximum packet size
#ifndef MQTT_MAX_PACKET_SIZE
+2 -1
View File
@@ -5,7 +5,8 @@ DeviceStruct::DeviceStruct() :
OutputDataType(Output_Data_type_t::Default),
PullUpOption(false), InverseLogicOption(false), FormulaOption(false),
Custom(false), SendDataOption(false), GlobalSyncOption(false),
TimerOption(false), TimerOptional(false), DecimalsOnly(false) {}
TimerOption(false), TimerOptional(false), DecimalsOnly(false),
ExitTaskBeforeSave(true) {}
bool DeviceStruct::connectedToGPIOpins() const {
switch(Type) {
+1
View File
@@ -93,6 +93,7 @@ struct DeviceStruct
bool TimerOption : 1; // Allow to set the "Interval" timer for the plugin.
bool TimerOptional : 1; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay)
bool DecimalsOnly : 1; // Allow to set the number of decimals (otherwise treated a 0 decimals)
bool ExitTaskBeforeSave : 1; // Optimization in memory usage, Do not exit when task data is needed during save.
};
typedef std::vector<DeviceStruct> DeviceVector;
+1 -1
View File
@@ -91,7 +91,7 @@ String flashGuard()
if (RTC.flashDayCounter > MAX_FLASHWRITES_PER_DAY)
{
String log = F("FS : Daily flash write rate exceeded! (powercycle to reset this)");
String log = F("FS : Daily flash write rate exceeded! (powercycle or send command 'resetFlashWriteCounter' to reset this)");
addLog(LOG_LEVEL_ERROR, log);
return log;
}
+75 -99
View File
@@ -7,13 +7,10 @@
# include <IRutils.h>
# ifdef P16_SETTINGS_V1
tCommandLinesV2::tCommandLinesV2() {} // Default constructor
// Conversion constructor
tCommandLinesV2::tCommandLinesV2(const String& command,
uint32_t oldCode,
uint32_t oldAlternativeCode,
uint8_t i) {
tCommandLinesV2::tCommandLinesV2(const tCommandLinesV1& lineV1, uint8_t i)
{
String log;
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
@@ -30,9 +27,8 @@ tCommandLinesV2::tCommandLinesV2(const String& command,
log += ':';
# endif // ifdef PLUGIN_016_DEBUG
if (command.length() > 0) {
safe_strncpy(Command, command, P16_Nchars);
}
memcpy(Command, lineV1.Command, P16_Nchars);
const uint32_t oldCode = lineV1.Code;
if (oldCode > 0) {
CodeDecodeType = static_cast<decode_type_t>((oldCode >> 24)); // decode_type
@@ -49,6 +45,8 @@ tCommandLinesV2::tCommandLinesV2(const String& command,
# endif // ifdef PLUGIN_016_DEBUG
}
const uint32_t oldAlternativeCode = lineV1.AlternativeCode;
if (oldAlternativeCode > 0) {
AlternativeCodeDecodeType = static_cast<decode_type_t>((oldAlternativeCode >> 24)); // decode_type
bitWrite(AlternativeCodeFlags, P16_FLAGS_REPEAT, oldAlternativeCode & (0x1 << P16_CMDBIT_REPEAT)); // Repeat flag
@@ -77,100 +75,77 @@ void P016_data_struct::init(struct EventStruct *event, uint16_t CmdInhibitTime)
iLastCmdTime = 0;
}
# ifdef P16_SETTINGS_V1
void P016_data_struct::convertCommandLines(struct EventStruct *event) {
String log;
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
# ifndef PLUGIN_016_DEBUG
log.reserve(20); // less space needed
# else // ifndef PLUGIN_016_DEBUG
log.reserve(80);
log = F("P016: struct size: ");
log += sizeof(tCommandLinesV2);
log += '/';
log += sizeof(tCommandLines);
log += F(" enum: ");
log += sizeof(decode_type_t);
log += F(" p016: ");
log += sizeof(P016_data_struct);
addLog(LOG_LEVEL_INFO, log);
# endif // ifdef PLUGIN_016_DEBUG
}
// read V1 data && convert
CommandLinesV1.clear(); // Start fresh
int loadOffset = 0;
for (uint8_t i = 0; i < P16_Nlines; i++) {
CommandLinesV1.push_back(tCommandLines());
LoadFromFile(SettingsType::Enum::CustomTaskSettings_Type,
event->TaskIndex,
reinterpret_cast<uint8_t *>(&(CommandLinesV1[i])),
sizeof(tCommandLines),
loadOffset);
loadOffset += sizeof(tCommandLines);
}
CommandLines.clear(); // Start fresh
for (int i = 0; i < P16_Nlines; ++i) {
CommandLines.push_back(tCommandLinesV2(String(CommandLinesV1[i].Command), CommandLinesV1[i].Code, CommandLinesV1[i].AlternativeCode, i));
delay(0);
}
CommandLinesV1.clear(); // clean up after conversion
}
# endif // ifdef P16_SETTINGS_V1
void P016_data_struct::loadCommandLines(struct EventStruct *event) {
# ifdef P16_SETTINGS_V1
// Convert the settings if both versions are defined and PCONFIG(7) != latest version
if (PCONFIG(7) != P16_SETTINGS_LATEST) {
addLog(LOG_LEVEL_ERROR, F("P016 IR: Settings conversion, save task settings to store in new format."));
convertCommandLines(event);
}
else
# endif // ifdef P16_SETTINGS_V1
{
// read V2 settings data
CommandLines.clear(); // Start fresh
CommandLines.clear(); // Start fresh
int loadOffset = 0;
for (uint8_t i = 0; i < P16_Nlines; i++) {
CommandLines.push_back(tCommandLinesV2());
LoadFromFile(SettingsType::Enum::CustomTaskSettings_Type,
event->TaskIndex,
reinterpret_cast<uint8_t *>(&(CommandLines[i])),
sizeof(tCommandLinesV2),
loadOffset);
loadOffset += sizeof(tCommandLinesV2);
}
}
for (int i = 0; i < P16_Nlines; ++i) {
CommandLines[i].Command[P16_Nchars - 1] = 0; // Terminate in case of uninitalized data
for (uint8_t i = 0; i < P16_Nlines; i++) {
CommandLines.push_back(tCommandLinesV2());
loadCommandLine(event, CommandLines[i], i);
}
}
void P016_data_struct::saveCommandLines(struct EventStruct *event) {
int saveOffset = 0;
for (uint8_t i = 0; i < P16_Nlines; i++) {
SaveToFile(SettingsType::Enum::CustomTaskSettings_Type,
event->TaskIndex,
reinterpret_cast<const uint8_t *>(&(CommandLines[i])),
sizeof(tCommandLinesV2),
saveOffset);
saveOffset += sizeof(tCommandLinesV2);
saveCommandLine(event, CommandLines[i], i);
}
}
void P016_data_struct::loadCommandLine(struct EventStruct *event, tCommandLinesV2& line, uint8_t lineNr)
{
# ifdef P16_SETTINGS_V1
if (PCONFIG(7) != P16_SETTINGS_LATEST) {
loadCommandLinev1(event, line, lineNr);
return;
}
# endif // ifdef P16_SETTINGS_V1
const int loadOffset = lineNr * sizeof(tCommandLinesV2);
LoadFromFile(SettingsType::Enum::CustomTaskSettings_Type,
event->TaskIndex,
reinterpret_cast<uint8_t *>(&line),
sizeof(tCommandLinesV2),
loadOffset);
line.Command[P16_Nchars - 1] = 0; // Terminate in case of uninitalized data
}
# ifdef P16_SETTINGS_V1
void P016_data_struct::loadCommandLinev1(struct EventStruct *event, tCommandLinesV2& line, uint8_t lineNr)
{
tCommandLinesV1 lineV1;
{
const int loadOffsetV1 = lineNr * sizeof(tCommandLinesV1);
LoadFromFile(SettingsType::Enum::CustomTaskSettings_Type,
event->TaskIndex,
reinterpret_cast<uint8_t *>(&lineV1),
sizeof(tCommandLinesV2),
loadOffsetV1);
}
line = tCommandLinesV2(lineV1, lineNr);
line.Command[P16_Nchars - 1] = 0;
}
# endif // ifdef P16_SETTINGS_V1
void P016_data_struct::saveCommandLine(struct EventStruct *event, const tCommandLinesV2& line, uint8_t lineNr)
{
const int saveOffset = lineNr * sizeof(tCommandLinesV2);
SaveToFile(SettingsType::Enum::CustomTaskSettings_Type,
event->TaskIndex,
reinterpret_cast<const uint8_t *>(&line),
sizeof(tCommandLinesV2),
saveOffset);
}
void P016_data_struct::AddCode(uint64_t Code, decode_type_t DecodeType, uint16_t CodeFlags) {
// add received code
int _index = P16_Nlines;
@@ -266,21 +241,22 @@ void P016_data_struct::ExecuteCode(uint64_t Code, decode_type_t DecodeType, uint
return;
}
# ifdef PLUGIN_016_DEBUG
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log;
log.reserve(128); // estimated
log = F("[P36] ValidateCode failed: ");
log += typeToString(DecodeType, bitRead(CodeFlags, P16_FLAGS_REPEAT));
log += F(" Code: 0x");
log += uint64ToString(Code, 16);
log += F(" / [");
log += (i + 1);
log += F("] = {");
log += typeToString(CommandLines[i].CodeDecodeType, bitRead(CommandLines[i].CodeFlags, P16_FLAGS_REPEAT));
log += F(" Code: 0x");
log += uint64ToString(CommandLines[i].Code, 16);
log += '}';
addLog(LOG_LEVEL_INFO, log);
String log;
log.reserve(128); // estimated
log = F("[P36] ValidateCode failed: ");
log += typeToString(DecodeType, bitRead(CodeFlags, P16_FLAGS_REPEAT));
log += F(" Code: 0x");
log += uint64ToString(Code, 16);
log += F(" / [");
log += (i + 1);
log += F("] = {");
log += typeToString(CommandLines[i].CodeDecodeType, bitRead(CommandLines[i].CodeFlags, P16_FLAGS_REPEAT));
log += F(" Code: 0x");
log += uint64ToString(CommandLines[i].Code, 16);
log += '}';
addLog(LOG_LEVEL_INFO, log);
}
# endif // PLUGIN_016_DEBUG
}
+42 -33
View File
@@ -6,18 +6,19 @@
# include <IRremoteESP8266.h>
# define PLUGIN_016_DEBUG // additional debug messages in the log
# define PLUGIN_016_DEBUG // additional debug messages in the log
// bit definition in PCONFIG_LONG(0)
# define P016_BitAddNewCode 0 // Add automatically new code into Code of the command structure
# define P016_BitExecuteCmd 1 // Execute command if received code matches Code or AlternativeCode of the command structure
# define P016_BitAcceptUnknownType 2 // Accept unknown DecodeType as valid IR code (will be set to RAW before calling AddCode() or ExecuteCode())
# define P016_BitAddNewCode 0 // Add automatically new code into Code of the command structure
# define P016_BitExecuteCmd 1 // Execute command if received code matches Code or AlternativeCode of the command structure
# define P016_BitAcceptUnknownType 2 // Accept unknown DecodeType as valid IR code (will be set to RAW before calling AddCode() or
// ExecuteCode())
# define P16_Nlines 10 // The number of different lines which can be displayed - each line is 64 chars max
# define P16_Nchars 64 // max chars per command line
# define P16_Cchars 20 // max chars per code
# define P16_Nlines 10 // The number of different lines which can be displayed - each line is 64 chars max
# define P16_Nchars 64 // max chars per command line
# define P16_Cchars 20 // max chars per code
# define P16_SETTINGS_V1 // Settings v1 original settings when enabled, settings conversion is also enabled
# define P16_SETTINGS_V1 // Settings v1 original settings when enabled, settings conversion is also enabled
// Settings v2 includes 64 bit codes and some separated flags
# ifdef P16_SETTINGS_V1
@@ -30,13 +31,19 @@
# define P16_FLAGS_REPEAT 0 // Repeat code
// # define P16_FLAGS_HASH 1 // Code is a Hash
# ifdef P16_SETTINGS_V1
typedef struct {
char Command[P16_Nchars] = { 0 };
uint32_t Code = 0; // received code (can be added automatically)
uint32_t AlternativeCode = 0; // alternative code fpr the same command
} tCommandLinesV1;
# endif // ifdef P16_SETTINGS_V1
struct tCommandLinesV2 {
# ifdef P16_SETTINGS_V1
tCommandLinesV2();
tCommandLinesV2(const String& command,
uint32_t oldCode,
uint32_t oldAlternativeCode,
uint8_t i);
tCommandLinesV2() = default;
tCommandLinesV2(const tCommandLinesV1& lineV1,
uint8_t i);
# endif // ifdef P16_SETTINGS_V1
char Command[P16_Nchars] = { 0 };
@@ -48,13 +55,6 @@ struct tCommandLinesV2 {
uint16_t AlternativeCodeFlags = 0;
};
# ifdef P16_SETTINGS_V1
typedef struct {
char Command[P16_Nchars] = { 0 };
uint32_t Code = 0; // received code (can be added automatically)
uint32_t AlternativeCode = 0; // alternative code fpr the same command
} tCommandLines;
# endif // ifdef P16_SETTINGS_V1
extern String uint64ToString(uint64_t input,
uint8_t base);
@@ -64,10 +64,23 @@ public:
P016_data_struct();
void init(struct EventStruct *event,
uint16_t CmdInhibitTime);
void loadCommandLines(struct EventStruct *event);
void saveCommandLines(struct EventStruct *event);
void init(struct EventStruct *event,
uint16_t CmdInhibitTime);
void loadCommandLines(struct EventStruct *event);
void saveCommandLines(struct EventStruct *event);
static void loadCommandLine(struct EventStruct *event,
tCommandLinesV2 & line,
uint8_t lineNr);
# ifdef P16_SETTINGS_V1
static void loadCommandLinev1(struct EventStruct *event,
tCommandLinesV2 & line,
uint8_t lineNr);
# endif // ifdef P16_SETTINGS_V1
static void saveCommandLine(struct EventStruct *event,
const tCommandLinesV2& line,
uint8_t lineNr);
void AddCode(uint64_t Code,
decode_type_t DecodeType = decode_type_t::UNKNOWN,
uint16_t CodeFlags = 0u);
@@ -86,16 +99,12 @@ private:
uint64_t Code,
decode_type_t DecodeType,
uint16_t CodeFlags);
void convertCommandLines(struct EventStruct *event);
# ifdef P16_SETTINGS_V1
std::vector<tCommandLines>CommandLinesV1; // holds the CustomTaskSettings V1, allocated when needed for conversion
# endif // ifdef P16_SETTINGS_V1
uint64_t iLastCmd = 0; // last command send
uint32_t iLastCmdTime = 0; // time while last command was send
decode_type_t iLastDecodeType = decode_type_t::UNKNOWN; // last decode_type sent
uint16_t iCmdInhibitTime = 0; // inhibit time for sending the same command again
uint16_t iLastCodeFlags = 0; // last flags sent
uint64_t iLastCmd = 0; // last command send
uint32_t iLastCmdTime = 0; // time while last command was send
decode_type_t iLastDecodeType = decode_type_t::UNKNOWN; // last decode_type sent
uint16_t iCmdInhibitTime = 0; // inhibit time for sending the same command again
uint16_t iLastCodeFlags = 0; // last flags sent
};
#endif // ifdef USES_P016
+467
View File
@@ -0,0 +1,467 @@
#include "../PluginStructs/P098_data_struct.h"
#ifdef USES_P098
# include "../ESPEasyCore/ESPEasyGPIO.h"
# include "../Commands/GPIO.h" // FIXME TD-er: Only needed till we can call GPIO commands from the ESPEasy core.
# include "../Helpers/Hardware.h"
# define GPIO_PLUGIN_ID 1
const __FlashStringHelper * P098_config_struct::toString(P098_config_struct::PWM_mode_type PWM_mode) {
switch (PWM_mode) {
case P098_config_struct::PWM_mode_type::NoPWM: return F("No PWM");
case P098_config_struct::PWM_mode_type::PWM: return F("PWM");
case P098_config_struct::PWM_mode_type::MAX_TYPE: break;
}
return F("");
}
P098_data_struct::P098_data_struct(const P098_config_struct& config) : _config(config) {}
P098_data_struct::~P098_data_struct() {
if (initialized) {
detachInterrupt(digitalPinToInterrupt(_config.limitA.gpio));
detachInterrupt(digitalPinToInterrupt(_config.limitB.gpio));
detachInterrupt(digitalPinToInterrupt(_config.encoder.gpio));
}
}
bool P098_data_struct::begin(int pos, int limitApos, int limitBpos)
{
if (!initialized) {
initialized = true;
const bool switchPosSet = pos != 0 && limitApos != 0;
limitA.switchposSet = switchPosSet;
limitA.switchpos = switchPosSet ? limitApos : 0;
limitB.switchpos = limitBpos;
position = pos + limitA.switchpos;
stop();
state = P098_data_struct::State::Idle;
if (setPinMode(_config.limitA)) {
attachInterruptArg(
digitalPinToInterrupt(_config.limitA.gpio),
reinterpret_cast<void (*)(void *)>(ISRlimitA),
this, CHANGE);
}
if (setPinMode(_config.limitB)) {
attachInterruptArg(
digitalPinToInterrupt(_config.limitB.gpio),
reinterpret_cast<void (*)(void *)>(ISRlimitB),
this, CHANGE);
}
if (setPinMode(_config.encoder)) {
attachInterruptArg(
digitalPinToInterrupt(_config.encoder.gpio),
reinterpret_cast<void (*)(void *)>(ISRencoder),
this, CHANGE); // Act on 'CHANGE', not on rising/falling
}
}
return true;
}
bool P098_data_struct::loop()
{
const State old_state(state);
check_limit_switch(_config.limitA, limitA);
check_limit_switch(_config.limitB, limitB);
if (check_encoder_timeout(_config.encoder)) {
stop();
state = P098_data_struct::State::StopEncoderTimeout;
return old_state == state;
}
switch (state) {
case P098_data_struct::State::Idle:
return true;
case P098_data_struct::State::RunFwd:
{
checkLimit(limitB);
break;
}
case P098_data_struct::State::RunRev:
{
checkLimit(limitA);
break;
}
case P098_data_struct::State::StopEncoderTimeout:
case P098_data_struct::State::StopLimitSw:
case P098_data_struct::State::StopPosReached:
// Still in a state that needs inspection
return false;
}
// Must check when state has changed from running to some other state
return old_state == state;
}
bool P098_data_struct::homePosSet() const
{
return limitA.switchposSet;
}
bool P098_data_struct::canRun()
{
if (!homePosSet()) { return false; }
switch (state) {
case P098_data_struct::State::Idle:
case P098_data_struct::State::RunFwd:
case P098_data_struct::State::RunRev:
return true;
case P098_data_struct::State::StopLimitSw:
case P098_data_struct::State::StopPosReached:
case P098_data_struct::State::StopEncoderTimeout:
// Still in a state that needs inspection
return false;
}
return false;
}
void P098_data_struct::findHome()
{
pos_dest = INT_MIN;
limitA.switchposSet = false;
startMoving();
}
void P098_data_struct::moveForward(int steps)
{
if (steps <= 0) {
pos_dest = INT_MAX;
limitB.switchposSet = false;
} else {
pos_dest = position + steps;
}
startMoving();
}
void P098_data_struct::moveReverse(int steps)
{
if (steps > 0) {
pos_dest = position - steps;
startMoving();
}
}
bool P098_data_struct::moveToPos(int pos)
{
if (!canRun()) { return false; }
const int offset = pos - getPosition();
pos_dest = position + offset;
startMoving();
return true;
}
void P098_data_struct::stop()
{
setPinState(_config.motorFwd, 0);
setPinState(_config.motorRev, 0);
}
int P098_data_struct::getPosition() const
{
if (limitA.switchposSet) {
return position - limitA.switchpos;
}
return position;
}
void P098_data_struct::getLimitSwitchStates(bool& limitA_triggered, bool& limitB_triggered) const
{
limitA_triggered = limitA.state == P098_limit_switch_state::State::High ? 1 : 0;
limitB_triggered = limitB.state == P098_limit_switch_state::State::High ? 1 : 0;
/*
limitA_triggered = _config.limitA.readState();
limitB_triggered = _config.limitB.readState();
*/
}
void P098_data_struct::getLimitSwitchPositions(int& limitApos, int& limitBpos) const
{
limitApos = limitA.switchposSet ? limitA.switchpos : 0;
limitBpos = limitB.switchposSet ? limitB.switchpos : 0;
}
void P098_data_struct::startMoving()
{
// Stop first, to make sure both outputs will not be set high
stop();
if (pos_dest > position) {
state = P098_data_struct::State::RunFwd;
setPinState(_config.motorFwd, 1);
} else {
state = P098_data_struct::State::RunRev;
setPinState(_config.motorRev, 1);
}
// Touch the timer, so it will not immediately timeout.
enc_lastChanged_us = getMicros64();
}
void P098_data_struct::checkLimit(volatile P098_limit_switch_state& switch_state)
{
if (switch_state.state == P098_limit_switch_state::State::High) {
stop();
state = P098_data_struct::State::StopLimitSw;
return;
}
checkPosition();
}
void P098_data_struct::checkPosition()
{
bool mustStop = false;
switch (state) {
case P098_data_struct::State::RunFwd:
mustStop = ((position + pos_overshoot) >= pos_dest);
break;
case P098_data_struct::State::RunRev:
mustStop = ((position - pos_overshoot) <= pos_dest);
break;
default:
return;
}
if (mustStop) {
stop();
pos_overshoot = 0;
state = P098_data_struct::State::StopPosReached;
/*
// Correct for position error
if (std::abs(position - pos_dest) > 10) {
startMoving();
}
*/
}
}
void P098_data_struct::setPinState(const P098_GPIO_config& gpio_config, int8_t state)
{
// FIXME TD-er: Must move this code to the ESPEasy core code.
uint8_t mode = PIN_MODE_OUTPUT;
state = state == 0 ? gpio_config.low() : gpio_config.high();
uint32_t key = createKey(GPIO_PLUGIN_ID, gpio_config.gpio);
if (globalMapPortStatus[key].mode != PIN_MODE_OFFLINE)
{
int8_t currentState;
GPIO_Read(GPIO_PLUGIN_ID, gpio_config.gpio, currentState);
if (currentState == -1) {
mode = PIN_MODE_OFFLINE;
state = -1;
}
switch (_config.PWM_mode) {
case P098_config_struct::PWM_mode_type::NoPWM:
if (mode == PIN_MODE_OUTPUT) {
createAndSetPortStatus_Mode_State(key, mode, state);
GPIO_Write(
GPIO_PLUGIN_ID,
gpio_config.gpio,
state,
mode);
}
break;
case P098_config_struct::PWM_mode_type::PWM:
{
const uint32_t dutycycle = state == 0 ? 0 : _config.pwm_duty_cycle;
const uint32_t fade_duration = _config.pwm_soft_startstop ?
100 /* (_config.encoder.timer_us / 1000) */
: 0;
uint32_t frequency = _config.pwm_freq;
set_Gpio_PWM(
gpio_config.gpio,
dutycycle,
fade_duration,
frequency,
key);
if (state == 0) {
// Turn off PWM mode too
createAndSetPortStatus_Mode_State(key, PIN_MODE_OUTPUT, state);
GPIO_Write(
GPIO_PLUGIN_ID,
gpio_config.gpio,
state,
PIN_MODE_OUTPUT);
}
}
break;
}
}
}
bool P098_data_struct::setPinMode(const P098_GPIO_config& gpio_config)
{
if (checkValidPortRange(GPIO_PLUGIN_ID, gpio_config.gpio)) {
pinMode(gpio_config.gpio, gpio_config.pullUp ? INPUT_PULLUP : INPUT);
return true;
}
return false;
}
void P098_data_struct::check_limit_switch(
const P098_GPIO_config & gpio_config,
volatile P098_limit_switch_state& switch_state)
{
if (gpio_config.gpio == -1) {
return;
}
// State is changed first in ISR, but compared after values are copied.
const int triggerpos = switch_state.triggerpos;
const uint64_t lastChanged_us = switch_state.lastChanged_us;
if (switch_state.state == P098_limit_switch_state::State::TriggerWaitBounce) {
if (lastChanged_us != 0) {
const uint64_t timeSinceLastTrigger = getMicros64() - lastChanged_us;
if (timeSinceLastTrigger > gpio_config.timer_us) {
mark_limit_switch_state(triggerpos, switch_state);
}
}
}
}
void P098_data_struct::mark_limit_switch_state(
int triggerpos,
volatile P098_limit_switch_state& switch_state)
{
if (!switch_state.switchposSet) {
switch_state.switchpos = triggerpos;
switch_state.switchposSet = true;
}
// Perform an extra check here on the state as it may have changed in the ISR call
if (switch_state.state == P098_limit_switch_state::State::TriggerWaitBounce) {
switch_state.state = P098_limit_switch_state::State::High;
}
}
bool P098_data_struct::check_encoder_timeout(const P098_GPIO_config & gpio_config)
{
if (gpio_config.gpio == -1) {
return false;
}
if (enc_lastChanged_us == 0) {
return false;
}
const bool expired = usecPassedSince(enc_lastChanged_us) > _config.encoder.timer_us;
if (!expired) {
return false;
}
switch (state) {
case P098_data_struct::State::RunFwd:
{
mark_limit_switch_state(position, limitB);
break;
}
case P098_data_struct::State::RunRev:
{
mark_limit_switch_state(position, limitA);
break;
}
default:
return false;
}
return true;
}
void ICACHE_RAM_ATTR P098_data_struct::process_limit_switch(
const P098_GPIO_config & gpio_config,
volatile P098_limit_switch_state& switch_state,
int position)
{
noInterrupts();
{
// Don't call gpio_config.readState() here
const bool pinState = gpio_config.inverted ? digitalRead(gpio_config.gpio) == 0 : digitalRead(gpio_config.gpio) != 0;
const uint64_t currentTime = getMicros64();
switch (switch_state.state) {
case P098_limit_switch_state::State::Low:
if (pinState) {
switch_state.state = P098_limit_switch_state::State::TriggerWaitBounce;
switch_state.lastChanged_us = currentTime;
switch_state.triggerpos = position;
}
break;
case P098_limit_switch_state::State::TriggerWaitBounce:
{
// Do not evaluate the debounce time here, evaluate in the loop
if (pinState) {
// Only situation we can get here is when we missed a low state interrupt.
switch_state.lastChanged_us = currentTime;
switch_state.triggerpos = position;
} else {
switch_state.state = P098_limit_switch_state::State::Low;
switch_state.lastChanged_us = 0;
switch_state.triggerpos = 0;
}
break;
}
case P098_limit_switch_state::State::High:
if (!pinState) {
switch_state.state = P098_limit_switch_state::State::Low;
switch_state.lastChanged_us = 0;
switch_state.triggerpos = 0;
}
break;
}
}
interrupts(); // enable interrupts again.
}
void ICACHE_RAM_ATTR P098_data_struct::ISRlimitA(P098_data_struct *self)
{
process_limit_switch(self->_config.limitA, self->limitA, self->position);
}
void ICACHE_RAM_ATTR P098_data_struct::ISRlimitB(P098_data_struct *self)
{
process_limit_switch(self->_config.limitB, self->limitB, self->position);
}
void ICACHE_RAM_ATTR P098_data_struct::ISRencoder(P098_data_struct *self)
{
noInterrupts();
switch (self->state) {
case P098_data_struct::State::RunFwd:
++(self->position);
break;
case P098_data_struct::State::RunRev:
--(self->position);
break;
default:
interrupts(); // enable interrupts again.
return;
}
self->enc_lastChanged_us = getMicros64();
interrupts(); // enable interrupts again.
}
#endif // ifdef USES_P098
+174
View File
@@ -0,0 +1,174 @@
#ifndef PLUGINSTRUCTS_P098_DATA_STRUCT_H
#define PLUGINSTRUCTS_P098_DATA_STRUCT_H
#include "../../_Plugin_Helper.h"
#ifdef USES_P098
# define P098_LIMIT_SWITCH_TRIGGERPOS_MARGIN 10
struct P098_GPIO_config {
byte high() const {
return inverted ? 0 : 1;
}
byte low() const {
return inverted ? 1 : 0;
}
// Don't call this from ISR functions.
bool readState() const {
const bool state = digitalRead(gpio) != 0;
return inverted ? !state : state;
}
uint64_t timer_us = 100000;
int gpio = -1;
bool pullUp = false;
bool inverted = false;
};
struct P098_config_struct {
// Stored, so do not change values
enum class PWM_mode_type {
NoPWM = 0,
PWM = 1,
MAX_TYPE
};
static const __FlashStringHelper* toString(P098_config_struct::PWM_mode_type PWM_mode);
P098_GPIO_config motorFwd;
P098_GPIO_config motorRev;
P098_GPIO_config limitA;
P098_GPIO_config limitB;
P098_GPIO_config encoder;
unsigned long pwm_freq = 1000;
int gpio_analogIn = -1;
uint32_t pwm_duty_cycle = 1023;
PWM_mode_type PWM_mode = PWM_mode_type::NoPWM;
bool encoder_pu = false;
bool pwm_soft_startstop = false;
};
struct P098_limit_switch_state {
// States:
// - Low : triggerpos 0, logic state = low
// - TriggerWaitBounce: Stored triggerpos is preliminary, logic state = high
// - TriggerRejected : logic state = low within bounce timeout
// -> clear triggerpos & clear timestamp
// -> new state: Low
// - High : logic state was high for minimal debounce duration
// triggerpos is accepted.
// May also store the position of the limit switch.
enum class State {
Low,
TriggerWaitBounce,
High
};
uint64_t lastChanged_us = 0;
int triggerpos = 0;
int switchpos = 0;
State state = State::Low;
bool switchposSet = false;
};
struct P098_data_struct : public PluginTaskData_base {
enum class State {
Idle,
RunFwd,
RunRev,
StopLimitSw,
StopPosReached,
StopEncoderTimeout
};
P098_data_struct(const P098_config_struct& config);
~P098_data_struct();
bool begin(int pos,
int limitApos,
int limitBpos);
// Perform regular loop
// Return false when state should be checked.
bool loop();
bool homePosSet() const;
bool canRun();
void findHome();
// Run the motor N steps forward
// N <= 0: Move till limit B switch is reached
void moveForward(int steps);
// Run the motor N steps in revere
void moveReverse(int steps);
// Move to absolute position.
// May return false if the state was not (yet) cleared or position of limit switch is unknown.
bool moveToPos(int pos);
// Stop the motors and set the state to idle.
void stop();
int getPosition() const;
void getLimitSwitchStates(bool& limitA,
bool& limitB) const;
void getLimitSwitchPositions(int& limitA,
int& limitB) const;
State state = State::Idle;
bool initialized = false;
private:
const P098_config_struct _config;
volatile P098_limit_switch_state limitA;
volatile P098_limit_switch_state limitB;
volatile int position = 0;
volatile uint64_t enc_lastChanged_us = 0;
int pos_dest = 0;
int pos_overshoot = 0;
void startMoving();
void checkLimit(volatile P098_limit_switch_state& switch_state);
void checkPosition();
void setPinState(const P098_GPIO_config& gpio_config,
int8_t state);
static bool setPinMode(const P098_GPIO_config& gpio_config);
static void check_limit_switch(
const P098_GPIO_config & gpio_config,
volatile P098_limit_switch_state& switch_state);
static void mark_limit_switch_state(
int triggerpos,
volatile P098_limit_switch_state& switch_state);
bool check_encoder_timeout(const P098_GPIO_config & gpio_config);
static void process_limit_switch(
const P098_GPIO_config & gpio_config,
volatile P098_limit_switch_state& switch_state,
int position);
static void ISRlimitA(P098_data_struct *self);
static void ISRlimitB(P098_data_struct *self);
static void ISRencoder(P098_data_struct *self);
};
#endif // ifdef USES_P098
#endif // ifndef PLUGINSTRUCTS_P098_DATA_STRUCT_H
+1 -1
View File
@@ -142,7 +142,7 @@ bool handle_custom(const String& path) {
line.reserve(128);
while (available > 0) {
uint32_t chunksize = 64;
if (available < chunksize) {
if (available < static_cast<int>(chunksize)) {
chunksize = available;
}
uint8_t buf[64] = {0};
+9
View File
@@ -374,7 +374,16 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task
// allow the plugin to save plugin-specific form settings.
{
String dummy;
if (Device[DeviceIndex].ExitTaskBeforeSave) {
PluginCall(PLUGIN_EXIT, &TempEvent, dummy);
}
PluginCall(PLUGIN_WEBFORM_SAVE, &TempEvent, dummy);
// Make sure the task needs to reload using the new settings.
if (!Device[DeviceIndex].ExitTaskBeforeSave) {
PluginCall(PLUGIN_EXIT, &TempEvent, dummy);
}
}
// notify controllers: CPlugin::Function::CPLUGIN_TASK_CHANGE_NOTIFICATION
+6 -2
View File
@@ -362,8 +362,12 @@ void handle_json()
const uint8_t valueCount = getValueCountForTask(TaskIndex);
if (valueCount != 0) {
if ((taskInterval > 0) && Settings.TaskDeviceEnabled[TaskIndex]) {
ttl_json = taskInterval;
if (Settings.TaskDeviceEnabled[TaskIndex]) {
if (taskInterval == 0) {
ttl_json = 1;
} else {
ttl_json = taskInterval;
}
if (ttl_json < lowest_ttl_json) {
lowest_ttl_json = ttl_json;
+1 -1
View File
@@ -156,7 +156,7 @@ size_t streamFromFS(String path, bool htmlEscape) {
String escaped;
while (available > 0) {
uint32_t chunksize = 64;
if (available < chunksize) {
if (available < static_cast<int>(chunksize)) {
chunksize = available;
}
uint8_t buf[64] = {0};
-1
View File
@@ -101,7 +101,6 @@ void sendHeadandTail(const String& tmplName, boolean Tail, boolean rebooting) {
}
f.close();
} else {
// TODO TD-er: Should send data directly to TXBuffer instead of using large strings.
getWebPageTemplateDefault(tmplName, templateParser);
}
#ifndef BUILD_NO_RAM_TRACKER
+1
View File
@@ -4,6 +4,7 @@
#include "../DataTypes/ControllerIndex.h"
#include "../Globals/Protocol.h"
#include "../Globals/Settings.h"
#include "../Helpers/ESPEasy_Storage.h"
+3 -1
View File
@@ -23,7 +23,7 @@ else:
custom_defines=[
"-DCONTROLLER_SET_ALL",
"-DNOTIFIER_SET_NONE",
# "-DPLUGIN_BUILD_NORMAL",
"-DPLUGIN_BUILD_NONE",
"-DUSES_P001", # Switch
"-DUSES_P002", # ADC
"-DUSES_P003", # Generic pulse counter
@@ -43,6 +43,8 @@ else:
"-DUSES_P081", # Cron
"-DUSES_P082", # GPS
"-DUSES_P085", # AcuDC24x
"-DUSES_P098", # PWM motor
"-DUSES_P100", # Pulse Counter - DS2423
# "-DUSES_P087", # Serial Proxy
# "-DUSES_P094", # CUL Reader
+7 -5
View File
@@ -24,10 +24,10 @@ else:
custom_defines=[
"-DCONTROLLER_SET_ALL",
"-DNOTIFIER_SET_NONE",
"-DPLUGIN_BUILD_NONE",
"-DPLUGIN_SET_NONE",
"-DUSES_P001", # Switch
"-DUSES_P002", # ADC
# "-DUSES_P003", # Generic Pulse Counter
"-DUSES_P003", # Generic Pulse Counter
"-DUSES_P004", # Dallas DS18b20
"-DUSES_P026", # System info
"-DUSES_P027", # INA219
@@ -41,9 +41,11 @@ else:
# "-DUSES_P059", # Encoder
# "-DUSES_P080", # Dallas iButton
"-DUSES_P081", # Cron
# "-DUSES_P082", # GPS
# "-DUSES_P085", # AcuDC24x
# "-DUSES_P100", # Pulse Counter - DS2423
"-DUSES_P082", # GPS
"-DUSES_P085", # AcuDC24x
"-DUSES_P098", # PWM motor
"-DUSES_P100", # Pulse Counter - DS2423
# "-DUSES_P087", # Serial Proxy
# "-DUSES_P094", # CUL Reader
# "-DUSES_P095", # TFT ILI9341