Repository navigation
0.7.0
Fixed
-
loadCfg()could restore a value the last save did not contain.saveCfg()
removes the key of an empty string rather than storing a blank, and
Preferences::getString()/getBytes()leave the destination untouched when a
key is absent — so clearing the hostname, SSID, password, MAC or MQTT broker
address, saving, and then loading back into the same object handed the old
value straight back, and the store read as agreeing with it. The affected
fields are now reset to their defaults before they are read, in
NetworkProfile,WiFiProfileandMqttProfilealike. The numeric fields
were never affected: they pass a default to the getter, which does the same
job. Neither was a load into a freshly constructed profile, which is the usual
boot path — this only ever bit a reload into a populated object, such as a
configuration page reverting its edits.MqttProfile::_doLoad()additionally passed the current value as each
getter's default, which had the same effect on the bools and the port; it now
passes a default-constructedMqttConfig's. -
WiFiProfile(const WiFiConfig&)droppedcfg.txPower. The constructor applied
the SSID and the password but left the transmit power at the platform default,
whilesetConfig(WiFiConfig)applied all three — so the same struct produced
two different profiles depending on whether it arrived through the constructor
or the setter. It is now validated and stored like the other two, and ignored
the same way when invalid, since a constructor cannot report a rejection. -
WiFiProfile::_doLoad()could load a TX power of NAN.
Preferences::getFloat()answers a missing key with NAN unless given a
default, and the call passed none — so a namespace written beforetxpwr
existed came back with a poisoned_txPower, after which every
isValidTxPower()comparison against it was false and the radio never got a
usable value. It now falls back toDEFAULT_WIFI_TX_POWER_dBm, and does the
same for a stored value outside what this build accepts: the range is not the
same on both platforms (ESP32 discrete levels up to 19.5 dBm, ESP8266
continuous up to 20.5), so a namespace carried between them, or a build whose
WIFI_PROFILE_DEFAULT_WIFI_TX_POWERchanged, can hold one the radio would
refuse. The rest of the profile still loads andloadCfg()still returns
true; only the TX power is replaced.
Added
-
NtpProfile::clearCfg()andMqttProfile::clearCfg(), matching
NetworkProfile's. Both classes could write a namespace and read it back but
not erase it, which left a factory reset unable to finish the job: a device
wiped of its network configuration still came back holding the time servers it
synchronised from and — worse — the broker's address and the credentials for
it.MqttProfileleaves the password out oftoJson()precisely because a
backup file travels, so leaving it in flash through a reset was the same
concern answered two different ways.Same contract as
NetworkProfile::clearCfg(), including that it clears the
store rather than the object: the instance keeps what it holds until something
loads over it. -
NetworkProfile::configCheckName(), naming aConfigCheckvalue for logs and
configuration UIs.checkConfig()exists so a caller can report which field
was refused rather than only that something was, but with no name for the
value every caller had to write the same nine-case switch — so in practice
callers reported nothing, or skipped the check. The string is flash-resident,
costing no RAM on AVR, and names the field rather than the remedy: a UI knows
better than this library how to phrase advice to its user. -
NtpProfile— the NTP servers as a device-level setting, withgetServer()/
setServer(),checkConfig(),saveCfg()/loadCfg()andtoJson(). A
device has one clock, so the servers it synchronises from belong to the device,
not to whichever interface is up: keeping a copy in everyNetworkProfilemeant
the samepool.ntp.orgtyped twice, stored twice, and carried twice in the
largest document the portal serves. Where interfaces really do reach different
time servers, twoNtpProfileinstances say so plainly. -
NtpProfile::importLegacy()reads servers written by an earlier version, which
stored them per interface underntp0..ntpNin the network profile's own
namespace. Without it an upgraded device would come back with no time servers
and no indication why. Scheduled for removal in 1.0.0. -
detail/ntp_config.hholdsNTP_PROFILE_SERVER_COUNTon its own, because two
callers ask different things of it:NtpProfile.hasks how many servers it
holds and refuses to be included when the answer is none, while NetworkManager
asks whether to compile its NTP support and must be able to ask that when the
answer is no. -
NTP_PROFILE_ENABLEDdecides whether NTP exists in the build;
NTP_PROFILE_SERVER_COUNTsays how many servers a profile holds, and is now a
count in its own right (1..3) rather than a switch that also happened to be a
number. Zero no longer disables anything and is refused with a message naming
the replacement — a profile holding no servers was never a coherent state, which
is why the class had to error out on it. Defaults: NTP off on AVR (the switch
also guards a UDP SNTP client there, not just a buffer), three servers on ESP32,
one everywhere else. Code sizes buffers fromNtpProfile::SERVER_COUNTrather
than the macro. -
NTP_PROFILE_SERVER_COUNTreplacesNETWORK_PROFILE_NTP_SERVER_COUNT; the old
name is reported by a#warningrather than ignored, since a build still
passing it would silently get the default count. -
json_cat_esc()indetail/json_tools.h— appends a string escaped, without
a scratch buffer. A fully escaped host name needs six bytes per character, and
a temporary that size has no business on an ESP8266 stack, least of all inside
an async request handler.MqttProfile::toJson()uses it.
Changed
-
NtpProfile::setConfig()andMqttProfile::setConfig()validate what they are
given, through their owncheckConfig(), and store nothing unless it passes.
Both classes already had the check written; it simply was not wired in, so the
same object accepted throughsetConfig()what it refused through
setServer(). A stored address the resolver will never accept is a clock that
silently never syncs, or a broker never reached — the kind of failure that
looks like a network fault.The three profile families now answer the same way:
setConfig()refuses an
invalid struct everywhere.checkConfig()here is virtual, so unlike
NetworkProfile's staticisValidConfig(), a subclass's own rules apply
through it automatically.What still passes is unchanged where it matters: an
NtpProfilewith every
slot empty is a device told to take its time from DHCP, and a disabled
MqttProfilekeeps whatever half-filled form the user switched off. The
portal pattern — stage a config, check it, keep it — works as before; it just
no longer matters whether the check was remembered.This is a tightening. A struct that
setConfig()used to swallow is now
refused, and agetConfig()→ edit →setConfig()round-trip of something
loaded from an older or hand-edited namespace can fail where it did not.
loadCfg()andimportLegacy()deliberately stay permissive: the check
belongs where a new value arrives, not where an existing one is recovered. -
NtpProfileandMqttProfilegainedconfigCheckName(), matching
NetworkProfile's. Each class has its ownConfigCheckenum, so one shared
function could not have covered them; without their own, a caller reporting
which field was refused had to write the switch that the third class had
been given. Now thatsetConfig()can refuse, the name is what makes the
refusal reportable. -
detail/prefs_tools.hnames AVR instead of catching it with#else, and an
unsupported architecture now fails at the include with a message saying what a
port would need.EEPROMPreferences.hdeclares its class unconditionally and
guards only its.cpp, so the#elsebranch compiled on a fourth
architecture and then failed at link time withundefined reference to EEPROMPreferences::begin— a long way from the cause.NetworkProfile.h
already used the narrower guard; the two now agree, and the message follows
the oneWiFiProfile.hhas carried for the same situation. -
ESP8266:
isValidTxPower()now also requires the value to sit on the 0.25 dBm
grid, not merely inside
[MIN_WIFI_TX_POWER_dBm,MAX_WIFI_TX_POWER_dBm].setOutputPower()
truncates to the grid on its way to the radio, so a value between two steps
was accepted, stored and reported back as something the radio never produced —
the same silent step-down the ESP32 branch already existed to catch. The two
platforms now ask the same question about the grid and differ only where they
genuinely do: ESP32 additionally accepts just the dozen discrete levels its
radio offers.API.mdalready documented ESP8266 as accepting any multiple of 0.25 dBm,
so this brings the code to the documentation rather than the other way round.
The prose above that table, which said every value in range was accepted,
contradicted it and has been corrected.This narrows what is accepted. A stored TX power off the grid now fails
validation, andloadCfg()replaces it withDEFAULT_WIFI_TX_POWER_dBmas it
does for any other unacceptable value. -
toJson()printstxpwrwith two decimals instead of%f's six
("txpwr":19.50). Two are exact for the 0.25 dBm grid now enforced on both
platforms, and the other four were always zeros. One would not do: 13.25 would
come back as 13.2, and ESP8266 offers the whole grid, not only the halves
ESP32's level set happens to use.
Removed
-
The library's two
using Preferences = EEPROMPreferences;declarations, one in
NetworkProfile.hand one indetail/prefs_tools.h.EEPROMPreferences.hhas
declared the alias itself since 0.2.0, so both were redundant — and the one in
NetworkProfile.hwas doubly so, sitting below an include ofprefs_tools.h
that had already brought it in.Redeclaring an alias to the same type is legal, which is why this went
unnoticed, but it overrodeEEPROMPREFERENCES_NO_PREFERENCES_ALIAS— and that
macro is a working extension point, not just a collision escape. An application
can define it, aliasPreferencesto its own EEPROMPreferences-compatible type
(a different EEPROM offset, for instance), include this library afterwards, and
the profile classes will store through the application's type. Our copies made
the macro look available while quietly cancelling it. -
The NTP fields are gone from
NetworkProfile:getNtp(),setNtp(),
isConfiguredNtp(), thentp[]array inNetworkConfig,NTP_SERVER_COUNT,
ConfigCheck::BadNtp, and theNETWORK_PROFILE_*_NTP_SERVERSmacros. Use
NtpProfile;importLegacy()reads what an earlier version stored.Each profile carried its own copy — around 780 bytes for three servers — so two
interfaces meant the samepool.ntp.orgstored twice and free to drift. What
is
saved is not only the RAM: the largest document the portal serves shrinks with
it, and there is one place to look when the time is wrong.
Documentation
- Comments that explained the code by what it used to be are reworded to explain
it by what it must do. A reader who never saw the earlier version cannot check
a sentence like "would have restored the old one" against anything; the same
point stated as a consequence — "without the reset, loading back into the same
object would hand the old one straight back" — needs no such memory. Affects
_doLoad()inNetworkProfile,WiFiProfileandMqttProfile, and
MqttProfile::setConfig(). The development history itself belongs here, in
this file. - The superseded way of giving interfaces different NTP servers is corrected in
API.md,README.mdand theNtpConfigexample. All three said to keep two
profiles and swap them on the manager from itsCONNECTEDhandler; the
binding now lives on the adapter, because the manager settles the DHCP
option-42 question before it starts an interface and no event has said which
interface is taking over that early. The change was made inNetworkManager,
so these were left behind. README.md's memory table said anNtpProfileis charged once for the device
rather than per interface. One instance is the usual case and the reason the
servers moved out of the network profiles, but per-interface instances are now
a supported choice and cost accordingly.NtpProfile::getServer()said it returns false for a short buffer. It
truncates withsnprintf()and reports true, which is what the accessor above
it inNetworkManageralready documented. The doxygen now says so, and says
why it matters: a truncated host name is a different server, not a partial
answer.API.mdstill listed NTP among the fieldsNetworkProfile::setConfig()and
getConfig()carry, and claimed NTP entries were validated there. They moved
toNtpProfile.library.propertiesdescribed the library as WiFiProfile and EthProfile with
NTP among their fields. It names all four classes now.- Comments and documents left behind by the NTP move and by earlier reworks are
corrected.NetworkConfig's note described its address fields as strings
validated as IP-or-FQDN, which was true only while they held NTP servers; the
default constructor's summary read "the hostname servers are empty", a sentence
left half-edited when "and NTP" came out of it; andJSON_LEN's sizing still
accounted for three NTP servers per profile, putting the worst case at ~1150
bytes when the real figure is now 482 (266 for anEthProfile). The constant
stays at 1200 — the headroom is for fields a subclass adds through_doJson(),
which are counted against the same buffer — but it now says so instead of
implying the space is spoken for. - The AVR persistence story is told as it is. The class header still said
saveCfg()/loadCfg()delegate to user-supplied callbacks andclearCfg()
claimed to be a no-op returning false there; all three have gone through
EEPROMPreferences for some time, andclear()works. TheSaveCallbackand
LoadCallbacktypedefs those sentences referred to are removed — they were
declared, never used, and never called by anything. NTP_PROFILE_SERVER_COUNTis no longer described as disabling NTP when set to
zero. It is a count (1..3); zero is refused by a#errornaming
NTP_PROFILE_ENABLED, which is the switch.API.mdandREADME.mdsaid
otherwise, and the CHANGELOG carried two entries for this macro that
contradicted each other — one describing the current behaviour, one still
claiming zero compiles the class away. The stale half is gone, as is a third
entry restating the ESP8266 default in terms of the obsolete
NETWORK_PROFILE_NTP_SERVER_COUNTname and of NTP servers "per profile".API.mdspelledConfigSourceasACTIVE/DEFAULT; the enum is
ACTIVE/FACTORY, which the surrounding prose already used. The DNS range
was given as 1–2, but ESP32 allows 3.- Doxygen blocks that had drifted onto the wrong declaration are moved back.
NetworkProfile::isValidConfig()andMqttProfile::checkConfig()each had
their description sitting immediately above the deleted template overload
that follows them, so Doxygen attached it there and the real function was
documented by nothing. - README and API.md follow the split: the memory section now charges the servers
once toNtpProfilerather than per interface, and states plainly that
declaring no instance costs nothing.HOST_FQDN_LENis called out as the most
effective knob, being multiplied by every profile and every NTP slot.
Examples
NtpConfigrewritten forNtpProfile: the old one exercisedsetNtp(),
getNtp()andisConfiguredNtp()on aNetworkProfile, none of which exist
any more, so it could not compile. It now covers the class's whole surface,
includingimportLegacy()for an upgraded device and the two-instance case for
interfaces that really do reach different servers.BasicUsageandBasicEthUsageno longer writecfg.ntp[], which is gone.