CARRIER SIGNAL START
- start working on autocon via phonebook - try to avoid autoconnecting to a guru meditation.
This commit is contained in:
parent
bec470f6d7
commit
fbeb54a830
|
@ -1,4 +1,5 @@
|
||||||
.pio
|
.pio
|
||||||
|
include/homenet.h
|
||||||
.vscode/.browse.c_cpp.db*
|
.vscode/.browse.c_cpp.db*
|
||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -26,6 +26,7 @@ deploy: $(FIRMWARE)
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
$(PIO) -t clean
|
$(PIO) -t clean
|
||||||
|
rm -rf *.bin unpacked_fs
|
||||||
|
|
||||||
.PHONY: cloc
|
.PHONY: cloc
|
||||||
cloc:
|
cloc:
|
||||||
|
@ -35,3 +36,7 @@ cloc:
|
||||||
test:
|
test:
|
||||||
$(PIO) -t test
|
$(PIO) -t test
|
||||||
|
|
||||||
|
.PHONY: downloadfs
|
||||||
|
downloadfs:
|
||||||
|
$(PIO) -t downloadfs
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef KIMODEM_ARENA_H
|
||||||
|
#define KIMODEM_ARENA_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *Store;
|
||||||
|
size_t Size;
|
||||||
|
int fd;
|
||||||
|
uint8_t Type;
|
||||||
|
} Arena;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InitializeArena is intended for use only with systems that
|
||||||
|
* do not initialize new variables to zero. It should be called
|
||||||
|
* exactly once, at the start of the program. Any other time the
|
||||||
|
* arena needs to be reset, it should be called with clear_arena
|
||||||
|
* or destroy_arena.
|
||||||
|
*/
|
||||||
|
void InitializeArena(Arena &arena);
|
||||||
|
int NewStaticArena(Arena &, uint8_t *, size_t);
|
||||||
|
int AllocNewArena(Arena &, size_t);
|
||||||
|
#if defined(__linux__)
|
||||||
|
int MMapArena(Arena &, int); /* arena will own fd */
|
||||||
|
int CreateArena(Arena &arena, const char *path, size_t size, mode_t mode);
|
||||||
|
int OpenArena(Arena &, const char *, size_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ClearArena(Arena &);
|
||||||
|
int DestroyArena(Arena &); /* dispose of any memory used by arena */
|
||||||
|
|
||||||
|
/* DANGER: if arena is file backed (mmap or open), DO NOT WRITE TO THE
|
||||||
|
* BACKING FILE! */
|
||||||
|
int WriteArena(const Arena &arena, const char *path);
|
||||||
|
|
||||||
|
void DisplayArena(const Arena &arena);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef KLIB_DICTIONARY_H
|
||||||
|
#define KLIB_DICTIONARY_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Arena.h"
|
||||||
|
#include "TLV.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define DICTIONARY_TAG_KEY 1
|
||||||
|
#define DICTIONARY_TAG_VAL 2
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A Dictionary is a collection of key-value pairs, similar to how
|
||||||
|
* a dictionary is a mapping of names to definitions.
|
||||||
|
*/
|
||||||
|
class Dictionary {
|
||||||
|
public:
|
||||||
|
Dictionary(Arena &arena) :
|
||||||
|
arena(arena),
|
||||||
|
kTag(DICTIONARY_TAG_KEY),
|
||||||
|
vTag(DICTIONARY_TAG_VAL) {} ;
|
||||||
|
Dictionary(Arena &arena, uint8_t kt, uint8_t vt) :
|
||||||
|
arena(arena),
|
||||||
|
kTag(kt),
|
||||||
|
vTag(vt) {};
|
||||||
|
|
||||||
|
bool Lookup(const char *key, uint8_t klen, TLV::Record &res);
|
||||||
|
int Set(const char *key, uint8_t klen, const char *val,
|
||||||
|
uint8_t vlen);
|
||||||
|
bool Has(const char *key, uint8_t klen);
|
||||||
|
void DumpKVPairs();
|
||||||
|
void DumpToFile(const char *path);
|
||||||
|
private:
|
||||||
|
uint8_t *seek(const char *key, uint8_t klen);
|
||||||
|
bool spaceAvailable(uint8_t klen, uint8_t vlen);
|
||||||
|
|
||||||
|
Arena &arena;
|
||||||
|
uint8_t kTag;
|
||||||
|
uint8_t vTag;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef KIMODEM_TLV_H
|
||||||
|
#define KIMODEM_TLV_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "Arena.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef TLV_MAX_LEN
|
||||||
|
#define TLV_MAX_LEN 253
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define TAG_EMPTY 0
|
||||||
|
|
||||||
|
|
||||||
|
namespace TLV {
|
||||||
|
|
||||||
|
|
||||||
|
struct Record {
|
||||||
|
uint8_t Tag;
|
||||||
|
uint8_t Len;
|
||||||
|
char Val[TLV_MAX_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t *WriteToMemory(Arena &, uint8_t *, Record &);
|
||||||
|
void ReadFromMemory(Record &, uint8_t *);
|
||||||
|
void SetRecord(Record &, uint8_t, uint8_t, const char *);
|
||||||
|
void DeleteRecord(Arena &, uint8_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns a pointer to memory where the record was found,
|
||||||
|
* e.g. LocateTag(...)[0] is the tag of the found record.
|
||||||
|
* FindTag will call LocateTag and then SkipRecord if the
|
||||||
|
* tag was found.
|
||||||
|
*/
|
||||||
|
uint8_t *FindTag(Arena &, uint8_t *, Record &);
|
||||||
|
uint8_t *LocateTag(Arena &, uint8_t *, Record &);
|
||||||
|
|
||||||
|
uint8_t *FindEmpty(Arena &, uint8_t *);
|
||||||
|
uint8_t *SkipRecord(Record &, uint8_t *);
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace TLV
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef KIMODEM_WIFI_H
|
||||||
|
#define KIMODEM_WIFI_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Dictionary.h"
|
||||||
|
#include "WiFiMgr.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool SetupWiFi();
|
||||||
|
bool Autoconnect(Dictionary &pb);
|
||||||
|
bool Autoconnect(Dictionary &pb, bool reset);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -8,7 +8,11 @@
|
||||||
; Please visit documentation for the other options and examples
|
; Please visit documentation for the other options and examples
|
||||||
; https://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[platformio]
|
||||||
|
|
||||||
[env:sparkfun_esp32micromod]
|
[env:sparkfun_esp32micromod]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = sparkfun_esp32micromod
|
board = sparkfun_esp32micromod
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
||||||
|
extra_scripts = scripts/download_fs.py
|
||||||
|
|
|
@ -0,0 +1,341 @@
|
||||||
|
# Written by Maximilian Gerhardt <maximilian.gerhardt@rub.de>
|
||||||
|
# 29th December 2020
|
||||||
|
# License: Apache
|
||||||
|
# Expanded from functionality provided by PlatformIO's espressif32 and espressif8266 platforms, credited below.
|
||||||
|
# This script provides functions to download the filesystem (SPIFFS or LittleFS) from a running ESP32 / ESP8266
|
||||||
|
# over the serial bootloader using esptool.py, and mklittlefs / mkspiffs for extracting.
|
||||||
|
# run by either using the VSCode task "Custom" -> "Download Filesystem"
|
||||||
|
# or by doing 'pio run -t downloadfs' (with optional '-e <environment>') from the commandline.
|
||||||
|
# output will be saved, by default, in the "unpacked_fs" of the project.
|
||||||
|
# this folder can be changed by writing 'custom_unpack_dir = some_other_dir' in the corresponding platformio.ini
|
||||||
|
# environment.
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from os.path import isfile, join
|
||||||
|
from enum import Enum
|
||||||
|
import typing
|
||||||
|
from platformio.builder.tools.pioupload import AutodetectUploadPort
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
platform = env.PioPlatform()
|
||||||
|
board = env.BoardConfig()
|
||||||
|
mcu = board.get("build.mcu", "esp32")
|
||||||
|
# needed for later
|
||||||
|
AutodetectUploadPort(env)
|
||||||
|
|
||||||
|
class FSType(Enum):
|
||||||
|
SPIFFS="spiffs"
|
||||||
|
LITTLEFS="littlefs"
|
||||||
|
FATFS="fatfs"
|
||||||
|
|
||||||
|
class FSInfo:
|
||||||
|
def __init__(self, fs_type, start, length, page_size, block_size):
|
||||||
|
self.fs_type = fs_type
|
||||||
|
self.start = start
|
||||||
|
self.length = length
|
||||||
|
self.page_size = page_size
|
||||||
|
self.block_size = block_size
|
||||||
|
def __repr__(self):
|
||||||
|
return f"FS type {self.fs_type} Start {hex(self.start)} Len {self.length} Page size {self.page_size} Block size {self.block_size}"
|
||||||
|
# extract command supposed to be implemented by subclasses
|
||||||
|
def get_extract_cmd(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
class LittleFSInfo(FSInfo):
|
||||||
|
def __init__(self, start, length, page_size, block_size):
|
||||||
|
if env["PIOPLATFORM"] == "espressif32":
|
||||||
|
#for ESP32: retrieve and evaluate, e.g. to mkspiffs_espressif32_arduino
|
||||||
|
#Espressif32 Framework 3.X.X: MKSPIFFSTOOL
|
||||||
|
#Espressif32 Framework 4.X.X and 5.X.X: MKFSTOOL
|
||||||
|
if "MKSPIFFSTOOL" in env:
|
||||||
|
self.tool = env.subst(env["MKSPIFFSTOOL"])
|
||||||
|
else:
|
||||||
|
self.tool = env.subst(env["MKFSTOOL"])
|
||||||
|
else:
|
||||||
|
self.tool = env["MKFSTOOL"] # from mkspiffs package
|
||||||
|
self.tool = join(platform.get_package_dir("tool-mklittlefs"), self.tool)
|
||||||
|
super().__init__(FSType.LITTLEFS, start, length, page_size, block_size)
|
||||||
|
def __repr__(self):
|
||||||
|
return f"FS type {self.fs_type} Start {hex(self.start)} Len {self.length} Page size {self.page_size} Block size {self.block_size} Tool: {self.tool}"
|
||||||
|
def get_extract_cmd(self, input_file, output_dir):
|
||||||
|
return [self.tool, "-b", str(self.block_size), "-p", str(self.page_size), "--unpack", output_dir, input_file]
|
||||||
|
|
||||||
|
|
||||||
|
class SPIFFSInfo(FSInfo):
|
||||||
|
def __init__(self, start, length, page_size, block_size):
|
||||||
|
if env["PIOPLATFORM"] == "espressif32":
|
||||||
|
#for ESP32: retrieve and evaluate, e.g. to mkspiffs_espressif32_arduino
|
||||||
|
#Espressif32 Framework 3.X.X: MKSPIFFSTOOL
|
||||||
|
#Espressif32 Framework 4.X.X and 5.X.X: MKFSTOOL
|
||||||
|
if "MKSPIFFSTOOL" in env:
|
||||||
|
self.tool = env.subst(env["MKSPIFFSTOOL"])
|
||||||
|
else:
|
||||||
|
self.tool = env.subst(env["MKFSTOOL"])
|
||||||
|
else:
|
||||||
|
self.tool = env["MKFSTOOL"] # from mkspiffs package
|
||||||
|
self.tool = join(platform.get_package_dir("tool-mkspiffs"), self.tool)
|
||||||
|
super().__init__(FSType.SPIFFS, start, length, page_size, block_size)
|
||||||
|
def __repr__(self):
|
||||||
|
return f"FS type {self.fs_type} Start {hex(self.start)} Len {self.length} Page size {self.page_size} Block size {self.block_size} Tool: {self.tool}"
|
||||||
|
def get_extract_cmd(self, input_file, output_dir):
|
||||||
|
return [self.tool, "-b", str(self.block_size), "-p", str(self.page_size), "--unpack", output_dir, input_file]
|
||||||
|
|
||||||
|
# SPIFFS helpers copied from ESP32, https://github.com/platformio/platform-espressif32/blob/develop/builder/main.py
|
||||||
|
# Copyright 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
def _parse_size(value):
|
||||||
|
if isinstance(value, int):
|
||||||
|
return value
|
||||||
|
elif value.isdigit():
|
||||||
|
return int(value)
|
||||||
|
elif value.startswith("0x"):
|
||||||
|
return int(value, 16)
|
||||||
|
elif value[-1].upper() in ("K", "M"):
|
||||||
|
base = 1024 if value[-1].upper() == "K" else 1024 * 1024
|
||||||
|
return int(value[:-1]) * base
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _parse_partitions(env):
|
||||||
|
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
|
||||||
|
if not isfile(partitions_csv):
|
||||||
|
sys.stderr.write("Could not find the file %s with partitions "
|
||||||
|
"table.\n" % partitions_csv)
|
||||||
|
env.Exit(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
result = []
|
||||||
|
next_offset = 0
|
||||||
|
with open(partitions_csv) as fp:
|
||||||
|
for line in fp.readlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
tokens = [t.strip() for t in line.split(",")]
|
||||||
|
if len(tokens) < 5:
|
||||||
|
continue
|
||||||
|
partition = {
|
||||||
|
"name": tokens[0],
|
||||||
|
"type": tokens[1],
|
||||||
|
"subtype": tokens[2],
|
||||||
|
"offset": tokens[3] or next_offset,
|
||||||
|
"size": tokens[4],
|
||||||
|
"flags": tokens[5] if len(tokens) > 5 else None
|
||||||
|
}
|
||||||
|
result.append(partition)
|
||||||
|
next_offset = (_parse_size(partition['offset']) +
|
||||||
|
_parse_size(partition['size']))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def esp32_fetch_spiffs_size(env):
|
||||||
|
spiffs = None
|
||||||
|
for p in _parse_partitions(env):
|
||||||
|
if p['type'] == "data" and p['subtype'] == "spiffs":
|
||||||
|
spiffs = p
|
||||||
|
if not spiffs:
|
||||||
|
sys.stderr.write(
|
||||||
|
env.subst("Could not find the `spiffs` section in the partitions "
|
||||||
|
"table $PARTITIONS_TABLE_CSV\n"))
|
||||||
|
env.Exit(1)
|
||||||
|
return
|
||||||
|
env["SPIFFS_START"] = _parse_size(spiffs['offset'])
|
||||||
|
env["SPIFFS_SIZE"] = _parse_size(spiffs['size'])
|
||||||
|
env["SPIFFS_PAGE"] = int("0x100", 16)
|
||||||
|
env["SPIFFS_BLOCK"] = int("0x1000", 16)
|
||||||
|
|
||||||
|
## FS helpers for ESP8266
|
||||||
|
# copied from https://github.com/platformio/platform-espressif8266/blob/develop/builder/main.py
|
||||||
|
# Copyright 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
def _get_board_f_flash(env):
|
||||||
|
frequency = env.subst("$BOARD_F_FLASH")
|
||||||
|
frequency = str(frequency).replace("L", "")
|
||||||
|
return int(int(frequency) / 1000000)
|
||||||
|
|
||||||
|
def _parse_ld_sizes(ldscript_path):
|
||||||
|
assert ldscript_path
|
||||||
|
result = {}
|
||||||
|
# get flash size from board's manifest
|
||||||
|
result['flash_size'] = int(env.BoardConfig().get("upload.maximum_size", 0))
|
||||||
|
# get flash size from LD script path
|
||||||
|
match = re.search(r"\.flash\.(\d+[mk]).*\.ld", ldscript_path)
|
||||||
|
if match:
|
||||||
|
result['flash_size'] = _parse_size(match.group(1))
|
||||||
|
|
||||||
|
appsize_re = re.compile(
|
||||||
|
r"irom0_0_seg\s*:.+len\s*=\s*(0x[\da-f]+)", flags=re.I)
|
||||||
|
filesystem_re = re.compile(
|
||||||
|
r"PROVIDE\s*\(\s*_%s_(\w+)\s*=\s*(0x[\da-f]+)\s*\)" % "FS"
|
||||||
|
if "arduino" in env.subst("$PIOFRAMEWORK")
|
||||||
|
else "SPIFFS",
|
||||||
|
flags=re.I,
|
||||||
|
)
|
||||||
|
with open(ldscript_path) as fp:
|
||||||
|
for line in fp.readlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("/*"):
|
||||||
|
continue
|
||||||
|
match = appsize_re.search(line)
|
||||||
|
if match:
|
||||||
|
result['app_size'] = _parse_size(match.group(1))
|
||||||
|
continue
|
||||||
|
match = filesystem_re.search(line)
|
||||||
|
if match:
|
||||||
|
result['fs_%s' % match.group(1)] = _parse_size(
|
||||||
|
match.group(2))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_flash_size(env):
|
||||||
|
ldsizes = _parse_ld_sizes(env.GetActualLDScript())
|
||||||
|
if ldsizes['flash_size'] < 1048576:
|
||||||
|
return "%dK" % (ldsizes['flash_size'] / 1024)
|
||||||
|
return "%dM" % (ldsizes['flash_size'] / 1048576)
|
||||||
|
|
||||||
|
def esp8266_fetch_fs_size(env):
|
||||||
|
ldsizes = _parse_ld_sizes(env.GetActualLDScript())
|
||||||
|
for key in ldsizes:
|
||||||
|
if key.startswith("fs_"):
|
||||||
|
env[key.upper()] = ldsizes[key]
|
||||||
|
|
||||||
|
assert all([
|
||||||
|
k in env
|
||||||
|
for k in ["FS_START", "FS_END", "FS_PAGE", "FS_BLOCK"]
|
||||||
|
])
|
||||||
|
|
||||||
|
# esptool flash starts from 0
|
||||||
|
for k in ("FS_START", "FS_END"):
|
||||||
|
_value = 0
|
||||||
|
if env[k] < 0x40300000:
|
||||||
|
_value = env[k] & 0xFFFFF
|
||||||
|
elif env[k] < 0x411FB000:
|
||||||
|
_value = env[k] & 0xFFFFFF
|
||||||
|
_value -= 0x200000 # correction
|
||||||
|
else:
|
||||||
|
_value = env[k] & 0xFFFFFF
|
||||||
|
_value += 0xE00000 # correction
|
||||||
|
|
||||||
|
env[k] = _value
|
||||||
|
|
||||||
|
def esp8266_get_esptoolpy_reset_flags(resetmethod):
|
||||||
|
# no dtr, no_sync
|
||||||
|
resets = ("no_reset_no_sync", "soft_reset")
|
||||||
|
if resetmethod == "nodemcu":
|
||||||
|
# dtr
|
||||||
|
resets = ("default_reset", "hard_reset")
|
||||||
|
elif resetmethod == "ck":
|
||||||
|
# no dtr
|
||||||
|
resets = ("no_reset", "soft_reset")
|
||||||
|
|
||||||
|
return ["--before", resets[0], "--after", resets[1]]
|
||||||
|
|
||||||
|
## Script interface functions
|
||||||
|
|
||||||
|
def get_fs_type_start_and_length():
|
||||||
|
platform = env["PIOPLATFORM"]
|
||||||
|
if platform == "espressif32":
|
||||||
|
print("Retrieving filesystem info for ESP32. Assuming SPIFFS.")
|
||||||
|
print("Partition file: " + str(env.subst("$PARTITIONS_TABLE_CSV")))
|
||||||
|
esp32_fetch_spiffs_size(env)
|
||||||
|
return SPIFFSInfo(env["SPIFFS_START"], env["SPIFFS_SIZE"], env["SPIFFS_PAGE"], env["SPIFFS_BLOCK"])
|
||||||
|
elif platform == "espressif8266":
|
||||||
|
print("Retrieving filesystem info for ESP8266.")
|
||||||
|
filesystem = board.get("build.filesystem", "spiffs")
|
||||||
|
if filesystem not in ("spiffs", "littlefs"):
|
||||||
|
print("Unrecognized board_build.filesystem option '" + str(filesystem) + "'.")
|
||||||
|
env.Exit(1)
|
||||||
|
# fetching sizes is the same for all filesystems
|
||||||
|
esp8266_fetch_fs_size(env)
|
||||||
|
print("FS_START: " + hex(env["FS_START"]))
|
||||||
|
print("FS_END: " + hex(env["FS_END"]))
|
||||||
|
print("FS_PAGE: " + hex(env["FS_PAGE"]))
|
||||||
|
print("FS_BLOCK: " + hex(env["FS_BLOCK"]))
|
||||||
|
if filesystem == "spiffs":
|
||||||
|
print("Recognized SPIFFS filesystem.")
|
||||||
|
return SPIFFSInfo(env["FS_START"], env["FS_END"] - env["FS_START"], env["FS_PAGE"], env["FS_BLOCK"])
|
||||||
|
elif filesystem == "littlefs":
|
||||||
|
print("Recognized LittleFS filesystem.")
|
||||||
|
return LittleFSInfo(env["FS_START"], env["FS_END"] - env["FS_START"], env["FS_PAGE"], env["FS_BLOCK"])
|
||||||
|
else:
|
||||||
|
print("Unrecongized configuration.")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def download_fs(fs_info: FSInfo):
|
||||||
|
esptoolpy = join(platform.get_package_dir("tool-esptoolpy") or "", "esptool.py")
|
||||||
|
fs_file = join(env["PROJECT_DIR"], f"downloaded_fs_{hex(fs_info.start)}_{hex(fs_info.length)}.bin")
|
||||||
|
esptoolpy_cmd = [
|
||||||
|
env["PYTHONEXE"],
|
||||||
|
esptoolpy,
|
||||||
|
"--chip", mcu,
|
||||||
|
"--port", env.subst("$UPLOAD_PORT"),
|
||||||
|
"--baud", env.subst("$UPLOAD_SPEED"),
|
||||||
|
"--before", "default_reset",
|
||||||
|
"--after", "hard_reset",
|
||||||
|
"read_flash",
|
||||||
|
hex(fs_info.start),
|
||||||
|
hex(fs_info.length),
|
||||||
|
fs_file
|
||||||
|
]
|
||||||
|
print("Executing flash download command.")
|
||||||
|
print(shlex.join(esptoolpy_cmd))
|
||||||
|
try:
|
||||||
|
subprocess.call(esptoolpy_cmd)
|
||||||
|
print("Downloaded filesystem binary.")
|
||||||
|
return (True, fs_file)
|
||||||
|
except subprocess.CalledProcessError as exc:
|
||||||
|
print("Downloading failed with " + str(exc))
|
||||||
|
return (False, "")
|
||||||
|
|
||||||
|
def unpack_fs(fs_info: FSInfo, downloaded_file: str):
|
||||||
|
# by writing custom_unpack_dir = some_dir in the platformio.ini, one can
|
||||||
|
# control the unpack directory
|
||||||
|
unpack_dir = env.GetProjectOption("custom_unpack_dir", "unpacked_fs")
|
||||||
|
#unpack_dir = "unpacked_fs"
|
||||||
|
try:
|
||||||
|
if os.path.exists(unpack_dir):
|
||||||
|
shutil.rmtree(unpack_dir)
|
||||||
|
except Exception as exc:
|
||||||
|
print("Exception while attempting to remove the folder '" + str(unpack_dir) + "': " + str(exc))
|
||||||
|
if not os.path.exists(unpack_dir):
|
||||||
|
os.makedirs(unpack_dir)
|
||||||
|
|
||||||
|
cmd = fs_info.get_extract_cmd(downloaded_file, unpack_dir)
|
||||||
|
print("Executing extraction command:", shlex.join(cmd))
|
||||||
|
try:
|
||||||
|
subprocess.call(cmd)
|
||||||
|
print("Unpacked filesystem.")
|
||||||
|
return (True, unpack_dir)
|
||||||
|
except subprocess.CalledProcessError as exc:
|
||||||
|
print("Unpacking filesystem failed with " + str(exc))
|
||||||
|
return (False, "")
|
||||||
|
|
||||||
|
def display_fs(extracted_dir):
|
||||||
|
# extract command already nicely lists all extracted files.
|
||||||
|
# no need to display that ourselves. just display a summary
|
||||||
|
file_count = sum([len(files) for r, d, files in os.walk(extracted_dir)])
|
||||||
|
print("Extracted " + str(file_count) + " file(s) from filesystem.")
|
||||||
|
|
||||||
|
def command_download_fs(*args, **kwargs):
|
||||||
|
print("Entrypoint")
|
||||||
|
#print(env.Dump())
|
||||||
|
info = get_fs_type_start_and_length()
|
||||||
|
print("Parsed FS info: " + str(info))
|
||||||
|
download_ok, downloaded_file = download_fs(info)
|
||||||
|
print("Download was okay: " + str(download_ok) + ". File at: "+ str(downloaded_file))
|
||||||
|
unpack_ok, unpacked_dir = unpack_fs(info, downloaded_file)
|
||||||
|
if unpack_ok is True:
|
||||||
|
display_fs(unpacked_dir)
|
||||||
|
|
||||||
|
env.AddCustomTarget(
|
||||||
|
name="downloadfs",
|
||||||
|
dependencies=None,
|
||||||
|
actions=[
|
||||||
|
command_download_fs
|
||||||
|
],
|
||||||
|
title="Download Filesystem",
|
||||||
|
description="Downloads and displays files stored in the target ESP32/ESP8266"
|
||||||
|
)
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
76
src/main.cc
76
src/main.cc
|
@ -1,18 +1,78 @@
|
||||||
#include <Arduino.h>
|
#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() {
|
void setup() {
|
||||||
// put your setup code here, to run once:
|
Serial.begin(115200);
|
||||||
int result = myFunction(2, 3);
|
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() {
|
void loop() {
|
||||||
// put your main code here, to run repeatedly:
|
// put your main code here, to run repeatedly:
|
||||||
}
|
}
|
||||||
|
|
||||||
// put function definitions here:
|
|
||||||
int myFunction(int x, int y) {
|
|
||||||
return x + y;
|
|
||||||
}
|
|
Loading…
Reference in New Issue