Compare commits

31 Commits

Author SHA1 Message Date
Philipp
59d87eb199 fixed RTC footprint and some labels 2025-08-23 15:18:17 +02:00
Philipp
630fc4aaf9 pcb v1 finish 2025-08-16 00:22:37 +02:00
21480cef4f added serde for serializing in the webserver 2025-08-15 16:46:44 +02:00
Philipp
fabb14de86 circuit diagram is finish v1 2025-08-15 15:57:41 +02:00
Psenfft
6a2d448f86 Update hardware.rs
changed pinlayout to add SD DECT top GPIO0
2025-08-15 14:04:02 +02:00
Philipp_EndevourOS
fc7bd8b089 still some errors with LED an levelshifter... 2025-08-13 02:36:25 +02:00
Philipp_EndevourOS
3117c55b1c LED is working and implement in feedback 2025-08-13 02:05:49 +02:00
Philipp_EndevourOS
593d98df74 test LED Array works.. 2025-08-13 01:51:00 +02:00
Philipp_EndevourOS
fa6d1f024c test LED Array works.. 2025-08-13 01:36:13 +02:00
Philipp_EndevourOS
36dc52f464 try to control LED with SmartLED and RMT 2025-08-13 00:55:25 +02:00
Philipp_EndevourOS
6831d7776c chanched GPIO Pin configuration 2025-08-11 14:27:41 +02:00
a015d6b983 fixed embassy taks arena size 2025-08-04 18:52:03 +02:00
Philipp_EndevourOS
1ae5250449 reworked RTC without alarms and without own thread 2025-08-04 18:12:46 +02:00
Philipp_EndevourOS
2f502e908e rtc is synchronized with compile time. start pub sub approach to share time 2025-08-02 00:39:48 +02:00
Philipp_EndevourOS
5950279dc4 buzzer without pwm, and feedback with embassy Sync. Error when more then 4 tasks spawn 2025-08-01 16:42:14 +02:00
fe6540ca3d added static assets on webserver 2025-07-30 22:13:52 +02:00
Philipp_EndevourOS
161ebf9bd2 added buzzer and rtc. rtc freeze the system, bevore interrupt can be initilized.. 2025-07-30 18:50:47 +02:00
Philipp_EndevourOS
c1b54920ff added comment for Pinout 2025-07-28 22:22:07 +02:00
Philipp_EndevourOS
5a2beb1fb3 deleted todos for rtc 2025-07-28 22:03:19 +02:00
Philipp_EndevourOS
d5c20bf348 worted on rtc task, still not tested 2025-07-28 17:53:26 +02:00
Philipp_EndevourOS
49027fed99 redesigned dir structure for rust 2018 style guide. made (untested) rtc funtion 2025-07-28 17:25:39 +02:00
Philipp_EndevourOS
4dda9548d3 added some todos 2025-07-27 02:48:27 +02:00
Philipp_EndevourOS
46e207bd2a addded lvl shifter init 2025-07-27 02:24:29 +02:00
Philipp_EndevourOS
8cb118e0ee added i2c init 2025-07-27 01:30:38 +02:00
Philipp_EndevourOS
9b4df77112 added hardware files (rtc and fram) 2025-07-27 00:32:41 +02:00
23bb1126a6 implemented channel for TallyIDs 2025-07-26 19:18:14 +02:00
a97e9c8080 added embassy-sync crate 2025-07-26 19:17:54 +02:00
4b39529a65 first implementation of IDStore and IDmapping 2025-07-26 18:30:45 +02:00
c91d2f070f improved project structure and hardware init 2025-07-26 16:53:23 +02:00
2e6094ea11 added esp bootload 2025-07-24 17:37:59 +02:00
43e964b5a0 init v2 2025-07-24 17:22:50 +02:00
106 changed files with 231285 additions and 3107 deletions

14
.cargo/config.toml Normal file
View File

@@ -0,0 +1,14 @@
[target.riscv32imac-unknown-none-elf]
runner = "espflash flash --monitor --chip esp32c6"
[build]
rustflags = [
# Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.)
# NOTE: May negatively impact performance of produced code
"-C", "force-frame-pointers",
]
target = "riscv32imac-unknown-none-elf"
[unstable]
build-std = ["alloc", "core"]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

2404
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,24 +3,81 @@ name = "fw-anwesenheit"
version = "0.1.0"
edition = "2024"
[features]
default = []
mock_pi = [] # Enable mocking of the rpi hardware
[[bin]]
name = "fw-anwesenheit"
path = "./src/main.rs"
test = false
doctest = false
bench = false
[dependencies]
chrono = { version = "0.4.40", features = ["serde"] }
gpio = "0.4.1"
regex = "1.11.1"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
rocket = { version = "0.5.1", features = ["json"] }
tokio = { version = "1.44.2", features = ["full"] }
rust-embed = "8.7.0"
log = "0.4.27"
simplelog = "0.12.2"
rppal = { version = "0.22.1", features = ["hal"] }
smart-leds = "0.3"
ws2812-spi = "0.3"
rgb = "0.8.50"
anyhow = "1.0.98"
esp-bootloader-esp-idf = "0.1.0"
embassy-net = { version = "0.7.0", features = [
"dhcpv4",
"medium-ethernet",
"tcp",
"udp",
] }
embedded-hal = "=1.0.0"
embedded-io = "0.6.1"
embedded-io-async = "0.6.1"
esp-alloc = "0.8.0"
esp-hal = { version = "1.0.0-beta.1", features = ["esp32c6", "unstable"] }
smoltcp = { version = "0.12.0", default-features = false, features = [
"medium-ethernet",
"multicast",
"proto-dhcpv4",
"proto-dns",
"proto-ipv4",
"socket-dns",
"socket-icmp",
"socket-raw",
"socket-tcp",
"socket-udp",
] }
# for more networking protocol support see https://crates.io/crates/edge-net
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8ae679e021b78f53fd33afb8bb35d0b62e", features = [
"async",
"macros",
] }
critical-section = "1.2.0"
embassy-executor = { version = "0.7.0", features = ["nightly"] }
embassy-time = { version = "0.4.0", features = ["generic-queue-8"] }
esp-hal-embassy = { version = "0.9.0", features = ["esp32c6"] }
esp-wifi = { version = "0.15.0", features = [
"wifi",
"builtin-scheduler",
"esp-alloc",
"esp32c6",
"log-04",
] }
heapless = { version = "0.8.0", default-features = false }
static_cell = { version = "2.1.0", features = ["nightly"] }
esp-println = { version = "0.15.0", features = ["esp32c6", "log-04"] }
log = { version = "0.4" }
edge-dhcp = { version = "0.6.0", features = ["log"] }
edge-nal = "0.5.0"
edge-nal-embassy = { version = "0.6.0", features = ["log"] }
picoserve = { version = "0.16.0", features = ["embassy", "log"] }
embassy-sync = { version = "0.7.0", features = ["log"] }
ds3231 = { version = "0.3.0", features = ["async", "temperature_f32"] }
chrono = { version = "0.4.41", default-features = false }
dir-embed = "0.3.0"
embedded-sdmmc-dev = "0.8.2"
esp-hal-smartled = { git = "https://github.com/esp-rs/esp-hal-community.git", package = "esp-hal-smartled", branch = "main", features = ["esp32c6"]}
smart-leds = "0.4.0"
serde = { version = "1.0.219", default-features = false, features = ["derive", "alloc"] }
[profile.dev]
# Rust debug is too slow.
# For debug builds always builds with some optimization
opt-level = "s"
[profile.release]
codegen-units = 1 # LLVM can perform better optimizations using a single thread
debug = 2
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 's'
overflow-checks = false

View File

@@ -1,61 +0,0 @@
PACKAGE_NAME := fwa
VERSION := 1.0
ARCH := armhf
BUILD_DIR := build
DEB_DIR := $(BUILD_DIR)/$(PACKAGE_NAME)-$(VERSION)
BIN_DIR := $(DEB_DIR)/usr/local/bin
SERVICE_DIR := $(DEB_DIR)/lib/systemd/system
CONFIG_DIR := $(DEB_DIR)/etc
PM3_DIR := $(DEB_DIR)/usr/share/pm3
.PHONY: all build clean package prepare_package
all: package
build: $(BUILD_DIR)/fwa
$(BUILD_DIR)/fwa: web/dist
cross build --release --target arm-unknown-linux-gnueabihf
cp ./target/arm-unknown-linux-gnueabihf/release/fw-anwesenheit $@
prepare_package: $(DEB_DIR)/DEBIAN $(BIN_DIR)/fwa
mkdir -p $(SERVICE_DIR)
cp ./service/fwa.service $(SERVICE_DIR)/
cp ./service/fwa-fail.service $(SERVICE_DIR)/
mkdir -p $(CONFIG_DIR)
cp ./service/fwa.env $(CONFIG_DIR)/
mkdir -p $(PM3_DIR)
cp -r ./pre-compiled/* $(PM3_DIR)/
mkdir -p $(DEB_DIR)/var/lib/fwa/
$(BIN_DIR)/fwa: $(BUILD_DIR)/fwa
mkdir -p $(BIN_DIR)
cp $< $@
$(DEB_DIR)/DEBIAN:
mkdir -p $(DEB_DIR)/DEBIAN
echo "Package: $(PACKAGE_NAME)" > $(DEB_DIR)/DEBIAN/control
echo "Version: $(VERSION)" >> $(DEB_DIR)/DEBIAN/control
echo "Section: utils" >> $(DEB_DIR)/DEBIAN/control
echo "Priority: optional" >> $(DEB_DIR)/DEBIAN/control
echo "Architecture: $(ARCH)" >> $(DEB_DIR)/DEBIAN/control
echo "Depends: libc6 (>= 2.28)" >> $(DEB_DIR)/DEBIAN/control
echo "Maintainer: Niklas Kapelle <niklas@kapelle.org>" >> $(DEB_DIR)/DEBIAN/control
echo "Description: Feuerwehr anwesenheit" >> $(DEB_DIR)/DEBIAN/control
echo "/etc/fwa.env" > $(DEB_DIR)/DEBIAN/conffiles
web/dist:
(cd web && npm run build)
package: prepare_package
dpkg-deb --build $(DEB_DIR)
clean:
cargo clean
rm -rf web/dist
rm -rf $(BUILD_DIR)

66
build.rs Normal file
View File

@@ -0,0 +1,66 @@
use std::env;
use std::path::Path;
use std::fs::File;
use std::io::Write;
fn main() {
linker_be_nice();
// make sure linkall.x is the last linker script (otherwise might cause problems with flip-link)
println!("cargo:rustc-link-arg=-Tlinkall.x");
save_build_time();
}
fn save_build_time() {
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("build_time.rs");
let system_time = std::time::SystemTime::now();
let unix_time = system_time
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
println!("cargo:rustc-env=BUILD_TIME={}", unix_time);
let content = format!(
"/// compile time as UNIX-Timestamp (seconds since 1970-01-01)
pub const BUILD_UNIX_TIME: u64 = {};",
unix_time
);
let mut f = File::create(dest_path).unwrap();
f.write_all(content.as_bytes()).unwrap();
}
fn linker_be_nice() {
let args: Vec<String> = std::env::args().collect();
if args.len() > 1 {
let kind = &args[1];
let what = &args[2];
match kind.as_str() {
"undefined-symbol" => match what.as_str() {
"_defmt_timestamp" => {
eprintln!();
eprintln!(
"💡 `defmt` not found - make sure `defmt.x` is added as a linker script and you have included `use defmt_rtt as _;`"
);
eprintln!();
}
"_stack_start" => {
eprintln!();
eprintln!("💡 Is the linker script `linkall.x` missing?");
eprintln!();
}
_ => (),
},
// we don't have anything helpful for "missing-lib" yet
_ => {
std::process::exit(1);
}
}
std::process::exit(0);
}
println!(
"cargo:rustc-link-arg=--error-handling-script={}",
std::env::current_exe().unwrap().display()
);
}

View File

@@ -1,24 +0,0 @@
import RPi.GPIO as GPIO
import time
BUZZER_PIN = 26
def beep(frequency, duration):
pwm = GPIO.PWM(BUZZER_PIN, frequency)
pwm.start(50) # 50 % duty cycle
time.sleep(duration)
pwm.stop()
def main():
GPIO.setmode(GPIO.BCM)
GPIO.setup(BUZZER_PIN, GPIO.OUT)
try:
beep(523, 0.3) # C5
beep(659, 0.3) # E5
beep(784, 0.3) # G5
finally:
GPIO.cleanup()
if __name__ == "__main__":
main()

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,34 +0,0 @@
@startuml
actor user
main -> pm3 :start
loop
pm3 -> pm3 : look for HITAG
end
user -> pm3 :show HITAG
pm3 -> parser : parse ID
parser -> pm3 : return ID
pm3 --> main : send ID
loop
main -> main : look for IDs
end
main -> idstore : send ID
idstore -> System : ask for day
alt
System -> idstore : return attendence_day
else
create collections attendence_day
idstore -> attendence_day : create new attendence_day
end
idstore -> attendence_day : add ID
attendence_day -> system : save json
@enduml

105757
pcb/fp-info-cache Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
{
"board": {
"active_layer": 0,
"active_layer_preset": "",
"auto_track_width": false,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 1.0,
"pads": 1.0,
"shapes": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 1.0
},
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": false,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
12
],
"visible_layers": "fffffff_ffffffff",
"zone_display_mode": 0
},
"git": {
"repo_password": "",
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "fw-anwesenheit.kicad_prl",
"version": 3
},
"net_inspector_panel": {
"col_hidden": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
],
"col_order": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13
],
"col_widths": [
162,
147,
91,
72,
91,
100,
91,
76,
91,
91,
91,
91,
91,
91
],
"custom_group_rules": [],
"expanded_rows": [],
"filter_by_net_name": true,
"filter_by_netclass": true,
"filter_text": "",
"group_by_constraint": false,
"group_by_netclass": false,
"show_unconnected_nets": false,
"show_zero_pad_nets": false,
"sort_ascending": true,
"sorting_column": 0
},
"open_jobsets": [],
"project": {
"files": []
},
"schematic": {
"selection_filter": {
"graphics": true,
"images": true,
"labels": true,
"lockedItems": false,
"otherItems": true,
"pins": true,
"symbols": true,
"text": true,
"wires": true
}
}
}

View File

@@ -0,0 +1,630 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.05,
"copper_line_width": 0.2,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.05,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": true,
"text_position": 0,
"units_format": 0
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.0,
"height": 1.7,
"width": 1.7
},
"silk_line_width": 0.1,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.1,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"creepage": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_filters_mismatch": "ignore",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_near_hole": "error",
"hole_to_hole": "error",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"mirrored_text_on_front_layer": "warning",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"nonmirrored_text_on_back_layer": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "warning",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_on_edge_cuts": "error",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_angle": "error",
"track_dangling": "warning",
"track_segment_length": "error",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.2,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_groove_width": 0.0,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.2,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.1,
"min_track_width": 0.1,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.3,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
}
],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"label_multiple_wires": "warning",
"lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "ignore",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "fw-anwesenheit.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "production/",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "${PROJECTNAME}.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": true,
"label": "Exclude from BOM",
"name": "${EXCLUDE_FROM_BOM}",
"show": true
},
{
"group_by": true,
"label": "Exclude from Board",
"name": "${EXCLUDE_FROM_BOARD}",
"show": true
},
{
"group_by": true,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": true,
"name": "Default Editing",
"sort_asc": true,
"sort_field": "Referenz"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"space_save_all_events": true,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"ccbf1fda-befd-42da-bcb2-5d3829184012",
"Root"
]
],
"text_variables": {}
}

25828
pcb/fw-anwesenheit.kicad_pcb Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,143 @@
{
"board": {
"active_layer": 5,
"active_layer_preset": "",
"auto_track_width": true,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
"pads": 1.0,
"shapes": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 0.6
},
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": false,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
"vias",
"footprint_text",
"footprint_anchors",
"ratsnest",
"grid",
"footprints_front",
"footprints_back",
"footprint_values",
"footprint_references",
"tracks",
"drc_errors",
"drawing_sheet",
"bitmaps",
"pads",
"zones",
"drc_warnings",
"drc_exclusions",
"locked_item_shadows",
"conflict_shadows",
"shapes"
],
"visible_layers": "ffffffff_ffffffff_ffffffff_fdff502a",
"zone_display_mode": 0
},
"git": {
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "fw-anwesenheit.kicad_prl",
"version": 5
},
"net_inspector_panel": {
"col_hidden": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
],
"col_order": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13
],
"col_widths": [
162,
147,
91,
72,
91,
100,
91,
76,
91,
91,
91,
91,
91,
91
],
"custom_group_rules": [],
"expanded_rows": [],
"filter_by_net_name": true,
"filter_by_netclass": true,
"filter_text": "",
"group_by_constraint": false,
"group_by_netclass": false,
"show_unconnected_nets": false,
"show_zero_pad_nets": false,
"sort_ascending": true,
"sorting_column": 0
},
"open_jobsets": [],
"project": {
"files": []
},
"schematic": {
"selection_filter": {
"graphics": true,
"images": true,
"labels": true,
"lockedItems": false,
"otherItems": true,
"pins": true,
"symbols": true,
"text": true,
"wires": true
}
}
}

View File

@@ -0,0 +1,632 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.05,
"copper_line_width": 0.2,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.05,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": true,
"text_position": 0,
"units_format": 0
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.0,
"height": 1.7,
"width": 1.7
},
"silk_line_width": 0.1,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.1,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"creepage": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_filters_mismatch": "ignore",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_near_hole": "error",
"hole_to_hole": "error",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"mirrored_text_on_front_layer": "warning",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"nonmirrored_text_on_back_layer": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "warning",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_on_edge_cuts": "error",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_angle": "error",
"track_dangling": "warning",
"track_segment_length": "error",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.2,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_groove_width": 0.0,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.2,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.1,
"min_track_width": 0.1,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.3,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpthpad": true,
"td_onroundshapesonly": false,
"td_onsmdpad": true,
"td_ontrackend": false,
"td_onvia": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
}
],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"label_multiple_wires": "warning",
"lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "ignore",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "fw-anwesenheit.kicad_pro",
"version": 3
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 4
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "production/",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "${PROJECTNAME}.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": true,
"label": "Exclude from BOM",
"name": "${EXCLUDE_FROM_BOM}",
"show": true
},
{
"group_by": true,
"label": "Exclude from Board",
"name": "${EXCLUDE_FROM_BOARD}",
"show": true
},
{
"group_by": true,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": true,
"name": "Default Editing",
"sort_asc": true,
"sort_field": "Referenz"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"space_save_all_events": true,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"ccbf1fda-befd-42da-bcb2-5d3829184012",
"Root"
]
],
"text_variables": {}
}

11937
pcb/fw-anwesenheit.kicad_sch Normal file

File diff suppressed because it is too large Load Diff

BIN
pcb/production.zip Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,9.0.3*
G04 #@! TF.CreationDate,2025-08-23T15:15:08+02:00*
G04 #@! TF.ProjectId,fw-anwesenheit,66772d61-6e77-4657-9365-6e686569742e,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Soldermask,Bot*
G04 #@! TF.FilePolarity,Negative*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 9.0.3) date 2025-08-23 15:15:08*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 Aperture macros list*
%AMRoundRect*
0 Rectangle with rounded corners*
0 $1 Rounding radius*
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
0 Add a 4 corners polygon primitive as box body*
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
0 Add four circle primitives for the rounded corners*
1,1,$1+$1,$2,$3*
1,1,$1+$1,$4,$5*
1,1,$1+$1,$6,$7*
1,1,$1+$1,$8,$9*
0 Add four rect primitives between the rounded corners*
20,1,$1+$1,$2,$3,$4,$5,0*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
G04 Aperture macros list end*
%ADD10R,1.350000X1.350000*%
%ADD11C,1.350000*%
%ADD12C,2.304000*%
%ADD13RoundRect,0.102000X-1.050000X1.050000X-1.050000X-1.050000X1.050000X-1.050000X1.050000X1.050000X0*%
%ADD14C,1.000000*%
%ADD15C,1.500000*%
%ADD16R,1.600000X2.000000*%
%ADD17O,1.600000X2.000000*%
%ADD18R,1.700000X1.700000*%
%ADD19C,1.700000*%
G04 APERTURE END LIST*
D10*
X89916000Y-118888000D03*
D11*
X89916000Y-120888000D03*
D12*
X89500000Y-59520000D03*
D13*
X89500000Y-62060000D03*
D12*
X104740000Y-87460000D03*
X104740000Y-84920000D03*
X89500000Y-87460000D03*
D13*
X89500000Y-92540000D03*
D12*
X104740000Y-90000000D03*
D13*
X104740000Y-92540000D03*
D12*
X104740000Y-82380000D03*
X89500000Y-90000000D03*
D14*
X125750000Y-83600000D03*
X117750000Y-83600000D03*
D15*
X107500000Y-90000000D03*
X111000000Y-90000000D03*
X128500000Y-90000000D03*
D16*
X113411000Y-103378000D03*
D17*
X92911000Y-103378000D03*
D15*
X125000000Y-90000000D03*
D18*
X120010000Y-127000000D03*
D19*
X122550000Y-127000000D03*
D15*
X114500000Y-90000000D03*
D18*
X83185000Y-110490000D03*
D19*
X83185000Y-113030000D03*
X83185000Y-115570000D03*
X83185000Y-118110000D03*
D15*
X121500000Y-90000000D03*
X118000000Y-90000000D03*
M02*

View File

@@ -0,0 +1,27 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,9.0.3*
G04 #@! TF.CreationDate,2025-08-23T15:15:08+02:00*
G04 #@! TF.ProjectId,fw-anwesenheit,66772d61-6e77-4657-9365-6e686569742e,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Paste,Bot*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 9.0.3) date 2025-08-23 15:15:08*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%ADD10R,1.700000X1.700000*%
%ADD11C,1.700000*%
G04 APERTURE END LIST*
D10*
X120010000Y-127000000D03*
D11*
X122550000Y-127000000D03*
D10*
X83185000Y-110490000D03*
D11*
X83185000Y-113030000D03*
X83185000Y-115570000D03*
X83185000Y-118110000D03*
M02*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,9.0.3*
G04 #@! TF.CreationDate,2025-08-23T15:15:08+02:00*
G04 #@! TF.ProjectId,fw-anwesenheit,66772d61-6e77-4657-9365-6e686569742e,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Profile,NP*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 9.0.3) date 2025-08-23 15:15:08*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 #@! TA.AperFunction,Profile*
%ADD10C,1.025000*%
G04 #@! TD*
G04 #@! TA.AperFunction,Profile*
%ADD11C,0.050000*%
G04 #@! TD*
G04 #@! TA.AperFunction,Profile*
%ADD12C,0.010000*%
G04 #@! TD*
G04 APERTURE END LIST*
D10*
X85512500Y-123500000D02*
G75*
G02*
X84487500Y-123500000I-512500J0D01*
G01*
X84487500Y-123500000D02*
G75*
G02*
X85512500Y-123500000I512500J0D01*
G01*
X85512500Y-85000000D02*
G75*
G02*
X84487500Y-85000000I-512500J0D01*
G01*
X84487500Y-85000000D02*
G75*
G02*
X85512500Y-85000000I512500J0D01*
G01*
D11*
X81045000Y-79502000D02*
X131045000Y-79502000D01*
X131045000Y-129502000D01*
X81045000Y-129502000D01*
X81045000Y-79502000D01*
D10*
X127512500Y-123500000D02*
G75*
G02*
X126487500Y-123500000I-512500J0D01*
G01*
X126487500Y-123500000D02*
G75*
G02*
X127512500Y-123500000I512500J0D01*
G01*
X109512500Y-85000000D02*
G75*
G02*
X108487500Y-85000000I-512500J0D01*
G01*
X108487500Y-85000000D02*
G75*
G02*
X109512500Y-85000000I512500J0D01*
G01*
D12*
X117400000Y-83100000D02*
X118100000Y-83100000D01*
X118100000Y-84100000D02*
X117400000Y-84100000D01*
X116900000Y-83600000D02*
G75*
G02*
X117400000Y-83100000I500000J0D01*
G01*
X117400000Y-84100000D02*
G75*
G02*
X116900000Y-83600000I0J500000D01*
G01*
X118100000Y-83100000D02*
G75*
G02*
X118600000Y-83600000I0J-500000D01*
G01*
X118600000Y-83600000D02*
G75*
G02*
X118100000Y-84100000I-500000J0D01*
G01*
M02*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,288 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,9.0.3*
G04 #@! TF.CreationDate,2025-08-23T15:15:08+02:00*
G04 #@! TF.ProjectId,fw-anwesenheit,66772d61-6e77-4657-9365-6e686569742e,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Soldermask,Top*
G04 #@! TF.FilePolarity,Negative*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 9.0.3) date 2025-08-23 15:15:08*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 Aperture macros list*
%AMRoundRect*
0 Rectangle with rounded corners*
0 $1 Rounding radius*
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
0 Add a 4 corners polygon primitive as box body*
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
0 Add four circle primitives for the rounded corners*
1,1,$1+$1,$2,$3*
1,1,$1+$1,$4,$5*
1,1,$1+$1,$6,$7*
1,1,$1+$1,$8,$9*
0 Add four rect primitives between the rounded corners*
20,1,$1+$1,$2,$3,$4,$5,0*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
%AMOutline5P*
0 Free polygon, 5 corners , with rotation*
0 The origin of the aperture is its center*
0 number of corners: always 5*
0 $1 to $10 corner X, Y*
0 $11 Rotation angle, in degrees counterclockwise*
0 create outline with 5 corners*
4,1,5,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$1,$2,$11*%
%AMOutline6P*
0 Free polygon, 6 corners , with rotation*
0 The origin of the aperture is its center*
0 number of corners: always 6*
0 $1 to $12 corner X, Y*
0 $13 Rotation angle, in degrees counterclockwise*
0 create outline with 6 corners*
4,1,6,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$1,$2,$13*%
%AMOutline7P*
0 Free polygon, 7 corners , with rotation*
0 The origin of the aperture is its center*
0 number of corners: always 7*
0 $1 to $14 corner X, Y*
0 $15 Rotation angle, in degrees counterclockwise*
0 create outline with 7 corners*
4,1,7,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$1,$2,$15*%
%AMOutline8P*
0 Free polygon, 8 corners , with rotation*
0 The origin of the aperture is its center*
0 number of corners: always 8*
0 $1 to $16 corner X, Y*
0 $17 Rotation angle, in degrees counterclockwise*
0 create outline with 8 corners*
4,1,8,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$1,$2,$17*%
%AMFreePoly0*
4,1,6,1.000000,0.000000,0.500000,-0.750000,-0.500000,-0.750000,-0.500000,0.750000,0.500000,0.750000,1.000000,0.000000,1.000000,0.000000,$1*%
%AMFreePoly1*
4,1,6,0.500000,-0.750000,-0.650000,-0.750000,-0.150000,0.000000,-0.650000,0.750000,0.500000,0.750000,0.500000,-0.750000,0.500000,-0.750000,$1*%
G04 Aperture macros list end*
%ADD10RoundRect,0.225000X0.225000X0.250000X-0.225000X0.250000X-0.225000X-0.250000X0.225000X-0.250000X0*%
%ADD11Outline5P,-1.250000X0.510000X-0.910000X0.850000X1.250000X0.850000X1.250000X-0.850000X-1.250000X-0.850000X0.000000*%
%ADD12RoundRect,0.170000X-1.080000X-0.680000X1.080000X-0.680000X1.080000X0.680000X-1.080000X0.680000X0*%
%ADD13RoundRect,0.250000X-1.000000X-0.600000X1.000000X-0.600000X1.000000X0.600000X-1.000000X0.600000X0*%
%ADD14RoundRect,0.525400X0.900400X0.525400X-0.900400X0.525400X-0.900400X-0.525400X0.900400X-0.525400X0*%
%ADD15RoundRect,0.300400X0.300400X-1.000400X0.300400X1.000400X-0.300400X1.000400X-0.300400X-1.000400X0*%
%ADD16C,1.700000*%
%ADD17C,1.500000*%
%ADD18RoundRect,0.218750X0.256250X-0.218750X0.256250X0.218750X-0.256250X0.218750X-0.256250X-0.218750X0*%
%ADD19RoundRect,0.237500X0.237500X-0.250000X0.237500X0.250000X-0.237500X0.250000X-0.237500X-0.250000X0*%
%ADD20RoundRect,0.237500X-0.237500X0.250000X-0.237500X-0.250000X0.237500X-0.250000X0.237500X0.250000X0*%
%ADD21RoundRect,0.225000X-0.225000X-0.250000X0.225000X-0.250000X0.225000X0.250000X-0.225000X0.250000X0*%
%ADD22RoundRect,0.150000X-0.875000X-0.150000X0.875000X-0.150000X0.875000X0.150000X-0.875000X0.150000X0*%
%ADD23R,1.350000X1.350000*%
%ADD24C,1.350000*%
%ADD25FreePoly0,0.000000*%
%ADD26FreePoly1,0.000000*%
%ADD27C,2.304000*%
%ADD28RoundRect,0.102000X-1.050000X1.050000X-1.050000X-1.050000X1.050000X-1.050000X1.050000X1.050000X0*%
%ADD29RoundRect,0.237500X0.250000X0.237500X-0.250000X0.237500X-0.250000X-0.237500X0.250000X-0.237500X0*%
%ADD30RoundRect,0.102000X-0.635000X-0.279400X0.635000X-0.279400X0.635000X0.279400X-0.635000X0.279400X0*%
%ADD31RoundRect,0.237500X-0.250000X-0.237500X0.250000X-0.237500X0.250000X0.237500X-0.250000X0.237500X0*%
%ADD32RoundRect,0.225000X0.250000X-0.225000X0.250000X0.225000X-0.250000X0.225000X-0.250000X-0.225000X0*%
%ADD33C,1.000000*%
%ADD34RoundRect,0.102000X0.350000X0.950000X-0.350000X0.950000X-0.350000X-0.950000X0.350000X-0.950000X0*%
%ADD35RoundRect,0.102000X0.850000X0.900000X-0.850000X0.900000X-0.850000X-0.900000X0.850000X-0.900000X0*%
%ADD36RoundRect,0.102000X0.700000X1.300000X-0.700000X1.300000X-0.700000X-1.300000X0.700000X-1.300000X0*%
%ADD37RoundRect,0.225000X-0.250000X0.225000X-0.250000X-0.225000X0.250000X-0.225000X0.250000X0.225000X0*%
%ADD38R,1.600000X2.000000*%
%ADD39O,1.600000X2.000000*%
G04 APERTURE END LIST*
G36*
X117483000Y-126250000D02*
G01*
X119933000Y-126250000D01*
X119933000Y-127750000D01*
X117483000Y-127750000D01*
X117483000Y-126250000D01*
G37*
D10*
X123775000Y-97500000D03*
X122225000Y-97500000D03*
D11*
X128778000Y-111252000D03*
D12*
X128778000Y-113792000D03*
D13*
X128778000Y-116332000D03*
D14*
X113821600Y-126598500D03*
X113821600Y-124058500D03*
X113821600Y-121518500D03*
X113821600Y-118978500D03*
X113821600Y-116438500D03*
X113821600Y-113898500D03*
X113821600Y-111358500D03*
X97656600Y-111358500D03*
X97656600Y-113898500D03*
X97656600Y-116438500D03*
X97656600Y-118978500D03*
X97656600Y-121518500D03*
X97656600Y-124058500D03*
X97656600Y-126598500D03*
D15*
X107548000Y-113446800D03*
X105008000Y-113446800D03*
D16*
X107040000Y-127620000D03*
X104500000Y-127620000D03*
X107040000Y-125080000D03*
X104500000Y-125080000D03*
X107040000Y-122540000D03*
X104500000Y-122540000D03*
X107040000Y-120000000D03*
X104592600Y-120056800D03*
D17*
X83000000Y-127500000D03*
D18*
X83500000Y-93787500D03*
X83500000Y-92212500D03*
D19*
X120000000Y-102412500D03*
X120000000Y-100587500D03*
X122000000Y-102412500D03*
X122000000Y-100587500D03*
D20*
X90000000Y-109087500D03*
X90000000Y-110912500D03*
D21*
X95618000Y-98552000D03*
X97168000Y-98552000D03*
D17*
X84000000Y-103500000D03*
D22*
X99871000Y-97282000D03*
X99871000Y-98552000D03*
X99871000Y-99822000D03*
X99871000Y-101092000D03*
X99871000Y-102362000D03*
X99871000Y-103632000D03*
X99871000Y-104902000D03*
X99871000Y-106172000D03*
X109171000Y-106172000D03*
X109171000Y-104902000D03*
X109171000Y-103632000D03*
X109171000Y-102362000D03*
X109171000Y-101092000D03*
X109171000Y-99822000D03*
X109171000Y-98552000D03*
X109171000Y-97282000D03*
D23*
X89916000Y-118888000D03*
D24*
X89916000Y-120888000D03*
D19*
X116000000Y-102412500D03*
X116000000Y-100587500D03*
D17*
X86175000Y-118373000D03*
D25*
X117983000Y-127000000D03*
D26*
X119433000Y-127000000D03*
D27*
X89500000Y-59520000D03*
D28*
X89500000Y-62060000D03*
D27*
X104740000Y-87460000D03*
X104740000Y-84920000D03*
X89500000Y-87460000D03*
D28*
X89500000Y-92540000D03*
D27*
X104740000Y-90000000D03*
D28*
X104740000Y-92540000D03*
D27*
X104740000Y-82380000D03*
X89500000Y-90000000D03*
D29*
X90412500Y-97000000D03*
X88587500Y-97000000D03*
X90412500Y-99000000D03*
X88587500Y-99000000D03*
D30*
X117704600Y-113560200D03*
X117704600Y-114500000D03*
X117704600Y-115439800D03*
X120295400Y-115439800D03*
X120295400Y-113560200D03*
D21*
X118225000Y-111000000D03*
X119775000Y-111000000D03*
D20*
X86000000Y-92087500D03*
X86000000Y-93912500D03*
D31*
X123087500Y-111000000D03*
X124912500Y-111000000D03*
D19*
X124000000Y-102412500D03*
X124000000Y-100587500D03*
D17*
X84000000Y-100500000D03*
X86500000Y-127500000D03*
X90000000Y-127500000D03*
D19*
X128000000Y-102500000D03*
X128000000Y-100675000D03*
D17*
X124500000Y-108500000D03*
D19*
X118000000Y-102412500D03*
X118000000Y-100587500D03*
D32*
X125984000Y-115583000D03*
X125984000Y-114033000D03*
D19*
X122500000Y-120912500D03*
X122500000Y-119087500D03*
D20*
X90000000Y-113087500D03*
X90000000Y-114912500D03*
D17*
X84000000Y-97500000D03*
D33*
X125750000Y-83600000D03*
X117750000Y-83600000D03*
D34*
X118550000Y-94650000D03*
X119650000Y-94650000D03*
X120750000Y-94650000D03*
X121850000Y-94650000D03*
X122950000Y-94650000D03*
X124050000Y-94650000D03*
X125150000Y-94650000D03*
X126250000Y-94650000D03*
X127350000Y-94650000D03*
D35*
X128900000Y-93600000D03*
X113400000Y-93600000D03*
D36*
X128650000Y-84000000D03*
X112950000Y-84000000D03*
D19*
X126000000Y-102412500D03*
X126000000Y-100587500D03*
D17*
X86175000Y-115373000D03*
D19*
X120000000Y-120927500D03*
X120000000Y-119102500D03*
D37*
X94500000Y-124225000D03*
X94500000Y-125775000D03*
D38*
X113411000Y-103378000D03*
D39*
X92911000Y-103378000D03*
M02*

View File

@@ -0,0 +1,215 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,9.0.3*
G04 #@! TF.CreationDate,2025-08-23T15:15:08+02:00*
G04 #@! TF.ProjectId,fw-anwesenheit,66772d61-6e77-4657-9365-6e686569742e,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Paste,Top*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 9.0.3) date 2025-08-23 15:15:08*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 Aperture macros list*
%AMRoundRect*
0 Rectangle with rounded corners*
0 $1 Rounding radius*
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
0 Add a 4 corners polygon primitive as box body*
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
0 Add four circle primitives for the rounded corners*
1,1,$1+$1,$2,$3*
1,1,$1+$1,$4,$5*
1,1,$1+$1,$6,$7*
1,1,$1+$1,$8,$9*
0 Add four rect primitives between the rounded corners*
20,1,$1+$1,$2,$3,$4,$5,0*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
%AMOutline5P*
0 Free polygon, 5 corners , with rotation*
0 The origin of the aperture is its center*
0 number of corners: always 5*
0 $1 to $10 corner X, Y*
0 $11 Rotation angle, in degrees counterclockwise*
0 create outline with 5 corners*
4,1,5,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$1,$2,$11*%
%AMOutline6P*
0 Free polygon, 6 corners , with rotation*
0 The origin of the aperture is its center*
0 number of corners: always 6*
0 $1 to $12 corner X, Y*
0 $13 Rotation angle, in degrees counterclockwise*
0 create outline with 6 corners*
4,1,6,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$1,$2,$13*%
%AMOutline7P*
0 Free polygon, 7 corners , with rotation*
0 The origin of the aperture is its center*
0 number of corners: always 7*
0 $1 to $14 corner X, Y*
0 $15 Rotation angle, in degrees counterclockwise*
0 create outline with 7 corners*
4,1,7,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$1,$2,$15*%
%AMOutline8P*
0 Free polygon, 8 corners , with rotation*
0 The origin of the aperture is its center*
0 number of corners: always 8*
0 $1 to $16 corner X, Y*
0 $17 Rotation angle, in degrees counterclockwise*
0 create outline with 8 corners*
4,1,8,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$1,$2,$17*%
G04 Aperture macros list end*
%ADD10RoundRect,0.225000X0.225000X0.250000X-0.225000X0.250000X-0.225000X-0.250000X0.225000X-0.250000X0*%
%ADD11Outline5P,-1.250000X0.510000X-0.910000X0.850000X1.250000X0.850000X1.250000X-0.850000X-1.250000X-0.850000X0.000000*%
%ADD12RoundRect,0.170000X-1.080000X-0.680000X1.080000X-0.680000X1.080000X0.680000X-1.080000X0.680000X0*%
%ADD13RoundRect,0.250000X-1.000000X-0.600000X1.000000X-0.600000X1.000000X0.600000X-1.000000X0.600000X0*%
%ADD14RoundRect,0.500000X0.875000X0.500000X-0.875000X0.500000X-0.875000X-0.500000X0.875000X-0.500000X0*%
%ADD15RoundRect,0.275000X0.275000X-0.975000X0.275000X0.975000X-0.275000X0.975000X-0.275000X-0.975000X0*%
%ADD16C,1.700000*%
%ADD17RoundRect,0.218750X0.256250X-0.218750X0.256250X0.218750X-0.256250X0.218750X-0.256250X-0.218750X0*%
%ADD18RoundRect,0.237500X0.237500X-0.250000X0.237500X0.250000X-0.237500X0.250000X-0.237500X-0.250000X0*%
%ADD19RoundRect,0.237500X-0.237500X0.250000X-0.237500X-0.250000X0.237500X-0.250000X0.237500X0.250000X0*%
%ADD20RoundRect,0.225000X-0.225000X-0.250000X0.225000X-0.250000X0.225000X0.250000X-0.225000X0.250000X0*%
%ADD21RoundRect,0.150000X-0.875000X-0.150000X0.875000X-0.150000X0.875000X0.150000X-0.875000X0.150000X0*%
%ADD22RoundRect,0.237500X0.250000X0.237500X-0.250000X0.237500X-0.250000X-0.237500X0.250000X-0.237500X0*%
%ADD23R,1.270000X0.558800*%
%ADD24RoundRect,0.237500X-0.250000X-0.237500X0.250000X-0.237500X0.250000X0.237500X-0.250000X0.237500X0*%
%ADD25RoundRect,0.225000X0.250000X-0.225000X0.250000X0.225000X-0.250000X0.225000X-0.250000X-0.225000X0*%
%ADD26R,0.700000X1.900000*%
%ADD27R,1.700000X1.800000*%
%ADD28R,1.400000X2.600000*%
%ADD29RoundRect,0.225000X-0.250000X0.225000X-0.250000X-0.225000X0.250000X-0.225000X0.250000X0.225000X0*%
G04 APERTURE END LIST*
D10*
X123775000Y-97500000D03*
X122225000Y-97500000D03*
D11*
X128778000Y-111252000D03*
D12*
X128778000Y-113792000D03*
D13*
X128778000Y-116332000D03*
D14*
X113821600Y-126598500D03*
X113821600Y-124058500D03*
X113821600Y-121518500D03*
X113821600Y-118978500D03*
X113821600Y-116438500D03*
X113821600Y-113898500D03*
X113821600Y-111358500D03*
X97656600Y-111358500D03*
X97656600Y-113898500D03*
X97656600Y-116438500D03*
X97656600Y-118978500D03*
X97656600Y-121518500D03*
X97656600Y-124058500D03*
X97656600Y-126598500D03*
D15*
X107548000Y-113446800D03*
X105008000Y-113446800D03*
D16*
X107040000Y-127620000D03*
X104500000Y-127620000D03*
X107040000Y-125080000D03*
X104500000Y-125080000D03*
X107040000Y-122540000D03*
X104500000Y-122540000D03*
X107040000Y-120000000D03*
X104592600Y-120056800D03*
D17*
X83500000Y-93787500D03*
X83500000Y-92212500D03*
D18*
X120000000Y-102412500D03*
X120000000Y-100587500D03*
X122000000Y-102412500D03*
X122000000Y-100587500D03*
D19*
X90000000Y-109087500D03*
X90000000Y-110912500D03*
D20*
X95618000Y-98552000D03*
X97168000Y-98552000D03*
D21*
X99871000Y-97282000D03*
X99871000Y-98552000D03*
X99871000Y-99822000D03*
X99871000Y-101092000D03*
X99871000Y-102362000D03*
X99871000Y-103632000D03*
X99871000Y-104902000D03*
X99871000Y-106172000D03*
X109171000Y-106172000D03*
X109171000Y-104902000D03*
X109171000Y-103632000D03*
X109171000Y-102362000D03*
X109171000Y-101092000D03*
X109171000Y-99822000D03*
X109171000Y-98552000D03*
X109171000Y-97282000D03*
D18*
X116000000Y-102412500D03*
X116000000Y-100587500D03*
D22*
X90412500Y-97000000D03*
X88587500Y-97000000D03*
X90412500Y-99000000D03*
X88587500Y-99000000D03*
D23*
X117704600Y-113560200D03*
X117704600Y-114500000D03*
X117704600Y-115439800D03*
X120295400Y-115439800D03*
X120295400Y-113560200D03*
D20*
X118225000Y-111000000D03*
X119775000Y-111000000D03*
D19*
X86000000Y-92087500D03*
X86000000Y-93912500D03*
D24*
X123087500Y-111000000D03*
X124912500Y-111000000D03*
D18*
X124000000Y-102412500D03*
X124000000Y-100587500D03*
X128000000Y-102500000D03*
X128000000Y-100675000D03*
X118000000Y-102412500D03*
X118000000Y-100587500D03*
D25*
X125984000Y-115583000D03*
X125984000Y-114033000D03*
D18*
X122500000Y-120912500D03*
X122500000Y-119087500D03*
D19*
X90000000Y-113087500D03*
X90000000Y-114912500D03*
D26*
X118550000Y-94650000D03*
X119650000Y-94650000D03*
X120750000Y-94650000D03*
X121850000Y-94650000D03*
X122950000Y-94650000D03*
X124050000Y-94650000D03*
X125150000Y-94650000D03*
X126250000Y-94650000D03*
X127350000Y-94650000D03*
D27*
X128900000Y-93600000D03*
X113400000Y-93600000D03*
D28*
X128650000Y-84000000D03*
X112950000Y-84000000D03*
D18*
X126000000Y-102412500D03*
X126000000Y-100587500D03*
X120000000Y-120927500D03*
X120000000Y-119102500D03*
D29*
X94500000Y-124225000D03*
X94500000Y-125775000D03*
M02*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,996 @@
%TF.GenerationSoftware,KiCad,Pcbnew,9.0.3*%
%TF.CreationDate,2025-08-23T15:15:25+02:00*%
%TF.ProjectId,fw-anwesenheit,66772d61-6e77-4657-9365-6e686569742e,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Drillmap*%
%TF.FilePolarity,Positive*%
%FSLAX45Y45*%
G04 Gerber Fmt 4.5, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 9.0.3) date 2025-08-23 15:15:25*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%ADD10C,1.025000*%
%ADD11C,0.050000*%
%ADD12C,0.010000*%
%ADD13C,0.200000*%
%ADD14C,0.100000*%
G04 APERTURE END LIST*
D10*
X8551250Y-12350000D02*
G75*
G02*
X8448750Y-12350000I-51250J0D01*
G01*
X8448750Y-12350000D02*
G75*
G02*
X8551250Y-12350000I51250J0D01*
G01*
X8551250Y-8500000D02*
G75*
G02*
X8448750Y-8500000I-51250J0D01*
G01*
X8448750Y-8500000D02*
G75*
G02*
X8551250Y-8500000I51250J0D01*
G01*
D11*
X8104500Y-7950200D02*
X13104500Y-7950200D01*
X13104500Y-12950200D01*
X8104500Y-12950200D01*
X8104500Y-7950200D01*
D10*
X12751250Y-12350000D02*
G75*
G02*
X12648750Y-12350000I-51250J0D01*
G01*
X12648750Y-12350000D02*
G75*
G02*
X12751250Y-12350000I51250J0D01*
G01*
X10951250Y-8500000D02*
G75*
G02*
X10848750Y-8500000I-51250J0D01*
G01*
X10848750Y-8500000D02*
G75*
G02*
X10951250Y-8500000I51250J0D01*
G01*
D12*
X11740000Y-8310000D02*
X11810000Y-8310000D01*
X11810000Y-8410000D02*
X11740000Y-8410000D01*
X11690000Y-8360000D02*
G75*
G02*
X11740000Y-8310000I50000J0D01*
G01*
X11740000Y-8410000D02*
G75*
G02*
X11690000Y-8360000I0J50000D01*
G01*
X11810000Y-8310000D02*
G75*
G02*
X11860000Y-8360000I0J-50000D01*
G01*
X11860000Y-8360000D02*
G75*
G02*
X11810000Y-8410000I-50000J0D01*
G01*
D13*
D14*
X11725000Y-8310000D02*
X11825000Y-8410000D01*
X11825000Y-8310000D02*
X11725000Y-8410000D01*
X12525000Y-8310000D02*
X12625000Y-8410000D01*
X12625000Y-8310000D02*
X12525000Y-8410000D01*
D13*
X8362777Y-13264184D02*
X8362777Y-13064184D01*
X8362777Y-13064184D02*
X8410396Y-13064184D01*
X8410396Y-13064184D02*
X8438967Y-13073708D01*
X8438967Y-13073708D02*
X8458015Y-13092755D01*
X8458015Y-13092755D02*
X8467539Y-13111803D01*
X8467539Y-13111803D02*
X8477063Y-13149898D01*
X8477063Y-13149898D02*
X8477063Y-13178469D01*
X8477063Y-13178469D02*
X8467539Y-13216565D01*
X8467539Y-13216565D02*
X8458015Y-13235612D01*
X8458015Y-13235612D02*
X8438967Y-13254660D01*
X8438967Y-13254660D02*
X8410396Y-13264184D01*
X8410396Y-13264184D02*
X8362777Y-13264184D01*
X8562777Y-13264184D02*
X8562777Y-13130850D01*
X8562777Y-13168946D02*
X8572301Y-13149898D01*
X8572301Y-13149898D02*
X8581824Y-13140374D01*
X8581824Y-13140374D02*
X8600872Y-13130850D01*
X8600872Y-13130850D02*
X8619920Y-13130850D01*
X8686586Y-13264184D02*
X8686586Y-13130850D01*
X8686586Y-13064184D02*
X8677063Y-13073708D01*
X8677063Y-13073708D02*
X8686586Y-13083231D01*
X8686586Y-13083231D02*
X8696110Y-13073708D01*
X8696110Y-13073708D02*
X8686586Y-13064184D01*
X8686586Y-13064184D02*
X8686586Y-13083231D01*
X8810396Y-13264184D02*
X8791348Y-13254660D01*
X8791348Y-13254660D02*
X8781824Y-13235612D01*
X8781824Y-13235612D02*
X8781824Y-13064184D01*
X8915158Y-13264184D02*
X8896110Y-13254660D01*
X8896110Y-13254660D02*
X8886586Y-13235612D01*
X8886586Y-13235612D02*
X8886586Y-13064184D01*
X9143729Y-13264184D02*
X9143729Y-13064184D01*
X9143729Y-13064184D02*
X9210396Y-13207041D01*
X9210396Y-13207041D02*
X9277063Y-13064184D01*
X9277063Y-13064184D02*
X9277063Y-13264184D01*
X9458015Y-13264184D02*
X9458015Y-13159422D01*
X9458015Y-13159422D02*
X9448491Y-13140374D01*
X9448491Y-13140374D02*
X9429444Y-13130850D01*
X9429444Y-13130850D02*
X9391348Y-13130850D01*
X9391348Y-13130850D02*
X9372301Y-13140374D01*
X9458015Y-13254660D02*
X9438967Y-13264184D01*
X9438967Y-13264184D02*
X9391348Y-13264184D01*
X9391348Y-13264184D02*
X9372301Y-13254660D01*
X9372301Y-13254660D02*
X9362777Y-13235612D01*
X9362777Y-13235612D02*
X9362777Y-13216565D01*
X9362777Y-13216565D02*
X9372301Y-13197517D01*
X9372301Y-13197517D02*
X9391348Y-13187993D01*
X9391348Y-13187993D02*
X9438967Y-13187993D01*
X9438967Y-13187993D02*
X9458015Y-13178469D01*
X9553253Y-13130850D02*
X9553253Y-13330850D01*
X9553253Y-13140374D02*
X9572301Y-13130850D01*
X9572301Y-13130850D02*
X9610396Y-13130850D01*
X9610396Y-13130850D02*
X9629444Y-13140374D01*
X9629444Y-13140374D02*
X9638967Y-13149898D01*
X9638967Y-13149898D02*
X9648491Y-13168946D01*
X9648491Y-13168946D02*
X9648491Y-13226088D01*
X9648491Y-13226088D02*
X9638967Y-13245136D01*
X9638967Y-13245136D02*
X9629444Y-13254660D01*
X9629444Y-13254660D02*
X9610396Y-13264184D01*
X9610396Y-13264184D02*
X9572301Y-13264184D01*
X9572301Y-13264184D02*
X9553253Y-13254660D01*
X9734205Y-13245136D02*
X9743729Y-13254660D01*
X9743729Y-13254660D02*
X9734205Y-13264184D01*
X9734205Y-13264184D02*
X9724682Y-13254660D01*
X9724682Y-13254660D02*
X9734205Y-13245136D01*
X9734205Y-13245136D02*
X9734205Y-13264184D01*
X9734205Y-13140374D02*
X9743729Y-13149898D01*
X9743729Y-13149898D02*
X9734205Y-13159422D01*
X9734205Y-13159422D02*
X9724682Y-13149898D01*
X9724682Y-13149898D02*
X9734205Y-13140374D01*
X9734205Y-13140374D02*
X9734205Y-13159422D01*
D14*
X8002000Y-13542700D02*
X8102000Y-13642700D01*
X8102000Y-13542700D02*
X8002000Y-13642700D01*
D13*
X8467539Y-13684184D02*
X8353253Y-13684184D01*
X8410396Y-13684184D02*
X8410396Y-13484184D01*
X8410396Y-13484184D02*
X8391348Y-13512755D01*
X8391348Y-13512755D02*
X8372301Y-13531803D01*
X8372301Y-13531803D02*
X8353253Y-13541327D01*
X8553253Y-13665136D02*
X8562777Y-13674660D01*
X8562777Y-13674660D02*
X8553253Y-13684184D01*
X8553253Y-13684184D02*
X8543729Y-13674660D01*
X8543729Y-13674660D02*
X8553253Y-13665136D01*
X8553253Y-13665136D02*
X8553253Y-13684184D01*
X8686586Y-13484184D02*
X8705634Y-13484184D01*
X8705634Y-13484184D02*
X8724682Y-13493708D01*
X8724682Y-13493708D02*
X8734205Y-13503231D01*
X8734205Y-13503231D02*
X8743729Y-13522279D01*
X8743729Y-13522279D02*
X8753253Y-13560374D01*
X8753253Y-13560374D02*
X8753253Y-13607993D01*
X8753253Y-13607993D02*
X8743729Y-13646088D01*
X8743729Y-13646088D02*
X8734205Y-13665136D01*
X8734205Y-13665136D02*
X8724682Y-13674660D01*
X8724682Y-13674660D02*
X8705634Y-13684184D01*
X8705634Y-13684184D02*
X8686586Y-13684184D01*
X8686586Y-13684184D02*
X8667539Y-13674660D01*
X8667539Y-13674660D02*
X8658015Y-13665136D01*
X8658015Y-13665136D02*
X8648491Y-13646088D01*
X8648491Y-13646088D02*
X8638967Y-13607993D01*
X8638967Y-13607993D02*
X8638967Y-13560374D01*
X8638967Y-13560374D02*
X8648491Y-13522279D01*
X8648491Y-13522279D02*
X8658015Y-13503231D01*
X8658015Y-13503231D02*
X8667539Y-13493708D01*
X8667539Y-13493708D02*
X8686586Y-13484184D01*
X8877063Y-13484184D02*
X8896110Y-13484184D01*
X8896110Y-13484184D02*
X8915158Y-13493708D01*
X8915158Y-13493708D02*
X8924682Y-13503231D01*
X8924682Y-13503231D02*
X8934205Y-13522279D01*
X8934205Y-13522279D02*
X8943729Y-13560374D01*
X8943729Y-13560374D02*
X8943729Y-13607993D01*
X8943729Y-13607993D02*
X8934205Y-13646088D01*
X8934205Y-13646088D02*
X8924682Y-13665136D01*
X8924682Y-13665136D02*
X8915158Y-13674660D01*
X8915158Y-13674660D02*
X8896110Y-13684184D01*
X8896110Y-13684184D02*
X8877063Y-13684184D01*
X8877063Y-13684184D02*
X8858015Y-13674660D01*
X8858015Y-13674660D02*
X8848491Y-13665136D01*
X8848491Y-13665136D02*
X8838967Y-13646088D01*
X8838967Y-13646088D02*
X8829444Y-13607993D01*
X8829444Y-13607993D02*
X8829444Y-13560374D01*
X8829444Y-13560374D02*
X8838967Y-13522279D01*
X8838967Y-13522279D02*
X8848491Y-13503231D01*
X8848491Y-13503231D02*
X8858015Y-13493708D01*
X8858015Y-13493708D02*
X8877063Y-13484184D01*
X9067539Y-13484184D02*
X9086586Y-13484184D01*
X9086586Y-13484184D02*
X9105634Y-13493708D01*
X9105634Y-13493708D02*
X9115158Y-13503231D01*
X9115158Y-13503231D02*
X9124682Y-13522279D01*
X9124682Y-13522279D02*
X9134205Y-13560374D01*
X9134205Y-13560374D02*
X9134205Y-13607993D01*
X9134205Y-13607993D02*
X9124682Y-13646088D01*
X9124682Y-13646088D02*
X9115158Y-13665136D01*
X9115158Y-13665136D02*
X9105634Y-13674660D01*
X9105634Y-13674660D02*
X9086586Y-13684184D01*
X9086586Y-13684184D02*
X9067539Y-13684184D01*
X9067539Y-13684184D02*
X9048491Y-13674660D01*
X9048491Y-13674660D02*
X9038967Y-13665136D01*
X9038967Y-13665136D02*
X9029444Y-13646088D01*
X9029444Y-13646088D02*
X9019920Y-13607993D01*
X9019920Y-13607993D02*
X9019920Y-13560374D01*
X9019920Y-13560374D02*
X9029444Y-13522279D01*
X9029444Y-13522279D02*
X9038967Y-13503231D01*
X9038967Y-13503231D02*
X9048491Y-13493708D01*
X9048491Y-13493708D02*
X9067539Y-13484184D01*
X9219920Y-13684184D02*
X9219920Y-13550850D01*
X9219920Y-13569898D02*
X9229444Y-13560374D01*
X9229444Y-13560374D02*
X9248491Y-13550850D01*
X9248491Y-13550850D02*
X9277063Y-13550850D01*
X9277063Y-13550850D02*
X9296110Y-13560374D01*
X9296110Y-13560374D02*
X9305634Y-13579422D01*
X9305634Y-13579422D02*
X9305634Y-13684184D01*
X9305634Y-13579422D02*
X9315158Y-13560374D01*
X9315158Y-13560374D02*
X9334205Y-13550850D01*
X9334205Y-13550850D02*
X9362777Y-13550850D01*
X9362777Y-13550850D02*
X9381825Y-13560374D01*
X9381825Y-13560374D02*
X9391348Y-13579422D01*
X9391348Y-13579422D02*
X9391348Y-13684184D01*
X9486586Y-13684184D02*
X9486586Y-13550850D01*
X9486586Y-13569898D02*
X9496110Y-13560374D01*
X9496110Y-13560374D02*
X9515158Y-13550850D01*
X9515158Y-13550850D02*
X9543729Y-13550850D01*
X9543729Y-13550850D02*
X9562777Y-13560374D01*
X9562777Y-13560374D02*
X9572301Y-13579422D01*
X9572301Y-13579422D02*
X9572301Y-13684184D01*
X9572301Y-13579422D02*
X9581825Y-13560374D01*
X9581825Y-13560374D02*
X9600872Y-13550850D01*
X9600872Y-13550850D02*
X9629444Y-13550850D01*
X9629444Y-13550850D02*
X9648491Y-13560374D01*
X9648491Y-13560374D02*
X9658015Y-13579422D01*
X9658015Y-13579422D02*
X9658015Y-13684184D01*
X10048491Y-13474660D02*
X9877063Y-13731803D01*
X10305634Y-13484184D02*
X10324682Y-13484184D01*
X10324682Y-13484184D02*
X10343729Y-13493708D01*
X10343729Y-13493708D02*
X10353253Y-13503231D01*
X10353253Y-13503231D02*
X10362777Y-13522279D01*
X10362777Y-13522279D02*
X10372301Y-13560374D01*
X10372301Y-13560374D02*
X10372301Y-13607993D01*
X10372301Y-13607993D02*
X10362777Y-13646088D01*
X10362777Y-13646088D02*
X10353253Y-13665136D01*
X10353253Y-13665136D02*
X10343729Y-13674660D01*
X10343729Y-13674660D02*
X10324682Y-13684184D01*
X10324682Y-13684184D02*
X10305634Y-13684184D01*
X10305634Y-13684184D02*
X10286587Y-13674660D01*
X10286587Y-13674660D02*
X10277063Y-13665136D01*
X10277063Y-13665136D02*
X10267539Y-13646088D01*
X10267539Y-13646088D02*
X10258015Y-13607993D01*
X10258015Y-13607993D02*
X10258015Y-13560374D01*
X10258015Y-13560374D02*
X10267539Y-13522279D01*
X10267539Y-13522279D02*
X10277063Y-13503231D01*
X10277063Y-13503231D02*
X10286587Y-13493708D01*
X10286587Y-13493708D02*
X10305634Y-13484184D01*
X10458015Y-13665136D02*
X10467539Y-13674660D01*
X10467539Y-13674660D02*
X10458015Y-13684184D01*
X10458015Y-13684184D02*
X10448491Y-13674660D01*
X10448491Y-13674660D02*
X10458015Y-13665136D01*
X10458015Y-13665136D02*
X10458015Y-13684184D01*
X10591348Y-13484184D02*
X10610396Y-13484184D01*
X10610396Y-13484184D02*
X10629444Y-13493708D01*
X10629444Y-13493708D02*
X10638968Y-13503231D01*
X10638968Y-13503231D02*
X10648491Y-13522279D01*
X10648491Y-13522279D02*
X10658015Y-13560374D01*
X10658015Y-13560374D02*
X10658015Y-13607993D01*
X10658015Y-13607993D02*
X10648491Y-13646088D01*
X10648491Y-13646088D02*
X10638968Y-13665136D01*
X10638968Y-13665136D02*
X10629444Y-13674660D01*
X10629444Y-13674660D02*
X10610396Y-13684184D01*
X10610396Y-13684184D02*
X10591348Y-13684184D01*
X10591348Y-13684184D02*
X10572301Y-13674660D01*
X10572301Y-13674660D02*
X10562777Y-13665136D01*
X10562777Y-13665136D02*
X10553253Y-13646088D01*
X10553253Y-13646088D02*
X10543729Y-13607993D01*
X10543729Y-13607993D02*
X10543729Y-13560374D01*
X10543729Y-13560374D02*
X10553253Y-13522279D01*
X10553253Y-13522279D02*
X10562777Y-13503231D01*
X10562777Y-13503231D02*
X10572301Y-13493708D01*
X10572301Y-13493708D02*
X10591348Y-13484184D01*
X10724682Y-13484184D02*
X10848491Y-13484184D01*
X10848491Y-13484184D02*
X10781825Y-13560374D01*
X10781825Y-13560374D02*
X10810396Y-13560374D01*
X10810396Y-13560374D02*
X10829444Y-13569898D01*
X10829444Y-13569898D02*
X10838968Y-13579422D01*
X10838968Y-13579422D02*
X10848491Y-13598469D01*
X10848491Y-13598469D02*
X10848491Y-13646088D01*
X10848491Y-13646088D02*
X10838968Y-13665136D01*
X10838968Y-13665136D02*
X10829444Y-13674660D01*
X10829444Y-13674660D02*
X10810396Y-13684184D01*
X10810396Y-13684184D02*
X10753253Y-13684184D01*
X10753253Y-13684184D02*
X10734206Y-13674660D01*
X10734206Y-13674660D02*
X10724682Y-13665136D01*
X10943729Y-13684184D02*
X10981825Y-13684184D01*
X10981825Y-13684184D02*
X11000872Y-13674660D01*
X11000872Y-13674660D02*
X11010396Y-13665136D01*
X11010396Y-13665136D02*
X11029444Y-13636565D01*
X11029444Y-13636565D02*
X11038968Y-13598469D01*
X11038968Y-13598469D02*
X11038968Y-13522279D01*
X11038968Y-13522279D02*
X11029444Y-13503231D01*
X11029444Y-13503231D02*
X11019920Y-13493708D01*
X11019920Y-13493708D02*
X11000872Y-13484184D01*
X11000872Y-13484184D02*
X10962777Y-13484184D01*
X10962777Y-13484184D02*
X10943729Y-13493708D01*
X10943729Y-13493708D02*
X10934206Y-13503231D01*
X10934206Y-13503231D02*
X10924682Y-13522279D01*
X10924682Y-13522279D02*
X10924682Y-13569898D01*
X10924682Y-13569898D02*
X10934206Y-13588946D01*
X10934206Y-13588946D02*
X10943729Y-13598469D01*
X10943729Y-13598469D02*
X10962777Y-13607993D01*
X10962777Y-13607993D02*
X11000872Y-13607993D01*
X11000872Y-13607993D02*
X11019920Y-13598469D01*
X11019920Y-13598469D02*
X11029444Y-13588946D01*
X11029444Y-13588946D02*
X11038968Y-13569898D01*
X11210396Y-13550850D02*
X11210396Y-13684184D01*
X11162777Y-13474660D02*
X11115158Y-13617517D01*
X11115158Y-13617517D02*
X11238967Y-13617517D01*
X11305634Y-13484184D02*
X11305634Y-13522279D01*
X11381825Y-13484184D02*
X11381825Y-13522279D01*
X11677063Y-13760374D02*
X11667539Y-13750850D01*
X11667539Y-13750850D02*
X11648491Y-13722279D01*
X11648491Y-13722279D02*
X11638968Y-13703231D01*
X11638968Y-13703231D02*
X11629444Y-13674660D01*
X11629444Y-13674660D02*
X11619920Y-13627041D01*
X11619920Y-13627041D02*
X11619920Y-13588946D01*
X11619920Y-13588946D02*
X11629444Y-13541327D01*
X11629444Y-13541327D02*
X11638968Y-13512755D01*
X11638968Y-13512755D02*
X11648491Y-13493708D01*
X11648491Y-13493708D02*
X11667539Y-13465136D01*
X11667539Y-13465136D02*
X11677063Y-13455612D01*
X11743729Y-13503231D02*
X11753253Y-13493708D01*
X11753253Y-13493708D02*
X11772301Y-13484184D01*
X11772301Y-13484184D02*
X11819920Y-13484184D01*
X11819920Y-13484184D02*
X11838968Y-13493708D01*
X11838968Y-13493708D02*
X11848491Y-13503231D01*
X11848491Y-13503231D02*
X11858015Y-13522279D01*
X11858015Y-13522279D02*
X11858015Y-13541327D01*
X11858015Y-13541327D02*
X11848491Y-13569898D01*
X11848491Y-13569898D02*
X11734206Y-13684184D01*
X11734206Y-13684184D02*
X11858015Y-13684184D01*
X12096110Y-13684184D02*
X12096110Y-13484184D01*
X12181825Y-13684184D02*
X12181825Y-13579422D01*
X12181825Y-13579422D02*
X12172301Y-13560374D01*
X12172301Y-13560374D02*
X12153253Y-13550850D01*
X12153253Y-13550850D02*
X12124682Y-13550850D01*
X12124682Y-13550850D02*
X12105634Y-13560374D01*
X12105634Y-13560374D02*
X12096110Y-13569898D01*
X12305634Y-13684184D02*
X12286587Y-13674660D01*
X12286587Y-13674660D02*
X12277063Y-13665136D01*
X12277063Y-13665136D02*
X12267539Y-13646088D01*
X12267539Y-13646088D02*
X12267539Y-13588946D01*
X12267539Y-13588946D02*
X12277063Y-13569898D01*
X12277063Y-13569898D02*
X12286587Y-13560374D01*
X12286587Y-13560374D02*
X12305634Y-13550850D01*
X12305634Y-13550850D02*
X12334206Y-13550850D01*
X12334206Y-13550850D02*
X12353253Y-13560374D01*
X12353253Y-13560374D02*
X12362777Y-13569898D01*
X12362777Y-13569898D02*
X12372301Y-13588946D01*
X12372301Y-13588946D02*
X12372301Y-13646088D01*
X12372301Y-13646088D02*
X12362777Y-13665136D01*
X12362777Y-13665136D02*
X12353253Y-13674660D01*
X12353253Y-13674660D02*
X12334206Y-13684184D01*
X12334206Y-13684184D02*
X12305634Y-13684184D01*
X12486587Y-13684184D02*
X12467539Y-13674660D01*
X12467539Y-13674660D02*
X12458015Y-13655612D01*
X12458015Y-13655612D02*
X12458015Y-13484184D01*
X12638968Y-13674660D02*
X12619920Y-13684184D01*
X12619920Y-13684184D02*
X12581825Y-13684184D01*
X12581825Y-13684184D02*
X12562777Y-13674660D01*
X12562777Y-13674660D02*
X12553253Y-13655612D01*
X12553253Y-13655612D02*
X12553253Y-13579422D01*
X12553253Y-13579422D02*
X12562777Y-13560374D01*
X12562777Y-13560374D02*
X12581825Y-13550850D01*
X12581825Y-13550850D02*
X12619920Y-13550850D01*
X12619920Y-13550850D02*
X12638968Y-13560374D01*
X12638968Y-13560374D02*
X12648491Y-13579422D01*
X12648491Y-13579422D02*
X12648491Y-13598469D01*
X12648491Y-13598469D02*
X12553253Y-13617517D01*
X12724682Y-13674660D02*
X12743730Y-13684184D01*
X12743730Y-13684184D02*
X12781825Y-13684184D01*
X12781825Y-13684184D02*
X12800872Y-13674660D01*
X12800872Y-13674660D02*
X12810396Y-13655612D01*
X12810396Y-13655612D02*
X12810396Y-13646088D01*
X12810396Y-13646088D02*
X12800872Y-13627041D01*
X12800872Y-13627041D02*
X12781825Y-13617517D01*
X12781825Y-13617517D02*
X12753253Y-13617517D01*
X12753253Y-13617517D02*
X12734206Y-13607993D01*
X12734206Y-13607993D02*
X12724682Y-13588946D01*
X12724682Y-13588946D02*
X12724682Y-13579422D01*
X12724682Y-13579422D02*
X12734206Y-13560374D01*
X12734206Y-13560374D02*
X12753253Y-13550850D01*
X12753253Y-13550850D02*
X12781825Y-13550850D01*
X12781825Y-13550850D02*
X12800872Y-13560374D01*
X12877063Y-13760374D02*
X12886587Y-13750850D01*
X12886587Y-13750850D02*
X12905634Y-13722279D01*
X12905634Y-13722279D02*
X12915158Y-13703231D01*
X12915158Y-13703231D02*
X12924682Y-13674660D01*
X12924682Y-13674660D02*
X12934206Y-13627041D01*
X12934206Y-13627041D02*
X12934206Y-13588946D01*
X12934206Y-13588946D02*
X12924682Y-13541327D01*
X12924682Y-13541327D02*
X12915158Y-13512755D01*
X12915158Y-13512755D02*
X12905634Y-13493708D01*
X12905634Y-13493708D02*
X12886587Y-13465136D01*
X12886587Y-13465136D02*
X12877063Y-13455612D01*
X13238968Y-13760374D02*
X13229444Y-13750850D01*
X13229444Y-13750850D02*
X13210396Y-13722279D01*
X13210396Y-13722279D02*
X13200872Y-13703231D01*
X13200872Y-13703231D02*
X13191349Y-13674660D01*
X13191349Y-13674660D02*
X13181825Y-13627041D01*
X13181825Y-13627041D02*
X13181825Y-13588946D01*
X13181825Y-13588946D02*
X13191349Y-13541327D01*
X13191349Y-13541327D02*
X13200872Y-13512755D01*
X13200872Y-13512755D02*
X13210396Y-13493708D01*
X13210396Y-13493708D02*
X13229444Y-13465136D01*
X13229444Y-13465136D02*
X13238968Y-13455612D01*
X13315158Y-13550850D02*
X13315158Y-13684184D01*
X13315158Y-13569898D02*
X13324682Y-13560374D01*
X13324682Y-13560374D02*
X13343730Y-13550850D01*
X13343730Y-13550850D02*
X13372301Y-13550850D01*
X13372301Y-13550850D02*
X13391349Y-13560374D01*
X13391349Y-13560374D02*
X13400872Y-13579422D01*
X13400872Y-13579422D02*
X13400872Y-13684184D01*
X13524682Y-13684184D02*
X13505634Y-13674660D01*
X13505634Y-13674660D02*
X13496111Y-13665136D01*
X13496111Y-13665136D02*
X13486587Y-13646088D01*
X13486587Y-13646088D02*
X13486587Y-13588946D01*
X13486587Y-13588946D02*
X13496111Y-13569898D01*
X13496111Y-13569898D02*
X13505634Y-13560374D01*
X13505634Y-13560374D02*
X13524682Y-13550850D01*
X13524682Y-13550850D02*
X13553253Y-13550850D01*
X13553253Y-13550850D02*
X13572301Y-13560374D01*
X13572301Y-13560374D02*
X13581825Y-13569898D01*
X13581825Y-13569898D02*
X13591349Y-13588946D01*
X13591349Y-13588946D02*
X13591349Y-13646088D01*
X13591349Y-13646088D02*
X13581825Y-13665136D01*
X13581825Y-13665136D02*
X13572301Y-13674660D01*
X13572301Y-13674660D02*
X13553253Y-13684184D01*
X13553253Y-13684184D02*
X13524682Y-13684184D01*
X13648492Y-13550850D02*
X13724682Y-13550850D01*
X13677063Y-13484184D02*
X13677063Y-13655612D01*
X13677063Y-13655612D02*
X13686587Y-13674660D01*
X13686587Y-13674660D02*
X13705634Y-13684184D01*
X13705634Y-13684184D02*
X13724682Y-13684184D01*
X13943730Y-13550850D02*
X13943730Y-13750850D01*
X13943730Y-13560374D02*
X13962777Y-13550850D01*
X13962777Y-13550850D02*
X14000873Y-13550850D01*
X14000873Y-13550850D02*
X14019920Y-13560374D01*
X14019920Y-13560374D02*
X14029444Y-13569898D01*
X14029444Y-13569898D02*
X14038968Y-13588946D01*
X14038968Y-13588946D02*
X14038968Y-13646088D01*
X14038968Y-13646088D02*
X14029444Y-13665136D01*
X14029444Y-13665136D02*
X14019920Y-13674660D01*
X14019920Y-13674660D02*
X14000873Y-13684184D01*
X14000873Y-13684184D02*
X13962777Y-13684184D01*
X13962777Y-13684184D02*
X13943730Y-13674660D01*
X14153253Y-13684184D02*
X14134206Y-13674660D01*
X14134206Y-13674660D02*
X14124682Y-13655612D01*
X14124682Y-13655612D02*
X14124682Y-13484184D01*
X14315158Y-13684184D02*
X14315158Y-13579422D01*
X14315158Y-13579422D02*
X14305634Y-13560374D01*
X14305634Y-13560374D02*
X14286587Y-13550850D01*
X14286587Y-13550850D02*
X14248492Y-13550850D01*
X14248492Y-13550850D02*
X14229444Y-13560374D01*
X14315158Y-13674660D02*
X14296111Y-13684184D01*
X14296111Y-13684184D02*
X14248492Y-13684184D01*
X14248492Y-13684184D02*
X14229444Y-13674660D01*
X14229444Y-13674660D02*
X14219920Y-13655612D01*
X14219920Y-13655612D02*
X14219920Y-13636565D01*
X14219920Y-13636565D02*
X14229444Y-13617517D01*
X14229444Y-13617517D02*
X14248492Y-13607993D01*
X14248492Y-13607993D02*
X14296111Y-13607993D01*
X14296111Y-13607993D02*
X14315158Y-13598469D01*
X14381825Y-13550850D02*
X14458015Y-13550850D01*
X14410396Y-13484184D02*
X14410396Y-13655612D01*
X14410396Y-13655612D02*
X14419920Y-13674660D01*
X14419920Y-13674660D02*
X14438968Y-13684184D01*
X14438968Y-13684184D02*
X14458015Y-13684184D01*
X14600873Y-13674660D02*
X14581825Y-13684184D01*
X14581825Y-13684184D02*
X14543730Y-13684184D01*
X14543730Y-13684184D02*
X14524682Y-13674660D01*
X14524682Y-13674660D02*
X14515158Y-13655612D01*
X14515158Y-13655612D02*
X14515158Y-13579422D01*
X14515158Y-13579422D02*
X14524682Y-13560374D01*
X14524682Y-13560374D02*
X14543730Y-13550850D01*
X14543730Y-13550850D02*
X14581825Y-13550850D01*
X14581825Y-13550850D02*
X14600873Y-13560374D01*
X14600873Y-13560374D02*
X14610396Y-13579422D01*
X14610396Y-13579422D02*
X14610396Y-13598469D01*
X14610396Y-13598469D02*
X14515158Y-13617517D01*
X14781825Y-13684184D02*
X14781825Y-13484184D01*
X14781825Y-13674660D02*
X14762777Y-13684184D01*
X14762777Y-13684184D02*
X14724682Y-13684184D01*
X14724682Y-13684184D02*
X14705634Y-13674660D01*
X14705634Y-13674660D02*
X14696111Y-13665136D01*
X14696111Y-13665136D02*
X14686587Y-13646088D01*
X14686587Y-13646088D02*
X14686587Y-13588946D01*
X14686587Y-13588946D02*
X14696111Y-13569898D01*
X14696111Y-13569898D02*
X14705634Y-13560374D01*
X14705634Y-13560374D02*
X14724682Y-13550850D01*
X14724682Y-13550850D02*
X14762777Y-13550850D01*
X14762777Y-13550850D02*
X14781825Y-13560374D01*
X14858015Y-13760374D02*
X14867539Y-13750850D01*
X14867539Y-13750850D02*
X14886587Y-13722279D01*
X14886587Y-13722279D02*
X14896111Y-13703231D01*
X14896111Y-13703231D02*
X14905634Y-13674660D01*
X14905634Y-13674660D02*
X14915158Y-13627041D01*
X14915158Y-13627041D02*
X14915158Y-13588946D01*
X14915158Y-13588946D02*
X14905634Y-13541327D01*
X14905634Y-13541327D02*
X14896111Y-13512755D01*
X14896111Y-13512755D02*
X14886587Y-13493708D01*
X14886587Y-13493708D02*
X14867539Y-13465136D01*
X14867539Y-13465136D02*
X14858015Y-13455612D01*
M02*

View File

@@ -0,0 +1,17 @@
M48
; DRILL file {KiCad 9.0.3} date 2025-08-23T15:15:25+0200
; FORMAT={-:-/ absolute / metric / decimal}
; #@! TF.CreationDate,2025-08-23T15:15:25+02:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,9.0.3
; #@! TF.FileFunction,NonPlated,1,6,NPTH
FMAT,2
METRIC
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T1C1.000
%
G90
G05
T1
X117.75Y-83.6
X125.75Y-83.6
M30

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
M48
; DRILL file {KiCad 9.0.3} date 2025-08-23T15:15:25+0200
; FORMAT={-:-/ absolute / metric / decimal}
; #@! TF.CreationDate,2025-08-23T15:15:25+02:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,9.0.3
; #@! TF.FileFunction,Plated,1,6,PTH
FMAT,2
METRIC
; #@! TA.AperFunction,Plated,PTH,ViaDrill
T1C0.200
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T2C0.800
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T3C0.850
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T4C1.400
%
G90
G05
T1
X82.677Y-80.645
X83.185Y-110.49
X83.5Y-92.212
X83.95Y-112.96
X84.328Y-126.873
X84.582Y-116.967
X84.709Y-80.645
X85.979Y-97.79
X86.487Y-99.822
X86.741Y-80.645
X87.505Y-126.494
X88.011Y-117.475
X88.519Y-103.124
X88.773Y-80.645
X90.0Y-109.087
X90.0Y-114.912
X90.412Y-97.0
X90.412Y-99.0
X90.805Y-80.645
X91.694Y-127.889
X92.583Y-119.888
X92.71Y-98.044
X92.837Y-80.645
X94.488Y-124.206
X94.488Y-125.73
X94.869Y-80.645
X94.869Y-128.397
X95.25Y-113.157
X95.377Y-118.364
X95.618Y-98.552
X95.885Y-101.092
X96.774Y-120.904
X96.774Y-121.539
X96.774Y-122.174
X96.774Y-123.444
X96.774Y-124.079
X96.774Y-124.714
X96.901Y-80.645
X96.901Y-128.397
X96.926Y-126.086
X96.926Y-126.644
X96.926Y-127.203
X97.168Y-98.552
X97.663Y-114.046
X97.663Y-116.332
X97.663Y-120.904
X97.663Y-121.539
X97.663Y-122.174
X97.663Y-123.444
X97.663Y-124.079
X97.663Y-124.714
X97.688Y-126.086
X97.688Y-126.644
X97.688Y-127.203
X98.044Y-102.362
X98.044Y-103.632
X98.044Y-104.902
X98.044Y-106.172
X98.552Y-94.361
X98.552Y-120.904
X98.552Y-121.539
X98.552Y-122.174
X98.552Y-123.444
X98.552Y-124.079
X98.552Y-124.714
X98.603Y-126.086
X98.603Y-126.644
X98.603Y-127.203
X98.933Y-80.645
X98.933Y-128.397
X100.965Y-80.645
X100.965Y-128.397
X102.489Y-98.552
X102.616Y-94.361
X102.743Y-123.317
X102.997Y-80.645
X102.997Y-128.397
X103.886Y-107.95
X104.267Y-119.761
X104.267Y-120.396
X104.521Y-125.095
X104.648Y-97.663
X104.902Y-119.761
X104.902Y-120.396
X105.029Y-80.645
X105.029Y-112.522
X105.029Y-113.411
X105.475Y-105.0
X107.061Y-80.645
X107.315Y-112.903
X107.315Y-113.665
X107.315Y-114.3
X107.442Y-107.95
X107.823Y-112.903
X107.823Y-113.665
X107.823Y-114.3
X108.839Y-120.396
X108.966Y-98.552
X108.966Y-112.014
X109.093Y-80.645
X109.093Y-128.397
X109.22Y-97.282
X109.791Y-110.68
X110.109Y-123.571
X110.49Y-115.189
X110.744Y-101.092
X110.744Y-102.362
X110.744Y-103.632
X110.744Y-104.902
X110.744Y-106.172
X110.871Y-123.571
X111.125Y-80.645
X111.125Y-128.397
X111.379Y-107.95
X112.903Y-83.947
X112.903Y-118.491
X113.157Y-80.645
X113.157Y-128.397
X113.411Y-93.853
X113.792Y-126.746
X113.822Y-116.438
X115.189Y-80.645
X115.189Y-128.397
X115.443Y-91.821
X115.951Y-117.094
X116.0Y-102.412
X116.078Y-118.491
X116.332Y-125.984
X116.459Y-125.476
X116.967Y-125.349
X117.221Y-80.645
X117.221Y-128.397
X117.475Y-116.84
X117.705Y-113.56
X117.705Y-115.443
X117.729Y-123.571
X118.0Y-102.412
X118.237Y-110.998
X118.364Y-123.571
X118.491Y-99.06
X118.55Y-94.65
X119.253Y-80.645
X119.253Y-128.397
X119.634Y-126.492
X119.634Y-127.0
X119.634Y-127.508
X119.65Y-94.65
X119.761Y-92.329
X119.761Y-110.998
X120.0Y-102.412
X120.0Y-121.0
X120.015Y-99.441
X120.015Y-119.253
X120.294Y-113.589
X120.75Y-94.65
X121.285Y-80.645
X121.285Y-128.397
X121.92Y-94.234
X121.92Y-95.123
X122.0Y-100.587
X122.0Y-102.412
X122.047Y-99.06
X122.174Y-97.536
X122.174Y-126.619
X122.174Y-127.381
X122.301Y-111.887
X122.301Y-115.697
X122.5Y-119.087
X122.5Y-120.912
X122.53Y-126.949
X122.936Y-94.65
X122.936Y-126.619
X122.936Y-127.381
X123.317Y-80.645
X123.317Y-128.397
X123.825Y-97.536
X124.0Y-102.412
X124.079Y-94.234
X124.079Y-94.996
X125.15Y-94.65
X125.349Y-80.645
X125.349Y-128.397
X125.73Y-92.837
X125.984Y-114.046
X125.984Y-115.57
X126.0Y-102.412
X126.25Y-94.65
X126.873Y-97.536
X127.35Y-94.65
X127.381Y-80.645
X127.381Y-128.397
X128.0Y-102.5
X128.219Y-113.284
X128.219Y-114.198
X128.27Y-115.824
X128.27Y-116.713
X128.651Y-83.947
X128.905Y-93.599
X128.981Y-113.741
X129.0Y-116.27
X129.413Y-80.645
X129.413Y-128.397
X129.642Y-113.335
X129.642Y-114.249
X129.794Y-115.824
X129.794Y-116.713
T2
X89.916Y-118.888
X89.916Y-120.888
T3
X92.911Y-103.378
X113.411Y-103.378
T4
X89.5Y-59.52
X89.5Y-62.06
X89.5Y-87.46
X89.5Y-90.0
X89.5Y-92.54
X104.74Y-82.38
X104.74Y-84.92
X104.74Y-87.46
X104.74Y-90.0
X104.74Y-92.54
M30

View File

@@ -1,23 +0,0 @@
#!/usr/bin/env bash
generate_random_hex() {
openssl rand -hex 4 | tr '[:lower:]' '[:upper:]'
}
output_hotspot_id() {
echo "[+] UID.... C1532B57"
}
trap 'output_hotspot_id' SIGUSR1
echo "Proxmark 3 mock script"
echo "Outputs a random ID every 1 to 5 seconds"
while true; do
random_id=$(generate_random_hex)
echo "[+] UID.... $random_id"
if (( RANDOM % 2 == 0 )); then
echo "[+] UID.... $random_id"
fi
sleep "$((RANDOM % 5 + 1))"
done

Binary file not shown.

Binary file not shown.

View File

@@ -1,550 +0,0 @@
#!/usr/bin/env bash
# Usage: run option -h to get help
# BT auto detection
# Shall we look for white HC-06-USB dongle ?
FINDBTDONGLE=true
# Shall we look for rfcomm interface ?
FINDBTRFCOMM=true
# Shall we look for registered BT device ? (Linux only)
FINDBTDIRECT=true
PM3PATH=$(dirname "$0")
EVALENV=""
FULLIMAGE="fullimage.elf"
BOOTIMAGE="bootrom.elf"
#Skip check if --list is used
if [ ! "$1" == "--list" ]; then
# try pm3 dirs in current repo workdir
if [ -d "$PM3PATH/client/" ]; then
if [ -x "$PM3PATH/client/proxmark3" ]; then
CLIENT="$PM3PATH/client/proxmark3"
elif [ -x "$PM3PATH/client/build/proxmark3" ]; then
CLIENT="$PM3PATH/client/build/proxmark3"
else
echo >&2 "[!!] In devel workdir but no executable found, did you compile it?"
exit 1
fi
# try install dir
elif [ -x "$PM3PATH/proxmark3" ]; then
CLIENT="$PM3PATH/proxmark3"
else
# hope it's installed somehow, still not sure where fw images and pm3.py are...
CLIENT="proxmark3"
fi
fi
# LeakSanitizer suppressions
if [ -e .lsan_suppressions ]; then
EVALENV+=" LSAN_OPTIONS=suppressions=.lsan_suppressions"
fi
if [ "$EVALENV" != "" ]; then
EVALENV="export $EVALENV"
fi
PM3LIST=()
SHOWLIST=false
function get_pm3_list_Linux {
N=$1
PM3LIST=()
if [ ! -c "/dev/tty0" ]; then
echo >&2 "[!!] Script cannot access /dev/ttyXXX files, insufficient privileges"
exit 1
fi
for DEV in $(find /dev/ttyACM* 2>/dev/null); do
if command -v udevadm >/dev/null; then
# WSL1 detection
if udevadm info -q property -n "$DEV" | grep -q "ID_VENDOR=proxmark.org"; then
PM3LIST+=("$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
fi
fi
# WSL2 with usbipd detection - doesn't report same things as WSL1
if grep -q "proxmark.org" "/sys/class/tty/${DEV#/dev/}/../../../manufacturer" 2>/dev/null; then
if echo "${PM3LIST[*]}" | grep -qv "${DEV}"; then
PM3LIST+=("$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
fi
fi
done
if $FINDBTDONGLE; then
# check if the HC-06-USB white dongle is present (still, that doesn't tell us if it's paired with a Proxmark3...)
for DEV in $(find /dev/ttyUSB* 2>/dev/null); do
if command -v udevadm >/dev/null; then
if udevadm info -q property -n "$DEV" | grep -q "ID_MODEL=CP2104_USB_to_UART_Bridge_Controller"; then
PM3LIST+=("$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
fi
else
if grep -q "DRIVER=cp210x" "/sys/class/tty/${DEV#/dev/}/../../uevent" 2>/dev/null; then
PM3LIST+=("$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
fi
fi
done
fi
if $FINDBTRFCOMM; then
# check if the MAC of a Proxmark3 was bound to a local rfcomm interface
# (on OSes without deprecated rfcomm and hcitool, the loop will be simply skipped)
for DEVMAC in $(rfcomm -a 2>/dev/null | grep " 20:19:0[45]" | sed 's/^\(.*\): \([0-9:]*\) .*/\1@\2/'); do
DEV=${DEVMAC/@*/}
MAC=${DEVMAC/*@/}
# check which are Proxmark3 and, side-effect, if they're actually present
if hcitool name "$MAC" | grep -q "PM3"; then
PM3LIST+=("/dev/$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
fi
done
fi
if $FINDBTDIRECT; then
# check if the MAC of a Proxmark3 was registered in the known devices
for MAC in $(dbus-send --system --print-reply --type=method_call --dest='org.bluez' '/' org.freedesktop.DBus.ObjectManager.GetManagedObjects 2>/dev/null|\
awk '/"Address"/{getline;gsub(/"/,"",$3);a=$3}/Name/{getline;if (/PM3_RDV4/ || /Proxmark3 SE/) print a}'); do
PM3LIST+=("bt:$MAC")
done
# we don't probe the device so there is no guarantee the device is actually present
fi
}
function get_pm3_list_macOS {
N=$1
PM3LIST=()
for DEV in $(ioreg -r -c "IOUSBHostDevice" -l | awk -F '"' '
$2=="USB Vendor Name"{b=($4=="proxmark.org")}
b==1 && $2=="IODialinDevice"{print $4}'); do
PM3LIST+=("$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
done
}
function get_pm3_list_Windows {
N=$1
PM3LIST=()
# Normal SERIAL PORTS (COM)
for DEV in $(wmic /locale:ms_409 path Win32_SerialPort Where "PNPDeviceID LIKE '%VID_9AC4&PID_4B8F%' Or PNPDeviceID LIKE '%VID_2D2D&PID_504D%'" Get DeviceID 2>/dev/null | awk -b '/^COM/{print $1}'); do
DEV=${DEV/ */}
#prevent soft bricking when using pm3-flash-all on an outdated bootloader
if [ $(basename -- "$0") = "pm3-flash-all" ]; then
line=$(wmic /locale:ms_409 path Win32_SerialPort Where "DeviceID='$DEV'" Get PNPDeviceID 2>/dev/null | awk -b '/^USB/{print $1}');
if [[ ! $line =~ ^"USB\VID_9AC4&PID_4B8F\ICEMAN" ]]; then
echo -e "\033[0;31m[!] Using pm3-flash-all on an oudated bootloader, use pm3-flash-bootrom first!"
exit 1
fi
fi
PM3LIST+=("$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
done
#BT direct SERIAL PORTS (COM)
if $FINDBTRFCOMM; then
for DEV in $(wmic /locale:ms_409 path Win32_PnPEntity Where "Caption LIKE '%Bluetooth%(COM%'" Get Name 2> /dev/null | awk -b 'match($0,/(COM[0-9]+)/,m){print m[1]}'); do
DEV=${DEV/ */}
PM3LIST+=("$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
done
fi
#white BT dongle SERIAL PORTS (COM)
if $FINDBTDONGLE; then
for DEV in $(wmic /locale:ms_409 path Win32_SerialPort Where "PNPDeviceID LIKE '%VID_10C4&PID_EA60%'" Get DeviceID 2>/dev/null | awk -b '/^COM/{print $1}'); do
DEV=${DEV/ */}
PM3LIST+=("$DEV")
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
done
fi
}
function get_pm3_list_WSL {
N=$1
PM3LIST=()
# Normal SERIAL PORTS (COM)
for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.PNPDeviceID -like '*VID_9AC4&PID_4B8F*' -or \$_.PNPDeviceID -like '*VID_2D2D&PID_504D*'} | Select -expandproperty DeviceID" 2>/dev/null); do
DEV=$(echo $DEV | tr -dc '[:print:]')
_comport=$DEV
DEV=$(echo $DEV | sed -nr 's#^COM([0-9]+)\b#/dev/ttyS\1#p')
# ttyS counterpart takes some more time to appear
if [ -e "$DEV" ]; then
#prevent soft bricking when using pm3-flash-all on an outdated bootloader
if [ $(basename -- "$0") = "pm3-flash-all" ]; then
line=$($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.DeviceID -eq '$_comport'} | Select -expandproperty PNPDeviceID" 2>/dev/null | tr -dc '[:print:]');
if [[ ! $line =~ ^"USB\VID_9AC4&PID_4B8F\ICEMAN" ]]; then
echo -e "\033[0;31m[!] Using pm3-flash-all on an oudated bootloader, use pm3-flash-bootrom first!"
exit 1
fi
fi
PM3LIST+=("$DEV")
if [ ! -w "$DEV" ]; then
echo "[!] Let's give users read/write access to $DEV"
sudo chmod 666 "$DEV"
fi
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
fi
done
#BT direct SERIAL PORTS (COM)
if $FINDBTRFCOMM; then
for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_PnPEntity | Where-Object Caption -like 'Standard Serial over Bluetooth link (COM*' | Select Name" 2> /dev/null | sed -nr 's#.*\bCOM([0-9]+)\b.*#/dev/ttyS\1#p'); do
# ttyS counterpart takes some more time to appear
if [ -e "$DEV" ]; then
PM3LIST+=("$DEV")
if [ ! -w "$DEV" ]; then
echo "[!] Let's give users read/write access to $DEV"
sudo chmod 666 "$DEV"
fi
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
fi
done
fi
#white BT dongle SERIAL PORTS (COM)
if $FINDBTDONGLE; then
for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object PNPDeviceID -like '*VID_10C4&PID_EA60*' | Select DeviceID" 2>/dev/null | sed -nr 's#^COM([0-9]+)\b#/dev/ttyS\1#p'); do
# ttyS counterpart takes some more time to appear
if [ -e "$DEV" ]; then
PM3LIST+=("$DEV")
if [ ! -w "$DEV" ]; then
echo "[!] Let's give users read/write access to $DEV"
sudo chmod 666 "$DEV"
fi
if [ ${#PM3LIST[*]} -ge "$N" ]; then
return
fi
fi
done
fi
}
SCRIPT=$(basename -- "$0")
if [ "$SCRIPT" = "pm3" ]; then
CMD() { eval "$EVALENV"; $CLIENT "$@"; }
HELP() {
cat << EOF
Quick helper script for proxmark3 client when working with a Proxmark3 device
Description:
The usage is the same as for the proxmark3 client, with the following differences:
* the correct port name will be automatically guessed;
* the script will wait for a Proxmark3 to be connected (same as option -w of the client).
You can also specify a first option -n N to access the Nth Proxmark3 connected.
To see a list of available ports, use --list.
Usage:
$SCRIPT [-n <N>] [<any other proxmark3 client option>]
$SCRIPT [--list] [-h|--help] [-hh|--helpclient]
$SCRIPT [-o|--offline]
Arguments:
-h/--help this help
-hh/--helpclient proxmark3 client help (the script will forward these options)
--list list all detected com ports
-n <N> connect device referred to the N:th number on the --list output
-o/--offline shortcut to use directly the proxmark3 client without guessing ports
Samples:
./$SCRIPT -- Auto detect/ select com port in the following order BT, USB/CDC, BT DONGLE
./$SCRIPT -p /dev/ttyACM0 -- connect to port /dev/ttyACM0
./$SCRIPT -n 2 -- use second item from the --list output
./$SCRIPT -c 'lf search' -i -- run command and stay in client once completed
EOF
}
elif [ "$SCRIPT" = "pm3-flash" ]; then
FINDBTDONGLE=false
FINDBTRFCOMM=false
FINDBTDIRECT=false
CMD() {
ARGS=("--port" "$1" "--flash")
shift;
while [ "$1" != "" ]; do
if [ "$1" == "-b" ]; then
ARGS+=("--unlock-bootloader")
elif [ "$1" == "--force" ]; then
ARGS+=("--force")
else
ARGS+=("--image" "$1")
fi
shift;
done
$CLIENT "${ARGS[@]}";
}
HELP() {
cat << EOF
Quick helper script for flashing a Proxmark3 device via USB
Description:
The usage is similar to the old proxmark3-flasher binary, except that the correct port name will be automatically guessed.
You can also specify a first option -n N to access the Nth Proxmark3 connected on USB.
If this doesn't work, you'll have to use manually the proxmark3 client, see "$CLIENT -h".
To see a list of available ports, use --list.
Usage:
$SCRIPT [-n <N>] [-b] image.elf [image.elf...]
$SCRIPT --list
Options:
-b Enable flashing of bootloader area (DANGEROUS)
Example:
$SCRIPT -b bootrom.elf fullimage.elf
EOF
}
elif [ "$SCRIPT" = "pm3-flash-all" ]; then
FINDBTDONGLE=false
FINDBTRFCOMM=false
FINDBTDIRECT=false
CMD() {
ARGS=("--port" "$1" "--flash" "--unlock-bootloader" "--image" "$BOOTIMAGE" "--image" "$FULLIMAGE")
shift;
while [ "$1" != "" ]; do
if [ "$1" == "--force" ]; then
ARGS+=("--force")
fi
shift;
done
$CLIENT "${ARGS[@]}";
}
HELP() {
cat << EOF
Quick helper script for flashing a Proxmark3 device via USB
Description:
The correct port name will be automatically guessed and the stock bootloader and firmware image will be flashed.
You can also specify a first option -n N to access the Nth Proxmark3 connected on USB.
If this doesn't work, you'll have to use manually the proxmark3 client, see "$CLIENT -h".
To see a list of available ports, use --list.
Usage:
$SCRIPT [-n <N>]
$SCRIPT --list
EOF
}
elif [ "$SCRIPT" = "pm3-flash-fullimage" ]; then
FINDBTDONGLE=false
FINDBTRFCOMM=false
FINDBTDIRECT=false
CMD() {
ARGS=("--port" "$1" "--flash" "--image" "$FULLIMAGE")
shift;
while [ "$1" != "" ]; do
if [ "$1" == "--force" ]; then
ARGS+=("--force")
fi
shift;
done
$CLIENT "${ARGS[@]}";
}
HELP() {
cat << EOF
Quick helper script for flashing a Proxmark3 device via USB
Description:
The correct port name will be automatically guessed and the stock firmware image will be flashed.
You can also specify a first option -n N to access the Nth Proxmark3 connected on USB.
If this doesn't work, you'll have to use manually the proxmark3 client, see "$CLIENT -h".
To see a list of available ports, use --list.
Usage:
$SCRIPT [-n <N>]
$SCRIPT --list
EOF
}
elif [ "$SCRIPT" = "pm3-flash-bootrom" ]; then
FINDBTDONGLE=false
FINDBTRFCOMM=false
FINDBTDIRECT=false
CMD() {
ARGS=("--port" "$1" "--flash" "--unlock-bootloader" "--image" "$BOOTIMAGE")
shift;
while [ "$1" != "" ]; do
if [ "$1" == "--force" ]; then
ARGS+=("--force")
fi
shift;
done
$CLIENT "${ARGS[@]}";
}
HELP() {
cat << EOF
Quick helper script for flashing a Proxmark3 device via USB
Description:
The correct port name will be automatically guessed and the stock bootloader will be flashed.
You can also specify a first option -n N to access the Nth Proxmark3 connected on USB.
If this doesn't work, you'll have to use manually the proxmark3 client, see "$CLIENT -h".
To see a list of available ports, use --list.
Usage:
$SCRIPT [-n <N>]
$SCRIPT --list
EOF
}
else
echo >&2 "[!!] Script ran under unknown name, abort: $SCRIPT"
exit 1
fi
# priority to the help options
for ARG; do
if [ "$ARG" == "-h" ] || [ "$ARG" == "--help" ]; then
HELP
exit 0
fi
if [ "$ARG" == "-hh" ] || [ "$ARG" == "--helpclient" ]; then
CMD "-h"
exit 0
fi
done
# if offline, bypass the script and forward all other args
for ARG; do
shift
if [ "$ARG" == "-o" ] || [ "$ARG" == "--offline" ]; then
CMD "$@"
exit $?
fi
set -- "$@" "$ARG"
done
# if a port is already provided, let's just run the command as such
for ARG; do
shift
if [ "$ARG" == "-p" ]; then
CMD "$@"
exit $?
fi
set -- "$@" "$ARG"
done
if [ "$1" == "--list" ]; then
shift
if [ "$1" != "" ]; then
echo >&2 "[!!] Option --list must be used alone"
exit 1
fi
SHOWLIST=true
fi
# Number of the Proxmark3 we're interested in
N=1
if [ "$1" == "-n" ]; then
shift
if [ "$1" -ge 1 ] && [ "$1" -lt 10 ]; then
N=$1
shift
else
echo >&2 "[!!] Option -n requires a number between 1 and 9, got \"$1\""
exit 1
fi
fi
HOSTOS=$(uname | awk '{print toupper($0)}')
if [ "$HOSTOS" = "LINUX" ]; then
# Detect when running under WSL1 (but exclude WSL2)
if uname -a | grep -qi Microsoft && uname -a | grep -qvi WSL2; then
# First try finding it using the PATH environment variable
PSHEXE=$(command -v powershell.exe 2>/dev/null)
# If it fails (such as if WSLENV is not set), try using the default installation path
if [ -z "$PSHEXE" ]; then
PSHEXE=/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe
fi
# Finally test if PowerShell is working
if ! "$PSHEXE" exit >/dev/null 2>&1; then
echo >&2 "[!!] Cannot run powershell.exe, are you sure your WSL is authorized to run Windows processes? (cf WSL interop flag)"
exit 1
fi
GETPM3LIST=get_pm3_list_WSL
else
GETPM3LIST=get_pm3_list_Linux
fi
elif [ "$HOSTOS" = "DARWIN" ]; then
GETPM3LIST=get_pm3_list_macOS
elif [[ "$HOSTOS" =~ MINGW(32|64)_NT* ]]; then
GETPM3LIST=get_pm3_list_Windows
else
echo >&2 "[!!] Host OS not recognized, abort: $HOSTOS"
exit 1
fi
if $SHOWLIST; then
# Probe for up to 9 devs
$GETPM3LIST 9
if [ ${#PM3LIST} -lt 1 ]; then
echo >&2 "[!!] No port found"
exit 1
fi
n=1
for DEV in "${PM3LIST[@]}"
do
echo "$n: $DEV"
n=$((n+1))
done
exit 0
fi
# Wait till we get at least N Proxmark3 devices
$GETPM3LIST "$N"
if [ ${#PM3LIST} -lt "$N" ]; then
echo >&2 "[=] Waiting for Proxmark3 to appear..."
fi
while true; do
if [ ${#PM3LIST[*]} -ge "$N" ]; then
break
fi
sleep .1
$GETPM3LIST "$N"
done
if [ ${#PM3LIST} -lt "$N" ]; then
HELP() {
cat << EOF
[!!] No port found, abort
[?] Hint: try '$SCRIPT --list' to see list of available ports, and use the -n command like below
[?] $SCRIPT [-n <N>]
EOF
}
HELP
exit 1
fi
CMD "${PM3LIST[$((N-1))]}" "$@"
exit $?

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
PM3PATH=$(dirname "$0")
. "$PM3PATH/pm3"

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
PM3PATH=$(dirname "$0")
. "$PM3PATH/pm3"

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
PM3PATH=$(dirname "$0")
. "$PM3PATH/pm3"

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
PM3PATH=$(dirname "$0")
. "$PM3PATH/pm3"

Binary file not shown.

4
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "nightly"
components = ["rust-src"]
targets = ["riscv32imac-unknown-none-elf"]

View File

@@ -1,19 +0,0 @@
[Unit]
Description=Failure state for fwa.service
Requires=local-fs.target
After=local-fs.target
StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
Type=simple
ExecStart=/usr/local/bin/fwa --error
Restart=on-failure
RestartSec=5
User=root
Group=root
WorkingDirectory=/var/lib/fwa
EnvironmentFile=/etc/fwa.env
[Install]
WantedBy=multi-user.target

View File

@@ -1,5 +0,0 @@
PM3_BIN=/usr/share/pm3/pm3
LOG_LEVEL=warn
HOTSPOT_IDS=578B5DF2;c1532b57
HOTSPOT_SSID=fwa
HOTSPOT_PW=a9LG2kUVrsRRVUo1

View File

@@ -1,20 +0,0 @@
[Unit]
Description=Feuerwehr Anwesenheit Service
Requires=local-fs.target
After=local-fs.target
StartLimitIntervalSec=500
StartLimitBurst=5
OnFailure= fwa-fail.service
[Service]
Type=simple
ExecStart=/usr/local/bin/fwa
Restart=on-failure
RestartSec=5
User=root
Group=root
WorkingDirectory=/var/lib/fwa
EnvironmentFile=/etc/fwa.env
[Install]
WantedBy=multi-user.target

3
src/drivers.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod nfc_reader;
pub mod rtc;
pub mod buzzer;

0
src/drivers/buzzer.rs Normal file
View File

9
src/drivers/fram.rs Normal file
View File

@@ -0,0 +1,9 @@
const DEVICE_TYPE_CODE: u8 = 0b10100000;
const DEVICE_ADDRESS_CODE: u8 = 0b000000; // 3 bits for device address | default A0 = 0 A1 = 0 A2 = 0
const WRITE_CODE: u8 = 0b00000000; // 0 for write
const READ_CODE: u8 = 0b00000001; // 1 for read
const DEVICE_ADDRESS_WRITE: u8 = DEVICE_TYPE_CODE | DEVICE_ADDRESS_CODE | WRITE_CODE; // I2C address write for FRAM
const DEVICE_ADDRESS_READ: u8 = DEVICE_TYPE_CODE | DEVICE_ADDRESS_CODE | READ_CODE; // I2C address read for FRAM

28
src/drivers/nfc_reader.rs Normal file
View File

@@ -0,0 +1,28 @@
use embassy_time::{Duration, Timer};
use esp_hal::{Async, uart::Uart};
use log::{debug, info};
use crate::TallyPublisher;
#[embassy_executor::task]
pub async fn rfid_reader_task(mut uart_device: Uart<'static, Async>, chan: TallyPublisher) {
let mut uart_buffer = [0u8; 64];
loop {
debug!("Looking for NFC...");
match uart_device.read_async(&mut uart_buffer).await {
Ok(n) => {
let mut hex_str = heapless::String::<64>::new();
for byte in &uart_buffer[..n] {
core::fmt::Write::write_fmt(&mut hex_str, format_args!("{:02X} ", byte)).ok();
}
info!("Read {n} bytes from UART: {hex_str}");
chan.publish([1, 0, 2, 5, 0, 8, 12, 15]).await;
}
Err(e) => {
log::error!("Error reading from UART: {e}");
}
}
Timer::after(Duration::from_millis(200)).await;
}
}

96
src/drivers/rtc.rs Normal file
View File

@@ -0,0 +1,96 @@
use ds3231::{
Config, DS3231, DS3231Error, InterruptControl, Oscillator, SquareWaveFrequency,
TimeRepresentation,
};
use esp_hal::{
Async,
i2c::{self, master::I2c},
};
use log::{debug, error, info};
use crate::{FEEDBACK_STATE, drivers, feedback};
use chrono::{TimeZone, Utc};
include!(concat!(env!("OUT_DIR"), "/build_time.rs"));
const RTC_ADDRESS: u8 = 0x68;
pub struct RTCClock {
dev: DS3231<I2c<'static, Async>>,
}
impl RTCClock {
pub async fn new(i2c: i2c::master::I2c<'static, Async>) -> Self {
debug!("configuring rtc...");
let rtc = drivers::rtc::rtc_config(i2c).await;
debug!("rtc up");
RTCClock { dev: rtc }
}
pub async fn get_time(&mut self) -> u64 {
match self.dev.datetime().await {
Ok(datetime) => {
let utc_time = datetime.and_utc().timestamp() as u64;
utc_time
}
Err(e) => {
FEEDBACK_STATE.signal(feedback::FeedbackState::Error);
error!("Failed to read RTC datetime: {:?}", e);
0
}
}
}
}
pub async fn rtc_config(i2c: I2c<'static, Async>) -> DS3231<I2c<'static, Async>> {
let mut rtc: DS3231<I2c<'static, Async>> = DS3231::new(i2c, RTC_ADDRESS);
let naive_dt = Utc
.timestamp_opt(BUILD_UNIX_TIME as i64, 0)
.single()
.unwrap()
.naive_utc();
let rtc_config = Config {
time_representation: TimeRepresentation::TwentyFourHour,
square_wave_frequency: SquareWaveFrequency::Hz1,
interrupt_control: InterruptControl::Interrupt, // Enable interrupt mode
battery_backed_square_wave: false,
oscillator_enable: Oscillator::Disabled,
};
match rtc.configure(&rtc_config).await {
Ok(_) => info!("DS3231 configured successfully"),
Err(e) => {
info!("Failed to configure DS3231: {:?}", e);
panic!("DS3231 configuration failed");
}
}
rtc.set_datetime(&naive_dt).await.unwrap_or_else(|e| {
FEEDBACK_STATE.signal(feedback::FeedbackState::Error);
error!("Failed to set RTC datetime: {:?}", e);
});
info!("RTC datetime set to: {}", naive_dt);
match rtc.status().await {
Ok(mut status) => {
status.set_alarm1_flag(false);
status.set_alarm2_flag(false);
match rtc.set_status(status).await {
Ok(_) => info!("Alarm flags cleared"),
Err(e) => info!("Failed to clear alarm flags: {:?}", e),
}
}
Err(e) => info!("Failed to read status: {:?}", e),
}
rtc
}
pub async fn read_rtc_time<'a>(
rtc: &'a mut DS3231<I2c<'static, Async>>,
) -> Result<u64, DS3231Error<esp_hal::i2c::master::Error>> {
let timestamp_result = rtc.datetime().await?;
Ok(timestamp_result.and_utc().timestamp() as u64)
}

View File

@@ -1,62 +1,101 @@
use anyhow::Result;
use log::error;
use rgb::RGB8;
use smart_leds::colors::{GREEN, RED};
use std::time::Duration;
use tokio::{join, time::sleep};
use embassy_time::{Delay, Duration, Timer};
use esp_hal::{delay, gpio::Output, peripherals, rmt::ConstChannelAccess};
use esp_hal_smartled::SmartLedsAdapterAsync;
use log::{debug, error, info};
use init::hardware;
use smart_leds::colors::{BLACK, GREEN, RED, YELLOW};
use smart_leds::{brightness, colors::BLUE};
use smart_leds::SmartLedsWriteAsync;
use crate::hardware::{Buzzer, StatusLed};
use crate::{FEEDBACK_STATE, init};
#[cfg(not(feature = "mock_pi"))]
use crate::{hardware::GPIOBuzzer, hardware::SpiLed};
#[cfg(feature = "mock_pi")]
use crate::hardware::{MockBuzzer, MockLed};
const LED_BLINK_DURATION: Duration = Duration::from_secs(1);
pub enum DeviceStatus {
NotReady,
Ready,
HotspotEnabled,
#[derive(Copy, Clone, Debug)]
pub enum FeedbackState {
Ack,
Nak,
Error,
Startup,
WIFI,
Idle,
}
impl DeviceStatus {
pub fn color(&self) -> RGB8 {
match self {
Self::NotReady => RGB8::new(0, 0, 0),
Self::Ready => RGB8::new(0, 50, 0),
Self::HotspotEnabled => RGB8::new(0, 0, 50),
const LED_LEVEL: u8 = 255;
#[embassy_executor::task]
pub async fn feedback_task(mut led: SmartLedsAdapterAsync<ConstChannelAccess<esp_hal::rmt::Tx, 0>, { init::hardware::LED_BUFFER_SIZE }>, buzzer: peripherals::GPIO21<'static>) {
debug!("Starting feedback task");
let mut buzzer = init::hardware::setup_buzzer(buzzer);
loop {
let feedback_state = FEEDBACK_STATE.wait().await;
match feedback_state {
FeedbackState::Ack => {
led.write(brightness([GREEN; init::hardware::NUM_LEDS].into_iter(), LED_LEVEL)).await.unwrap();
buzzer.set_high();
Timer::after(Duration::from_millis(100)).await;
buzzer.set_low();
Timer::after(Duration::from_millis(50)).await;
}
FeedbackState::Nak => {
led.write(brightness([YELLOW; init::hardware::NUM_LEDS].into_iter(), LED_LEVEL)).await.unwrap();
buzzer.set_high();
Timer::after(Duration::from_millis(100)).await;
buzzer.set_low();
Timer::after(Duration::from_millis(100)).await;
buzzer.set_high();
Timer::after(Duration::from_millis(100)).await;
buzzer.set_low();
led.write(brightness([BLACK; init::hardware::NUM_LEDS].into_iter(), LED_LEVEL)).await.unwrap();
}
FeedbackState::Error => {
led.write(brightness([RED; init::hardware::NUM_LEDS].into_iter(), LED_LEVEL)).await.unwrap();
buzzer.set_high();
Timer::after(Duration::from_millis(500)).await;
buzzer.set_low();
Timer::after(Duration::from_millis(500)).await;
buzzer.set_high();
Timer::after(Duration::from_millis(500)).await;
buzzer.set_low();
}
FeedbackState::Startup => {
led.write(brightness([GREEN; init::hardware::NUM_LEDS].into_iter(), LED_LEVEL)).await.unwrap();
buzzer.set_high();
Timer::after(Duration::from_millis(10)).await;
buzzer.set_low();
Timer::after(Duration::from_millis(10)).await;
buzzer.set_high();
Timer::after(Duration::from_millis(10)).await;
buzzer.set_low();
Timer::after(Duration::from_millis(50)).await;
buzzer.set_high();
Timer::after(Duration::from_millis(100)).await;
buzzer.set_low();
led.write(brightness([BLACK; init::hardware::NUM_LEDS].into_iter(), LED_LEVEL)).await.unwrap();
}
FeedbackState::WIFI => {
led.write(brightness([BLUE; init::hardware::NUM_LEDS].into_iter(), LED_LEVEL)).await.unwrap();
}
FeedbackState::Idle => {
// Do nothing
}
};
debug!("Feedback state: {:?}", feedback_state);
}
pub struct Feedback<B: Buzzer, L: StatusLed> {
device_status: DeviceStatus,
buzzer: B,
led: L,
}
impl<B: Buzzer, L: StatusLed> Feedback<B, L> {
pub async fn success(&mut self) {
let buzzer_handle = Self::beep_ack(&mut self.buzzer);
let led_handle = Self::flash_led_for_duration(&mut self.led, GREEN, LED_BLINK_DURATION);
let (buzzer_result, _) = join!(buzzer_handle, led_handle);
buzzer_result.unwrap_or_else(|err| {
error!("Failed to buzz: {err}");
});
// async fn beep_ack() {
// buzzer.set_high();
// buzzer.set_low();
// //Timer::after(Duration::from_millis(100)).await;
// }
let _ = self.led_to_status();
}
pub async fn failure(&mut self) {
/* pub async fn failure(&mut self) {
let buzzer_handle = Self::beep_nak(&mut self.buzzer);
let led_handle = Self::flash_led_for_duration(&mut self.led, RED, LED_BLINK_DURATION);
let (buzzer_result, _) = join!(buzzer_handle, led_handle);
buzzer_result.unwrap_or_else(|err| {
error!("Failed to buzz: {err}");
buzzer_result.unwrap_or_else(|err| { error!("Failed to buzz: {err}");
});
let _ = self.led_to_status();
@@ -87,14 +126,6 @@ impl<B: Buzzer, L: StatusLed> Feedback<B, L> {
let _ = self.led_to_status();
}
pub fn set_device_status(&mut self, status: DeviceStatus){
self.device_status = status;
let _ = self.led_to_status();
}
fn led_to_status(&mut self) -> Result<()> {
self.led.turn_on(self.device_status.color())
}
async fn flash_led_for_duration(led: &mut L, color: RGB8, duration: Duration) -> Result<()> {
led.turn_on(color)?;
@@ -152,30 +183,5 @@ impl<B: Buzzer, L: StatusLed> Feedback<B, L> {
.await?;
Ok(())
}
}
#[cfg(feature = "mock_pi")]
pub type FeedbackImpl = Feedback<MockBuzzer, MockLed>;
#[cfg(not(feature = "mock_pi"))]
pub type FeedbackImpl = Feedback<GPIOBuzzer, SpiLed>;
impl FeedbackImpl {
pub fn new() -> Result<Self> {
#[cfg(feature = "mock_pi")]
{
Ok(Feedback {
device_status: DeviceStatus::NotReady,
buzzer: MockBuzzer {},
led: MockLed {},
})
}
#[cfg(not(feature = "mock_pi"))]
{
Ok(Feedback {
device_status: DeviceStatus::NotReady,
buzzer: GPIOBuzzer::new_default()?,
led: SpiLed::new()?,
})
}
}
}
*/

View File

@@ -1,37 +0,0 @@
use anyhow::Result;
use rppal::pwm::{Channel, Polarity, Pwm};
use std::time::Duration;
use tokio::time::sleep;
use crate::hardware::Buzzer;
const DEFAULT_PWM_CHANNEL_BUZZER: Channel = Channel::Pwm0; //PWM0 = GPIO18/Physical pin 12
pub struct GPIOBuzzer {
pwm: Pwm,
}
impl GPIOBuzzer {
pub fn new_from_channel(channel: Channel) -> Result<Self, rppal::pwm::Error> {
// Enable with dummy values; we'll set frequency/duty in the tone method
let duty_cycle: f64 = 0.5;
let pwm = Pwm::with_frequency(channel, 1000.0, duty_cycle, Polarity::Normal, true)?;
pwm.disable()?;
Ok(GPIOBuzzer { pwm })
}
pub fn new_default() -> Result<Self, rppal::pwm::Error> {
Self::new_from_channel(DEFAULT_PWM_CHANNEL_BUZZER)
}
}
impl Buzzer for GPIOBuzzer {
async fn modulated_tone(&mut self, frequency_hz: f64, duration: Duration) -> Result<()> {
self.pwm.set_frequency(frequency_hz, 0.5)?; // 50% duty cycle (square wave)
self.pwm.enable()?;
sleep(duration).await;
self.pwm.disable()?;
Ok(())
}
}

View File

@@ -1,131 +0,0 @@
use anyhow::{Result, anyhow};
use log::{trace, warn};
use std::env;
use tokio::process::Command;
use crate::hardware::Hotspot;
const SSID: &str = "fwa";
const CON_NAME: &str = "fwa-hotspot";
const PASSWORD: &str = "a9LG2kUVrsRRVUo1";
const IPV4_ADDRES: &str = "192.168.4.1/24";
/// NetworkManager Hotspot
pub struct NMHotspot {
ssid: String,
con_name: String,
password: String,
ipv4: String,
}
impl NMHotspot {
pub fn new_from_env() -> Result<Self> {
let ssid = env::var("HOTSPOT_SSID").unwrap_or(SSID.to_owned());
let password = env::var("HOTSPOT_PW").unwrap_or_else(|_| {
warn!("HOTSPOT_PW not set. Using default password");
PASSWORD.to_owned()
});
if password.len() < 8 {
return Err(anyhow!("Hotspot password to short"));
}
Ok(NMHotspot {
ssid,
con_name: CON_NAME.to_owned(),
password,
ipv4: IPV4_ADDRES.to_owned(),
})
}
async fn create_hotspot(&self) -> Result<()> {
let cmd = Command::new("nmcli")
.args(["device", "wifi", "hotspot"])
.arg("con-name")
.arg(&self.con_name)
.arg("ssid")
.arg(&self.ssid)
.arg("password")
.arg(&self.password)
.output()
.await?;
trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
if !cmd.status.success() {
return Err(anyhow!("nmcli command had non-zero exit code"));
}
let cmd = Command::new("nmcli")
.arg("connection")
.arg("modify")
.arg(&self.con_name)
.arg("ipv4.method")
.arg("shared")
.arg("ipv4.addresses")
.arg(&self.ipv4)
.output()
.await?;
if !cmd.status.success() {
return Err(anyhow!("nmcli command had non-zero exit code"));
}
Ok(())
}
/// Checks if the connection already exists
async fn exists(&self) -> Result<bool> {
let cmd = Command::new("nmcli")
.args(["connection", "show"])
.arg(&self.con_name)
.output()
.await?;
trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
Ok(cmd.status.success())
}
}
impl Hotspot for NMHotspot {
async fn enable_hotspot(&self) -> Result<()> {
if !self.exists().await? {
self.create_hotspot().await?;
}
let cmd = Command::new("nmcli")
.args(["connection", "up"])
.arg(&self.con_name)
.output()
.await?;
trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
if !cmd.status.success() {
return Err(anyhow!("nmcli command had non-zero exit code"));
}
Ok(())
}
async fn disable_hotspot(&self) -> Result<()> {
let cmd = Command::new("nmcli")
.args(["connection", "down"])
.arg(&self.con_name)
.output()
.await?;
trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
if !cmd.status.success() {
return Err(anyhow!("nmcli command had non-zero exit code"));
}
Ok(())
}
}

View File

@@ -1,45 +0,0 @@
use anyhow::Result;
use std::time::Duration;
mod gpio_buzzer;
mod hotspot;
mod mock;
mod spi_led;
pub use gpio_buzzer::GPIOBuzzer;
pub use mock::{MockBuzzer, MockHotspot, MockLed};
pub use spi_led::SpiLed;
pub trait StatusLed {
fn turn_off(&mut self) -> Result<()>;
fn turn_on(&mut self, color: rgb::RGB8) -> Result<()>;
}
pub trait Buzzer {
fn modulated_tone(
&mut self,
frequency_hz: f64,
duration: Duration,
) -> impl Future<Output = Result<()>> + std::marker::Send;
}
pub trait Hotspot {
fn enable_hotspot(&self) -> impl std::future::Future<Output = Result<()>> + std::marker::Send;
fn disable_hotspot(&self) -> impl std::future::Future<Output = Result<()>> + std::marker::Send;
}
/// Create a struct to manage the hotspot
/// Respects the `mock_pi` flag.
pub fn create_hotspot() -> Result<impl Hotspot> {
#[cfg(feature = "mock_pi")]
{
Ok(mock::MockHotspot {})
}
#[cfg(not(feature = "mock_pi"))]
{
hotspot::NMHotspot::new_from_env()
}
}

View File

@@ -1,33 +0,0 @@
use anyhow::Result;
use rppal::spi::{Bus, Mode, SlaveSelect, Spi};
use smart_leds::SmartLedsWrite;
use ws2812_spi::Ws2812;
use crate::hardware::StatusLed;
const SPI_CLOCK_SPEED: u32 = 3_800_000;
pub struct SpiLed {
controller: Ws2812<Spi>,
}
impl SpiLed {
pub fn new() -> Result<Self, rppal::spi::Error> {
let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, SPI_CLOCK_SPEED, Mode::Mode0)?;
let controller = Ws2812::new(spi);
Ok(SpiLed { controller })
}
}
impl StatusLed for SpiLed {
fn turn_off(&mut self) -> Result<()> {
self.controller
.write(vec![rgb::RGB8::new(0, 0, 0)].into_iter())?;
Ok(())
}
fn turn_on(&mut self, color: rgb::RGB8) -> Result<()> {
self.controller.write(vec![color].into_iter())?;
Ok(())
}
}

3
src/init.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod hardware;
pub mod network;
pub mod wifi;

215
src/init/hardware.rs Normal file
View File

@@ -0,0 +1,215 @@
use embassy_executor::Spawner;
use embassy_net::Stack;
use embassy_time::{Duration, Timer};
use embedded_sdmmc_dev::SdCard;
use esp_hal::i2c::master::Config;
use esp_hal::peripherals::{
self, GPIO0, GPIO1, GPIO2, GPIO10, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22,
GPIO23, I2C0, RMT, SPI2, UART1,
};
use esp_hal::rmt::{ConstChannelAccess, Rmt, Tx};
use esp_hal::spi::{
Mode,
master::{Config as Spi_config, Spi},
};
use esp_hal::time::Rate;
use esp_hal::timer::timg::TimerGroup;
use esp_hal::{
Async,
clock::CpuClock,
gpio::{Output, OutputConfig},
i2c::master::I2c,
timer::systimer::SystemTimer,
uart::Uart,
};
use esp_hal_smartled::{SmartLedsAdapterAsync, buffer_size_async};
use smart_leds::colors::{BLUE, GREEN, RED};
use smart_leds::{
RGB8, SmartLedsWriteAsync, brightness, gamma,
hsv::{Hsv, hsv2rgb},
};
use esp_println::logger::init_logger;
use log::{debug, error};
use crate::init::network;
use crate::init::wifi;
/*************************************************
* GPIO Pinout Xiao Esp32c6
*
* D0 -> GPIO0 -> SD DECT
* D1 -> GPIO1 -> Level Shifter A0 -> LED
* D2 -> GPIO2 -> SPI/CS
* D3 -> GPIO21 -> Buzzer
* D4 -> GPIO22 -> I2C/SDA
* D5 -> GPIO23 -> I2C/SCL
* D6 -> GPIO16 -> UART/TX
* D7 -> GPIO17 -> UART/RX -> Level Shifter A1 -> NFC Reader
* D8 -> GPIO19 -> SPI/SCLK
* D9 -> GPIO20 -> SPI/MISO
* D10 -> GPIO10 -> SPI/MOSI
*
*************************************************/
pub const NUM_LEDS: usize = 66;
pub const LED_BUFFER_SIZE: usize = NUM_LEDS * 25;
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
loop {
error!("PANIC: {info}");
}
}
esp_bootloader_esp_idf::esp_app_desc!();
pub async fn hardware_init(
spawner: &mut Spawner,
) -> (
Uart<'static, Async>,
Stack<'static>,
I2c<'static, Async>,
SmartLedsAdapterAsync<ConstChannelAccess<esp_hal::rmt::Tx, 0>, LED_BUFFER_SIZE>,
GPIO21<'static>,
) {
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
esp_alloc::heap_allocator!(size: 72 * 1024);
let timer0 = SystemTimer::new(peripherals.SYSTIMER);
esp_hal_embassy::init(timer0.alarm0);
init_logger(log::LevelFilter::Debug);
let timer1 = TimerGroup::new(peripherals.TIMG0);
let mut rng = esp_hal::rng::Rng::new(peripherals.RNG);
let network_seed = (rng.random() as u64) << 32 | rng.random() as u64;
wifi::set_antenna_mode(peripherals.GPIO3, peripherals.GPIO14).await;
let interfaces = wifi::setup_wifi(timer1.timer0, rng, peripherals.WIFI, spawner);
let stack = network::setup_network(network_seed, interfaces.ap, spawner);
Timer::after(Duration::from_millis(1)).await;
init_lvl_shifter(peripherals.GPIO0);
let uart_device = setup_uart(peripherals.UART1, peripherals.GPIO16, peripherals.GPIO17);
let i2c_device = setup_i2c(peripherals.I2C0, peripherals.GPIO22, peripherals.GPIO23);
let spi_device = setup_spi(
peripherals.SPI2,
peripherals.GPIO19,
peripherals.GPIO20,
peripherals.GPIO18,
peripherals.GPIO2,
);
let sd_card = setup_sdcard(spi_device);
let buzzer_gpio = peripherals.GPIO21;
Timer::after(Duration::from_millis(500)).await;
let led = setup_led(peripherals.RMT, peripherals.GPIO1);
debug!("hardware init done");
(uart_device, stack, i2c_device, led, buzzer_gpio)
}
// Initialize the level shifter for the NFC reader and LED (output-enable (OE) input is low, all outputs are placed in the high-impedance (Hi-Z) state)
fn init_lvl_shifter(oe_pin: GPIO0<'static>) {
let mut oe_lvl_shifter =
Output::new(oe_pin, esp_hal::gpio::Level::Low, OutputConfig::default().with_drive_mode(esp_hal::gpio::DriveMode::PushPull).with_drive_strength(esp_hal::gpio::DriveStrength::_10mA));
oe_lvl_shifter.set_high();
}
fn setup_uart(
uart1: UART1<'static>,
uart_tx: GPIO16<'static>,
uart_rx: GPIO17<'static>,
) -> Uart<'static, Async> {
let uard_device = Uart::new(uart1, esp_hal::uart::Config::default().with_baudrate(9600));
match uard_device {
Ok(block) => block.with_rx(uart_rx).with_tx(uart_tx).into_async(),
Err(e) => {
error!("Failed to initialize UART: {e}");
panic!(); //TODO panic!
}
}
}
fn setup_i2c(
i2c0: I2C0<'static>,
sda: GPIO22<'static>,
scl: GPIO23<'static>,
) -> I2c<'static, Async> {
debug!("init I2C");
let config = Config::default().with_frequency(Rate::from_khz(400));
let i2c = match I2c::new(i2c0, config) {
Ok(i2c) => i2c.with_sda(sda).with_scl(scl).into_async(),
Err(e) => {
error!("Failed to initialize I2C: {:?}", e);
panic!(); //TODO panic!
}
};
i2c
}
fn setup_spi(
spi2: SPI2<'static>,
sck: GPIO19<'static>,
miso: GPIO20<'static>,
mosi: GPIO18<'static>,
cs: GPIO2<'static>,
) -> Spi<'static, Async> {
let spi = match Spi::new(spi2, Spi_config::default()) {
Ok(spi) => spi
.with_sck(sck)
.with_miso(miso)
.with_mosi(mosi)
.with_cs(cs)
.into_async(),
Err(e) => panic!("Failed to initialize SPI: {:?}", e),
};
spi
}
fn setup_sdcard(spi_device: Spi<'static, Async>) {
//let sdcard = SdCard::new(spi_device as embedded_hal::spi::SpiDevice(), delayer)
}
pub fn setup_buzzer(buzzer_gpio: GPIO21<'static>) -> Output<'static> {
let config = esp_hal::gpio::OutputConfig::default()
.with_drive_strength(esp_hal::gpio::DriveStrength::_40mA);
let buzzer = Output::new(buzzer_gpio, esp_hal::gpio::Level::Low, config);
buzzer
}
fn setup_led(
rmt: RMT<'static>,
led_gpio: GPIO1<'static>,
) -> SmartLedsAdapterAsync<ConstChannelAccess<esp_hal::rmt::Tx, 0>, LED_BUFFER_SIZE> {
debug!("setup led");
let rmt: Rmt<'_, esp_hal::Async> = {
let frequency: Rate = Rate::from_mhz(80);
Rmt::new(rmt, frequency)
}
.expect("Failed to initialize RMT")
.into_async();
let rmt_channel = rmt.channel0;
let rmt_buffer = [0_u32; buffer_size_async(NUM_LEDS)];
let led: SmartLedsAdapterAsync<_, LED_BUFFER_SIZE> =
SmartLedsAdapterAsync::new(rmt_channel, led_gpio, rmt_buffer);
led
}

72
src/init/network.rs Normal file
View File

@@ -0,0 +1,72 @@
use core::{net::Ipv4Addr, str::FromStr};
use embassy_executor::Spawner;
use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
use embassy_time::{Duration, Timer};
use esp_wifi::wifi::WifiDevice;
use static_cell::make_static;
pub fn setup_network<'a>(seed: u64, wifi: WifiDevice<'static>, spawner: &mut Spawner) -> Stack<'a> {
let gw_ip_addr_str = "192.168.2.1";
let gw_ip_addr = Ipv4Addr::from_str(gw_ip_addr_str).expect("failed to parse gateway ip");
let config = embassy_net::Config::ipv4_static(StaticConfigV4 {
address: Ipv4Cidr::new(gw_ip_addr, 24),
gateway: Some(gw_ip_addr),
dns_servers: Default::default(),
});
let (stack, runner) =
embassy_net::new(wifi, config, make_static!(StackResources::<3>::new()), seed);
spawner.must_spawn(net_task(runner));
spawner.must_spawn(run_dhcp(stack, gw_ip_addr_str));
stack
}
#[embassy_executor::task]
async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) {
use core::net::{Ipv4Addr, SocketAddrV4};
use edge_dhcp::{
io::{self, DEFAULT_SERVER_PORT},
server::{Server, ServerOptions},
};
use edge_nal::UdpBind;
use edge_nal_embassy::{Udp, UdpBuffers};
let ip = Ipv4Addr::from_str(gw_ip_addr).expect("dhcp task failed to parse gw ip");
let mut buf = [0u8; 1500];
let mut gw_buf = [Ipv4Addr::UNSPECIFIED];
let buffers = UdpBuffers::<3, 1024, 1024, 10>::new();
let unbound_socket = Udp::new(stack, &buffers);
let mut bound_socket = unbound_socket
.bind(core::net::SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::UNSPECIFIED,
DEFAULT_SERVER_PORT,
)))
.await
.unwrap();
loop {
_ = io::server::run(
&mut Server::<_, 64>::new_with_et(ip),
&ServerOptions::new(ip, Some(&mut gw_buf)),
&mut bound_socket,
&mut buf,
)
.await
.inspect_err(|e| log::warn!("DHCP server error: {e:?}"));
Timer::after(Duration::from_millis(500)).await;
}
}
#[embassy_executor::task]
async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
runner.run().await;
}

56
src/init/wifi.rs Normal file
View File

@@ -0,0 +1,56 @@
use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use esp_hal::gpio::{Output, OutputConfig};
use esp_hal::peripherals::{GPIO3, GPIO14, WIFI};
use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController, WifiEvent, WifiState};
use esp_wifi::{EspWifiRngSource, EspWifiTimerSource, wifi::Interfaces};
use static_cell::make_static;
pub async fn set_antenna_mode(gpio3: GPIO3<'static>, gpio14: GPIO14<'static>) {
let mut rf_switch = Output::new(gpio3, esp_hal::gpio::Level::Low, OutputConfig::default());
rf_switch.set_low();
Timer::after_millis(150).await;
let mut antenna_mode = Output::new(gpio14, esp_hal::gpio::Level::Low, OutputConfig::default());
antenna_mode.set_low();
}
pub fn setup_wifi<'d: 'static>(
timer: impl EspWifiTimerSource + 'd,
rng: impl EspWifiRngSource + 'd,
wifi: WIFI<'static>,
spawner: &mut Spawner,
) -> Interfaces<'d> {
let esp_wifi_ctrl = make_static!(esp_wifi::init(timer, rng).unwrap());
let (controller, interfaces) = esp_wifi::wifi::new(esp_wifi_ctrl, wifi).unwrap();
spawner.must_spawn(connection(controller));
interfaces
}
#[embassy_executor::task]
async fn connection(mut controller: WifiController<'static>) {
loop {
match esp_wifi::wifi::wifi_state() {
WifiState::ApStarted => {
// wait until we're no longer connected
controller.wait_for_event(WifiEvent::ApStop).await;
Timer::after(Duration::from_millis(5000)).await
}
_ => {}
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
ssid: "esp-wifi".try_into().unwrap(),
..Default::default()
});
controller.set_configuration(&client_config).unwrap();
controller.start_async().await.unwrap();
}
}
}

1
src/lib.rs Normal file
View File

@@ -0,0 +1 @@
#![no_std]

View File

@@ -1,25 +0,0 @@
use std::env;
use log::LevelFilter;
use simplelog::{ConfigBuilder, SimpleLogger};
pub fn setup_logger() {
let log_level = env::var("LOG_LEVEL")
.ok()
.and_then(|level| level.parse::<LevelFilter>().ok())
.unwrap_or({
if cfg!(debug_assertions) {
LevelFilter::Debug
} else {
LevelFilter::Warn
}
});
let config = ConfigBuilder::new()
.set_target_level(LevelFilter::Off)
.set_location_level(LevelFilter::Off)
.set_thread_level(LevelFilter::Off)
.build();
let _ = SimpleLogger::init(log_level, config);
}

View File

@@ -1,192 +1,90 @@
#![allow(dead_code)]
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]
use anyhow::Result;
use feedback::{Feedback, FeedbackImpl};
use log::{error, info, warn};
use std::{
env::{self, args},
sync::Arc,
time::Duration,
use embassy_executor::Spawner;
use embassy_net::Stack;
use embassy_sync::{
blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}, channel::Channel, pubsub::{
PubSubChannel, Publisher,
WaitResult::{Lagged, Message},
}, signal::Signal
};
use tally_id::TallyID;
use tokio::{
fs,
signal::unix::{SignalKind, signal},
sync::{
Mutex,
broadcast::{self, Receiver, Sender},
},
try_join,
};
use webserver::start_webserver;
use embassy_time::{Duration, Timer};
use log::{debug, info};
use static_cell::make_static;
use crate::{hardware::{create_hotspot, Hotspot}, pm3::run_pm3, store::IDStore, webserver::{spawn_idle_watcher, ActivityNotifier}};
use crate::{store::TallyID, webserver::start_webserver};
extern crate alloc;
mod drivers;
mod feedback;
mod hardware;
mod pm3;
mod logger;
mod tally_id;
mod webserver;
mod init;
mod store;
mod webserver;
const STORE_PATH: &str = "./data.json";
static FEEDBACK_STATE: Signal<CriticalSectionRawMutex, feedback::FeedbackState> = Signal::new();
async fn run_webserver<H>(
store: Arc<Mutex<IDStore>>,
id_channel: Sender<String>,
hotspot: Arc<Mutex<H>>,
user_feedback: Arc<Mutex<FeedbackImpl>>,
) -> Result<()>
where
H: Hotspot + Send + Sync + 'static,
{
let activity_channel = spawn_idle_watcher(Duration::from_secs(60 * 30), move || {
info!("No activity on webserver. Disabling hotspot");
let cloned_hotspot = hotspot.clone();
let cloned_user_feedback = user_feedback.clone();
tokio::spawn(async move {
let _ = cloned_hotspot.lock().await.disable_hotspot().await;
cloned_user_feedback
.lock()
.await
.set_device_status(feedback::DeviceStatus::Ready);
});
});
type TallyChannel = PubSubChannel<NoopRawMutex, TallyID, 8, 2, 1>;
type TallyPublisher = Publisher<'static, NoopRawMutex, TallyID, 8, 2, 1>;
let notifier = ActivityNotifier {
sender: activity_channel,
};
#[esp_hal_embassy::main]
async fn main(mut spawner: Spawner) {
let (uart_device, stack, _i2c, _led, buzzer_gpio) =
init::hardware::hardware_init(&mut spawner).await;
start_webserver(store, id_channel, notifier).await?;
wait_for_stack_up(stack).await;
Ok(())
}
info!("Starting up...");
async fn load_or_create_store() -> Result<IDStore> {
if fs::try_exists(STORE_PATH).await? {
info!("Loading data from file");
IDStore::new_from_json(STORE_PATH).await
} else {
info!("No data file found. Creating empty one.");
Ok(IDStore::new())
let chan: &'static mut TallyChannel = make_static!(PubSubChannel::new());
//start_webserver(&mut spawner, stack);
let publisher = chan.publisher().unwrap();
let mut rtc = drivers::rtc::RTCClock::new(_i2c).await;
/****************************** Spawning tasks ***********************************/
debug!("spawing NFC reader task...");
spawner.must_spawn(drivers::nfc_reader::rfid_reader_task(
uart_device,
publisher,
));
debug!("spawing feedback task..");
spawner.must_spawn(feedback::feedback_task(_led, buzzer_gpio));
/******************************************************************************/
let mut sub = chan.subscriber().unwrap();
debug!("everything spawned");
FEEDBACK_STATE.signal(feedback::FeedbackState::Startup);
loop {
rtc.get_time().await;
info!("Current RTC time: {}", rtc.get_time().await);
Timer::after(Duration::from_millis(1000)).await;
// let wait_result = sub.next_message().await;
// match wait_result {
// Lagged(_) => debug!("Lagged"),
// Message(msg) => debug!("Got message: {msg:?}"),
// }
}
}
fn get_hotspot_enable_ids() -> Vec<TallyID> {
let hotspot_ids: Vec<TallyID> = env::var("HOTSPOT_IDS")
.map(|ids| ids.split(";").map(|id| TallyID(id.to_owned())).collect())
.unwrap_or_default();
if hotspot_ids.is_empty() {
warn!(
"HOTSPOT_IDS is not set or empty. You will not be able to activate the hotspot via a tally!"
);
async fn wait_for_stack_up(stack: Stack<'static>) {
loop {
if stack.is_link_up() {
break;
}
hotspot_ids
Timer::after(Duration::from_millis(500)).await;
if stack.is_config_up() {
break;
}
async fn handle_ids_loop(
mut id_channel: Receiver<String>,
hotspot_enable_ids: Vec<TallyID>,
id_store: Arc<Mutex<IDStore>>,
hotspot: Arc<Mutex<impl Hotspot>>,
user_feedback: Arc<Mutex<FeedbackImpl>>,
) -> Result<()> {
while let Ok(tally_id_string) = id_channel.recv().await {
let tally_id = TallyID(tally_id_string);
if hotspot_enable_ids.contains(&tally_id) {
info!("Enableing hotspot");
let hotspot_enable_result = hotspot.lock().await.enable_hotspot().await;
match hotspot_enable_result {
Ok(_) => {
user_feedback
.lock()
.await
.set_device_status(feedback::DeviceStatus::HotspotEnabled);
}
Err(e) => {
error!("Hotspot: {e}");
Timer::after(Duration::from_millis(500)).await;
}
}
// TODO: Should the ID be added anyway or ignored ?
}
if id_store.lock().await.add_id(tally_id) {
info!("Added new id to current day");
user_feedback.lock().await.success().await;
if let Err(e) = id_store.lock().await.export_json(STORE_PATH).await {
error!("Failed to save id store to file: {e}");
user_feedback.lock().await.failure().await;
// TODO: How to handle a failure to save ?
}
}
}
Ok(())
}
async fn enter_error_state(feedback: Arc<Mutex<FeedbackImpl>>, hotspot: Arc<Mutex<impl Hotspot>>) {
let _ = feedback.lock().await.activate_error_state().await;
let _ = hotspot.lock().await.enable_hotspot().await;
let mut sigterm = signal(SignalKind::terminate()).unwrap();
sigterm.recv().await;
}
#[tokio::main]
async fn main() -> Result<()> {
logger::setup_logger();
info!("Starting application");
let user_feedback = Arc::new(Mutex::new(Feedback::new()?));
let hotspot = Arc::new(Mutex::new(create_hotspot()?));
let error_flag_set = args().any(|e| e == "--error" || e == "-e");
if error_flag_set {
error!("Error flag set. Entering error state");
enter_error_state(user_feedback.clone(), hotspot).await;
return Ok(());
}
let store: Arc<Mutex<IDStore>> = Arc::new(Mutex::new(load_or_create_store().await?));
let hotspot_enable_ids = get_hotspot_enable_ids();
let (tx, rx) = broadcast::channel::<String>(32);
let sse_tx = tx.clone();
let pm3_handle = run_pm3(tx);
user_feedback.lock().await.startup().await;
let loop_handle = handle_ids_loop(
rx,
hotspot_enable_ids,
store.clone(),
hotspot.clone(),
user_feedback.clone(),
);
let webserver_handle = run_webserver(
store.clone(),
sse_tx,
hotspot.clone(),
user_feedback.clone(),
);
let run_result = try_join!(pm3_handle, loop_handle, webserver_handle);
if let Err(e) = run_result {
error!("Failed to run application: {e}");
return Err(e);
}
Ok(())
}

View File

@@ -1,4 +0,0 @@
mod runner;
mod parser;
pub use runner::run_pm3;

View File

@@ -1,10 +0,0 @@
use regex::Regex;
/// Parses the output of PM3 finds the read IDs
/// Example input: `[+] UID.... 3112B710`
pub fn parse_line(line: &str) -> Option<String> {
let regex = Regex::new(r"(?m)^\[\+\] UID.... (.*)$").unwrap();
let result = regex.captures(line);
result.map(|c| c.get(1).unwrap().as_str().to_owned())
}

View File

@@ -1,95 +0,0 @@
use anyhow::{Result, anyhow};
use log::{debug, info, trace, warn};
use std::env;
use std::process::Stdio;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::process::Command;
use tokio::select;
use tokio::signal::unix::{SignalKind, signal};
use tokio::sync::broadcast;
/// Runs the pm3 binary and monitors it's output
/// The pm3 binary is ether set in the env var PM3_BIN or found in the path
/// The ouput is parsed and send via the `tx` channel
pub async fn run_pm3(tx: broadcast::Sender<String>) -> Result<()> {
kill_orphans().await;
let pm3_path = match env::var("PM3_BIN") {
Ok(path) => path,
Err(_) => {
info!("PM3_BIN not set. Using default value");
"pm3".to_owned()
}
};
let mut cmd = Command::new("stdbuf")
.arg("-oL")
.arg(pm3_path)
.arg("-c")
.arg("lf hitag reader -@")
.stdout(Stdio::piped())
.stderr(Stdio::null())
.stdin(Stdio::piped())
.spawn()?;
let stdout = cmd.stdout.take().ok_or(anyhow!("Failed to get stdout"))?;
let mut stdin = cmd.stdin.take().ok_or(anyhow!("Failed to get stdin"))?;
let mut reader = BufReader::new(stdout).lines();
let mut sigterm = signal(SignalKind::terminate())?;
let child_handle = tokio::spawn(async move {
let mut last_id: String = "".to_owned();
while let Some(line) = reader.next_line().await.unwrap_or(None) {
trace!("PM3: {line}");
if let Some(uid) = super::parser::parse_line(&line) {
if last_id == uid {
let _ = tx.send(uid.clone());
}
last_id = uid;
}
}
});
select! {
_ = child_handle => {}
_ = sigterm.recv() => {
debug!("Graceful shutdown of PM3");
let _ = stdin.write_all(b"\n").await;
let _ = stdin.flush().await;
}
};
let status = cmd.wait().await?;
// We use the exit code here because status.success() is false if the child was terminated by a
// signal
let code = status.code().unwrap_or(0);
if code == 0 {
Ok(())
} else {
Err(anyhow!("PM3 exited with a non-zero exit code: {code}"))
}
}
/// Kills any open pm3 instances
/// Also funny name. hehehe.
async fn kill_orphans() {
let kill_result = Command::new("pkill")
.arg("-KILL")
.arg("-x")
.arg("proxmark3")
.output()
.await;
match kill_result {
Ok(_) => {
debug!("Successfully killed orphaned pm3 instances");
}
Err(e) => {
warn!("Failed to kill pm3 orphans: {e} Continuing anyway");
}
}
}

View File

@@ -1,22 +1,23 @@
use crate::tally_id::TallyID;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use super::TallyID;
use alloc::collections::BTreeMap;
use alloc::string::String;
use serde::Serialize;
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
#[derive(Clone, Serialize)]
pub struct Name {
pub first: String,
pub last: String,
}
#[derive(Deserialize, Serialize, Clone)]
#[derive(Clone, Serialize)]
pub struct IDMapping {
id_map: HashMap<TallyID, Name>,
id_map: BTreeMap<TallyID, Name>,
}
impl IDMapping {
pub fn new() -> Self {
IDMapping {
id_map: HashMap::new(),
id_map: BTreeMap::new(),
}
}
@@ -28,49 +29,3 @@ impl IDMapping {
self.id_map.insert(id, name);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic() {
let mut map = IDMapping::new();
let id1 = TallyID("A2Fb44".to_owned());
let name1 = Name {
first: "Max".to_owned(),
last: "Mustermann".to_owned(),
};
map.add_mapping(id1.clone(), name1.clone());
let res = map.map(&id1);
assert_eq!(res, Some(&name1));
}
#[test]
fn multiple() {
let mut map = IDMapping::new();
let id1 = TallyID("A2Fb44".to_owned());
let name1 = Name {
first: "Max".to_owned(),
last: "Mustermann".to_owned(),
};
let id2 = TallyID("7D3DF5B5".to_owned());
let name2 = Name {
first: "First".to_owned(),
last: "Last".to_owned(),
};
map.add_mapping(id1.clone(), name1.clone());
map.add_mapping(id2.clone(), name2.clone());
let res = map.map(&id1);
assert_eq!(res, Some(&name1));
let res = map.map(&id2);
assert_eq!(res, Some(&name2));
}
}

View File

@@ -1,123 +1,19 @@
use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use tokio::fs;
use super::Date;
use super::IDMapping;
use super::TallyID;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use crate::{store::IDMapping, tally_id::TallyID};
/// Represents a single day that IDs can attend
#[derive(Deserialize, Serialize)]
#[derive(Clone)]
pub struct AttendanceDay {
date: String,
date: Date,
ids: Vec<TallyID>,
}
/// Stores all the days
#[derive(Deserialize, Serialize)]
pub struct IDStore {
days: HashMap<String, AttendanceDay>,
pub mapping: IDMapping,
}
impl IDStore {
pub fn new() -> Self {
IDStore {
days: HashMap::new(),
mapping: IDMapping::new(),
}
}
/// Creats a new `IDStore` from a json file
pub async fn new_from_json(filepath: &str) -> Result<Self> {
let read_string = fs::read_to_string(filepath).await?;
Ok(serde_json::from_str(&read_string)?)
}
/// Add a new id for the current day
/// Returns false if ID is already present at the current day.
pub fn add_id(&mut self, id: TallyID) -> bool {
self.get_current_day().add_id(id)
}
/// Get the `AttendanceDay` of the current day
/// Creates a new if not exists
pub fn get_current_day(&mut self) -> &mut AttendanceDay {
let current_day = get_day_str();
if self.days.contains_key(&current_day) {
return self.days.get_mut(&current_day).unwrap();
}
self.days.insert(
current_day.clone(),
AttendanceDay::new(&current_day.clone()),
);
self.days.get_mut(&current_day.clone()).unwrap()
}
/// Writes the store to a json file
pub async fn export_json(&self, filepath: &str) -> Result<()> {
fs::write(filepath, serde_json::to_string(&self)?).await?;
Ok(())
}
/// Export the store to a csv file.
/// With days in the rows and IDs in the collum.
pub fn export_csv(&self) -> Result<String> {
let mut csv = String::new();
let seperator = ";";
let mut user_ids: HashSet<TallyID> = HashSet::new();
for day in self.days.values() {
for id in day.ids.iter() {
user_ids.insert(id.clone());
}
}
let mut user_ids: Vec<TallyID> = user_ids.into_iter().collect();
user_ids.sort();
let mut days: Vec<String> = self.days.keys().cloned().collect();
days.sort();
let header = days.join(seperator);
csv.push_str(&format!(
"ID{seperator}Nachname{seperator}Vorname{seperator}{header}\n"
));
for user_id in user_ids.iter() {
let id = &user_id.0.to_string();
let name = self.mapping.map(user_id);
let firstname = name.map(|e| e.first.clone()).unwrap_or("".to_owned());
let lastname = name.map(|e| e.last.clone()).unwrap_or("".to_owned());
csv.push_str(&format!("{id}{seperator}{lastname}{seperator}{firstname}"));
for day in days.iter() {
let was_there: bool = self
.days
.get(day)
.ok_or(anyhow!("Failed to access day"))?
.ids
.contains(user_id);
if was_there {
csv.push_str(&format!("{seperator}x"));
} else {
csv.push_str(seperator);
}
}
csv.push('\n');
}
Ok(csv)
}
}
impl AttendanceDay {
fn new(day: &str) -> Self {
fn new(date: Date) -> Self {
Self {
date: day.to_owned(),
date,
ids: Vec::new(),
}
}
@@ -133,7 +29,43 @@ impl AttendanceDay {
}
}
fn get_day_str() -> String {
let now = chrono::offset::Local::now();
now.format("%Y-%m-%d").to_string()
#[derive(Clone)]
pub struct IDStore {
pub days: BTreeMap<Date, AttendanceDay>,
pub mapping: IDMapping,
}
impl IDStore {
pub fn new() -> Self {
IDStore {
days: BTreeMap::new(),
mapping: IDMapping::new(),
}
}
pub fn new_from_storage() -> Self {
// TODO: implement
todo!()
}
/// Add a new id for the current day
/// Returns false if ID is already present at the current day.
pub fn add_id(&mut self, id: TallyID) -> bool {
self.get_current_day().add_id(id)
}
/// Get the `AttendanceDay` of the current day
/// Creates a new if not exists
pub fn get_current_day(&mut self) -> &mut AttendanceDay {
let current_day: Date = 1;
if self.days.contains_key(&current_day) {
return self.days.get_mut(&current_day).unwrap();
}
self.days
.insert(current_day, AttendanceDay::new(current_day));
self.days.get_mut(&current_day.clone()).unwrap()
}
}

Some files were not shown because too many files have changed in this diff Show More