You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
8.7 KiB
288 lines
8.7 KiB
/**
|
|
* AutoConnectCredential class implementation.
|
|
* @file AutoConnectCredential.cpp
|
|
* @author hieromon@gmail.com
|
|
* @version 1.0.0
|
|
* @date 2018-02-17
|
|
* @copyright MIT license.
|
|
*/
|
|
|
|
#include <EEPROM.h>
|
|
#include "AutoConnectCredential.h"
|
|
|
|
#define AC_IDENTIFIER "AC_CREDT"
|
|
#define AC_HEADERSIZE ((int)(_offset + sizeof(AC_IDENTIFIER) - 1 + sizeof(uint8_t) + sizeof(uint16_t)))
|
|
|
|
/**
|
|
* AutoConnectCredential constructor takes the available count of saved
|
|
* entries.
|
|
* A stored credential data structure in EEPROM.
|
|
* 0 7 8 9a b (t)
|
|
* +--------+-+--+-----------------+-----------------+--+
|
|
* |AC_CREDT|e|ss|ssid\0pass\0bssid|ssid\0pass\0bssid|\0|
|
|
* +--------+-+--+-----------------+-----------------+--+
|
|
* AC_CREDT : Identifier. 8 characters.
|
|
* e : Number of contained entries(uint8_t).
|
|
* ss : Container size, excluding ID and number of entries(uint16_t).
|
|
* ssid: SSID string with null termination.
|
|
* password : Password string with null termination.
|
|
* bssid : BSSID 6 bytes.
|
|
* t : The end of the container is a continuous '\0'.
|
|
* The AC_CREDT identifier is at the beginning of the area.
|
|
* SSID and PASSWORD are terminated by '\ 0'.
|
|
* Free area are filled with FF, which is reused as an area for insertion.
|
|
*/
|
|
AutoConnectCredential::AutoConnectCredential() {
|
|
_offset = AC_IDENTIFIER_OFFSET;
|
|
_allocateEntry();
|
|
}
|
|
|
|
AutoConnectCredential::AutoConnectCredential(uint16_t offset) {
|
|
// Save offset for the credential area.
|
|
_offset = offset;
|
|
_allocateEntry();
|
|
}
|
|
|
|
void AutoConnectCredential::_allocateEntry() {
|
|
char id_c[sizeof(AC_IDENTIFIER) - 1];
|
|
uint8_t c;
|
|
|
|
EEPROM.begin(AC_HEADERSIZE);
|
|
|
|
// Validate the save area of the EEPROM.
|
|
// If it is a valid area, retrieve the stored number of entries,
|
|
// if the identifier is not saved, initialize the EEPROM area.
|
|
_dp = _offset;
|
|
for (c = 0; c < sizeof(id_c); c++) {
|
|
id_c[c] = static_cast<char>(EEPROM.read(_dp++));
|
|
}
|
|
if (!strncmp(id_c, AC_IDENTIFIER, sizeof(id_c))) {
|
|
_entries = EEPROM.read(static_cast<int>(_dp++));
|
|
_containSize = EEPROM.read(static_cast<int>(_dp++));
|
|
_containSize += EEPROM.read(static_cast<int>(_dp)) << 8;
|
|
}
|
|
else {
|
|
_entries = 0;
|
|
_containSize = 0;
|
|
}
|
|
|
|
EEPROM.end();
|
|
}
|
|
|
|
/**
|
|
* The destructor ends EEPROM access.
|
|
*/
|
|
AutoConnectCredential::~AutoConnectCredential() {
|
|
EEPROM.end();
|
|
}
|
|
|
|
/**
|
|
* Delete the credential entry for the specified SSID in the EEPROM.
|
|
* @param ssid A SSID character string to be deleted.
|
|
* @retval true The entry successfully delete.
|
|
* false Could not deleted.
|
|
*/
|
|
bool AutoConnectCredential::del(const char* ssid) {
|
|
struct station_config entry;
|
|
bool rc = false;
|
|
|
|
if (load(ssid, &entry) >= 0) {
|
|
// Saved credential detected, _ep has the entry location.
|
|
EEPROM.begin(AC_HEADERSIZE + _containSize);
|
|
_dp = _ep;
|
|
|
|
// Erase SSID
|
|
while (EEPROM.read(_dp) != 0x00)
|
|
EEPROM.write(_dp++, 0xff);
|
|
|
|
// Erase Password
|
|
EEPROM.write(_dp++, 0xff);
|
|
while (EEPROM.read(_dp) != 0x00)
|
|
EEPROM.write(_dp++, 0xff);
|
|
|
|
// Erase BSSID
|
|
EEPROM.write(_dp++, 0xff);
|
|
for (uint8_t i = 0; i < sizeof(station_config::bssid); i++)
|
|
EEPROM.write(_dp++, 0xff);
|
|
|
|
// End 0xff writing, update headers.
|
|
_entries--;
|
|
EEPROM.write(_offset + static_cast<int>(sizeof(AC_IDENTIFIER)) - 1, _entries);
|
|
|
|
// commit it.
|
|
rc = EEPROM.commit();
|
|
delay(10);
|
|
EEPROM.end();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Load the credential entry for the specified SSID from the EEPROM.
|
|
* The credentials are stored to the station_config structure which specified
|
|
* by *config as the SSID and password.
|
|
* @param ssid A SSID character string to be loaded.
|
|
* @param config A station_config structure pointer.
|
|
* @retval The entry number of the SSID in EEPROM. If the number less than 0,
|
|
* the specified SSID was not found.
|
|
*/
|
|
int8_t AutoConnectCredential::load(const char* ssid, struct station_config* config) {
|
|
int8_t entry = -1;
|
|
|
|
_dp = AC_HEADERSIZE;
|
|
if (_entries) {
|
|
EEPROM.begin(AC_HEADERSIZE + _containSize);
|
|
for (uint8_t i = 0; i < _entries; i++) {
|
|
_retrieveEntry(reinterpret_cast<char*>(config->ssid), reinterpret_cast<char*>(config->password), config->bssid);
|
|
if (!strcmp(ssid, (const char*)config->ssid)) {
|
|
entry = i;
|
|
break;
|
|
}
|
|
}
|
|
EEPROM.end();
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* Load the credential entry for the specified number from the EEPROM.
|
|
* The credentials are stored to the station_config structure which specified
|
|
* by *config as the SSID and password.
|
|
* @param entry A number of entry to be loaded.
|
|
* @param config A station_config structure pointer.
|
|
* @retval true The entry number of the SSID in EEPROM.
|
|
* false The number is not available.
|
|
*/
|
|
bool AutoConnectCredential::load(int8_t entry, struct station_config* config) {
|
|
_dp = AC_HEADERSIZE;
|
|
if (_entries && entry < _entries) {
|
|
EEPROM.begin(AC_HEADERSIZE + _containSize);
|
|
while (entry-- >= 0)
|
|
_retrieveEntry(reinterpret_cast<char*>(config->ssid), reinterpret_cast<char*>(config->password), config->bssid);
|
|
EEPROM.end();
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save SSID and password to EEPROM.
|
|
* When the same SSID already exists, it will be replaced. If the current
|
|
* entry size insufficient for the new entry, the entry will be appended
|
|
* and increase whole size. Its previous areas are freed with FF and reused.
|
|
* @param config A pointer to the station_config structure storing SSID and password.
|
|
* @retval true Successfully saved.
|
|
* @retval false EEPROM commit failed.
|
|
*/
|
|
bool AutoConnectCredential::save(const struct station_config* config) {
|
|
static const char _id[] = AC_IDENTIFIER;
|
|
struct station_config stage;
|
|
int8_t entry;
|
|
bool rep = false;
|
|
|
|
// Detect same entry for replacement.
|
|
entry = load((const char*)(config->ssid), &stage);
|
|
|
|
// Saving start.
|
|
EEPROM.begin(AC_HEADERSIZE + _containSize + sizeof(struct station_config));
|
|
|
|
// Determine insertion or replacement.
|
|
if (entry >= 0) {
|
|
// An entry with the same SSID was found, release the area for replacement.
|
|
_dp = _ep;
|
|
for (uint8_t dm = 2; dm; _dp++) {
|
|
if (EEPROM.read(_dp) == '\0')
|
|
dm--;
|
|
EEPROM.write(_dp, 0xff); // Clear SSID, Passphrase
|
|
}
|
|
for (uint8_t i = 0; i < sizeof(station_config::bssid); i++)
|
|
EEPROM.write(_dp++, 0xff); // Clear BSSID
|
|
}
|
|
else {
|
|
// Same entry not found. increase the entry.
|
|
_entries++;
|
|
int i;
|
|
for (i = 0; i < static_cast<int>(sizeof(_id)) - 1; i++)
|
|
EEPROM.write(i + _offset, (uint8_t)_id[i]);
|
|
EEPROM.write(i + _offset, _entries);
|
|
}
|
|
|
|
// Seek insertion point, evaluate capacity to insert the new entry.
|
|
uint8_t eSize = strlen((const char*)config->ssid) + strlen((const char*)config->password) + sizeof(station_config::bssid) + 2;
|
|
for (_dp = AC_HEADERSIZE; _dp < _containSize + AC_HEADERSIZE; _dp++) {
|
|
if (EEPROM.read(_dp) == 0xff) {
|
|
uint8_t fp = _dp;
|
|
while (EEPROM.read(++_dp) == 0xff) {}
|
|
if (_dp - fp >= eSize) {
|
|
_dp = fp;
|
|
rep = true;
|
|
break;
|
|
}
|
|
_dp--;
|
|
}
|
|
}
|
|
|
|
// Save new entry
|
|
uint8_t c;
|
|
const uint8_t* dt;
|
|
dt = config->ssid;
|
|
do { // Write SSID
|
|
c = *dt++;
|
|
EEPROM.write(_dp++, c);
|
|
} while (c != '\0');
|
|
dt = config->password;
|
|
do { // Write password
|
|
c = *dt++;
|
|
EEPROM.write(_dp++, c);
|
|
} while (c != '\0');
|
|
for (uint8_t i = 0; i < sizeof(station_config::bssid); i++)
|
|
EEPROM.write(_dp++, config->bssid[i]); // write BSSID
|
|
// Terminate container, mark to the end of credential area.
|
|
// When the entry is replaced, not mark a terminator.
|
|
if (!rep) {
|
|
EEPROM.write(_dp, '\0');
|
|
|
|
// Update container size
|
|
_containSize = _dp - AC_HEADERSIZE;
|
|
EEPROM.write(_offset + sizeof(AC_IDENTIFIER) - 1 + sizeof(uint8_t), (uint8_t)_containSize);
|
|
EEPROM.write(_offset + sizeof(AC_IDENTIFIER) - 1 + sizeof(uint8_t) + 1, (uint8_t)(_containSize >> 8));
|
|
}
|
|
|
|
bool rc = EEPROM.commit();
|
|
delay(10);
|
|
EEPROM.end();
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Get the SSID and password from EEPROM indicated by _dp as the pointer
|
|
* of current read address. FF is skipped as unavailable area.
|
|
* @param ssid A SSID storing address.
|
|
* @param password A password storing address.
|
|
*/
|
|
void AutoConnectCredential::_retrieveEntry(char* ssid, char* password, uint8_t* bssid) {
|
|
uint8_t ec;
|
|
|
|
// Skip unavailable entry.
|
|
while ((ec = EEPROM.read(_dp++)) == 0xff) {}
|
|
|
|
// Retrieve SSID
|
|
_ep = _dp - 1;
|
|
*ssid++ = ec;
|
|
do {
|
|
ec = EEPROM.read(_dp++);
|
|
*ssid++ = ec;
|
|
} while (ec != '\0');
|
|
// Retrieve Password
|
|
do {
|
|
ec = EEPROM.read(_dp++);
|
|
*password++ = ec;
|
|
} while (ec != '\0');
|
|
// Retrieve BSSID
|
|
for (uint8_t i = 0; i < sizeof(station_config::bssid); i++) {
|
|
bssid[i] = EEPROM.read(_dp++);
|
|
}
|
|
}
|
|
|