Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 13 additions & 34 deletions src/_P126_74HC595.ino
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// #######################################################################################################

/** Changelog:
* 2026-06-30 tonhuisman: Code optimization by moving ul2stringFixed to StringConverter_Numerical
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Shift registers)
* 2022-02-27 tonhuisman: Rename plugin title to Output - Shift registers (74HC595)
* 2022-02-25 tonhuisman: Again rename commands, now using separate prefix shiftout and the rest of the previous command as subcommand.
Expand Down Expand Up @@ -59,19 +60,6 @@

# include "./src/PluginStructs/P126_data_struct.h"

// TODO tonhuisman: ? Move to StringConverter ? though it is a bit specific, can also be used by P129
String P126_ul2stringFixed(uint32_t value, uint8_t base) {
uint64_t val = static_cast<uint64_t>(value);

val &= 0x0ffffffff; // Keep 32 bits
val |= 0x100000000; // Set bit just left of 32 bits so we will see the leading zeroes
String valStr = ull2String(val, base);

valStr.remove(0, 1); // Delete leading 1 we added
valStr.toUpperCase(); // uppercase hex for readability
return valStr;
}

boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string)
{
boolean success = false;
Expand Down Expand Up @@ -286,15 +274,16 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string)
(P126_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P126_OUTPUT_HEXBIN)) {
string += '0';
string += (P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? 'b' : 'x');
string += P126_ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx),
# ifdef P126_SHOW_VALUES
(P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN :
# endif // ifdef P126_SHOW_VALUES
HEX
# ifdef P126_SHOW_VALUES
)
# endif // ifdef P126_SHOW_VALUES
);

string += ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx),
# ifdef P126_SHOW_VALUES
(P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN :
# endif // ifdef P126_SHOW_VALUES
HEX
# ifdef P126_SHOW_VALUES
)
# endif // ifdef P126_SHOW_VALUES
, 1 == event->ParN[event->idx]);
}
success = true;
break;
Expand All @@ -308,21 +297,15 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string)
const String abcd = F("ABCDEFGH"); // In case anyone dares to extend
// VARS_PER_TASK to 8...
const uint16_t endCheck = P126_CONFIG_CHIP_COUNT + (P126_CONFIG_CHIP_COUNT == 255 ? 3 : 4); // 4(.0) = nr of bytes in an uint32_t.
const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), static_cast<uint8_t>(ceil(P126_CONFIG_CHIP_COUNT / 4.0)));
uint8_t dotInsert;
uint8_t dotOffset;
const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), static_cast<uint8_t>(ceilf(P126_CONFIG_CHIP_COUNT / 4.0f)));

for (uint16_t varNr = 0; varNr < maxVar; ++varNr) {
if (P126_CONFIG_FLAGS_GET_VALUES_DISPLAY) {
label = F("Bin");
state = F("0b");
dotInsert = 10;
dotOffset = 9;
} else {
label = F("Hex");
state = F("0x");
dotInsert = 4;
dotOffset = 3;
}
label += strformat(F(" State_%s "), abcd.substring(varNr, varNr + 1).c_str());

Expand All @@ -331,11 +314,7 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string)
label += (P126_CONFIG_SHOW_OFFSET + (4 * varNr) + 1); // 4 = nr of bytes in an uint32_t.

if ((P126_CONFIG_SHOW_OFFSET + (4 * varNr) + 4) <= endCheck) { // Only show if still in range
state += P126_ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX);

for (uint8_t i = 0; i < 3; ++i, dotInsert += dotOffset) { // Insert readability separators
state = state.substring(0, dotInsert) + '.' + state.substring(dotInsert);
}
state += ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX, true);
pluginWebformShowValue(event->TaskIndex, VARS_PER_TASK + varNr, label, state, true);
}
}
Expand Down
64 changes: 22 additions & 42 deletions src/_P129_74HC165.ino
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// #######################################################################################################

/** Changelog:
* 2026-06-30 tonhuisman: Code optimization by moving ul2stringFixed to StringConverter_Numerical
* 2025-06-14 tonhuisman: Add support for Custom Value Type per task value
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Shift registers)
* 2023-01-04 tonhuisman: Use DIRECT_pin GPIO functions for faster GPIO handling (mostly on ESP32), string optimization
Expand Down Expand Up @@ -43,19 +44,6 @@

# include "./src/PluginStructs/P129_data_struct.h"

// TODO tonhuisman: ? Move to StringConverter ? though it is a bit specific, can also be used by P126
String P129_ul2stringFixed(uint32_t value, uint8_t base) {
// Set bit just left of 32 bits so we will see the leading zeroes
const uint64_t val = static_cast<uint64_t>(value) | 0x100000000ull;

String valStr = ull2String(val, base).substring(1); // Delete leading 1 we added

if (base == HEX) {
valStr.toUpperCase(); // uppercase hex for readability
}
return valStr;
}

boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
{
boolean success = false;
Expand Down Expand Up @@ -146,7 +134,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
case PLUGIN_GET_DEVICEVALUECOUNT:
{
event->Par1 = min(static_cast<uint8_t>(VARS_PER_TASK),
static_cast<uint8_t>(ceil(P129_CONFIG_CHIP_COUNT / 4.0f)));
static_cast<uint8_t>(ceilf(P129_CONFIG_CHIP_COUNT / 4.0f)));
success = true;
break;
}
Expand All @@ -155,7 +143,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
{
event->sensorType = static_cast<Sensor_VType>(
min(static_cast<uint8_t>(VARS_PER_TASK),
static_cast<uint8_t>(ceil(P129_CONFIG_CHIP_COUNT / 4.0f))));
static_cast<uint8_t>(ceilf(P129_CONFIG_CHIP_COUNT / 4.0f))));
event->idx = 0;
success = true;
break;
Expand Down Expand Up @@ -187,6 +175,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
F("Number of chips (Q7 &rarr; DS)"), F("chipcnt"), P129_CONFIG_CHIP_COUNT);
addUnit(concat(F("Daisychained 1.."), P129_MAX_CHIP_COUNT));
# ifndef LIMIT_BUILD_SIZE

// addFormNote(F("Changing the number of chips will reload the page and update the Event configuration."));
# endif // ifndef LIMIT_BUILD_SIZE
}
Expand Down Expand Up @@ -240,12 +229,12 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
if (i % 4 == 0) {
bits = PCONFIG_ULONG(i / 4) & 0x0ffffffff;
off = 0;
# ifndef P129_DEBUG_LOG
# ifdef P129_DEBUG_LOG

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
addLog(LOG_LEVEL_INFO, strformat(F("74HC165 Reading from: %d, bits: %s"), i / 4, P129_ul2stringFixed(bits, BIN).c_str()));
addLog(LOG_LEVEL_INFO, strformat(F("74HC165 Reading from: %d, bits: %s"), i / 4, ul2stringFixed(bits, BIN, false).c_str()));
}
# endif // ifndef P129_DEBUG_LOG
# endif // ifdef P129_DEBUG_LOG
}
html_TR();
addHtml(F("<td align =\"center\">"));
Expand Down Expand Up @@ -310,15 +299,15 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
}
PCONFIG_ULONG(i / 4) = bits;

# ifndef P129_DEBUG_LOG
# ifdef P129_DEBUG_LOG

if (loglevelActiveFor(LOG_LEVEL_INFO) && ((i % 4 == 3) || (i == P129_CONFIG_CHIP_COUNT))) {
addLog(LOG_LEVEL_INFO, strformat(F("74HC165 Writing to: %d, offset: %d, bits: %s"),
i / 4,
off * 8,
P129_ul2stringFixed(bits, BIN).c_str()));
ul2stringFixed(bits, BIN, false).c_str()));
}
# endif // ifndef P129_DEBUG_LOG
# endif // ifdef P129_DEBUG_LOG
off++;
}
success = true;
Expand Down Expand Up @@ -391,15 +380,16 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
(P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P129_OUTPUT_HEXBIN)) {
string += '0';
string += (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? 'b' : 'x');
string += P129_ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx),
# ifdef P129_SHOW_VALUES
(P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN :
# endif // ifdef P129_SHOW_VALUES
HEX
# ifdef P129_SHOW_VALUES
)
# endif // ifdef P129_SHOW_VALUES
);

string += ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx),
# ifdef P129_SHOW_VALUES
(P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN :
# endif // ifdef P129_SHOW_VALUES
HEX
# ifdef P129_SHOW_VALUES
)
# endif // ifdef P129_SHOW_VALUES
, 1 == event->ParN[event->idx]);
}
success = true;
break;
Expand All @@ -412,21 +402,15 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
state.reserve(40);
const String abcd = F("ABCDEFGH"); // In case anyone dares to extend VARS_PER_TASK to 8...
const uint16_t endCheck = P129_CONFIG_CHIP_COUNT + 4; // 4(.0) = nr of bytes in an uint32_t.
const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), static_cast<uint8_t>(ceil(P129_CONFIG_CHIP_COUNT / 4.0f)));
uint8_t dotInsert;
uint8_t dotOffset;
const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), static_cast<uint8_t>(ceilf(P129_CONFIG_CHIP_COUNT / 4.0f)));

for (uint16_t varNr = 0; varNr < maxVar; ++varNr) {
if (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY) {
label = F("Bin");
state = F("0b");
dotInsert = 10;
dotOffset = 9;
} else {
label = F("Hex");
state = F("0x");
dotInsert = 4;
dotOffset = 3;
}
label += strformat(F(" State_%s "), abcd.substring(varNr, varNr + 1).c_str());

Expand All @@ -435,11 +419,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
label += (P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 1); // 4 = nr of bytes in an uint32_t.

if ((P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 4) <= endCheck) { // Only show if still in range
state += P129_ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX);

for (uint8_t i = 0; i < 3; ++i, dotInsert += dotOffset) { // Insert readability separators
state = state.substring(0, dotInsert) + '.' + state.substring(dotInsert);
}
state += ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX, true);
pluginWebformShowValue(event->TaskIndex, VARS_PER_TASK + varNr, label, state, true);
}
}
Expand Down
36 changes: 36 additions & 0 deletions src/src/Helpers/StringConverter_Numerical.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,42 @@ String ll2String(int64_t value, uint8_t base) {
}
}

/**
* format an uint32_t with 0-prefixed in the provided base of 2, 16 and insert an optional dot as separator for each byte
* separator only applied for base 2 and 16
*/
String ul2stringFixed(uint32_t value, uint8_t base, bool dotSeparator) {
// Set bit just left of 32 bits so we will see the leading zeroes
const uint64_t val = static_cast<uint64_t>(value) | 0x100000000ull;

String valStr = ull2String(val, base).substring(1); // Delete leading 1 we added

if (base == HEX) {
valStr.toUpperCase(); // uppercase hex for readability
}

if (dotSeparator) {
uint8_t dotInsert{};
uint8_t dotOffset{};

if (BIN == base) {
dotInsert = 10;
dotOffset = 9;
} else
if (HEX == base) {
dotInsert += 4;
dotOffset = 3;
}

if (dotInsert) {
for (uint8_t i = 0; i < 3; ++i, dotInsert += dotOffset) { // Insert readability separators
valStr = valStr.substring(0, dotInsert) + '.' + valStr.substring(dotInsert);
}
}
}
return valStr;
}

String trimTrailingZeros(const String& value) {
String res(value);
int dot_pos = res.lastIndexOf('.');
Expand Down
5 changes: 5 additions & 0 deletions src/src/Helpers/StringConverter_Numerical.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ String ull2String(uint64_t value,
String ll2String(int64_t value,
uint8_t base = 10);

String ul2stringFixed(uint32_t value,
uint8_t base,
bool dotSeparator);


String trimTrailingZeros(const String& value);

String toStringNoZero(int64_t value);
Expand Down
2 changes: 1 addition & 1 deletion src/src/PluginStructs/P126_data_struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ bool P126_data_struct::plugin_init(struct EventStruct *event) {
}

const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK),
static_cast<uint8_t>(ceil((P126_CONFIG_CHIP_COUNT - P126_CONFIG_SHOW_OFFSET) / 4.0)));
static_cast<uint8_t>(ceilf((P126_CONFIG_CHIP_COUNT - P126_CONFIG_SHOW_OFFSET) / 4.0f)));
uint32_t par;

for (uint16_t varNr = 0; varNr < maxVar; ++varNr) {
Expand Down
23 changes: 23 additions & 0 deletions src/src/WebServer/JSON.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,29 @@ void handle_json()
#endif // if FEATURE_STRING_VARIABLES
uom);
}

if (Device[DeviceIndex].HasFormatUserVar) {
struct EventStruct TempEvent(TaskIndex);

for (uint8_t x = 0; x < valueCount; x++)
{
String value;
TempEvent.idx = x;
TempEvent.ParN[x] = 1; // Get formatted version of the value
PluginCall(PLUGIN_FORMAT_USERVAR, &TempEvent, value);

if (!value.isEmpty()) {
handle_json_stream_task_value_data(taskValueWriter.get(),
VARS_PER_TASK + x + 1,
Cache.getTaskDeviceValueName(TaskIndex, x),
255,
value,
EMPTY_STRING,
EMPTY_STRING);
}
}
// FIXME tonhuisman: HasFormatUserVar is not really compatible with Derived Values...
}
#if FEATURE_STRING_VARIABLES

if (Settings.ShowDerivedTaskValues(TaskIndex)) {
Expand Down