├── .gitignore ├── LICENSE ├── README.md ├── all-boxplot-uq.svg ├── analysis ├── README.md ├── all-boxplot-uq.svg ├── all-esp32-1024B-cdf.svg ├── cdf.R ├── esp32-ble-results-notify-write.csv ├── esp32-ble-results.csv ├── esp32-inconsistent-12B.csv ├── esp32-nimble-fixes.svg ├── esp32-nimble-hci-fix.csv ├── esp32-nimble-notify-write-hci-fix.csv ├── esp32-nimble-notify-write-override-connparams.csv ├── esp32-nimble-override-connparams.csv ├── esp32-nimble-results-notify-write.csv ├── esp32-nimble-results.csv ├── esp32-spp-chunked.csv ├── esp32-spp.csv ├── esp32-tcp-defaults.csv ├── esp32-tcp-modified.csv ├── esp32-udp-defaults.csv ├── esp32-udp-modified.csv ├── esp32-websockets-results.csv ├── esp32C6-802154-results.csv ├── espnow-test-data.csv ├── hc-05-test-data-faster.csv ├── hc-05-test-data.csv ├── hc-05-test-defaults.csv ├── increasing-baudrate-tests.csv ├── increasing-baudrate-transport-duration-removed.csv ├── lora-logs.csv ├── nrf24-test-data.csv ├── nrf24-test-results.csv ├── nrf52-12B-directionality.csv ├── nrf52-results-with-static-and-swept-trigger.csv ├── overall-comparison-barcharts.R ├── overall-esp32-comparisons.csv ├── pi-results-zoomed.svg ├── pi4-node-ws-results.svg ├── ridgeline.R ├── rpi-node-ws-results.csv ├── saleae-latency-log-cleanup.R ├── sik-12B-rf-trig-delay-113ms.csv ├── sik-test-data.csv ├── stm32-gpio-tests.csv ├── stm32-gpio-tests.svg ├── stm32-ll-uart-baudrate-total-durations.svg ├── stm32-ll-uart-builds.csv ├── stm32-ll-uart-builds.svg ├── stm32-ll-uart-overhead-pretty.svg ├── stm32-ll-uart-overhead.svg ├── uart-overhead-facet-ridgelines.R └── uart-test-data.csv ├── firmware ├── esp-802154 │ ├── .gitignore │ ├── 1024B-ACK.csv │ ├── 1024B-FaF.csv │ ├── 1024B-throughput-opt.csv │ ├── 128B-ACK.csv │ ├── 128B-FaF.csv │ ├── 12B-ACK.csv │ ├── 12B-FaF.csv │ ├── 1kb-no-ack.pcapng │ ├── 1kb-with-ack.pcapng │ ├── CMakeLists.txt │ ├── README.md │ ├── main │ │ ├── CMakeLists.txt │ │ └── lrpwan_main.c │ └── sdkconfig ├── esp-tcp │ ├── .gitignore │ ├── 1024B-modified.csv │ ├── 1024B.csv │ ├── 128B-modified.csv │ ├── 128B.csv │ ├── 12B-modified.csv │ ├── 12B.csv │ ├── CMakeLists.txt │ ├── README.md │ ├── main │ │ ├── CMakeLists.txt │ │ ├── idf_component.yml │ │ ├── tcp_client.c │ │ ├── tcp_client.h │ │ ├── tcp_main.c │ │ ├── tcp_main_defs.h │ │ ├── tcp_server.c │ │ └── tcp_server.h │ └── sdkconfig ├── esp-udp │ ├── .gitignore │ ├── 1024B-modified.csv │ ├── 1024B.csv │ ├── 128B-modified.csv │ ├── 128B.csv │ ├── 12B-modified.csv │ ├── 12B.csv │ ├── CMakeLists.txt │ ├── README.md │ ├── main │ │ ├── CMakeLists.txt │ │ ├── idf_component.yml │ │ ├── udp_main.c │ │ ├── udp_main_defs.h │ │ ├── udp_server.c │ │ └── udp_server.h │ └── sdkconfig ├── esp-websockets │ ├── .gitignore │ ├── 1024B.csv │ ├── 128B.csv │ ├── 12B.csv │ ├── C6-12B.csv │ ├── CMakeLists.txt │ ├── README.md │ ├── dependencies.lock │ ├── logs-with-nagles │ │ ├── 1024B.csv │ │ ├── 128B.csv │ │ ├── 12B-1.csv │ │ ├── 12B-2.csv │ │ ├── 12B-3.csv │ │ ├── 12B-4.csv │ │ ├── 12B-5.csv │ │ ├── 12B-6.csv │ │ ├── 12B-7.csv │ │ ├── 12B-8.csv │ │ └── 12B-9.csv │ ├── main │ │ ├── CMakeLists.txt │ │ ├── idf_component.yml │ │ ├── websocket_client.c │ │ ├── websocket_client.h │ │ ├── websocket_server.c │ │ ├── websocket_server.h │ │ ├── websockets_main.c │ │ └── websockets_main_defs.h │ └── sdkconfig.defaults ├── esp32-ble │ ├── .gitignore │ ├── 1024B-sc.csv │ ├── 128B-sc.csv │ ├── 12B-cs.csv │ ├── 12B-sc.csv │ ├── CMakeLists.txt │ ├── README.md │ ├── main │ │ ├── CMakeLists.txt │ │ ├── ble_main.c │ │ ├── ble_main_defs.h │ │ ├── client.c │ │ ├── client.h │ │ ├── server.c │ │ ├── server.h │ │ └── spp_defs.h │ ├── sdkconfig │ └── sdkconfig.defaults ├── esp32-nimble │ ├── .gitignore │ ├── 1024B-sc-defaults.csv │ ├── 1024B-sc-hci-fix.csv │ ├── 1024B-sc-override-conn-params.csv │ ├── 128B-sc-MTU20.csv │ ├── 128B-sc-defaults.csv │ ├── 128B-sc-hci-fix.csv │ ├── 128B-sc-override-conn-params.csv │ ├── 12B-cs-defaults.csv │ ├── 12B-cs-hci-fix.csv │ ├── 12B-cs-override-conn-params.csv │ ├── 12B-cs2-defaults.csv │ ├── 12B-sc-defaults.csv │ ├── 12B-sc-hci-fix.csv │ ├── 12B-sc-override-conn-params.csv │ ├── 12B-sc2-defaults.csv │ ├── CMakeLists.txt │ ├── L2CAP-fragment-behaviour.pcapng.gz │ ├── L2CAP-fragment-fixed.pcapng.gz │ ├── README.md │ ├── dependencies.lock │ ├── main │ │ ├── CMakeLists.txt │ │ ├── Kconfig.projbuild │ │ ├── ble_main.c │ │ ├── ble_main_defs.h │ │ ├── client.c │ │ ├── client.h │ │ ├── idf_component.yml │ │ ├── server.c │ │ └── server.h │ ├── sdkconfig │ ├── sdkconfig.defaults │ └── test-setup.jpg ├── esp32-spp │ ├── .gitignore │ ├── 1024B-32B-chunks.csv │ ├── 1024B-64B-chunks.csv │ ├── 1024B.csv │ ├── 128B.csv │ ├── 12B.csv │ ├── CMakeLists.txt │ ├── README.md │ ├── main │ │ ├── CMakeLists.txt │ │ ├── Kconfig.projbuild │ │ └── espspp_main.c │ ├── sdkconfig │ └── sdkconfig.defaults ├── espnow │ ├── .gitignore │ ├── 1024B.csv │ ├── 128B.csv │ ├── 12B.csv │ ├── CMakeLists.txt │ ├── LR-1024B.csv │ ├── LR-128B.csv │ ├── LR-12B.csv │ ├── README.md │ ├── main │ │ ├── CMakeLists.txt │ │ ├── Kconfig.projbuild │ │ └── espnow_example_main.c │ └── sdkconfig ├── gpio_tests │ ├── esp-idf │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── main │ │ │ ├── CMakeLists.txt │ │ │ └── esp_gpio_main.c │ │ └── sdkconfig │ ├── espIDF-o3.csv │ ├── ll-o3.csv │ ├── ll-og.csv │ ├── ll-os.csv │ ├── rpi-node.csv │ ├── stm-ll │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── F429VETx.ld │ │ ├── F429ZITx.ld │ │ ├── README.md │ │ └── src │ │ │ └── main.c │ ├── stm32-ll-asm-debug.txt │ ├── stm32-ll-asm-release.txt │ ├── stm32-ll-asm-small.txt │ ├── stm32duino-o1.csv │ ├── stm32duino-og.csv │ ├── stm32duino-os.csv │ ├── stm32duino-s0.png │ └── stm32duino │ │ └── stm32duino.ino ├── hc-05 │ ├── 460800-1024B.csv │ ├── 460800-1024B.sal │ ├── 460800-128B.csv │ ├── 460800-128B.sal │ ├── 460800-12B.csv │ ├── 57600-1024B.csv │ ├── 57600-128B.csv │ ├── 57600-128B.sal │ ├── 57600-12B.csv │ ├── 57600-12B.sal │ ├── 9600-1024B.csv │ ├── 9600-12B.csv │ ├── 9600-256B.csv │ ├── AT-commands-list.jpg │ └── README.md ├── nrf24 │ ├── 128B_via_12B_packet_sequence.sal │ ├── 256kbps-1024B.csv │ ├── 256kbps-128b.csv │ ├── 256kbps-12B-padded.csv │ ├── 2Mbps-1024B.csv │ ├── 2Mbps-128B.csv │ ├── 2Mbps-12B-padded.csv │ ├── 2Mbps-12B.csv │ └── stm-ll │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── F429VETx.ld │ │ ├── F429ZITx.ld │ │ ├── README.md │ │ ├── libs │ │ ├── nrf24.c │ │ └── nrf24.h │ │ └── src │ │ ├── main.c │ │ └── support.h ├── nrf58240 │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── 1024B-sc-2.csv │ ├── 1024B-sc.csv │ ├── 128B-sc-2.csv │ ├── 128B-sc.csv │ ├── 12B-cs-write.csv │ ├── 12B-sc-2.csv │ ├── 12B-sc-3.csv │ ├── 12B-sc-notify.csv │ ├── CMakeLists.txt │ ├── Kconfig.sysbuild │ ├── README.md │ ├── child_image │ │ └── rpc_host.conf │ ├── early-results │ │ ├── 1024B-cs.csv │ │ ├── 128B-cs.csv │ │ ├── 128B-sc.csv │ │ ├── 12B-cs-3.csv │ │ ├── 12B-cs-4.csv │ │ ├── 12B-cs-5.csv │ │ ├── 12B-cs.csv │ │ ├── 12B-cs2.csv │ │ ├── 12B-notify-write-jittered-trig.csv │ │ ├── 12B-sc-3.csv │ │ ├── 12B-sc-4.csv │ │ ├── 12B-sc-5.csv │ │ ├── 12B-sc-cs-comparison-1-2.csv │ │ ├── 12B-sc.csv │ │ └── 12B-sc2.csv │ ├── notification-validation.png │ ├── prj-periph.conf │ ├── prj.conf │ ├── sample.yaml │ ├── src │ │ ├── benchmark_defs.h │ │ ├── central.c │ │ ├── central.h │ │ ├── main.c │ │ ├── peripheral.c │ │ └── peripheral.h │ └── write-resp-validation.png ├── rfm95 │ ├── 250k-SF128-1024B.csv │ ├── 250k-SF128-128B.csv │ ├── 250k-SF128-12B.csv │ ├── 64k5-SF2048-4o6-1024B.csv │ ├── 64k5-SF2048-4o6-128B.csv │ ├── 64k5-SF2048-4o6-12B.csv │ ├── rx-capture-64k5-SF2048-4o6.sal │ ├── rx-capture-irq.sal │ ├── stm-ll │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── F429VETx.ld │ │ ├── F429ZITx.ld │ │ ├── README.md │ │ ├── libs │ │ │ ├── rfm95.c │ │ │ ├── rfm95.h │ │ │ ├── rfm95_defines.h │ │ │ └── rfm95_private_defines.h │ │ └── src │ │ │ └── main.c │ └── tx-capture-1024B-irq.sal ├── rpi-node │ ├── 1024B-eth.csv │ ├── 1024B-wifi.csv │ ├── 128B-eth.csv │ ├── 128B-wifi.csv │ ├── 12B-eth.csv │ ├── 12B-wifi.csv │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ │ ├── client.ts │ │ ├── payload.ts │ │ ├── server.ts │ │ └── simple.ts │ └── yarn.lock ├── sik │ ├── README.md │ ├── packet-size-logs │ │ ├── 1024B-500ms-trigger.csv │ │ ├── 1024B-500ms-trigger.sal │ │ ├── 128B-250ms-trigger.csv │ │ ├── 128B-250ms-trigger.sal │ │ ├── 12B-250ms-trigger.csv │ │ └── 12B-250ms-trigger.sal │ └── trigger-logs │ │ ├── 12B-250ms-trigger.csv │ │ ├── 12B-250ms-trigger.sal │ │ ├── 12B-rf-trigger-delayed-113ms.csv │ │ ├── 12B-rf-trigger-delayed-113ms.sal │ │ ├── 12B-rf-trigger.csv │ │ └── 12B-rf-trigger.sal └── uart_tests │ ├── baudrate-12B-logs │ ├── 115200-dma.csv │ ├── 115200-dma.sal │ ├── 115200-irq.csv │ ├── 115200-irq.sal │ ├── 115200-poll.csv │ ├── 115200-poll.sal │ ├── 1843200-dma.csv │ ├── 1843200-dma.sal │ ├── 1843200-irq.csv │ ├── 1843200-poll.csv │ ├── 1843200-poll.sal │ ├── 230400-dma.csv │ ├── 230400-dma.sal │ ├── 230400-irq.csv │ ├── 230400-poll.csv │ ├── 230400-poll.sal │ ├── 460800-dma.csv │ ├── 460800-dma.sal │ ├── 460800-irq.csv │ ├── 460800-poll.csv │ ├── 460800-poll.sal │ ├── 921600-dma.csv │ ├── 921600-dma.sal │ ├── 921600-irq.csv │ ├── 921600-irq.sal │ ├── 921600-poll.csv │ └── 921600-poll.sal │ ├── packet-size-tests │ ├── 115200-1024B.csv │ ├── 115200-1024B.sal │ ├── 115200-128B.csv │ ├── 115200-128B.sal │ ├── 115200-12B.csv │ ├── 115200-12B.sal │ ├── 912600-128B.csv │ ├── 912600-128B.sal │ ├── 912600-12B.csv │ ├── 912600-12B.sal │ ├── 921600-1024B.csv │ └── 921600-1024B.sal │ ├── peripheral-configuration-12B-logs │ ├── ll-115200-dma-debug.csv │ ├── ll-115200-dma-minsize.csv │ ├── ll-115200-dma-release.csv │ ├── ll-115200-irq-debug.csv │ ├── ll-115200-irq-minsize.csv │ ├── ll-115200-irq-release.csv │ ├── ll-115200-poll-debug.csv │ ├── ll-115200-poll-minsize.csv │ ├── ll-115200-poll-release.csv │ └── uart-dma-loopback-release.sal │ └── stm-ll │ ├── .gitignore │ ├── CMakeLists.txt │ ├── F429VETx.ld │ ├── F429ZITx.ld │ ├── README.md │ └── src │ ├── fifo.c │ ├── fifo.h │ ├── main.c │ ├── uart.c │ └── uart.h └── screenshots ├── logic-on-table.jpg ├── lora-chirp-sa.png ├── module-assortment.jpg ├── sik-channels.png ├── sik-hopping.png └── sik-pvt-packet.png /.gitignore: -------------------------------------------------------------------------------- 1 | **/.idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Scott Rapson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microbenchmarking Wireless Modules for Embedded Projects 2 | 3 | This repo contains the test firmware, raw logs and post-processing scripts for my ["Comparing latencies of common wireless links for microcontrollers" blog post](https://electricui.com/blog/latency-comparison). 4 | 5 | ![module-assortment](screenshots/module-assortment.jpg) 6 | 7 | If you have any suggestions or feedback for the completed or any future tests, [make an issue](https://github.com/Scottapotamas/embedded-wireless-latency-eval/issues/new) or email me. 8 | 9 | At some point I might also compare power consumption and range behaviour. 10 | 11 | ## Test Firmware 12 | 13 | `/firmware` contains sub-folders with project setups for the test variations. Each project has it's own README with minimal description and instructions. 14 | 15 | In general: 16 | 17 | - Ensure firmware is built with release optimisations for performance i.e. `-o2`. 18 | - Disable any debug logging or console echo as these impact test results. 19 | - For most build-chains I used environment variables to control which test packet size is sent/expected. 20 | 21 | The raw logic-analyser captures have been left in these folders because I'm too lazy to re-organise the tree... 22 | 23 | ## Experiment Setup 24 | 25 | Test setups normally looked like a variation of this: 26 | 27 | ![logic-on-table](screenshots/logic-on-table.jpg) 28 | 29 | **Equipment:** 30 | 31 | - Rigol DG1000 series signal generator to produce stimulus test pulses. 32 | - Saleae Logic 8 for test timing capture. 33 | - RSA3000 series real-time spectrum analyser. 34 | - Oscilloscope & [Minicircuits ZX47-60-S+](https://www.minicircuits.com/WebStore/dashboard.html?model=ZX47-60-S%2B) RF power meter. 35 | - My treasured assortment of RF adapters, antennas, test cables, etc. 36 | 37 | **Process:** 38 | 39 | - Use spectrum analyser to check the spectral region of interest isn't occupied. 40 | - Disable other hardware, add shielding and generally reduce noise floor where possible. 41 | - Flash two identical devkits and/or breakout boards with relevant benchmark firmware, 42 | - Typically positioned on table 1 m apart (closer for the photo). 43 | - Connected to host PC for power and programming. 44 | - Signal generator provides a 3.3 VDC, 50 μs wide square pulse at configurable interval as stimulus signal. 45 | - The interval was typically set manually to ensure the test was completed in half the trigger interval. 46 | - Some exceptions were made (i.e. LoRA), but I always test for any impact of trigger interval on the link behaviour prior to recording data. 47 | - When the trigger signal is flagged in firmware, the relevant test payload is sent. 48 | - When the payload is recieved by the other board, it checks length and CRC. 49 | - If valid, the micro drives an IO line high to indicate a valid transmission. 50 | - The logic analyser captures both the trigger and complete signals at 100 MS/s (10 ns resolution). 51 | - I typically captured for 1000x the trigger interval, i.e. 200 seconds for a 200 ms trigger interval. 52 | 53 | 54 | 55 | ## Data Processing 56 | 57 | Some R scripts help reshape the data from the Saleae Logic edge-timing export to pretty looking comparison charts like these: 58 | 59 | ![esp32-nimble-fixes](analysis/esp32-nimble-fixes.svg) 60 | 61 | - `saleae-latency-log-cleanup.R` will create a column per valid `.csv` file in the working directory with rows per identified stimulus/result edge timing in milliseconds. 62 | - For most hardware benchmark tests, I've left the post-processed files in the analysis folder. 63 | - `ridgeline.R` was used for simpler plots - typically comparing the test packet sizes for a given setup. 64 | - The filename for a converted `.csv` from the prior step needs to be set in the script, along with relevant title changes and manually configured x-axis bounds. 65 | - It generates a .svg file. These aren't kept in this repo, but can be seen in the blog post. 66 | - Other ridgeline variation scripts handle more complex comparisons with chart facets. These typically have extra logic hacked on to improve specific comparisons. 67 | 68 | > I should point out that I don't really know or use R - it was just what I decided to try for this project, so sorry in advance if you actually read the R scripts... -------------------------------------------------------------------------------- /analysis/README.md: -------------------------------------------------------------------------------- 1 | # Analysis 2 | 3 | Timing captures from Saleae Logic were exported as .csv files (found alongside their test firmwares in this repo). 4 | 5 | The `saleae-latency-log-cleanup.R` script is a quick hack to get edge transition timestamps into a set of durations. 6 | 7 | Instead of trying to learn R's charting/boxplot tools and syntax, I take this exported file through the BoxPlotR tool. 8 | -------------------------------------------------------------------------------- /analysis/cdf.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | library(ggtext) 3 | library(ggridges) 4 | library(ggdist) 5 | library(dplyr) 6 | library(sjPlot) 7 | library(readr) 8 | library(tidyr) 9 | 10 | # Read the CSV file into a data frame 11 | data <- read.csv("esp32-comparisons.csv", 12 | check.names = FALSE, 13 | stringsAsFactors = FALSE) 14 | 15 | # What packet size we're filtering 16 | packet_size_str = '1024B' 17 | 18 | # Reshape from wide to long format 19 | data_long <- data %>% 20 | pivot_longer( 21 | cols = everything(), 22 | names_to = "category", 23 | values_to = "duration" 24 | ) %>% 25 | # Create the packetsize column by extracting it from the column name 26 | mutate(packetsize = gsub("-.*", "", category), 27 | # Create the subgroup column (12B, 128B, 1024B) 28 | subgroup = gsub(".*-", "", category)) %>% 29 | filter(!is.na(duration)) %>% 30 | filter(packetsize == packet_size_str) 31 | 32 | # Group data by category and calculate the CDF for each group 33 | data_long <- data_long %>% 34 | group_by(category) %>% 35 | arrange(duration) %>% 36 | mutate(cdf = cumsum(duration)/sum(duration)) 37 | 38 | data_long <- data_long %>% 39 | mutate(subgroup = factor(subgroup, levels = c("ESPNOW", "TCP", "UDP", "Websockets", "SPP", "Bluedroid", "NimBLE" ))) 40 | protocol_colours <- c("ESPNOW"="#BBBBBB", "TCP"="#0077BB", "UDP"="#33BBEE", "Websockets"="#009988", "SPP"="#EE3377", "Bluedroid"="#CC3311", "NimBLE"="#EE7733") 41 | 42 | p <- data_long %>% 43 | # mutate(packetsize = factor(packetsize, levels = c("12B", "128B", "1024B"))) %>% 44 | ggplot( aes(x=duration, y=cdf, color=subgroup)) + 45 | geom_line( 46 | size = 1.25, 47 | ) + 48 | # facet_grid( 49 | # packetsize ~ ., 50 | # scales = "fixed", 51 | # space = "fixed", 52 | # switch = "y", 53 | # ) + 54 | # Override axis range and ticks 55 | coord_cartesian( 56 | xlim = c(0, 75), 57 | ) + 58 | # scale_alpha_manual( 59 | # name = "Packet Size", 60 | # values = c("1024B" = 0.4, "128B" = 1) 61 | # ) + 62 | scale_color_manual( 63 | name = "Protocol", 64 | values = protocol_colours, 65 | ) + 66 | scale_y_continuous( 67 | breaks = seq(0, 1, by = 0.25), 68 | labels = scales::percent 69 | ) + 70 | scale_x_continuous( 71 | limits = c(0, NA), 72 | breaks = seq(0, 1000, by = 5), 73 | expand = c(0.01, 1) 74 | ) + 75 | # Axis Labels 76 | labs( 77 | title = "ESP32 Protocol Latency Comparisons", 78 | subtitle = paste(packet_size_str, "Packets, IDF 5.1, No PowerSave, IRAM Optimisations"), 79 | y = "Cumulative Distribution of Results", 80 | x = "Duration (milliseconds)", 81 | caption = "Lower is better" 82 | ) + 83 | # Styling 84 | theme_minimal() + 85 | theme( 86 | text = element_text(family = "Roboto Mono", size = 20, face = "plain"), 87 | # Legend Formatting 88 | legend.position = c(0.87, 0.33), 89 | legend.title = element_text( 90 | size=15, 91 | face="bold" 92 | ), 93 | legend.spacing.x = unit(0.5, 'cm'), 94 | legend.background = element_rect(fill="white", 95 | size=0.0, linetype="solid"), 96 | 97 | plot.margin = margin(5, 5, 5, 5), 98 | plot.title = element_text(hjust = 0.5), # Center the title 99 | plot.subtitle = element_text(hjust = 0.5, size = 16), # Center the subtitle 100 | plot.caption = element_text(hjust = 0.5), # Center the caption 101 | axis.text.x = element_text(margin = margin(t = 5, r = 0, b = 15, l = 0)), # +Gap between axis and label 102 | # axis.text.y = element_text(face = "bold"), 103 | strip.placement = "outside", 104 | ) 105 | 106 | save_plot("test.svg", fig = p, width=30, height=16) -------------------------------------------------------------------------------- /analysis/espnow-test-data.csv: -------------------------------------------------------------------------------- 1 | LR-1024B,LR-128B,LR-12B,1024B,128B,12B 2 | 55.65382,3.42945,5.07552,25.5686,5.73297,5.51889 3 | 56.39415,3.42948,5.49692,26.27799,5.73431,5.5283 4 | 56.42256,3.4362,7.9682,23.87301,5.73147,5.53458 5 | 58.62494,5.38922,10.41379,28.43224,6.0082,5.54191 6 | 59.56379,7.84695,5.06807,25.13452,5.73601,5.53821 7 | 56.40762,10.24905,5.0844,25.28278,5.71733,5.54857 8 | 56.423,13.38206,5.09571,25.18004,5.73857,5.5359 9 | 56.47033,15.10286,5.12909,25.20822,5.90707,5.58719 10 | 56.41379,3.42526,5.10435,25.22459,5.85648,5.55055 11 | 56.49015,3.41071,5.09968,25.19676,5.75543,5.55783 12 | 56.41742,3.43523,5.09897,23.8194,5.74962,5.55916 13 | 56.42688,3.45025,5.1023,23.78553,5.72898,5.53945 14 | 58.65223,3.43797,5.14367,23.8585,5.72621,5.54268 15 | 57.60981,3.42997,5.18103,23.81079,5.78741,5.55304 16 | 65.75106,3.43274,5.15834,24.08001,5.73773,5.55841 17 | 56.76141,3.44578,5.12672,24.05214,5.75596,5.5637 18 | 59.13515,3.46447,5.12698,23.79314,5.72826,5.56108 19 | 61.49622,3.51638,7.22736,23.80359,5.74455,5.60035 20 | 56.55247,5.29118,14.59175,23.82965,5.73077,5.5557 21 | 56.44174,3.47412,5.11981,23.83114,5.73109,5.55906 22 | 56.42007,3.46585,5.1163,23.83127,5.73538,5.56232 23 | 58.03049,3.45395,5.14364,23.83233,5.75356,5.55066 24 | 60.45987,3.46966,5.14697,23.82879,5.74385,5.54889 25 | 56.42712,3.4567,5.1793,23.81797,5.8111,5.64322 26 | 56.45559,3.44142,5.14862,23.81142,5.76337,7.72851 27 | 56.42591,3.46244,5.14699,23.88972,5.7717,5.62102 28 | 56.45725,3.46515,5.13928,23.79007,5.75886,5.58528 29 | 56.4696,3.51915,5.15852,23.83781,5.7562,5.6295 30 | 56.443,3.4739,5.13091,23.84616,5.75449,5.56478 31 | 56.44738,3.46786,5.14218,23.79562,5.74978,5.55814 32 | 56.45276,3.47265,5.12757,23.84887,7.20302,6.72953 33 | 56.42292,3.47169,5.13794,23.81601,5.73629,5.52876 34 | 56.50343,3.46041,5.16823,23.84213,5.95157,5.59614 35 | 56.43175,3.45644,5.19056,23.86595,5.79782,5.58546 36 | 56.45516,3.48115,5.1359,23.82034,5.77122,5.58575 37 | 56.42947,3.48818,5.20016,27.6303,5.76537,5.57407 38 | 56.47691,3.50187,5.1305,23.87856,5.7876,5.5904 39 | 56.50222,3.53089,7.27888,23.85238,5.79185,5.63067 40 | 56.4586,3.50165,9.66426,23.87051,5.79512,5.60505 41 | 56.45286,3.48759,12.01857,25.40323,5.8004,5.58333 42 | 58.68234,3.46941,5.07434,25.59649,5.80168,5.60171 43 | 59.72863,3.48942,5.14117,25.5288,6.55106,5.59303 44 | 56.45701,3.46813,5.16451,25.58406,8.92724,5.60935 45 | 56.46039,3.48716,5.2247,25.37624,6.09192,5.59574 46 | 56.54976,3.50387,6.87625,33.46156,5.79669,5.59888 47 | 56.46712,3.77892,9.31054,23.95464,5.79699,5.58821 48 | 58.75051,6.21965,5.12689,25.85495,5.77028,5.62363 49 | 56.49567,8.6517,5.16712,25.21022,5.77852,5.63495 50 | 56.46518,11.06153,5.18354,25.25551,5.80281,5.60423 51 | 56.48655,13.58848,5.15676,25.26577,5.79315,5.60456 52 | 64.799,15.9963,5.16904,25.29411,5.79729,5.58185 53 | 56.52324,3.45047,5.15134,25.24728,5.77964,5.58023 54 | 56.46961,3.48463,5.15484,23.86194,5.77789,5.59251 55 | 56.4749,3.4813,5.27507,23.98779,5.82612,5.58979 56 | 58.70036,3.50234,5.18744,27.60403,5.81339,5.6071 57 | 56.51155,3.50404,5.17975,24.02824,5.81367,5.59848 58 | 56.48653,3.51507,5.18417,23.89567,5.79695,5.59277 59 | 56.58893,3.55871,5.1714,23.89329,5.79622,5.64916 60 | 57.754,3.53777,5.20575,23.87046,5.79149,5.59738 61 | 60.14767,3.5145,5.66593,23.88926,5.78976,5.62281 62 | 62.4688,3.70639,8.06743,23.92252,5.82199,5.61407 63 | 56.50348,5.78925,5.16475,23.87663,5.78932,5.62744 64 | 56.49223,3.52427,5.16308,23.86928,5.79257,5.61673 65 | 56.50947,3.50898,5.28334,23.95823,5.8608,5.62706 66 | 58.50996,3.53202,5.21271,23.87077,5.79112,5.61625 67 | 60.98034,3.50973,5.17307,23.8968,5.81541,5.83968 68 | 56.49361,3.54377,5.19734,23.85249,5.7986,6.15092 69 | 56.569,3.58745,5.18957,23.81121,5.79787,10.3395 70 | 69.8215,3.55948,5.21704,23.89668,5.80015,5.61173 71 | 56.49664,3.53625,5.20634,23.84355,5.80943,5.61994 72 | 56.48606,3.51916,5.20059,23.89872,5.80269,5.61528 73 | 56.49932,3.55297,5.17999,23.89947,5.81595,5.63961 74 | 56.5078,3.51102,5.19536,23.83515,6.01224,5.62592 75 | 56.49808,3.53372,5.27665,23.88326,5.84453,5.59325 76 | 56.51248,3.52476,5.21397,23.88142,5.93108,5.62953 77 | 56.50188,6.7075,5.17714,23.92933,7.09506,5.64888 78 | 56.48413,3.53948,5.18966,23.86846,5.83336,5.61411 79 | 56.5706,3.61817,5.18496,23.87849,5.80378,5.66633 80 | 56.52191,3.55825,5.1983,23.91431,5.83983,5.6658 81 | 56.51019,3.52196,5.5796,23.92328,5.82016,5.65913 82 | 56.48556,3.5509,7.97693,23.9105,5.82039,5.65246 83 | 56.52102,3.54771,5.17671,23.92783,5.84768,5.66579 84 | 56.50535,3.54075,5.20359,28.94781,5.82795,5.64911 85 | 58.75172,3.52447,5.24885,28.35038,5.88409,5.66945 86 | 60.94611,3.52946,5.22622,25.66861,7.37539,5.64974 87 | 56.52346,3.5262,5.2206,25.74185,5.84573,5.65603 88 | 56.51682,3.55524,5.26387,28.90186,5.86098,5.65739 89 | 58.81713,6.79795,7.72332,23.92731,5.84923,5.67363 90 | 58.7335,5.224,10.17961,23.9584,5.85551,5.63596 91 | 76.9511,7.62578,5.17796,25.30486,5.8339,5.64233 92 | 56.51325,10.06873,5.20918,25.25817,5.86304,5.65865 93 | 56.52158,12.49357,5.20746,25.32638,5.8673,5.63198 94 | 56.52995,14.96157,5.20482,25.33064,5.85559,5.66627 95 | 56.51022,3.51004,5.32024,25.3539,5.87583,5.65063 96 | 56.54368,3.51858,5.24552,25.26212,5.88405,5.65196 97 | 56.51391,3.56997,5.22292,23.96133,5.88237,5.6522 98 | 56.55437,3.55098,5.24316,23.91923,5.85565,5.64648 99 | 56.64175,3.63668,5.23351,23.95089,5.85796,5.68982 100 | 56.54765,3.56874,5.25082,23.9672,5.83921,5.65114 101 | 56.56193,3.56245,5.21413,23.90582,5.85146,5.68256 102 | 56.81103,3.57344,5.23847,23.94866,5.86774,5.65576 103 | 59.18869,3.55609,5.24876,23.9265,5.86099,5.66318 104 | 61.4968,3.58222,6.20911,23.9275,5.86326,5.6715 105 | 56.58196,4.21494,8.58658,23.93135,5.9225,5.65873 106 | 56.57023,6.63931,5.27083,23.94571,5.87773,5.66007 107 | 56.5526,3.57869,5.24316,23.97292,5.85017,5.67538 108 | 57.16097,3.56972,5.23644,23.91733,5.8683,5.66072 109 | 59.78236,3.66541,5.25373,23.93801,5.86361,5.71314 110 | 62.24771,3.59443,5.27806,23.96069,5.85989,6.3864 111 | 56.59603,3.57619,5.25046,27.625,5.87516,9.8221 112 | 56.5664,3.59222,5.24075,23.96221,5.87241,5.70911 113 | 56.53859,3.58883,5.25508,23.91786,5.86664,5.71645 114 | 56.57609,3.59472,5.25438,23.96774,5.88693,5.67969 115 | 56.54947,3.60557,5.32578,23.956,5.90923,5.69301 116 | 56.56782,3.61961,5.28301,23.91591,5.88038,5.6753 117 | 56.53711,3.58544,5.23133,23.9395,6.99273,6.17368 118 | 56.55244,3.59943,5.26271,23.89825,5.84,5.64879 119 | 56.59882,3.69313,5.2621,23.98398,8.54035,5.95019 120 | 56.60222,3.62917,5.30335,24.46026,7.46953,5.70152 121 | 56.55751,3.62393,5.25672,23.92496,5.88204,5.7099 122 | ,3.57797,5.25503,23.94766,5.90221, 123 | -------------------------------------------------------------------------------- /analysis/overall-comparison-barcharts.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | library(ggtext) 3 | library(ggridges) 4 | library(ggdist) 5 | library(dplyr) 6 | library(sjPlot) 7 | library(readr) 8 | library(tidyr) 9 | 10 | # Read the CSV file into a data frame 11 | data <- read.csv("module-comparison-data.csv", 12 | check.names = FALSE, 13 | stringsAsFactors = FALSE) 14 | 15 | # Reshape from wide to long format 16 | data_long <- data %>% 17 | pivot_longer( 18 | cols = everything(), 19 | names_to = "category", 20 | values_to = "duration" 21 | ) %>% 22 | # Create the packetsize column by extracting it from the column name 23 | mutate(packetsize = gsub("-.*", "", category), 24 | # Create the subgroup column (12B, 128B, 1024B) 25 | subgroup = gsub(".*-", "", category)) # %>% 26 | 27 | # Compute stats 28 | summary_data <- data_long %>% 29 | filter(!is.na(duration)) %>% 30 | group_by(subgroup, packetsize) %>% 31 | summarise( 32 | median = median(duration), 33 | mean = mean(duration), 34 | lower_quantile = quantile(duration, 0.25), 35 | upper_quantile = quantile(duration, 0.75), 36 | min = min(duration), 37 | max = max(duration), 38 | variance = var(duration), 39 | num_samples = length(duration) 40 | )%>% 41 | ungroup() %>% 42 | arrange(upper_quantile) 43 | 44 | print(summary_data) 45 | 46 | # Reorder based on the statistic and packetsize filter 47 | reorder_variable <- summary_data %>% 48 | filter(packetsize == '12B') %>% 49 | arrange(-upper_quantile) %>% 50 | pull(subgroup) 51 | # Apply the new order 52 | summary_data$subgroup <- factor(summary_data$subgroup, levels = reorder_variable) 53 | 54 | # Custom bar colours 55 | myPalette <- c("12B"="#004c6d", "128B"="#7295b0", "1024B"="#d0e5f8") 56 | 57 | # Plotting 58 | p <- summary_data %>% 59 | ggplot(aes(x = subgroup)) + 60 | 61 | # Manually draw bars in correct draw order... 62 | # 1024B bars 63 | geom_col(data = subset(summary_data, packetsize == "1024B"), 64 | aes(y = upper_quantile, fill = packetsize), 65 | position = "identity", 66 | alpha = 0.8, 67 | width = .6) + 68 | # 128B bars 69 | geom_col(data = subset(summary_data, packetsize == "128B"), 70 | aes(y = upper_quantile, fill = packetsize), 71 | position = "identity", 72 | alpha = 0.8, 73 | width = .6) + 74 | # 12B bars 75 | geom_col(data = subset(summary_data, packetsize == "12B"), 76 | aes(y = upper_quantile, fill = packetsize), 77 | position = "identity", 78 | alpha = 0.8, 79 | width = .6) + 80 | # Label values under each bar 81 | geom_text( 82 | aes(y = upper_quantile, label = ifelse(upper_quantile > 20, as.character(signif(upper_quantile, 3)), as.character(signif(upper_quantile, 2)))), 83 | family = "Roboto Mono", 84 | size = 3.6, 85 | vjust = 3.15, 86 | alpha = 0.8, 87 | check_overlap = TRUE, # prevent overdraw 88 | ) + 89 | # When a value clips offscreen, render it on-canvas as a text label and arrow 90 | geom_text( aes(y = upper_quantile, 91 | label = ifelse(upper_quantile > 150, as.character(paste(round(upper_quantile, 0), "→")), '') 92 | ), 93 | y = 149, 94 | vjust = 3.15, 95 | alpha = 0.8, 96 | size = 3.6, 97 | family = "Roboto Mono", 98 | fontface = "bold", 99 | 100 | ) + 101 | # Manual Legend formatting 102 | scale_fill_manual( 103 | values = myPalette, 104 | name = "Payload Size", 105 | breaks = c("12B", "128B", "1024B"), 106 | ) + 107 | annotate("text", 108 | label = "*Sorted by 12B result", 109 | size = 4, 110 | alpha = 0.7, 111 | x = 9.7, 112 | y = 130, 113 | ) + 114 | annotate("text", 115 | label = "*Tested indoors, 1m range", 116 | size = 4, 117 | alpha = 0.7, 118 | x = 9.3, 119 | y = 132.5, 120 | ) + 121 | # Horizontal layout 122 | coord_flip( 123 | ylim = c(0, 146), 124 | # clip = "on" 125 | ) + 126 | # Override x-axis range and ticks 127 | scale_y_continuous( 128 | limits = c(0, NA), 129 | breaks = seq(0, 1000, by = 10) 130 | ) + 131 | # Axis Labels 132 | labs( 133 | x = NULL, 134 | y = "Duration (milliseconds)", 135 | title = "Wireless Latency Benchmark Results", 136 | subtitle = "75% of payload transfers complete faster than...", 137 | caption = "Lower is better." 138 | ) + 139 | theme_minimal() + 140 | theme( 141 | text = element_text(family = "Roboto Mono", size = 20, face = "plain"), 142 | 143 | # Legend Formatting 144 | legend.position = c(0.85, 0.87), 145 | legend.title = element_text( 146 | size=15, 147 | face="bold" 148 | ), 149 | legend.spacing.x = unit(0.5, 'cm'), 150 | legend.background = element_rect(fill="white", 151 | size=0.0, linetype="solid"), 152 | 153 | plot.margin = margin(5, 5, 5, 5), 154 | plot.title = element_text(hjust = 0.5), # Center the title 155 | plot.subtitle = element_text(hjust = 0.5, size = 16), # Center the subtitle 156 | plot.caption = element_text(hjust = 0.5), # Center the caption 157 | axis.text.x = element_text(margin = margin(t = 8, r = 0, b = 15, l = 0)), # +Gap between axis and label 158 | 159 | # axis.line.y = element_line(color = "grey", size = 1) # Adjust line thickness 160 | ) 161 | 162 | save_plot("test.svg", fig = p, width=30, height=25) 163 | 164 | -------------------------------------------------------------------------------- /analysis/ridgeline.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | library(ggtext) 3 | library(ggridges) 4 | library(ggdist) 5 | library(dplyr) 6 | library(sjPlot) 7 | library(readr) 8 | library(tidyr) 9 | 10 | # Read the CSV file into a data frame 11 | data <- read.csv("esp32-nimble-results.csv", 12 | check.names = FALSE, 13 | stringsAsFactors = FALSE) 14 | 15 | column_order <- names(data) 16 | 17 | # Reshape from wide to long format 18 | data_long <- data %>% 19 | pivot_longer( 20 | cols = everything(), 21 | names_to = "category", 22 | values_to = "duration" 23 | ) 24 | 25 | # Convert 'category' to a factor with levels defined by column order from the CSV 26 | data_long$category <- factor(data_long$category, levels = column_order) 27 | # data_long$category <- factor(data_long$category, levels = rev(column_order)) 28 | 29 | # Convert to microseconds 30 | # data_long$duration <- data_long$duration/1e3 31 | 32 | # Sum the number of samples to show as n=123 text 33 | add_sample <- function(x){ 34 | return(c(y = max(x) + .025, 35 | label = length(x))) 36 | } 37 | 38 | # Main Chart 39 | p <- data_long %>% 40 | filter(!is.na(duration)) %>% 41 | ggplot(aes(x = category, y = duration)) + 42 | # Distribution curve 43 | ggdist::stat_halfeye( 44 | aes(), 45 | adjust = .3, 46 | normalize = "groups", 47 | scale = 0.75, 48 | height = 0.75, 49 | width = .5, 50 | .width = 0, 51 | justification = -.5, 52 | alpha = 0.70, 53 | na.rm = TRUE, 54 | point_color = NA 55 | ) + 56 | #Boxplot 57 | geom_boxplot( 58 | aes(), 59 | width = .3, 60 | outlier.colour = 'black', 61 | outlier.alpha = 0.6, 62 | outlier.size = 0.4, 63 | ) + 64 | # Median value text 65 | stat_summary( 66 | geom = "text", 67 | fun = "median", 68 | aes(label = round(..y.., 2)), 69 | family = "Roboto Mono", 70 | fontface = "bold", 71 | size = 5, 72 | vjust = 2.75 73 | ) + 74 | # Sample Count text 75 | stat_summary( 76 | geom = "text", 77 | fun.data = add_sample, 78 | aes(label = paste("n =", ..label..)), 79 | family = "Roboto Condensed", 80 | size = 4, 81 | vjust = 1.5, 82 | hjust = -0.4 83 | ) + 84 | coord_flip( ylim = c(0, NA), clip = "off" ) + 85 | # Override x-axis range and ticks 86 | scale_y_continuous( 87 | limits = c(0, 195), 88 | breaks = seq(0, 1500, by = 20) 89 | # breaks = pretty(range(data_long$duration, na.rm = TRUE), n = 6, min.n = 4), 90 | #expand = c(0.05, 1) 91 | ) + 92 | # Axis Labels 93 | labs( 94 | x = NULL, 95 | y = "Duration (milliseconds)", 96 | title = "ESP32 NimBLE GATT Transfer Durations", 97 | subtitle = "ESP-IDF 5.1.1, NimBLE, MTU=200, Notify", 98 | caption = "Lower is better" 99 | ) + 100 | theme_minimal() + 101 | theme( 102 | text = element_text(family = "Roboto Mono", size = 20, face = "plain"), 103 | legend.position = "none", 104 | plot.margin = margin(5, 5, 5, 5), 105 | plot.title = element_text(hjust = 0.5), # Center the title 106 | plot.subtitle = element_text(hjust = 0.5, size = 16), # Center the subtitle 107 | plot.caption = element_text(hjust = 0.5), # Center the caption 108 | axis.text.x = element_text(margin = margin(t = 5, r = 0, b = 15, l = 0)), # +Gap between axis and label 109 | axis.text.y = element_text(face = "bold"), 110 | #axis.line.y = element_line(color = "grey", size = 1) # Adjust line thickness 111 | #panel.grid.minor = element_blank(), 112 | #panel.grid.major.y = element_blank(), 113 | #axis.ticks = element_blank(), 114 | #axis.text.x = element_text(family = "Roboto Mono"), 115 | 116 | ) 117 | 118 | p 119 | 120 | save_plot("test.svg", fig = p, width=30, height=14) 121 | 122 | -------------------------------------------------------------------------------- /analysis/saleae-latency-log-cleanup.R: -------------------------------------------------------------------------------- 1 | # Import a set of Saleae Logic 2 export csv files using ISO8061 timestamps 2 | # Calculates the time difference between the trigger (CH0) and the 'done' strobe (CH1) 3 | # Exports a column of durations per file found in the workspace 4 | 5 | # I don't know R, so sorry in advance! 6 | 7 | 8 | # Get list of all CSV files 9 | csv_files <- list.files(pattern="*.csv") 10 | 11 | # Initialize an empty data frame to store consolidated results 12 | consolidated_df <- data.frame() 13 | 14 | # Loop through each file 15 | for (file_name in csv_files) { 16 | print(paste("Processing file:", file_name)) 17 | 18 | # Read CSV file into a data frame with custom column names 19 | df <- read.csv(file_name, col.names=c("Time", "Channel0", "Channel1")) 20 | 21 | # Handle timestamp formatting i.e. 2023-10-23T23:10:34.036973000+00:00 22 | 23 | # Extract nanoseconds into another column, reformat time 24 | nanoseconds <- gsub("^.*\\.(\\d{9}).*$", "\\1", df$Time) 25 | df$Nanoseconds <- as.numeric(nanoseconds) 26 | df$Time <- as.POSIXct(sub("\\.\\d{9}", "", df$Time), format="%Y-%m-%dT%H:%M:%S", tz="UTC") 27 | 28 | # Create an empty vector to store Time_Diff for this file 29 | time_diff_vector <- numeric(0) 30 | 31 | # Identify start and end indices 32 | start_idx <- which(df$Channel0 == 1 & df$Channel1 == 0) 33 | end_idx <- which((df$Channel0 == 0 | df$Channel0 == 1) & df$Channel1 == 1) 34 | 35 | # Calculate time differences 36 | for (s in start_idx) { 37 | # Check the row above this pattern was 0,0 i.e. reset state. 38 | if (s > 1 && df$Channel0[s - 1] == 0 && df$Channel1[s - 1] == 0) { 39 | 40 | for (e in end_idx) { 41 | if (e > s) { 42 | start_time_s <- as.numeric(df$Time[s]) 43 | start_time_ns <- df$Nanoseconds[s] 44 | 45 | end_time_s <- as.numeric(df$Time[e]) 46 | end_time_ns <- df$Nanoseconds[e] 47 | 48 | time_diff_s <- end_time_s - start_time_s 49 | time_diff_ns <- end_time_ns - start_time_ns 50 | 51 | if (time_diff_ns < 0) { 52 | time_diff_ns <- 1e9 + time_diff_ns 53 | time_diff_s <- time_diff_s - 1 54 | } 55 | 56 | # Combine time differences 57 | time_diff_sec <- time_diff_s + (time_diff_ns / 1e9) 58 | 59 | # Append to this file's Time_Diff vector (in milliseconds) 60 | time_diff_vector <- c(time_diff_vector, (time_diff_sec*1e3)) 61 | 62 | break 63 | } 64 | } 65 | } 66 | } 67 | 68 | # Add this file's Time_Diff vector as a new column to the consolidated data frame 69 | column_name <- gsub(".csv$", "", file_name) # Remove .csv from the file name for the column name 70 | 71 | if (nrow(consolidated_df) < length(time_diff_vector)) { 72 | # Extend the data frame with NA values if new vector is longer 73 | consolidated_df[nrow(consolidated_df) + 1:length(time_diff_vector), ] <- NA 74 | } 75 | 76 | consolidated_df[1:length(time_diff_vector), column_name] <- time_diff_vector 77 | } 78 | 79 | # Export 'cleaned' latency duration as csv 80 | write.csv(consolidated_df, "consolidated_df.csv", row.names = FALSE) 81 | -------------------------------------------------------------------------------- /analysis/uart-overhead-facet-ridgelines.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | library(ggtext) 3 | library(ggridges) 4 | library(ggdist) 5 | library(dplyr) 6 | library(sjPlot) 7 | library(readr) 8 | library(tidyr) 9 | 10 | # Read the CSV file into a data frame 11 | data <- read.csv("increasing-baudrate-transport-duration-removed.csv", 12 | check.names = FALSE, 13 | stringsAsFactors = FALSE) 14 | 15 | # Reshape from wide to long format 16 | data_long <- data %>% 17 | pivot_longer( 18 | cols = everything(), 19 | names_to = "category", 20 | values_to = "duration" 21 | ) %>% 22 | # Create the baudrate column by extracting the baud rate 23 | mutate(baudrate = gsub("-.*", "", category), 24 | # Create the subgroup column by extracting the type (POLL, IRQ, DMA) 25 | subgroup = gsub(".*-", "", category)) 26 | 27 | # Convert baudrate to numeric to sort numerically 28 | data_long$baudrate <- as.numeric(as.character(data_long$baudrate)) 29 | 30 | # Order the baudrate numerically and then convert back to a factor 31 | data_long$baudrate <- factor(data_long$baudrate, levels = sort(unique(data_long$baudrate), decreasing = FALSE)) 32 | 33 | # Convert to microseconds 34 | data_long$duration <- data_long$duration/1e3 35 | 36 | 37 | # Custom colours 38 | less_saturated_colors <- c('#b27c7c', '#a7b27c', '#7cb292', '#7c92b2', '#a77cb2') 39 | 40 | # Sum the number of samples to show as n=123 text 41 | add_sample <- function(x){ 42 | return(c(y = max(x) + .025, 43 | label = length(x))) 44 | } 45 | 46 | # Main Chart 47 | p <- data_long %>% 48 | filter(!is.na(duration)) %>% 49 | ggplot(aes(x = subgroup, y = duration, fill = baudrate,)) + 50 | # Distribution curve 51 | ggdist::stat_halfeye( 52 | aes(), 53 | adjust = .3, 54 | normalize = "groups", 55 | scale = 0.75, 56 | height = 0.75, 57 | width = .3, 58 | .width = 0, 59 | justification = -.8, 60 | alpha = 0.70, 61 | na.rm = TRUE, 62 | point_color = NA 63 | ) + 64 | #Boxplot 65 | geom_boxplot( 66 | aes(), 67 | width = .2, 68 | outlier.colour = 'black', 69 | outlier.alpha = 0.6, 70 | outlier.size = 0.4, 71 | ) + 72 | # Median value text 73 | stat_summary( 74 | geom = "text", 75 | fun = "median", 76 | aes(label = round(..y.., 2)), 77 | family = "Roboto Mono", 78 | fontface = "bold", 79 | size = 5, 80 | vjust = 2.2 81 | ) + 82 | # Sample Count text 83 | stat_summary( 84 | geom = "text", 85 | fun.data = add_sample, 86 | aes(label = paste("n =", ..label..)), 87 | family = "Roboto Condensed", 88 | size = 4, 89 | vjust = 1.5, 90 | hjust = -0.4 91 | ) + 92 | 93 | # Each baudrate should use it's own colour 94 | # scale_fill_manual(values = less_saturated_colors) + 95 | # scale_color_manual(values = less_saturated_colors) + 96 | 97 | # Separate facet for each baudrate 98 | # One column layout 99 | facet_grid( 100 | baudrate ~ ., 101 | scales = "fixed", 102 | space = "fixed", 103 | switch = "y", 104 | ) + 105 | # Horizontal layout 106 | coord_flip( 107 | # Specify y-axis limit now, this approach ensures boxplot/dist curves 108 | # continue to plot offscreen, as we're intentionally overflowing some 109 | ylim = c(0, 17), 110 | clip = "off" 111 | ) + 112 | 113 | # # Annotation arrow to show data going off edge of plot 114 | # annotate( 115 | # "segment", 116 | # x = 1, 117 | # xend = 1, 118 | # y = 16.9, 119 | # yend = 17.9, 120 | # arrow = arrow(length = unit(0.4, "cm")), 121 | # linewidth = 1, 122 | # ) + 123 | # 124 | # annotate( 125 | # "segment", 126 | # x = 4, 127 | # xend = 4, 128 | # y = 16.9, 129 | # yend = 17.9, 130 | # arrow = arrow(length = unit(0.4, "cm")), 131 | # linewidth = 1, 132 | # colour = less_saturated_colors[2] 133 | # ) + 134 | # 135 | # annotate( 136 | # "segment", 137 | # x = 7, 138 | # xend = 7, 139 | # y = 16.9, 140 | # yend = 17.9, 141 | # arrow = arrow(length = unit(0.4, "cm")), 142 | # linewidth = 1, 143 | # colour = less_saturated_colors[3] 144 | # ) + 145 | # 146 | # annotate( 147 | # "segment", 148 | # x = 9.7, 149 | # xend = 9.7, 150 | # y = 16.9, 151 | # yend = 17.9, 152 | # arrow = arrow(length = unit(0.4, "cm")), 153 | # linewidth = 1, 154 | # colour = less_saturated_colors[4] 155 | # ) + 156 | 157 | # Override x-axis range and ticks 158 | scale_y_continuous( 159 | #limits = c(0, 20), 160 | #breaks = seq(0, 1000, by = 5) 161 | # breaks = pretty(range(data_long$duration, na.rm = TRUE), n = 6, min.n = 4), 162 | #expand = c(0.05, 1) 163 | ) + 164 | # Axis Labels 165 | labs( 166 | x = NULL, 167 | y = "Overhead (microseconds)", 168 | title = "STM32 UART Overhead (transport duration subtracted)", 169 | subtitle = "F429 Nucleo-144 @ 168MHz, LL + FiFO", 170 | caption = "Lower is better" 171 | ) + 172 | theme_minimal() + 173 | theme( 174 | text = element_text(family = "Roboto Mono", size = 20, face = "plain"), 175 | legend.position = "none", 176 | plot.margin = margin(5, 5, 5, 5), 177 | plot.title = element_text(hjust = 0.5), # Center the title 178 | plot.subtitle = element_text(hjust = 0.5, size = 16), # Center the subtitle 179 | plot.caption = element_text(hjust = 0.5), # Center the caption 180 | axis.text.x = element_text(margin = margin(t = 5, r = 0, b = 15, l = 0)), # +Gap between axis and label 181 | axis.text.y = element_text(face = "bold"), 182 | 183 | strip.text.x = element_text(face = "bold", size = 12, color = "black",hjust = 0), 184 | strip.placement = "outside", 185 | panel.spacing = unit(0.5, "lines") 186 | 187 | #axis.line.y = element_line(color = "grey", size = 1) # Adjust line thickness 188 | ) 189 | 190 | p 191 | 192 | save_plot("test.svg", fig = p, width=30, height=28) 193 | 194 | -------------------------------------------------------------------------------- /firmware/esp-802154/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *build*/ -------------------------------------------------------------------------------- /firmware/esp-802154/1kb-no-ack.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/esp-802154/1kb-no-ack.pcapng -------------------------------------------------------------------------------- /firmware/esp-802154/1kb-with-ack.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/esp-802154/1kb-with-ack.pcapng -------------------------------------------------------------------------------- /firmware/esp-802154/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(lrpwan) -------------------------------------------------------------------------------- /firmware/esp-802154/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 802.15.4 Test Firmware 2 | 3 | Firmware and logs for ESP32-C6 based latency tests. 4 | 5 | IO Setup: 6 | 7 | - IO19 is driven with a 3.3V trigger pulse from my sig-gen. 8 | - IO18 is a 3.3V output signal 9 | 10 | ## Firwmare 11 | 12 | Developed using CLion, and I run my IDF development environment via docker container: 13 | 14 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v4.4` 15 | 16 | `idf.py set-target esp32c6` 17 | 18 | I used `idf.py menuconfig` to set the compiler optimisation level to 'performance' for o2. 19 | 20 | There's a 'throughput optimisation' setting in "Component Config/IEEE 802.15.4" menu that I also experimented with enabling. 21 | -------------------------------------------------------------------------------- /firmware/esp-802154/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( SRCS 2 | "lrpwan_main.c" 3 | INCLUDE_DIRS 4 | "." 5 | ) -------------------------------------------------------------------------------- /firmware/esp-tcp/.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /firmware/esp-tcp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(tcp) 7 | -------------------------------------------------------------------------------- /firmware/esp-tcp/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 TCP Tests 2 | 3 | Builds on the [ESP-IDF examples for TCP server/client](https://github.com/espressif/esp-idf/tree/master/examples/protocols/sockets/tcp_server). 4 | 5 | IO Setup: 6 | 7 | - IO19 is driven with a 3.3V trigger pulse from my sig-gen. 8 | - IO18 is a 3.3V output signal 9 | 10 | ## Performance Tweaks 11 | 12 | - Disabled Nagle's Algorithm (TCP_NODELAY=1) on server via sockopts. 13 | - Disable WiFi power saving (WIFI_PS_NONE=1) on server and client. 14 | - Ensure CONFIG_ESP_WIFI_IRAM_OPT, CONFIG_LWIP_IRAM_OPTIMIZATION are enabled 15 | 16 | ## Firwmare 17 | 18 | I run my development environment via docker 19 | 20 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v5.1` 21 | 22 | I used `idf.py menuconfig` to set the compiler optimisation level to 'performance' for o2, as well as IRAM configs mentioned above. 23 | 24 | Also provide WiFi AP SSID/credentials in the Example Connection Configuration menu. 25 | 26 | Use `idf.py build` and/or `idf.py -p /dev/ttyUSB0 flash` to build and flash to hardware. 27 | 28 | -------------------------------------------------------------------------------- /firmware/esp-tcp/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( SRCS 2 | "tcp_main.c" 3 | "tcp_client.c" 4 | "tcp_server.c" 5 | INCLUDE_DIRS "." 6 | ) -------------------------------------------------------------------------------- /firmware/esp-tcp/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | protocol_examples_common: 3 | path: ${IDF_PATH}/examples/common_components/protocol_examples_common -------------------------------------------------------------------------------- /firmware/esp-tcp/main/tcp_client.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_CLIENT_H 2 | #define TCP_CLIENT_H 3 | 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | #include "tcp_main_defs.h" 12 | 13 | /* -------------------------------------------------------------------------- */ 14 | 15 | void tcp_client_task(void *pvParameters); 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | 19 | void tcp_client_send_payload( uint8_t *data, uint32_t length ); 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | void tcp_client_register_user_evt_queue( QueueHandle_t *queue ); 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif // end TCP_CLIENT_H -------------------------------------------------------------------------------- /firmware/esp-tcp/main/tcp_main_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_DEFS_H 2 | #define TCP_DEFS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* -------------------------------------------------------------------------- */ 9 | 10 | typedef enum { 11 | BENCH_SEND_CB, 12 | BENCH_RECV_CB, 13 | } bench_event_id_t; 14 | 15 | typedef struct { 16 | uint32_t bytes_sent; 17 | } bench_event_send_cb_t; 18 | 19 | typedef struct { 20 | uint8_t *data; 21 | uint32_t data_len; 22 | } bench_event_recv_cb_t; 23 | 24 | typedef union { 25 | bench_event_send_cb_t send_cb; 26 | bench_event_recv_cb_t recv_cb; 27 | } bench_event_data_t; 28 | 29 | // Main task queue needs to support send and receive events 30 | // The ID field helps distinguish between them 31 | typedef struct { 32 | bench_event_id_t id; 33 | bench_event_data_t data; 34 | } bench_event_t; 35 | 36 | #define BENCHMARK_QUEUE_SIZE (8) 37 | 38 | #define BENCH_DATA_MAX_LEN (2048) 39 | 40 | /* -------------------------------------------------------------------------- */ 41 | 42 | #define PORT (3333) 43 | #define KEEPALIVE_IDLE (5) 44 | #define KEEPALIVE_INTERVAL (5) 45 | #define KEEPALIVE_COUNT (3) 46 | #define NODELAY_CONFIG (1) // disables Nagle's Algorithm 47 | 48 | /* -------------------------------------------------------------------------- */ 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif // end TCP_DEFS_H -------------------------------------------------------------------------------- /firmware/esp-tcp/main/tcp_server.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_SERVER_H 2 | #define TCP_SERVER_H 3 | 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | #include "tcp_main_defs.h" 12 | 13 | /* -------------------------------------------------------------------------- */ 14 | 15 | void tcp_server_task(void *pvParameters); 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | 19 | void tcp_server_send_payload( uint8_t *data, uint32_t length ); 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | void tcp_server_register_user_evt_queue( QueueHandle_t *queue ); 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif // end TCP_SERVER_H -------------------------------------------------------------------------------- /firmware/esp-udp/.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /firmware/esp-udp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(udp) 7 | -------------------------------------------------------------------------------- /firmware/esp-udp/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 UDP Tests 2 | 3 | A flat copy of the TCP test firwmare with modifications made to socket connections for UDP behaviour. 4 | 5 | Unlike the TCP test, the same firmware runs on both devices. The hard-coded IP needs to be set correctly though. 6 | 7 | IO Setup: 8 | 9 | - IO19 is driven with a 3.3V trigger pulse from my sig-gen. 10 | - IO18 is a 3.3V output signal 11 | 12 | ## Performance Tweaks 13 | 14 | - Disable WiFi power saving (WIFI_PS_NONE=1). 15 | - Ensure CONFIG_ESP_WIFI_IRAM_OPT, CONFIG_LWIP_IRAM_OPTIMIZATION are enabled 16 | 17 | ## Firwmare 18 | 19 | I run my development environment via docker 20 | 21 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v5.1` 22 | 23 | I used `idf.py menuconfig` to set the compiler optimisation level to 'performance' for o2, as well as IRAM configs mentioned above. 24 | 25 | Also provide WiFi AP SSID/credentials in the Example Connection Configuration menu. 26 | 27 | Use `idf.py build` and/or `idf.py -p /dev/ttyUSB0 flash` to build and flash to hardware. 28 | 29 | -------------------------------------------------------------------------------- /firmware/esp-udp/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( SRCS 2 | "udp_main.c" 3 | "udp_server.c" 4 | INCLUDE_DIRS "." 5 | ) -------------------------------------------------------------------------------- /firmware/esp-udp/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | protocol_examples_common: 3 | path: ${IDF_PATH}/examples/common_components/protocol_examples_common -------------------------------------------------------------------------------- /firmware/esp-udp/main/udp_main_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef UDP_DEFS_H 2 | #define UDP_DEFS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* -------------------------------------------------------------------------- */ 9 | 10 | typedef enum { 11 | BENCH_SEND_CB, 12 | BENCH_RECV_CB, 13 | } bench_event_id_t; 14 | 15 | typedef struct { 16 | uint32_t bytes_sent; 17 | } bench_event_send_cb_t; 18 | 19 | typedef struct { 20 | uint8_t *data; 21 | uint32_t data_len; 22 | } bench_event_recv_cb_t; 23 | 24 | typedef union { 25 | bench_event_send_cb_t send_cb; 26 | bench_event_recv_cb_t recv_cb; 27 | } bench_event_data_t; 28 | 29 | // Main task queue needs to support send and receive events 30 | // The ID field helps distinguish between them 31 | typedef struct { 32 | bench_event_id_t id; 33 | bench_event_data_t data; 34 | } bench_event_t; 35 | 36 | #define BENCHMARK_QUEUE_SIZE (8) 37 | 38 | #define BENCH_DATA_MAX_LEN (2048) 39 | 40 | /* -------------------------------------------------------------------------- */ 41 | 42 | #define PORT (3333) 43 | 44 | /* -------------------------------------------------------------------------- */ 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif // end UDP_DEFS_H -------------------------------------------------------------------------------- /firmware/esp-udp/main/udp_server.c: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | 3 | #include 4 | #include 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | #include "esp_system.h" 8 | #include "esp_wifi.h" 9 | #include "esp_event.h" 10 | #include "esp_log.h" 11 | #include "esp_netif.h" 12 | #include "protocol_examples_common.h" 13 | 14 | #include "lwip/err.h" 15 | #include "lwip/sockets.h" 16 | #include "lwip/sys.h" 17 | #include 18 | 19 | #include "udp_main_defs.h" 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | static const char *TAG = "SERVER"; 24 | 25 | // #define DEST_IP_ADDR "192.168.1.20" 26 | #define DEST_IP_ADDR "192.168.1.12" 27 | 28 | /* -------------------------------------------------------------------------- */ 29 | 30 | static QueueHandle_t *user_evt_queue; 31 | static int active_sock = -1; 32 | 33 | /* -------------------------------------------------------------------------- */ 34 | 35 | /** 36 | * Modified from https://github.com/espressif/esp-idf/blob/master/examples/protocols/sockets/non_blocking/main/non_blocking_socket_example.c 37 | * Non-blocking read, 38 | * 39 | * @param[in] tag Logging tag 40 | * @param[in] sock Socket for reception 41 | * @param[out] data Data pointer to write the received data 42 | * @param[in] max_len Maximum size of the allocated space for receiving data 43 | * @return 44 | * >0 : Size of received data 45 | * =0 : No data available 46 | * -1 : Error occurred during socket read operation 47 | * -2 : Socket is not connected, to distinguish between an actual socket error and active disconnection 48 | */ 49 | static int try_receive(const int sock, char * data, size_t max_len) 50 | { 51 | struct sockaddr_in source_addr; // For storing the sender's address 52 | socklen_t addr_len = sizeof(source_addr); 53 | 54 | int len = recvfrom(sock, data, max_len, 0, (struct sockaddr *)&source_addr, &addr_len); 55 | 56 | if (len < 0) 57 | { 58 | if( errno == EAGAIN || errno == EWOULDBLOCK ) 59 | { 60 | return 0; // Not an error 61 | } 62 | 63 | ESP_LOGW(TAG, "[sock=%d]: Rx Error", sock); 64 | return -1; 65 | } 66 | 67 | // TODO: Consider passing the sender's address back to the caller 68 | 69 | return len; 70 | } 71 | 72 | /* -------------------------------------------------------------------------- */ 73 | 74 | void udp_server_task(void *pvParameters) 75 | { 76 | char addr_str[128]; 77 | int addr_family = AF_INET; 78 | int ip_protocol = 0; 79 | struct sockaddr_storage dest_addr; 80 | 81 | active_sock = -128; 82 | 83 | // ipv4 84 | struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr; 85 | dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); 86 | dest_addr_ip4->sin_family = AF_INET; 87 | dest_addr_ip4->sin_port = htons(PORT); 88 | ip_protocol = IPPROTO_IP; 89 | 90 | int listen_sock = socket(addr_family, SOCK_DGRAM, ip_protocol); 91 | if (listen_sock < 0) 92 | { 93 | ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); 94 | vTaskDelete(NULL); 95 | return; 96 | } 97 | 98 | int opt = 1; 99 | setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 100 | ESP_LOGI(TAG, "Socket created"); 101 | 102 | // Marking the socket as non-blocking 103 | int flags = fcntl(listen_sock, F_GETFL); 104 | if( fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK) == -1 ) 105 | { 106 | ESP_LOGE(TAG, "Unable to set socket non blocking: errno %d", errno); 107 | goto CLEAN_UP; 108 | } 109 | ESP_LOGI(TAG, "Socket marked as non blocking"); 110 | 111 | int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 112 | if (err != 0) 113 | { 114 | ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); 115 | ESP_LOGE(TAG, "IPPROTO: %d", addr_family); 116 | goto CLEAN_UP; 117 | } 118 | ESP_LOGI(TAG, "Socket bound, port %d", PORT); 119 | 120 | // Inbound temp buffer 121 | char rx_buffer[2048]; 122 | 123 | active_sock = listen_sock; 124 | 125 | while (1) 126 | { 127 | // This is an open socket -> try to serve it 128 | int len = try_receive(active_sock, rx_buffer, sizeof(rx_buffer)); 129 | 130 | // struct sockaddr_storage source_addr; 131 | // socklen_t addr_len = sizeof(source_addr); 132 | 133 | if( len < 0 ) 134 | { 135 | // Error occurred within this client's socket -> close and mark invalid 136 | ESP_LOGI(TAG, "[sock=%d]: try_receive() returned %d -> closing the socket", active_sock, len); 137 | close(active_sock); 138 | active_sock = -1; 139 | } 140 | else if( len > 0 ) 141 | { 142 | // ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer); 143 | 144 | // Post an event to the user-space event queue with the inbound data 145 | if( user_evt_queue && len) 146 | { 147 | bench_event_t evt; 148 | bench_event_recv_cb_t *recv_cb = &evt.data.recv_cb; 149 | evt.id = BENCH_RECV_CB; 150 | 151 | // Allocate a sufficiently large chunk of memory and copy the payload into it 152 | // User task is responsible for freeing this memory 153 | recv_cb->data = malloc( len ); 154 | if( recv_cb->data == NULL ) 155 | { 156 | ESP_LOGE(TAG, "RX Malloc fail"); 157 | return; 158 | } 159 | 160 | memcpy(recv_cb->data, rx_buffer, len); 161 | recv_cb->data_len = len; 162 | 163 | // Put the event into the queue for processing 164 | if( xQueueSend(user_evt_queue, &evt, 512) != pdTRUE ) 165 | { 166 | ESP_LOGW(TAG, "RX event failed to enqueue"); 167 | free(recv_cb->data); 168 | } 169 | } 170 | } 171 | 172 | vTaskDelay(pdMS_TO_TICKS(1)); 173 | } 174 | 175 | CLEAN_UP: 176 | active_sock = -1; 177 | 178 | close(listen_sock); 179 | vTaskDelete(NULL); 180 | } 181 | 182 | /* -------------------------------------------------------------------------- */ 183 | 184 | void udp_server_send_payload( uint8_t *data, uint32_t length ) 185 | { 186 | // Uses hard-coded IP + port 187 | struct sockaddr_in dest_addr; 188 | dest_addr.sin_addr.s_addr = inet_addr(DEST_IP_ADDR); 189 | dest_addr.sin_family = AF_INET; 190 | dest_addr.sin_port = htons(PORT); 191 | 192 | int sent = sendto(active_sock, data, length, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 193 | if (sent < 0) 194 | { 195 | ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); 196 | return; 197 | } 198 | 199 | // Optionally log the number of bytes sent 200 | // ESP_LOGI(TAG, "Sent %d bytes", sent); 201 | } 202 | 203 | /* -------------------------------------------------------------------------- */ 204 | 205 | void udp_server_register_user_evt_queue( QueueHandle_t *queue ) 206 | { 207 | if( queue ) 208 | { 209 | user_evt_queue = queue; 210 | } 211 | } 212 | 213 | /* -------------------------------------------------------------------------- */ 214 | -------------------------------------------------------------------------------- /firmware/esp-udp/main/udp_server.h: -------------------------------------------------------------------------------- 1 | #ifndef UDP_SERVER_H 2 | #define UDP_SERVER_H 3 | 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | #include "udp_main_defs.h" 12 | 13 | /* -------------------------------------------------------------------------- */ 14 | 15 | void udp_server_task(void *pvParameters); 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | 19 | void udp_server_send_payload( uint8_t *data, uint32_t length ); 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | void udp_server_register_user_evt_queue( QueueHandle_t *queue ); 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif // end UDP_SERVER_H -------------------------------------------------------------------------------- /firmware/esp-websockets/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | managed_components/ -------------------------------------------------------------------------------- /firmware/esp-websockets/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(websockets) 7 | -------------------------------------------------------------------------------- /firmware/esp-websockets/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 Websockets Tests 2 | 3 | Builds on the [ESP-IDF examples for websockets echo](https://github.com/espressif/esp-idf/tree/master/examples/protocols/http_server/ws_echo_server). 4 | 5 | IO Setup: 6 | 7 | - IO19 is driven with a 3.3V trigger pulse from my sig-gen. 8 | - IO18 is a 3.3V output signal 9 | 10 | ## Performance Tweaks 11 | 12 | - Disable WiFi power saving (WIFI_PS_NONE=1) on server and client. 13 | - Ensure CONFIG_ESP_WIFI_IRAM_OPT, CONFIG_LWIP_IRAM_OPTIMIZATION are enabled 14 | 15 | ## Firwmare 16 | 17 | I run my development environment via docker 18 | 19 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v5.1` 20 | 21 | I used `idf.py menuconfig` to set the compiler optimisation level to 'performance' for o2, as well as IRAM configs mentioned above. 22 | 23 | `CONFIG_HTTPD_WS_SUPPORT` must be enabled in the HTTP component config. 24 | 25 | `idf.py add-dependency "espressif/esp_websocket_client^1.0.0"` was needed to add the client component, which is then in `/managed_components` 26 | 27 | Also provide WiFi AP SSID/credentials in the Example Connection Configuration menu. 28 | 29 | Use `idf.py build` and/or `idf.py -p /dev/ttyUSB0 flash` to build and flash to hardware. 30 | 31 | -------------------------------------------------------------------------------- /firmware/esp-websockets/dependencies.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | espressif/esp_websocket_client: 3 | component_hash: d8ccaffac2871dc756bdc08d7ff0bfa2daa7db3d94b0c7bf714b1962efe5e7c9 4 | source: 5 | service_url: https://api.components.espressif.com/ 6 | type: service 7 | version: 1.2.2 8 | idf: 9 | component_hash: null 10 | source: 11 | type: idf 12 | version: 5.1.2 13 | protocol_examples_common: 14 | component_hash: null 15 | source: 16 | path: /opt/esp/idf/examples/common_components/protocol_examples_common 17 | type: local 18 | version: '*' 19 | manifest_hash: 5d243d4cb4d9724037c11c24be0350eedf752dbc37d0ec9b36cb3576e870ee8b 20 | target: esp32 21 | version: 1.0.0 22 | -------------------------------------------------------------------------------- /firmware/esp-websockets/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( SRCS 2 | "websockets_main.c" 3 | "websocket_client.c" 4 | "websocket_server.c" 5 | INCLUDE_DIRS "." 6 | ) -------------------------------------------------------------------------------- /firmware/esp-websockets/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | espressif/esp_websocket_client: "^1.0.0" 3 | protocol_examples_common: 4 | path: ${IDF_PATH}/examples/common_components/protocol_examples_common -------------------------------------------------------------------------------- /firmware/esp-websockets/main/websocket_client.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSOCKET_CLIENT_H 2 | #define WEBSOCKET_CLIENT_H 3 | 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | #include "websockets_main_defs.h" 12 | 13 | /* -------------------------------------------------------------------------- */ 14 | 15 | void websocket_client_task(void *pvParameters); 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | 19 | void websocket_client_send_payload( uint8_t *data, uint32_t length ); 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | void websocket_client_register_user_evt_queue( QueueHandle_t *queue ); 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif // end WEBSOCKET_CLIENT_H -------------------------------------------------------------------------------- /firmware/esp-websockets/main/websocket_server.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSOCKET_SERVER_H 2 | #define WEBSOCKET_SERVER_H 3 | 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | #include "websockets_main_defs.h" 12 | 13 | /* -------------------------------------------------------------------------- */ 14 | 15 | void websocket_server_task(void *pvParameters); 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | 19 | void websocket_server_send_payload( uint8_t *data, uint32_t length ); 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | void websocket_server_register_user_evt_queue( QueueHandle_t *queue ); 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif // end WEBSOCKET_SERVER_H -------------------------------------------------------------------------------- /firmware/esp-websockets/main/websockets_main_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_DEFS_H 2 | #define TCP_DEFS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* -------------------------------------------------------------------------- */ 9 | 10 | typedef enum { 11 | BENCH_SEND_CB, 12 | BENCH_RECV_CB, 13 | } bench_event_id_t; 14 | 15 | typedef struct { 16 | uint32_t bytes_sent; 17 | } bench_event_send_cb_t; 18 | 19 | typedef struct { 20 | uint8_t *data; 21 | uint32_t data_len; 22 | } bench_event_recv_cb_t; 23 | 24 | typedef union { 25 | bench_event_send_cb_t send_cb; 26 | bench_event_recv_cb_t recv_cb; 27 | } bench_event_data_t; 28 | 29 | // Main task queue needs to support send and receive events 30 | // The ID field helps distinguish between them 31 | typedef struct { 32 | bench_event_id_t id; 33 | bench_event_data_t data; 34 | } bench_event_t; 35 | 36 | #define BENCHMARK_QUEUE_SIZE (8) 37 | 38 | #define BENCH_DATA_MAX_LEN (2048) 39 | 40 | /* -------------------------------------------------------------------------- */ 41 | 42 | #define NO_DATA_TIMEOUT_SEC (15) 43 | 44 | #define CONFIG_WEBSOCKET_SERVER_URI "ws://192.168.1.20/ws" 45 | 46 | /* -------------------------------------------------------------------------- */ 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif // end TCP_DEFS_H -------------------------------------------------------------------------------- /firmware/esp-websockets/sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | CONFIG_HTTPD_WS_SUPPORT=y -------------------------------------------------------------------------------- /firmware/esp32-ble/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *build*/ -------------------------------------------------------------------------------- /firmware/esp32-ble/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(espble) -------------------------------------------------------------------------------- /firmware/esp32-ble/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 BLE Test Firmware 2 | 3 | Firmware and logs for ESP32 Bluedroid BLE latency tests. 4 | 5 | 6 | - I ran tests with builds set to -O2 (performance) via menuconfig. 7 | 8 | IO Setup: 9 | 10 | - IO19 is driven with a 3.3V trigger pulse from my sig-gen. 11 | - IO18 is a 3.3V output signal 12 | 13 | ## Firwmare 14 | 15 | I run my development environment via docker 16 | 17 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v5.1` 18 | 19 | I used `idf.py menuconfig` to set the compiler optimisation level to 'performance' for o3. 20 | 21 | Use `idf.py build` and/or `idf.py -p /dev/ttyUSB0 flash` to build and flash to hardware. 22 | 23 | ### Config 24 | 25 | 26 | 27 | # References 28 | 29 | 30 | 31 | 32 | 33 | # TODO 34 | 35 | Remove uart tasks and redirect input/output as events into benchmark task 36 | 37 | 38 | gap_event_handler and esp_gap_cb are similar and don't conflict, could condense server cases into existing client functionality 39 | 40 | gatts_event_handler and esp_gattc_cb do nearly the same thing, could be condensed into one function 41 | 42 | -------------------------------------------------------------------------------- /firmware/esp32-ble/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( SRCS 2 | "ble_main.c" 3 | "client.c" 4 | "server.c" 5 | INCLUDE_DIRS ".") -------------------------------------------------------------------------------- /firmware/esp32-ble/main/ble_main_defs.h: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | 3 | typedef enum { 4 | SPP_SEND_CB, 5 | SPP_RECV_CB, 6 | } spp_event_id_t; 7 | 8 | typedef struct { 9 | uint32_t bytes_sent; 10 | bool congested; 11 | } spp_event_send_cb_t; 12 | 13 | typedef struct { 14 | uint8_t *data; 15 | uint32_t data_len; 16 | } spp_event_recv_cb_t; 17 | 18 | typedef union { 19 | spp_event_send_cb_t send_cb; 20 | spp_event_recv_cb_t recv_cb; 21 | } spp_event_data_t; 22 | 23 | // Main task queue needs to support send and receive events 24 | // The ID field helps distinguish between them 25 | typedef struct { 26 | spp_event_id_t id; 27 | spp_event_data_t data; 28 | } spp_event_t; 29 | 30 | /* -------------------------------------------------------------------------- */ 31 | -------------------------------------------------------------------------------- /firmware/esp32-ble/main/client.h: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | 3 | #include "spp_defs.h" 4 | #include "freertos/FreeRTOS.h" 5 | #include "freertos/semphr.h" 6 | 7 | /* -------------------------------------------------------------------------- */ 8 | 9 | enum{ 10 | SPP_IDX_SVC, 11 | 12 | SPP_IDX_SPP_DATA_RECV_VAL, 13 | 14 | SPP_IDX_SPP_DATA_NTY_VAL, 15 | SPP_IDX_SPP_DATA_NTF_CFG, 16 | 17 | SPP_IDX_SPP_COMMAND_VAL, 18 | 19 | SPP_IDX_SPP_STATUS_VAL, 20 | SPP_IDX_SPP_STATUS_CFG, 21 | 22 | SPP_IDX_NB, 23 | }; 24 | 25 | /* -------------------------------------------------------------------------- */ 26 | 27 | void ble_client_register_callbacks( void ); 28 | 29 | /* -------------------------------------------------------------------------- */ 30 | 31 | void ble_client_register_user_evt_queue( QueueHandle_t *queue ); 32 | 33 | /* -------------------------------------------------------------------------- */ 34 | 35 | void ble_client_send_payload( uint8_t* buffer, uint16_t length ); 36 | 37 | /* -------------------------------------------------------------------------- */ 38 | 39 | -------------------------------------------------------------------------------- /firmware/esp32-ble/main/server.h: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | 3 | #include "spp_defs.h" 4 | #include "freertos/FreeRTOS.h" 5 | #include "freertos/semphr.h" 6 | 7 | /* -------------------------------------------------------------------------- */ 8 | 9 | enum{ 10 | SPP_IDX_SVC, 11 | 12 | SPP_IDX_SPP_DATA_RECV_CHAR, 13 | SPP_IDX_SPP_DATA_RECV_VAL, 14 | 15 | SPP_IDX_SPP_DATA_NOTIFY_CHAR, 16 | SPP_IDX_SPP_DATA_NTY_VAL, 17 | SPP_IDX_SPP_DATA_NTF_CFG, 18 | 19 | SPP_IDX_SPP_COMMAND_CHAR, 20 | SPP_IDX_SPP_COMMAND_VAL, 21 | 22 | SPP_IDX_SPP_STATUS_CHAR, 23 | SPP_IDX_SPP_STATUS_VAL, 24 | SPP_IDX_SPP_STATUS_CFG, 25 | 26 | SPP_IDX_NB, 27 | }; 28 | 29 | /* -------------------------------------------------------------------------- */ 30 | 31 | void ble_server_register_callbacks( void ); 32 | 33 | /* -------------------------------------------------------------------------- */ 34 | 35 | 36 | void ble_server_register_user_evt_queue( QueueHandle_t *queue ); 37 | 38 | /* -------------------------------------------------------------------------- */ 39 | 40 | void ble_server_send_payload( uint8_t* buffer, uint16_t length ); 41 | 42 | /* -------------------------------------------------------------------------- */ 43 | -------------------------------------------------------------------------------- /firmware/esp32-ble/main/spp_defs.h: -------------------------------------------------------------------------------- 1 | 2 | /* -------------------------------------------------------------------------- */ 3 | 4 | #define SPP_DATA_MAX_LEN (197) 5 | #define SPP_CMD_MAX_LEN (20) 6 | #define SPP_STATUS_MAX_LEN (20) 7 | #define SPP_DATA_BUFF_MAX_LEN (2*1024) 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | 12 | /* -------------------------------------------------------------------------- */ -------------------------------------------------------------------------------- /firmware/esp32-ble/sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # Add defaults here... 2 | CONFIG_BT_ENABLED=y 3 | CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n 4 | CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y 5 | # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. 6 | CONFIG_BT_LE_50_FEATURE_SUPPORT=n -------------------------------------------------------------------------------- /firmware/esp32-nimble/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *build*/ -------------------------------------------------------------------------------- /firmware/esp32-nimble/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(espble) -------------------------------------------------------------------------------- /firmware/esp32-nimble/L2CAP-fragment-behaviour.pcapng.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/esp32-nimble/L2CAP-fragment-behaviour.pcapng.gz -------------------------------------------------------------------------------- /firmware/esp32-nimble/L2CAP-fragment-fixed.pcapng.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/esp32-nimble/L2CAP-fragment-fixed.pcapng.gz -------------------------------------------------------------------------------- /firmware/esp32-nimble/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 NimBLE BLE Test Firmware 2 | 3 | Firmware and logs for ESP32 NimBLE BLE latency tests. 4 | 5 | IO Setup: 6 | 7 | - IO19 is driven with a 3.3V trigger pulse from my sig-gen. 8 | - IO18 is a 3.3V output signal 9 | 10 | ## Firwmare 11 | 12 | I run my development environment via docker 13 | 14 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v5.1` 15 | 16 | I used `idf.py menuconfig` to set: 17 | 18 | - the compiler optimisation level to 'performance' for o2. 19 | - NimBLE internal logging output verbosity to `Warning` in "Component Config -> Bluetooth -> NimBLE Options NimBLE Host log verbosity (Warning logs)" 20 | 21 | Use `idf.py build` and/or `idf.py -p /dev/ttyUSB0 flash` to build and flash to hardware. 22 | 23 | # References 24 | 25 | Espressif IDF [NimBLE Server/Client GATT/SPP example](https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/nimble/ble_spp) was a base that I needed to modify for IDF 5.1 and feature parity with other tests. 26 | -------------------------------------------------------------------------------- /firmware/esp32-nimble/dependencies.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | idf: 3 | component_hash: null 4 | source: 5 | type: idf 6 | version: 5.1.1 7 | nimble_central_utils: 8 | component_hash: null 9 | source: 10 | path: /opt/esp/idf/examples/bluetooth/nimble/common/nimble_central_utils 11 | type: local 12 | version: '*' 13 | manifest_hash: 794a2cec85e0255abac18143e81a3739195cbf99e41888b669d87b2a217cc088 14 | target: esp32 15 | version: 1.0.0 16 | -------------------------------------------------------------------------------- /firmware/esp32-nimble/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( SRCS 2 | "ble_main.c" 3 | "client.c" 4 | "server.c" 5 | INCLUDE_DIRS ".") -------------------------------------------------------------------------------- /firmware/esp32-nimble/main/Kconfig.projbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/esp32-nimble/main/Kconfig.projbuild -------------------------------------------------------------------------------- /firmware/esp32-nimble/main/ble_main_defs.h: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | 3 | typedef enum { 4 | SPP_SEND_CB, 5 | SPP_RECV_CB, 6 | } spp_event_id_t; 7 | 8 | typedef struct { 9 | uint32_t bytes_sent; 10 | bool congested; 11 | } spp_event_send_cb_t; 12 | 13 | typedef struct { 14 | uint8_t *data; 15 | uint32_t data_len; 16 | } spp_event_recv_cb_t; 17 | 18 | typedef union { 19 | spp_event_send_cb_t send_cb; 20 | spp_event_recv_cb_t recv_cb; 21 | } spp_event_data_t; 22 | 23 | // Main task queue needs to support send and receive events 24 | // The ID field helps distinguish between them 25 | typedef struct { 26 | spp_event_id_t id; 27 | spp_event_data_t data; 28 | } spp_event_t; 29 | 30 | /* -------------------------------------------------------------------------- */ 31 | -------------------------------------------------------------------------------- /firmware/esp32-nimble/main/client.h: -------------------------------------------------------------------------------- 1 | #ifndef H_BLESPPCLIENT_ 2 | #define H_BLESPPCLIENT_ 3 | 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | #include "freertos/FreeRTOS.h" 12 | #include "freertos/semphr.h" 13 | 14 | /* -------------------------------------------------------------------------- */ 15 | 16 | void ble_client_setup( void ); 17 | 18 | /* -------------------------------------------------------------------------- */ 19 | 20 | void ble_client_register_user_evt_queue( QueueHandle_t *queue ); 21 | 22 | /* -------------------------------------------------------------------------- */ 23 | 24 | void ble_client_send_payload( uint8_t* buffer, uint16_t length ); 25 | 26 | /* -------------------------------------------------------------------------- */ 27 | 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif -------------------------------------------------------------------------------- /firmware/esp32-nimble/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | nimble_central_utils: 3 | path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_central_utils -------------------------------------------------------------------------------- /firmware/esp32-nimble/main/server.h: -------------------------------------------------------------------------------- 1 | #ifndef H_BLESPPSERVER_ 2 | #define H_BLESPPSERVER_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* -------------------------------------------------------------------------- */ 9 | 10 | #include 11 | #include "freertos/FreeRTOS.h" 12 | #include "freertos/semphr.h" 13 | 14 | /* -------------------------------------------------------------------------- */ 15 | 16 | void ble_server_setup( void ); 17 | 18 | /* -------------------------------------------------------------------------- */ 19 | 20 | void ble_server_register_user_evt_queue( QueueHandle_t *queue ); 21 | 22 | /* -------------------------------------------------------------------------- */ 23 | 24 | void ble_server_send_payload( uint8_t* buffer, uint16_t length ); 25 | 26 | /* -------------------------------------------------------------------------- */ 27 | 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif -------------------------------------------------------------------------------- /firmware/esp32-nimble/sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # Add defaults here... 2 | CONFIG_BT_ENABLED=y 3 | CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y 4 | CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n 5 | CONFIG_BTDM_CTRL_MODE_BTDM=n 6 | CONFIG_BT_BLUEDROID_ENABLED=n 7 | CONFIG_BT_NIMBLE_ENABLED=y -------------------------------------------------------------------------------- /firmware/esp32-nimble/test-setup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/esp32-nimble/test-setup.jpg -------------------------------------------------------------------------------- /firmware/esp32-spp/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *build*/ -------------------------------------------------------------------------------- /firmware/esp32-spp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(espspp) -------------------------------------------------------------------------------- /firmware/esp32-spp/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 SPP Test Firmware 2 | 3 | Firmware and logs for ESP32 Bluedroid Classic SPP latency tests. 4 | 5 | - I've merged initiator and acceptor codebases into one program. Use the define at the top to choose. 6 | - This example only does legacy pairing with hard-coded pin-code of 1234. 7 | - I ran tests with builds set to -O2 (performance) via menuconfig. 8 | 9 | IO Setup: 10 | 11 | - IO19 is driven with a 3.3V trigger pulse from my sig-gen. 12 | - IO18 is a 3.3V output signal 13 | 14 | ## Firwmare 15 | 16 | I run my development environment via docker 17 | 18 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v5.1` 19 | 20 | I used `idf.py menuconfig` to set the compiler optimisation level to 'performance' for o3. 21 | 22 | Use `idf.py build` and/or `idf.py -p /dev/ttyUSB0 flash` to build and flash to hardware. 23 | 24 | ### Config 25 | 26 | Run `idf.py menuconfig` and check that SPP is enabled (included sdkconfig.defaults should do this): 27 | 28 | `Component config --> Bluetooth --> Bluedroid Options --> SPP` 29 | 30 | # References 31 | 32 | Espressif's [`bt_spp_initiator`](https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator) and acceptor example projects. -------------------------------------------------------------------------------- /firmware/esp32-spp/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "espspp_main.c" 2 | INCLUDE_DIRS ".") -------------------------------------------------------------------------------- /firmware/esp32-spp/main/Kconfig.projbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/esp32-spp/main/Kconfig.projbuild -------------------------------------------------------------------------------- /firmware/esp32-spp/sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # Override some defaults so BT stack is enabled 2 | CONFIG_BT_ENABLED=y 3 | CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n 4 | CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y 5 | CONFIG_BTDM_CTRL_MODE_BTDM=n 6 | CONFIG_BT_CLASSIC_ENABLED=y 7 | CONFIG_BT_SPP_ENABLED=y 8 | CONFIG_BT_BLE_ENABLED=n -------------------------------------------------------------------------------- /firmware/espnow/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *build*/ -------------------------------------------------------------------------------- /firmware/espnow/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(espnow_example) -------------------------------------------------------------------------------- /firmware/espnow/README.md: -------------------------------------------------------------------------------- 1 | # ESPNow 2 | 3 | Firmware and logs for ESP32 ESPNow latency tests. 4 | 5 | This is based on Espressif's example espnow project. I simplified it and made some changes: 6 | 7 | - a pair of boards can learn each other's mac addresses via one-time broadcast at startup 8 | - When the trigger pin condition is met _and_ a pair exists, blindly send the payload packet 9 | - If an inbound payload packet arrives, parse it and check data for validity as per uart firmware etc 10 | 11 | This firmware doesn't handle error cases particularly well, and isn't particularly refined. 12 | It's intended to work well enough in lab conditions (ideal) and little else. 13 | 14 | IO Setup: 15 | 16 | - IO19 is driven with a 3.3V trigger pulse from my sig-gen. 17 | - IO18 is a 3.3V output signal 18 | 19 | ## Firwmare 20 | 21 | Developed using CLion, and I run my IDF development environment via docker container: 22 | 23 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v4.4` 24 | -------------------------------------------------------------------------------- /firmware/espnow/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "espnow_example_main.c" 2 | INCLUDE_DIRS ".") -------------------------------------------------------------------------------- /firmware/espnow/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Example Configuration" 2 | 3 | config ESPNOW_PMK 4 | string "ESPNOW primary master key" 5 | default "pmk1234567890123" 6 | help 7 | ESPNOW primary master for the example to use. The length of ESPNOW primary master must be 16 bytes. 8 | 9 | config ESPNOW_LMK 10 | string "ESPNOW local master key" 11 | default "lmk1234567890123" 12 | help 13 | ESPNOW local master for the example to use. The length of ESPNOW local master must be 16 bytes. 14 | 15 | config ESPNOW_CHANNEL 16 | int "Channel" 17 | default 1 18 | range 0 14 19 | help 20 | The channel on which sending and receiving ESPNOW data. 21 | 22 | config ESPNOW_ENABLE_LONG_RANGE 23 | bool "Enable Long Range" 24 | default "n" 25 | help 26 | When enable long range, the PHY rate of ESP32 will be 512Kbps or 256Kbps 27 | 28 | config ESPNOW_ENABLE_POWER_SAVE 29 | bool "Enable ESPNOW Power Save" 30 | default "n" 31 | select ESP_WIFI_STA_DISCONNECTED_PM_ENABLE 32 | depends on ESPNOW_WIFI_MODE_STATION 33 | help 34 | With ESPNOW power save enabled, chip would be able to wakeup and sleep periodically 35 | Notice ESP_WIFI_STA_DISCONNECTED_PM_ENABLE is essential at Wi-Fi disconnected 36 | 37 | config ESPNOW_WAKE_WINDOW 38 | int "ESPNOW wake window, unit in millisecond" 39 | range 0 65535 40 | default 50 41 | depends on ESPNOW_ENABLE_POWER_SAVE 42 | help 43 | ESPNOW wake window 44 | 45 | config ESPNOW_WAKE_INTERVAL 46 | int "ESPNOW wake interval, unit in millisecond" 47 | range 1 65535 48 | default 100 49 | depends on ESPNOW_ENABLE_POWER_SAVE 50 | help 51 | ESPNOW wake interval 52 | 53 | endmenu -------------------------------------------------------------------------------- /firmware/gpio_tests/esp-idf/.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /firmware/gpio_tests/esp-idf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(esp_gpio) 7 | -------------------------------------------------------------------------------- /firmware/gpio_tests/esp-idf/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 GPIO Tests 2 | 3 | Meant to provide basic implementation for comparison against STM32 GPIO interrupt and LED toggle performance. 4 | 5 | ## Firwmare 6 | 7 | I run my development environment via docker 8 | 9 | `docker run -i --privileged --rm -v $PWD:/project -w /project -it espressif/idf:release-v5.1` 10 | 11 | I used `idf.py menuconfig` to set the compiler optimisation level to 'performance' for o3. 12 | 13 | Use `idf.py build` and/or `idf.py -p /dev/ttyUSB0 flash` to build and flash to hardware. 14 | 15 | -------------------------------------------------------------------------------- /firmware/gpio_tests/esp-idf/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "esp_gpio_main.c" 2 | INCLUDE_DIRS ".") -------------------------------------------------------------------------------- /firmware/gpio_tests/esp-idf/main/esp_gpio_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | #include "driver/gpio.h" 8 | 9 | static const char *TAG = "esp_gpio_test"; 10 | 11 | // Test stimulus input pin 12 | #define GPIO_INPUT_IO_0 19 13 | #define GPIO_INPUT_PIN_SEL ((1ULL<FLASH 30 | 31 | /* The program code and other data goes into FLASH */ 32 | .text : 33 | { 34 | . = ALIGN(4); 35 | *(.text) /* .text sections (code) */ 36 | *(.text*) /* .text* sections (code) */ 37 | *(.glue_7) /* glue arm to thumb code */ 38 | *(.glue_7t) /* glue thumb to arm code */ 39 | *(.eh_frame) 40 | 41 | KEEP (*(.init)) 42 | KEEP (*(.fini)) 43 | 44 | . = ALIGN(4); 45 | _etext = .; /* define a global symbols at end of code */ 46 | } >FLASH 47 | 48 | /* Constant data goes into FLASH */ 49 | .rodata : 50 | { 51 | . = ALIGN(4); 52 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 53 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 54 | . = ALIGN(4); 55 | } >FLASH 56 | 57 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH 58 | .ARM : { 59 | __exidx_start = .; 60 | *(.ARM.exidx*) 61 | __exidx_end = .; 62 | } >FLASH 63 | 64 | .preinit_array : 65 | { 66 | PROVIDE_HIDDEN (__preinit_array_start = .); 67 | KEEP (*(.preinit_array*)) 68 | PROVIDE_HIDDEN (__preinit_array_end = .); 69 | } >FLASH 70 | .init_array : 71 | { 72 | PROVIDE_HIDDEN (__init_array_start = .); 73 | KEEP (*(SORT(.init_array.*))) 74 | KEEP (*(.init_array*)) 75 | PROVIDE_HIDDEN (__init_array_end = .); 76 | } >FLASH 77 | .fini_array : 78 | { 79 | PROVIDE_HIDDEN (__fini_array_start = .); 80 | KEEP (*(SORT(.fini_array.*))) 81 | KEEP (*(.fini_array*)) 82 | PROVIDE_HIDDEN (__fini_array_end = .); 83 | } >FLASH 84 | 85 | /* used by the startup to initialize data */ 86 | _sidata = LOADADDR(.data); 87 | 88 | /* Initialized data sections goes into RAM, load LMA copy after code */ 89 | .data : 90 | { 91 | . = ALIGN(4); 92 | _sdata = .; /* create a global symbol at data start */ 93 | *(.data) /* .data sections */ 94 | *(.data*) /* .data* sections */ 95 | 96 | . = ALIGN(4); 97 | _edata = .; /* define a global symbol at data end */ 98 | } >RAM AT> FLASH 99 | 100 | _siccmram = LOADADDR(.ccmram); 101 | 102 | /* CCM-RAM section 103 | * 104 | * IMPORTANT NOTE! 105 | * If initialized variables will be placed in this section, 106 | * the startup code needs to be modified to copy the init-values. 107 | */ 108 | .ccmram (NOLOAD): 109 | { 110 | . = ALIGN(4); 111 | _sccmram = .; /* create a global symbol at ccmram start */ 112 | *(.ccmram) 113 | *(.ccmram*) 114 | 115 | . = ALIGN(4); 116 | _eccmram = .; /* create a global symbol at ccmram end */ 117 | } >CCMRAM AT> FLASH 118 | 119 | 120 | /* Uninitialized data section */ 121 | . = ALIGN(4); 122 | .bss : 123 | { 124 | /* This is used by the startup in order to initialize the .bss secion */ 125 | _sbss = .; /* define a global symbol at bss start */ 126 | __bss_start__ = _sbss; 127 | *(.bss) 128 | *(.bss*) 129 | *(COMMON) 130 | 131 | . = ALIGN(4); 132 | _ebss = .; /* define a global symbol at bss end */ 133 | __bss_end__ = _ebss; 134 | } >RAM 135 | 136 | /* User_heap_stack section, used to check that there is enough RAM left */ 137 | ._user_heap_stack : 138 | { 139 | . = ALIGN(8); 140 | PROVIDE ( end = . ); 141 | PROVIDE ( _end = . ); 142 | . = . + _Min_Heap_Size; 143 | . = . + _Min_Stack_Size; 144 | . = ALIGN(8); 145 | } >RAM 146 | 147 | 148 | 149 | /* Remove information from the standard libraries */ 150 | /DISCARD/ : 151 | { 152 | libc.a ( * ) 153 | libm.a ( * ) 154 | libgcc.a ( * ) 155 | } 156 | 157 | .ARM.attributes 0 : { *(.ARM.attributes) } 158 | } -------------------------------------------------------------------------------- /firmware/gpio_tests/stm-ll/F429ZITx.ld: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | ** 4 | ** @file : LinkerScript.ld 5 | ** 6 | ** @author : Auto-generated by STM32CubeIDE 7 | ** 8 | ** Abstract : Linker script for NUCLEO-F429ZI Board embedding STM32F429ZITx Device from stm32f4 series 9 | ** 2048Kbytes FLASH 10 | ** 64Kbytes CCMRAM 11 | ** 192Kbytes RAM 12 | ** 13 | ** Set heap size, stack size and stack location according 14 | ** to application requirements. 15 | ** 16 | ** Set memory bank area and size if external memory is used 17 | ** 18 | ** Target : STMicroelectronics STM32 19 | ** 20 | ** Distribution: The file is distributed as is, without any warranty 21 | ** of any kind. 22 | ** 23 | ****************************************************************************** 24 | ** @attention 25 | ** 26 | ** Copyright (c) 2022 STMicroelectronics. 27 | ** All rights reserved. 28 | ** 29 | ** This software is licensed under terms that can be found in the LICENSE file 30 | ** in the root directory of this software component. 31 | ** If no LICENSE file comes with this software, it is provided AS-IS. 32 | ** 33 | ****************************************************************************** 34 | */ 35 | 36 | /* Entry Point */ 37 | ENTRY(Reset_Handler) 38 | 39 | /* Highest address of the user mode stack */ 40 | _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ 41 | 42 | _Min_Heap_Size = 0x200 ; /* required amount of heap */ 43 | _Min_Stack_Size = 0x400 ; /* required amount of stack */ 44 | 45 | /* Memories definition */ 46 | MEMORY 47 | { 48 | CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K 49 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 50 | FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K 51 | } 52 | 53 | /* Sections */ 54 | SECTIONS 55 | { 56 | /* The startup code into "FLASH" Rom type memory */ 57 | .isr_vector : 58 | { 59 | . = ALIGN(4); 60 | KEEP(*(.isr_vector)) /* Startup code */ 61 | . = ALIGN(4); 62 | } >FLASH 63 | 64 | /* The program code and other data into "FLASH" Rom type memory */ 65 | .text : 66 | { 67 | . = ALIGN(4); 68 | *(.text) /* .text sections (code) */ 69 | *(.text*) /* .text* sections (code) */ 70 | *(.glue_7) /* glue arm to thumb code */ 71 | *(.glue_7t) /* glue thumb to arm code */ 72 | *(.eh_frame) 73 | 74 | KEEP (*(.init)) 75 | KEEP (*(.fini)) 76 | 77 | . = ALIGN(4); 78 | _etext = .; /* define a global symbols at end of code */ 79 | } >FLASH 80 | 81 | /* Constant data into "FLASH" Rom type memory */ 82 | .rodata : 83 | { 84 | . = ALIGN(4); 85 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 86 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 87 | . = ALIGN(4); 88 | } >FLASH 89 | 90 | .ARM.extab : { 91 | . = ALIGN(4); 92 | *(.ARM.extab* .gnu.linkonce.armextab.*) 93 | . = ALIGN(4); 94 | } >FLASH 95 | 96 | .ARM : { 97 | . = ALIGN(4); 98 | __exidx_start = .; 99 | *(.ARM.exidx*) 100 | __exidx_end = .; 101 | . = ALIGN(4); 102 | } >FLASH 103 | 104 | .preinit_array : 105 | { 106 | . = ALIGN(4); 107 | PROVIDE_HIDDEN (__preinit_array_start = .); 108 | KEEP (*(.preinit_array*)) 109 | PROVIDE_HIDDEN (__preinit_array_end = .); 110 | . = ALIGN(4); 111 | } >FLASH 112 | 113 | .init_array : 114 | { 115 | . = ALIGN(4); 116 | PROVIDE_HIDDEN (__init_array_start = .); 117 | KEEP (*(SORT(.init_array.*))) 118 | KEEP (*(.init_array*)) 119 | PROVIDE_HIDDEN (__init_array_end = .); 120 | . = ALIGN(4); 121 | } >FLASH 122 | 123 | .fini_array : 124 | { 125 | . = ALIGN(4); 126 | PROVIDE_HIDDEN (__fini_array_start = .); 127 | KEEP (*(SORT(.fini_array.*))) 128 | KEEP (*(.fini_array*)) 129 | PROVIDE_HIDDEN (__fini_array_end = .); 130 | . = ALIGN(4); 131 | } >FLASH 132 | 133 | /* Used by the startup to initialize data */ 134 | _sidata = LOADADDR(.data); 135 | 136 | /* Initialized data sections into "RAM" Ram type memory */ 137 | .data : 138 | { 139 | . = ALIGN(4); 140 | _sdata = .; /* create a global symbol at data start */ 141 | *(.data) /* .data sections */ 142 | *(.data*) /* .data* sections */ 143 | *(.RamFunc) /* .RamFunc sections */ 144 | *(.RamFunc*) /* .RamFunc* sections */ 145 | 146 | . = ALIGN(4); 147 | _edata = .; /* define a global symbol at data end */ 148 | 149 | } >RAM AT> FLASH 150 | 151 | _siccmram = LOADADDR(.ccmram); 152 | 153 | /* CCM-RAM section 154 | * 155 | * IMPORTANT NOTE! 156 | * If initialized variables will be placed in this section, 157 | * the startup code needs to be modified to copy the init-values. 158 | */ 159 | .ccmram : 160 | { 161 | . = ALIGN(4); 162 | _sccmram = .; /* create a global symbol at ccmram start */ 163 | *(.ccmram) 164 | *(.ccmram*) 165 | 166 | . = ALIGN(4); 167 | _eccmram = .; /* create a global symbol at ccmram end */ 168 | } >CCMRAM AT> FLASH 169 | 170 | /* Uninitialized data section into "RAM" Ram type memory */ 171 | . = ALIGN(4); 172 | .bss : 173 | { 174 | /* This is used by the startup in order to initialize the .bss section */ 175 | _sbss = .; /* define a global symbol at bss start */ 176 | __bss_start__ = _sbss; 177 | *(.bss) 178 | *(.bss*) 179 | *(COMMON) 180 | 181 | . = ALIGN(4); 182 | _ebss = .; /* define a global symbol at bss end */ 183 | __bss_end__ = _ebss; 184 | } >RAM 185 | 186 | /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ 187 | ._user_heap_stack : 188 | { 189 | . = ALIGN(8); 190 | PROVIDE ( end = . ); 191 | PROVIDE ( _end = . ); 192 | . = . + _Min_Heap_Size; 193 | . = . + _Min_Stack_Size; 194 | . = ALIGN(8); 195 | } >RAM 196 | 197 | /* Remove information from the compiler libraries */ 198 | /DISCARD/ : 199 | { 200 | libc.a ( * ) 201 | libm.a ( * ) 202 | libgcc.a ( * ) 203 | } 204 | 205 | .ARM.attributes 0 : { *(.ARM.attributes) } 206 | } 207 | -------------------------------------------------------------------------------- /firmware/gpio_tests/stm-ll/README.md: -------------------------------------------------------------------------------- 1 | # STM32F429 LL EXTI Timing Test 2 | 3 | This is a super-minimal test program that waits for the trigger signal via EXTI interrupt and then sets an IO pin high. 4 | 5 | This is for comparison against Arduino and STCubeMX HAL implementation. 6 | 7 | - PA0 is driven with a 3.3V trigger pulse from my sig-gen. 8 | - PB0 is a 3.3V output signal (also connected to the nucleo's onboard green LED) 9 | 10 | ## Deps 11 | 12 | I use CMake with CLion for development/builds, so this project is slightly opinionated. 13 | 14 | Requires [`stm32-cmake`](https://github.com/ObKo/stm32-cmake) in this folder to get the build system setup etc. 15 | 16 | I set my CMake options to use my system's arm gcc toolchain `-DSTM32_TOOLCHAIN_PATH=/usr/bin/ -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/arm-none-eabi-g++ -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/arm-none-eabi-gcc`. 17 | 18 | With CLion's build settings, I set the build generator to "Let CMake Decide". -------------------------------------------------------------------------------- /firmware/gpio_tests/stm-ll/src/main.c: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "stm32f4xx_ll_cortex.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "stm32f4xx_ll_gpio.h" 15 | #include "stm32f4xx_ll_exti.h" 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | 19 | void hal_core_init( void ); 20 | void hal_core_clock_configure( void ); 21 | void portAssertHandler( const char *file, 22 | unsigned line, 23 | const char *fmt, 24 | ... ); 25 | 26 | /* -------------------------------------------------------------------------- */ 27 | 28 | void setup_gpio_output( void ); 29 | void setup_gpio_input( void ); 30 | 31 | volatile bool trigger_pending = false; 32 | 33 | /* -------------------------------------------------------------------------- */ 34 | 35 | int main(void) 36 | { 37 | hal_core_init(); 38 | hal_core_clock_configure(); 39 | 40 | setup_gpio_output(); 41 | setup_gpio_input(); 42 | 43 | while(1) 44 | { 45 | 46 | if(trigger_pending) 47 | { 48 | // GPIO high 49 | LL_GPIO_SetOutputPin( GPIOB, LL_GPIO_PIN_0); 50 | trigger_pending = false; 51 | } 52 | else 53 | { 54 | // GPIO low 55 | LL_GPIO_ResetOutputPin( GPIOB, LL_GPIO_PIN_0 ); 56 | } 57 | } 58 | 59 | return 0; 60 | } 61 | 62 | /* -------------------------------------------------------------------------- */ 63 | 64 | void setup_gpio_output( void ) 65 | { 66 | LL_PWR_DisableWakeUpPin( LL_PWR_WAKEUP_PIN1 ); 67 | 68 | // PB0 69 | LL_AHB1_GRP1_EnableClock( LL_AHB1_GRP1_PERIPH_GPIOB ); 70 | 71 | LL_GPIO_SetPinMode( GPIOB, LL_GPIO_PIN_0, LL_GPIO_MODE_OUTPUT ); 72 | LL_GPIO_SetPinSpeed( GPIOB, LL_GPIO_PIN_0, LL_GPIO_SPEED_FREQ_LOW ); 73 | LL_GPIO_SetPinOutputType( GPIOB, LL_GPIO_PIN_0, LL_GPIO_OUTPUT_PUSHPULL ); 74 | LL_GPIO_SetPinPull( GPIOB, LL_GPIO_PIN_0, LL_GPIO_PULL_NO ); 75 | LL_GPIO_ResetOutputPin( GPIOB, LL_GPIO_PIN_0 ); 76 | } 77 | 78 | /* -------------------------------------------------------------------------- */ 79 | 80 | void setup_gpio_input( void ) 81 | { 82 | // PA0 as input 83 | LL_AHB1_GRP1_EnableClock( LL_AHB1_GRP1_PERIPH_GPIOA ); 84 | 85 | LL_GPIO_SetPinMode( GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT ); 86 | LL_GPIO_SetPinSpeed( GPIOA, LL_GPIO_PIN_0, LL_GPIO_SPEED_FREQ_LOW ); 87 | LL_GPIO_SetPinOutputType( GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT ); 88 | LL_GPIO_SetPinPull( GPIOA, LL_GPIO_PIN_0, LL_GPIO_PULL_NO ); 89 | LL_GPIO_ResetOutputPin( GPIOA, LL_GPIO_PIN_0 ); 90 | 91 | // EXTI0 setup 92 | LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_0); 93 | LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_0); 94 | 95 | LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0); 96 | 97 | // IRQ config 98 | NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority( 99 | NVIC_GetPriorityGrouping(), 100 | 0, 101 | 0 102 | )); 103 | NVIC_EnableIRQ(EXTI0_IRQn); 104 | 105 | } 106 | 107 | /* -------------------------------------------------------------------------- */ 108 | 109 | void SysTick_Handler(void) 110 | { 111 | // trigger_pending = !trigger_pending; 112 | } 113 | 114 | void EXTI0_IRQHandler(void) 115 | { 116 | if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)) 117 | { 118 | LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); 119 | trigger_pending = true; 120 | } 121 | } 122 | 123 | 124 | /* -------------------------------------------------------------------------- */ 125 | 126 | void hal_core_init( void ) 127 | { 128 | LL_FLASH_EnableInstCache(); 129 | LL_FLASH_EnableDataCache(); 130 | LL_FLASH_EnablePrefetch(); 131 | 132 | LL_APB2_GRP1_EnableClock( LL_APB2_GRP1_PERIPH_SYSCFG ); 133 | LL_APB1_GRP1_EnableClock( LL_APB1_GRP1_PERIPH_PWR ); 134 | 135 | NVIC_SetPriority( MemoryManagement_IRQn, NVIC_EncodePriority( NVIC_GetPriorityGrouping(), 0, 0 ) ); 136 | NVIC_SetPriority( BusFault_IRQn, NVIC_EncodePriority( NVIC_GetPriorityGrouping(), 0, 0 ) ); 137 | NVIC_SetPriority( UsageFault_IRQn, NVIC_EncodePriority( NVIC_GetPriorityGrouping(), 0, 0 ) ); 138 | NVIC_SetPriority( SVCall_IRQn, NVIC_EncodePriority( NVIC_GetPriorityGrouping(), 0, 0 ) ); 139 | NVIC_SetPriority( DebugMonitor_IRQn, NVIC_EncodePriority( NVIC_GetPriorityGrouping(), 0, 0 ) ); 140 | NVIC_SetPriority( PendSV_IRQn, NVIC_EncodePriority( NVIC_GetPriorityGrouping(), 0, 0 ) ); 141 | NVIC_SetPriority( SysTick_IRQn, NVIC_EncodePriority( NVIC_GetPriorityGrouping(), 0, 0 ) ); 142 | } 143 | 144 | 145 | // Startup the internal and external clocks, set PLL etc 146 | void hal_core_clock_configure( void ) 147 | { 148 | LL_FLASH_SetLatency( LL_FLASH_LATENCY_5 ); 149 | 150 | if( LL_FLASH_GetLatency() != LL_FLASH_LATENCY_5 ) 151 | { 152 | portAssertHandler("main.c", __LINE__, 0); 153 | } 154 | 155 | LL_PWR_SetRegulVoltageScaling( LL_PWR_REGU_VOLTAGE_SCALE1 ); 156 | LL_PWR_DisableOverDriveMode(); 157 | LL_RCC_HSE_EnableBypass(); 158 | 159 | LL_RCC_HSE_Enable(); 160 | while( LL_RCC_HSE_IsReady() != 1 ) 161 | { 162 | } 163 | 164 | // LL_RCC_LSI_Enable(); 165 | // while( LL_RCC_LSI_IsReady() != 1 ) 166 | // { 167 | // } 168 | 169 | LL_RCC_PLL_ConfigDomain_SYS( LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_4, 168, LL_RCC_PLLP_DIV_2 ); 170 | LL_RCC_PLL_ConfigDomain_48M( LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_4, 168, LL_RCC_PLLQ_DIV_7 ); 171 | LL_RCC_PLL_Enable(); 172 | while( LL_RCC_PLL_IsReady() != 1 ) 173 | { 174 | } 175 | while( LL_PWR_IsActiveFlag_VOS() == 0 ) 176 | { 177 | } 178 | 179 | LL_RCC_SetAHBPrescaler( LL_RCC_SYSCLK_DIV_1 ); 180 | LL_RCC_SetAPB1Prescaler( LL_RCC_APB1_DIV_4 ); 181 | LL_RCC_SetAPB2Prescaler( LL_RCC_APB2_DIV_2 ); 182 | 183 | LL_RCC_SetSysClkSource( LL_RCC_SYS_CLKSOURCE_PLL ); 184 | while( LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL ) 185 | { 186 | } 187 | 188 | LL_Init1msTick( 168000000 ); 189 | LL_SetSystemCoreClock( 168000000 ); 190 | // LL_RCC_SetTIMPrescaler( LL_RCC_TIM_PRESCALER_TWICE ); 191 | 192 | LL_SYSTICK_EnableIT(); 193 | } 194 | 195 | void portAssertHandler( const char *file, 196 | unsigned line, 197 | const char *fmt, 198 | ... ) 199 | { 200 | va_list args; 201 | 202 | // Forward directly to the 'in-memory cache' handler function 203 | va_start( args, fmt ); 204 | // Read/handle file/line strings here 205 | va_end( args ); 206 | 207 | // Wait for the watch dog to bite 208 | for( ;; ) 209 | { 210 | asm("NOP"); 211 | } 212 | 213 | } 214 | 215 | /* -------------------------------------------------------------------------- */ -------------------------------------------------------------------------------- /firmware/gpio_tests/stm32-ll-asm-debug.txt: -------------------------------------------------------------------------------- 1 | Debug - O0g 2 | 3 | main: 4 | push {r7, lr} 5 | add r7, sp, ; 0 6 | bl 0x8000af4 ; 7 | bl 0x8000bdc ; 8 | bl 0x80009fc ; 9 | bl 0x8000a48 ; 10 | ldr r3, [pc, ; 32] @ (0x80009f4 ) 11 | ldrb r3, [r3, ; 0] 12 | uxtb r3, r3 13 | cmp r3, ; 0 14 | beq.n 0x80009ea ; 15 | movs r1, ; 1 16 | ldr r0, [pc, ; 24] @ (0x80009f8 ) 17 | bl 0x80008ee ; 18 | ldr r3, [pc, ; 16] @ (0x80009f4 ) 19 | movs r2, ; 0 20 | strb r2, [r3, ; 0] 21 | b.n 0x80009d0 ; 22 | movs r1, ; 1 23 | ldr r0, [pc, ; 8] @ (0x80009f8 ) 24 | bl 0x800090a ; 25 | b.n 0x80009d0 ; 26 | movs r4, r4 27 | movs r0, ; 0 28 | lsls r0, r0, ; 16 29 | ands r2, r0 -------------------------------------------------------------------------------- /firmware/gpio_tests/stm32-ll-asm-release.txt: -------------------------------------------------------------------------------- 1 | Release - O3 2 | 3 | main: 4 | push {r3, lr} 5 | bl 0x800039c ; 6 | bl 0x8000428 ; 7 | bl 0x80001ec ; 8 | bl 0x80002a4 ; 9 | ldr r2, [pc, ; 28] @ (0x8000554 ) 10 | ldr r1, [pc, ; 28] @ (0x8000558 ) 11 | mov.w r5, ; 65536 @ 0x10000 12 | movs r4, ; 1 13 | movs r0, ; 0 14 | ldrb r3, [r2, ; 0] 15 | cbz r3, 0x8000550 ; 16 | str r4, [r1, ; 24] 17 | strb r0, [r2, ; 0] 18 | ldrb r3, [r2, ; 0] 19 | cmp r3, ; 0 20 | bne.n 0x8000546 ; 21 | str r5, [r1, ; 24] 22 | b.n 0x8000542 ; 23 | movs r4, r4 24 | movs r0, ; 0 25 | lsls r0, r0, ; 16 26 | ands r2, r0 27 | -------------------------------------------------------------------------------- /firmware/gpio_tests/stm32-ll-asm-small.txt: -------------------------------------------------------------------------------- 1 | MinSizeRel - Os 2 | 3 | main: 4 | push {r3, lr} 5 | bl 0x800036c ; 6 | bl 0x8000434 ; 7 | bl 0x8000274 ; 8 | bl 0x80002c4 ; 9 | ldr r2, [pc, ; 24] @ (0x8000560 ) 10 | ldr r3, [pc, ; 24] @ (0x8000564 ) 11 | mov.w r1, ; 65536 @ 0x10000 12 | movs r0, ; 1 13 | movs r4, ; 0 14 | ldrb r5, [r2, ; 0] 15 | cbz r5, 0x800055c ; 16 | str r0, [r3, ; 24] 17 | strb r4, [r2, ; 0] 18 | b.n 0x8000552 ; 19 | str r1, [r3, ; 24] 20 | b.n 0x8000552 ; 21 | movs r4, r4 22 | movs r0, ; 0 23 | lsls r0, r0, ; 16 24 | ands r2, r0 25 | -------------------------------------------------------------------------------- /firmware/gpio_tests/stm32duino-s0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/gpio_tests/stm32duino-s0.png -------------------------------------------------------------------------------- /firmware/gpio_tests/stm32duino/stm32duino.ino: -------------------------------------------------------------------------------- 1 | bool trigger_pending = false; 2 | 3 | void setup( void ) 4 | { 5 | // Output LED 6 | pinMode(PB0, OUTPUT); 7 | 8 | // Input IO 9 | attachInterrupt( digitalPinToInterrupt(PA0), 10 | externalTrigger, 11 | RISING ); 12 | } 13 | 14 | void externalTrigger( void ) 15 | { 16 | trigger_pending = true; 17 | } 18 | 19 | void loop( void ) 20 | { 21 | 22 | if( trigger_pending ) 23 | { 24 | digitalWrite(PB0, HIGH); 25 | trigger_pending = false; 26 | } 27 | else 28 | { 29 | digitalWrite(PB0, LOW); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /firmware/hc-05/460800-1024B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/hc-05/460800-1024B.sal -------------------------------------------------------------------------------- /firmware/hc-05/460800-128B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/hc-05/460800-128B.sal -------------------------------------------------------------------------------- /firmware/hc-05/57600-128B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/hc-05/57600-128B.sal -------------------------------------------------------------------------------- /firmware/hc-05/57600-12B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/hc-05/57600-12B.sal -------------------------------------------------------------------------------- /firmware/hc-05/AT-commands-list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/hc-05/AT-commands-list.jpg -------------------------------------------------------------------------------- /firmware/hc-05/README.md: -------------------------------------------------------------------------------- 1 | # HC-05 Tests 2 | 3 | The firmware used for these tests was the same as the UART test firmware. 4 | 5 | The HC-05 runs at 38400 baud for AT commands, and at 9600 for data transfer by default 6 | 7 | My modules report firmware version `2.0-20100601` 8 | 9 | ## AT Configuration 10 | 11 | Connected via UART-TTL adapter at 38400 while holding EN button during power up. 12 | 13 | CR+LF on enter should be configured in the serial terminal. 14 | 15 | Check the native baud rate: 16 | 17 | AT+UART? 18 | 19 | Find the slave address: 20 | 21 | AT+ROLE? should return role 0 22 | AT+ADDR? should retrun an address 98d3:b1:fe609a 23 | 24 | Configure the master to bind with the slave: 25 | 26 | AT+ROLE=1 27 | AT+CMODE=0 28 | AT+BIND=98d3,b1,f3609a 29 | 30 | 31 | # References 32 | 33 | [Martyn Currey](https://www.martyncurrey.com/arduino-with-hc-05-bluetooth-module-at-mode/) wrote an accurate and useful blog post on AT configuration of these modules. 34 | -------------------------------------------------------------------------------- /firmware/nrf24/128B_via_12B_packet_sequence.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/nrf24/128B_via_12B_packet_sequence.sal -------------------------------------------------------------------------------- /firmware/nrf24/stm-ll/.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build* 2 | stm32-cmake/* -------------------------------------------------------------------------------- /firmware/nrf24/stm-ll/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | set(PROJ_NAME nrf24-test) 3 | set(STM32_CMAKE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/stm32-cmake) 4 | set(CMAKE_TOOLCHAIN_FILE ${STM32_CMAKE_PATH}/cmake/stm32_gcc.cmake) 5 | 6 | #SET(CMAKE_CXX_FLAGS "-O1") 7 | #SET(CMAKE_C_FLAGS "-O1") 8 | 9 | project(${PROJ_NAME} C ASM) 10 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 11 | 12 | stm32_fetch_cmsis(F4) 13 | stm32_fetch_hal(F4) 14 | 15 | find_package(CMSIS COMPONENTS STM32F4 REQUIRED) 16 | find_package(HAL 17 | COMPONENTS 18 | STM32F4 19 | REQUIRED) 20 | 21 | add_definitions(-DUSE_FULL_LL_DRIVER) 22 | 23 | # Clock speed definitions for custom hardware 24 | add_definitions(-DHSE_VALUE=8000000) 25 | add_definitions(-DLSE_VALUE=32768) 26 | 27 | add_executable(${PROJ_NAME}) 28 | 29 | target_sources( 30 | ${PROJ_NAME} 31 | PRIVATE 32 | ${CMAKE_SOURCE_DIR}/src/main.c 33 | ${CMAKE_SOURCE_DIR}/libs/nrf24.c 34 | ) 35 | 36 | target_include_directories( 37 | ${PROJ_NAME} 38 | PUBLIC 39 | ${CMAKE_CURRENT_SOURCE_DIR} 40 | ${CMAKE_CURRENT_SOURCE_DIR}/src 41 | ${CMAKE_CURRENT_SOURCE_DIR}/libs/ 42 | ) 43 | 44 | target_link_libraries( 45 | ${PROJ_NAME} 46 | PRIVATE 47 | HAL::STM32::F4::LL_RCC 48 | HAL::STM32::F4::LL_PWR 49 | HAL::STM32::F4::LL_UTILS 50 | HAL::STM32::F4::LL_GPIO 51 | HAL::STM32::F4::LL_EXTI 52 | HAL::STM32::F4::LL_SPI 53 | HAL::STM32::F4::LL_DMA 54 | CMSIS::STM32::F429xx 55 | STM32::NoSys 56 | ) 57 | 58 | target_link_options(${PROJ_NAME} PRIVATE -Wl,--print-memory-usage) 59 | stm32_add_linker_script(${PROJ_NAME} PRIVATE F429ZITx.ld) 60 | 61 | stm32_generate_binary_file(${PROJ_NAME}) 62 | stm32_generate_hex_file(${PROJ_NAME}) -------------------------------------------------------------------------------- /firmware/nrf24/stm-ll/F429VETx.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler) 2 | 3 | /* Highest address of the user mode stack */ 4 | _estack = 0x20000000 + 192K; /* end of RAM */ 5 | _Min_Heap_Size = 0x200; 6 | _Min_Stack_Size = 0x400; 7 | 8 | /* Specify the memory areas */ 9 | MEMORY 10 | { 11 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K 12 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 13 | CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K 14 | } 15 | 16 | /* See page 78 of ST doc RM0090 for single bank flash memory layout. */ 17 | /* Layout in sequential sectors is as follows: 16k, 16k, 16k, 16k, 64k, 128k, 128k, 128k */ 18 | /* FLASH_BOOT (rx) : ORIGIN = 0x08000000, LENGTH = 32K */ 19 | /* EMULATED_EEPROM (rwx) : ORIGIN = 0x08008000, LENGTH = 32K */ 20 | 21 | SECTIONS 22 | { 23 | /* The startup code goes first into FLASH */ 24 | .isr_vector : 25 | { 26 | . = ALIGN(4); 27 | KEEP(*(.isr_vector)) /* Startup code */ 28 | . = ALIGN(4); 29 | } >FLASH 30 | 31 | /* The program code and other data goes into FLASH */ 32 | .text : 33 | { 34 | . = ALIGN(4); 35 | *(.text) /* .text sections (code) */ 36 | *(.text*) /* .text* sections (code) */ 37 | *(.glue_7) /* glue arm to thumb code */ 38 | *(.glue_7t) /* glue thumb to arm code */ 39 | *(.eh_frame) 40 | 41 | KEEP (*(.init)) 42 | KEEP (*(.fini)) 43 | 44 | . = ALIGN(4); 45 | _etext = .; /* define a global symbols at end of code */ 46 | } >FLASH 47 | 48 | /* Constant data goes into FLASH */ 49 | .rodata : 50 | { 51 | . = ALIGN(4); 52 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 53 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 54 | . = ALIGN(4); 55 | } >FLASH 56 | 57 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH 58 | .ARM : { 59 | __exidx_start = .; 60 | *(.ARM.exidx*) 61 | __exidx_end = .; 62 | } >FLASH 63 | 64 | .preinit_array : 65 | { 66 | PROVIDE_HIDDEN (__preinit_array_start = .); 67 | KEEP (*(.preinit_array*)) 68 | PROVIDE_HIDDEN (__preinit_array_end = .); 69 | } >FLASH 70 | .init_array : 71 | { 72 | PROVIDE_HIDDEN (__init_array_start = .); 73 | KEEP (*(SORT(.init_array.*))) 74 | KEEP (*(.init_array*)) 75 | PROVIDE_HIDDEN (__init_array_end = .); 76 | } >FLASH 77 | .fini_array : 78 | { 79 | PROVIDE_HIDDEN (__fini_array_start = .); 80 | KEEP (*(SORT(.fini_array.*))) 81 | KEEP (*(.fini_array*)) 82 | PROVIDE_HIDDEN (__fini_array_end = .); 83 | } >FLASH 84 | 85 | /* used by the startup to initialize data */ 86 | _sidata = LOADADDR(.data); 87 | 88 | /* Initialized data sections goes into RAM, load LMA copy after code */ 89 | .data : 90 | { 91 | . = ALIGN(4); 92 | _sdata = .; /* create a global symbol at data start */ 93 | *(.data) /* .data sections */ 94 | *(.data*) /* .data* sections */ 95 | 96 | . = ALIGN(4); 97 | _edata = .; /* define a global symbol at data end */ 98 | } >RAM AT> FLASH 99 | 100 | _siccmram = LOADADDR(.ccmram); 101 | 102 | /* CCM-RAM section 103 | * 104 | * IMPORTANT NOTE! 105 | * If initialized variables will be placed in this section, 106 | * the startup code needs to be modified to copy the init-values. 107 | */ 108 | .ccmram (NOLOAD): 109 | { 110 | . = ALIGN(4); 111 | _sccmram = .; /* create a global symbol at ccmram start */ 112 | *(.ccmram) 113 | *(.ccmram*) 114 | 115 | . = ALIGN(4); 116 | _eccmram = .; /* create a global symbol at ccmram end */ 117 | } >CCMRAM AT> FLASH 118 | 119 | 120 | /* Uninitialized data section */ 121 | . = ALIGN(4); 122 | .bss : 123 | { 124 | /* This is used by the startup in order to initialize the .bss secion */ 125 | _sbss = .; /* define a global symbol at bss start */ 126 | __bss_start__ = _sbss; 127 | *(.bss) 128 | *(.bss*) 129 | *(COMMON) 130 | 131 | . = ALIGN(4); 132 | _ebss = .; /* define a global symbol at bss end */ 133 | __bss_end__ = _ebss; 134 | } >RAM 135 | 136 | /* User_heap_stack section, used to check that there is enough RAM left */ 137 | ._user_heap_stack : 138 | { 139 | . = ALIGN(8); 140 | PROVIDE ( end = . ); 141 | PROVIDE ( _end = . ); 142 | . = . + _Min_Heap_Size; 143 | . = . + _Min_Stack_Size; 144 | . = ALIGN(8); 145 | } >RAM 146 | 147 | 148 | 149 | /* Remove information from the standard libraries */ 150 | /DISCARD/ : 151 | { 152 | libc.a ( * ) 153 | libm.a ( * ) 154 | libgcc.a ( * ) 155 | } 156 | 157 | .ARM.attributes 0 : { *(.ARM.attributes) } 158 | } -------------------------------------------------------------------------------- /firmware/nrf24/stm-ll/F429ZITx.ld: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | ** 4 | ** @file : LinkerScript.ld 5 | ** 6 | ** @author : Auto-generated by STM32CubeIDE 7 | ** 8 | ** Abstract : Linker script for NUCLEO-F429ZI Board embedding STM32F429ZITx Device from stm32f4 series 9 | ** 2048Kbytes FLASH 10 | ** 64Kbytes CCMRAM 11 | ** 192Kbytes RAM 12 | ** 13 | ** Set heap size, stack size and stack location according 14 | ** to application requirements. 15 | ** 16 | ** Set memory bank area and size if external memory is used 17 | ** 18 | ** Target : STMicroelectronics STM32 19 | ** 20 | ** Distribution: The file is distributed as is, without any warranty 21 | ** of any kind. 22 | ** 23 | ****************************************************************************** 24 | ** @attention 25 | ** 26 | ** Copyright (c) 2022 STMicroelectronics. 27 | ** All rights reserved. 28 | ** 29 | ** This software is licensed under terms that can be found in the LICENSE file 30 | ** in the root directory of this software component. 31 | ** If no LICENSE file comes with this software, it is provided AS-IS. 32 | ** 33 | ****************************************************************************** 34 | */ 35 | 36 | /* Entry Point */ 37 | ENTRY(Reset_Handler) 38 | 39 | /* Highest address of the user mode stack */ 40 | _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ 41 | 42 | _Min_Heap_Size = 0x200 ; /* required amount of heap */ 43 | _Min_Stack_Size = 0x400 ; /* required amount of stack */ 44 | 45 | /* Memories definition */ 46 | MEMORY 47 | { 48 | CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K 49 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 50 | FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K 51 | } 52 | 53 | /* Sections */ 54 | SECTIONS 55 | { 56 | /* The startup code into "FLASH" Rom type memory */ 57 | .isr_vector : 58 | { 59 | . = ALIGN(4); 60 | KEEP(*(.isr_vector)) /* Startup code */ 61 | . = ALIGN(4); 62 | } >FLASH 63 | 64 | /* The program code and other data into "FLASH" Rom type memory */ 65 | .text : 66 | { 67 | . = ALIGN(4); 68 | *(.text) /* .text sections (code) */ 69 | *(.text*) /* .text* sections (code) */ 70 | *(.glue_7) /* glue arm to thumb code */ 71 | *(.glue_7t) /* glue thumb to arm code */ 72 | *(.eh_frame) 73 | 74 | KEEP (*(.init)) 75 | KEEP (*(.fini)) 76 | 77 | . = ALIGN(4); 78 | _etext = .; /* define a global symbols at end of code */ 79 | } >FLASH 80 | 81 | /* Constant data into "FLASH" Rom type memory */ 82 | .rodata : 83 | { 84 | . = ALIGN(4); 85 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 86 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 87 | . = ALIGN(4); 88 | } >FLASH 89 | 90 | .ARM.extab : { 91 | . = ALIGN(4); 92 | *(.ARM.extab* .gnu.linkonce.armextab.*) 93 | . = ALIGN(4); 94 | } >FLASH 95 | 96 | .ARM : { 97 | . = ALIGN(4); 98 | __exidx_start = .; 99 | *(.ARM.exidx*) 100 | __exidx_end = .; 101 | . = ALIGN(4); 102 | } >FLASH 103 | 104 | .preinit_array : 105 | { 106 | . = ALIGN(4); 107 | PROVIDE_HIDDEN (__preinit_array_start = .); 108 | KEEP (*(.preinit_array*)) 109 | PROVIDE_HIDDEN (__preinit_array_end = .); 110 | . = ALIGN(4); 111 | } >FLASH 112 | 113 | .init_array : 114 | { 115 | . = ALIGN(4); 116 | PROVIDE_HIDDEN (__init_array_start = .); 117 | KEEP (*(SORT(.init_array.*))) 118 | KEEP (*(.init_array*)) 119 | PROVIDE_HIDDEN (__init_array_end = .); 120 | . = ALIGN(4); 121 | } >FLASH 122 | 123 | .fini_array : 124 | { 125 | . = ALIGN(4); 126 | PROVIDE_HIDDEN (__fini_array_start = .); 127 | KEEP (*(SORT(.fini_array.*))) 128 | KEEP (*(.fini_array*)) 129 | PROVIDE_HIDDEN (__fini_array_end = .); 130 | . = ALIGN(4); 131 | } >FLASH 132 | 133 | /* Used by the startup to initialize data */ 134 | _sidata = LOADADDR(.data); 135 | 136 | /* Initialized data sections into "RAM" Ram type memory */ 137 | .data : 138 | { 139 | . = ALIGN(4); 140 | _sdata = .; /* create a global symbol at data start */ 141 | *(.data) /* .data sections */ 142 | *(.data*) /* .data* sections */ 143 | *(.RamFunc) /* .RamFunc sections */ 144 | *(.RamFunc*) /* .RamFunc* sections */ 145 | 146 | . = ALIGN(4); 147 | _edata = .; /* define a global symbol at data end */ 148 | 149 | } >RAM AT> FLASH 150 | 151 | _siccmram = LOADADDR(.ccmram); 152 | 153 | /* CCM-RAM section 154 | * 155 | * IMPORTANT NOTE! 156 | * If initialized variables will be placed in this section, 157 | * the startup code needs to be modified to copy the init-values. 158 | */ 159 | .ccmram : 160 | { 161 | . = ALIGN(4); 162 | _sccmram = .; /* create a global symbol at ccmram start */ 163 | *(.ccmram) 164 | *(.ccmram*) 165 | 166 | . = ALIGN(4); 167 | _eccmram = .; /* create a global symbol at ccmram end */ 168 | } >CCMRAM AT> FLASH 169 | 170 | /* Uninitialized data section into "RAM" Ram type memory */ 171 | . = ALIGN(4); 172 | .bss : 173 | { 174 | /* This is used by the startup in order to initialize the .bss section */ 175 | _sbss = .; /* define a global symbol at bss start */ 176 | __bss_start__ = _sbss; 177 | *(.bss) 178 | *(.bss*) 179 | *(COMMON) 180 | 181 | . = ALIGN(4); 182 | _ebss = .; /* define a global symbol at bss end */ 183 | __bss_end__ = _ebss; 184 | } >RAM 185 | 186 | /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ 187 | ._user_heap_stack : 188 | { 189 | . = ALIGN(8); 190 | PROVIDE ( end = . ); 191 | PROVIDE ( _end = . ); 192 | . = . + _Min_Heap_Size; 193 | . = . + _Min_Stack_Size; 194 | . = ALIGN(8); 195 | } >RAM 196 | 197 | /* Remove information from the compiler libraries */ 198 | /DISCARD/ : 199 | { 200 | libc.a ( * ) 201 | libm.a ( * ) 202 | libgcc.a ( * ) 203 | } 204 | 205 | .ARM.attributes 0 : { *(.ARM.attributes) } 206 | } 207 | -------------------------------------------------------------------------------- /firmware/nrf24/stm-ll/README.md: -------------------------------------------------------------------------------- 1 | # Test firmware for NRF24L01+ using LL SPI on STM32F429 2 | 3 | Minimal test firmware to test NRF24 communication latency. 4 | 5 | IO Setup: 6 | 7 | - PA0 is driven with a 3.3V trigger pulse from my sig-gen. 8 | - PB0 is a 3.3V output signal (also connected to the nucleo's onboard green LED) 9 | - The NRF24 modules requires some IO and uses SPI1: 10 | - PB4 for Chip enable 11 | - PA4 for Chip select 12 | - PB3 for IRQ 13 | - PA5 for SPI CLK 14 | - PA6 for SPI MISO 15 | - PA7 for SPI MOSI 16 | 17 | ## Deps 18 | 19 | I use CMake with CLion for development/builds, so this project is slightly opinionated. 20 | 21 | Requires [`stm32-cmake`](https://github.com/ObKo/stm32-cmake) in this folder to get the build system setup etc. 22 | 23 | I set my CMake options to use my system's arm gcc toolchain `-DSTM32_TOOLCHAIN_PATH=/usr/bin/ -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/arm-none-eabi-g++ -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/arm-none-eabi-gcc`. 24 | 25 | With CLion's build settings, I set the build generator to "Let CMake Decide". 26 | 27 | # Acknowledgements 28 | 29 | I read and tried both of these C libraries + rough examples: 30 | 31 | - [Eunhye Seok's `stm32_hal_nrf24l01p`](https://github.com/mokhwasomssi/stm32_hal_nrf24l01p) 32 | - [Ilia Motornyi's `nrf24l01-lib`](https://github.com/elmot/nrf24l01-lib) (Unlicense licensed) 33 | 34 | But had varying issues with both as published, this implementation is mostly `nrf24l01-lib` but I needed to fix some issues and implement IRQ on RX/TX. -------------------------------------------------------------------------------- /firmware/nrf24/stm-ll/src/support.h: -------------------------------------------------------------------------------- 1 | #ifndef SUPPORT_H 2 | #define SUPPORT_H 3 | 4 | #include "stm32f4xx_ll_gpio.h" 5 | #include "stm32f4xx_ll_spi.h" 6 | 7 | #define NRF_SPI SPI1 8 | 9 | static inline void nRF24_CE_L() 10 | { 11 | LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_4); 12 | } 13 | 14 | static inline void nRF24_CE_H() 15 | { 16 | LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_4); 17 | } 18 | 19 | static inline void nRF24_CSN_L() 20 | { 21 | LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_4); 22 | } 23 | 24 | static inline void nRF24_CSN_H() 25 | { 26 | LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_4); 27 | } 28 | 29 | 30 | static inline uint8_t nRF24_LL_RW(uint8_t data) { 31 | 32 | LL_SPI_Enable(NRF_SPI); 33 | // Wait until TX buffer is empty 34 | while (LL_SPI_IsActiveFlag_BSY(NRF_SPI)); 35 | while (!LL_SPI_IsActiveFlag_TXE(NRF_SPI)); 36 | LL_SPI_TransmitData8(NRF_SPI, data); 37 | while (!LL_SPI_IsActiveFlag_RXNE(NRF_SPI)); 38 | return LL_SPI_ReceiveData8(NRF_SPI); 39 | } 40 | 41 | #endif //SUPPORT_H -------------------------------------------------------------------------------- /firmware/nrf58240/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | server/ 3 | client/ -------------------------------------------------------------------------------- /firmware/nrf58240/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "array": "c", 4 | "string": "c", 5 | "string_view": "c", 6 | "uart.h": "c", 7 | "central.h": "c", 8 | "benchmark_defs.h": "c", 9 | "peripheral.h": "c", 10 | "nus.h": "c" 11 | } 12 | } -------------------------------------------------------------------------------- /firmware/nrf58240/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | cmake_minimum_required(VERSION 3.20.0) 7 | 8 | find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 9 | project(nus_test) 10 | 11 | # NORDIC SDK APP START 12 | target_sources(app PRIVATE 13 | src/main.c 14 | src/central.c 15 | # src/peripheral.c 16 | ) 17 | # NORDIC SDK APP END 18 | 19 | zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 20 | -------------------------------------------------------------------------------- /firmware/nrf58240/Kconfig.sysbuild: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | source "${ZEPHYR_BASE}/share/sysbuild/Kconfig" 8 | 9 | config NRF_DEFAULT_BLUETOOTH 10 | default y 11 | -------------------------------------------------------------------------------- /firmware/nrf58240/README.md: -------------------------------------------------------------------------------- 1 | # NRF52840 BLE Tests 2 | 3 | Using a pair of the nRF52840-DK boards. 4 | 5 | - Test stimulus pin is P0.25 (Button4) 6 | - Output status pin is P0.15 (LED3) 7 | 8 | nRF Connect installed the toolchain, I develop with the Nordic recommended VSCode plugin approach. -------------------------------------------------------------------------------- /firmware/nrf58240/child_image/rpc_host.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | CONFIG_BT=y 8 | CONFIG_BT_PERIPHERAL=n 9 | CONFIG_BT_CENTRAL=y 10 | CONFIG_BT_OBSERVER=y 11 | CONFIG_BT_DEVICE_NAME="Zephyr" 12 | CONFIG_BT_SMP=y 13 | CONFIG_BT_GATT_CLIENT=y 14 | CONFIG_BT_GATT_DM=y 15 | CONFIG_HEAP_MEM_POOL_SIZE=2048 16 | CONFIG_BT_DEBUG_LOG=y 17 | CONFIG_BT_GATT_DM_DATA_PRINT=y 18 | CONFIG_SETTINGS=y 19 | CONFIG_BT_SETTINGS=y 20 | CONFIG_NRF_RPC_THREAD_STACK_SIZE=2048 21 | -------------------------------------------------------------------------------- /firmware/nrf58240/notification-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/nrf58240/notification-validation.png -------------------------------------------------------------------------------- /firmware/nrf58240/prj-periph.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | # Enable the UART driver 8 | CONFIG_UART_ASYNC_API=y 9 | CONFIG_NRFX_UARTE0=y 10 | CONFIG_SERIAL=y 11 | CONFIG_CONSOLE=y 12 | CONFIG_UART_CONSOLE=y 13 | 14 | CONFIG_GPIO=y 15 | CONFIG_ZERO_LATENCY_IRQS=y 16 | 17 | # Enable the BLE stack with GATT Client configuration 18 | CONFIG_BT=y 19 | CONFIG_BT_PERIPHERAL=y 20 | CONFIG_BT_DEVICE_NAME="Nordic_UART_Service" 21 | CONFIG_BT_DEVICE_APPEARANCE=833 22 | CONFIG_BT_MAX_CONN=1 23 | CONFIG_BT_MAX_PAIRED=1 24 | 25 | # Enable the NUS service 26 | CONFIG_BT_NUS=y 27 | 28 | # Enable bonding 29 | CONFIG_BT_SETTINGS=y 30 | CONFIG_FLASH=y 31 | CONFIG_FLASH_PAGE_LAYOUT=y 32 | CONFIG_FLASH_MAP=y 33 | CONFIG_NVS=y 34 | CONFIG_SETTINGS=y 35 | 36 | CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n 37 | CONFIG_BT_AUTO_PHY_UPDATE=y 38 | CONFIG_BT_AUTO_DATA_LEN_UPDATE=y 39 | 40 | CONFIG_BT_BUF_ACL_RX_SIZE=251 41 | CONFIG_BT_ATT_PREPARE_COUNT=2 42 | CONFIG_BT_L2CAP_TX_BUF_COUNT=10 43 | CONFIG_BT_L2CAP_TX_MTU=247 44 | CONFIG_BT_BUF_ACL_TX_COUNT=10 45 | CONFIG_BT_BUF_ACL_TX_SIZE=251 46 | CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 47 | 48 | CONFIG_BT_CTLR_PHY_2M=y 49 | CONFIG_BT_CTLR_PHY_CODED=y 50 | CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=4000000 51 | 52 | # This example requires more stack 53 | CONFIG_MAIN_STACK_SIZE=1152 54 | 55 | CONFIG_HEAP_MEM_POOL_SIZE=4096 56 | 57 | # This example requires more workqueue stack 58 | CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 59 | 60 | # Config logger 61 | CONFIG_LOG=y 62 | CONFIG_USE_SEGGER_RTT=y 63 | CONFIG_LOG_BACKEND_RTT=y 64 | CONFIG_LOG_BACKEND_UART=n 65 | CONFIG_LOG_PRINTK=n 66 | 67 | CONFIG_ASSERT=y 68 | -------------------------------------------------------------------------------- /firmware/nrf58240/prj.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2018 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | # Enable the UART driver 8 | CONFIG_UART_ASYNC_API=y 9 | CONFIG_NRFX_UARTE0=y 10 | CONFIG_SERIAL=y 11 | CONFIG_CONSOLE=y 12 | CONFIG_UART_CONSOLE=y 13 | 14 | CONFIG_GPIO=y 15 | CONFIG_ZERO_LATENCY_IRQS=y 16 | 17 | # Enable the BLE stack with GATT Client configuration 18 | CONFIG_BT=y 19 | CONFIG_BT_CENTRAL=y 20 | CONFIG_BT_SMP=y 21 | CONFIG_BT_GATT_CLIENT=y 22 | 23 | CONFIG_BT_AUTO_PHY_UPDATE=y 24 | CONFIG_BT_AUTO_DATA_LEN_UPDATE=y 25 | 26 | CONFIG_BT_BUF_ACL_RX_SIZE=251 27 | CONFIG_BT_ATT_PREPARE_COUNT=2 28 | CONFIG_BT_L2CAP_TX_BUF_COUNT=10 29 | CONFIG_BT_L2CAP_TX_MTU=247 30 | # CONFIG_BT_CTLR_RX_BUFFERS=2 31 | CONFIG_BT_BUF_ACL_TX_COUNT=10 32 | CONFIG_BT_BUF_ACL_TX_SIZE=251 33 | CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 34 | 35 | 36 | CONFIG_BT_CTLR_PHY_2M=y 37 | CONFIG_BT_CTLR_PHY_CODED=y 38 | CONFIG_BT_CTLR_SDC_MAX_CONN_EVENT_LEN_DEFAULT=4000000 39 | 40 | # Enable the BLE modules from NCS 41 | CONFIG_BT_NUS_CLIENT=y 42 | CONFIG_BT_SCAN=y 43 | CONFIG_BT_SCAN_FILTER_ENABLE=y 44 | CONFIG_BT_SCAN_UUID_CNT=1 45 | CONFIG_BT_GATT_DM=y 46 | CONFIG_HEAP_MEM_POOL_SIZE=4096 47 | 48 | # This example requires more workqueue stack 49 | CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 50 | 51 | # Enable bonding 52 | CONFIG_BT_SETTINGS=y 53 | CONFIG_FLASH=y 54 | CONFIG_FLASH_PAGE_LAYOUT=y 55 | CONFIG_FLASH_MAP=y 56 | CONFIG_NVS=y 57 | CONFIG_SETTINGS=y 58 | 59 | # Config logger 60 | CONFIG_LOG=y 61 | CONFIG_USE_SEGGER_RTT=y 62 | CONFIG_LOG_BACKEND_RTT=y 63 | CONFIG_LOG_BACKEND_UART=n 64 | CONFIG_LOG_PRINTK=n 65 | 66 | CONFIG_ASSERT=y 67 | -------------------------------------------------------------------------------- /firmware/nrf58240/sample.yaml: -------------------------------------------------------------------------------- 1 | sample: 2 | description: Latency benchmark for Nordic UART GATT 3 | name: BLE GATT Benchmark 4 | tests: 5 | sample.bluetooth.central_uart: 6 | build_only: true 7 | integration_platforms: 8 | - nrf52dk_nrf52832 9 | - nrf52840dk_nrf52840 10 | - nrf5340dk_nrf5340_cpuapp 11 | - nrf5340dk_nrf5340_cpuapp_ns 12 | - nrf21540dk_nrf52840 13 | platform_allow: nrf52dk_nrf52832 nrf52840dk_nrf52840 nrf52dk_nrf52810 14 | nrf5340dk_nrf5340_cpuapp nrf5340dk_nrf5340_cpuapp_ns nrf21540dk_nrf52840 15 | tags: bluetooth ci_build 16 | -------------------------------------------------------------------------------- /firmware/nrf58240/src/central.h: -------------------------------------------------------------------------------- 1 | #ifndef CENTRAL_H 2 | #define CENTRAL_H 3 | 4 | /* -------------------------------------------------------------------------- */ 5 | 6 | #include "benchmark_defs.h" 7 | #include 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | void central_init(void); 12 | 13 | /* -------------------------------------------------------------------------- */ 14 | 15 | void central_send_payload( uint8_t *data, uint32_t length ); 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | 19 | void central_register_user_evt_queue( struct k_msgq *queue ); 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | #endif // end CENTRAL_H -------------------------------------------------------------------------------- /firmware/nrf58240/src/peripheral.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | /** @file 8 | * @brief Modified from the Nordic UART Service sample 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include "peripheral.h" 32 | 33 | /* -------------------------------------------------------------------------- */ 34 | 35 | struct k_msgq *user_periph_evt_queue; 36 | 37 | /* -------------------------------------------------------------------------- */ 38 | 39 | #define LOG_MODULE_NAME peripheral_uart 40 | LOG_MODULE_REGISTER(LOG_MODULE_NAME); 41 | 42 | #define DEVICE_NAME CONFIG_BT_DEVICE_NAME 43 | #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) 44 | 45 | #define MIN_CONN_INTERVAL (6) // (N * 1.25 ms) 46 | #define MAX_CONN_INTERVAL (24) // (N * 1.25 ms) 47 | #define CONN_LATENCY (0) 48 | #define SUPERVISION_TIMEOUT (42) // (N * 10 ms) 49 | 50 | static struct bt_conn *current_conn; 51 | 52 | static const struct bt_data ad[] = { 53 | BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), 54 | BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), 55 | }; 56 | 57 | static const struct bt_data sd[] = { 58 | BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL), 59 | }; 60 | 61 | /* -------------------------------------------------------------------------- */ 62 | 63 | static void connected(struct bt_conn *conn, uint8_t err) 64 | { 65 | char addr[BT_ADDR_LE_STR_LEN]; 66 | 67 | if (err) { 68 | LOG_ERR("Connection failed (err %u)", err); 69 | return; 70 | } 71 | 72 | bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); 73 | printk("Connected %s\n", addr); 74 | 75 | current_conn = bt_conn_ref(conn); 76 | 77 | struct bt_le_conn_param *conn_param = BT_LE_CONN_PARAM( MIN_CONN_INTERVAL, 78 | MAX_CONN_INTERVAL, 79 | CONN_LATENCY, 80 | SUPERVISION_TIMEOUT ); 81 | 82 | err = bt_conn_le_param_update(current_conn, conn_param); 83 | if (err) { 84 | LOG_WRN("ITVL exchange failed (err %d)", err); 85 | } 86 | 87 | } 88 | 89 | static void disconnected(struct bt_conn *conn, uint8_t reason) 90 | { 91 | char addr[BT_ADDR_LE_STR_LEN]; 92 | 93 | bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); 94 | 95 | printk("Disconnected: %s (reason %u)\n", addr, reason); 96 | 97 | if (current_conn) { 98 | bt_conn_unref(current_conn); 99 | current_conn = NULL; 100 | } 101 | } 102 | 103 | BT_CONN_CB_DEFINE(conn_callbacks) = { 104 | .connected = connected, 105 | .disconnected = disconnected, 106 | }; 107 | 108 | static void ble_send_cb( struct bt_conn *conn ) 109 | { 110 | // printk("TxDone?\n"); 111 | 112 | if( user_periph_evt_queue ) 113 | { 114 | bench_event_t evt; 115 | bench_event_send_cb_t *send_cb = &evt.data.send_cb; 116 | evt.id = BENCH_SEND_CB; 117 | send_cb->bytes_sent = 0; 118 | k_msgq_put(user_periph_evt_queue, &evt, K_MSEC(10) ); 119 | } 120 | } 121 | 122 | static void ble_receive_cb(struct bt_conn *conn, const uint8_t *const data, 123 | uint16_t len) 124 | { 125 | char addr[BT_ADDR_LE_STR_LEN] = {0}; 126 | bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr)); 127 | // printk("Received %i data from: %s\n", len, addr); 128 | 129 | if( user_periph_evt_queue && len ) 130 | { 131 | bench_event_t evt; 132 | evt.id = BENCH_RECV_CB; 133 | bench_event_recv_cb_t *recv_cb = &evt.data.recv_cb; 134 | 135 | // Allocate a sufficiently large chunk of memory and copy the payload into it 136 | // User task is responsible for freeing this memory 137 | recv_cb->data = malloc( len ); 138 | if( recv_cb->data == NULL ) 139 | { 140 | LOG_WRN("Not able to allocate recv data buffer"); 141 | return; 142 | } 143 | 144 | // Copy inbound data in 145 | memcpy(recv_cb->data, data, len); 146 | recv_cb->data_len = len; 147 | 148 | // Copy the event into the queue for processing 149 | k_msgq_put(user_periph_evt_queue, &evt, K_MSEC(10)); 150 | } 151 | } 152 | 153 | static void ble_notify_enabled(enum bt_nus_send_status status ) 154 | { 155 | if(status == BT_NUS_SEND_STATUS_ENABLED) 156 | { 157 | printk("TX Notify Enabled\n"); 158 | } 159 | else if(status == BT_NUS_SEND_STATUS_DISABLED) 160 | { 161 | printk("TX Notify Disabled\n"); 162 | } 163 | } 164 | 165 | static struct bt_nus_cb nus_cb = { 166 | .received = ble_receive_cb, 167 | .sent = ble_send_cb, 168 | .send_enabled = ble_notify_enabled, 169 | }; 170 | 171 | /* -------------------------------------------------------------------------- */ 172 | 173 | void peripheral_init(void) 174 | { 175 | int err; 176 | 177 | err = bt_enable(NULL); 178 | 179 | printk("Bluetooth initialized\n"); 180 | 181 | if (IS_ENABLED(CONFIG_SETTINGS)) { 182 | settings_load(); 183 | } 184 | 185 | err = bt_nus_init(&nus_cb); 186 | if (err) { 187 | LOG_ERR("Failed to initialize UART service (err: %d)", err); 188 | return; 189 | } 190 | 191 | err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, 192 | ARRAY_SIZE(sd)); 193 | if (err) { 194 | LOG_ERR("Advertising failed to start (err %d)", err); 195 | return; 196 | } 197 | 198 | printk("Bluetooth initialized\n"); 199 | 200 | } 201 | 202 | /* -------------------------------------------------------------------------- */ 203 | 204 | void peripheral_send_payload( uint8_t *data, uint32_t length ) 205 | { 206 | if( current_conn ) 207 | { 208 | uint8_t *buffer = malloc(length * sizeof(uint8_t)); 209 | memcpy(buffer, data, length); 210 | // printk("Sending %iB\n", length); 211 | 212 | if( bt_nus_send(NULL, buffer, length) ) { 213 | LOG_WRN("Failed to send data over BLE connection"); 214 | } 215 | 216 | free(buffer); 217 | } 218 | } 219 | 220 | /* -------------------------------------------------------------------------- */ 221 | 222 | void peripheral_register_user_evt_queue( struct k_msgq *queue ) 223 | { 224 | if( queue ) 225 | { 226 | user_periph_evt_queue = queue; 227 | } 228 | } 229 | 230 | /* -------------------------------------------------------------------------- */ -------------------------------------------------------------------------------- /firmware/nrf58240/src/peripheral.h: -------------------------------------------------------------------------------- 1 | #ifndef PERIPHERAL_H 2 | #define PERIPHERAL_H 3 | 4 | /* -------------------------------------------------------------------------- */ 5 | 6 | #include "benchmark_defs.h" 7 | #include 8 | 9 | /* -------------------------------------------------------------------------- */ 10 | 11 | void peripheral_init(void); 12 | 13 | /* -------------------------------------------------------------------------- */ 14 | 15 | void peripheral_send_payload( uint8_t *data, uint32_t length ); 16 | 17 | /* -------------------------------------------------------------------------- */ 18 | 19 | void peripheral_register_user_evt_queue( struct k_msgq *queue ); 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | #endif // end PERIPHERAL_H -------------------------------------------------------------------------------- /firmware/nrf58240/write-resp-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/nrf58240/write-resp-validation.png -------------------------------------------------------------------------------- /firmware/rfm95/rx-capture-64k5-SF2048-4o6.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/rfm95/rx-capture-64k5-SF2048-4o6.sal -------------------------------------------------------------------------------- /firmware/rfm95/rx-capture-irq.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/rfm95/rx-capture-irq.sal -------------------------------------------------------------------------------- /firmware/rfm95/stm-ll/.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build* 2 | stm32-cmake/* -------------------------------------------------------------------------------- /firmware/rfm95/stm-ll/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | set(PROJ_NAME rfm95-test) 3 | set(STM32_CMAKE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/stm32-cmake) 4 | set(CMAKE_TOOLCHAIN_FILE ${STM32_CMAKE_PATH}/cmake/stm32_gcc.cmake) 5 | 6 | #SET(CMAKE_CXX_FLAGS "-O1") 7 | #SET(CMAKE_C_FLAGS "-O1") 8 | 9 | project(${PROJ_NAME} C ASM) 10 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 11 | 12 | stm32_fetch_cmsis(F4) 13 | stm32_fetch_hal(F4) 14 | 15 | find_package(CMSIS COMPONENTS STM32F4 REQUIRED) 16 | find_package(HAL 17 | COMPONENTS 18 | STM32F4 19 | REQUIRED) 20 | 21 | add_definitions(-DUSE_FULL_LL_DRIVER) 22 | 23 | # Clock speed definitions for custom hardware 24 | add_definitions(-DHSE_VALUE=8000000) 25 | add_definitions(-DLSE_VALUE=32768) 26 | 27 | add_executable(${PROJ_NAME}) 28 | 29 | target_sources( 30 | ${PROJ_NAME} 31 | PRIVATE 32 | ${CMAKE_SOURCE_DIR}/src/main.c 33 | ${CMAKE_SOURCE_DIR}/libs/rfm95.c 34 | ) 35 | 36 | target_include_directories( 37 | ${PROJ_NAME} 38 | PUBLIC 39 | ${CMAKE_CURRENT_SOURCE_DIR} 40 | ${CMAKE_CURRENT_SOURCE_DIR}/src 41 | ${CMAKE_CURRENT_SOURCE_DIR}/libs/ 42 | 43 | ) 44 | 45 | target_link_libraries( 46 | ${PROJ_NAME} 47 | PRIVATE 48 | HAL::STM32::F4::LL_RCC 49 | HAL::STM32::F4::LL_PWR 50 | HAL::STM32::F4::LL_UTILS 51 | HAL::STM32::F4::LL_GPIO 52 | HAL::STM32::F4::LL_EXTI 53 | HAL::STM32::F4::LL_SPI 54 | HAL::STM32::F4::LL_DMA 55 | CMSIS::STM32::F429xx 56 | STM32::NoSys 57 | ) 58 | 59 | target_link_options(${PROJ_NAME} PRIVATE -Wl,--print-memory-usage) 60 | stm32_add_linker_script(${PROJ_NAME} PRIVATE F429ZITx.ld) 61 | 62 | stm32_generate_binary_file(${PROJ_NAME}) 63 | stm32_generate_hex_file(${PROJ_NAME}) -------------------------------------------------------------------------------- /firmware/rfm95/stm-ll/F429VETx.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler) 2 | 3 | /* Highest address of the user mode stack */ 4 | _estack = 0x20000000 + 192K; /* end of RAM */ 5 | _Min_Heap_Size = 0x200; 6 | _Min_Stack_Size = 0x400; 7 | 8 | /* Specify the memory areas */ 9 | MEMORY 10 | { 11 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K 12 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 13 | CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K 14 | } 15 | 16 | /* See page 78 of ST doc RM0090 for single bank flash memory layout. */ 17 | /* Layout in sequential sectors is as follows: 16k, 16k, 16k, 16k, 64k, 128k, 128k, 128k */ 18 | /* FLASH_BOOT (rx) : ORIGIN = 0x08000000, LENGTH = 32K */ 19 | /* EMULATED_EEPROM (rwx) : ORIGIN = 0x08008000, LENGTH = 32K */ 20 | 21 | SECTIONS 22 | { 23 | /* The startup code goes first into FLASH */ 24 | .isr_vector : 25 | { 26 | . = ALIGN(4); 27 | KEEP(*(.isr_vector)) /* Startup code */ 28 | . = ALIGN(4); 29 | } >FLASH 30 | 31 | /* The program code and other data goes into FLASH */ 32 | .text : 33 | { 34 | . = ALIGN(4); 35 | *(.text) /* .text sections (code) */ 36 | *(.text*) /* .text* sections (code) */ 37 | *(.glue_7) /* glue arm to thumb code */ 38 | *(.glue_7t) /* glue thumb to arm code */ 39 | *(.eh_frame) 40 | 41 | KEEP (*(.init)) 42 | KEEP (*(.fini)) 43 | 44 | . = ALIGN(4); 45 | _etext = .; /* define a global symbols at end of code */ 46 | } >FLASH 47 | 48 | /* Constant data goes into FLASH */ 49 | .rodata : 50 | { 51 | . = ALIGN(4); 52 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 53 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 54 | . = ALIGN(4); 55 | } >FLASH 56 | 57 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH 58 | .ARM : { 59 | __exidx_start = .; 60 | *(.ARM.exidx*) 61 | __exidx_end = .; 62 | } >FLASH 63 | 64 | .preinit_array : 65 | { 66 | PROVIDE_HIDDEN (__preinit_array_start = .); 67 | KEEP (*(.preinit_array*)) 68 | PROVIDE_HIDDEN (__preinit_array_end = .); 69 | } >FLASH 70 | .init_array : 71 | { 72 | PROVIDE_HIDDEN (__init_array_start = .); 73 | KEEP (*(SORT(.init_array.*))) 74 | KEEP (*(.init_array*)) 75 | PROVIDE_HIDDEN (__init_array_end = .); 76 | } >FLASH 77 | .fini_array : 78 | { 79 | PROVIDE_HIDDEN (__fini_array_start = .); 80 | KEEP (*(SORT(.fini_array.*))) 81 | KEEP (*(.fini_array*)) 82 | PROVIDE_HIDDEN (__fini_array_end = .); 83 | } >FLASH 84 | 85 | /* used by the startup to initialize data */ 86 | _sidata = LOADADDR(.data); 87 | 88 | /* Initialized data sections goes into RAM, load LMA copy after code */ 89 | .data : 90 | { 91 | . = ALIGN(4); 92 | _sdata = .; /* create a global symbol at data start */ 93 | *(.data) /* .data sections */ 94 | *(.data*) /* .data* sections */ 95 | 96 | . = ALIGN(4); 97 | _edata = .; /* define a global symbol at data end */ 98 | } >RAM AT> FLASH 99 | 100 | _siccmram = LOADADDR(.ccmram); 101 | 102 | /* CCM-RAM section 103 | * 104 | * IMPORTANT NOTE! 105 | * If initialized variables will be placed in this section, 106 | * the startup code needs to be modified to copy the init-values. 107 | */ 108 | .ccmram (NOLOAD): 109 | { 110 | . = ALIGN(4); 111 | _sccmram = .; /* create a global symbol at ccmram start */ 112 | *(.ccmram) 113 | *(.ccmram*) 114 | 115 | . = ALIGN(4); 116 | _eccmram = .; /* create a global symbol at ccmram end */ 117 | } >CCMRAM AT> FLASH 118 | 119 | 120 | /* Uninitialized data section */ 121 | . = ALIGN(4); 122 | .bss : 123 | { 124 | /* This is used by the startup in order to initialize the .bss secion */ 125 | _sbss = .; /* define a global symbol at bss start */ 126 | __bss_start__ = _sbss; 127 | *(.bss) 128 | *(.bss*) 129 | *(COMMON) 130 | 131 | . = ALIGN(4); 132 | _ebss = .; /* define a global symbol at bss end */ 133 | __bss_end__ = _ebss; 134 | } >RAM 135 | 136 | /* User_heap_stack section, used to check that there is enough RAM left */ 137 | ._user_heap_stack : 138 | { 139 | . = ALIGN(8); 140 | PROVIDE ( end = . ); 141 | PROVIDE ( _end = . ); 142 | . = . + _Min_Heap_Size; 143 | . = . + _Min_Stack_Size; 144 | . = ALIGN(8); 145 | } >RAM 146 | 147 | 148 | 149 | /* Remove information from the standard libraries */ 150 | /DISCARD/ : 151 | { 152 | libc.a ( * ) 153 | libm.a ( * ) 154 | libgcc.a ( * ) 155 | } 156 | 157 | .ARM.attributes 0 : { *(.ARM.attributes) } 158 | } -------------------------------------------------------------------------------- /firmware/rfm95/stm-ll/F429ZITx.ld: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | ** 4 | ** @file : LinkerScript.ld 5 | ** 6 | ** @author : Auto-generated by STM32CubeIDE 7 | ** 8 | ** Abstract : Linker script for NUCLEO-F429ZI Board embedding STM32F429ZITx Device from stm32f4 series 9 | ** 2048Kbytes FLASH 10 | ** 64Kbytes CCMRAM 11 | ** 192Kbytes RAM 12 | ** 13 | ** Set heap size, stack size and stack location according 14 | ** to application requirements. 15 | ** 16 | ** Set memory bank area and size if external memory is used 17 | ** 18 | ** Target : STMicroelectronics STM32 19 | ** 20 | ** Distribution: The file is distributed as is, without any warranty 21 | ** of any kind. 22 | ** 23 | ****************************************************************************** 24 | ** @attention 25 | ** 26 | ** Copyright (c) 2022 STMicroelectronics. 27 | ** All rights reserved. 28 | ** 29 | ** This software is licensed under terms that can be found in the LICENSE file 30 | ** in the root directory of this software component. 31 | ** If no LICENSE file comes with this software, it is provided AS-IS. 32 | ** 33 | ****************************************************************************** 34 | */ 35 | 36 | /* Entry Point */ 37 | ENTRY(Reset_Handler) 38 | 39 | /* Highest address of the user mode stack */ 40 | _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ 41 | 42 | _Min_Heap_Size = 0x200 ; /* required amount of heap */ 43 | _Min_Stack_Size = 0x400 ; /* required amount of stack */ 44 | 45 | /* Memories definition */ 46 | MEMORY 47 | { 48 | CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K 49 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 50 | FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K 51 | } 52 | 53 | /* Sections */ 54 | SECTIONS 55 | { 56 | /* The startup code into "FLASH" Rom type memory */ 57 | .isr_vector : 58 | { 59 | . = ALIGN(4); 60 | KEEP(*(.isr_vector)) /* Startup code */ 61 | . = ALIGN(4); 62 | } >FLASH 63 | 64 | /* The program code and other data into "FLASH" Rom type memory */ 65 | .text : 66 | { 67 | . = ALIGN(4); 68 | *(.text) /* .text sections (code) */ 69 | *(.text*) /* .text* sections (code) */ 70 | *(.glue_7) /* glue arm to thumb code */ 71 | *(.glue_7t) /* glue thumb to arm code */ 72 | *(.eh_frame) 73 | 74 | KEEP (*(.init)) 75 | KEEP (*(.fini)) 76 | 77 | . = ALIGN(4); 78 | _etext = .; /* define a global symbols at end of code */ 79 | } >FLASH 80 | 81 | /* Constant data into "FLASH" Rom type memory */ 82 | .rodata : 83 | { 84 | . = ALIGN(4); 85 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 86 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 87 | . = ALIGN(4); 88 | } >FLASH 89 | 90 | .ARM.extab : { 91 | . = ALIGN(4); 92 | *(.ARM.extab* .gnu.linkonce.armextab.*) 93 | . = ALIGN(4); 94 | } >FLASH 95 | 96 | .ARM : { 97 | . = ALIGN(4); 98 | __exidx_start = .; 99 | *(.ARM.exidx*) 100 | __exidx_end = .; 101 | . = ALIGN(4); 102 | } >FLASH 103 | 104 | .preinit_array : 105 | { 106 | . = ALIGN(4); 107 | PROVIDE_HIDDEN (__preinit_array_start = .); 108 | KEEP (*(.preinit_array*)) 109 | PROVIDE_HIDDEN (__preinit_array_end = .); 110 | . = ALIGN(4); 111 | } >FLASH 112 | 113 | .init_array : 114 | { 115 | . = ALIGN(4); 116 | PROVIDE_HIDDEN (__init_array_start = .); 117 | KEEP (*(SORT(.init_array.*))) 118 | KEEP (*(.init_array*)) 119 | PROVIDE_HIDDEN (__init_array_end = .); 120 | . = ALIGN(4); 121 | } >FLASH 122 | 123 | .fini_array : 124 | { 125 | . = ALIGN(4); 126 | PROVIDE_HIDDEN (__fini_array_start = .); 127 | KEEP (*(SORT(.fini_array.*))) 128 | KEEP (*(.fini_array*)) 129 | PROVIDE_HIDDEN (__fini_array_end = .); 130 | . = ALIGN(4); 131 | } >FLASH 132 | 133 | /* Used by the startup to initialize data */ 134 | _sidata = LOADADDR(.data); 135 | 136 | /* Initialized data sections into "RAM" Ram type memory */ 137 | .data : 138 | { 139 | . = ALIGN(4); 140 | _sdata = .; /* create a global symbol at data start */ 141 | *(.data) /* .data sections */ 142 | *(.data*) /* .data* sections */ 143 | *(.RamFunc) /* .RamFunc sections */ 144 | *(.RamFunc*) /* .RamFunc* sections */ 145 | 146 | . = ALIGN(4); 147 | _edata = .; /* define a global symbol at data end */ 148 | 149 | } >RAM AT> FLASH 150 | 151 | _siccmram = LOADADDR(.ccmram); 152 | 153 | /* CCM-RAM section 154 | * 155 | * IMPORTANT NOTE! 156 | * If initialized variables will be placed in this section, 157 | * the startup code needs to be modified to copy the init-values. 158 | */ 159 | .ccmram : 160 | { 161 | . = ALIGN(4); 162 | _sccmram = .; /* create a global symbol at ccmram start */ 163 | *(.ccmram) 164 | *(.ccmram*) 165 | 166 | . = ALIGN(4); 167 | _eccmram = .; /* create a global symbol at ccmram end */ 168 | } >CCMRAM AT> FLASH 169 | 170 | /* Uninitialized data section into "RAM" Ram type memory */ 171 | . = ALIGN(4); 172 | .bss : 173 | { 174 | /* This is used by the startup in order to initialize the .bss section */ 175 | _sbss = .; /* define a global symbol at bss start */ 176 | __bss_start__ = _sbss; 177 | *(.bss) 178 | *(.bss*) 179 | *(COMMON) 180 | 181 | . = ALIGN(4); 182 | _ebss = .; /* define a global symbol at bss end */ 183 | __bss_end__ = _ebss; 184 | } >RAM 185 | 186 | /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ 187 | ._user_heap_stack : 188 | { 189 | . = ALIGN(8); 190 | PROVIDE ( end = . ); 191 | PROVIDE ( _end = . ); 192 | . = . + _Min_Heap_Size; 193 | . = . + _Min_Stack_Size; 194 | . = ALIGN(8); 195 | } >RAM 196 | 197 | /* Remove information from the compiler libraries */ 198 | /DISCARD/ : 199 | { 200 | libc.a ( * ) 201 | libm.a ( * ) 202 | libgcc.a ( * ) 203 | } 204 | 205 | .ARM.attributes 0 : { *(.ARM.attributes) } 206 | } 207 | -------------------------------------------------------------------------------- /firmware/rfm95/stm-ll/README.md: -------------------------------------------------------------------------------- 1 | # Test firmware for RFM95 using LL SPI on STM32F429 2 | 3 | Minimal test firmware to test direct device-device communication latency. 4 | 5 | IO Setup: 6 | 7 | - PA0 is driven with a 3.3V trigger pulse from my sig-gen. 8 | - PB0 is a 3.3V output signal (also connected to the nucleo's onboard green LED) 9 | - The RFM95 modules requires some IO and uses SPI1: 10 | - PB4 for Reset 11 | - PB3 for G0 IRQ 12 | - PB8 for G1 IRQ 13 | - PB9 for G5 IRQ 14 | - PA4 for Chip select 15 | - PA5 for SPI CLK 16 | - PA6 for SPI MISO 17 | - PA7 for SPI MOSI 18 | 19 | ## Deps 20 | 21 | I use CMake with CLion for development/builds, so this project is slightly opinionated. 22 | 23 | Requires [`stm32-cmake`](https://github.com/ObKo/stm32-cmake) in this folder to get the build system setup etc. 24 | 25 | I set my CMake options to use my system's arm gcc toolchain `-DSTM32_TOOLCHAIN_PATH=/usr/bin/ -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/arm-none-eabi-g++ -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/arm-none-eabi-gcc`. 26 | 27 | With CLion's build settings, I set the build generator to "Let CMake Decide". 28 | 29 | # Acknowledgements 30 | 31 | [Henri Heimann's `stm32-hal-rfm95`](https://github.com/henriheimann/stm32-hal-rfm95) (MIT Licence) library was used as starting point but is more intended for LoRaWAN use, 32 | [PeeWeeLabs's `pwl_rfm9X`](https://github.com/PeeWeeLabs/pwl_rfm9X) (MIT Licence) was also referenced (it's in C++). 33 | -------------------------------------------------------------------------------- /firmware/rfm95/stm-ll/libs/rfm95.h: -------------------------------------------------------------------------------- 1 | #ifndef RFM95_H 2 | #define RFM95_H 3 | 4 | #include 5 | #include 6 | 7 | #include "rfm95_defines.h" 8 | 9 | // User callbacks for SPI register read/write functions 10 | typedef uint32_t (*rfm9X_reg_rwr_fptr_t)(uint8_t reg_addr, uint8_t *reg_data, uint32_t len); 11 | 12 | // Prototype definition for the required delay function 13 | typedef void (*rfm9X_ms_delay_t)(uint32_t ms_count); 14 | 15 | typedef void (*rfm9X_enable_irq_t)(void); 16 | 17 | typedef void (*rfm9X_rx_data_cb_t)(uint8_t*, uint8_t); 18 | 19 | // The radio can handle packet sizes up to 255 bytes long 20 | // If you don't intend to send packets that long you can 21 | // adjust/define this to a smaller value that will save 22 | // some memory space. 23 | #if !defined(RFM9X_RX_BUFFER_LEN) 24 | #define RFM9X_RX_BUFFER_LEN 256 25 | #endif 26 | 27 | #if !defined(RFM9X_MAX_TX_LEN) 28 | #define RFM9X_MAX_TX_LEN 255 29 | #endif 30 | 31 | // Generally this doesn't need to be adjusted, but just in 32 | // case someone has a radio with a different clock. 33 | #if !defined(RFM9X_BASE_CLOCK_FREQENCY) 34 | #define RFM9X_BASE_CLOCK_FREQENCY (32000000) 35 | #endif 36 | 37 | typedef enum 38 | { 39 | RFM9X_POLL_RX_ERROR = 0x00, 40 | RFM9X_POLL_NO_STATUS, 41 | RFM9X_POLL_RX_READY, 42 | RFM9X_POLL_TX_DONE 43 | } rfm95_poll_status_t; 44 | 45 | typedef enum 46 | { 47 | RFM95_INTERRUPT_DIO0, 48 | RFM95_INTERRUPT_DIO1, 49 | RFM95_INTERRUPT_DIO5, 50 | RFM95_INTERRUPT_DIO_NUM 51 | } rfm95_interrupt_t; 52 | 53 | 54 | // Setup the callbacks and library internals. 55 | rfm95_status_t rfm95_setup_library( rfm9X_reg_rwr_fptr_t read_cb, 56 | rfm9X_reg_rwr_fptr_t write_cb, 57 | rfm9X_enable_irq_t en_irq_cb, 58 | rfm9X_rx_data_cb_t rx_data_cb, 59 | rfm9X_ms_delay_t delay_cb ); 60 | 61 | // Call init with the following LoRa radio parameters. 62 | // Hz is the target carrier/center frequency. (i.e. 915000000) 63 | // tx_power_dbm must be between 2 and 20. 64 | rfm95_status_t rfm95_init_radio( uint32_t center_frequency_hz, 65 | uint8_t tx_power_dbm, 66 | lora_bw_t bandwidth_channel, 67 | lora_cr_t coding_rate, 68 | lora_sf_t spreading_factor ); 69 | 70 | void rfm95_on_interrupt(rfm95_interrupt_t interrupt); 71 | 72 | rfm95_poll_status_t handle_pending_interrupts( void ); 73 | 74 | rfm95_status_t set_irq( rfm95_interrupt_t dio, rfm95_irq_type irq_type ); 75 | 76 | rfm95_status_t clear_irq( void ); 77 | 78 | rfm95_status_t invert_tx_iq( void ); 79 | 80 | rfm95_status_t invert_rx_iq( void ); 81 | 82 | rfm95_status_t set_preamble( void ); 83 | 84 | rfm95_status_t set_lna( void ); 85 | 86 | // Set the chirp bandwidth, coding and spreading factor settings 87 | rfm95_status_t set_chirp_config( lora_bw_t bandwidth_channel, 88 | lora_cr_t coding_rate, 89 | lora_sf_t spreading_factor ); 90 | 91 | // Use set_center_frequency to adjust the carrier frequency after initialization if desired 92 | rfm95_status_t set_center_frequency( uint32_t Hz ); 93 | 94 | // Use set_power_amp to adjust the TX power after initialization if desired 95 | // dBm must be between 5 and 20 96 | rfm95_status_t set_power_amp( uint8_t dBm ); 97 | 98 | // Set modem max payload length 99 | rfm95_status_t set_max_payload_length( uint8_t bytes ); 100 | 101 | // Use set_mode to select one of the operating modes given by lora_mode_t 102 | rfm95_status_t set_mode( lora_mode_t mode ); 103 | 104 | // Read back the current mode from the radio 105 | lora_mode_t get_mode( void ); 106 | 107 | // Return the Receive Signal Strength Indicator (approximated by the radio) 108 | int16_t get_rssi( void ); 109 | 110 | // Ask the radio what it's version number is 111 | uint8_t get_version( void ); 112 | 113 | 114 | 115 | 116 | 117 | 118 | // Instead of interrupt driven, this driver is polled. 119 | // poll() must be called regularly when waiting for RX data in RFM9X_LORA_MODE_RX_CONTINUOUS 120 | // mode. It can also be called regularly during TX to determine when TX is complete. 121 | // Once RX data is identified by polling call get_rx_data to copy the data to 122 | // your local buffer. 123 | // Returns one of the defined "RFM9X_POLL_..." status values 124 | rfm95_poll_status_t poll( void ); 125 | 126 | // Sets the mode to RXContinuous and polls IRQ status once 127 | // Assumes IRQ handler is able to run the poll() when DI0 fires 128 | rfm95_poll_status_t start_rx_with_irq( void ); 129 | 130 | // Transmit the given data 131 | rfm95_status_t send( uint8_t *data, uint8_t len ); 132 | 133 | 134 | #endif //RFM95_H -------------------------------------------------------------------------------- /firmware/rfm95/stm-ll/libs/rfm95_defines.h: -------------------------------------------------------------------------------- 1 | #ifndef RFM95_DEFINES_H 2 | #define RFM95_DEFINES_H 3 | 4 | typedef enum { 5 | RFM9X_LORA_BW_7p8k, // 0x00 6 | RFM9X_LORA_BW_10p4k, // 0x01 7 | RFM9X_LORA_BW_15p6k, // 0x02 8 | RFM9X_LORA_BW_20p8k, // 0x03 9 | RFM9X_LORA_BW_31p25k, // 0x04 10 | RFM9X_LORA_BW_41p7k, // 0x05 11 | RFM9X_LORA_BW_62p5k, // 0x06 12 | RFM9X_LORA_BW_125k, // 0x07 13 | RFM9X_LORA_BW_250k, // 0x08 14 | RFM9X_LORA_BW_500k, // 0x09 15 | } lora_bw_t; 16 | 17 | typedef enum { 18 | RFM9X_LORA_CR_4_5 = 0x01, // 0x01 19 | RFM9X_LORA_CR_4_6, // 0x02 20 | RFM9X_LORA_CR_4_7, // 0x03 21 | RFM9X_LORA_CR_4_8, // 0x04 22 | } lora_cr_t; 23 | 24 | typedef enum { 25 | // Currently this driver supports only explicit header mode 26 | // Setting the spreading factor to 6 is no compatible with 27 | // explicit header mode... so no support. 28 | // RFM9X_LORA_SF_64 = 6, // 0x06 29 | RFM9X_LORA_SF_128 = 7, // 0x07 30 | RFM9X_LORA_SF_256, // 0x08 31 | RFM9X_LORA_SF_512, // 0x09 32 | RFM9X_LORA_SF_1024, // 0x0A SF10 33 | RFM9X_LORA_SF_2048, // 0x0B 34 | RFM9X_LORA_SF_4096, // 0x0C SF12 35 | } lora_sf_t; 36 | 37 | // Highest bit (0x80 offset) enables Long Range (LoRA) mode 38 | // Bits 6,5 specify FSK (00) or OOK (01) 39 | // Bottom 3 bits specify the mode 40 | typedef enum { 41 | RFM9X_MODE_SLEEP = 0x00, // 0x00 42 | 43 | RFM9X_FSK_MODE_SLEEP = 0x00, // 0x00 44 | RFM9X_FSK_MODE_STDBY, // 0x01 45 | RFM9X_FSK_MODE_FSTX, // 0x02 // No support in driver 46 | RFM9X_FSK_MODE_TX, // 0x03 47 | RFM9X_FSK_MODE_FSRX, // 0x04 // No support in driver 48 | RFM9X_FSK_MODE_RX_CONTINUOUS, // 0x05 49 | RFM9X_FSK_MODE_RX, // 0x06 // No support in driver 50 | RFM9X_FSK_MODE_CAD, // 0x07 // No support in driver 51 | 52 | RFM9X_OOK_MODE_SLEEP = 0x20, // 0x00 53 | RFM9X_OOK_MODE_STDBY, // 0x01 54 | RFM9X_OOK_MODE_FSTX, // 0x02 // No support in driver 55 | RFM9X_OOK_MODE_TX, // 0x03 56 | RFM9X_OOK_MODE_FSRX, // 0x04 // No support in driver 57 | RFM9X_OOK_MODE_RX_CONTINUOUS, // 0x05 58 | RFM9X_OOK_MODE_RX, // 0x06 // No support in driver 59 | RFM9X_OOK_MODE_CAD, // 0x07 // No support in driver 60 | 61 | RFM9X_LORA_MODE_SLEEP = 0x80, // 0x00 62 | RFM9X_LORA_MODE_STDBY, // 0x01 63 | RFM9X_LORA_MODE_FSTX, // 0x02 // No support in driver 64 | RFM9X_LORA_MODE_TX, // 0x03 65 | RFM9X_LORA_MODE_FSRX, // 0x04 // No support in driver 66 | RFM9X_LORA_MODE_RX_CONTINUOUS, // 0x05 67 | RFM9X_LORA_MODE_RX, // 0x06 // No support in driver 68 | RFM9X_LORA_MODE_CAD, // 0x07 // No support in driver 69 | RFM9X_LORA_MODE_INVALID = 0xFF 70 | } lora_mode_t; 71 | 72 | typedef enum { 73 | RFM95_STATUS_OK = 0x00, 74 | RFM95_STATUS_ERROR, 75 | } rfm95_status_t; 76 | 77 | // See sx1276_77_78_79.pdf page 69 for mapping table 78 | typedef enum { 79 | // TODO: Most of this functionality isn't implemented... 80 | // sorry if you're reading this... 81 | RFM95_IRQ_TXDONE = 0x00, 82 | RFM95_IRQ_RXDONE, 83 | RFM95_IRQ_RXTIMEOUT, 84 | RFM95_IRQ_MODEREADY, 85 | RFM95_IRQ_VALIDHEADER, 86 | RFM95_IRQ_PAYLOADCRCERROR, 87 | RFM95_IRQ_FHSSCHANGECHANNEL, 88 | RFM95_IRQ_CLKOUT, 89 | RFM95_IRQ_PLLLOCK, 90 | RFM95_IRQ_CADDONE, 91 | RFM95_IRQ_CADDETECTED, 92 | } rfm95_irq_type; 93 | 94 | #endif //RFM95_DEFINES_H -------------------------------------------------------------------------------- /firmware/rfm95/stm-ll/libs/rfm95_private_defines.h: -------------------------------------------------------------------------------- 1 | #ifndef RFM95_PRIVATE_DEFINES_H 2 | #define RFM95_PRIVATE_DEFINES_H 3 | 4 | #define RFM95_VERSION (0x12) 5 | 6 | // Define masks for each interrupt flag bit 7 | #define RFM9X_IRQ_MASK_RX_TIMEOUT 0x80 8 | #define RFM9X_IRQ_MASK_RX_DONE 0x40 9 | #define RFM9X_IRQ_MASK_PAYLOAD_CRC_ERROR 0x20 10 | #define RFM9X_IRQ_MASK_VALID_HEADER 0x10 11 | #define RFM9X_IRQ_MASK_TX_DONE 0x08 12 | #define RFM9X_IRQ_MASK_CAD_DONE 0x04 13 | #define RFM9X_IRQ_MASK_FHSS_CHANGE_CHANNEL 0x02 14 | #define RFM9X_IRQ_MASK_CAD_DETECTED 0x01 15 | 16 | // Macros to check flags 17 | #define IS_FLAG_SET(irq_flags, mask) ((irq_flags) & (mask)) 18 | 19 | enum { 20 | RFM9X_LORA_BW_BitPos = 0x04, 21 | RFM9X_LORA_CR_BitPos = 0x01, 22 | RFM9X_LORA_SF_BitPos = 0x04, 23 | }; 24 | 25 | enum { 26 | RFM9X_REG_Fifo = 0x00, 27 | RFM9X_REG_OpMode = 0x01, 28 | RFM9X_REG_FrfMsb = 0x06, 29 | RFM9X_REG_FrfMid = 0x07, 30 | RFM9X_REG_FrfLsb = 0x08, 31 | RFM9X_REG_PaConfig = 0x09, 32 | RFM9X_REG_PaRamp = 0x0A, 33 | RFM9X_REG_Ocp = 0x0B, 34 | RFM9X_REG_Lna = 0x0C, 35 | RFM9X_REG_FifoAddrPtr = 0x0D, 36 | RFM9X_REG_FifoTxBaseAddr = 0x0E, 37 | RFM9X_REG_FifoRxBaseAddr = 0x0F, 38 | RFM9X_REG_FifoRxCurrentAddr = 0x10, 39 | RFM9X_REG_IrqFlagsMask = 0x11, 40 | RFM9X_REG_IrqFlags = 0x12, 41 | RFM9X_REG_RxNbBytes = 0x13, 42 | RFM9X_REG_RxHeaderCntValueMsb = 0x14, 43 | RFM9X_REG_RxHeaderCntValueLsb = 0x15, 44 | RFM9X_REG_RxPacketCntValueMsb = 0x16, 45 | RFM9X_REG_RxPacketCntValueLsb = 0x17, 46 | RFM9X_REG_ModemStat = 0x18, 47 | RFM9X_REG_PktSnrValue = 0x19, 48 | RFM9X_REG_PktRssiValue = 0x1A, 49 | RFM9X_REG_RssiValue = 0x1B, 50 | RFM9X_REG_HopChannel = 0x1C, 51 | RFM9X_REG_ModemConfig1 = 0x1D, 52 | RFM9X_REG_ModemConfig2 = 0x1E, 53 | RFM9X_REG_SymbTimeoutLsb = 0x1F, 54 | RFM9X_REG_PreambleMsb = 0x20, 55 | RFM9X_REG_PreambleLsb = 0x21, 56 | RFM9X_REG_PayloadLength = 0x22, 57 | RFM9X_REG_MaxPayloadLength = 0x23, 58 | RFM9X_REG_HopPeriod = 0x24, 59 | RFM9X_REG_FifoRxByteAddr = 0x25, 60 | RFM9X_REG_ModemConfig3 = 0x26, 61 | RFM9X_REG_PpmCorrection = 0x27, 62 | RFM9X_REG_FeiMsb = 0x28, 63 | RFM9X_REG_FeiMid = 0x29, 64 | RFM9X_REG_FeiLsb = 0x2A, 65 | RFM9X_REG_RssiWideband = 0x2C, 66 | RFM9X_REG_IfFreq1 = 0x2F, 67 | RFM9X_REG_IfFreq2 = 0x30, 68 | RFM9X_REG_DetectOptimize = 0x31, 69 | RFM9X_REG_InvertIQ = 0x33, 70 | RFM9X_REG_HighBwOptimize1 = 0x36, 71 | RFM9X_REG_DetectionThreshold = 0x37, 72 | RFM9X_REG_SyncWord = 0x39, 73 | RFM9X_REG_HighBwOptimize2 = 0x3A, 74 | RFM9X_REG_InvertIQ2 = 0x3B, 75 | RFM9X_REG_DioMapping1 = 0x40, 76 | RFM9X_REG_DioMapping2 = 0x41, 77 | RFM9X_REG_Version = 0x42, 78 | RFM9X_REG_Tcxo = 0x4B, 79 | RFM9X_REG_PaDac = 0x4D, 80 | RFM9X_REG_FormerTemp = 0x5B, 81 | RFM9X_REG_AgcRef = 0x61, 82 | RFM9X_REG_AgcThresh1 = 0x62, 83 | RFM9X_REG_AgcThresh2 = 0x63, 84 | RFM9X_REG_AgcThresh3 = 0x64, 85 | RFM9X_REG_Pll = 0x70, 86 | RFM9X_REG_PA_DAC_LOW_POWER = 0x84, 87 | RFM9X_REG_PA_DAC_HIGH_POWER = 0x87, 88 | }; 89 | 90 | typedef struct 91 | { 92 | union { 93 | struct { 94 | uint8_t output_power : 4; 95 | uint8_t max_power : 3; 96 | uint8_t pa_select : 1; 97 | }; 98 | uint8_t buffer; 99 | }; 100 | } rfm95_pa_config_byte_t; 101 | 102 | #endif //RFM95_PRIVATE_DEFINES_H 103 | -------------------------------------------------------------------------------- /firmware/rfm95/tx-capture-1024B-irq.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/rfm95/tx-capture-1024B-irq.sal -------------------------------------------------------------------------------- /firmware/rpi-node/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Michael 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /firmware/rpi-node/README.md: -------------------------------------------------------------------------------- 1 | # rpi-gpio-latency-test 2 | 3 | Used NVM to install Node 20.11.0. 4 | Make sure corepack is enabled. 5 | 6 | `yarn install` to install everything. 7 | 8 | IO connections are against Pins 11 (input) and 15 (output). 9 | 10 | Change the websockets address in client.ts to match the server's IP and port. 11 | 12 | ## Simple test 13 | 14 | `yarn simple` to run the simple GPIO test. Signal on pin is processed and another pin goes high then low. 15 | 16 | ## WS 17 | 18 | Server / client receives the pin, sends the data via WS to the other, the other triggers pin high then low. 19 | 20 | `yarn server` to start the server. `yarn client` to start the client. 21 | 22 | # Test Variations 23 | 24 | I tested with ethernet and Wifi-only connections for comparison. -------------------------------------------------------------------------------- /firmware/rpi-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rpi-gpio-latency-test", 3 | "packageManager": "yarn@4.1.0", 4 | "devDependencies": { 5 | "@types/node": "^20.11.14", 6 | "@types/rpi-gpio": "^2.1.4", 7 | "@types/ws": "^6.0.4", 8 | "ts-node": "^10.9.2", 9 | "tslib": "^2.6.2", 10 | "typescript": "^5.3.3", 11 | "ws": "^6.2.2" 12 | }, 13 | "scripts": { 14 | "simple": "ts-node ./src/simple.ts", 15 | "server": "ts-node ./src/server.ts", 16 | "client": "ts-node ./src/client.ts" 17 | }, 18 | "dependencies": { 19 | "rpi-gpio": "^2.1.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /firmware/rpi-node/src/client.ts: -------------------------------------------------------------------------------- 1 | import gpio from 'rpi-gpio' 2 | import WebSocket from 'ws' 3 | 4 | import { payload } from './payload' 5 | 6 | const enum CONFIG { 7 | PIN_IN = 11, 8 | PIN_OUT = 15, 9 | } 10 | 11 | const URI = 'ws://www.host.com/path' 12 | 13 | function main() { 14 | console.log(`setting up... IN pin: ${CONFIG.PIN_IN}, OUT pin: ${CONFIG.PIN_OUT}`) 15 | 16 | // Setup IN 17 | gpio.setup(CONFIG.PIN_IN, gpio.DIR_IN, gpio.EDGE_RISING, err => { 18 | if (err !== null) { 19 | console.error(`Failed to setup IN:`, err) 20 | } 21 | }) 22 | 23 | // Setup OUT 24 | gpio.setup(CONFIG.PIN_OUT, gpio.DIR_OUT, err => { 25 | if (err !== null) { 26 | console.error(`Failed to setup OUT:`, err) 27 | } 28 | }) 29 | 30 | // Setup the pin function 31 | const writePinHighLow = () => { 32 | gpio.write(CONFIG.PIN_OUT, true, err => { 33 | if (err !== null) { 34 | console.error(`Failed to write HIGH:`, err) 35 | } 36 | 37 | // After that, write it low again 38 | gpio.write(CONFIG.PIN_OUT, false, err => { 39 | if (err !== null) { 40 | console.error(`Failed to write LOW:`, err) 41 | } 42 | 43 | // Do the console log now, after the latency sensitive bit 44 | // console.log(`wrote high then low`) 45 | }) 46 | }) 47 | } 48 | 49 | const ws = new WebSocket(URI) 50 | 51 | ws.on('error', console.error) 52 | 53 | ws.on('open', () => { 54 | console.log(`connected to ${URI}`) 55 | }) 56 | 57 | ws.on('close', () => { 58 | console.log(`connection closed`) 59 | }) 60 | 61 | // When the client receives messages 62 | ws.on('message', data => { 63 | let type = -1 64 | let buf = data as Buffer 65 | 66 | // Validate the packet matches 67 | if (Buffer.isBuffer(data)) { 68 | // this is the expected route 69 | type = 0 70 | } else if (Array.isArray(data) && data.every(buf => Buffer.isBuffer(buf))) { 71 | // The TS types say it can sometimes be an array of buffers? 72 | // concat them so we can do the comparison. 73 | buf = Buffer.concat(data) 74 | 75 | type = 1 76 | } else { 77 | console.log(`unknown data was returned by WS:`, data) 78 | 79 | return 80 | } 81 | 82 | if (Buffer.compare(buf, payload) === 0) { 83 | writePinHighLow() 84 | // console.log(`(received data type ${type}), matched expected payload`) 85 | } else { 86 | console.log(`received data type ${type} but data didn't match, received:`, data) 87 | } 88 | }) 89 | 90 | // Setup on change handler 91 | gpio.on('change', function (channel, value) { 92 | // Don't bother checking which channel it was, immediately write to WS 93 | 94 | ws.send(payload, err => { 95 | if (err !== null) { 96 | console.error(`Failed to send data:`, err) 97 | return 98 | } 99 | // console.log(`sent data over WS`) 100 | }) 101 | }) 102 | 103 | console.log(`ready`) 104 | } 105 | 106 | main() 107 | -------------------------------------------------------------------------------- /firmware/rpi-node/src/server.ts: -------------------------------------------------------------------------------- 1 | import gpio from 'rpi-gpio' 2 | import WebSocket from 'ws' 3 | 4 | import { payload } from './payload' 5 | 6 | const enum CONFIG { 7 | PIN_IN = 11, 8 | PIN_OUT = 15, 9 | } 10 | 11 | function main() { 12 | console.log(`setting up... IN pin: ${CONFIG.PIN_IN}, OUT pin: ${CONFIG.PIN_OUT}`) 13 | 14 | // Setup IN 15 | gpio.setup(CONFIG.PIN_IN, gpio.DIR_IN, gpio.EDGE_RISING, err => { 16 | if (err !== null) { 17 | console.error(`Failed to setup IN:`, err) 18 | } 19 | }) 20 | 21 | // Setup OUT 22 | gpio.setup(CONFIG.PIN_OUT, gpio.DIR_OUT, err => { 23 | if (err !== null) { 24 | console.error(`Failed to setup OUT:`, err) 25 | } 26 | }) 27 | 28 | // Setup the pin function 29 | const writePinHighLow = () => { 30 | gpio.write(CONFIG.PIN_OUT, true, err => { 31 | if (err !== null) { 32 | console.error(`Failed to write HIGH:`, err) 33 | } 34 | 35 | // After that, write it low again 36 | gpio.write(CONFIG.PIN_OUT, false, err => { 37 | if (err !== null) { 38 | console.error(`Failed to write LOW:`, err) 39 | } 40 | 41 | // Do the console log now, after the latency sensitive bit 42 | // console.log(`wrote high then low`) 43 | }) 44 | }) 45 | } 46 | 47 | // Setup WS 48 | const wss = new WebSocket.Server({ port: 8080 }) 49 | 50 | // Hold the latest client outside of the connection closure 51 | let client: WebSocket | null = null 52 | 53 | wss.on('connection', function connection(ws, req) { 54 | const ip = req.socket.remoteAddress 55 | console.log(`connection detected from ${ip}, setting as client`) 56 | 57 | // Set the client to the latest connection received 58 | client = ws 59 | 60 | ws.on('error', console.error) 61 | 62 | ws.on('close', () => { 63 | console.log(`connection closed`) 64 | client = null 65 | }) 66 | 67 | // When the server receives messages 68 | ws.on('message', data => { 69 | let type = -1 70 | let buf = data as Buffer 71 | 72 | // Validate the packet matches 73 | if (Buffer.isBuffer(data)) { 74 | // this is the expected route 75 | type = 0 76 | } else if (Array.isArray(data) && data.every(buf => Buffer.isBuffer(buf))) { 77 | // The TS types say it can sometimes be an array of buffers? 78 | // concat them so we can do the comparison. 79 | buf = Buffer.concat(data) 80 | 81 | type = 1 82 | } else { 83 | console.log(`unknown data was returned by WS:`, data) 84 | 85 | return 86 | } 87 | 88 | if (Buffer.compare(buf, payload) === 0) { 89 | writePinHighLow() 90 | // console.log(`(received data type ${type}), matched expected payload`) 91 | } else { 92 | console.log(`received data type ${type} but data didn't match, received:`, data) 93 | } 94 | }) 95 | }) 96 | 97 | // Setup on change handler 98 | gpio.on('change', function (channel, value) { 99 | // Don't bother checking which channel it was, immediately write to WS 100 | 101 | if (client) { 102 | client.send(payload, err => { 103 | if (err !== null) { 104 | console.error(`Failed to send data:`, err) 105 | return 106 | } 107 | // console.log(`sent data over WS`) 108 | }) 109 | } else { 110 | console.log(`received GPIO but no WS client has connected`) 111 | } 112 | }) 113 | 114 | console.log(`ready`) 115 | } 116 | 117 | main() 118 | -------------------------------------------------------------------------------- /firmware/rpi-node/src/simple.ts: -------------------------------------------------------------------------------- 1 | import gpio from 'rpi-gpio' 2 | 3 | // pins: 17/27/22 4 | // Unsure if this is the correct mapping for rpi-gpio - it's not 5 | // https://elinux.org/RPi_Low-level_peripherals 6 | 7 | const enum CONFIG { 8 | PIN_IN = 11, 9 | PIN_OUT = 15, 10 | } 11 | 12 | function main() { 13 | console.log(`setting up... IN pin: ${CONFIG.PIN_IN}, OUT pin: ${CONFIG.PIN_OUT}`) 14 | 15 | // Setup IN 16 | gpio.setup(CONFIG.PIN_IN, gpio.DIR_IN, gpio.EDGE_RISING, err => { 17 | if (err !== null) { 18 | console.error(`Failed to setup IN:`, err) 19 | } 20 | }) 21 | 22 | // Setup OUT 23 | gpio.setup(CONFIG.PIN_OUT, gpio.DIR_OUT, err => { 24 | if (err !== null) { 25 | console.error(`Failed to setup OUT:`, err) 26 | } 27 | }) 28 | 29 | // Setup on change handler 30 | gpio.on('change', function (channel, value) { 31 | // Don't bother checking which channel it was, immediately write 32 | gpio.write(CONFIG.PIN_OUT, true, err => { 33 | if (err !== null) { 34 | console.error(`Failed to write HIGH:`, err) 35 | } 36 | 37 | // After that, write it low again 38 | gpio.write(CONFIG.PIN_OUT, false, err => { 39 | if (err !== null) { 40 | console.error(`Failed to write LOW:`, err) 41 | } 42 | 43 | // Do the console log now, after the latency sensitive bit 44 | // console.log(`channel ${channel}: ${value}, wrote high then low.`) 45 | }) 46 | }) 47 | }) 48 | 49 | console.log(`ready`) 50 | } 51 | 52 | main() 53 | -------------------------------------------------------------------------------- /firmware/sik/README.md: -------------------------------------------------------------------------------- 1 | # SiK Radio Tests 2 | 3 | The firmware used for these tests was the same as the UART test firmware. 4 | 5 | Radio modules were configured with 64k air rate, and 57600 baud wired serial rate. 6 | 7 | -------------------------------------------------------------------------------- /firmware/sik/packet-size-logs/1024B-500ms-trigger.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/sik/packet-size-logs/1024B-500ms-trigger.sal -------------------------------------------------------------------------------- /firmware/sik/packet-size-logs/128B-250ms-trigger.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/sik/packet-size-logs/128B-250ms-trigger.sal -------------------------------------------------------------------------------- /firmware/sik/packet-size-logs/12B-250ms-trigger.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/sik/packet-size-logs/12B-250ms-trigger.sal -------------------------------------------------------------------------------- /firmware/sik/trigger-logs/12B-250ms-trigger.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/sik/trigger-logs/12B-250ms-trigger.sal -------------------------------------------------------------------------------- /firmware/sik/trigger-logs/12B-rf-trigger-delayed-113ms.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/sik/trigger-logs/12B-rf-trigger-delayed-113ms.sal -------------------------------------------------------------------------------- /firmware/sik/trigger-logs/12B-rf-trigger.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/sik/trigger-logs/12B-rf-trigger.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/115200-dma.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/115200-dma.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/115200-irq.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/115200-irq.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/115200-poll.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/115200-poll.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/1843200-dma.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/1843200-dma.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/1843200-poll.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/1843200-poll.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/230400-dma.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/230400-dma.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/230400-poll.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/230400-poll.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/460800-dma.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/460800-dma.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/460800-poll.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/460800-poll.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/921600-dma.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/921600-dma.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/921600-irq.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/921600-irq.sal -------------------------------------------------------------------------------- /firmware/uart_tests/baudrate-12B-logs/921600-poll.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/baudrate-12B-logs/921600-poll.sal -------------------------------------------------------------------------------- /firmware/uart_tests/packet-size-tests/115200-1024B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/packet-size-tests/115200-1024B.sal -------------------------------------------------------------------------------- /firmware/uart_tests/packet-size-tests/115200-128B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/packet-size-tests/115200-128B.sal -------------------------------------------------------------------------------- /firmware/uart_tests/packet-size-tests/115200-12B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/packet-size-tests/115200-12B.sal -------------------------------------------------------------------------------- /firmware/uart_tests/packet-size-tests/912600-128B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/packet-size-tests/912600-128B.sal -------------------------------------------------------------------------------- /firmware/uart_tests/packet-size-tests/912600-12B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/packet-size-tests/912600-12B.sal -------------------------------------------------------------------------------- /firmware/uart_tests/packet-size-tests/921600-1024B.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/packet-size-tests/921600-1024B.sal -------------------------------------------------------------------------------- /firmware/uart_tests/peripheral-configuration-12B-logs/uart-dma-loopback-release.sal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/firmware/uart_tests/peripheral-configuration-12B-logs/uart-dma-loopback-release.sal -------------------------------------------------------------------------------- /firmware/uart_tests/stm-ll/.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build* 2 | stm32-cmake/* -------------------------------------------------------------------------------- /firmware/uart_tests/stm-ll/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | set(PROJ_NAME uart-test) 3 | set(STM32_CMAKE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/stm32-cmake) 4 | set(CMAKE_TOOLCHAIN_FILE ${STM32_CMAKE_PATH}/cmake/stm32_gcc.cmake) 5 | 6 | #SET(CMAKE_CXX_FLAGS "-O1") 7 | #SET(CMAKE_C_FLAGS "-O1") 8 | 9 | project(${PROJ_NAME} C ASM) 10 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 11 | 12 | stm32_fetch_cmsis(F4) 13 | stm32_fetch_hal(F4) 14 | 15 | find_package(CMSIS COMPONENTS STM32F4 REQUIRED) 16 | find_package(HAL 17 | COMPONENTS 18 | STM32F4 19 | # LL_RCC 20 | # LL_PWR 21 | # LL_UTILS 22 | # LL_GPIO 23 | # LL_EXTI 24 | REQUIRED) 25 | 26 | add_definitions(-DUSE_FULL_LL_DRIVER) 27 | 28 | # Clock speed definitions for custom hardware 29 | add_definitions(-DHSE_VALUE=8000000) 30 | add_definitions(-DLSE_VALUE=32768) 31 | 32 | #add_definitions(-DUART_POLL) 33 | add_definitions(-DUART_IRQ) 34 | #add_definitions(-DUART_DMA) 35 | 36 | #add_definitions(-DPAYLOAD_12B) 37 | #add_definitions(-DPAYLOAD_128B) 38 | add_definitions(-DPAYLOAD_1024B) 39 | 40 | 41 | add_executable(${PROJ_NAME}) 42 | 43 | target_sources( 44 | ${PROJ_NAME} 45 | PRIVATE 46 | ${CMAKE_SOURCE_DIR}/src/main.c 47 | ${CMAKE_SOURCE_DIR}/src/uart.c 48 | ${CMAKE_SOURCE_DIR}/src/fifo.c 49 | ) 50 | 51 | target_include_directories( 52 | ${PROJ_NAME} 53 | PUBLIC 54 | ${CMAKE_CURRENT_SOURCE_DIR} 55 | ) 56 | 57 | target_link_libraries( 58 | ${PROJ_NAME} 59 | PRIVATE 60 | HAL::STM32::F4::LL_RCC 61 | HAL::STM32::F4::LL_PWR 62 | HAL::STM32::F4::LL_UTILS 63 | HAL::STM32::F4::LL_GPIO 64 | HAL::STM32::F4::LL_EXTI 65 | HAL::STM32::F4::LL_USART 66 | HAL::STM32::F4::LL_DMA 67 | CMSIS::STM32::F429xx 68 | STM32::NoSys 69 | ) 70 | 71 | target_link_options(${PROJ_NAME} PRIVATE -Wl,--print-memory-usage) 72 | stm32_add_linker_script(${PROJ_NAME} PRIVATE F429ZITx.ld) 73 | 74 | stm32_generate_binary_file(${PROJ_NAME}) 75 | stm32_generate_hex_file(${PROJ_NAME}) -------------------------------------------------------------------------------- /firmware/uart_tests/stm-ll/F429VETx.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler) 2 | 3 | /* Highest address of the user mode stack */ 4 | _estack = 0x20000000 + 192K; /* end of RAM */ 5 | _Min_Heap_Size = 0x200; 6 | _Min_Stack_Size = 0x400; 7 | 8 | /* Specify the memory areas */ 9 | MEMORY 10 | { 11 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K 12 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 13 | CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K 14 | } 15 | 16 | /* See page 78 of ST doc RM0090 for single bank flash memory layout. */ 17 | /* Layout in sequential sectors is as follows: 16k, 16k, 16k, 16k, 64k, 128k, 128k, 128k */ 18 | /* FLASH_BOOT (rx) : ORIGIN = 0x08000000, LENGTH = 32K */ 19 | /* EMULATED_EEPROM (rwx) : ORIGIN = 0x08008000, LENGTH = 32K */ 20 | 21 | SECTIONS 22 | { 23 | /* The startup code goes first into FLASH */ 24 | .isr_vector : 25 | { 26 | . = ALIGN(4); 27 | KEEP(*(.isr_vector)) /* Startup code */ 28 | . = ALIGN(4); 29 | } >FLASH 30 | 31 | /* The program code and other data goes into FLASH */ 32 | .text : 33 | { 34 | . = ALIGN(4); 35 | *(.text) /* .text sections (code) */ 36 | *(.text*) /* .text* sections (code) */ 37 | *(.glue_7) /* glue arm to thumb code */ 38 | *(.glue_7t) /* glue thumb to arm code */ 39 | *(.eh_frame) 40 | 41 | KEEP (*(.init)) 42 | KEEP (*(.fini)) 43 | 44 | . = ALIGN(4); 45 | _etext = .; /* define a global symbols at end of code */ 46 | } >FLASH 47 | 48 | /* Constant data goes into FLASH */ 49 | .rodata : 50 | { 51 | . = ALIGN(4); 52 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 53 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 54 | . = ALIGN(4); 55 | } >FLASH 56 | 57 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH 58 | .ARM : { 59 | __exidx_start = .; 60 | *(.ARM.exidx*) 61 | __exidx_end = .; 62 | } >FLASH 63 | 64 | .preinit_array : 65 | { 66 | PROVIDE_HIDDEN (__preinit_array_start = .); 67 | KEEP (*(.preinit_array*)) 68 | PROVIDE_HIDDEN (__preinit_array_end = .); 69 | } >FLASH 70 | .init_array : 71 | { 72 | PROVIDE_HIDDEN (__init_array_start = .); 73 | KEEP (*(SORT(.init_array.*))) 74 | KEEP (*(.init_array*)) 75 | PROVIDE_HIDDEN (__init_array_end = .); 76 | } >FLASH 77 | .fini_array : 78 | { 79 | PROVIDE_HIDDEN (__fini_array_start = .); 80 | KEEP (*(SORT(.fini_array.*))) 81 | KEEP (*(.fini_array*)) 82 | PROVIDE_HIDDEN (__fini_array_end = .); 83 | } >FLASH 84 | 85 | /* used by the startup to initialize data */ 86 | _sidata = LOADADDR(.data); 87 | 88 | /* Initialized data sections goes into RAM, load LMA copy after code */ 89 | .data : 90 | { 91 | . = ALIGN(4); 92 | _sdata = .; /* create a global symbol at data start */ 93 | *(.data) /* .data sections */ 94 | *(.data*) /* .data* sections */ 95 | 96 | . = ALIGN(4); 97 | _edata = .; /* define a global symbol at data end */ 98 | } >RAM AT> FLASH 99 | 100 | _siccmram = LOADADDR(.ccmram); 101 | 102 | /* CCM-RAM section 103 | * 104 | * IMPORTANT NOTE! 105 | * If initialized variables will be placed in this section, 106 | * the startup code needs to be modified to copy the init-values. 107 | */ 108 | .ccmram (NOLOAD): 109 | { 110 | . = ALIGN(4); 111 | _sccmram = .; /* create a global symbol at ccmram start */ 112 | *(.ccmram) 113 | *(.ccmram*) 114 | 115 | . = ALIGN(4); 116 | _eccmram = .; /* create a global symbol at ccmram end */ 117 | } >CCMRAM AT> FLASH 118 | 119 | 120 | /* Uninitialized data section */ 121 | . = ALIGN(4); 122 | .bss : 123 | { 124 | /* This is used by the startup in order to initialize the .bss secion */ 125 | _sbss = .; /* define a global symbol at bss start */ 126 | __bss_start__ = _sbss; 127 | *(.bss) 128 | *(.bss*) 129 | *(COMMON) 130 | 131 | . = ALIGN(4); 132 | _ebss = .; /* define a global symbol at bss end */ 133 | __bss_end__ = _ebss; 134 | } >RAM 135 | 136 | /* User_heap_stack section, used to check that there is enough RAM left */ 137 | ._user_heap_stack : 138 | { 139 | . = ALIGN(8); 140 | PROVIDE ( end = . ); 141 | PROVIDE ( _end = . ); 142 | . = . + _Min_Heap_Size; 143 | . = . + _Min_Stack_Size; 144 | . = ALIGN(8); 145 | } >RAM 146 | 147 | 148 | 149 | /* Remove information from the standard libraries */ 150 | /DISCARD/ : 151 | { 152 | libc.a ( * ) 153 | libm.a ( * ) 154 | libgcc.a ( * ) 155 | } 156 | 157 | .ARM.attributes 0 : { *(.ARM.attributes) } 158 | } -------------------------------------------------------------------------------- /firmware/uart_tests/stm-ll/F429ZITx.ld: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | ** 4 | ** @file : LinkerScript.ld 5 | ** 6 | ** @author : Auto-generated by STM32CubeIDE 7 | ** 8 | ** Abstract : Linker script for NUCLEO-F429ZI Board embedding STM32F429ZITx Device from stm32f4 series 9 | ** 2048Kbytes FLASH 10 | ** 64Kbytes CCMRAM 11 | ** 192Kbytes RAM 12 | ** 13 | ** Set heap size, stack size and stack location according 14 | ** to application requirements. 15 | ** 16 | ** Set memory bank area and size if external memory is used 17 | ** 18 | ** Target : STMicroelectronics STM32 19 | ** 20 | ** Distribution: The file is distributed as is, without any warranty 21 | ** of any kind. 22 | ** 23 | ****************************************************************************** 24 | ** @attention 25 | ** 26 | ** Copyright (c) 2022 STMicroelectronics. 27 | ** All rights reserved. 28 | ** 29 | ** This software is licensed under terms that can be found in the LICENSE file 30 | ** in the root directory of this software component. 31 | ** If no LICENSE file comes with this software, it is provided AS-IS. 32 | ** 33 | ****************************************************************************** 34 | */ 35 | 36 | /* Entry Point */ 37 | ENTRY(Reset_Handler) 38 | 39 | /* Highest address of the user mode stack */ 40 | _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ 41 | 42 | _Min_Heap_Size = 0x200 ; /* required amount of heap */ 43 | _Min_Stack_Size = 0x400 ; /* required amount of stack */ 44 | 45 | /* Memories definition */ 46 | MEMORY 47 | { 48 | CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K 49 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K 50 | FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K 51 | } 52 | 53 | /* Sections */ 54 | SECTIONS 55 | { 56 | /* The startup code into "FLASH" Rom type memory */ 57 | .isr_vector : 58 | { 59 | . = ALIGN(4); 60 | KEEP(*(.isr_vector)) /* Startup code */ 61 | . = ALIGN(4); 62 | } >FLASH 63 | 64 | /* The program code and other data into "FLASH" Rom type memory */ 65 | .text : 66 | { 67 | . = ALIGN(4); 68 | *(.text) /* .text sections (code) */ 69 | *(.text*) /* .text* sections (code) */ 70 | *(.glue_7) /* glue arm to thumb code */ 71 | *(.glue_7t) /* glue thumb to arm code */ 72 | *(.eh_frame) 73 | 74 | KEEP (*(.init)) 75 | KEEP (*(.fini)) 76 | 77 | . = ALIGN(4); 78 | _etext = .; /* define a global symbols at end of code */ 79 | } >FLASH 80 | 81 | /* Constant data into "FLASH" Rom type memory */ 82 | .rodata : 83 | { 84 | . = ALIGN(4); 85 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 86 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 87 | . = ALIGN(4); 88 | } >FLASH 89 | 90 | .ARM.extab : { 91 | . = ALIGN(4); 92 | *(.ARM.extab* .gnu.linkonce.armextab.*) 93 | . = ALIGN(4); 94 | } >FLASH 95 | 96 | .ARM : { 97 | . = ALIGN(4); 98 | __exidx_start = .; 99 | *(.ARM.exidx*) 100 | __exidx_end = .; 101 | . = ALIGN(4); 102 | } >FLASH 103 | 104 | .preinit_array : 105 | { 106 | . = ALIGN(4); 107 | PROVIDE_HIDDEN (__preinit_array_start = .); 108 | KEEP (*(.preinit_array*)) 109 | PROVIDE_HIDDEN (__preinit_array_end = .); 110 | . = ALIGN(4); 111 | } >FLASH 112 | 113 | .init_array : 114 | { 115 | . = ALIGN(4); 116 | PROVIDE_HIDDEN (__init_array_start = .); 117 | KEEP (*(SORT(.init_array.*))) 118 | KEEP (*(.init_array*)) 119 | PROVIDE_HIDDEN (__init_array_end = .); 120 | . = ALIGN(4); 121 | } >FLASH 122 | 123 | .fini_array : 124 | { 125 | . = ALIGN(4); 126 | PROVIDE_HIDDEN (__fini_array_start = .); 127 | KEEP (*(SORT(.fini_array.*))) 128 | KEEP (*(.fini_array*)) 129 | PROVIDE_HIDDEN (__fini_array_end = .); 130 | . = ALIGN(4); 131 | } >FLASH 132 | 133 | /* Used by the startup to initialize data */ 134 | _sidata = LOADADDR(.data); 135 | 136 | /* Initialized data sections into "RAM" Ram type memory */ 137 | .data : 138 | { 139 | . = ALIGN(4); 140 | _sdata = .; /* create a global symbol at data start */ 141 | *(.data) /* .data sections */ 142 | *(.data*) /* .data* sections */ 143 | *(.RamFunc) /* .RamFunc sections */ 144 | *(.RamFunc*) /* .RamFunc* sections */ 145 | 146 | . = ALIGN(4); 147 | _edata = .; /* define a global symbol at data end */ 148 | 149 | } >RAM AT> FLASH 150 | 151 | _siccmram = LOADADDR(.ccmram); 152 | 153 | /* CCM-RAM section 154 | * 155 | * IMPORTANT NOTE! 156 | * If initialized variables will be placed in this section, 157 | * the startup code needs to be modified to copy the init-values. 158 | */ 159 | .ccmram : 160 | { 161 | . = ALIGN(4); 162 | _sccmram = .; /* create a global symbol at ccmram start */ 163 | *(.ccmram) 164 | *(.ccmram*) 165 | 166 | . = ALIGN(4); 167 | _eccmram = .; /* create a global symbol at ccmram end */ 168 | } >CCMRAM AT> FLASH 169 | 170 | /* Uninitialized data section into "RAM" Ram type memory */ 171 | . = ALIGN(4); 172 | .bss : 173 | { 174 | /* This is used by the startup in order to initialize the .bss section */ 175 | _sbss = .; /* define a global symbol at bss start */ 176 | __bss_start__ = _sbss; 177 | *(.bss) 178 | *(.bss*) 179 | *(COMMON) 180 | 181 | . = ALIGN(4); 182 | _ebss = .; /* define a global symbol at bss end */ 183 | __bss_end__ = _ebss; 184 | } >RAM 185 | 186 | /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ 187 | ._user_heap_stack : 188 | { 189 | . = ALIGN(8); 190 | PROVIDE ( end = . ); 191 | PROVIDE ( _end = . ); 192 | . = . + _Min_Heap_Size; 193 | . = . + _Min_Stack_Size; 194 | . = ALIGN(8); 195 | } >RAM 196 | 197 | /* Remove information from the compiler libraries */ 198 | /DISCARD/ : 199 | { 200 | libc.a ( * ) 201 | libm.a ( * ) 202 | libgcc.a ( * ) 203 | } 204 | 205 | .ARM.attributes 0 : { *(.ARM.attributes) } 206 | } 207 | -------------------------------------------------------------------------------- /firmware/uart_tests/stm-ll/README.md: -------------------------------------------------------------------------------- 1 | # STM32F429 LL UART Timing Tests 2 | 3 | Minimal test firmware to compare polled, interrupt and DMA backed UART transfer implementations. 4 | 5 | This is only meant to serve to highlight differences in handling/latency and not: 6 | 7 | - peripheral handling efficiency 8 | - efficiency (both power, and in code) 9 | - performance with higher baud links 10 | 11 | IO Setup: 12 | 13 | - PA0 is driven with a 3.3V trigger pulse from my sig-gen. 14 | - PB0 is a 3.3V output signal (also connected to the nucleo's onboard green LED) 15 | - UART5 is used with PD2 as RX, and PC12 as TX 16 | 17 | ## Deps 18 | 19 | I use CMake with CLion for development/builds, so this project is slightly opinionated. 20 | 21 | Requires [`stm32-cmake`](https://github.com/ObKo/stm32-cmake) in this folder to get the build system setup etc. 22 | 23 | I set my CMake options to use my system's arm gcc toolchain `-DSTM32_TOOLCHAIN_PATH=/usr/bin/ -DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/arm-none-eabi-g++ -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/arm-none-eabi-gcc`. 24 | 25 | With CLion's build settings, I set the build generator to "Let CMake Decide". -------------------------------------------------------------------------------- /firmware/uart_tests/stm-ll/src/fifo.c: -------------------------------------------------------------------------------- 1 | /* ----- System Includes ---------------------------------------------------- */ 2 | 3 | /* ----- Local Includes ----------------------------------------------------- */ 4 | 5 | #include "fifo.h" 6 | 7 | /* ----- Private Prototypes ------------------------------------------------- */ 8 | 9 | static uint32_t fifo_next(fifo_t *restrict f, uint32_t index); 10 | 11 | /* ----- Public Functions --------------------------------------------------- */ 12 | 13 | /** This initializes the FIFO structure with the given buffer and size */ 14 | void fifo_init(fifo_t *restrict f, uint8_t *buf, uint32_t buf_size) { 15 | f->head = 0; 16 | f->tail = 0; 17 | f->capacity = buf_size; 18 | f->buf = buf; 19 | } 20 | 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | /** Returns the amount of data in use in the fifo. 24 | * This can be at max the capacity less 1 25 | */ 26 | uint32_t fifo_size(fifo_t *restrict f) { 27 | return f->capacity - 1; 28 | } 29 | 30 | /* -------------------------------------------------------------------------- */ 31 | 32 | /** Returns the amount of data in use in the fifo. 33 | * This can be at max the capacity less 1 34 | */ 35 | uint32_t fifo_used(fifo_t *restrict f) { 36 | if (f->head != f->tail) { 37 | if (f->head > f->tail) { 38 | return f->head - f->tail; 39 | } else { 40 | return f->head - f->tail + f->capacity; 41 | } 42 | } 43 | return 0; 44 | } 45 | 46 | /* -------------------------------------------------------------------------- */ 47 | 48 | /** Returns the amount of data in a sequential run in the fifo 49 | * i.e doesn't count bytes which cross over the overflow boundary 50 | * This can be at max the capacity less 1 51 | */ 52 | uint32_t fifo_used_linear(fifo_t *restrict f) { 53 | if (f->head != f->tail) { 54 | if (f->head > f->tail) { 55 | return f->head - f->tail; 56 | } else { 57 | return f->capacity - f->tail; // from tail to end of buffer 58 | } 59 | } 60 | return 0; 61 | } 62 | 63 | /* -------------------------------------------------------------------------- */ 64 | 65 | /** Returns the amount of data free in the fifo. */ 66 | uint32_t fifo_free(fifo_t *restrict f) { 67 | return fifo_size(f) - fifo_used(f); 68 | } 69 | 70 | /* -------------------------------------------------------------------------- */ 71 | 72 | /** Write a byte to the FIFO. Return true when OK */ 73 | bool fifo_put(fifo_t *restrict f, const uint8_t ch) { 74 | const uint32_t new_head = fifo_next(f, f->head); 75 | if (new_head != f->tail) { 76 | f->buf[f->head] = ch; 77 | f->head = new_head; 78 | return true; /* successfully added to queue */ 79 | } 80 | return false; //no more room 81 | } 82 | 83 | /* -------------------------------------------------------------------------- */ 84 | 85 | /** Get a byte from the FIFO. Return NULL when empty */ 86 | uint8_t * 87 | fifo_get(fifo_t *restrict f) { 88 | if (f->tail != f->head) { 89 | uint8_t *ch = &f->buf[f->tail]; 90 | f->tail = fifo_next(f, f->tail); 91 | return ch; 92 | } 93 | return 0; 94 | } 95 | 96 | /* -------------------------------------------------------------------------- */ 97 | 98 | /** Peek a byte from the FIFO. Return NULL when empty */ 99 | uint8_t * 100 | fifo_peek(fifo_t *restrict f) { 101 | if (f->tail != f->head) { 102 | uint8_t *ch = &f->buf[f->tail]; 103 | return ch; 104 | } 105 | return 0; 106 | } 107 | 108 | /* -------------------------------------------------------------------------- */ 109 | 110 | /** Writes up to nbytes bytes to the FIFO 111 | * If the head runs in to the tail, not all bytes are written 112 | * The number of bytes written is returned 113 | */ 114 | uint32_t fifo_write(fifo_t *restrict f, const uint8_t *buf, uint32_t nbytes) { 115 | uint32_t count = 0; 116 | for (uint32_t i = 0; i < nbytes; i++) { 117 | if (fifo_put(f, buf[i])) { 118 | count++; 119 | } else { 120 | break; 121 | } 122 | } 123 | return count; 124 | } 125 | 126 | /* -------------------------------------------------------------------------- */ 127 | 128 | /** Reads nbytes bytes from the FIFO. The number of bytes read is returned */ 129 | uint32_t fifo_read(fifo_t *restrict f, uint8_t *buf, uint32_t nbytes) { 130 | uint32_t count = 0; 131 | for (uint32_t i = 0; i < nbytes; i++) { 132 | uint8_t *p = fifo_get(f); 133 | if (p) { 134 | buf[i] = *p; 135 | count++; 136 | } else { 137 | break; 138 | } 139 | } 140 | return count; 141 | } 142 | 143 | /* -------------------------------------------------------------------------- */ 144 | 145 | /** Returns a pointer to the tail position. If the nbytes length is illegal, returns null */ 146 | uint32_t * fifo_get_tail_ptr(fifo_t *restrict f, uint32_t nbytes) 147 | { 148 | uint32_t *ptr = 0; 149 | 150 | if (nbytes <= fifo_used_linear(f)) 151 | { 152 | ptr = &f->buf[f->tail]; 153 | } 154 | 155 | return ptr; 156 | } 157 | 158 | /* -------------------------------------------------------------------------- */ 159 | 160 | /** Moves/flushes the tail forward by nbytes */ 161 | uint32_t fifo_skip(fifo_t *restrict f, uint32_t nbytes) 162 | { 163 | if (nbytes <= fifo_used_linear(f)) 164 | { 165 | f->tail += nbytes; 166 | 167 | if (f->tail >= f->capacity) 168 | { 169 | f->tail = 0; 170 | } 171 | 172 | return nbytes; 173 | } 174 | 175 | return 0; 176 | } 177 | 178 | /* ----- Private Functions -------------------------------------------------- */ 179 | 180 | static uint32_t fifo_next(fifo_t *restrict f, uint32_t index) 181 | { 182 | uint32_t current = index; 183 | current++; 184 | 185 | if (current >= f->capacity) 186 | { 187 | current = 0; 188 | } 189 | 190 | return current; 191 | } 192 | 193 | /* ----- End ---------------------------------------------------------------- */ -------------------------------------------------------------------------------- /firmware/uart_tests/stm-ll/src/fifo.h: -------------------------------------------------------------------------------- 1 | #ifndef FIFO_H 2 | #define FIFO_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* ----- System Includes ---------------------------------------------------- */ 9 | 10 | #include 11 | #include 12 | 13 | /* ----- Local Includes ----------------------------------------------------- */ 14 | 15 | /* ----- Types -------------------------------------------------------------- */ 16 | 17 | typedef struct 18 | { 19 | uint8_t * buf; 20 | uint32_t head; 21 | uint32_t tail; 22 | uint32_t capacity; 23 | } fifo_t; 24 | 25 | /* ----- Public Functions --------------------------------------------------- */ 26 | 27 | /** This initializes the FIFO structure with the given buffer and size */ 28 | 29 | void fifo_init( fifo_t * restrict f, uint8_t * buf, uint32_t buf_size ); 30 | 31 | /* -------------------------------------------------------------------------- */ 32 | 33 | /** Returns the capacity of the fifo. */ 34 | 35 | uint32_t fifo_size( fifo_t * restrict f ); 36 | 37 | /* -------------------------------------------------------------------------- */ 38 | 39 | /** Returns the amount of data used in the fifo. */ 40 | 41 | uint32_t fifo_used( fifo_t * restrict f ); 42 | 43 | /* -------------------------------------------------------------------------- */ 44 | 45 | /** Returns the amount of data in the fifo _in_ a sequential block of memory. */ 46 | 47 | uint32_t fifo_used_linear( fifo_t * restrict f ); 48 | 49 | /* -------------------------------------------------------------------------- */ 50 | 51 | /** Returns the amount of free space in the fifo. */ 52 | 53 | uint32_t fifo_free( fifo_t * restrict f ); 54 | 55 | /* -------------------------------------------------------------------------- */ 56 | 57 | /** Write a byte to the FIFO. Return true when OK */ 58 | 59 | bool fifo_put( fifo_t * restrict f, const uint8_t ch ); 60 | 61 | /* -------------------------------------------------------------------------- */ 62 | 63 | /** Get a byte from the FIFO. Return NULL when empty */ 64 | 65 | uint8_t * fifo_get( fifo_t * restrict f ); 66 | 67 | /* -------------------------------------------------------------------------- */ 68 | 69 | /** Peek a byte from the FIFO. Return NULL when empty */ 70 | 71 | uint8_t * fifo_peek( fifo_t * restrict f ); 72 | 73 | /* -------------------------------------------------------------------------- */ 74 | 75 | /** Writes up to nbytes bytes to the FIFO 76 | * If the head runs in to the tail, not all bytes are written 77 | * The number of bytes written is returned 78 | */ 79 | 80 | uint32_t fifo_write( fifo_t * restrict f, const uint8_t * buf, uint32_t nbytes ); 81 | 82 | /* -------------------------------------------------------------------------- */ 83 | 84 | /** Reads nbytes bytes from the FIFO. The number of bytes read is returned */ 85 | 86 | uint32_t fifo_read( fifo_t * restrict f, uint8_t * buf, uint32_t nbytes ); 87 | 88 | /* -------------------------------------------------------------------------- */ 89 | 90 | /** Get the pointer to the tail, if the proposed nbytes is legal 91 | * Only use this function while promising that underlying data isn't mutated 92 | */ 93 | 94 | uint32_t * fifo_get_tail_ptr( fifo_t * restrict f, uint32_t nbytes ); 95 | 96 | /* -------------------------------------------------------------------------- */ 97 | 98 | /** Moves the tail forwards by nbytes, functionally skipping data (partial flush) 99 | * Returns number bytes flushed 100 | */ 101 | 102 | uint32_t fifo_skip( fifo_t * restrict f, uint32_t nbytes ); 103 | 104 | /* ----- End ---------------------------------------------------------------- */ 105 | 106 | #ifdef __cplusplus 107 | } 108 | #endif 109 | #endif /* FIFO_H */ -------------------------------------------------------------------------------- /firmware/uart_tests/stm-ll/src/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef UART_H 2 | #define UART_H 3 | 4 | void uart_init( void ); 5 | 6 | /* Non-blocking send for a number of characters to the UART tx FIFO queue. 7 | * Returns true when successful. false when queue was full. 8 | */ 9 | uint32_t hal_uart_write( const uint8_t *data, uint32_t length ); 10 | 11 | /* -------------------------------------------------------------------------- */ 12 | 13 | /* Returns number of available characters in the RX FIFO queue. */ 14 | uint32_t hal_uart_rx_data_available( void ); 15 | 16 | /* -------------------------------------------------------------------------- */ 17 | 18 | /* Retrieve a single byte from the rx FIFO queue. 19 | * Returns 0 when no data is available or when rx callback is in use. 20 | */ 21 | uint8_t hal_uart_rx_get( void ); 22 | 23 | /* -------------------------------------------------------------------------- */ 24 | 25 | /* Retrieve a number of bytes from the rx FIFO queue up to 26 | * buffer length. Returns the number of bytes actually read. 27 | */ 28 | uint32_t hal_uart_read( uint8_t *buffer, uint32_t bufferlen ); 29 | 30 | /* -------------------------------------------------------------------------- */ 31 | 32 | #endif //UART_H 33 | -------------------------------------------------------------------------------- /screenshots/logic-on-table.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/screenshots/logic-on-table.jpg -------------------------------------------------------------------------------- /screenshots/lora-chirp-sa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/screenshots/lora-chirp-sa.png -------------------------------------------------------------------------------- /screenshots/module-assortment.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/screenshots/module-assortment.jpg -------------------------------------------------------------------------------- /screenshots/sik-channels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/screenshots/sik-channels.png -------------------------------------------------------------------------------- /screenshots/sik-hopping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/screenshots/sik-hopping.png -------------------------------------------------------------------------------- /screenshots/sik-pvt-packet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scottapotamas/embedded-wireless-latency-eval/a4c16a10af1ed971e7e7b7b7a367f38b6bdbd577/screenshots/sik-pvt-packet.png --------------------------------------------------------------------------------