mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Merge branch 'mega' into feature/build_core_3_0_0
This commit is contained in:
+33
-54
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(" "));
|
||||
addHtml(formatGpioLabel(Settings.TaskDevicePin1[event->TaskIndex], true));
|
||||
addHtml(event->String1);
|
||||
addHtml(F("M Rev:"));
|
||||
addHtml(F(" "));
|
||||
addHtml(formatGpioLabel(Settings.TaskDevicePin2[event->TaskIndex], true));
|
||||
addHtml(event->String1);
|
||||
addHtml(F("Enc:"));
|
||||
addHtml(F(" "));
|
||||
addHtml(formatGpioLabel(Settings.TaskDevicePin3[event->TaskIndex], true));
|
||||
addHtml(event->String1);
|
||||
# ifdef ESP32
|
||||
addHtml(F("Analog:"));
|
||||
addHtml(F(" "));
|
||||
addHtml(formatGpioLabel(P098_ANALOG_GPIO, true));
|
||||
addHtml(event->String1);
|
||||
# endif // ifdef ESP32
|
||||
addHtml(F("Lim A:"));
|
||||
addHtml(F(" "));
|
||||
addHtml(formatGpioLabel(P098_LIMIT_SWA_GPIO, true));
|
||||
addHtml(event->String1);
|
||||
addHtml(F("Lim B:"));
|
||||
addHtml(F(" "));
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "../DataTypes/ControllerIndex.h"
|
||||
|
||||
#include "../Globals/Protocol.h"
|
||||
#include "../Globals/Settings.h"
|
||||
|
||||
#include "../Helpers/ESPEasy_Storage.h"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user