From a95f4b378f66eae327a2807c79caa59320a9ef84 Mon Sep 17 00:00:00 2001 From: Thanasi Pantazides Date: Mon, 2 Dec 2024 23:55:16 -0600 Subject: [PATCH 1/3] documentation updates --- README.md | 147 ++++++++++++++++---------------- doc/assets/formatter_layout.pdf | Bin 0 -> 38449 bytes doc/assets/software_layers.pdf | Bin 0 -> 11264 bytes doc/breathe/source/index.rst | 6 +- util/build_doc.sh | 4 +- 5 files changed, 82 insertions(+), 75 deletions(-) create mode 100644 doc/assets/formatter_layout.pdf create mode 100644 doc/assets/software_layers.pdf diff --git a/README.md b/README.md index 438c3d4..f326226 100644 --- a/README.md +++ b/README.md @@ -2,39 +2,48 @@ # foxsi-4matter Code for the FOXSI-4 formatter. -This formatter replaces older FOXSI formatters with a [Raspberry Pi](https://www.raspberrypi.org) processor unit. A peripheral SPMU-001 board (based on the Xilinx Spartan 7) for [SpaceWire](https://www.star-dundee.com/wp-content/star_uploads/general/SpaceWire-Users-Guide.pdf) communication support. +This formatter replaces older FOXSI formatters with a [Raspberry Pi](https://www.raspberrypi.org) processor unit. There's also an SPMU-001 board (based on the Xilinx Spartan 7) for [SpaceWire](https://www.star-dundee.com/wp-content/star_uploads/general/SpaceWire-Users-Guide.pdf) communication support, and a [Housekeeping board](https://github.com/foxsi/foxsi4-hk) for commanding the onboard power supply. ## Setting up -See [PISETUP.md](PISETUP.md) for instructions on booting and configuring the Raspberry Pi. - -### Dependencies -- [Boost](https://www.boost.org/) - - boost::asio - - boost::bind -- [nlohmann JSON](https://github.com/nlohmann/json) -- [Doxygen](https://doxygen.nl/) -- [googletest](https://github.com/google/googletest) -- [concurrentqueue](https://github.com/cameron314/concurrentqueue) (included) -- [spdlog](https://github.com/gabime/spdlog) (included) - -### Examples -You can find a few examples in the [examples folder](examples). They are, unfortunately, out of date. +If you are setting up a fresh Raspbeery Pi, see [PISETUP.md](PISETUP.md) for instructions. If you are just trying to get this software running, stay where you are (this README). ## Network configuration The physical system is laid out like this: ![The Formatter is connected to many systems. Sorry, an image would be handy.](doc/assets/formatter_layout.svg "Formatter physical interfaces") +There are a lot of things that plug into the Formatter. The role of this software is to pipe data between all these interfaces. That involves +1. configuring the software to have permission to access all these ports on startup, +2. then handling all the idiosyncrasies of packet formatting for all the connected systems, so that the actual data can pass between interfaces without extraneous headers/wrappers coming along for the ride. + +This software owes its life to the [`foxsi4-commands`](https://github.com/foxsi/foxsi4-commands/tree/main) repository, which serves tells the code about the diagram above. Specifically, the top-level file [`foxsi4-commands/systems.json`](https://github.com/foxsi/foxsi4-commands/blob/main/systems.json) defines which interfaces are available to the Formatter and how they are constrained—what IP addresses and port numbers and maximum packet sizes and data rates and timeouts should be used for some given interface. For downlink and uplink commands, `systems.json` is the "contract" between the GSE and Formatter that defines their common interface. + +> [!NOTE] +> As of [v1.2.1](https://github.com/foxsi/foxsi-4matter/releases/tag/v1.2.1), there is a nuisance in [foxsi4-commands/systems.json](https://github.com/foxsi/foxsi4-commands/blob/main/systems.json) for Formater use. The Formatter will run and transmit downlink data to the Ethernet endpoint defined by [`gse.ethernet_interface.address`](https://github.com/foxsi/foxsi4-commands/blob/77d61f94183432e3a4fdff0bf0356e8361575445/systems.json#L5-L11). But the GSE will listen to [`gse.ethernet_interface.mcast_group`](https://github.com/foxsi/foxsi4-commands/blob/77d61f94183432e3a4fdff0bf0356e8361575445/systems.json#L5-L11). +> +> So you'll need to replace the **Formatter's** GSE configuration in `systems.json` with this: +> ```json +> "ethernet_interface": { +> "protocol": "udp", +> "address": "224.1.1.118", +> "port": 9999, +> "max_payload_bytes": 2000 +> }, +> ``` + ## How to build +Here's how you can build this software from scratch to run on the Raspberry Pi. + In the following, "your computer" is a laptop or desktop connected to the Raspberry Pi, which actually runs the software. -Decide whether you need to build the software on your local computer in addition to building on the Raspberry Pi. If you make local edits and want to check that everything compiles, or if you want to run certain tests, or if you want to use the `foxsimile` emulator, you should build locally. If all you need to do is forward code to the Raspberry Pi and build it there, skip to [building on the Raspberry Pi to run](#building-on-the-raspberry-pi-to-run). +Decide whether you need to build the software on your computer *in addition* to building on the Raspberry Pi. If you make local edits and want to check that everything compiles, or if you want to run certain tests, or if you want to use the `foxsimile` emulator, you should build on your machine. If all you need to do is forward code to the Raspberry Pi and build it there, skip to [building on the Raspberry Pi to run](#building-on-the-raspberry-pi-to-run). ### Installing dependencies -If this is your first time building on your computer, you may need to install two dependencies. If you are setting up the Raspberry Pi, see [PISETUP.md](PISETUP.md) for instructions on installing dependencies. +If this is your first time building on your computer, you may need to install two dependencies. If you are setting up a new Raspberry Pi, see [PISETUP.md](PISETUP.md) for instructions on installing dependencies. #### macOS ```bash +brew install cmake brew install nlohmann-json brew install boost brew install doxygen @@ -42,7 +51,7 @@ brew install googletest ``` (if you want to check if they are already installed, you can run `brew info ` to check). -I have used `boost` v1.83.0 and v1.80.0, and `nlohmann-hson` v3.11.3. I expect other 1.8x versions of `boost` and other 3.1x versions of `nlohmann-json` to also work, but have not tested. +I have used `boost` v1.83.0 and v1.80.0, and `nlohmann-hson` v3.11.3. I expect other `boost` versions 1.7x and up, and other 3.1x versions of `nlohmann-json` to also work, but have not tested. ### Building on the Raspberry Pi to run The flight Raspberry Pi already has dependencies installed. If you are setting up a brand new Raspberry Pi, see [PISETUP.md](PISETUP.md). Otherwise, choose instructions for either @@ -52,7 +61,7 @@ The flight Raspberry Pi already has dependencies installed. If you are setting u #### Set up from a release Select the release you want to build on the [`foxsi-4matter` tags page](https://github.com/foxsi/foxsi-4matter/tags). On the page for a specific release ([v0.0.8, as an example](https://github.com/foxsi/foxsi-4matter/releases/tag/v0.0.8)) download the .zip or tar.gz archive to your computer. -Unzip the archive you downloaded, then open a terminal inside that folder (which should be named `foxsi-4matter-x.y.z`, where `x.y.z` is the version number you downloaded). From that terminal, run: +Unzip the archive you downloaded, then open a terminal *inside* that folder (which should be named `foxsi-4matter-x.y.z`, where `x.y.z` is the version number you downloaded). From that terminal, run: ```bash git clone https://github.com/foxsi/foxsi4-commands.git ``` @@ -66,6 +75,8 @@ cmake --build . You can omit this local build step if you just want to build on the Raspberry Pi. #### Set up from latest `main` +I try to keep the latest `main` on GitHub versioned anyway. But this way you can use `git clone` instead of downloading zips. + Open a terminal in a folder where you want this code, and run ```bash git clone --recursive https://github.com/foxsi/foxsi-4matter.git @@ -80,94 +91,74 @@ cmake --build . ``` #### Building on Raspberry Pi -Now that you have code stored on your computer, you can push the code base to the Raspberry Pi over an Ethernet connection. First, `ssh` into the Raspberry Pi: +Now that you have code stored on your computer, you can push the code to the Raspberry Pi over an Ethernet connection. First, `ssh` into the Raspberry Pi: ``` ssh foxsi@192.168.1.8 password:... ``` -If you have issues `ssh`ing, make sure you can ping the IP `192.168.1.8` and that your local subnetwork includes that address. +The password for the flight Pi is `four`. -Once inside the Pi, create a new folder to hold your build and enter it: +If you have issues `ssh`ing, make sure you can `ping` the IP `192.168.1.8` and that your local subnetwork includes that address. + +Now you can prepare to build new code on the Formatter. Before building any new code, make sure to stop and power off any detector systems onboard. Then stop running the Formatter service: ```bash -mkdir your-folder-name -cd your-folder-name +sudo systemctl stop formatter.service ``` +The Formatter service will resume running after a reboot. + +> [!NOTE] This will overwrite the existing `formatter` binary in the Formatter. +> If you want to save a copy of it first, do the following to save a copy of the binary in whatever directory you are currently in on your machine: +> ```bash +> scp foxsi@192.168.1.8:foxsi-4matter/bin/formatter . +> ``` + + From another terminal on your computer, navigate to the downloaded `foxsi-4matter` code, which should be in a folder called either `foxsi-4matter` (for a build from `main`) or `foxsi-4matter-x.y.z` (for a build from a release). Then run this command (it's long) to push the code into `your-folder-name` in the Raspberry Pi: ```bash -rsync -av --exclude=build --exclude=bin --exclude=doc --exclude=log ../foxsi-4matter-x.y.z foxsi@192.168.1.8:/path/to/your-folder-name +rsync -av --exclude=build --exclude=bin --exclude=doc --exclude=log --exclude=foxsi4-commands ../foxsi-4matter-x.y.z foxsi@192.168.1.8:/home/foxsi ``` -but replace `-x.y.z` with the version number, or omit it entirely for a build from `main`; and replace `/path/to/your-folder-name` with the real path to the folder you created. +but replace `-x.y.z` with the version number, or omit it entirely for a build from `main`. -Now, go back to your other terminal that is still `ssh`'d inside the Raspberry Pi, and create a `build` folder inside `your-folder-name/`: +Now, go back to your other terminal that is still `ssh`'d inside the Raspberry Pi, and create a new build folder: ```bash -mkdir build +cd foxsi-4matter +rm -r build/* cd build cmake .. cmake --build . -j4 ``` -The Raspberry Pi build process takes a couple minutes. This will create several binaries in `your-folder-name/bin/` that you can run. - -## How to use - -First, command the power board to turn on/off the desired systems. Then start software on detector readout systems. +The Raspberry Pi build process takes a couple minutes. This will create several binaries in `foxsi-4matter/bin/` that you can run. The main program that runs in flight is `foxsi-4matter/bin/formatter`. -### Starting remote software -#### CdTe DE -As of Dec 16 2023, the DE software no longer needs to be started manually. You can ignore this section. -```bash -ssh de -``` -(on the GSE computer) then navigate to the DE main software: -```bash -cd CdTe_DE/production/run/ -``` -and run the DE software: +You should have stopped executing `formatter` via `systemctl` above. The new binary you just built will be run after a reboot. Or, if you want to see all the `cout` output as it happens, you can run it now like this: ```bash -./main_CdTeDE configuration_spmu001.xml +./bin/formatter --verbose --config foxsi4-commands/systems.json ``` -For detail, go to the [DE github repository](https://github.com/foxsi/CdTe_DE). +## How to use +Once the Formatter software starts running (either on boot, or because you have manually started running something) it will try to poll all the detector systems for housekeeping data. These systems may or may not be connected or powered on. -The DE will store raw data for each connected detector in `~/CdTe_DE/production/run/data/`. Note that the DE timestamp is disconnected from any all clock time. +You can use the GSE to command onboard systems on/off. Then start software on each detector readout systems. Refer to the FOXSI-4 -#### CMOS -The CMOS readout software automatically starts running on boot. When you start this Formatter software it will command any connected CMOS to start collecting and saving data. +For detail on the CdTe DE software, go to the [DE github repository](https://github.com/foxsi/CdTe_DE). ### Detector mapping -The DE identifies connected canisters (in the raw data recordings) on the SpaceWire port they are connected to. The power system requires a unique byte to be sent to turn on/off each system. For the flight configuration, this is the DE nomenclature for each detector and power code: +The DE identifies connected canisters (in the raw data recordings) on the SpaceWire port they are connected to. The power system requires a unique byte to be sent to turn on/off each system. For the FOXSI-4 flight configuration, this is the DE nomenclature for each detector and power code: | System (by focal plane position) | Detector | DE number | Power board byte | | -------------------------------- | --------- | --------- | ---------------- | | Canister 2 | no2021_06 | `det4` | `0x06` | -| Canister 3 | no2021_07 | `det2` | `0x05` | -| Canister 4 | no2021_05 | `det3` | `0x04` | +| Canister 3 | no2021_07 | `det2` | `0x04` | +| Canister 4 | no2021_05 | `det3` | `0x05` | | Canister 5 | no2022_01 | `det1` | `0x03` | | DE | --- | --- | `0x00` | | CMOS 1 | `0010` | --- | `0x08` | | CMOS 2 | `0002` | --- | `0x07` | | Timepix | --- | --- | `0x01` | -| SAAS | --- | --- | `0x01` | +| SAAS | --- | --- | `0x02` | ### Starting the Formatter software -As of Nov 28 2023, the Formatter software also needs to be started manually. Do this: -```bash -ssh formatter -``` -then navigate to the Formatter software directory: -```bash -cd foxsi-4matter -``` -and then run the formatter software: -```bash -./bin/formatter --verbose --config foxsi4-commands/systems.json -``` -When the Formatter runs, it will locally record log files (describing Formatter behavior, not saving detector data) to `~/foxsi-4matter/log/`. You can stop the Formatter with ctrl-C. - -On startup the Formatter will walk through each system and try to set it up. No bias voltage will be applied to CdTe. If it cannot talk to a specific system, it will ignore it for data readout (this behavior will be different for flight). - -#### Automatically running on boot -The flight Raspberry Pi will automatically run the Formatter software after booting, and restart it if it crashes. This is managed by Linux `systemd`, with the Formatter software running as a "service." You can interact with this service using these commands: +The Formatter will try to run the binary `/home/foxsi/foxsi4-matter/bin/formatter` after boot, and again every 10 seconds after that binary stops running. This is managed by [[`systemctl`](https://www.freedesktop.org/software/systemd/man/latest/systemctl.html)], which is a powerful Linux utility for running *services*. You can interact with the Formatter service using these commands: ```bash sudo systemctl stop formatter.service # stop running the Formatter service @@ -179,6 +170,15 @@ sudo systemctl status formatter.service # report the current status of th The last command, querying the `status` of the running Formatter service, will tell you if it is running still or has stopped. It will also print some of the Formatter's recent output. Note that while the Formatter service may still be running, the main loop may have effectively stopped due to subsystem disconnects. If you query the status multiple times and always see identical printout, the service may no longer be running correctly. +When the service is running, if you need to debug you will need to find the correct `~/foxsi-4matter/log/` file to collect evidence. This can be inconvenient to track down since Unixtime changes with every reboot. + +A helpful debugging workflow is to stop the Formatter service, then launch it manually so you can see the stdout output: +```bash +sudo systemctl stop formatter.service +./bin/formatter --verbose --config foxsi4-commands/systems.json +``` +You can stop the Formatter with ctrl-C. + ## Downlink data The Formatter transmits data over the UDP interface defined in `foxsi4-commands/systems.json`'s `gse` field. A complete raw data frame from an onboard system may be larger than the maximum downlink packet size, in which case it will be fragmented. A given downlink packet has the following header: @@ -189,7 +189,7 @@ The Formatter transmits data over the UDP interface defined in `foxsi4-commands/ | `1-2` | 2 bytes | `n` | Number of packets in this frame. | | `3-4` | 2 bytes | `i` | Index of this packet in the frame. **This is 1-indexed.** | | `5` | 1 bytes | `data` | An identifier of data type stored in the packet. | -| `6-7` | 2 bytes | `reserved` | Reserved. | +| `6-7` | 2 bytes | `frame_counter` | Identifier used to associate packets with the complete frame they originally came from. Planned addition in v1.3.0. | The raw data payload is concatenated after the 8-byte header. @@ -205,6 +205,7 @@ For the `system` field, the ID value is taken from `foxsi4-commands/systems.json | `0x12` | `temp` | Temperature data | | `0x13` | `stat` | System status data | | `0x14` | `err` | System error data | +| `0x20` | `ping` | Software status (added in v1.3.0) | | `0x30` | `reply` | Forwarded reply to command | | `0xff` | `none` | No data type | @@ -233,6 +234,8 @@ Additionally, an ASCII-formatted file called `catch.log` is produced to log any ## Common issues 1. If you run the formatter software, kill it, then try to run it immediately again, you will get an error containing `connect: Address already in use`. This is because the kernel retains control of TCP sockets for a while (~1 minute) after you close them to allow any messages still on the wire to come through. Just wait a moment and try running again. Of course, it is possible (depending on your configuration) that another process on the same machine actually is using the IP address you want as well. +2. Unix time resets to a similar value on every reboot. Do not trust any timestamps you get out of this software. +3. If you never clear the `~/foxsi-4matter/log/` folder, you can completely fill the disk and get a read-only filesystem. Then you will need to edit `fstab` manually and with a racing heart while the whole flare campaign waits for you. This happened at PFRR during the FOXSI-4 launch campaign. ## Documentation If you enjoyed this measly little README, you're going to *love* the rest of the documentation! You can view these docs in a web browser (they are HTML), and until they are hosted somewhere, you will need these instructions to build them: diff --git a/doc/assets/formatter_layout.pdf b/doc/assets/formatter_layout.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1dc0296fbbff97752a7be1afbaaf15e98a42ac6a GIT binary patch literal 38449 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 B0ATTPteSHc}2eIvV!#%6G08Q}za~h_8r8 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 literal 0 HcmV?d00001 diff --git a/doc/breathe/source/index.rst b/doc/breathe/source/index.rst index 2e27a7f..28a6ee7 100644 --- a/doc/breathe/source/index.rst +++ b/doc/breathe/source/index.rst @@ -41,7 +41,7 @@ Some default parameters are defined in ``Parameters.h``, and the bulk of configu Here is the network configuration: -.. image:: ../../assets/formatter_layout.svg +.. image:: ../../assets/formatter_layout.pdf An overview of the system configuration data can be found here: @@ -65,7 +65,9 @@ Class reference The software is abstracted like this, with lower layers constraining and configuring the behavior of higher layers: -.. image:: ../../assets/software_layers.svg +.. image:: ../../assets/software_layers.pdf + +An element in brackets like ``[SystemManager]`` means there's an array of them. .. toctree:: :maxdepth: 2 diff --git a/util/build_doc.sh b/util/build_doc.sh index 432cf44..6b601a3 100644 --- a/util/build_doc.sh +++ b/util/build_doc.sh @@ -6,6 +6,8 @@ echo "running doxygen..." doxygen echo "running sphinx..." source doc/env/bin/activate -sphinx-build -M html doc/breathe/source doc/breathe/build +cd doc/breathe +sphinx-build -M html source build +sphinx-build -M latexpdf source build echo "opening docs..." open doc/breathe/build/html/index.html \ No newline at end of file From 6d1bf45f25c2302d7b6b050a6d14fcd087312c75 Mon Sep 17 00:00:00 2001 From: Thanasi Pantazides Date: Tue, 3 Dec 2024 08:57:27 -0600 Subject: [PATCH 2/3] util doc updates --- util/README.md | 24 +++++++++++++----------- util/udpgarbage.py | 4 ++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/util/README.md b/util/README.md index 8531abd..0e34649 100644 --- a/util/README.md +++ b/util/README.md @@ -1,18 +1,20 @@ # util -This folder includes some high-level utilities for Formatter actions. +This folder includes some convenient utilities for Formatter actions. -## Contents: +A typical workflow is to pull from GitHub, modify some code on your laptop, try to build it, and if that works, push it over an Ethernet cable to the Formatter using [update_formatter.sh](update_formatter.sh). Then you can build the code on the Formatter (Raspberry Pi) and test it out. Maybe you find Ethernet connection issues! Then you can use some of the Python scripts to send dummy packets and validate Ethernet interfaces. The files in this `util/` folder help with that development workflow. + +## Shell scripts +1. [update_formatter.sh](update_formatter.sh) will push code you have on a computer over an Ethernet connection to the Formatter. A typical workflow is to write some code This script will not push build, bin, doc, or log folders over to the Formatter. It uses `rsync`. +2. [build_doc.sh](build_doc.sh) is used to build all the autodoc-type documentation (from docstrings in the code). This builds both PDF (LaTeX) and HTML docs in `foxsi-4matter/doc/breathe/build/`. +3. [copy_remote_logs.sh](copy_remote_logs.sh) will copy all the log fils on the Formatter into the `foxsi-4matter/log/formatter/` folder on this machine. +4. [delete_logs.sh](delete_logs.sh) deletes the logs in the `log/` folder. This doesn't do anything remotely—if you want to delete logs from the Formatter instead of your laptop, you'll need to `ssh` and run it there. +5. [assign_all_loopbacks.sh](assign_all_loopbacks.sh) will add all loopback IP addresses on your laptop. So you can send loopback not just from 127.0.0.1, but up to 127.0.0.255. This is used for mock testing in the `foxsimile` emulator. If you're doing stuff with `foxsimile` you'll need to run this first. + +## Python scripts +A lot of these have to do with network testing between the GSE and Formatter. They contain hardcoded IP addresses/ports. These may or may not match the configuration in foxsi4-commands/systems.json. 1. [spipower.py](spipower.py) is a Python script to control (via command line) the MAX7317 serial-to-parallel chip on the power distribution board. You can use this to enable/disable power output from the board to specific subsystems. 2. [monitortemp.py](monitortemp.py) is a Python script that prints the Raspberry Pi's CPU temperature to the command line at 5-second intervals. 3. [udpecho.py](udpecho.py) is a Python script that echoes received UDP packets back to the sender. -4. [udpgarbage.py](udpgarbage.py) is a Python script that continuously transmits roughly 18 Mbps of garbage data to the ground station. - -## [spipower.py](spipower.py) - -This script is intended to be used with the Power Distribution board developed at UMN. To use, connect Raspberry Pi SPI0 (GPIOs 8-11, pins 24, 21, 19, 23) to the Power Distribution board. Run this script and follow the command prompt to turn systems on and off. - -## [monitortemp.py](monitortemp.py) - -This script prints out the CPU temperature every 5 seconds. \ No newline at end of file +4. [udpgarbage.py](udpgarbage.py) is a Python script that continuously transmits roughly 18 Mbps of garbage data to the ground station IP address. \ No newline at end of file diff --git a/util/udpgarbage.py b/util/udpgarbage.py index 27e1419..1188d23 100644 --- a/util/udpgarbage.py +++ b/util/udpgarbage.py @@ -3,8 +3,8 @@ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # multicast group address and port -mcast_grp = '224.1.1.0' -mcast_port = 3000 +mcast_grp = '224.1.1.118' +mcast_port = 9999 while True: # make some random data to transmit From a812e5b6ff3866cdec705de836ebd44460222a3d Mon Sep 17 00:00:00 2001 From: Thanasi Pantazides Date: Tue, 3 Dec 2024 08:59:54 -0600 Subject: [PATCH 3/3] doc update --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f326226..3f0a9ed 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ There are a lot of things that plug into the Formatter. The role of this softwar This software owes its life to the [`foxsi4-commands`](https://github.com/foxsi/foxsi4-commands/tree/main) repository, which serves tells the code about the diagram above. Specifically, the top-level file [`foxsi4-commands/systems.json`](https://github.com/foxsi/foxsi4-commands/blob/main/systems.json) defines which interfaces are available to the Formatter and how they are constrained—what IP addresses and port numbers and maximum packet sizes and data rates and timeouts should be used for some given interface. For downlink and uplink commands, `systems.json` is the "contract" between the GSE and Formatter that defines their common interface. > [!NOTE] -> As of [v1.2.1](https://github.com/foxsi/foxsi-4matter/releases/tag/v1.2.1), there is a nuisance in [foxsi4-commands/systems.json](https://github.com/foxsi/foxsi4-commands/blob/main/systems.json) for Formater use. The Formatter will run and transmit downlink data to the Ethernet endpoint defined by [`gse.ethernet_interface.address`](https://github.com/foxsi/foxsi4-commands/blob/77d61f94183432e3a4fdff0bf0356e8361575445/systems.json#L5-L11). But the GSE will listen to [`gse.ethernet_interface.mcast_group`](https://github.com/foxsi/foxsi4-commands/blob/77d61f94183432e3a4fdff0bf0356e8361575445/systems.json#L5-L11). +> For [v1.2.1](https://github.com/foxsi/foxsi-4matter/releases/tag/v1.2.1) and earlier, there is a nuisance in [foxsi4-commands/systems.json](https://github.com/foxsi/foxsi4-commands/blob/main/systems.json) for Formater use. The Formatter will run and transmit downlink data to the Ethernet endpoint defined by [`gse.ethernet_interface.address`](https://github.com/foxsi/foxsi4-commands/blob/77d61f94183432e3a4fdff0bf0356e8361575445/systems.json#L5-L11). But the GSE will listen to [`gse.ethernet_interface.mcast_group`](https://github.com/foxsi/foxsi4-commands/blob/77d61f94183432e3a4fdff0bf0356e8361575445/systems.json#L5-L11). > > So you'll need to replace the **Formatter's** GSE configuration in `systems.json` with this: > ```json @@ -30,6 +30,7 @@ This software owes its life to the [`foxsi4-commands`](https://github.com/foxsi/ > "max_payload_bytes": 2000 > }, > ``` +> This is fixed after v1.2.2, and the Formatter will try to use `gse.ethernet_interface.mcast_group`, but if that doesn't exist, it will fall back to `gse.ethernet_interface.address`. ## How to build Here's how you can build this software from scratch to run on the Raspberry Pi.