CARRIER SIGNAL START

- start working on autocon via phonebook
- try to avoid autoconnecting to a guru meditation.
This commit is contained in:
2023-10-06 08:39:30 +00:00
parent bec470f6d7
commit fbeb54a830
13 changed files with 1361 additions and 8 deletions

291
src/Arena.cc Normal file
View File

@@ -0,0 +1,291 @@
#if defined(__linux__) || defined(MSVC)
#include <stdio.h>
#endif
#include <stdlib.h>
#include <string.h>
#if defined(__linux__)
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#if defined(DESKTOP_BUILD)
#include <iostream>
#include <ios>
#endif
#if defined(ESP_PLATFORM)
#include <Arduino.h>
#include <SPIFFS.h>
#endif
#include "Arena.h"
#define ARENA_UNINIT 0
#define ARENA_STATIC 1
#define ARENA_ALLOC 2
#if defined(__linux__)
#define ARENA_MMAP 3
#define PROT_RW PROT_READ|PROT_WRITE
#endif
void
InitializeArena(Arena &arena)
{
arena.Store = NULL;
arena.Size = 0;
arena.Type = ARENA_UNINIT;
arena.fd = 0;
}
int
NewStaticArena(Arena &arena, uint8_t *mem, size_t size)
{
arena.Store = mem;
arena.Size = size;
arena.Type = ARENA_STATIC;
return 0;
}
int
AllocNewArena(Arena & arena, size_t size)
{
if (arena.Size > 0) {
if (DestroyArena(arena) != 0) {
return -1;
}
}
arena.Type = ARENA_ALLOC;
arena.Size = size;
arena.Store = (uint8_t *)calloc(sizeof(uint8_t), size);
if (arena.Store == NULL) {
return -1;
}
return 0;
}
#if defined(__linux__)
int
MMapArena(Arena &arena, int fd, size_t size)
{
if (arena.Size > 0) {
if (DestroyArena(arena) != 0) {
return -1;
}
}
arena.Type = ARENA_MMAP;
arena.Size = size;
arena.Store = (uint8_t *)mmap(NULL, size, PROT_RW, MAP_SHARED, fd, 0);
if ((void *)arena.Store == MAP_FAILED) {
return -1;
}
arena.fd = fd;
return 0;
}
int
OpenArena(Arena &arena, const char *path)
{
struct stat st;
if (arena.Size > 0) {
if (DestroyArena(arena) != 0) {
return -1;
}
}
if (stat(path, &st) != 0) {
return -1;
}
arena.fd = open(path, O_RDWR);
if (arena.fd == -1) {
return -1;
}
return MMapArena(arena, arena.fd, (size_t)st.st_size);
}
int
CreateArena(Arena &arena, const char *path, size_t size, mode_t mode)
{
int fd = 0;
if (arena.Size > 0) {
if (DestroyArena(arena) != 0) {
return -1;
}
}
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
if (fd == -1) {
return -1;
}
if (ftruncate(fd, size) == -1) {
return -1;
}
close(fd);
return OpenArena(arena, path);
}
#endif
/*
* ClearArena clears the memory being used, removing any data
* present. It does not free the memory; it is effectively a
* wrapper around memset.
*/
void
ClearArena(Arena &arena)
{
if (arena.Size == 0) {
return;
}
memset(arena.Store, 0, arena.Size);
}
int
DestroyArena(Arena &arena)
{
if (arena.Type == ARENA_UNINIT) {
return 0;
}
switch (arena.Type) {
case ARENA_STATIC:
break;
case ARENA_ALLOC:
free(arena.Store);
break;
#if defined(__linux__)
case ARENA_MMAP:
if (munmap(arena.Store, arena.Size) == -1) {
return -1;
}
if (close(arena.fd) == -1) {
return -1;
}
arena.fd = 0;
break;
#endif
default:
#if defined(NDEBUG)
return -1;
#else
abort();
#endif
}
arena.Type = ARENA_UNINIT;
arena.Size = 0;
arena.Store = NULL;
return 0;
}
#if defined(DESKTOP_BUILD)
void
DisplayArena(const Arena &arena)
{
std::cout << "Arena @ 0x";
std::cout << std::hex << (uintptr_t)&arena << std::endl;
std::cout << std::dec;
std::cout << "\tStore is " << arena.Size << " bytes at address 0x";
std::cout << std::hex << (uintptr_t)&(arena.Store) << std::endl;
std::cout << "\tType: ";
switch (arena.Type) {
case ARENA_UNINIT:
std::cout << "uninitialized";
break;
case ARENA_STATIC:
std::cout << "static";
break;
case ARENA_ALLOC:
std::cout << "allocated";
break;
#if defined(__linux__)
case ARENA_MMAP:
std::cout << "mmap/file";
break;
#endif
default:
std::cout << "unknown (this is a bug)";
}
std::cout << std::endl;
}
#else
void
DisplayArena(const Arena &arena)
{
}
#endif
#if defined(__linux__) || defined(__MSVC__)
int
WriteArena(const Arena &arena, const char *path)
{
FILE *arenaFile = NULL;
int retc = -1;
arenaFile = fopen(path, "w");
if (arenaFile == NULL) {
return -1;
}
if (fwrite(arena.Store, sizeof(*arena.Store), arena.Size,
arenaFile) == arena.Size) {
retc = 0;
}
if (fclose(arenaFile) != 0) {
return -1;
}
return retc;
}
#elif defined(ESP_PLATFORM)
int
WriteArena(const Arena &arena, const char *path)
{
File arenaDataFile = SPIFFS.open(path, FILE_WRITE);
size_t offset = 0;
size_t written = 0;
while (written < arena.Size) {
offset = arenaDataFile.write(arena.Store + written, arena.Size - written);
if (offset == 0) {
break;
}
written += offset;
}
if (written != arena.Size) {
return -1;
}
return 0;
}
#endif

186
src/Dictionary.cc Normal file
View File

@@ -0,0 +1,186 @@
#include <Arduino.h>
#include <cstring>
#include <cstdlib>
#include "Dictionary.h"
#if defined(DESKTOP_BUILD)
#include <iostream>
#endif
bool
Dictionary::Lookup(const char *key, uint8_t klen, TLV::Record &res)
{
res.Tag = this->kTag;
uint8_t *cursor = TLV::FindTag(this->arena, NULL, res);
while ((cursor != NULL) && (res.Tag != TAG_EMPTY)) {
if (klen != res.Len) {
cursor = TLV::FindTag(this->arena, cursor, res);
continue;
}
if (memcmp(res.Val, key, klen) == 0) {
TLV::ReadFromMemory(res, cursor);
if (res.Tag != this->vTag) {
abort();
}
return true;
}
cursor = TLV::FindTag(this->arena, cursor, res);
}
return false;
}
int
Dictionary::Set(const char *key, uint8_t klen, const char *val, uint8_t vlen)
{
TLV::Record rec;
uint8_t *cursor = NULL;
SetRecord(rec, this->kTag, klen, key);
cursor = this->seek(key, klen);
if (cursor != NULL) {
TLV::DeleteRecord(this->arena, cursor);
TLV::DeleteRecord(this->arena, cursor);
}
if (!spaceAvailable(klen, vlen)) {
return -1;
}
cursor = TLV::WriteToMemory(this->arena, NULL, rec);
if (cursor == NULL) {
return -1;
}
SetRecord(rec, this->vTag, vlen, val);
if (TLV::WriteToMemory(this->arena, NULL, rec) == NULL) {
return -1;
}
return 0;
}
uint8_t *
Dictionary::seek(const char *key, uint8_t klen)
{
TLV::Record rec;
rec.Tag = this->kTag;
uint8_t *cursor = TLV::LocateTag(this->arena, NULL, rec);
while (cursor != NULL) {
if ((klen == rec.Len) && (this->kTag == rec.Tag)) {
if (memcmp(rec.Val, key, klen) == 0) {
return cursor;
}
}
cursor = TLV::SkipRecord(rec, cursor);
cursor = TLV::LocateTag(this->arena, cursor, rec);
}
return NULL;
}
bool
Dictionary::Has(const char *key, uint8_t klen)
{
return this->seek(key, klen) != NULL;
}
bool
Dictionary::spaceAvailable(uint8_t klen, uint8_t vlen)
{
size_t required = 0;
uintptr_t remaining = 0;
uint8_t *cursor = NULL;
cursor = TLV::FindEmpty(this->arena, NULL);
if (cursor == NULL) {
return false;
}
required += klen + 2;
required += vlen + 2;
remaining = (uintptr_t)cursor - (uintptr_t)arena.Store;
remaining = arena.Size - remaining;
return ((size_t)remaining >= required);
}
#if defined(DESKTOP_BUILD)
void
Dictionary::DumpKVPairs()
{
uint8_t *cursor = (this->arena).Store;
TLV::Record rec;
TLV::ReadFromMemory(rec, cursor);
std::cout << "Dictionary KV pairs" << std::endl;
if (rec.Tag == TAG_EMPTY) {
std::cout << "\t(NONE)" << std::endl;
return;
}
while ((cursor != NULL) && (rec.Tag != TAG_EMPTY)) {
std::cout << "\t" << rec.Val << "->";
cursor = TLV::SkipRecord(rec, cursor);
TLV::ReadFromMemory(rec, cursor);
std::cout << rec.Val << std::endl;
cursor = TLV::SkipRecord(rec, cursor);
TLV::ReadFromMemory(rec, cursor);
}
}
#elif defined(ESP_PLATFORM)
#include <Arduino.h>
void
Dictionary::DumpKVPairs()
{
uint8_t *cursor = (this->arena).Store;
TLV::Record rec;
TLV::ReadFromMemory(rec, cursor);
Serial.println("dictionary entries: {");
if (rec.Tag == TAG_EMPTY) {
Serial.println("\tNONE");
Serial.println("}");
return;
}
while ((cursor != NULL) && (rec.Tag != TAG_EMPTY)) {
Serial.print("\t");
Serial.print(rec.Val);
Serial.print("->");
cursor = TLV::SkipRecord(rec, cursor);
TLV::ReadFromMemory(rec, cursor);
Serial.println(rec.Val);
cursor = TLV::SkipRecord(rec, cursor);
TLV::ReadFromMemory(rec, cursor);
}
Serial.println("}");
}
#else
void
Dictionary::DumpKVPairs(const char *label)
{
}
#endif
void
Dictionary::DumpToFile(const char *path)
{
WriteArena(this->arena, path);
}

219
src/TLV.cc Normal file
View File

@@ -0,0 +1,219 @@
#include <cstring>
#include "TLV.h"
#if defined(ESP_PLATFORM)
#include <Arduino.h>
#endif
#define REC_SIZE(x) ((std::size_t)x.Len + 2)
namespace TLV {
static inline size_t
spaceRemaining(const Arena &arena, const uint8_t *cursor)
{
uintptr_t remaining = 0;
if (cursor == NULL) {
cursor = arena.Store;
}
remaining = (uintptr_t)cursor - (uintptr_t)arena.Store;
remaining = arena.Size - remaining;
return remaining;
}
static bool
cursorInArena(const Arena &arena, const uint8_t *cursor)
{
if (cursor == NULL) {
return false;
}
uint8_t *end = arena.Store + spaceRemaining(arena, NULL);
if (cursor < arena.Store) {
return false;
}
return cursor < end;
}
static bool
spaceAvailable(const Arena &arena, const uint8_t *cursor, const uint8_t len)
{
uintptr_t remaining = 0;
if (cursor == NULL) {
return false;
}
remaining = (uintptr_t)cursor - (uintptr_t)arena.Store;
remaining = arena.Size - remaining;
return ((size_t)remaining > ((size_t)len+2));
}
static inline void
clearUnused(Record &rec)
{
uint8_t trail = TLV_MAX_LEN-rec.Len;
memset(rec.Val+rec.Len, 0, trail);
}
uint8_t *
WriteToMemory(Arena &arena, uint8_t *cursor, Record &rec)
{
// If cursor is NULL, the user needs us to select an empty
// slot for the record. If we can't find one, that's an
// error.
//
// If, however, the user gives us a cursor, we'll trust it
// (though spaceAvailable will sanity check that cursor).
if (cursor == NULL) {
cursor = FindEmpty(arena, cursor);
if (cursor == NULL) {
return NULL;
}
}
if (!spaceAvailable(arena, cursor, rec.Len)) {
return NULL;
}
memcpy(cursor, &rec, REC_SIZE(rec));
cursor = SkipRecord(rec, cursor);
return cursor;
}
void
SetRecord(Record &rec, uint8_t tag, uint8_t len, const char *val)
{
rec.Tag = tag;
rec.Len = len;
memcpy(rec.Val, val, len);
clearUnused(rec);
}
void
ReadFromMemory(Record &rec, uint8_t *cursor)
{
rec.Tag = cursor[0];
rec.Len = cursor[1];
memcpy(rec.Val, cursor+2, rec.Len);
clearUnused(rec);
}
/*
* returns a pointer to memory where the record was found,
* e.g. FindTag(...)[0] is the tag of the found record.
*/
uint8_t *
FindTag(Arena &arena, uint8_t *cursor, Record &rec)
{
cursor = LocateTag(arena, cursor, rec);
if (cursor == NULL) {
return NULL;
}
if (rec.Tag != TAG_EMPTY) {
cursor = SkipRecord(rec, cursor);
}
return cursor;
}
uint8_t *
LocateTag(Arena &arena, uint8_t *cursor, Record &rec)
{
uint8_t tag, len;
if (cursor == NULL) {
cursor = arena.Store;
}
if (!cursorInArena(arena, cursor)) {
return NULL;
}
while ((tag = cursor[0]) != rec.Tag) {
// We could call SkipRecord, but we already need
// to pull the length to figure out if we're at
// the end or not.
len = cursor[1];
if ((tag == TAG_EMPTY) && (len == 0)) {
return NULL;
}
if (!spaceAvailable(arena, cursor, len)) {
return NULL;
}
cursor += len;
cursor += 2;
}
if (tag != rec.Tag) {
return NULL;
}
if (tag != TAG_EMPTY) {
ReadFromMemory(rec, cursor);
}
return cursor;
}
uint8_t *
FindEmpty(Arena &arena, uint8_t *cursor) {
Record rec;
rec.Tag = TAG_EMPTY;
return FindTag(arena, cursor, rec);
}
uint8_t *
SkipRecord(Record &rec, uint8_t *cursor)
{
return (uint8_t *)((uintptr_t)cursor + rec.Len + 2);
}
void
DeleteRecord(Arena &arena, uint8_t *cursor)
{
if (cursor == NULL) {
return;
}
uint8_t len = cursor[1] + 2;
uint8_t *stop = arena.Store + arena.Size;
stop -= len;
while (cursor != stop) {
cursor[0] = cursor[len];
cursor++;
}
stop += len;
while (cursor != stop) {
cursor[0] = 0;
cursor++;
}
}
} // namespace TLV

95
src/WiFiMgr.cc Normal file
View File

@@ -0,0 +1,95 @@
#include <Arduino.h>
#include <WiFi.h>
#include <string.h>
#include "Dictionary.h"
#include "TLV.h"
#include "WiFiMgr.h"
#define MAX_WAIT 10000
#define CONN_WAIT 250
bool
SetupWiFi()
{
WiFi.mode(WIFI_STA);
return true;
}
static bool
tryConnect(Dictionary &pb, int network)
{
const char *ssid = WiFi.SSID(network).c_str();
size_t ssidLen = strnlen(ssid, TLV_MAX_LEN);
TLV::Record password;
size_t waitedFor = 0;
if (ssidLen == 0) {
return false;
}
Serial.print("MODEM: CHECK ");
Serial.println(ssid);
if (!pb.Lookup(ssid, uint8_t(ssidLen), password)) {
Serial.println("SSID NOT IN PHONEBOOK");
return false;
}
Serial.println("CONNECTING");
WiFi.begin(ssid, password.Val);
while ((WiFi.status() != WL_CONNECTED) && (waitedFor < MAX_WAIT)) {
waitedFor += CONN_WAIT;
if ((waitedFor % 1000) == 0) {
Serial.print(".");
}
delay(250);
}
if (WiFi.status() == WL_CONNECTED) {
Serial.print("MODEM ADDR ");
Serial.println(WiFi.localIP());
return true;
}
Serial.println("CARRIER SIGNAL LOST");
return false;
}
bool
Autoconnect(Dictionary &pb)
{
Autoconnect(pb, true);
}
bool
Autoconnect(Dictionary &pb, bool reset)
{
int networkCount = 0;
int network = 0;
Serial.println("MODEM: TRY AUTOCONNECT");
if (reset) {
Serial.println("RADIO RESET");
SetupWiFi();
WiFi.disconnect();
delay(1000);
} else {
Serial.println("NO RESET");
}
networkCount = WiFi.scanNetworks();
for (network = 0; network < networkCount; network++) {
if (tryConnect(pb, network)) {
return true;
}
}
Serial.println("NO CARRIER");
return false;
}

View File

@@ -1,18 +1,78 @@
#include <Arduino.h>
#include <SPIFFS.h>
#include "Arena.h"
#include "Dictionary.h"
#include "WiFiMgr.h"
#include "homenet.h"
constexpr size_t PHONEBOOK_SIZE = 512;
constexpr char pbFilePath[] = "/pb.dat";
uint8_t phonebookBuffer[PHONEBOOK_SIZE];
Arena arena;
Dictionary phonebook(arena);
static bool
setupPhonebook()
{
File pbFile = SPIFFS.open(pbFilePath, FILE_READ);
size_t fileSize = pbFile.size();
bool ok = false;
Serial.print("DAT FILE ");
Serial.print(fileSize);
Serial.println("B");
if (fileSize == 0) {
Serial.println("INIT PHONEBOOK");
phonebook.Set(HOME_SSID, HOME_SSIDLEN, HOME_WPA, HOME_WPALEN);
if (WriteArena(arena, pbFilePath) == 0) {
ok = true;
}
pbFile.close();
return ok;
}
fileSize = fileSize > PHONEBOOK_SIZE ? PHONEBOOK_SIZE : fileSize;
Serial.print("LOAD PHONEBOOK ");
if (fileSize != pbFile.read(phonebookBuffer, fileSize)) {
Serial.println("FAILED");
pbFile.close();
return false;
}
Serial.println("OK");
pbFile.close();
phonebook.DumpKVPairs();
return true;
}
// put function declarations here:
int myFunction(int, int);
void setup() {
// put your setup code here, to run once:
int result = myFunction(2, 3);
Serial.begin(115200);
while (!Serial) ;
Serial.println("MODEM BOOT");
InitializeArena(arena);
NewStaticArena(arena, phonebookBuffer, PHONEBOOK_SIZE);
if (!SPIFFS.begin(true) && !SPIFFS.begin(false)) {
Serial.println("SPIFFS BEGIN FAIL");
while (true) ;
}
setupPhonebook();
while (!Autoconnect(phonebook)) {
Serial.println("STANDBY");
delay(1000);
}
Serial.println("MODEM READY");
}
void loop() {
// put your main code here, to run repeatedly:
}
// put function definitions here:
int myFunction(int x, int y) {
return x + y;
}