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
 | 
			
		||||
include/homenet.h
 | 
			
		||||
.vscode/.browse.c_cpp.db*
 | 
			
		||||
.vscode/c_cpp_properties.json
 | 
			
		||||
.vscode/launch.json
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										5
									
								
								Makefile
								
								
								
								
							| 
						 | 
				
			
			@ -26,6 +26,7 @@ deploy: $(FIRMWARE)
 | 
			
		|||
.PHONY: clean
 | 
			
		||||
clean:
 | 
			
		||||
	$(PIO) -t clean
 | 
			
		||||
	rm -rf *.bin unpacked_fs
 | 
			
		||||
 | 
			
		||||
.PHONY: cloc
 | 
			
		||||
cloc:
 | 
			
		||||
| 
						 | 
				
			
			@ -35,3 +36,7 @@ cloc:
 | 
			
		|||
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
 | 
			
		||||
; https://docs.platformio.org/page/projectconf.html
 | 
			
		||||
 | 
			
		||||
[platformio]
 | 
			
		||||
 | 
			
		||||
[env:sparkfun_esp32micromod]
 | 
			
		||||
platform = espressif32
 | 
			
		||||
board = sparkfun_esp32micromod
 | 
			
		||||
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 <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;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue