0
0
Fork 0

Compare commits

..

1 commit

Author SHA1 Message Date
Jannik Beyerstedt
202d874dee TMP: Add message generator 2026-07-27 13:45:12 +02:00
32 changed files with 730 additions and 27541 deletions

4
.gitignore vendored
View file

@ -24,7 +24,3 @@ target/
# Ignore .DS_Store file in mac
**/.DS_Store
# Ignore KiCad backups and lock files
hardware/c_its-companion/c_its-companion-backups/
*.kicad_pro.lck

41
Cargo.lock generated
View file

@ -192,9 +192,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "c-its-parser"
version = "2.4.1"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e94d9990b20498f621f164f23ae198c4365115eb4191ae106352ec74d4a4110"
checksum = "cd4db66f495280ec88eef8712dd9ab7d1aacc590f62485e3e03a89b3ce4f8b14"
dependencies = [
"chrono",
"etherparse",
@ -1171,7 +1171,6 @@ dependencies = [
"esp-println",
"esp-radio",
"esp-rtos",
"esp32-cits-core",
"geo-types",
"log",
"mipidsi",
@ -1179,19 +1178,6 @@ dependencies = [
"profont",
"prost",
"prost-build",
"ublox",
]
[[package]]
name = "esp32-cits-core"
version = "0.1.0"
source = "git+https://git.hamburg.ccc.de/jtbx/esp32-cits-core?branch=main#a6d0ea6ca2da130fde2b84060a34ef45e772a459"
dependencies = [
"c-its-parser",
"chrono",
"esp-hal",
"esp-radio",
"geo-types",
]
[[package]]
@ -2504,29 +2490,6 @@ version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
name = "ublox"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bcc96bd937066775fb87034e1dbf6d7165a8975c74ddec1105e31b4a050538b"
dependencies = [
"bitflags",
"chrono",
"num-traits",
"ublox_derive",
]
[[package]]
name = "ublox_derive"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5eda7d1c1151a40ae85c7c065a19959909573535034e18e1015b738993cd2c0"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "ufmt-write"
version = "0.1.0"

View file

@ -14,8 +14,14 @@ name = "test"
path = "./src/test.rs"
required-features = ["std", "cam"]
[[bin]]
## run with cargo run --target host-tuple --bin test-proto --no-default-features -F std,uart
name = "test-proto"
path = "./src/test-proto.rs"
required-features = ["std", "uart", "cam"]
[features]
default = ["esp", "spat", "gnss_ubx", "screen"]
default = ["esp", "spat", "gnss", "screen"]
# build for ESP (needed to build test for `host-tuple`)
esp = [
@ -30,15 +36,11 @@ esp = [
"dep:esp-backtrace",
"dep:esp-println",
"dep:esp-radio",
"esp32-cits-core/esp32c5",
"esp32-cits-core/log-04",
]
std = ["c-its-parser/std", "esp32-cits-core/std"]
std = ["c-its-parser/std"]
# enable u-blox GNSS module
gnss_ubx = ["_gnss", "_uart", "dep:ublox"]
# enable L67K GNSS module
gnss_l67k = ["_gnss", "dep:embassy_gps"]
gnss = ["_gnss", "dep:embassy_gps"]
# echo CAM data on serial
cam = ["c-its-parser/cam"]
# send CAM autonomously
@ -95,7 +97,6 @@ esp-radio = { version = "0.18.0", optional = true, features = [
] }
embassy_gps = { version = "0.1.0", optional = true, default-features = false, features = ["esp", "log-04"], git = "https://github.com/jbeyerstedt/embassy_gps.git", branch = "feat/rework-gpsfix" }
esp32-cits-core = { version = "0.1.0", default-features = false, git = "https://git.hamburg.ccc.de/jtbx/esp32-cits-core", branch = "main" }
embedded-hal-bus = { version = "0.3.0", optional = true }
embedded-graphics = { version = "0.8.2", optional = true }
# needs newer GN with removed bitvec dependency
@ -112,7 +113,6 @@ mipidsi = { version = "0.10.0", optional = true }
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
profont = { version = "0.7.0", optional = true }
prost = { version = "0.14", optional = true, default-features = false, features = ["derive"] }
ublox = { version = "0.10", optional = true, default-features = false, features = ["alloc", "ubx_proto27"] }
[dev-dependencies]
assert_float_eq = "1.2.0"

View file

@ -5,16 +5,15 @@ Displays different C-ITS receive-only use cases for cheap.
## Hardware Requirements
The code is currently tailored to:
- Seeed XIAO ESP32-C5 (8MB PSRAM)
- U-Blox M10Q GNSS (connected on GPIO12 (RX), GPIO11 (TX), and GPIO25 (reset))
or Seeed XIAO L67K (connected on GPIO12 (RX), GPIO11 (TX), GPIO1 (wakeup) and GPIO25 (reset))
only available with `gnss_ubx` or `gnss_l67k` feature (which is included in `spat` feature)
- Seeed XIAO L67K (connected on GPIO12 (RX), GPIO11 (TX), GPIO1 (wakeup) and GPIO25 (reset))
only available with `gnss` feature (which is included in `spat` feature)
- Some ST7789 172*320px screen (connected on GPIO8 (SCL/ SCK), GPIO10 (SDA/ MOSI), GPIO7 (reset), GPIO23 (DC), GPIO24 (CS), TODO GPIO0 (BL))
only available with `screen` feature (which is included in `spat` feature)
## Features
### GNSS
Enable u-blox or L67K GNSS module and in main application logic/ state.
Enable L67K GNSS module and in main application logic/ state.
### SPAT
Takes the current position and heading from the GNSS receiver and displays the signal phases of the upcoming intersection.
@ -33,25 +32,6 @@ Just prints a line for each received CAM.
Generates and transmits a CAM when a GNSS update was received.
Depends on the `gnss` feature.
Note: In germany, using the 5875 to 5915 MHz frequency band is only permitted for C-ITS use cases which implement transmit power reduction measures with at least 30 dB reduction range (or equivalent measures) and have a maximum EIRP of 33 dBm.
This is based on EU regulation 2014/53/EU, so it applies to other EU countries in similar ways.
The relevant harmonized standard is ETSI EN 302 571 which mandates (excerpt of most relevant points):
- a maximum power spectral density of 23 dBm/Mhz EIRP
- transmit power control (TCP) in a range from 3 dBm up to the maximum output power of the equipment
* used for CEN DSRC and HDR DSRC interference mitigation by implementing measures from ETSI TS 102 792
+ Mode A or B: Output power less than 10 dBm EIRP and less than -65 or -45 dBm/MHz EIRP in CEN DSRC band -> **needs to be measured**
+ Mode C: Maximum transmission time of less than 1 ms and at least a few 100 ms between transmission (realistically) -> **can't be ensured with big messages**
+ Mode D: "any" realistically possible transmission time, but at least 500 ms between transmissions for ~5 ITS stations/ 1000ms for 10 ITS stations -> **safe to assume when we only send CAMs at 1 Hz**
+ But modes C and D also need to:
- use protected zone information from CAM messages
- use a protected zone database, e.g. from www.asecap-pzdb.com
* used for dynamic congestion control (DCC)
+ mandatory mechanism described in ETSI EN 302 571, chapter 4.2.10
This is **not** implemented in this application!
**So legally, you're not allowed to send with this.**
### UART
Enable UART I/O protocol according to [docs/uart-protocol.md](./docs/uart-protocol.md).
@ -90,10 +70,10 @@ Additional features are available through feature flags:
- `cam_tx`: Enable CAM generation and transmission (needs time and position source)
Officially supported feature combinations are:
- `esp,spat,gnss_ubx,screen` (default features, "standalone")
- `esp,spat,gnss_ubx,screen,cam_tx` ("standalone with CAM")
- `esp,spat,gnss_ubx,screen,cam_tx,cam,denm`
- `esp,uart,cam,denm` ("smartphone companion")
- `esp,spat,gnss,screen` (default features)
- `esp,spat,gnss,screen,cam_tx`
- `esp,spat,gnss,screen,cam_tx,cam,denm`
- `esp,uart,cam,denm`
Connect your ESP32-C5 and run:

View file

@ -9,12 +9,12 @@ extended=${2:-false}
features_base=(
uart,cam,denm
spat,gnss_ubx,screen
spat,gnss_ubx,screen,cam_tx
spat,gnss,screen
spat,gnss,screen,cam_tx
)
features_more=(
spat,gnss_ubx,screen,cam,denm
spat,gnss_ubx,screen,cam_tx,cam,denm
spat,gnss,screen,cam,denm
spat,gnss,screen,cam_tx,cam,denm
)
features_test=(
spat,cam,denm,uart

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

File diff suppressed because it is too large Load diff

View file

@ -1,138 +0,0 @@
{
"board": {
"active_layer": 0,
"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
},
"prototype_zone_fills": false,
"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",
"board_outline_area",
"ly_points"
],
"visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff",
"zone_display_mode": 0
},
"git": {
"integration_disabled": false,
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "c_its-companion.kicad_prl",
"version": 5
},
"net_inspector_panel": {
"col_hidden": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
],
"col_order": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9
],
"col_widths": [
156,
141,
103,
71,
103,
103,
103,
74,
103,
103
],
"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_time_domain_details": false,
"show_unconnected_nets": false,
"show_zero_pad_nets": false,
"sort_ascending": true,
"sorting_column": 0
},
"open_jobsets": [],
"project": {
"files": []
},
"schematic": {
"hierarchy_collapsed": [],
"selection_filter": {
"graphics": true,
"images": true,
"labels": true,
"lockedItems": false,
"otherItems": true,
"pins": true,
"ruleAreas": true,
"symbols": true,
"text": true,
"wires": true
}
}
}

View file

@ -1,744 +0,0 @@
{
"board": {
"3dviewports": [
{
"name": "ISO-back",
"ww": 1.0,
"wx": 23.768348693847656,
"wy": 1.82621169090271,
"wz": -13.010103225708008,
"xw": 0.0,
"xx": -0.6424655318260193,
"xy": 0.5901128053665161,
"xz": -0.4888812303543091,
"yw": 0.0,
"yx": 0.7663145065307617,
"yy": 0.49455612897872925,
"yz": -0.41009292006492615,
"zw": 0.0,
"zx": -0.00022190232994034886,
"zy": -0.638107419013977,
"zz": -0.7699472904205322
},
{
"name": "ISO-front",
"ww": 1.0,
"wx": 4.608643054962158,
"wy": 18.730253219604492,
"wz": -27.170215606689453,
"xw": 0.0,
"xx": 0.6427879333496094,
"xy": -0.5868241190910339,
"xz": 0.49240362644195557,
"yw": 0.0,
"yx": 0.7660443186759949,
"yy": 0.4924042820930481,
"yz": -0.4131760001182556,
"zw": 0.0,
"zx": -2.1759660739917308e-08,
"zy": 0.6427874565124512,
"zz": 0.7660446763038635
}
],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_barcodes": false,
"apply_defaults_to_fp_dimensions": false,
"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.8,
"height": 1.27,
"width": 1.27
},
"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": {
"border_display_style": 2,
"border_hatch_pitch": 0.5,
"corner_radius": 0.0,
"corner_smoothing": 0,
"fill_mode": 0,
"hatch_gap": 1.5,
"hatch_orientation": 0.0,
"hatch_smoothing_level": 0,
"hatch_smoothing_value": 0.1,
"hatch_thickness": 1.0,
"min_clearance": 0.5,
"min_island_area": 10.0,
"min_thickness": 0.25,
"pad_connection": 1,
"remove_islands": 0,
"thermal_relief_gap": 0.5,
"thermal_relief_spoke_width": 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_field_mismatch": "warning",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_to_hole": "warning",
"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",
"missing_tuning_profile": "warning",
"net_conflict": "warning",
"nonmirrored_text_on_back_layer": "warning",
"npth_inside_courtyard": "error",
"padstack": "warning",
"pth_inside_courtyard": "error",
"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_not_centered_on_via": "ignore",
"track_on_post_machined_layer": "error",
"track_segment_length": "error",
"track_width": "error",
"tracks_crossing": "error",
"tuning_profile_track_geometries": "ignore",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.0,
"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.25,
"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.3,
"min_track_width": 0.2,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.5,
"solder_mask_to_copper_clearance": 0.005,
"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": {
"bom_rev": "",
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": "",
"sch_revision": "v0.1"
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"component_class_settings": {
"assignments": [],
"meta": {
"version": 0
},
"sheet_component_classes": {
"enabled": false
}
},
"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",
"field_name_whitespace": "warning",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"ground_pin_not_ground": "warning",
"hier_label_mismatch": "error",
"isolated_pin_label": "warning",
"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",
"stacked_pin_name": "warning",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"undefined_netclass": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "c_its-companion.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,
"tuning_profile": "",
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
},
{
"name": "Power",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 0,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.5,
"tuning_profile": ""
}
],
"meta": {
"version": 5
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": [
{
"netclass": "Power",
"pattern": "GND"
},
{
"netclass": "Power",
"pattern": "+3V3"
},
{
"netclass": "Power",
"pattern": "/BAT*"
},
{
"netclass": "Power",
"pattern": "/V_*"
}
]
},
"pcbnew": {
"last_paths": {
"idf": "",
"netlist": "",
"plot": "",
"specctra_dsn": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"annotation": {
"method": 0,
"sort_order": 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": "Reference"
},
"bus_aliases": {},
"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": [],
"hop_over_size_choice": 0,
"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
},
"page_layout_descr_file": "",
"plot_directory": "",
"reuse_designators": true,
"subpart_first_id": 65,
"subpart_id_separator": 0,
"top_level_sheets": [
{
"filename": "c_its-companion.kicad_sch",
"name": "Root",
"uuid": "9ad468b0-d7e4-4d4a-b652-c4a81a952e30"
}
],
"used_designators": "C1,#PWR1-10,J1-2,SW1,TP1,U1-2,#FLG1-3",
"variants": []
},
"sheets": [
[
"9ad468b0-d7e4-4d4a-b652-c4a81a952e30",
"Root"
]
],
"text_variables": {},
"tuning_profiles": {
"meta": {
"version": 0
},
"tuning_profiles_impedance_geometric": []
}
}

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
{"ARCHIVE_NAME": "", "EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false, "OPEN BROWSER": true, "BACKUP_OPT": true}

View file

@ -1,4 +0,0 @@
(fp_lib_table
(version 7)
(lib (name "library") (type "KiCad") (uri "${KIPRJMOD}/library") (options "") (descr ""))
)

View file

@ -1,542 +0,0 @@
(footprint "XIAO-ESP32-C5-DIP"
(version 20241229)
(generator "pcbnew")
(generator_version "9.0")
(layer "F.Cu")
(property "Reference" "REF**"
(at -0.275 -10.275 0)
(unlocked yes)
(layer "F.SilkS")
(uuid "7bab359d-5e5b-4dff-9db4-e7c8562ce635")
(effects
(font
(size 0.635 0.635)
(thickness 0.1016)
)
)
)
(property "Value" "XIAO-ESP32-C5-DIP"
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(uuid "79cabf23-4ebc-4d37-9172-bb2cf4ea975f")
(effects
(font
(size 0.635 0.635)
(thickness 0.1016)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "663dc686-104e-47e1-9669-e1228477fa6f")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "a25b2a4a-94ce-41bd-8341-b98204aa9142")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(attr smd)
(fp_line
(start -10.414 -6.985)
(end -10.414 6.985)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "e213e2f5-659f-4cdb-b390-b284746621aa")
)
(fp_line
(start -8.509 -8.89)
(end 8.636 -8.89)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "f58afa0e-63f9-4096-9d17-7cf8e5de7759")
)
(fp_line
(start -8.509 8.89)
(end 8.636 8.89)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "9c6409b2-5f1b-4299-85bf-d07c96f13a2a")
)
(fp_line
(start 10.541 -4.495)
(end 11.551272 -4.491272)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "4c45fdff-98df-401f-bf6d-768af24b67a9")
)
(fp_line
(start 10.541 6.985)
(end 10.541 -6.985)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "eb7ba2c6-3729-4940-9c6b-4ac369cf3075")
)
(fp_line
(start 10.541 6.985)
(end 10.541 -6.985)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "6f680559-bbef-46de-9ef5-efda7c2411e9")
)
(fp_line
(start 11.551 4.504)
(end 10.541 4.504)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "eddbec16-11c2-42ad-90d6-191cfc991c10")
)
(fp_line
(start 12.051 -3.991272)
(end 12.051 4.004)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "e973f8d1-81df-437d-bffe-4f734e1ac18e")
)
(fp_arc
(start -10.414 -6.985)
(mid -9.856038 -8.332038)
(end -8.509 -8.89)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "88e5d6ea-6b70-4830-9817-980fd2b32f79")
)
(fp_arc
(start -8.509 8.89)
(mid -9.856038 8.332038)
(end -10.414 6.985)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "f108f05c-d5f6-4de1-bc6f-4c31ef53e967")
)
(fp_arc
(start 8.636 -8.89)
(mid 9.983038 -8.332038)
(end 10.541 -6.985)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "c6870bc4-897a-485a-b951-3feda1bdd32e")
)
(fp_arc
(start 10.541 6.985)
(mid 9.983038 8.332038)
(end 8.636 8.89)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "e1d8f660-1495-43db-8a6b-7fdc7dc42e35")
)
(fp_arc
(start 11.551272 -4.491272)
(mid 11.904644 -4.344724)
(end 12.051 -3.991272)
(stroke
(width 0.127)
(type default)
)
(layer "F.SilkS")
(uuid "a9b2ba2a-ad25-4191-9070-ebc0333a9101")
)
(fp_arc
(start 12.051 4.004)
(mid 11.904553 4.357553)
(end 11.551 4.504)
(stroke
(width 0.127)
(type default)
)
(layer "F.SilkS")
(uuid "45e349ac-e285-4bed-a05b-ae5a80336af0")
)
(fp_circle
(center 9.37 -7.857)
(end 9.624 -7.857)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.SilkS")
(uuid "6839324b-c75a-49ba-8c0c-16e2191166eb")
)
(fp_circle
(center 10.25 -9)
(end 10.504 -9)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.SilkS")
(uuid "6e5f4857-2ad9-4493-8234-6091a6821232")
)
(fp_rect
(start -10.425 -8.9)
(end 10.55 8.9)
(stroke
(width 0.05)
(type default)
)
(fill no)
(layer "F.CrtYd")
(uuid "b53697bc-d105-40e2-af66-708b16a7f26c")
)
(fp_rect
(start -10.425 -8.9)
(end 10.55 8.9)
(stroke
(width 0.1)
(type default)
)
(fill no)
(layer "F.Fab")
(uuid "a45a327e-f036-4877-8d89-39f4f4b0c715")
)
(fp_circle
(center 9.376 -7.854)
(end 9.63 -7.854)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.Fab")
(uuid "12a1812e-f866-498e-9d6e-50e427087f34")
)
(pad "1" smd roundrect
(at 7.62 -8.455 90)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "82bbfb55-8baa-4218-ac89-ab07d82ef7f8")
)
(pad "1" thru_hole circle
(at 7.62 -7.62 90)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "f2fffd4f-460c-4b4d-b954-811e935ed1d6")
)
(pad "2" smd roundrect
(at 5.08 -8.455 90)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "18ef9914-2d15-494c-9020-2334abcc1460")
)
(pad "2" thru_hole circle
(at 5.08 -7.62 90)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "4732cc3f-e641-4870-a9b3-ae13729c7bba")
)
(pad "3" smd roundrect
(at 2.54 -8.455 90)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "15338a55-8514-4218-a2c1-2cae003104c3")
)
(pad "3" thru_hole circle
(at 2.54 -7.62 90)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "0cef5e55-d8ff-4394-9cc1-cc318608b0d9")
)
(pad "4" smd roundrect
(at 0 -8.455 90)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "191a259d-5773-4a80-91de-baa69611fab8")
)
(pad "4" thru_hole circle
(at 0 -7.62 90)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "200b8da4-9395-48a5-b7ac-92a6a147c215")
)
(pad "5" smd roundrect
(at -2.54 -8.455 90)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "52012467-1c46-4a8e-aa82-6f6ce69bd2ca")
)
(pad "5" thru_hole circle
(at -2.54 -7.62 90)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "2b50ccce-3634-47d1-bf7d-a3db4f3f9a9f")
)
(pad "6" smd roundrect
(at -5.08 -8.455 90)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "04aeaf95-142a-40f6-97a3-04453fc50824")
)
(pad "6" thru_hole circle
(at -5.08 -7.62 90)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "7636de9d-d8e5-49c9-9fff-0cd76515690d")
)
(pad "7" smd roundrect
(at -7.62 -8.455 90)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "e997fd0b-233a-4656-afad-cb2f8b9d87c9")
)
(pad "7" thru_hole circle
(at -7.62 -7.62 90)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "9d2c0d0c-05dd-477e-8384-b14eec83fc2e")
)
(pad "8" thru_hole circle
(at -7.62 7.62 270)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "1e50c66f-49b8-4b03-9889-8cc6ca45ad8e")
)
(pad "8" smd roundrect
(at -7.62 8.455 270)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "e7b72803-b5f0-433c-8313-83247355fb43")
)
(pad "9" thru_hole circle
(at -5.08 7.62 270)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "808bf9dc-1c26-4a77-93c9-e4c6d5eccbb2")
)
(pad "9" smd roundrect
(at -5.08 8.455 270)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "532de3b6-bd30-47c5-b70e-0bca6abbd997")
)
(pad "10" thru_hole circle
(at -2.54 7.62 270)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "a792a2ac-537b-4136-8f92-b81f7ce8f753")
)
(pad "10" smd roundrect
(at -2.54 8.455 270)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "6e3f5c75-6143-468e-bb07-d89020c03f59")
)
(pad "11" thru_hole circle
(at 0 7.62 270)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "fb78046a-9b91-4893-b010-827957cc07be")
)
(pad "11" smd roundrect
(at 0 8.455 270)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "5249f425-bece-432d-8076-28e1b8b386e3")
)
(pad "12" thru_hole circle
(at 2.54 7.62 270)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "0b8ccc97-5100-4dcf-8500-e99789ee0b4c")
)
(pad "12" smd roundrect
(at 2.54 8.455 270)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "82f1bbd1-708b-4d2d-9113-7ba3c2313670")
)
(pad "13" thru_hole circle
(at 5.08 7.62 270)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "03882061-01af-484b-b620-9e92a3daaf68")
)
(pad "13" smd roundrect
(at 5.08 8.455 270)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "954f51d1-3d75-42fb-a02a-7389b30718d9")
)
(pad "14" thru_hole circle
(at 7.62 7.62 270)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "c6d1af47-da13-4409-9ab1-55d358cc57eb")
)
(pad "14" smd roundrect
(at 7.62 8.455 270)
(size 2.432 1.524)
(layers "F.Cu" "F.Mask")
(roundrect_rratio 0.1)
(thermal_bridge_angle 45)
(uuid "6b725fd4-d6f0-46bf-b3e0-7ec07cc05622")
)
(embedded_fonts no)
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/XIAO-nRF52840 v15.step"
(hide yes)
(offset
(xyz -1.778 -6.096 0.381)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz -90 0 0)
)
)
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/Seeeduino XIAO SAMD21 v4.step"
(offset
(xyz 0 0 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz 0 0 0)
)
)
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/Seeed Studio XIAO RP2040 v26.step"
(hide yes)
(offset
(xyz 0 0 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz 0 0 90)
)
)
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/Seeed Studio XIAO-ESP32-C3.step"
(hide yes)
(offset
(xyz -1.7018 -6.096 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz -90 0 0)
)
)
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/XIAO-ESP32S3 v2.step"
(hide yes)
(offset
(xyz -1.778 -6.1722 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz -90 0 0)
)
)
)

View file

@ -1,558 +0,0 @@
(footprint "XIAO-ESP32-C5-SMD-DIP"
(version 20260206)
(generator "pcbnew")
(generator_version "10.0")
(layer "F.Cu")
(property "Reference" "REF**"
(at 7.312 -21.336 0)
(layer "F.SilkS")
(uuid "95a6896c-2eab-4f28-adc5-7fd64550efd1")
(effects
(font
(size 0.78232 0.78232)
(thickness 0.10668)
)
(justify left bottom)
)
)
(property "Value" "Val**"
(at 8.89 -10.795 0)
(layer "F.Fab")
(uuid "ead571aa-8f25-4413-9dd8-3ecea3d8b1b5")
(effects
(font
(size 0.536448 0.536448)
(thickness 0.073152)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "e0e614f2-f99b-42a7-87d5-6d40f74c047a")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "074d8848-2ba7-4146-8c49-a0cf65e2aa82")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(duplicate_pad_numbers_are_jumpers no)
(fp_line
(start 0 -1.987)
(end 0 -19.132)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "e72c3c07-5bc8-4f5f-97cd-36412308e250")
)
(fp_line
(start 1.905 -0.082)
(end 15.875 -0.082)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "e3e0ab25-f18a-4068-92e8-92a17b4caf05")
)
(fp_line
(start 4.595 -21.037)
(end 4.598728 -22.047272)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "4838598f-afa6-445e-b237-7544ae7e9968")
)
(fp_line
(start 5.098728 -22.547)
(end 13.094 -22.547)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "101b5417-f570-447c-b9f6-42639e5eff3c")
)
(fp_line
(start 13.594 -22.047)
(end 13.594 -21.037)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "37ee6532-c85a-4899-82db-921e02cce4af")
)
(fp_line
(start 15.875 -21.037)
(end 1.905 -21.037)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "f94b2798-d11f-47d7-8f2c-02478417b6f1")
)
(fp_line
(start 17.78 -1.987)
(end 17.78 -19.132)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "8c3282cc-86c2-4cf3-a698-856916df5f40")
)
(fp_arc
(start 0 -19.136)
(mid 0.557962 -20.483038)
(end 1.905 -21.041)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "2a1b788b-cdcf-4a49-acac-d187cf344ffc")
)
(fp_arc
(start 1.905 -0.082)
(mid 0.557962 -0.639962)
(end 0 -1.987)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "677c2a8e-0212-4651-99b4-af42d29d0bd1")
)
(fp_arc
(start 4.598728 -22.047272)
(mid 4.745276 -22.400644)
(end 5.098728 -22.547)
(stroke
(width 0.127)
(type default)
)
(layer "F.SilkS")
(uuid "b38fc005-51b4-4609-b7e3-78320e82da0d")
)
(fp_arc
(start 13.094 -22.547)
(mid 13.447553 -22.400553)
(end 13.594 -22.047)
(stroke
(width 0.127)
(type default)
)
(layer "F.SilkS")
(uuid "46a43512-01fe-440e-b1e8-31a50057cbf8")
)
(fp_arc
(start 15.875 -21.037)
(mid 17.222038 -20.479038)
(end 17.78 -19.132)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "f77dffb2-d331-43db-9018-069e9fa47c57")
)
(fp_arc
(start 17.78 -1.987)
(mid 17.222038 -0.639962)
(end 15.875 -0.082)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "535b0a70-da0d-4557-a5dd-d25a51ee5468")
)
(fp_circle
(center -0.21 -20.621)
(end -0.21 -20.875)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.SilkS")
(uuid "c4f8c64e-4c5d-41c1-8b14-aca8fcf77968")
)
(fp_circle
(center 1.064 -19.904)
(end 1.064 -20.158)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.SilkS")
(uuid "163ea809-1cd6-43c9-968e-14c093715603")
)
(fp_rect
(start 17.8 0)
(end 0 -21)
(stroke
(width 0.05)
(type default)
)
(fill no)
(layer "F.CrtYd")
(uuid "477d102d-f2d0-4f67-ad93-9f60da51bd76")
)
(fp_rect
(start 0 -21)
(end 17.8 0)
(stroke
(width 0.1)
(type default)
)
(fill no)
(layer "F.Fab")
(uuid "e7107e2f-485b-49f6-9f5d-2537ab0ef458")
)
(fp_circle
(center 1.084 -19.9)
(end 1.084 -20.154)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.Fab")
(uuid "089122fe-193e-4cef-a9b8-54d37cb7fa99")
)
(pad "1" smd roundrect
(at 0.835 -18.12)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "351f5f05-8bc3-4b53-9daf-3207bd71876b")
)
(pad "1" thru_hole circle
(at 1.26 -18.14 180)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "6a16c3b2-8d4e-4efe-a84e-dbc418c7fe79")
)
(pad "2" smd roundrect
(at 0.835 -15.58)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "cfdee4aa-d600-484f-9a5c-2062a00bde1d")
)
(pad "2" thru_hole circle
(at 1.26 -15.6 180)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "69193d02-4e31-4519-a90d-949c3f48e92c")
)
(pad "3" smd roundrect
(at 0.835 -13.04)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "52a69e1c-edab-4079-a491-6bcb12907f61")
)
(pad "3" thru_hole circle
(at 1.26 -13.06 180)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "a3aed707-7e8b-4b1b-ba96-06cfb9154fcb")
)
(pad "4" smd roundrect
(at 0.835 -10.5)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "1e864c46-432c-460c-af69-13a1c9d0fd2f")
)
(pad "4" thru_hole circle
(at 1.26 -10.52 180)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "eee65312-1942-42a1-a13a-2a28a114ec95")
)
(pad "5" smd roundrect
(at 0.835 -7.96)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "da854e17-c3b3-4a83-9d47-a379e4d7efa5")
)
(pad "5" thru_hole circle
(at 1.26 -7.98 180)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "febdd29f-f361-4b8e-b2b5-7502d3e5fdea")
)
(pad "6" smd roundrect
(at 0.835 -5.42)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "3fde7f90-983d-44c1-939e-47d36175373f")
)
(pad "6" thru_hole circle
(at 1.26 -5.44 180)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "323ef0a5-3407-4ac3-b76f-8ac19e044d1e")
)
(pad "7" smd roundrect
(at 0.835 -2.88)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "ebfd6bef-ea17-4635-877b-58b57c784dec")
)
(pad "7" thru_hole circle
(at 1.26 -2.9 180)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "2b179783-65b0-4d7c-850b-74c9fa3a7a64")
)
(pad "8" thru_hole circle
(at 16.5 -2.9)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "5cbc8aa6-fdfd-49e6-b018-da1d4ff342aa")
)
(pad "8" smd roundrect
(at 17 -2.88)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "19eb14d0-5713-47b7-86f2-4bccede8ba54")
)
(pad "9" thru_hole circle
(at 16.5 -5.44)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "e7f197b5-1fc8-47f6-9f49-60ffd6fa2335")
)
(pad "9" smd roundrect
(at 17 -5.42)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "4c3b83bc-a000-4a70-887c-6b775bc40ee1")
)
(pad "10" thru_hole circle
(at 16.5 -7.98)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "4df7fd17-71d5-4f8f-a3f2-68c6537a767f")
)
(pad "10" smd roundrect
(at 17 -7.96)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "223d5c33-d2a3-4398-8d3f-544bd6345a86")
)
(pad "11" thru_hole circle
(at 16.5 -10.52)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "dad67976-a604-485a-bb37-2d67ea771b2c")
)
(pad "11" smd roundrect
(at 17 -10.5)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "1f04f1d2-ce82-4256-b122-36f63f189efe")
)
(pad "12" thru_hole circle
(at 16.5 -13.06)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "c55d522b-9f8b-4fa4-afaa-880f3b1c9c5f")
)
(pad "12" smd roundrect
(at 17 -13.04)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "31818d88-bbc0-4c22-b4ed-d6884ef71ebb")
)
(pad "13" thru_hole circle
(at 16.5 -15.6)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "e46165bc-b289-44b0-b7f6-6759bba3d4b8")
)
(pad "13" smd roundrect
(at 17 -15.58)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "3fbf02f8-81da-4fba-bc95-9e47b9abaa05")
)
(pad "14" thru_hole circle
(at 16.5 -18.14)
(size 1.524 1.524)
(drill 0.889)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "71ca3093-fd4e-46c3-9980-6d986d711719")
)
(pad "14" smd roundrect
(at 17 -18.12)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "ce7a6ff3-5c3a-4077-bf86-beedc24debb3")
)
(pad "15" smd circle
(at 7.6166 -19.1415)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "7273149b-9aea-4a23-9b4e-db1e0d6c76fc")
)
(pad "16" smd circle
(at 10.1566 -19.1415)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "0aef11c3-14ad-4cb5-9b05-95a892988ce8")
)
(pad "17" smd circle
(at 7.6166 -16.6015)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "158a5cbb-af86-4fa6-8d58-6831485085c5")
)
(pad "18" smd circle
(at 10.1566 -16.6015)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "3b7bcd8a-c97c-46f6-9faa-4e525d03d6e8")
)
(pad "19" smd circle
(at 7.6166 -14.0615)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "ad4ba963-211d-4878-a4cb-255a615c14de")
)
(pad "20" smd circle
(at 10.1566 -14.0615)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "8c7e088d-0846-4d7d-9143-5ce3fdb0c4fd")
)
(pad "21" smd circle
(at 7.6166 -11.5215)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "c6bbe198-c8c7-4b83-a36e-6a6fdeb3b94f")
)
(pad "22" smd circle
(at 10.064 -11.5783)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "4f1e574b-3b71-4a23-b765-d779726fd3c6")
)
(pad "23" smd roundrect
(at 7.3086 -4.9683 270)
(size 2.5 1.1)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(thermal_bridge_angle 45)
(uuid "08d345bf-1b32-499f-94bb-44332eb5115d")
)
(pad "24" smd roundrect
(at 9.8486 -4.9683 270)
(size 2.5 1.1)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(thermal_bridge_angle 45)
(uuid "8b3ca1bb-eee0-4c3d-8f3b-eefea4a68bbb")
)
(pad "25" smd roundrect
(at 9.949 -8.412 270)
(size 3.1 1.9)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(thermal_bridge_angle 45)
(uuid "45570478-e499-45ab-bb97-539cabc4e177")
)
(embedded_fonts no)
)

View file

@ -1,445 +0,0 @@
(footprint "XIAO-ESP32-C5-SMD"
(version 20241229)
(generator "pcbnew")
(generator_version "9.0")
(layer "F.Cu")
(property "Reference" "REF**"
(at 7.312 -21.336 0)
(layer "F.SilkS")
(uuid "a9f5f5dd-597c-45dd-b31d-a35c3963eed2")
(effects
(font
(size 0.78232 0.78232)
(thickness 0.10668)
)
(justify left bottom)
)
)
(property "Value" "Val**"
(at 8.89 -10.795 0)
(layer "F.Fab")
(uuid "2d66aca7-e0b3-44df-838e-be285890792c")
(effects
(font
(size 0.536448 0.536448)
(thickness 0.073152)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "5173b428-b87b-497c-9fd1-0d444c2ced85")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "ea7181c4-fdca-44f1-ad15-d0627f50a68e")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(fp_line
(start 0 -1.987)
(end 0 -19.132)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "df0486b4-d04f-4201-a301-beede6e15862")
)
(fp_line
(start 1.905 -0.082)
(end 15.875 -0.082)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "f54abd8d-62ed-4b05-8e37-f29acdadc0f8")
)
(fp_line
(start 4.595 -21.037)
(end 4.598728 -22.047272)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "fead6daa-b9f4-4bd1-a131-b5d2cd3184de")
)
(fp_line
(start 5.098728 -22.547)
(end 13.094 -22.547)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "b90ae92d-7c1a-4689-bf64-c3528a3ea3ee")
)
(fp_line
(start 13.594 -22.047)
(end 13.594 -21.037)
(stroke
(width 0.127)
(type solid)
)
(layer "F.SilkS")
(uuid "b540fbcc-8e06-44d7-95f6-8b142e4c90fb")
)
(fp_line
(start 15.875 -21.037)
(end 1.905 -21.037)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "2b80542c-5088-4f89-ae32-3400cf894ca1")
)
(fp_line
(start 17.78 -1.987)
(end 17.78 -19.132)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "4cd8add0-8a8c-468d-ba6f-b86787c288af")
)
(fp_arc
(start 0 -19.136)
(mid 0.557962 -20.483038)
(end 1.905 -21.041)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "4d9bf7a5-4ab4-48bb-b8ac-0a5ce4523642")
)
(fp_arc
(start 1.905 -0.082)
(mid 0.557962 -0.639962)
(end 0 -1.987)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "d09661c8-1ad3-40e9-8323-9dabc8f3f98b")
)
(fp_arc
(start 4.598728 -22.047272)
(mid 4.745276 -22.400644)
(end 5.098728 -22.547)
(stroke
(width 0.127)
(type default)
)
(layer "F.SilkS")
(uuid "7a58f637-14d7-4804-b127-f1d029b5d1e6")
)
(fp_arc
(start 13.094 -22.547)
(mid 13.447553 -22.400553)
(end 13.594 -22.047)
(stroke
(width 0.127)
(type default)
)
(layer "F.SilkS")
(uuid "d0bc8663-987f-4e2e-b464-096a547b822f")
)
(fp_arc
(start 15.875 -21.037)
(mid 17.222038 -20.479038)
(end 17.78 -19.132)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "77004ea5-53a4-40ab-b35c-7bdd22107e34")
)
(fp_arc
(start 17.78 -1.987)
(mid 17.222038 -0.639962)
(end 15.875 -0.082)
(stroke
(width 0.1)
(type solid)
)
(layer "F.SilkS")
(uuid "94189dc1-14b5-4f17-b9d3-0163ed99e583")
)
(fp_circle
(center -0.21 -20.621)
(end -0.21 -20.875)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.SilkS")
(uuid "dc606cc5-10bc-4635-81d1-fa27b1e6b4e2")
)
(fp_circle
(center 1.064 -19.904)
(end 1.064 -20.158)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.SilkS")
(uuid "2b5e2829-6d01-441f-9983-fc4a273ff483")
)
(fp_rect
(start 17.8 0)
(end 0 -21)
(stroke
(width 0.05)
(type default)
)
(fill no)
(layer "F.CrtYd")
(uuid "e428eb56-00a2-4249-bb91-d2e5823b2899")
)
(fp_rect
(start 0 -21)
(end 17.8 0)
(stroke
(width 0.1)
(type default)
)
(fill no)
(layer "F.Fab")
(uuid "2133d927-559d-4615-ab92-732772c02d94")
)
(fp_circle
(center 1.084 -19.9)
(end 1.084 -20.154)
(stroke
(width 0.5)
(type solid)
)
(fill yes)
(layer "F.Fab")
(uuid "50e04a65-8ff2-4d1f-be98-2db48101e90a")
)
(pad "1" smd roundrect
(at 0.835 -18.12)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "c68f6b12-dcf5-46b1-b159-2eef9023b0ac")
)
(pad "2" smd roundrect
(at 0.835 -15.58)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "525b09fc-9099-4274-98c3-45f84d0591ab")
)
(pad "3" smd roundrect
(at 0.835 -13.04)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "99cc76a7-0a78-4868-871b-c214ecac5525")
)
(pad "4" smd roundrect
(at 0.835 -10.5)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "8038b67b-b3bf-4a3e-b790-40857963c957")
)
(pad "5" smd roundrect
(at 0.835 -7.96)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "84711cb2-d222-464e-bcd5-f76af75cc612")
)
(pad "6" smd roundrect
(at 0.835 -5.42)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "aabcfc3b-fa72-4e48-95d1-d53474158cc0")
)
(pad "7" smd roundrect
(at 0.835 -2.88)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "3e086061-1f3f-4b1e-9354-920126d40249")
)
(pad "8" smd roundrect
(at 17 -2.88)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "974a7e29-b5d3-475c-b481-2bc14f5b3495")
)
(pad "9" smd roundrect
(at 17 -5.42)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "a553cc0b-22bc-49aa-9fb7-680c812e8abd")
)
(pad "10" smd roundrect
(at 17 -7.96)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "b650936d-c436-4d93-abb7-7957add5cfb1")
)
(pad "11" smd roundrect
(at 17 -10.5)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "177728f1-7b02-41a6-9de4-bdaee546f6d9")
)
(pad "12" smd roundrect
(at 17 -13.04)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "eb6539ef-4d84-4ae5-bc4e-237dae21b9ef")
)
(pad "13" smd roundrect
(at 17 -15.58)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "c8744fbe-93c9-4c54-84bc-435357de679f")
)
(pad "14" smd roundrect
(at 17 -18.12)
(size 2.75 2)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(solder_mask_margin 0.0508)
(thermal_bridge_angle 45)
(uuid "fd579d63-bbef-4609-9252-aad3f944a314")
)
(pad "15" smd circle
(at 7.6166 -19.1415)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "ad890579-1e55-4b71-9d59-080a59598896")
)
(pad "16" smd circle
(at 10.1566 -19.1415)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "25d6fdae-01ac-4ca4-8702-55badc8bcbc8")
)
(pad "17" smd circle
(at 7.6166 -16.6015)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "e73ae295-736f-4acf-8ee1-0c120ebcebea")
)
(pad "18" smd circle
(at 10.1566 -16.6015)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "7adfba87-f861-42a5-bb73-c3f9fac53180")
)
(pad "19" smd circle
(at 7.6166 -14.0615)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "619dc2a5-519d-48ac-9c78-8c438df2a14d")
)
(pad "20" smd circle
(at 10.1566 -14.0615)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "aa8b72d0-17bf-47d4-94b1-df9199638a29")
)
(pad "21" smd circle
(at 7.6166 -11.5215)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "8539b5cc-db2c-4fa4-9ead-b842a1885293")
)
(pad "22" smd circle
(at 10.064 -11.5783)
(size 1.7 1.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "313edcfe-ad25-4ea7-b786-d8290dfb479a")
)
(pad "23" smd roundrect
(at 7.3086 -4.9683 270)
(size 2.5 1.1)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(thermal_bridge_angle 45)
(uuid "144d3c3d-6207-433c-9e61-4f75722af865")
)
(pad "24" smd roundrect
(at 9.8486 -4.9683 270)
(size 2.5 1.1)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(thermal_bridge_angle 45)
(uuid "3780a4ef-348c-4241-b04b-2cb4f7127fa2")
)
(pad "25" smd roundrect
(at 9.949 -8.412 270)
(size 3.1 1.9)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.25)
(thermal_bridge_angle 45)
(uuid "00be3957-5a21-4898-8292-59f5f447623f")
)
(embedded_fonts no)
)

View file

@ -1,4 +0,0 @@
(sym_lib_table
(version 7)
(lib (name "Seeed_Studio_XIAO_Series") (type "KiCad") (uri "${KIPRJMOD}/library/Seeed_Studio_XIAO_Series.kicad_sym") (options "") (descr ""))
)

View file

@ -1,6 +0,0 @@
#!/bin/sh
## Builds common config for smartphone connected device
## -> only receive CAM and DENMs via UART (TODO: and BLE)
cargo run --release --no-default-features -F esp,cam,denm,uart

View file

@ -1,6 +0,0 @@
#!/bin/sh
## Builds common config for standalone device with automatic CAM transmission
## -> enable SPAT/ GLOSA with GNSS and a screen + CAM transmission
cargo run --release -F cam_tx

View file

@ -1,6 +0,0 @@
#!/bin/sh
## Builds common config for standalone device
## -> enable SPAT/ GLOSA with GNSS and a screen
cargo run --release

View file

@ -11,45 +11,6 @@ const PH_MAX_LENGHT_M: f32 = 500.;
const PH_MAX_ITEMS: usize = 23;
const PH_MIN_DIST_M: f32 = 20.;
/// Stand-alone CAM generator
///
/// Generates CAM messages from time and [`PosVel`] updates
///
/// # Send a message
/// ```
/// use esp32_cits_core::tx::GnItsPayload as _;
///
/// let (mut seq_no, mac_bytes) = {
/// // unwrap is fine since [`esp_hal::efuse::MacAddress`] is just an `[u8; 6]`
/// let mac_bytes = mac.as_bytes().try_into().unwrap();
///
/// (0, mac_bytes)
/// };
///
/// let mut cam = {
/// let station_id = make_station_id(mac);
/// info!("V2X Station ID: {station_id}");
///
/// applogic::cam_tx::CamState::new(
/// station_id,
/// OWN_STATION_TYPE,
/// OWN_VEHICLE_LENGTH,
/// OWN_VEHICLE_WIDTH,
/// )
/// };
///
/// cam.update(time, fix.clone().into());
/// match cam.encode_packet(mac_bytes, pos_vel, time, GN_IS_MOBILE, &mut seq_no) {
/// Ok(data) => {
/// if let Err(err) =
/// radio::send_80211_bcast_frame(&mut wlan_iface, &mac_bytes.clone(), &data)
/// {
/// warn!("Failed to send 802.11 frame: {err}");
/// }
/// }
/// Err(err) => warn!("Failed to create CAM: {err}"),
/// }
/// ```
#[derive(Debug)]
pub struct CamState {
station_id: u32,
@ -59,7 +20,7 @@ pub struct CamState {
vehicle_width_dm: u8, // vehicle width is in 10cm steps
time: chrono::DateTime<chrono::Utc>,
pos_state: esp32_cits_core::PosVel,
pos_state: super::PositionState,
/// database for path history, oldest item at the back of the vector
path_history: Vec<PathPoint>,
@ -74,7 +35,7 @@ struct PathPoint {
}
impl PathPoint {
fn new_from_pos(pos: &esp32_cits_core::PosVel, time: chrono::DateTime<chrono::Utc>) -> Self {
fn new_from_pos(pos: &super::PositionState, time: chrono::DateTime<chrono::Utc>) -> Self {
Self {
pos: pos.position,
time,
@ -83,7 +44,7 @@ impl PathPoint {
}
fn new_from_pos_with_dist(
pos: &esp32_cits_core::PosVel,
pos: &super::PositionState,
time: chrono::DateTime<chrono::Utc>,
dist_m: f32,
) -> Self {
@ -108,12 +69,12 @@ impl CamState {
vehicle_length_dm,
vehicle_width_dm,
time: chrono::DateTime::<chrono::Utc>::default(),
pos_state: esp32_cits_core::PosVel::default(),
pos_state: super::PositionState::default(),
path_history: Vec::with_capacity(PH_MAX_ITEMS + 1),
}
}
pub fn update(&mut self, time: chrono::DateTime<chrono::Utc>, pos: esp32_cits_core::PosVel) {
pub fn update(&mut self, time: chrono::DateTime<chrono::Utc>, pos: super::PositionState) {
self.time = time;
self.pos_state = pos;
@ -125,7 +86,7 @@ impl CamState {
fn update_ph(
path_history: &mut Vec<PathPoint>,
time: chrono::DateTime<chrono::Utc>,
pos: &esp32_cits_core::PosVel,
pos: &super::PositionState,
) {
// Update path history
if path_history.is_empty() {
@ -332,11 +293,11 @@ impl CamState {
}
}
impl esp32_cits_core::tx::GnItsPayload for CamState {
impl crate::v2x_tx::GnItsPayload for CamState {
fn make_eh(
&self,
address: [u8; 6],
own_position: &esp32_cits_core::PosVel,
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
_seq_no: &mut u16,
) -> Result<
@ -352,7 +313,7 @@ impl esp32_cits_core::tx::GnItsPayload for CamState {
// CAM always uses SHB, hop-limit 1
Ok((
esp32_cits_core::tx::gn::make_shb_eh(address, station_type, own_position, time)?,
crate::v2x_tx::gn::make_shb_eh(address, station_type, own_position, time),
None,
1,
))
@ -375,8 +336,8 @@ mod tests {
use super::*;
use crate::init_test_env_logger;
fn posstate_from_pos(position: geo_types::Point) -> esp32_cits_core::PosVel {
esp32_cits_core::PosVel {
fn posstate_from_pos(position: geo_types::Point) -> crate::applogic::PositionState {
crate::applogic::PositionState {
position,
heading_deg: None,
speed_mps: None,

View file

@ -49,7 +49,7 @@ pub struct State {
time: chrono::NaiveDateTime,
#[allow(unused)]
pos: esp32_cits_core::PosVel,
pos: PositionState,
#[cfg(feature = "spat")]
last_prune: chrono::NaiveDateTime,
@ -130,7 +130,15 @@ pub struct GnssFix {
pub speed_mps: Option<f32>,
}
impl From<&GnssFix> for esp32_cits_core::PosVel {
#[allow(unused)]
#[derive(Debug, Default, Clone, PartialEq)]
pub struct PositionState {
pub position: geo_types::Point,
pub heading_deg: Option<f32>,
pub speed_mps: Option<f32>,
}
impl From<&GnssFix> for PositionState {
fn from(value: &GnssFix) -> Self {
let position = geo_types::Point::new(value.longitude_deg, value.latitude_deg);
@ -141,7 +149,7 @@ impl From<&GnssFix> for esp32_cits_core::PosVel {
}
}
}
impl From<GnssFix> for esp32_cits_core::PosVel {
impl From<GnssFix> for PositionState {
fn from(value: GnssFix) -> Self {
(&value).into()
}
@ -204,7 +212,7 @@ impl State {
Self {
initialized: false,
time: chrono::NaiveDateTime::default(),
pos: esp32_cits_core::PosVel::default(),
pos: PositionState::default(),
#[cfg(feature = "spat")]
last_prune: chrono::NaiveDateTime::default(),
#[cfg(feature = "spat")]
@ -633,7 +641,7 @@ impl State {
}
#[allow(unused)]
pub fn pos(&self) -> &esp32_cits_core::PosVel {
pub fn pos(&self) -> &PositionState {
&self.pos
}
}

View file

@ -169,7 +169,7 @@ impl From<msg::PositionState> for crate::applogic::GnssFix {
}
}
impl From<msg::PositionState> for esp32_cits_core::PosVel {
impl From<msg::PositionState> for crate::applogic::PositionState {
fn from(value: msg::PositionState) -> Self {
let position = geo_types::Point::new(
value.position.longitude_deg.into(),
@ -194,7 +194,7 @@ impl From<msg::PositionState> for esp32_cits_core::PosVel {
}
}
impl From<msg::ItsPosition> for esp32_cits_core::PosVel {
impl From<msg::ItsPosition> for crate::applogic::PositionState {
fn from(value: msg::ItsPosition) -> Self {
let latitude_deg = f64::from(value.latitude) / 10_000_000.;
let longitude_deg = f64::from(value.longitude) / 10_000_000.;
@ -208,11 +208,11 @@ impl From<msg::ItsPosition> for esp32_cits_core::PosVel {
}
}
impl esp32_cits_core::tx::GnItsPayload for msg::RawMsgTx {
impl crate::v2x_tx::GnItsPayload for msg::RawMsgTx {
fn make_eh(
&self,
address: [u8; 6],
own_position: &esp32_cits_core::PosVel,
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
seq_no: &mut u16,
) -> Result<
@ -275,19 +275,19 @@ impl esp32_cits_core::tx::GnItsPayload for msg::RawMsgTx {
*seq_no += 1;
(
esp32_cits_core::tx::gn::make_tsb_eh(
crate::v2x_tx::gn::make_tsb_eh(
address,
station_type,
own_position,
time,
*seq_no,
)?,
),
None,
hop_limit,
)
}
msg::GnTransport::Shb => (
esp32_cits_core::tx::gn::make_shb_eh(address, station_type, own_position, time)?,
crate::v2x_tx::gn::make_shb_eh(address, station_type, own_position, time),
None,
1,
),
@ -308,7 +308,7 @@ impl msg::RawMsgTx {
fn make_geobcast(
address: [u8; 6],
station_type: geonetworking::en302636_4_1::StationType,
own_position: &esp32_cits_core::PosVel,
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
gn_area: msg::GnArea,
sequence_number: u16,
@ -320,7 +320,7 @@ impl msg::RawMsgTx {
alloc::string::String,
> {
let source_position_vector =
esp32_cits_core::tx::gn::make_lpv(address, station_type, own_position, time)?;
crate::v2x_tx::gn::make_lpv(address, station_type, own_position, time);
let (atype, dist_a, dist_b, angle) = match gn_area.area() {
msg::GnAreaShape::EtsiAreashapeCircle => (

View file

@ -8,16 +8,11 @@
// #![deny(clippy::large_stack_frames)]
// we can't use UART I/O and GNSS at the same time
#[cfg(all(feature = "uart", feature = "gnss_ubx", feature = "gnss_l67k"))]
compile_error!(
"feature \"uart\" and features \"gnss_ubx\" or \"gnss_l67k\" cannot be enabled at the same time"
);
#[cfg(all(feature = "uart", feature = "gnss"))]
compile_error!("feature \"uart\" and feature \"gnss\" cannot be enabled at the same time");
// autonomous CAM generation need a time and position source (aka _gnss)
#[cfg(all(feature = "cam_tx", not(feature = "_gnss")))]
compile_error!("feature \"cam_tx\" needs a time and position source (either GNSS or UART)");
// we can't have both GNSS drivers enabled at the same time
#[cfg(all(feature = "gnss_ubx", feature = "gnss_l67k"))]
compile_error!("feature \"gnss_ubx\" and feature \"gnss_l67k\" cannot be enabled at the same time");
use alloc::string::ToString as _;
use alloc::vec::Vec;
@ -31,14 +26,14 @@ use c_its_parser::standards::extensions::ItsStationType;
use critical_section::Mutex;
use crossbeam_queue::ArrayQueue;
use embassy_executor::Spawner;
#[cfg(feature = "gnss_l67k")]
#[cfg(feature = "gnss")]
use embassy_gps::gps::l76k;
use esp_backtrace as _;
use esp_hal::clock::CpuClock;
use esp_hal::timer::timg::TimerGroup;
#[cfg(all(target_arch = "riscv32", feature = "spat"))]
use esp_println::println;
use esp32_cits_core::radio;
use esp_radio::wifi;
use geonetworking::Decode as _;
use log::{error, info, warn};
@ -49,13 +44,13 @@ mod cache;
mod geo_alg;
#[cfg(feature = "uart")]
mod io;
mod radio;
#[cfg(feature = "screen")]
mod screen;
#[cfg(feature = "gnss_ubx")]
mod ubx;
#[cfg(any(feature = "cam_tx", feature = "uart"))]
mod v2x_tx;
const WIFI_CHANNEL: radio::Channel = 180;
const WIFI_TXPOWER: i8 = 10; // dBm, 2..20
#[cfg(feature = "spat")]
const SPAT_RATE_LIMIT: u8 = 5; // keep every n-th message
#[cfg(any(feature = "cam_tx", feature = "uart"))]
@ -168,13 +163,9 @@ async fn main(spawner: Spawner) -> ! {
esp_radio::wifi::ControllerConfig::default(),
)
.expect("Failed to initialize Wi-Fi controller");
if let Err(err) = wifi_controller.set_band_mode(esp_radio::wifi::BandMode::_5G) {
error!("Failed to set WiFi to 5GHz only: {err}");
}
if let Err(err) = wifi_controller.set_max_tx_power(WIFI_TXPOWER * 4) {
error!("Failed to set WiFi TX power: {err}");
}
critical_section::with(|cs| {
WIFI_RX_QUEUE.borrow(cs).replace(Some(ArrayQueue::new(2)));
@ -188,15 +179,14 @@ async fn main(spawner: Spawner) -> ! {
let mac = esp_hal::efuse::base_mac_address();
info!("WiFi MAC: {mac}");
// Setup GNSS:
// Seeed XIAO L67K:
// Setup GNSS: Seeed XIAO L67K:
// RX: D7/ GPIO12
// TX: D6/ GPIO11
// WAKEUP: D0/ GPIO1 -> HIGH for active, LOW for Sleep
// RESET: D2/ GPIO25 -> HIGH for normal, LOW for Reset
#[cfg(feature = "gnss_l67k")]
#[cfg(feature = "gnss")]
spawner.spawn(
gnss_l67k_task(
gnss_task(
peripherals.GPIO1,
peripherals.GPIO25,
peripherals.UART0,
@ -205,29 +195,7 @@ async fn main(spawner: Spawner) -> ! {
)
.expect("Failed to spawn GNSS task"),
);
// U-Blox module:
// RX: D7/ GPIO12
// TX: D6/ GPIO11
// RESET: D2/ GPIO25 -> HIGH for normal, LOW for Reset
#[cfg(feature = "gnss_ubx")]
{
setup_uart0(
9600,
Some(b'\n'),
peripherals.GPIO12,
peripherals.GPIO11,
peripherals.UART0,
serial_handler,
)
.expect("Failed to initialize UART for GNSS");
// do some settings (UART receive and parsing runs in main loop)
spawner
.spawn(gnss_ubx_config(peripherals.GPIO25).expect("Failed to spawn GNSS config task"));
}
#[cfg(feature = "_gnss")]
#[cfg(feature = "gnss")]
info!("GNSS task started, waiting for GNSS fix...");
#[cfg(any(feature = "cam_tx", feature = "uart"))]
@ -241,7 +209,7 @@ async fn main(spawner: Spawner) -> ! {
let mut state = applogic::State::new();
#[cfg(feature = "cam_tx")]
let mut cam = {
let station_id = make_station_id(mac);
let station_id = v2x_tx::make_station_id(mac);
info!("V2X Station ID: {station_id}");
applogic::cam_tx::CamState::new(
@ -253,8 +221,6 @@ async fn main(spawner: Spawner) -> ! {
};
#[cfg(feature = "uart")]
let mut uart_input = io::Parser::default();
#[cfg(feature = "gnss_ubx")]
let mut ubx_parser = ublox::Parser::default();
loop {
embassy_time::Timer::after(embassy_time::Duration::from_millis(1)).await;
@ -286,7 +252,7 @@ async fn main(spawner: Spawner) -> ! {
});
}
Some(io::msg::serial_input_msg::Payload::TxMsg(tx_request)) => {
use esp32_cits_core::tx::GnItsPayload as _;
use v2x_tx::GnItsPayload as _;
info!(
"UART: Got new TX message: Type {} with {} bytes",
@ -319,49 +285,6 @@ async fn main(spawner: Spawner) -> ! {
}
}
}
#[cfg(feature = "gnss_ubx")]
{
let mut it = ubx_parser.consume_ubx(&data);
loop {
match it.next() {
Some(Ok(ublox::UbxPacket::Proto27(packet_ref))) => match &packet_ref {
ublox::proto27::PacketRef::MonVer(packet) => {
println!("{}", ubx::pretty_print_mon_ver(packet));
}
ublox::proto27::PacketRef::MonComms(packet) => {
println!("{}", ubx::pretty_print_mon_comms(packet));
}
ublox::proto27::PacketRef::MonRf(packet) => {
println!("{}", ubx::pretty_print_mon_rf(packet));
}
ublox::proto27::PacketRef::NavSat(packet) => {
println!("{}", ubx::pretty_print_nav_sat(packet));
}
ublox::proto27::PacketRef::NavPvt(pvt) => {
match applogic::GnssFix::try_from(pvt) {
Ok(pos_state) => {
critical_section::with(|cs| {
let gnss_update_ref = GNSS_UPDATE.borrow(cs);
gnss_update_ref.replace(Some(pos_state));
});
}
Err(err) => warn!("{err}"),
}
}
_ => {
println!("UBX: {packet_ref:?}");
}
},
Some(Err(_)) => {
// Received a malformed packet
}
None => {
// The internal buffer is now empty
break;
}
}
}
}
}
}
@ -377,7 +300,7 @@ async fn main(spawner: Spawner) -> ! {
#[cfg(feature = "cam_tx")]
{
use esp32_cits_core::tx::GnItsPayload as _;
use v2x_tx::GnItsPayload;
let time = state.time().and_utc();
cam.update(time, fix.clone().into());
@ -510,7 +433,7 @@ async fn main(spawner: Spawner) -> ! {
reason = "adhering to callback interface"
)]
#[allow(clippy::too_many_lines)]
fn handle_frame(frame: esp_radio::wifi::sniffer::PromiscuousPkt<'_>) {
fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) {
#[cfg(feature = "spat")]
const PRUNE_INTERVAL: chrono::Duration = chrono::Duration::seconds(60);
#[cfg(feature = "spat")]
@ -531,18 +454,27 @@ fn handle_frame(frame: esp_radio::wifi::sniffer::PromiscuousPkt<'_>) {
.naive_utc()
};
match esp32_cits_core::rx::is_broadcast_frame(&frame) {
Err(err) => {
warn!("{err}");
return;
}
Ok(false) => {
// drop frame
return;
}
Ok(true) => {
// continue
}
// Ignore frames with errors and non-data frames
if frame.rx_cntl.rx_state != 0 {
warn!("Received frame has RX error: {}", frame.rx_cntl.rx_state);
return;
}
if !radio::PromiscuousPktType::Data.matches(frame.frame_type) {
return;
}
// Ignore non-broadcast frames
if frame.len < (4 + 6) {
warn!(
"Received frame too small to be a valid broadcast frame: {} bytes",
frame.len
);
return;
}
let dest_mac = &frame.data[4..10];
if dest_mac != [0xff; 6] {
warn!("Received frame is not broadcast frame: Dest MAC {dest_mac:02x?}");
return;
}
// info!("Received frame with {} bytes", frame.len);
@ -689,10 +621,10 @@ fn get_its_header<'p>(
}
}
#[cfg(feature = "gnss_l67k")]
#[cfg(feature = "gnss")]
#[allow(clippy::large_stack_frames, reason = "GNSS driver needs some space")]
#[embassy_executor::task]
async fn gnss_l67k_task(
async fn gnss_task(
gps_wakeup: esp_hal::peripherals::GPIO1<'static>,
gps_reset: esp_hal::peripherals::GPIO25<'static>,
uart: esp_hal::peripherals::UART0<'static>,
@ -742,38 +674,6 @@ async fn gnss_l67k_task(
}
}
#[cfg(feature = "gnss_ubx")]
#[embassy_executor::task]
async fn gnss_ubx_config(reset_pin: esp_hal::peripherals::GPIO25<'static>) {
// reset the module
let mut gnss_reset = esp_hal::gpio::Output::new(
reset_pin,
esp_hal::gpio::Level::Low,
esp_hal::gpio::OutputConfig::default(),
);
embassy_time::Timer::after(embassy_time::Duration::from_millis(2)).await;
gnss_reset.set_high();
embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await;
info!("UBX: Enabling ubx protocol with NAV-PVT and NAV-SAT");
let buffer = ublox::packets::cfg_val::CfgValSetBuilder {
version: 1,
layers: ublox::packets::cfg_val::CfgLayerSet::RAM,
reserved1: 0,
cfg_data: &[
ublox::cfg_val::CfgVal::Uart1OutProtNmea(false),
ublox::cfg_val::CfgVal::Uart1OutProtUbx(true),
// CFG-MSGOUT-UBX_NAV_PVT_UART1
ublox::cfg_val::CfgVal::MsgOutUbxNavPvtUart1(1),
// CFG-MSGOUT-UBX_NAV_SAT_UART1
// ublox::cfg_val::CfgVal::MsgOutUbxNavSatUart1(10),
],
}
.into_packet_vec();
send_uart0(&buffer);
}
#[cfg(feature = "screen")]
fn log_screen_error<E>(error: &E)
where
@ -873,19 +773,3 @@ fn send_uart0(data: &[u8]) {
}
});
}
#[cfg(all(feature = "esp", feature = "cam_tx"))]
/// Creates a station ID from the WLAN MAC address
///
/// Will use `0xC173` as static prefix and uses last 16 bit from MAC address for the remainder.
///
/// # Panics
/// Won't panic in practive b/c of pre-conditions
#[must_use]
pub fn make_station_id(mac: esp_hal::efuse::MacAddress) -> u32 {
// unwrap is fine here since we can be sure that the MacAddress is 6 byte
// and a slice of 2 bytes can be converted to [u8; 2]
let mac_last_16bit = u16::from_be_bytes(mac.as_bytes().get(4..6).unwrap().try_into().unwrap());
0xC173_0000 | u32::from(mac_last_16bit)
}

288
src/radio.rs Normal file
View file

@ -0,0 +1,288 @@
//! IEEE 802.11p C-ITS Capture with ESP32-C5
#![allow(unused)]
// -----------------------------
// missing interface definitions
// -----------------------------
pub type Channel = core::ffi::c_int;
unsafe extern "C" {
/// Changes the 802.11 PHY channel
///
/// C function signature: `void phy_change_channel(int,int,int,int)`
///
/// Options:
/// - `primary`: Channel frequency in MHz
/// - `ignored1`: Ignored (set to 1)
/// - `ignored2`: Ignored (set to 0)
/// - `ht_mode`: Probably HT mode
unsafe fn phy_change_channel(
primary: Channel,
ignored1: core::ffi::c_int,
ignored2: core::ffi::c_int,
ht_mode: core::ffi::c_int,
) -> core::ffi::c_int;
/// Enables 802.11p mode on the ESP32-C5
///
/// C function signature: `void phy_11p_set(int,int)`
///
/// Options:
/// - `enable`: Boolean option to enable 802.11p mode
/// - `half_rate`: 5 MHz mode
unsafe fn phy_11p_set(enable: u8, half_rate: u8);
}
/// Enum values for `wifi_promiscuous_pkt_type_t` used in `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type`
///
/// Original value is actually `core::ffi::c_uint`, but that can't be used in `#[repr()]` attributes,
/// so we're using `u8` as the smallest suitable type here that can be casted to a bigger type.
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub(crate) enum PromiscuousPktType {
/// Management frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t`
Mgmt = 0,
/// Control frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t`
Ctrl = 1,
/// Data frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t`
Data = 2,
/// Other type, such as MIMO etc. 'buf' argument is `wifi_promiscuous_pkt_t` but the payload is zero length.
Misc = 3,
}
impl PromiscuousPktType {
#[allow(dead_code)]
pub fn as_repr(self) -> u8 {
self as u8
}
/// Converts to underlying type of `wifi_promiscuous_pkt_type_t` (used by `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type`)
pub fn as_frame_type(self) -> core::ffi::c_uint {
core::ffi::c_uint::from(self.as_repr())
}
/// Determines if we are the same frame type as `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type`
pub fn matches(self, frame_type: core::ffi::c_uint) -> bool {
self.as_frame_type() == frame_type
}
}
#[cfg(feature = "esp")]
/// Configure WiFi Sniffer to a certain 802.11p channel
pub fn setup_wifi_sniffer(
channel: Channel,
sniffer: &mut esp_radio::wifi::sniffer::Sniffer,
callback: fn(esp_radio::wifi::sniffer::PromiscuousPkt<'_>),
) -> Result<(), alloc::string::String> {
sniffer
.set_promiscuous_mode(true)
.map_err(|err| alloc::format!("Failed to enable promiscuous mode: {err:?}"))?;
sniffer.set_receive_cb(callback);
// convert channel number to frequency in MHz
let primary = 5000 + (channel * 5);
unsafe {
phy_11p_set(1, 0);
match phy_change_channel(primary, 1, 0, 0) {
0 => Ok(()),
ret => Err(alloc::format!("Failed to change channel: {ret}")),
}
}
}
#[cfg(feature = "esp")]
/// Sends 802.11p broadcast frame
///
/// Expects `data` to contain GN header (BTP header and ITS payload) to be a valid C-ITS/ ITS-G5 packet/ frame.
///
/// # Errors
/// Fails when MAC is not the right size or returns frame send error
pub fn send_80211_bcast_frame(
sniffer: &mut esp_radio::wifi::sniffer::Sniffer,
mac_addr: &[u8],
data: &[u8],
) -> Result<(), alloc::string::String> {
const HEADER_SIZE: usize = 24 + 8;
let mac = mac_addr
.try_into()
.map_err(|err| alloc::format!("Unexpected MAC address size: {err}"))?;
// assemble frame
let mut frame_buf = alloc::vec::Vec::with_capacity(HEADER_SIZE + data.len());
frame_buf.extend_from_slice(&DataFrameHeader::new_80211p(mac).to_binary());
frame_buf.extend_from_slice(&LlcHeader::new_80211p().to_binary());
frame_buf.extend_from_slice(data);
// send
sniffer
.send_raw_frame(true, &frame_buf, true)
.map_err(|err| alloc::format!("Failed to send 802.11p frame: {err:?}"))
}
/// IEEE 802.11p frame header
#[derive(Debug, Default)]
struct DataFrameHeader {
/// byte 0: subtype in upper nibble, type in lower nibble
/// "normal" data frame: 0x08, QoS data frame: 0x88
pub type_and_subtype: u8,
/// byte 1: frame contol field, 0x00 for 802.11p
pub fcf_flags: u8,
/// byte 2..3: duration in micros, usually 0x0000 for 802.11p (toDS = 0, fromDS = 0)
pub duration: u16,
/// byte 4..9: receiver/ destination for 802.11p
pub address_1: [u8; 6],
/// byte 10..15: transmitter/ source for 802.11p
pub address_2: [u8; 6],
/// byte 16..21: BSSID for 802.11p
pub address_3: [u8; 6],
/// byte 22..24: upper 12 bits: seq. no, lower 4 bits: fragment no
pub sequence_control: u16,
// Note: address 4 is not used in 802.11p
// pub qos: Option<[u8; 2]>, // QoS frames are supported by radio library
// Note: 802.11p does not use HT mode(s)
}
impl DataFrameHeader {
const BCAST_MAC: [u8; 6] = [0xFF; 6];
/// Create an 802.11p (non-QoS) data frame header with a source MAC address
pub(crate) fn new_80211p(source_mac: &[u8; 6]) -> Self {
Self {
type_and_subtype: 0x08,
address_1: Self::BCAST_MAC,
address_2: *source_mac,
address_3: Self::BCAST_MAC,
..Default::default()
}
}
pub(crate) fn to_binary(&self) -> [u8; 24] {
let mut buf = [0u8; 24];
buf[0] = self.type_and_subtype;
buf[1] = self.fcf_flags;
Self::write_u16(&mut buf, 2, self.duration);
Self::write_mac_addr(&mut buf, 4, &self.address_1);
Self::write_mac_addr(&mut buf, 10, &self.address_2);
Self::write_mac_addr(&mut buf, 16, &self.address_3);
Self::write_u16(&mut buf, 22, self.sequence_control);
buf
}
fn write_u16(buf: &mut [u8; 24], offset: usize, data: u16) {
for (idx, byte) in data.to_be_bytes().iter().enumerate() {
buf[idx + offset] = *byte;
}
}
fn write_mac_addr(buf: &mut [u8; 24], offset: usize, data: &[u8; 6]) {
for (idx, byte) in data.iter().enumerate() {
buf[idx + offset] = *byte;
}
}
}
/// IEEE 802.11p LLC header
#[derive(Debug, Default)]
struct LlcHeader {
/// byte 0: SNAP (0xaa) in 802.11p
pub dsap: u8, // SNAP in
/// byte 1: SNAP (0xaa) in 802.11p
pub lsap: u8, // SNAP in
/// byte 2: SNAP (0xaa) in 802.11p
pub control_1: u8, // 0x03
// Note: optional second control byte not used in 802.11p
// SNAP fields
/// set to 0 since ethertype is used for protocol ID in 802.11p
pub oui: [u8; 3],
/// ethertype GeoNetworking (0x8947) in 802.11p
pub protocol_id: u16,
}
impl LlcHeader {
pub(crate) fn new_80211p() -> Self {
Self {
dsap: 0xaa,
lsap: 0xaa,
control_1: 0x03,
oui: [0; 3],
protocol_id: 0x8947,
}
}
pub(crate) fn to_binary(&self) -> [u8; 8] {
let mut buf: [u8; 8] = [
self.dsap,
self.lsap,
self.control_1,
0x00,
0x00,
0x00,
0x00,
0x00,
];
buf[3] = self.oui[0];
buf[4] = self.oui[1];
buf[5] = self.oui[2];
buf[6] = self.protocol_id.to_be_bytes()[0];
buf[7] = self.protocol_id.to_be_bytes()[1];
buf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn data_frame_test() {
// build a typical IEEE 802.11p frame
const OWN_MAC: [u8; 6] = [0x12, 0x34, 0x56, 0x78, 0xab, 0xcd];
const REF_HEADER: [u8; 24] = [
0x08, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x12, 0x34, 0x56, 0x78,
0xab, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
];
let frame = DataFrameHeader::new_80211p(&OWN_MAC);
assert_eq!(0x08, frame.type_and_subtype); // data frame, subtype 0
assert_eq!(0x0000, frame.fcf_flags);
assert_eq!(0x0000, frame.duration);
assert_eq!([0xFF; 6], frame.address_1);
assert_eq!(OWN_MAC, frame.address_2);
assert_eq!([0xFF; 6], frame.address_3);
assert_eq!(0x0000, frame.sequence_control);
assert_eq!(REF_HEADER, frame.to_binary());
}
#[test]
fn llc_test() {
// build a typical IEEE 802.11p LLC header
const REF_HEADER: [u8; 8] = [0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x89, 0x47];
let llc = LlcHeader::new_80211p();
assert_eq!(0xaa, llc.dsap);
assert_eq!(0xaa, llc.lsap);
assert_eq!(0x03, llc.control_1);
assert_eq!([0x00; 3], llc.oui);
assert_eq!(0x8947, llc.protocol_id);
assert_eq!(REF_HEADER, llc.to_binary());
}
}

89
src/test-proto.rs Normal file
View file

@ -0,0 +1,89 @@
#![cfg(not(target_arch = "riscv32"))]
#![allow(unused)]
extern crate alloc;
mod applogic;
mod io;
mod v2x_tx;
fn main() {
use prost::Message as _;
// env_logger::init();
// generate some test UART messages as hex strings
let raw_its = input_from_raw(io::msg::RawMsgTx {
payload: [0xaa, 0xbb, 0xcc, 0xdd].to_vec(),
message_type: 1,
gn_transport: io::msg::GnTransport::Shb.into(),
gn_area: io::msg::GnArea {
area: None,
position: None,
dist_a: None,
dist_b: None,
angle: None,
},
hop_limit: 2,
station_type: None,
});
println!("{}", serialize_serial_msg_hex(&raw_its));
let pos = input_from_position(io::msg::PositionState {
timestamp_ms: 1785144196000,
position: io::msg::Position {
latitude_deg: 42.,
longitude_deg: 23.,
},
heading: None,
speed: None,
});
println!("{}", serialize_serial_msg_hex(&pos));
}
fn input_from_raw(value: io::msg::RawMsgTx) -> io::msg::SerialInputMsg {
use io::msg::serial_input_msg::Payload;
io::msg::SerialInputMsg {
payload: Some(Payload::TxMsg(value)),
}
}
fn input_from_position(value: io::msg::PositionState) -> io::msg::SerialInputMsg {
use io::msg::serial_input_msg::Payload;
io::msg::SerialInputMsg {
payload: Some(Payload::Position(value)),
}
}
pub(crate) fn serialize_serial_msg_hex(msg: &io::msg::SerialInputMsg) -> alloc::string::String {
use prost::Message as _;
let mut msg_buf = msg.encode_to_vec();
let sof = [0x56u8, 0x32, 0x58, 0x2B];
let eof = [0x0du8, 0x0a];
let len: [u8; 2] = (msg_buf.len() as u16).to_be_bytes();
let mut out = sof.to_vec();
out.extend_from_slice(&len);
out.append(&mut msg_buf);
out.extend_from_slice(&eof);
// convert to hex string
let mut hex_str = alloc::string::String::default();
for c in out {
hex_str.push_str(format!("{c:02x}").as_str());
}
hex_str
}
// /// Initializes the env-logger to be used inside tests
// fn init_test_env_logger() {
// let _ = env_logger::builder()
// .format_timestamp(None)
// .filter_level(log::LevelFilter::Debug)
// .is_test(true)
// .try_init();
// }

View file

@ -7,7 +7,9 @@ mod applogic;
mod cache;
mod geo_alg;
mod io;
mod radio;
mod testdata;
mod v2x_tx;
fn main() {
// env_logger::init();

View file

@ -1,106 +0,0 @@
//! u-blox helpers
#![allow(unused)]
use alloc::vec::Vec;
pub fn pretty_print_mon_ver(packet: &ublox::mon_ver::MonVerRef) -> alloc::string::String {
alloc::format!(
"UBX-MON-VER: SW version: {} HW version: {}; Extensions: {:?}",
packet.software_version(),
packet.hardware_version(),
packet.extension().collect::<Vec<&str>>()
)
}
pub fn pretty_print_mon_comms(packet: &ublox::mon_comms::MonCommsRef) -> alloc::string::String {
alloc::format!(
"UBX-MON-COMMS: {} ports ({:?}), {} tx err, info {:?}",
packet.n_ports(),
packet.prot_ids(),
packet.tx_errors(),
packet
.ports()
.map(|v| alloc::format!("{v:?}"))
.collect::<Vec<_>>()
)
}
pub fn pretty_print_mon_rf(packet: &ublox::mon_rf::MonRfRef) -> alloc::string::String {
alloc::format!(
"UBX-MON-RF: {:?}",
packet
.blocks()
.map(|v| alloc::format!("{v:?}"))
.collect::<Vec<_>>()
)
}
pub fn pretty_print_nav_sat(packet: &ublox::nav_sat::NavSatRef) -> alloc::string::String {
// sort by quality flags
let mut sat_infos = packet.svs().collect::<Vec<_>>();
sat_infos.sort_unstable_by_key(|v| v.flags_raw() & 0x7);
sat_infos.reverse();
let sat_info_strings = sat_infos
.iter()
.map(|v| {
alloc::format!(
"- {}/{:3}: E {:3.0}, A {:3.0}, CNO {:2.1} dBHz, {:?}, {:?}",
v.gnss_id(),
v.sv_id(),
v.elev(),
v.azim(),
v.cno(),
v.flags().quality_ind(),
v.flags().health(),
)
})
.collect::<Vec<_>>();
alloc::format!(
"UBX-NAV-SAT: num {:?}\n{}",
packet.num_svs(),
sat_info_strings.join("\n")
)
}
impl<'a> TryFrom<&ublox::nav_pvt::proto27::NavPvtRef<'a>> for crate::applogic::GnssFix {
type Error = alloc::string::String;
fn try_from(value: &ublox::nav_pvt::proto27::NavPvtRef<'a>) -> Result<Self, Self::Error> {
let has_time = value.fix_type() == ublox::GnssFixType::Fix3D
|| value.fix_type() == ublox::GnssFixType::GPSPlusDeadReckoning
|| value.fix_type() == ublox::GnssFixType::TimeOnlyFix;
let has_posvel = value.fix_type() == ublox::GnssFixType::Fix3D
|| value.fix_type() == ublox::GnssFixType::GPSPlusDeadReckoning;
if !has_posvel || !has_time {
return Err(alloc::format!(
"NAV-PVT without valid fix: {:?}",
value.fix_type()
));
}
let pos: ublox::PositionLLA = value.into();
let vel: ublox::Velocity = value.into();
let time: chrono::DateTime<chrono::Utc> = value
.try_into()
.map_err(|err| alloc::format!("Failed to convert time: {err}"))?;
let latitude_deg = pos.lat;
let longitude_deg = pos.lon;
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
let heading_deg = vel.heading as f32;
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
let speed_mps = vel.speed as f32;
Ok(Self {
time: Some(time.naive_utc()),
latitude_deg,
longitude_deg,
heading_deg: Some(heading_deg),
speed_mps: Some(speed_mps),
})
}
}

94
src/v2x_tx/gn.rs Normal file
View file

@ -0,0 +1,94 @@
//! Geonetworking utilities
use c_its_parser::gn as geonetworking;
#[allow(unused)]
pub fn make_shb_eh(
address: [u8; 6],
station_type: geonetworking::en302636_4_1::StationType,
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
) -> geonetworking::en302636_4_1::ExtendedHeader {
let source_position_vector = make_lpv(address, station_type, own_position, time);
geonetworking::en302636_4_1::ExtendedHeader::SHB(
geonetworking::en302636_4_1::SingleHopBroadcast {
source_position_vector,
media_dependent_data: [0; 4],
},
)
}
#[allow(unused)]
pub fn make_tsb_eh(
address: [u8; 6],
station_type: geonetworking::en302636_4_1::StationType,
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
sequence_number: u16,
) -> geonetworking::en302636_4_1::ExtendedHeader {
let source_position_vector = make_lpv(address, station_type, own_position, time);
geonetworking::en302636_4_1::ExtendedHeader::TSB(
geonetworking::en302636_4_1::TopologicallyScopedBroadcast::new(
sequence_number,
source_position_vector,
),
)
}
#[allow(unused)]
pub fn make_gbc_eh(
address: [u8; 6],
station_type: geonetworking::en302636_4_1::StationType,
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
target_pos: geo_types::Point,
radius_m: u16,
sequence_number: u16,
) -> geonetworking::en302636_4_1::ExtendedHeader {
let source_position_vector = make_lpv(address, station_type, own_position, time);
#[allow(clippy::cast_possible_truncation)]
let latitude_deg = target_pos.x() as f32;
#[allow(clippy::cast_possible_truncation)]
let longitude_deg = target_pos.y() as f32;
geonetworking::en302636_4_1::ExtendedHeader::GBC(
geonetworking::en302636_4_1::GeoBroadcast::try_from_values(
sequence_number,
source_position_vector,
latitude_deg,
longitude_deg,
radius_m,
0,
0,
)
.expect("Failed to create circular GBC"),
)
}
pub fn make_lpv(
address: [u8; 6],
station_type: geonetworking::en302636_4_1::StationType,
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
) -> geonetworking::en302636_4_1::LongPositionVector {
let timestamp_its = c_its_parser::time_utils::TimestampIts::from(time);
#[allow(clippy::cast_possible_truncation)]
let latitude_deg = own_position.position.y() as f32;
#[allow(clippy::cast_possible_truncation)]
let longitude_deg = own_position.position.x() as f32;
let speed_mps = own_position.speed_mps.unwrap_or_default();
let heading_deg = own_position.heading_deg.unwrap_or_default();
geonetworking::en302636_4_1::LongPositionVector::try_from_values(
geonetworking::en302636_4_1::Address::new(false, station_type, address),
timestamp_its.0,
latitude_deg,
longitude_deg,
true,
speed_mps,
heading_deg,
)
.expect("Failed to create LongPositionVector")
}

164
src/v2x_tx/mod.rs Normal file
View file

@ -0,0 +1,164 @@
//! V2X Transmission
use alloc::vec::Vec;
use c_its_parser::gn::{self as geonetworking, Encode};
pub mod gn;
#[cfg(all(feature = "esp", feature = "cam_tx"))]
/// Creates a station ID from the WLAN MAC address
///
/// Will use `0xC173` as static prefix and uses last 16 bit from MAC address for the remainder.
pub fn make_station_id(mac: esp_hal::efuse::MacAddress) -> u32 {
// unwrap is fine here since we can be sure that the MacAddress is 6 byte
// and a slice of 2 bytes can be converted to [u8; 2]
let mac_last_16bit = u16::from_be_bytes(mac.as_bytes().get(4..6).unwrap().try_into().unwrap());
0xC173_0000 | u32::from(mac_last_16bit)
}
pub trait GnItsPayload {
// common GN parameters
const LIFETIME_MILLIS: u32 = 60_000; // itsGnDefaultPacketLifetime is 60 seconds according to ETSI EN 302 636-4-1 V1.4.1
// ---------------------------------------------
// methods to build a GN packet with ITS payload
// ---------------------------------------------
/// Builds the [`geonetworking::en302636_4_1::ExtendedHeader`] and additional information for the ITS message
///
/// Output:
/// - Extended header
/// - Area type (when using GAC or GBC)
/// - hop limit
///
/// Note: Make sure to increase the sequence number on multi-hop packets.
fn make_eh(
&self,
address: [u8; 6],
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
seq_no: &mut u16,
) -> Result<
(
geonetworking::en302636_4_1::ExtendedHeader,
Option<geonetworking::en302636_4_1::AreaType>,
u8,
),
alloc::string::String,
>;
/// Creates the ITS payload (without BTP header)
fn make_payload(
&self,
) -> Result<(Vec<u8>, c_its_parser::standards::extensions::ItsMessageId), alloc::string::String>;
// ---------------------------------------------
// common methods (with implementation)
// ---------------------------------------------
/// Encodes the GN packet
fn encode_packet(
&self,
address: [u8; 6],
own_position: &crate::applogic::PositionState,
time: chrono::DateTime<chrono::Utc>,
is_mobile: bool,
seq_no: &mut u16,
) -> Result<Vec<u8>, alloc::string::String> {
// Build payload (including BTP header)
let (mut its_payload, msg_type) = self.make_payload()?;
let mut payload = make_btp_b(msg_type).encode()?;
payload.append(&mut its_payload);
#[allow(clippy::cast_possible_truncation)]
let payload_length = (payload.len()) as u16;
// get extended header (depending on message type and sometimes content as well)
let (eh, area_type, maximum_hop_limit) =
self.make_eh(address, own_position, time, seq_no)?;
// Build GN headers
let traffic_class = geonetworking::en302636_4_1::TrafficClass::try_new(
false,
false,
make_traffic_class(msg_type),
)
.map_err(|err| alloc::format!("Failed to create TrafficClass: {err:?}"))?;
let common = geonetworking::en302636_4_1::CommonHeader::from_values(
geonetworking::en302636_4_1::NextAfterCommon::BTPB,
geonetworking::en302636_4_1::HeaderType::new_from(&eh, area_type)?,
traffic_class,
is_mobile,
payload_length,
maximum_hop_limit,
);
let remaining_hop_limit = maximum_hop_limit;
let basic = geonetworking::en302636_4_1::BasicHeader::try_new(
1,
geonetworking::en302636_4_1::NextAfterBasic::CommonHeader,
geonetworking::en302636_4_1::Lifetime::from_milliseconds(Self::LIFETIME_MILLIS),
remaining_hop_limit,
)
.map_err(|err| alloc::format!("Failed to create BasicHeader: {err}"))?;
// Assemble and encode packet
let packet = geonetworking::Packet::Unsecured {
basic,
common,
extended: Some(eh),
payload: &payload,
};
packet
.encode_to_vec()
.map_err(|err| alloc::format!("Failed to encode GN packet: {err:?}"))
}
}
/// Creates a BTP-B header based on the ITS message ID (message type)
///
/// Port numbers and destination port info according to ETSI TS 103 248
fn make_btp_b(
msg_type: c_its_parser::standards::extensions::ItsMessageId,
) -> c_its_parser::transport::TransportHeader {
let (destination_port, destination_port_info) = match msg_type {
c_its_parser::standards::extensions::ItsMessageId::Denm => (2002, 0),
c_its_parser::standards::extensions::ItsMessageId::Cam => (2001, 0),
c_its_parser::standards::extensions::ItsMessageId::Mapem => (2003, 0),
c_its_parser::standards::extensions::ItsMessageId::Spatem => (2004, 0),
c_its_parser::standards::extensions::ItsMessageId::Saem => (2005, 0),
c_its_parser::standards::extensions::ItsMessageId::Ivim => (2006, 0),
c_its_parser::standards::extensions::ItsMessageId::Srem => (2007, 0),
c_its_parser::standards::extensions::ItsMessageId::Ssem => (2008, 0),
c_its_parser::standards::extensions::ItsMessageId::Cpm => (2009, 0),
c_its_parser::standards::extensions::ItsMessageId::Poi => (2010, 0),
c_its_parser::standards::extensions::ItsMessageId::EvRsr => (2012, 0),
c_its_parser::standards::extensions::ItsMessageId::Rtcmem => (2013, 0),
_ => (0, 0), // no information found for the subsequent types
};
c_its_parser::transport::TransportHeader::BtpB(c_its_parser::transport::BasicTransportBHeader {
destination_port,
destination_port_info,
})
}
/// Creates a traffic class ID based on the ITS message ID (message type)
fn make_traffic_class(msg_type: c_its_parser::standards::extensions::ItsMessageId) -> u8 {
#[allow(clippy::match_same_arms)]
match msg_type {
c_its_parser::standards::extensions::ItsMessageId::Denm => 1,
c_its_parser::standards::extensions::ItsMessageId::Cam => 2,
c_its_parser::standards::extensions::ItsMessageId::Mapem => 3, // from C-Roads RS_RSP_063(1)
c_its_parser::standards::extensions::ItsMessageId::Spatem => 3, // from C-Roads RS_RSP_063(1)
c_its_parser::standards::extensions::ItsMessageId::Ivim => 3, // from C-Roads RS_RSP_063(1)
c_its_parser::standards::extensions::ItsMessageId::Srem => 2, // from C-Roads RS_RSP_063(1)
c_its_parser::standards::extensions::ItsMessageId::Ssem => 2, // from C-Roads RS_RSP_063(1)
c_its_parser::standards::extensions::ItsMessageId::Rtcmem => 3, // CSP_MaxPrio=252 -> 3
_ => 3, // fall-back to lowest priority
}
}