{"id":20520907,"url":"https://github.com/manuelbl/sx127x-probe","last_synced_at":"2025-04-14T02:33:24.054Z","repository":{"id":92264797,"uuid":"220855447","full_name":"manuelbl/sx127x-probe","owner":"manuelbl","description":"LMIC LoRa Timing Analysis","archived":false,"fork":false,"pushed_at":"2020-02-22T11:22:40.000Z","size":380,"stargazers_count":8,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-27T16:39:59.424Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/manuelbl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-10T21:44:40.000Z","updated_at":"2025-01-17T09:09:38.000Z","dependencies_parsed_at":"2023-06-08T05:15:45.230Z","dependency_job_id":null,"html_url":"https://github.com/manuelbl/sx127x-probe","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelbl%2Fsx127x-probe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelbl%2Fsx127x-probe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelbl%2Fsx127x-probe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manuelbl%2Fsx127x-probe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/manuelbl","download_url":"https://codeload.github.com/manuelbl/sx127x-probe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248811031,"owners_count":21165213,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T22:24:19.649Z","updated_at":"2025-04-14T02:33:24.041Z","avatar_url":"https://github.com/manuelbl.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SX127x Probe – STM32F1x software to monitor the LoRa timings of a Semtech SX127x chip\n\nSoftware to monitor the communictaion with a Semtech SX127x chip to verify the firmware such a LMIC achieves correct LoRa timings. The software is run on a STM32F1x board (e.g. Blue Pill or Black Pill) and connected to  several pins of the LoRa board. It monitors the SPI communication and the interrupt pins, measures the timing and analyzes whether they conform to the LoRa standard.\n\n\n## Connections\n\n### Connections between probe (STM32 board) and SX127x board\n\n| SX127x     | Probe     |\n| ---------- | --------- |\n| GND        | GND       |\n| NSS / CS   | PB12      |\n| SCLK       | PB13      |\n| MOSI       | PB15      |\n| DIO0       | PB3       |\n| DIO1       | PB4       |\n\nWith the exception of GND, all connections are configured as inputs with no pull up/down and they are assigned to 5V tolerant pins.\nSo they can be connected in addition to the already existing circuitry between the SX127x chip and the MCU.\nThey do not affect the SX127x/LoRa board, and they work both 3.3V and 5V boards.\n\n### Outputs\n\nThe analysis output is written to the serial connection provided via USB. No driver is needed as the serial connection is implemented as a USB CDC device class.\n\nIf the USB connection is not convenient, the output can instead be written to a UART pin, using 115,200 bps:\n\n- PA2: TX\n\nFor the UART output, the code must be compiled with:\n\n```\n-D UART_OUTPUT=1\n```\n\nAdditionally, a 1 kHz square wave is output so you can measure the accurracy of the probe clock.\n\n- PA1: 1 kHz reference clock\n\n\n## Analysis\n\nThe probe listens to the SPI communication between the MCU and the SX127x chip and tracks the settings such a spreading factor and bandwidth. Additionally, it listens for the *opmode* command that puts the transceiver into transmit or receive mode.\n\nThe time when the CS signal returns to *high* after the *opmode* command is recorded (see figure below). In addition, the time of the *done* and *timeout* interrupts (pins DIO0 and DIO1) are recorded.\n\nFor the further analysis, it is then assumed that the interrupts occur immediately after transmission (air time), reception (air time) or timeout expiration. The remaining duration is assumed to be the preceding ramp-up time, e.g. to lock the PLL to the desired frequency.\n\n![Analysis](doc/Analysis.png)\n\nBased on this data, the timing of the RX window is examined. The window should be scheduled such that the preamble that precedes the payload overlaps with the window. If a preamble is detected, the receiver receives the payload. Otherwise, it will stop when the timeout expires.\n\nThe typical payload length is 8 symbols. 4 or 5 symbols are usually sufficient to detect the preamble (called *minimum preamble length* in below figure). Therefore it is sufficient if somewhat more than half of the preamble fall into the window. The code currently calculates with a minimum of 6 symbols.\n\nThe probe calculates the timing margin, separately for the start and the end of the RX window. The margin must be greater than 0 and optimally the start and end margin should be about the same length. A start and end margin of 5ms means that the timing for the RX windows could be off by 5ms in either direction and a downlink message would still be successfully received.\n\n![Timing](doc/Timing.png)\n\nThe probe prints the start and end margin for each RX window and propose a correction offset to properly center the RX window and the expected preamble.\n\nThere are two reason why they are not properly centered:\n\n1. The timing calculation might be flawed.\n\n2. The delay caused by the code run on the MCU, the SPI communication to change the *opmode* and the ramp-up of the transceiver might not have been fully accounted for. The delay is dependent of the type of MCU, the MCU's clock speed and the SPI speed.\n\n\n## Clock calibration\n\nThe analysis accuracy depends on the STM32's clock accuracy. If the clock is not exact but stable, it can be compensated with a calibration. Using a multimeter or frequency counter, the square wave on pin PA1 can be measured. Then the macro `MEASURED_CLOCK` is set to the measured value, the code is recompiled and uploaded.\n\nExample if a frequency of 999.31 Hz is measured:\n\n```\nbuild_flags = -D MEASURED_CLOCK=999.31\n```\n\nIf the MCU uses an external oscillator, it is usually sufficienlty accurate to make the measurements without calibration.\n\n\n## Project\n\nThe software project uses the STM32Cube HAL library and the [PlatformIO](https://platformio.org/) build system.\n\n\n## Architecture\n\nThe software is divided into several parts:\n\n- Data recording uses interrupts and DMA and mainly runs in interrupt handlers.\n- Data analysis and output runs as the main code (main loop).\n- USB and UART output is fully asynchronous and mainly runs in interrupt handlers.\n\nThe recorded data is written to two circular buffer:\n\n- In the *event buffer*, the type and time stamp of the events (such a SPI transaction completed and interrupts) are recorded.\n- In the *SPI buffer*, the SPI data is recorded.\n\nThe circlar buffers are read by the data analysis code.\n\n\n### SPI Recording\n\nThe device is configured as an SPI slave that receives only (no transmissions). It listens on the master-to-slave communication. It does not listen to the slave-to-master communication.\n\nThe SPI peripheral is configured with DMA, a circular buffer and hardware NSS. The NSS input is additionally configured with an external interrupt. Each time the raising edge triggers it, the time and the position within the SPI buffer is recorded and written to the event buffer.\n\n\n### DIO0 and DIO1 pins\n\nThe DIO0 and DIO1 pins are configured with an external interrupt. On each raising edge, the time is recorded and written to the event buffer.\n\n\n### Analysis\n\nThe analysis code waits for new entries in the event buffer and processes them. SPI transactions are analyzed. If they are a commmand to put the transceiver in TX or RX_SINGLE mode, the event is output. DIO0 and DIO1 triggers are also output.\n\n### Serial Output\n\nSerial output is written asynchronously so it does not interfer with anything else. Both the USB and the UART code use a similar approach.\n\nText is first put in a fixed, circular buffer. Additionally, there is a second circular queue to manage the text chunks. Each time a chunk is added or a chunk transmission is completed, the queue is checked. If it contains further chunks, the transmission of the next chunk is started.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanuelbl%2Fsx127x-probe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanuelbl%2Fsx127x-probe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanuelbl%2Fsx127x-probe/lists"}