Init
This commit is contained in:
31
.envrc
Normal file
31
.envrc
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# disable nix builders because they are slow for copy operations
|
||||||
|
export NIX_BUILDERS=""
|
||||||
|
|
||||||
|
# Automatically load the Nix development environment when entering this directory
|
||||||
|
use flake
|
||||||
|
|
||||||
|
# Export ESP-IDF related environment variables
|
||||||
|
export IDF_TOOLS_PATH="$HOME/.espressif"
|
||||||
|
export CCACHE_DIR="$HOME/.ccache"
|
||||||
|
|
||||||
|
# Prevent Python bytecode generation
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
# Enable colored output for various tools
|
||||||
|
export FORCE_COLOR=1
|
||||||
|
|
||||||
|
# Set common ESP32 development variables
|
||||||
|
export ESP_TARGET=esp32
|
||||||
|
export ESP_BOARD=esp32dev
|
||||||
|
|
||||||
|
# Set locale to avoid issues
|
||||||
|
export LANG=C.UTF-8
|
||||||
|
export LC_ALL=C.UTF-8
|
||||||
|
|
||||||
|
# Create necessary directories
|
||||||
|
mkdir -p "$IDF_TOOLS_PATH"
|
||||||
|
mkdir -p "$CCACHE_DIR"
|
||||||
|
|
||||||
|
# Show a message when the environment is loaded
|
||||||
|
echo "🚀 ESP32 Tang Server environment loaded!"
|
||||||
|
echo "ESP-IDF ready - run 'make help' for available commands"
|
||||||
60
.gitignore
vendored
Normal file
60
.gitignore
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Linker files
|
||||||
|
*.ilk
|
||||||
|
|
||||||
|
# Debugger Files
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# debug information files
|
||||||
|
*.dwo
|
||||||
|
|
||||||
|
# direnv
|
||||||
|
.direnv
|
||||||
|
|
||||||
|
# gitignore template for esp-idf, the official development framework for ESP32
|
||||||
|
# https://github.com/espressif/esp-idf
|
||||||
|
build/
|
||||||
|
sdkconfig
|
||||||
|
sdkconfig.old
|
||||||
|
|
||||||
|
managed_components/
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
# Cryptographic files
|
||||||
|
*.jwk
|
||||||
|
*.jwe
|
||||||
|
|
||||||
|
# Other Binary files
|
||||||
|
*.pdf
|
||||||
17
.vscode/c_cpp_properties.json
vendored
Normal file
17
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "NixOS",
|
||||||
|
"compileCommands": [
|
||||||
|
"${workspaceFolder}/build/compile_commands.json"
|
||||||
|
],
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**",
|
||||||
|
"${workspaceFolder}/managed_components/**",
|
||||||
|
"${workspaceFolder}/managed_components/esp-cryptoauthlib/**"
|
||||||
|
],
|
||||||
|
"compilerPath": "/nix/store/x45d95acx6rybkb5cidrxxffval9k2pg-gcc-wrapper-14.3.0/bin/gcc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cmake.automaticReconfigure": true,
|
||||||
|
"python-envs.defaultEnvManager": "ms-python.python:system",
|
||||||
|
"python-envs.pythonProjects": []
|
||||||
|
}
|
||||||
11
CMakeLists.txt
Normal file
11
CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Top-level CMakeLists.txt for ESP32 Tang Server
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
# Set the project name
|
||||||
|
set(PROJECT_NAME "esp32-tang")
|
||||||
|
|
||||||
|
# Include ESP-IDF build system
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
|
||||||
|
# Define the project
|
||||||
|
project(${PROJECT_NAME})
|
||||||
183
Makefile
Normal file
183
Makefile
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
# ESP32 Tang Server Makefile
|
||||||
|
# Convenient shortcuts for ESP-IDF development tasks
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
# Configuration variables
|
||||||
|
PORT ?= /dev/ttyUSB0
|
||||||
|
BAUD ?= 115200
|
||||||
|
TARGET ?= esp32
|
||||||
|
|
||||||
|
# Help target
|
||||||
|
.PHONY: help
|
||||||
|
help:
|
||||||
|
@echo "ESP32 Tang Server Development Commands"
|
||||||
|
@echo "====================================="
|
||||||
|
@echo ""
|
||||||
|
@echo "Setup:"
|
||||||
|
@echo " setup-target Set ESP32 as build target"
|
||||||
|
@echo " menuconfig Open configuration menu"
|
||||||
|
@echo ""
|
||||||
|
@echo "Build:"
|
||||||
|
@echo " build Build the project"
|
||||||
|
@echo " clean Clean build files"
|
||||||
|
@echo " fullclean Full clean (including config)"
|
||||||
|
@echo ""
|
||||||
|
@echo "Flash & Monitor:"
|
||||||
|
@echo " flash Flash to device"
|
||||||
|
@echo " monitor Open serial monitor"
|
||||||
|
@echo " flash-monitor Flash and immediately monitor"
|
||||||
|
@echo ""
|
||||||
|
@echo "Development:"
|
||||||
|
@echo " size Show binary size analysis"
|
||||||
|
@echo " erase Erase flash completely"
|
||||||
|
@echo " bootloader Flash bootloader only"
|
||||||
|
@echo ""
|
||||||
|
@echo "Board Management:"
|
||||||
|
@echo " list-boards List connected boards"
|
||||||
|
@echo " detect-port Detect serial ports"
|
||||||
|
@echo " board-info Show board information"
|
||||||
|
@echo ""
|
||||||
|
@echo "Environment variables:"
|
||||||
|
@echo " PORT=/dev/ttyUSB0 (default serial port)"
|
||||||
|
@echo " BAUD=115200 (default baud rate)"
|
||||||
|
@echo " TARGET=esp32 (default target)"
|
||||||
|
|
||||||
|
# Setup commands
|
||||||
|
.PHONY: setup-target
|
||||||
|
setup-target:
|
||||||
|
idf.py set-target $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: menuconfig
|
||||||
|
menuconfig:
|
||||||
|
idf.py menuconfig
|
||||||
|
|
||||||
|
# Build commands
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
idf.py build
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
idf.py clean
|
||||||
|
|
||||||
|
.PHONY: fullclean
|
||||||
|
fullclean:
|
||||||
|
idf.py fullclean
|
||||||
|
|
||||||
|
# Flash and monitor commands
|
||||||
|
.PHONY: flash
|
||||||
|
flash:
|
||||||
|
idf.py -p $(PORT) -b $(BAUD) flash
|
||||||
|
|
||||||
|
.PHONY: monitor
|
||||||
|
monitor:
|
||||||
|
idf.py -p $(PORT) -b $(BAUD) monitor
|
||||||
|
|
||||||
|
.PHONY: flash-monitor
|
||||||
|
flash-monitor:
|
||||||
|
idf.py -p $(PORT) -b $(BAUD) flash monitor
|
||||||
|
|
||||||
|
# Development utilities
|
||||||
|
.PHONY: size
|
||||||
|
size:
|
||||||
|
idf.py size
|
||||||
|
|
||||||
|
.PHONY: size-components
|
||||||
|
size-components:
|
||||||
|
idf.py size-components
|
||||||
|
|
||||||
|
.PHONY: size-files
|
||||||
|
size-files:
|
||||||
|
idf.py size-files
|
||||||
|
|
||||||
|
.PHONY: erase
|
||||||
|
erase:
|
||||||
|
idf.py -p $(PORT) erase-flash
|
||||||
|
|
||||||
|
.PHONY: bootloader
|
||||||
|
bootloader:
|
||||||
|
idf.py -p $(PORT) bootloader-flash
|
||||||
|
|
||||||
|
# Show partition table
|
||||||
|
.PHONY: partition-table
|
||||||
|
partition-table:
|
||||||
|
idf.py partition-table
|
||||||
|
|
||||||
|
# Generate compilation database for IDE support
|
||||||
|
.PHONY: compile-commands
|
||||||
|
compile-commands:
|
||||||
|
idf.py build --cmake-args="-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||||
|
|
||||||
|
# Development shortcuts
|
||||||
|
.PHONY: dev
|
||||||
|
dev: build flash-monitor
|
||||||
|
|
||||||
|
.PHONY: quick
|
||||||
|
quick: build flash
|
||||||
|
|
||||||
|
# Board management
|
||||||
|
.PHONY: list-boards
|
||||||
|
list-boards:
|
||||||
|
idf.py -p $(PORT) board_info || echo "Connect board to $(PORT)"
|
||||||
|
|
||||||
|
.PHONY: detect-port
|
||||||
|
detect-port:
|
||||||
|
@echo "Scanning for ESP32 devices..."
|
||||||
|
@if ls /dev/ttyUSB* >/dev/null 2>&1; then \
|
||||||
|
echo "Found USB serial devices:"; \
|
||||||
|
ls -la /dev/ttyUSB* | head -5; \
|
||||||
|
elif ls /dev/ttyACM* >/dev/null 2>&1; then \
|
||||||
|
echo "Found ACM serial devices:"; \
|
||||||
|
ls -la /dev/ttyACM* | head -5; \
|
||||||
|
else \
|
||||||
|
echo "No serial devices found. Make sure ESP32 is connected."; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
.PHONY: board-info
|
||||||
|
board-info:
|
||||||
|
@echo "Board Information:"
|
||||||
|
@echo "=================="
|
||||||
|
@echo "Target: $(TARGET)"
|
||||||
|
@echo "Port: $(PORT)"
|
||||||
|
@echo "Baud: $(BAUD)"
|
||||||
|
@echo ""
|
||||||
|
@echo "ESP-IDF Version:"
|
||||||
|
@idf.py --version
|
||||||
|
@echo ""
|
||||||
|
@echo "Project status:"
|
||||||
|
@idf.py show-port-info -p $(PORT) 2>/dev/null || echo "No device connected to $(PORT)"
|
||||||
|
|
||||||
|
# Show ESP-IDF version and tools
|
||||||
|
.PHONY: version
|
||||||
|
version:
|
||||||
|
@echo "ESP-IDF Version Information:"
|
||||||
|
@idf.py --version
|
||||||
|
@echo ""
|
||||||
|
@echo "Toolchain versions:"
|
||||||
|
@xtensa-esp32-elf-gcc --version | head -1 || echo "Toolchain not found"
|
||||||
|
@python3 --version
|
||||||
|
@cmake --version | head -1
|
||||||
|
|
||||||
|
# Validate project structure
|
||||||
|
.PHONY: validate
|
||||||
|
validate:
|
||||||
|
@echo "Validating ESP-IDF project structure..."
|
||||||
|
@if [ ! -f "CMakeLists.txt" ]; then \
|
||||||
|
echo "Error: CMakeLists.txt not found"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@if [ ! -d "main" ]; then \
|
||||||
|
echo "Error: main directory not found"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "Project validation passed!"
|
||||||
|
|
||||||
|
# Full development cycle
|
||||||
|
.PHONY: full-setup
|
||||||
|
full-setup: setup-target menuconfig build
|
||||||
|
@echo ""
|
||||||
|
@echo "Full setup complete! You can now:"
|
||||||
|
@echo " make flash # Flash to device"
|
||||||
|
@echo " make monitor # View serial output"
|
||||||
|
@echo " make dev # Flash and monitor"
|
||||||
29
README.md
Normal file
29
README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# KeyPi Client using SSH and ATECC608B
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
Set the target:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
idf.py set-target
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `menuconfig` to configure the KeyPi (choose `KeyPi` from the list):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
To build, flash and monitor:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
idf.py build flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Once the ESP32 connects to the wifi: user LED will light up.
|
||||||
|
|
||||||
|
Press button once: LED starts blinking slowly -> Sends open command after 5s.
|
||||||
|
Press button twice: LED starts blinking fast -> Sends close command after 5s.
|
||||||
|
Press button again: cancels the action.
|
||||||
30
dependencies.lock
Normal file
30
dependencies.lock
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
dependencies:
|
||||||
|
esp-cryptoauthlib:
|
||||||
|
component_hash: 8d24c37df1e906f9bbee9a869ca86f263ab135179d1c2ba6062448269437b192
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
version: '>=4.3'
|
||||||
|
source:
|
||||||
|
git: https://github.com/espressif/esp-cryptoauthlib.git
|
||||||
|
path: .
|
||||||
|
type: git
|
||||||
|
version: d9792119ebaec0c54839e6605acd3f11dd937205
|
||||||
|
idf:
|
||||||
|
source:
|
||||||
|
type: idf
|
||||||
|
version: 5.5.2
|
||||||
|
libssh2_esp:
|
||||||
|
component_hash: 75612f8fe15b7793de2d9d2eba920e66a7aab7424963012282a419cdb86399ad
|
||||||
|
dependencies: []
|
||||||
|
source:
|
||||||
|
git: https://github.com/skuodi/libssh2_esp.git
|
||||||
|
path: .
|
||||||
|
type: git
|
||||||
|
version: 378f0bd47900bffacbf29cac328c6e9b5391c886
|
||||||
|
direct_dependencies:
|
||||||
|
- esp-cryptoauthlib
|
||||||
|
- idf
|
||||||
|
- libssh2_esp
|
||||||
|
manifest_hash: a6766e71931c845fac37dab1b735cded43d414aa83e5ce0443ba4285e1980180
|
||||||
|
target: esp32c6
|
||||||
|
version: 2.0.0
|
||||||
117
flake.lock
generated
Normal file
117
flake.lock
generated
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1771419570,
|
||||||
|
"narHash": "sha256-bxAlQgre3pcQcaRUm/8A0v/X8d2nhfraWSFqVmMcBcU=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6d41bc27aaf7b6a3ba6b169db3bd5d6159cfaa47",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-25.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-esp-dev": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1767865407,
|
||||||
|
"narHash": "sha256-QWF1rZYd+HvNzLIeRS+OEBX7HF0EhWCGeLbMkgtbsIo=",
|
||||||
|
"owner": "mirrexagon",
|
||||||
|
"repo": "nixpkgs-esp-dev",
|
||||||
|
"rev": "5287d6e1ca9e15ebd5113c41b9590c468e1e001b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "mirrexagon",
|
||||||
|
"repo": "nixpkgs-esp-dev",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-esp-dev",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-esp-dev": "nixpkgs-esp-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
189
flake.nix
Normal file
189
flake.nix
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
{
|
||||||
|
description = "ESP32 Tang Server Development Environment";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
nixpkgs-esp-dev = {
|
||||||
|
url = "github:mirrexagon/nixpkgs-esp-dev";
|
||||||
|
};
|
||||||
|
nixpkgs.follows = "nixpkgs-esp-dev/nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
flake-utils,
|
||||||
|
nixpkgs-esp-dev,
|
||||||
|
}:
|
||||||
|
flake-utils.lib.eachDefaultSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
config = {
|
||||||
|
permittedInsecurePackages = [
|
||||||
|
"python3.13-ecdsa-0.19.1"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
overlays = [ nixpkgs-esp-dev.overlays.default ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
name = "esp32-tang-dev";
|
||||||
|
|
||||||
|
buildInputs =
|
||||||
|
with pkgs;
|
||||||
|
[
|
||||||
|
python3Packages.pandas
|
||||||
|
|
||||||
|
# ESP-IDF with full toolchain
|
||||||
|
esp-idf-full
|
||||||
|
jose
|
||||||
|
clevis
|
||||||
|
|
||||||
|
# Development tools
|
||||||
|
gnumake
|
||||||
|
cmake
|
||||||
|
ninja
|
||||||
|
ccache
|
||||||
|
|
||||||
|
# Serial communication tools
|
||||||
|
picocom
|
||||||
|
screen
|
||||||
|
minicom
|
||||||
|
|
||||||
|
# Development utilities
|
||||||
|
curl
|
||||||
|
wget
|
||||||
|
unzip
|
||||||
|
file
|
||||||
|
which
|
||||||
|
jq
|
||||||
|
|
||||||
|
# Text processing tools
|
||||||
|
gawk
|
||||||
|
gnused
|
||||||
|
gnugrep
|
||||||
|
|
||||||
|
# Optional development tools
|
||||||
|
clang-tools
|
||||||
|
bear
|
||||||
|
]
|
||||||
|
++ lib.optionals stdenv.isLinux [
|
||||||
|
# Linux-specific packages for USB device access
|
||||||
|
udev
|
||||||
|
libusb1
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "🚀 ESP32 Tang Server Development Environment"
|
||||||
|
echo "============================================="
|
||||||
|
echo
|
||||||
|
echo "ESP-IDF: $(idf.py --version 2>/dev/null || echo 'Ready')"
|
||||||
|
echo "Python: $(python3 --version)"
|
||||||
|
echo "CMake: $(cmake --version | head -1)"
|
||||||
|
echo
|
||||||
|
echo "Available commands:"
|
||||||
|
echo " 📦 idf.py build - Build the project"
|
||||||
|
echo " 📡 idf.py flash - Flash to device"
|
||||||
|
echo " 💻 idf.py monitor - Serial monitor"
|
||||||
|
echo " 🔧 idf.py flash monitor - Flash and monitor"
|
||||||
|
echo " ⚙️ idf.py menuconfig - Configuration menu"
|
||||||
|
echo " 🎯 idf.py set-target - Set target (esp32)"
|
||||||
|
echo
|
||||||
|
echo "Or use the Makefile shortcuts:"
|
||||||
|
echo " make setup-target - Set ESP32 target"
|
||||||
|
echo " make build - Build project"
|
||||||
|
echo " make flash-monitor - Flash and monitor"
|
||||||
|
echo " make menuconfig - Configuration"
|
||||||
|
echo
|
||||||
|
echo "Quick start:"
|
||||||
|
echo " 1. Set target: make setup-target"
|
||||||
|
echo " 2. Configure: make menuconfig"
|
||||||
|
echo " 3. Build: make build"
|
||||||
|
echo " 4. Flash & Monitor: make flash-monitor PORT=/dev/ttyUSB0"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Set up development environment
|
||||||
|
export IDF_TOOLS_PATH="$HOME/.espressif"
|
||||||
|
export CCACHE_DIR="$HOME/.ccache"
|
||||||
|
|
||||||
|
# Create necessary directories
|
||||||
|
mkdir -p "$IDF_TOOLS_PATH"
|
||||||
|
mkdir -p "$CCACHE_DIR"
|
||||||
|
|
||||||
|
# Check for serial devices
|
||||||
|
if ls /dev/ttyUSB* >/dev/null 2>&1; then
|
||||||
|
echo "📡 Found USB serial devices:"
|
||||||
|
ls -la /dev/ttyUSB* 2>/dev/null
|
||||||
|
elif ls /dev/ttyACM* >/dev/null 2>&1; then
|
||||||
|
echo "📡 Found ACM serial devices:"
|
||||||
|
ls -la /dev/ttyACM* 2>/dev/null
|
||||||
|
else
|
||||||
|
echo "📡 No serial devices found. Connect your ESP32 board."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check serial permissions
|
||||||
|
if ! groups | grep -q dialout 2>/dev/null && ! groups | grep -q uucp 2>/dev/null; then
|
||||||
|
echo
|
||||||
|
echo "⚠️ Note: You may need to add your user to the 'dialout' group"
|
||||||
|
echo " to access serial devices:"
|
||||||
|
echo " sudo usermod -a -G dialout $USER"
|
||||||
|
echo " Then log out and log back in."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Ready for ESP32 development with Arduino support! 🎯"
|
||||||
|
echo
|
||||||
|
'';
|
||||||
|
|
||||||
|
# allow /dev/ access
|
||||||
|
extraDevPaths = [
|
||||||
|
"/dev/ttyUSB*"
|
||||||
|
"/dev/ttyACM*"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
IDF_TOOLS_PATH = "$HOME/.espressif";
|
||||||
|
CCACHE_DIR = "$HOME/.ccache";
|
||||||
|
|
||||||
|
# Prevent Python from creating __pycache__ directories
|
||||||
|
PYTHONDONTWRITEBYTECODE = "1";
|
||||||
|
|
||||||
|
# Enable colored output
|
||||||
|
FORCE_COLOR = "1";
|
||||||
|
|
||||||
|
# Set locale to avoid issues
|
||||||
|
LANG = "C.UTF-8";
|
||||||
|
LC_ALL = "C.UTF-8";
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells.minimal = pkgs.mkShell {
|
||||||
|
name = "esp32-tang-minimal";
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
esp-idf-full
|
||||||
|
picocom
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "⚡ ESP32 Tang Server (Minimal Environment)"
|
||||||
|
echo "========================================"
|
||||||
|
echo "Ready for ESP32 development!"
|
||||||
|
echo
|
||||||
|
|
||||||
|
export IDF_TOOLS_PATH="$HOME/.espressif"
|
||||||
|
export CCACHE_DIR="$HOME/.ccache"
|
||||||
|
mkdir -p "$CCACHE_DIR"
|
||||||
|
'';
|
||||||
|
|
||||||
|
extraDevPaths = [
|
||||||
|
"/dev/ttyUSB*"
|
||||||
|
"/dev/ttyACM*"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
6
main/CMakeLists.txt
Normal file
6
main/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
idf_component_register(SRCS "main.c"
|
||||||
|
"wifi.c"
|
||||||
|
"atecc608a.c"
|
||||||
|
"ssh_client.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
REQUIRES mbedtls esp-cryptoauthlib esp_wifi nvs_flash driver)
|
||||||
45
main/Kconfig.projbuild
Normal file
45
main/Kconfig.projbuild
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
menu "KeyPi"
|
||||||
|
|
||||||
|
config WIFI_SSID
|
||||||
|
string "Wi-Fi SSID"
|
||||||
|
default ""
|
||||||
|
help
|
||||||
|
SSID of the Wi-Fi network to connect to.
|
||||||
|
|
||||||
|
config WIFI_PASSWORD
|
||||||
|
string "Wi-Fi Password"
|
||||||
|
default ""
|
||||||
|
help
|
||||||
|
Password for the Wi-Fi network.
|
||||||
|
|
||||||
|
config SSH_HOSTNAME
|
||||||
|
string "SSH Server Hostname or IP"
|
||||||
|
default "192.168.1.1"
|
||||||
|
help
|
||||||
|
Hostname or IP address of the SSH server (door controller).
|
||||||
|
|
||||||
|
config SSH_PORT
|
||||||
|
int "SSH Server Port"
|
||||||
|
default 22
|
||||||
|
help
|
||||||
|
TCP port of the SSH server.
|
||||||
|
|
||||||
|
config SSH_USERNAME
|
||||||
|
string "SSH Username"
|
||||||
|
default "pi"
|
||||||
|
help
|
||||||
|
Username to authenticate with on the SSH server.
|
||||||
|
|
||||||
|
config SSH_OPEN_COMMAND
|
||||||
|
string "SSH Open Command"
|
||||||
|
default "door open"
|
||||||
|
help
|
||||||
|
Shell command to execute on the SSH server to open the door.
|
||||||
|
|
||||||
|
config SSH_LOCK_COMMAND
|
||||||
|
string "SSH Lock Command"
|
||||||
|
default "door lock"
|
||||||
|
help
|
||||||
|
Shell command to execute on the SSH server to lock the door.
|
||||||
|
|
||||||
|
endmenu
|
||||||
103
main/atecc608a.c
Normal file
103
main/atecc608a.c
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#include "atecc608a.h"
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static const char *TAG = "atecc608b";
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Module-private state
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static ATCAIfaceCfg s_atecc_cfg;
|
||||||
|
static uint8_t s_config_data[128];
|
||||||
|
static bool s_config_valid = false;
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Internal helpers
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static bool read_config_zone(void)
|
||||||
|
{
|
||||||
|
ATCA_STATUS status = atcab_read_config_zone(s_config_data);
|
||||||
|
|
||||||
|
if (status != ATCA_SUCCESS) {
|
||||||
|
ESP_LOGI(TAG, "Failed to read config zone (0x%02X) – check wiring", status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_config_valid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Public API
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
bool atecc608B_init(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Initialising ATECC608B...");
|
||||||
|
|
||||||
|
s_atecc_cfg.iface_type = ATCA_I2C_IFACE;
|
||||||
|
s_atecc_cfg.devtype = ATECC608B;
|
||||||
|
s_atecc_cfg.atcai2c.address = CONFIG_ATCA_I2C_ADDRESS;
|
||||||
|
s_atecc_cfg.atcai2c.bus = 0;
|
||||||
|
s_atecc_cfg.atcai2c.baud = CONFIG_ATCA_I2C_BAUD_RATE;
|
||||||
|
s_atecc_cfg.wake_delay = 1500;
|
||||||
|
s_atecc_cfg.rx_retries = 20;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "SDA=GPIO%d SCL=GPIO%d addr=0x%02X (7-bit 0x%02X)",
|
||||||
|
CONFIG_ATCA_I2C_SDA_PIN, CONFIG_ATCA_I2C_SCL_PIN,
|
||||||
|
CONFIG_ATCA_I2C_ADDRESS, CONFIG_ATCA_I2C_ADDRESS >> 1);
|
||||||
|
|
||||||
|
ATCA_STATUS status = atcab_init(&s_atecc_cfg);
|
||||||
|
if (status != ATCA_SUCCESS) {
|
||||||
|
ESP_LOGE(TAG, "atcab_init failed: 0x%02X", status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
|
||||||
|
status = atcab_wakeup();
|
||||||
|
if (status != ATCA_SUCCESS) {
|
||||||
|
ESP_LOGW(TAG, "Wake returned 0x%02X (may be normal)", status);
|
||||||
|
}
|
||||||
|
atcab_idle();
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "ATECC608B initialised");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void atecc608B_print_config(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "=== ATECC608B Configuration Zone ===");
|
||||||
|
|
||||||
|
uint8_t serial[9];
|
||||||
|
ATCA_STATUS status = atcab_read_serial_number(serial);
|
||||||
|
if (status != ATCA_SUCCESS) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read serial number (0x%02X) – check wiring", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!read_config_zone()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t *c = s_config_data;
|
||||||
|
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
if (i % 16 == 0) printf("\n0x%02X: ", i);
|
||||||
|
printf("%02X ", c[i]);
|
||||||
|
}
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "=== End of ATECC608B Configuration ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
void atecc608B_release(void)
|
||||||
|
{
|
||||||
|
atcab_release();
|
||||||
|
}
|
||||||
24
main/atecc608a.h
Normal file
24
main/atecc608a.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef ATECC608B_H
|
||||||
|
#define ATECC608B_H
|
||||||
|
|
||||||
|
#include "cryptoauthlib.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the ATECC608B over I2C.
|
||||||
|
* Returns true on success.
|
||||||
|
*/
|
||||||
|
bool atecc608B_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the full configuration zone and print it to the console.
|
||||||
|
* Useful for first-time setup diagnostics.
|
||||||
|
*/
|
||||||
|
void atecc608B_print_config(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release cryptoauthlib resources.
|
||||||
|
*/
|
||||||
|
void atecc608B_release(void);
|
||||||
|
|
||||||
|
#endif /* ATECC608B_H */
|
||||||
8
main/idf_component.yml
Normal file
8
main/idf_component.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
idf:
|
||||||
|
version: ">=4.1.0"
|
||||||
|
esp-cryptoauthlib:
|
||||||
|
git: https://github.com/espressif/esp-cryptoauthlib.git
|
||||||
|
libssh2_esp:
|
||||||
|
git: https://github.com/skuodi/libssh2_esp.git
|
||||||
281
main/main.c
Normal file
281
main/main.c
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
/*
|
||||||
|
* keypitecc – door controller
|
||||||
|
*
|
||||||
|
* Hardware:
|
||||||
|
* GPIO 9 – Boot button (active-low, press = GND)
|
||||||
|
* GPIO 15 – User LED (active-high, 1 = on)
|
||||||
|
*
|
||||||
|
* Button state machine (5-second window):
|
||||||
|
* IDLE
|
||||||
|
* └─ press ──► PENDING_OPEN (slow blink, will run SSH open command)
|
||||||
|
* └─ press ──► PENDING_LOCK (fast blink, will run SSH lock command)
|
||||||
|
* └─ press ──► IDLE (cancelled)
|
||||||
|
* Any pending state expires after 5 s → command is executed → IDLE
|
||||||
|
*
|
||||||
|
* LED idle behaviour:
|
||||||
|
* WiFi connected → LED ON
|
||||||
|
* WiFi disconnected → LED OFF
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "atecc608a.h"
|
||||||
|
#include "ssh_client.h"
|
||||||
|
#include "wifi.h"
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/queue.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <freertos/timers.h>
|
||||||
|
#include <nvs_flash.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static const char *TAG = "main";
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* GPIO defines
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
#define GPIO_BTN 9 /* Boot button, active-low */
|
||||||
|
#define GPIO_LED 15 /* User LED, active-high */
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Button / LED types
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
typedef enum {
|
||||||
|
BTN_STATE_IDLE,
|
||||||
|
BTN_STATE_PENDING_OPEN,
|
||||||
|
BTN_STATE_PENDING_LOCK,
|
||||||
|
} btn_state_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LED_MODE_WIFI_STATUS, /* ON = connected, OFF = disconnected */
|
||||||
|
LED_MODE_SLOW_BLINK, /* 500 ms on / 500 ms off (open pending) */
|
||||||
|
LED_MODE_FAST_BLINK, /* 100 ms on / 100 ms off (lock pending) */
|
||||||
|
} led_mode_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
EVT_BUTTON_PRESS,
|
||||||
|
EVT_TIMER_EXPIRED,
|
||||||
|
} event_type_t;
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Shared state
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static volatile led_mode_t s_led_mode = LED_MODE_WIFI_STATUS;
|
||||||
|
static btn_state_t s_btn_state = BTN_STATE_IDLE;
|
||||||
|
|
||||||
|
static QueueHandle_t s_event_queue;
|
||||||
|
static TimerHandle_t s_action_timer;
|
||||||
|
|
||||||
|
/* Debounce: ignore button edges within 200 ms of the previous one */
|
||||||
|
static volatile TickType_t s_last_btn_tick = 0;
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* SSH task – spawned on demand so the button task stays responsive
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static void ssh_task(void *arg)
|
||||||
|
{
|
||||||
|
const char *cmd = (const char *)arg;
|
||||||
|
ESP_LOGI(TAG, "Executing SSH command: %s", cmd);
|
||||||
|
bool ok = ssh_execute_command(cmd);
|
||||||
|
ESP_LOGI(TAG, "SSH command %s: %s", cmd, ok ? "succeeded" : "FAILED");
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trigger_ssh(const char *cmd)
|
||||||
|
{
|
||||||
|
/* Stack size 16 kB – SSH needs heap + TLS, keep priority low */
|
||||||
|
if (xTaskCreate(ssh_task, "ssh", 16384, (void *)cmd, 3, NULL) != pdPASS) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create SSH task");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* FreeRTOS software timer callback – runs in timer daemon task context
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static void action_timer_cb(TimerHandle_t xTimer)
|
||||||
|
{
|
||||||
|
(void)xTimer;
|
||||||
|
event_type_t evt = EVT_TIMER_EXPIRED;
|
||||||
|
xQueueSend(s_event_queue, &evt, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* GPIO ISR
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static void IRAM_ATTR button_isr(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
TickType_t now = xTaskGetTickCountFromISR();
|
||||||
|
if ((now - s_last_btn_tick) < pdMS_TO_TICKS(200)) {
|
||||||
|
return; /* debounce */
|
||||||
|
}
|
||||||
|
s_last_btn_tick = now;
|
||||||
|
|
||||||
|
event_type_t evt = EVT_BUTTON_PRESS;
|
||||||
|
xQueueSendFromISR(s_event_queue, &evt, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Button task – owns the state machine
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static void button_task(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
event_type_t evt;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (xQueueReceive(s_event_queue, &evt, portMAX_DELAY) != pdTRUE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (s_btn_state) {
|
||||||
|
|
||||||
|
case BTN_STATE_IDLE:
|
||||||
|
if (evt == EVT_BUTTON_PRESS) {
|
||||||
|
ESP_LOGI(TAG, "Button: PENDING_OPEN in 5s (press again for LOCK, third press cancels)");
|
||||||
|
s_btn_state = BTN_STATE_PENDING_OPEN;
|
||||||
|
s_led_mode = LED_MODE_SLOW_BLINK;
|
||||||
|
xTimerStart(s_action_timer, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BTN_STATE_PENDING_OPEN:
|
||||||
|
if (evt == EVT_BUTTON_PRESS) {
|
||||||
|
ESP_LOGI(TAG, "Button: switching to PENDING_LOCK");
|
||||||
|
s_btn_state = BTN_STATE_PENDING_LOCK;
|
||||||
|
s_led_mode = LED_MODE_FAST_BLINK;
|
||||||
|
xTimerReset(s_action_timer, 0); /* restart 5s from now */
|
||||||
|
} else if (evt == EVT_TIMER_EXPIRED) {
|
||||||
|
ESP_LOGI(TAG, "Timer expired: executing OPEN command");
|
||||||
|
s_btn_state = BTN_STATE_IDLE;
|
||||||
|
s_led_mode = LED_MODE_WIFI_STATUS;
|
||||||
|
trigger_ssh(CONFIG_SSH_OPEN_COMMAND);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BTN_STATE_PENDING_LOCK:
|
||||||
|
if (evt == EVT_BUTTON_PRESS) {
|
||||||
|
ESP_LOGI(TAG, "Button: action CANCELLED");
|
||||||
|
xTimerStop(s_action_timer, 0);
|
||||||
|
s_btn_state = BTN_STATE_IDLE;
|
||||||
|
s_led_mode = LED_MODE_WIFI_STATUS;
|
||||||
|
} else if (evt == EVT_TIMER_EXPIRED) {
|
||||||
|
ESP_LOGI(TAG, "Timer expired: executing LOCK command");
|
||||||
|
s_btn_state = BTN_STATE_IDLE;
|
||||||
|
s_led_mode = LED_MODE_WIFI_STATUS;
|
||||||
|
trigger_ssh(CONFIG_SSH_LOCK_COMMAND);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* LED task – drives GPIO 15 according to the current led_mode
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static void led_task(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
bool led_state = false;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
led_mode_t mode = s_led_mode; /* atomic read (single-byte enum) */
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case LED_MODE_WIFI_STATUS:
|
||||||
|
led_state = wifi_is_connected();
|
||||||
|
gpio_set_level(GPIO_LED, led_state ? 0 : 1);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(250));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_MODE_SLOW_BLINK:
|
||||||
|
gpio_set_level(GPIO_LED, 1);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
gpio_set_level(GPIO_LED, 0);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_MODE_FAST_BLINK:
|
||||||
|
gpio_set_level(GPIO_LED, 1);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
gpio_set_level(GPIO_LED, 0);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* GPIO initialisation
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static void gpio_init(void)
|
||||||
|
{
|
||||||
|
/* LED output */
|
||||||
|
gpio_config_t led_cfg = {
|
||||||
|
.pin_bit_mask = (1ULL << GPIO_LED),
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
};
|
||||||
|
gpio_config(&led_cfg);
|
||||||
|
gpio_set_level(GPIO_LED, 1); /* off (active-high) */
|
||||||
|
|
||||||
|
/* Button input with interrupt on falling edge (active-low) */
|
||||||
|
gpio_config_t btn_cfg = {
|
||||||
|
.pin_bit_mask = (1ULL << GPIO_BTN),
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_NEGEDGE,
|
||||||
|
};
|
||||||
|
gpio_config(&btn_cfg);
|
||||||
|
|
||||||
|
gpio_install_isr_service(0);
|
||||||
|
gpio_isr_handler_add(GPIO_BTN, button_isr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* app_main
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
/* NVS (required by WiFi) */
|
||||||
|
esp_err_t ret = nvs_flash_init();
|
||||||
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
|
ret = nvs_flash_init();
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
/* ATECC608B */
|
||||||
|
if (!atecc608B_init()) {
|
||||||
|
ESP_LOGW(TAG, "ATECC608B init failed – SSH authentication will not work");
|
||||||
|
} else {
|
||||||
|
atecc608B_print_config();
|
||||||
|
ssh_print_public_key(); /* print key for authorized_keys setup */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WiFi */
|
||||||
|
wifi_init();
|
||||||
|
|
||||||
|
/* GPIO and event infrastructure */
|
||||||
|
gpio_init();
|
||||||
|
|
||||||
|
s_event_queue = xQueueCreate(4, sizeof(event_type_t));
|
||||||
|
configASSERT(s_event_queue);
|
||||||
|
|
||||||
|
/* 5-second one-shot timer (auto-reload disabled) */
|
||||||
|
s_action_timer = xTimerCreate("action", pdMS_TO_TICKS(5000),
|
||||||
|
pdFALSE, NULL, action_timer_cb);
|
||||||
|
configASSERT(s_action_timer);
|
||||||
|
|
||||||
|
/* Tasks */
|
||||||
|
xTaskCreate(button_task, "button", 4096, NULL, 5, NULL);
|
||||||
|
xTaskCreate(led_task, "led", 2048, NULL, 4, NULL);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "keypitecc ready – press the boot button to operate the door");
|
||||||
|
}
|
||||||
285
main/ssh_client.c
Normal file
285
main/ssh_client.c
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
#include "ssh_client.h"
|
||||||
|
|
||||||
|
#include "cryptoauthlib.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <libssh2.h>
|
||||||
|
#include <mbedtls/base64.h>
|
||||||
|
#include <mbedtls/sha256.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static const char *TAG = "ssh_client";
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* SSH blob / key helpers
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Write a big-endian uint32 into buf. */
|
||||||
|
static void write_be32(uint8_t *buf, uint32_t val)
|
||||||
|
{
|
||||||
|
buf[0] = (val >> 24) & 0xFF;
|
||||||
|
buf[1] = (val >> 16) & 0xFF;
|
||||||
|
buf[2] = (val >> 8) & 0xFF;
|
||||||
|
buf[3] = val & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format one ECDSA integer as an SSH mpint:
|
||||||
|
* 4-byte big-endian length | optional 0x00 pad | value (leading zeros stripped)
|
||||||
|
*
|
||||||
|
* Returns number of bytes written to buf.
|
||||||
|
*/
|
||||||
|
static uint32_t write_mpint(uint8_t *buf, const uint8_t *val, uint32_t size)
|
||||||
|
{
|
||||||
|
uint32_t start = 0;
|
||||||
|
while (start < (size - 1) && val[start] == 0x00) {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pad = (val[start] & 0x80) != 0;
|
||||||
|
uint32_t len = (size - start) + (pad ? 1 : 0);
|
||||||
|
|
||||||
|
uint32_t off = 0;
|
||||||
|
buf[off++] = (len >> 24) & 0xFF;
|
||||||
|
buf[off++] = (len >> 16) & 0xFF;
|
||||||
|
buf[off++] = (len >> 8) & 0xFF;
|
||||||
|
buf[off++] = len & 0xFF;
|
||||||
|
|
||||||
|
if (pad) {
|
||||||
|
buf[off++] = 0x00;
|
||||||
|
}
|
||||||
|
memcpy(&buf[off], &val[start], size - start);
|
||||||
|
return off + (size - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a 104-byte SSH public key blob for ecdsa-sha2-nistp256:
|
||||||
|
* [uint32 19] "ecdsa-sha2-nistp256"
|
||||||
|
* [uint32 8] "nistp256"
|
||||||
|
* [uint32 65] 0x04 || X(32) || Y(32)
|
||||||
|
*
|
||||||
|
* @param atecc_pubkey 64-byte raw public key (X||Y) from ATECC608B.
|
||||||
|
* @param out_blob Must point to a buffer of at least 104 bytes.
|
||||||
|
*/
|
||||||
|
static void build_pubkey_blob(const uint8_t *atecc_pubkey, uint8_t *out_blob)
|
||||||
|
{
|
||||||
|
uint32_t off = 0;
|
||||||
|
|
||||||
|
/* Key type */
|
||||||
|
const char *key_type = "ecdsa-sha2-nistp256";
|
||||||
|
write_be32(&out_blob[off], 19); off += 4;
|
||||||
|
memcpy(&out_blob[off], key_type, 19); off += 19;
|
||||||
|
|
||||||
|
/* Curve name */
|
||||||
|
const char *curve = "nistp256";
|
||||||
|
write_be32(&out_blob[off], 8); off += 4;
|
||||||
|
memcpy(&out_blob[off], curve, 8); off += 8;
|
||||||
|
|
||||||
|
/* Uncompressed EC point: 0x04 || X || Y */
|
||||||
|
write_be32(&out_blob[off], 65); off += 4;
|
||||||
|
out_blob[off++] = 0x04;
|
||||||
|
memcpy(&out_blob[off], atecc_pubkey, 64);
|
||||||
|
/* off += 64 — total = 104 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* libssh2 signing callback (ATECC608B signs the hash)
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static int sign_callback(LIBSSH2_SESSION *session,
|
||||||
|
unsigned char **sig, size_t *sig_len,
|
||||||
|
const unsigned char *data, size_t data_len,
|
||||||
|
void **abstract)
|
||||||
|
{
|
||||||
|
(void)session;
|
||||||
|
(void)abstract;
|
||||||
|
|
||||||
|
uint8_t digest[32];
|
||||||
|
uint8_t raw_sig[64];
|
||||||
|
|
||||||
|
/* Hash the challenge data */
|
||||||
|
mbedtls_sha256(data, data_len, digest, 0);
|
||||||
|
|
||||||
|
/* Sign with the ATECC608B hardware key in slot 0 */
|
||||||
|
if (atcab_sign(0, digest, raw_sig) != ATCA_SUCCESS) {
|
||||||
|
ESP_LOGE(TAG, "ATECC608B signing failed!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode as [mpint R][mpint S] (no outer string wrapper) */
|
||||||
|
unsigned char *buf = (unsigned char *)malloc(80);
|
||||||
|
if (!buf) return -1;
|
||||||
|
|
||||||
|
uint32_t off = 0;
|
||||||
|
off += write_mpint(&buf[off], &raw_sig[0], 32); /* R */
|
||||||
|
off += write_mpint(&buf[off], &raw_sig[32], 32); /* S */
|
||||||
|
|
||||||
|
*sig = buf;
|
||||||
|
*sig_len = off;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Public API
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
void ssh_print_public_key(void)
|
||||||
|
{
|
||||||
|
uint8_t raw_key[64];
|
||||||
|
ATCA_STATUS st = atcab_get_pubkey(0, raw_key);
|
||||||
|
if (st != ATCA_SUCCESS) {
|
||||||
|
ESP_LOGE(TAG, "atcab_get_pubkey failed: 0x%02X", st);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t blob[104];
|
||||||
|
build_pubkey_blob(raw_key, blob);
|
||||||
|
|
||||||
|
size_t b64_len = 0;
|
||||||
|
mbedtls_base64_encode(NULL, 0, &b64_len, blob, sizeof(blob));
|
||||||
|
|
||||||
|
unsigned char *b64 = (unsigned char *)malloc(b64_len + 1);
|
||||||
|
if (!b64) {
|
||||||
|
ESP_LOGE(TAG, "malloc failed for base64 buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mbedtls_base64_encode(b64, b64_len, &b64_len, blob, sizeof(blob));
|
||||||
|
b64[b64_len] = '\0';
|
||||||
|
|
||||||
|
printf("\n=== SSH Public Key (add to authorized_keys) ===\n");
|
||||||
|
printf("ecdsa-sha2-nistp256 %s keypitecc\n", b64);
|
||||||
|
printf("================================================\n\n");
|
||||||
|
|
||||||
|
free(b64);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ssh_execute_command(const char *cmd)
|
||||||
|
{
|
||||||
|
if (!cmd) return false;
|
||||||
|
|
||||||
|
/* --- Read public key blob ------------------------------------------ */
|
||||||
|
uint8_t raw_key[64];
|
||||||
|
ATCA_STATUS st = atcab_get_pubkey(0, raw_key);
|
||||||
|
if (st != ATCA_SUCCESS) {
|
||||||
|
ESP_LOGE(TAG, "atcab_get_pubkey failed: 0x%02X", st);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint8_t pubkey_blob[104];
|
||||||
|
build_pubkey_blob(raw_key, pubkey_blob);
|
||||||
|
|
||||||
|
/* --- TCP connect ------------------------------------------------------- */
|
||||||
|
int rc;
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sock < 0) {
|
||||||
|
ESP_LOGE(TAG, "socket() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in sin = {};
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_port = htons(CONFIG_SSH_PORT);
|
||||||
|
sin.sin_addr.s_addr = inet_addr(CONFIG_SSH_HOSTNAME);
|
||||||
|
|
||||||
|
if (sin.sin_addr.s_addr == 0xFFFFFFFF) {
|
||||||
|
struct hostent *hp = gethostbyname(CONFIG_SSH_HOSTNAME);
|
||||||
|
if (!hp) {
|
||||||
|
ESP_LOGE(TAG, "gethostbyname(%s) failed", CONFIG_SSH_HOSTNAME);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sin.sin_addr.s_addr = ((struct ip4_addr *)hp->h_addr)->addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
|
||||||
|
ESP_LOGE(TAG, "connect to %s:%d failed", CONFIG_SSH_HOSTNAME, CONFIG_SSH_PORT);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "TCP connected to %s:%d", CONFIG_SSH_HOSTNAME, CONFIG_SSH_PORT);
|
||||||
|
|
||||||
|
/* --- libssh2 session -------------------------------------------------- */
|
||||||
|
rc = libssh2_init(0);
|
||||||
|
if (rc) {
|
||||||
|
ESP_LOGE(TAG, "libssh2_init failed: %d", rc);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBSSH2_SESSION *session = libssh2_session_init();
|
||||||
|
if (!session) {
|
||||||
|
ESP_LOGE(TAG, "libssh2_session_init failed");
|
||||||
|
libssh2_exit();
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = libssh2_session_handshake(session, sock);
|
||||||
|
if (rc) {
|
||||||
|
ESP_LOGE(TAG, "SSH handshake failed: %d", rc);
|
||||||
|
goto cleanup_session;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "SSH handshake OK");
|
||||||
|
|
||||||
|
/* --- Authenticate with ATECC608B hardware key ------------------------- */
|
||||||
|
void *abstract = NULL;
|
||||||
|
rc = libssh2_userauth_publickey(session, CONFIG_SSH_USERNAME,
|
||||||
|
pubkey_blob, sizeof(pubkey_blob),
|
||||||
|
sign_callback, &abstract);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "SSH public-key authentication failed: %d", rc);
|
||||||
|
goto cleanup_session;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "SSH authenticated as '%s'", CONFIG_SSH_USERNAME);
|
||||||
|
|
||||||
|
/* --- Open channel and execute command --------------------------------- */
|
||||||
|
LIBSSH2_CHANNEL *channel = libssh2_channel_open_session(session);
|
||||||
|
if (!channel) {
|
||||||
|
ESP_LOGE(TAG, "Failed to open SSH channel");
|
||||||
|
goto cleanup_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Executing: %s", cmd);
|
||||||
|
rc = libssh2_channel_exec(channel, cmd);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "channel_exec failed: %d", rc);
|
||||||
|
libssh2_channel_free(channel);
|
||||||
|
goto cleanup_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Read output ------------------------------------------------------- */
|
||||||
|
char buf[256];
|
||||||
|
int bytes;
|
||||||
|
ESP_LOGI(TAG, "--- command output ---");
|
||||||
|
while ((bytes = libssh2_channel_read(channel, buf, sizeof(buf) - 1)) > 0) {
|
||||||
|
buf[bytes] = '\0';
|
||||||
|
printf("%s", buf);
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "--- end output ---");
|
||||||
|
|
||||||
|
/* --- Shutdown --------------------------------------------------------- */
|
||||||
|
libssh2_channel_close(channel);
|
||||||
|
int exit_status = libssh2_channel_get_exit_status(channel);
|
||||||
|
ESP_LOGI(TAG, "Command exited with status: %d", exit_status);
|
||||||
|
|
||||||
|
libssh2_channel_free(channel);
|
||||||
|
|
||||||
|
libssh2_session_disconnect(session, "Bye");
|
||||||
|
libssh2_session_free(session);
|
||||||
|
libssh2_exit();
|
||||||
|
close(sock);
|
||||||
|
return (exit_status == 0);
|
||||||
|
|
||||||
|
cleanup_session:
|
||||||
|
libssh2_session_disconnect(session, "Error");
|
||||||
|
libssh2_session_free(session);
|
||||||
|
libssh2_exit();
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
25
main/ssh_client.h
Normal file
25
main/ssh_client.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef SSH_CLIENT_H
|
||||||
|
#define SSH_CLIENT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the ATECC608B public key, format it as an SSH authorized_keys entry,
|
||||||
|
* and print it to the console. Call this once at boot so the key can be
|
||||||
|
* added to the server's authorized_keys file.
|
||||||
|
*
|
||||||
|
* Output format:
|
||||||
|
* ecdsa-sha2-nistp256 <base64-blob> keypitecc
|
||||||
|
*/
|
||||||
|
void ssh_print_public_key(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a TCP connection to the configured SSH server, authenticate using the
|
||||||
|
* ATECC608B hardware key, execute @p cmd, log the output, and disconnect.
|
||||||
|
*
|
||||||
|
* @param cmd Shell command string to run on the remote host.
|
||||||
|
* @return true on success, false on any error.
|
||||||
|
*/
|
||||||
|
bool ssh_execute_command(const char *cmd);
|
||||||
|
|
||||||
|
#endif /* SSH_CLIENT_H */
|
||||||
81
main/wifi.c
Normal file
81
main/wifi.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "wifi.h"
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_netif.h>
|
||||||
|
#include <esp_wifi.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/event_groups.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char *TAG = "wifi";
|
||||||
|
|
||||||
|
static EventGroupHandle_t s_wifi_event_group;
|
||||||
|
static const int WIFI_CONNECTED_BIT = BIT0;
|
||||||
|
static volatile bool s_connected = false;
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Event handler
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||||
|
esp_wifi_connect();
|
||||||
|
ESP_LOGI(TAG, "WiFi connecting...");
|
||||||
|
} else if (event_base == WIFI_EVENT &&
|
||||||
|
event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||||
|
s_connected = false;
|
||||||
|
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||||
|
esp_wifi_connect();
|
||||||
|
ESP_LOGI(TAG, "WiFi disconnected, reconnecting...");
|
||||||
|
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||||
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||||
|
ESP_LOGI(TAG, "WiFi connected, IP: " IPSTR, IP2STR(&event->ip_info.ip));
|
||||||
|
s_connected = true;
|
||||||
|
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Public API
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
void wifi_init(void)
|
||||||
|
{
|
||||||
|
s_wifi_event_group = xEventGroupCreate();
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
|
||||||
|
configASSERT(sta_netif);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_netif_set_hostname(sta_netif, "keypitecc"));
|
||||||
|
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||||
|
&wifi_event_handler, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
||||||
|
&wifi_event_handler, NULL));
|
||||||
|
|
||||||
|
wifi_config_t wifi_config = {};
|
||||||
|
strncpy((char *)wifi_config.sta.ssid, CONFIG_WIFI_SSID,
|
||||||
|
sizeof(wifi_config.sta.ssid) - 1);
|
||||||
|
strncpy((char *)wifi_config.sta.password, CONFIG_WIFI_PASSWORD,
|
||||||
|
sizeof(wifi_config.sta.password) - 1);
|
||||||
|
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Connecting to SSID: %s", CONFIG_WIFI_SSID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wifi_is_connected(void)
|
||||||
|
{
|
||||||
|
return s_connected;
|
||||||
|
}
|
||||||
19
main/wifi.h
Normal file
19
main/wifi.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#ifndef WIFI_H
|
||||||
|
#define WIFI_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise WiFi in STA mode, connect to the SSID defined in Kconfig,
|
||||||
|
* and register event handlers. Blocks until a first connection attempt
|
||||||
|
* is made, but does NOT block forever – reconnection is handled in the
|
||||||
|
* background.
|
||||||
|
*/
|
||||||
|
void wifi_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the station has a valid IP address (i.e. WiFi is up).
|
||||||
|
*/
|
||||||
|
bool wifi_is_connected(void);
|
||||||
|
|
||||||
|
#endif /* WIFI_H */
|
||||||
15
sdkconfig.defaults
Normal file
15
sdkconfig.defaults
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
|
# Espressif IoT Development Framework (ESP-IDF) 5.5.2 Project Minimal Configuration
|
||||||
|
#
|
||||||
|
CONFIG_IDF_TARGET="esp32c6"
|
||||||
|
CONFIG_WIFI_SSID="DoNotSetTheRealValueHere"
|
||||||
|
CONFIG_WIFI_PASSWORD="PutTheRealPassInTheSdkconfigFile"
|
||||||
|
CONFIG_SSH_HOSTNAME="192.168.178.1"
|
||||||
|
CONFIG_SSH_USERNAME="user"
|
||||||
|
CONFIG_SSH_OPEN_COMMAND="open"
|
||||||
|
CONFIG_SSH_LOCK_COMMAND="lock"
|
||||||
|
CONFIG_ATECC608A_TCUSTOM=y
|
||||||
|
CONFIG_ATCA_I2C_SDA_PIN=22
|
||||||
|
CONFIG_ATCA_I2C_SCL_PIN=23
|
||||||
|
CONFIG_ATCA_I2C_BAUD_RATE=1000000
|
||||||
|
CONFIG_LIBSSH2_DEBUG_ENABLE=n
|
||||||
Reference in New Issue
Block a user