# Communication system

# Ethernet Driver

The documentation for the embedded Ethernet driver

# Setup of Embedded Ethernet

***This page:** *How to set up ethernet.**

---

## **Introduction**

**Ethernet** <span style="white-space: pre-wrap;">is the protocol used to communicate between all the components. It uses the </span>**auto-generated Ethernet driver**<span style="white-space: pre-wrap;"> code from cubeMX to use the physical Ethernet peripheral. It also uses </span>**LWIP** <span style="white-space: pre-wrap;">to do the lowest levels of packet handling. Lastly, we use </span>**freeRTOS** <span style="white-space: pre-wrap;">for multi threading. The current implementation cannot work without it. </span>

---

## **CubeMX**

<span style="white-space: pre-wrap;">CubeMX is used to automatically generate setup code for the stm32. To use Ethernet you have to set up a few things in cubeMX. </span>

<span style="white-space: pre-wrap;">To write this driver I mostly used videos from ControllersTech on YouTube. The most useful video is </span>[STM32 Ethernet (Part 1): How to configure Ethernet peripheral and perform successful ping test](https://www.youtube.com/watch?v=8r8w6mgSn1A&t=907s)<span style="white-space: pre-wrap;"> \[1\]. So if you do not understand anything, watch that video. </span>

---

### 1) ETH

<span style="white-space: pre-wrap;">ETH is under connectivity in cubeMX. It sets up the Ethernet peripheral. Set it to </span>`<span class="editor-theme-code">RMII</span>`<span style="white-space: pre-wrap;"> mode. </span>

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/dceafbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/dceafbeelding.png)

##### **NVIC &gt; NVIC**

<span style="white-space: pre-wrap;">Turn on Ethernet global interrupt in NVIC settings. </span>

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/oqlafbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/oqlafbeelding.png)

##### **ETH &gt; GPIO settings**

<p class="callout warning"><span style="white-space: pre-wrap;">When Ethernet is enabled, the pins should </span>**automatically** be configured according to the below schematic. This can be wrong, please **confirm it**!</p>

<span style="white-space: pre-wrap;">Setup all the PINS like it is done in the </span>[PIN schematic](https://www.st.com/en/evaluation-tools/nucleo-h753zi.html#cad-resources)<span style="white-space: pre-wrap;"> (Download: </span>[MB1364-H753ZI-C01 Board schematic](https://www.st.com/resource/en/schematic_pack/mb1364-h753zi-c01-schematic.pdf)<span style="white-space: pre-wrap;">, pg. 6) \[2\]. </span>

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/gxsafbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/gxsafbeelding.png)

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/2G1afbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/2G1afbeelding.png)

<details id="bkmrk-troubleshooting%3A-pin"><summary>Troubleshooting: Pins not set as schematic</summary>

<p class="callout danger">If any of the pins are not as in the schematic, refer to the above information. You can click each pin in CubeMX and choose the correct function (e.g. ETH\_RXD0), the other (incorrect) pin will be automatically disabled.</p>

</details>---

### 2) LWIP

LWIP is a middleware generated by cubeMX. You can find it under middleware.

##### **LWIP &gt; Platform Settings**

<span style="white-space: pre-wrap;">Set up LAN8742, to signal that that is the physical Ethernet driver you use. </span>

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/N9gafbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/N9gafbeelding.png)

##### **LWIP &gt; General settings**

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/zIsafbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/zIsafbeelding.png)

The most important configurations here are the DHCP (**DHCP = Disabled**<span style="white-space: pre-wrap;">) and IP address settings. </span>  
<span style="white-space: pre-wrap;">The IP address settings </span>**don't matter**<span style="white-space: pre-wrap;">, they will be </span>**overwritten** <span style="white-space: pre-wrap;">in a configuration file in the code. </span>

##### **LWIP &gt; Key options**

<span style="white-space: pre-wrap;">The most important part of the tab </span>**Key options**<span style="white-space: pre-wrap;"> is to check if </span>`<span class="editor-theme-code">LWIP_ARP = Enabled</span>`<span style="white-space: pre-wrap;">. You also need to keep track of the </span>`<span class="editor-theme-code">MEM_SIZE (Heap Memory Size)</span>`<span style="white-space: pre-wrap;">. Start with a value of </span>`<span class="editor-theme-code">1024*16 bytes</span>`<span style="white-space: pre-wrap;">. </span>`<span class="editor-theme-code">LWIP_RAM_HEAP_POINTER</span>`<span style="white-space: pre-wrap;"> should be </span>`<span class="editor-theme-code">0x30004900</span>`<span style="white-space: pre-wrap;"> such that the heap doesn't overlap different memory. That is the value I use, but it can be changed if need be. </span>  
<span style="white-space: pre-wrap;">Lastly, you need to add </span>`<span class="editor-theme-code">ETHARP_SUPPORT_STATIC_ENTRIES = Enabled</span>`<span style="white-space: pre-wrap;">. This is only shown when you turn on </span>`<span class="editor-theme-code">Show Advanced Parameters</span>`.

[![image.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-05/scaled-1680-/idcimage.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-05/idcimage.png)[![image.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-05/scaled-1680-/f6Fimage.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-05/f6Fimage.png)

---

### 3) CORTEX\_M7

<span style="white-space: pre-wrap;">You can find </span>**CORTEX\_M7**<span style="white-space: pre-wrap;"> under System Core. </span>

##### **CORTEX\_M7 &gt; Parameter Settings**

<span style="white-space: pre-wrap;">Turn on </span>`<span class="editor-theme-code">CPU ICache</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">CPU DCache</span>`.

[![image.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/tQ9image.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/tQ9image.png)

Setup memory protection like seen below:

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-05/scaled-1680-/afbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-05/afbeelding.png)<span style="white-space: pre-wrap;">The </span>`<span class="editor-theme-code">MPU Region Base Address</span>`<span style="white-space: pre-wrap;"> is the address of the first Rx descriptor (</span>**ETH &gt; Parameter Settings &gt; General**<span style="white-space: pre-wrap;">). The </span>`<span class="editor-theme-code">MPU region size</span>`<span style="white-space: pre-wrap;"> is calculated using heap memory size and the RX buffers. Watch the </span>[video](https://www.youtube.com/watch?v=8r8w6mgSn1A&t=907s)<span style="white-space: pre-wrap;"> \[1\] mentioned above for more information. </span>

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/sNFafbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/sNFafbeelding.png)---

### 4) FreeRTOS

<span style="white-space: pre-wrap;">Freertos is automatically integrated in LWIP if you turn it on in cubeMX. It does not have to be modified, it just has to be turned on. Make sure you use </span>`<span class="editor-theme-code">CMSIS_V2</span>`<span style="white-space: pre-wrap;">, because V1 did not work well. </span>

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/PCAafbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/PCAafbeelding.png)

<p class="callout danger">**NOTE:**<span style="white-space: pre-wrap;"> when you use FreeRTOS, you will have to select </span>**another SYS Timebase Source**<span style="white-space: pre-wrap;">. Go to </span>**SYS &gt; Timebase Source**<span style="white-space: pre-wrap;"> and select any of the free timers, just make sure it is </span>**NOT**<span style="white-space: pre-wrap;"> SysTick. Make sure you do not use that timer for any other activities.</span></p>

<span style="white-space: pre-wrap;">Make sure you set your </span>`<span class="editor-theme-code">TOTAL_HEAP_SIZE</span>`<span style="white-space: pre-wrap;"> to a sufficient number. When it is not big enough, the threads that you will create using FreeRTOS will suffocate and not work. This will also not give you any errors so watch out for it!</span>

[![afbeelding.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-06/scaled-1680-/afbeelding.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-06/afbeelding.png)

---

## **The Code**

<span style="white-space: pre-wrap;">After you generate the code for your board, you can look through networking component, in the </span>`<span class="editor-theme-code">ethernet.h</span>`<span style="white-space: pre-wrap;"> file, to see all public Ethernet functions. </span>

<p class="callout info"><span style="white-space: pre-wrap;">For a full guide of how to use Ethernet, I refer you to </span>[Driver usage](https://bookstack.roboteamtwente.nl/books/communication-system/page/sending-your-first-message "https://bookstack.roboteamtwente.nl/books/communication-system/page/driver-usage")<span style="white-space: pre-wrap;"> \[3\].</span></p>

---

## **Resources**

1. <span style="white-space: pre-wrap;"> </span>[STM32 Ethernet (Part 1): How to configure Ethernet peripheral and perform successful ping test](https://www.youtube.com/watch?v=8r8w6mgSn1A&t=907s)
2. [PIN schematic](https://www.st.com/en/evaluation-tools/nucleo-h753zi.html#cad-resources)<span style="white-space: pre-wrap;"> (Download: </span>[MB1364-H753ZI-C01 Board schematic](https://www.st.com/resource/en/schematic_pack/mb1364-h753zi-c01-schematic.pdf), pg. 6)
3. <span style="white-space: pre-wrap;"> </span>[Driver usage](https://bookstack.roboteamtwente.nl/books/communication-system/page/sending-your-first-message "https://bookstack.roboteamtwente.nl/books/communication-system/page/driver-usage")<span style="white-space: pre-wrap;"></span>

# Sending your first message

***This page:** *how to initialize your Ethernet driver and send your first message. A lot of constants can be seen in the explanatory code. Those can be found in* `<em class="editor-theme-code editor-theme-italic">components/common/networking_constants</em>`*. They are also added at the end of this file.**

<p class="callout info"><span style="white-space: pre-wrap;">The functions that are used for Ethernet can be found in </span>`<span class="editor-theme-code">components/common/networking/inc/ethernet.h</span>`<span style="white-space: pre-wrap;">. </span></p>

<p class="callout warning"><span style="white-space: pre-wrap;">The headers in the folder </span>`<span class="editor-theme-code">components/common/networking/stm/</span>`<span style="white-space: pre-wrap;"> are only to be used inside the Ethernet driver!</span></p>

<p class="callout danger">**NOTE:** <span style="white-space: pre-wrap;">The implementation only works with FreeRTOS, but it works fine with only 1 thread. </span>**Before you implement anything, do an introductory tutorial on FreeRTOS!**</p>

---

## **Code Usage**

### 1) Linker file updating

In the linker file, you need to say what memory addresses LWIP uses. You set these values in Cubemx and the sections are generated for you, but you have to add it to the linker file yourself.

<p class="callout info">You can find your linker (`<span class="editor-theme-code">.ld</span>`<span style="white-space: pre-wrap;">) file in </span>`<span class="editor-theme-code">components/{env_name}/firmware/</span>`</p>

The codeblock added for the default values is:

```c
.lwip_sec (NOLOAD) :
  {
      . = ABSOLUTE(0x30000000);
      *(.RxDecripSection)

      . = ABSOLUTE(0x30000080);
      *(.TxDecripSection)
      
      . = ABSOLUTE(0x30000100);
      *(.Rx_PoolSection)

  } >RAM_D2
```

<span style="white-space: pre-wrap;"> You can just use the generated linker file and append to it, because it never gets regenerated, but you can also create a new one. If you create a new one, you have to put in the platformio configuration file that you use the new linker file. An example of how to do it is shown below. The path is relative to where the platformio configuration file is located. </span>

```c
board_build.ldscript = components/network_board/firmware/STM32H753XX_FLASH.ld
```

---

### 2) Code initialization

<span style="white-space: pre-wrap;">When you first use the Ethernet driver, you have to initialize a few things. </span>

First make sure that HAL is initialized, the D and I cache are enabled, the system clock and the memory protection unit as well. These auto-generated functions start processes that Ethernet uses.

```c
//Initialize HAL
HAL_Init();

//Enable D&I cache (for ETH)
SCB_EnableICache();
SCB_EnableDCache();

//Start the system clock
SystemClock_config();

//Memory protection unit
MPU_Config_wrapper();
```

<details id="bkmrk-note-for-debugging-w"><summary>Note for debugging with SCB\_EnableDCache()</summary>

<span style="white-space: pre-wrap;">When debugging and trying to step over </span>`<span class="editor-theme-code">SCB_EnableDCache()</span>`<span style="white-space: pre-wrap;">, the program will keep infinitely running </span>**unless** <span style="white-space: pre-wrap;">you place a breakpoint on the next line </span>**while in active debugging mode**.

</details>##### ETH\_Init(...)

```c
result_t ETH_init(linkstatus_callback_t link_state_change_callback,
                  uint8_t ip[4], uint8_t netmask[4],
                  uint8_t gateway[4],
                  uint8_t mac_address[6])
```

<span style="white-space: pre-wrap;">For </span>`<span class="editor-theme-code">ETH_init</span>`<span style="white-space: pre-wrap;">, the </span>`<span class="editor-theme-code">link_status_change_callback</span>`<span style="white-space: pre-wrap;"> can be set to </span>`<span class="editor-theme-code">NULL</span>`<span style="white-space: pre-wrap;">, to use the default function. The others have to be set. For initial testing you can set these to the same values as set in CubeMX. </span>

<details id="bkmrk-example-usage-of-eth"><summary>Example usage of ETH\_init(...)</summary>

```c
//Example values
uint8_t ip[4] = {192, 168, 0, 223};
uint8_t mac[6] = {255, 255, 255, 255, 255, 255};
uint8_t gateway[4] = {192, 168, 0, 1};
uint8_t netmask[4] = {255, 255, 255, 0};

ETH_init(NULL, ip, netmask, gateway, mac);
```

</details><p class="callout info">**NOTE on multithreading:**<span style="white-space: pre-wrap;"> make sure you set the stack size big enough</span><span style="color: rgb(52, 73, 94); white-space: pre-wrap;"> (I use </span>`<span class="editor-theme-code">.stack_size = 1024 * 8</span>`<span style="color: rgb(52, 73, 94);">) when s</span><span style="white-space: pre-wrap;">etting the </span>`<span class="editor-theme-code">task_attributes</span>`<span style="white-space: pre-wrap;"> for the thread Ethernet will be running in!</span></p>

##### ETH\_udp\_init()

```c
void ETH_udp_init(uint8_t sender_prio_buf, 
                  QueueHandle_t *send_queues,
                  receive_callback_t receiver_callback)
```

- `<span class="editor-theme-code">sender_prio_buf</span>`<span style="white-space: pre-wrap;"> </span>  
    <span style="white-space: pre-wrap;">Defines the </span>**number** <span style="white-space: pre-wrap;">of priority queues you use for queuing the send messages. The queues we use are freeRTOS queues. More information can be found </span>[here](https://freertos.org/Documentation/02-Kernel/02-Kernel-features/02-Queues-mutexes-and-semaphores/01-Queues)<span style="white-space: pre-wrap;">. </span>
- `<span class="editor-theme-code">send_queues</span>`<span style="white-space: pre-wrap;"> </span>  
    <span style="white-space: pre-wrap;">You need to pass freeRTOS queues as a pointer to an array. The queues need to be implemented before passing them to </span>`<span class="editor-theme-code">ETH_udp_init(...)</span>`, see below.

<details id="bkmrk-you-need-to-implemen"><summary>Example queue implementation</summary>

```c
int SendQueueSize = 80;

//Create queue 1
static StaticQueue_t xStaticQueue1;
uint8_t ucQueueStorageArea1[SendQueueSize * ETHERNET_SQ_ITEM_SIZE]; //NOTE: constants from <ethernet_constants.h>
QueueHandle_t udp_receiver_queue1 = xQueueCreateStatic(SendQueueSize, ETHERNET_SQ_ITEM_SIZE, ucQueueStorageArea1, &xStaticQueue1);

//Create queue 2
static StaticQueue_t xStaticQueue2;
uint8_t ucQueueStorageArea2[SendQueueSize * ETHERNET_SQ_ITEM_SIZE];
QueueHandle_t udp_receiver_queue2 = xQueueCreateStatic(SendQueueSize, ETHERNET_SQ_ITEM_SIZE, ucQueueStorageArea2, &xStaticQueue2);

//queues object we pass as an argument later
QueueHandle_t queues[2] = {udp_receiver_queue1, udp_receiver_queue2};
```

</details>- `<span class="editor-theme-code">receiver_callback</span>`  
    <span style="white-space: pre-wrap;">This can be any function that takes a </span>`<span class="editor-theme-code">receive_frame_t</span>`<span style="white-space: pre-wrap;">, found in </span>`<span class="editor-theme-code">ethernet_udp.h</span>`<span style="white-space: pre-wrap;">. However, we also implemented a "packet dispatcher", to handle incoming packets. </span>

<p class="callout info"><span style="white-space: pre-wrap;">More information about the packet dispatcher is in the </span>[Packet Dispatcher documentation](https://bookstack.roboteamtwente.nl/books/embedded-infastructure/page/packet-dispatcher "https://bookstack.roboteamtwente.nl/books/embedded-infastructure/page/packet-dispatcher").</p>

<details id="bkmrk-example-implementati"><summary>Example implementation WITHOUT packet dispatcher</summary>

```c
void HandlePacket(receive_frame_t *receive_frame) {
 printf("Wayoo, message received");
}

int main(void) {
  ...
  /*The other code examples above would go HERE */
  ETH_udp_init(2, queues, HandlePacket);
  ...
}
```

</details><span style="white-space: pre-wrap;">Now Ethernet is started and you can </span>**receive packages**!

---

### 3) Sending Packets

##### ETH\_add\_arp(...)

```c
result_t ETH_add_arp(uint8_t ip[4], uint8_t mac[6], int retry_count)
```

**To send to a certain IP, you have to set the ARP table.**<span style="white-space: pre-wrap;"> Otherwise, it will only send ARP resolution messages. You do that by using the </span>`<span class="editor-theme-code">ETH_add_arp(...)</span>`<span style="white-space: pre-wrap;"> function with the IP you want to send to, and any random mac address.</span>

<p class="callout warning">**WARNING:**<span style="white-space: pre-wrap;"> Make sure you have initialized udp </span>**before** using ETH\_add\_arp(...)!</p>

##### <span style="white-space: pre-wrap;">ETH\_udp\_send(...) </span>

```c
void ETH_udp_send(uint8_t ip[4], 
                  uint8_t port, 
                  uint8_t *payload,
                  uint16_t payload_len, 
                  uint8_t prio_num)
```

- `<span class="editor-theme-code">payload</span>`  
    <span style="white-space: pre-wrap;">The payload can be any byte array, where the size of that byte array is the 4th argument: </span>`<span class="editor-theme-code">payload_len</span>`<span style="white-space: pre-wrap;">. </span>
- `<span class="editor-theme-code">prio_num</span>`  
    <span style="white-space: pre-wrap;">Priority level for transmission. Must be less than </span>`<span class="editor-theme-code">sender_prio_buf</span>`<span style="white-space: pre-wrap;"> specified in </span>`<span class="editor-theme-code">ETH_udp_init(...)</span>`.

Now you can send messages!

<details id="bkmrk-example%3A-sending-a-m"><summary>Example: sending a message</summary>

```c
//NOTE: These are the receiving ip/mac, 
//not the sending ones we specified earlier
uint8_t ip[4] = SAMPLE_BOARD_IP;
uint8_t mac[6] = SAMPLE_BOARD_MAC; //constants from <ip_mac_constants.h>

ETH_add_arp(ip, mac, 5);

uint8_t packet1_payload[4] = {14,06,20,04};
ETH_udp_send(ip, 8, packet1_payload, 4, 1); //Send a single packet
```

</details>---

## **Appendix**

### 1) Networking\_constants

```c
#ifndef IP_MAC_CONSTANTS
#define IP_MAC_CONSTANTS

#define SAMPLE_BOARD_IP {192, 168, 0, 111}
#define SAMPEL_BOARD_MAC {0x00, 0x80, 0xe1, 0x00, 0x00, 0x00}

#define NETWORK_IP {192, 168, 0, 223}
#define NETWORK_MAC {0x00, 0x43, 0x23, 0xee, 0x21, 0x64}

#define GATEWAY {192, 168, 0, 1}
#define NETMASK {255, 255, 255, 0}
#endif //! IP_MAC_CONSTANTS
```

### 2) Full Example Code

<p class="callout info"><span style="white-space: pre-wrap;">Import </span>`<span class="editor-theme-code">"networking_constants.h"</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">"ip_mac_constants.h"</span>`<span style="white-space: pre-wrap;"> to use the predefined test constants</span></p>

```c
#include "ethernet.h"
#include "networking_constants.h"
#include "ip_mac_constants.h"


/* Callback function that handles a specific packet*/
void HandlePacket(receive_frame_t *receive_frame) {
    printf("Wayoo, message received");
}

int outgoing_counter = 0;
int main(void) {
    /*Inits*/
    HAL_Init();
    SystemClock_Config();

    MPU_Config_wrapper();

    SCB_EnableICache();
    SCB_EnableDCache();

    MX_GPIO_Init();

    /*Config + init sending side*/
    uint8_t mac[6] = NETWORK_MAC;
    uint8_t ip[4] = NETWORK_IP;
    uint8_t netmask[4] = NETMASK;
    uint8_t gateway[4] = GATEWAY;

    ETH_init(NULL, ip, netmask, gateway, mac);

    /*Making queues*/
    int SendQueueSize = 80;

    static StaticQueue_t xStaticQueue1;
    uint8_t ucQueueStorageArea1[SendQueueSize * ETHERNET_SQ_ITEM_SIZE];
    QueueHandle_t udp_receiver_queue1 = xQueueCreateStatic(SendQueueSize, ETHERNET_SQ_ITEM_SIZE, ucQueueStorageArea1, &xStaticQueue1);

    static StaticQueue_t xStaticQueue2;
    uint8_t ucQueueStorageArea2[SendQueueSize * ETHERNET_SQ_ITEM_SIZE];
    QueueHandle_t udp_receiver_queue2 = xQueueCreateStatic(SendQueueSize, ETHERNET_SQ_ITEM_SIZE, ucQueueStorageArea2, &xStaticQueue2);
    
    QueueHandle_t queues[2] = {udp_receiver_queue1, udp_receiver_queue2};

    ETH_udp_init(2, queues, HandlePacket);

    /*Config + add ARP receiving side*/
    uint8_t ip[4] = SAMPLE_BOARD_IP;
    uint8_t mac[6] = SAMPLE_BOARD_MAC;

    ETH_add_arp(ip, mac, 5);

    /*Sending a message*/
    uint8_t packet1_payload[4] = {14,06,20,04};

    /*Test sending*/
    while (outgoing_counter < 100) { //NOTE: after 80 packages the queue will be full!
          ETH_udp_send(ip, 8, packet1_payload, 4, 1);
          osDelay(10);
          outgoing_counter += 1;
          LOGI(TAG, "%d", outgoing_counter);
      }

}
```

# Ethernet Testing

## **Send Testing**

#### Requirements

- STM32
- Ethernet Cable
- Wireshark

#### Testing

Send testing is easy. The only thing you have to do is upload the setup and sending code to your STM32 and connect it to your PC/laptop using an Ethernet cable. Then you can go into Wireshark, look at the activity on you ethernet periphial, to see if it sends something. It also logs to the terminal if you send, if you have turned logging on.

---

## **Receive Testing**

### Requirements

- Linux laptop 🫠
- <span style="white-space: pre-wrap;">STM32 </span>
- <span style="white-space: pre-wrap;">Ethernet Cable </span>  
    Connect the Ethernet cable between the STM and the laptop that is sending you packets.
- Any pcap packet sender  
    We recommend packeth, installation manual will be below.
- Wireshark (optional)

### Installation of Packeth

<p class="callout danger">**NOTE:**<span style="white-space: pre-wrap;"> you need a Linux laptop for this</span></p>

#### Testing

Receive testing is done by sending packets, saved in test/networking, to the STM32 using a pcap packet sender. If you want to use new packets, I advise to send the packet you want from the STM32. Then use wireshark to save the packet such that you can send it back. In wireshark you can see, when you send a package, to which IP and MAC it is being send, and it should match with your IP and MAC.

To see if the messages are received, make sure you print messages in your receive function. Then check the serial

#### <span style="white-space: pre-wrap;"></span>

# Extra Functions

### Introduction

<span style="white-space: pre-wrap;">This documentation is about extra functions, that are not necessary to get it running, but can be used if need be. </span>

### Non-deprecated functions

#### ETH\_setup\_MAC\_address\_filtering

<span style="white-space: pre-wrap;">This function is used for perfect mac address filtering. It is used by giving it MAC-addresses as arguments. If you receive packets with those addresses, they will also be processed. </span>

### Deprecated Functions

There are deprecated functions for raw sending (`<span class="editor-theme-code">ETH_raw_send</span>`) and custom protocol receiving (`<span class="editor-theme-code">ETH_custom_protocol_receiver</span>`). Those can be used if you don't want to use UDP, but I don't know if they still work.

##### <span style="white-space: pre-wrap;"> Raw receiving</span>

<span style="white-space: pre-wrap;"> If you want to receive messages, you need to have </span>`<span class="editor-theme-code">#define LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif) eth_reader(netif, pbuf)</span>`<span style="white-space: pre-wrap;"> in the </span>`<span class="editor-theme-code">cubemx_main.h</span>`<span style="white-space: pre-wrap;"> file. </span>

###   


###   

# Debugging

### Introduction

This documentation gives some tips on how to debug the Ethernet Driver.

### Debug Flags

<span style="white-space: pre-wrap;">By setting the flag " LWIP\_DEBUG" to 1 in the cubemx main file, you will get extra lwip debug messages in your terminal. </span>

### Hard Faults

<span style="white-space: pre-wrap;">While debugging, it is easy to use a debugger to go step by step through your code. However, the deeper you go into the code, the bigger the chance that your debugging will trigger an hard fault. So when that happens verify if the hard fault is your doing or not, by debugging less deep while you have a debug point set on the hard fault function, to check if it still hard faults. </span>

# Issues

## Introduction

There are some issues with ethernet. Those are described here.

## The Issues

### Arp Table removement

<span style="white-space: pre-wrap;">The ARP function will fail when Ethernet is not started properly yet. Current fix is </span>`<span class="editor-theme-code">retry_count</span>`<span style="white-space: pre-wrap;"> that retries the arp x amount of times. </span>

# Protobuffers

# Starting with Protobufs

## What are protobufs?

[Protocol Buffers](https://protobuf.dev/programming-guides/proto3/)<span style="white-space: pre-wrap;"> (protobufs) are a way to define structured messages in </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> files.</span>

The point is simple: define the message format once, generate code for your language, and now everyone agrees what the bytes mean (without inventing a new packet format every semester).

## Where they live

<span style="white-space: pre-wrap;">The source of truth for RoboTeam message definitions is: </span>[RoboTeamTwente/ERC-Protobufs](https://github.com/RoboTeamTwente/ERC-Protobufs).

<span style="white-space: pre-wrap;">Other repositories (embedded, software, tooling) should </span>**consume**<span style="white-space: pre-wrap;"> these definitions (often via a git submodule) instead of copy-pasting </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> files around.</span>

## Editing rules (the important part)

### Field numbers are the real API

<span style="white-space: pre-wrap;">In protobuf, the </span>**field number**<span style="white-space: pre-wrap;"> is what goes on the wire. Renaming a field is usually harmless; changing its number is usually the problem.</span>

**Never renumber an existing field**<span style="white-space: pre-wrap;"> unless you are intentionally breaking compatibility and coordinating the update across all consumers.</span>

### Safe changes (usually)

- <span style="white-space: pre-wrap;">Add a new field with a </span>**new**<span style="white-space: pre-wrap;"> unused number.</span>
- Add new enum values (don’t reuse old numeric values for new meanings).
- Stop using a field before deleting it, and reserve its number if your style guide does that.

### Changes that need extra care

- <span style="white-space: pre-wrap;">Changing a field type (e.g. </span>`<span class="editor-theme-code">int32</span>`<span style="white-space: pre-wrap;"> → </span>`<span class="editor-theme-code">string</span>`).
- Changing semantics/units (mm vs m, degrees vs radians).

## Typical workflow

1. <span style="white-space: pre-wrap;">Edit / add </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> files in </span>`<span class="editor-theme-code">ERC-Protobufs</span>`.
2. Generate / update code as required by your project (this step is repo-specific).
3. Update consumers to use the new fields (and handle missing fields safely).
4. Test at least one real send/receive path.
5. Open a PR that lists: what changed, field numbers, and compatibility expectations.

## Troubleshooting

### “My message doesn’t parse”

- <span style="white-space: pre-wrap;">Confirm both sides use the same (or compatible) </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> definitions.</span>
- Check field numbers: no reuse, no renumbering.
- Check transport framing (length prefix / delimiter / CRC). Protobuf gives bytes; transport still matters.

### “It parses, but values are nonsense”

- Check units and semantics (the most common “it’s wrong but not broken” bug).
- Make sure generated code is up to date (stale generated files cause creative failures).

<p class="callout info">Protobufs are boring on purpose. Boring schemas prevent exciting debugging sessions. Keep changes small, reviewable, and compatible.</p>

# Design

This page explains rules that apply to all protobuffers, and a bit on how we use them.

## PBEnvelope

<span style="white-space: pre-wrap;">All protobuffers are wrapped in the </span>`<span class="editor-theme-code">PBEnvelope</span>`<span style="white-space: pre-wrap;"> message, which contains metadata we need in all protobufs.</span>

## Lost packets &amp; throttling

<span style="white-space: pre-wrap;">In every line of communications, the packets must be sent periodically. The frequency of sent packets is indicated in their </span>`<span class="editor-theme-code">PBEnvelope</span>`. If the receiver does not get a packet within given timeout, it means the packet was lost. In most cases this means that the brakes should be engaged for safety.

# Arm Board Protobuffers

***This page:** *each arm\_board protobuffer explained.**

<p class="callout success"><span style="white-space: pre-wrap;">The protobuffers for the arm\_board are passed between software, control and embedded. </span></p>

---

##### Movement\_software\_target.proto

**Software -&gt; control**

This file contains two protobuffers detailing the source of a movement.

**ArmBoardTargetMovement** contains the target coordinates for a movement. This gets sent to control, they use this information to calculate motor angles.

**ArmBoardObstructions**<span style="background-color: rgb(251, 238, 184); white-space: pre-wrap;"> is a placeholder for now</span>. The idea is that software would be able to pass a list of coordinates of physical obstructions to control, around which they would manoeuvre the movement of the arm.

---

##### Movement\_control\_in.pb.h

**Control -&gt; Embedded**

<span style="white-space: pre-wrap;">After control calculates the angles using the information from the previous protobuf, it sends the control signals back to us (embedded) with the </span><span style="background-color: rgb(248, 202, 198); white-space: pre-wrap;">absolute </span>angles and frequency for the motors.<span style="background-color: rgb(251, 238, 184); white-space: pre-wrap;"> So, this is the information embedded uses to actuate the motors.</span>

**ArmboardControlSignals**<span style="background-color: rgb(248, 202, 198); white-space: pre-wrap;"> contains all the angles the motors need to be turned to</span>. We then use this information to set the PWM pins.

---

##### Movement\_software\_feedback.proto

**Embedded -&gt; Software**

After the movement happens (or fails), embedded will send feedback to software for them to calculate the next movement.

**ArmBoardMovementFeedback** sends an error code to software.

Possible error codes are:

- Point\_not\_in\_range
- Obstruction
- Calibration
- Motor\_malfunction
- All\_ok

**ArmBoardActualPositions** <span style="white-space: pre-wrap;">sends back the angle of each motor of the arm. Software will use this to display a 3D model of the arm position. </span><span style="background-color: rgb(248, 202, 198);">(Allegedly)</span>

---

##### Motor\_diagnostics.proto

**Arm board -&gt; Debugging board**

This protobuf gets periodically sent to the debugging board to indicate the status.

**ArmBoardDiagnostics** <span style="white-space: pre-wrap;">the </span>`<span class="editor-theme-code">MotorInformation</span>`<span style="white-space: pre-wrap;"> for each motor and the state of the </span>**entire** <span style="white-space: pre-wrap;">board. </span>

Possible states are:

- Idle
- Operating
- Calibrating
- Errored

<p class="callout info">**MotorInformation** is a common protobuf shared between all boards that use motors (this and driving board). MotorInformation contains the state (same as above), motor\_id, rpm, voltage and encoder\_angle for a single motor.</p>

# Sensor Board Protobuf

***This page:** *Complete protobuf message definitions for the sensor\_board component.**

<p class="callout success">The protobuffers for the sensor\_board are passed between embedded, the network, and other boards for diagnostics and data collection. This page documents the actual message definitions from the ERC-Protobufs repository.</p>

---

## Common Definitions (sensor.proto)

**Location: ERC-Protobufs/components/sensor\_board/sensor.proto**

### SensorState Enum

Shared across all sensor types - indicates the current operational state of a sensor.

```protobuf
enum SensorState {
  SENSOR_IDLE = 0;           // Sensor is idle/not currently sampling
  SENSOR_OPERATING = 1;      // Sensor is actively sampling/operating normally
  SENSOR_CALIBRATING = 2;    // Sensor is in calibration mode
  SENSOR_ERROR = 3;          // Sensor has encountered an error
}
```

---

## GPS Sensor (gps\_sensor.proto)

**Location: ERC-Protobufs/components/sensor\_board/gps\_sensor.proto**

**Direction: Sensor Board → Navigation/Localization Systems**

### GPSFixQuality Enum

Indicates the type and quality of GPS fix obtained.

```protobuf
enum GPSFixQuality {
  NO_FIX = 0;       // No fix available
  GPS_FIX = 1;      // Standard GPS fix
  DGPS_FIX = 2;     // Differential GPS fix
  PPS_FIX = 3;      // PPS (Pulse Per Second) fix
  RTK_FIX = 4;      // Real-Time Kinematic fix
  RTK_FLOAT = 5;    // RTK float solution
}
```

### GPSErrorCode Enum

Detailed error codes for GPS/GNSS sensor failures.

```protobuf
enum GPSErrorCode {
  GPS_NO_ERROR = 0;
  GPS_COMMUNICATION_FAILURE = 1;   // UART connection lost
  GPS_INVALID_DATA = 2;             // Data parsing or validation failed
  GPS_ANTENNA_FAULT = 3;            // GPS antenna disconnected or faulty
  GPS_LOW_SIGNAL_QUALITY = 4;       // Signal strength too weak for fix
}
```

### SensorBoardGPSInfo Message

Complete GPS positioning and velocity information.

```protobuf
message SensorBoardGPSInfo {
  // GPS coordinates
  double latitude = 1;           // Degrees (positive = North, negative = South)
  double longitude = 2;          // Degrees (positive = East, negative = West)
  float altitude = 3;            // Meters above sea level
  
  // Velocity data
  float speed = 4;               // Speed in meters per second
  float heading = 5;             // Course over ground in degrees (0-360)
  
  // Position accuracy and quality
  float hdop = 6;                // Horizontal dilution of precision (lower is better)
  float vdop = 7;                // Vertical dilution of precision (lower is better)
  int32 satellites = 8;          // Number of satellites in view (up to 16)
  
  GPSFixQuality fix_quality = 9;
  
  SensorState state = 10;
  GPSErrorCode error_code = 11;  // Error code if state is SENSOR_ERROR
  
  // Timestamp from GPS
  int64 utc_timestamp = 12;      // Unix timestamp in milliseconds
}
```

---

## IMU Sensor (imu\_sensor.proto)

**Location: ERC-Protobufs/components/sensor\_board/imu\_sensor.proto**

**Direction: Sensor Board → Motion Control/Attitude Systems**

### IMUErrorCode Enum

Detailed error codes for IMU sensor failures.

```protobuf
enum IMUErrorCode {
  IMU_NO_ERROR = 0;
  IMU_COMMUNICATION_FAILURE = 1;      // I2C/SPI connection lost
  IMU_CALIBRATION_REQUIRED = 2;       // Sensor needs calibration
  IMU_CALIBRATION_FAILED = 3;         // Calibration procedure failed
  IMU_INVALID_DATA = 4;               // Data out of valid range
  IMU_SENSOR_FAULT = 5;               // Hardware fault detected
  IMU_GYROSCOPE_ERROR = 6;            // Gyroscope component failure
  IMU_MAGNETOMETER_ERROR = 7;         // Magnetometer component failure
  IMU_ACCELEROMETER_ERROR = 8;        // Accelerometer component failure
}
```

### SensorBoardIMUInfo Message

3-axis inertial measurement data (acceleration, angular velocity, magnetic field).

```protobuf
message SensorBoardIMUInfo {
  // 3-Axis Accelerometer data
  float accel_x = 1;             // X-axis acceleration
  float accel_y = 2;             // Y-axis acceleration
  float accel_z = 3;             // Z-axis acceleration
  
  // 3-Axis Gyroscope data
  float gyro_x = 4;              // X-axis angular velocity
  float gyro_y = 5;              // Y-axis angular velocity
  float gyro_z = 6;              // Z-axis angular velocity
  
  // 3-Axis Magnetometer data (9-axis IMU only)
  float mag_x = 7;               // X-axis magnetic field
  float mag_y = 8;               // Y-axis magnetic field
  float mag_z = 9;               // Z-axis magnetic field

  bool is_calibrated = 13;       // True if IMU has been calibrated on level surface
  
  SensorState state = 14;
  IMUErrorCode error_code = 15;  // Error code if state is SENSOR_ERROR
}
```

---

## pH Sensor (ph\_sensor.proto)

**Location: ERC-Protobufs/components/sensor\_board/ph\_sensor.proto**

**Direction: Sensor Board → Environmental Monitoring Systems**

### PHErrorCode Enum

Detailed error codes for pH sensor failures.

```protobuf
enum PHErrorCode {
  PH_NO_ERROR = 0;
  PH_COMMUNICATION_FAILURE = 1;       // ADC connection lost
  PH_OUT_OF_RANGE = 2;                // Reading outside 0-14 range
  PH_CALIBRATION_REQUIRED = 3;        // Sensor needs calibration
  PH_INVALID_DATA = 4;                // Data validation failed
  PH_PROBE_FAULT = 5;                 // Electrode fault detected
  PH_TEMPERATURE_SENSOR_ERROR = 6;    // Temperature compensation sensor failed
}
```

### SensorBoardPHInfo Message

Water quality measurement with temperature compensation.

```protobuf
message SensorBoardPHInfo {
  float ph_value = 1;            // Raw pH measurement (0-14 scale, 7 is neutral)
  float voltage = 2;             // Voltage reading from pH sensor in millivolts
  float temperature = 3;         // Temperature reading in Celsius (if external sensor available)
  
  SensorState state = 4;         // Current state of the pH sensor
  PHErrorCode error_code = 5;    // Error code if state is SENSOR_ERROR
}
```

**Notes:**

- pH value is derived from voltage via linear calibration equation
- 40-sample moving average applied internally to reduce ADC noise
- Temperature used for automatic compensation (default ~25°C)

---

## Load Cell Sensor (load\_cell.proto)

**Location: ERC-Protobufs/components/sensor\_board/load\_cell.proto**

**Direction: Sensor Board → Arm Control/Gripper Systems**

### LoadCellErrorCode Enum

Detailed error codes for load cell sensor failures.

```protobuf
enum LoadCellErrorCode {
  LOAD_CELL_NO_ERROR = 0;
  LOAD_CELL_COMMUNICATION_FAILURE = 1;   // ADC connection lost
  LOAD_CELL_OUT_OF_RANGE = 2;            // Force measurement exceeds limits
  LOAD_CELL_CALIBRATION_REQUIRED = 3;    // Sensor needs calibration
  LOAD_CELL_INVALID_DATA = 4;            // Data validation failed
  LOAD_CELL_SENSOR_FAULT = 5;            // Hardware fault detected
}
```

### SensorBoardLoadCellInfo Message

Force measurement for robotic gripper control and load monitoring.

```protobuf
message SensorBoardLoadCellInfo {
  uint32 sensor_index = 1;               // 0-based index of the load cell (0 or 1)
  float force_newtons = 2;               // Force in Newtons
  float mass_grams = 3;                  // Mass in grams (derived from force)
  int32 raw_counts = 4;                  // Raw ADC counts (for debugging)

  // Calibration status and parameters
  bool is_calibrated = 5;                // True if calibration parameters are valid
  float scale_newtons_per_count = 6;     // Conversion factor (N/count)
  int32 tare_offset_counts = 7;          // Zero-load ADC offset

  SensorState state = 8;
  LoadCellErrorCode error_code = 9;      // Error code if state is SENSOR_ERROR
}
```

**Dual Sensor Support:**

- Up to 2 independent load cells (sensor\_index 0 and 1)
- Typically used on dual-pad grippers for load distribution feedback
- Each sensor maintains independent calibration parameters
- Enables slip detection via load imbalance analysis

---

## Pressure Sensor (pressure\_sensor.proto)

**Location: ERC-Protobufs/components/sensor\_board/pressure\_sensor.proto**

**Direction: Sensor Board → Arm Control/Gripper Systems / Environmental Monitoring**

### PressureErrorCode Enum

Detailed error codes for pressure sensor failures.

```protobuf
enum PressureErrorCode {
  PRESSURE_NO_ERROR = 0;
  PRESSURE_COMMUNICATION_FAILURE = 1;    // I2C/ADC connection lost
  PRESSURE_OUT_OF_RANGE = 2;             // Reading exceeds sensor limits
  PRESSURE_CALIBRATION_REQUIRED = 3;     // Sensor needs calibration
  PRESSURE_INVALID_DATA = 4;             // Data validation failed
  PRESSURE_SENSOR_FAULT = 5;             // Hardware fault detected
}
```

### SensorBoardPressureInfo Message

Pressure measurement for robotic gripper control and environmental sensing.

```protobuf
message SensorBoardPressureInfo {
  uint32 sensor_index = 1;               // 0-based index of the pressure sensor (0 or 1)

  // Pressure data
  float pressure_kpa = 2;                // Pressure in kilopascals
  float temperature_c = 3;               // Temperature in Celsius (if available)
  float voltage = 4;                     // Sensor output voltage (if available)

  // Calibration status
  bool is_calibrated = 5;                // True if calibration parameters are valid

  SensorState state = 6;
  PressureErrorCode error_code = 7;      // Error code if state is SENSOR_ERROR
}
```

**Dual Sensor Support:**

- Up to 2 independent pressure sensors (sensor\_index 0 and 1)
- Primary use: gripper pad force feedback for PID control
- Enables real-time grip force regulation and slip detection
- Secondary uses: depth sensing, altitude sensing, system pressure monitoring

---

## Board Diagnostics (diagnostics.proto)

**Location: ERC-Protobufs/components/sensor\_board/diagnostics.proto**

**Direction: Sensor Board → Debugging Board / Health Monitoring Systems**

### SensorBoardDiagnostics Message

Complete system health and status snapshot sent periodically (5 second default).

```protobuf
message SensorBoardDiagnostics {
  enum State {
    IDLE = 0;
    OPERATING = 1;
    CALIBRATING = 2;
    ERROR = 3;
  }

  State state = 1;                       // Overall board state
  SensorBoardPHInfo ph_sensor = 2;       // pH sensor status
  SensorBoardIMUInfo imu_sensor = 3;     // IMU sensor status

  // Overall sensor board health
  float board_temperature = 4;           // Board temperature in Celsius
  float board_voltage = 5;               // System voltage (3.3V supply)
  
  SensorBoardGPSInfo gps_sensor_1 = 6;   // GPS sensor status
}
```

**Board States:**

- **IDLE (0)**<span style="white-space: pre-wrap;"> - Board initialized but not actively polling sensors</span>
- **OPERATING (1)**<span style="white-space: pre-wrap;"> - All sensors functioning normally, data being collected</span>
- **CALIBRATING (2)**<span style="white-space: pre-wrap;"> - Board or sensors in calibration mode</span>
- **ERROR (3)**<span style="white-space: pre-wrap;"> - Critical system failure detected</span>

**Health Indicators:**

- **board\_temperature**<span style="white-space: pre-wrap;"> - STM32H753 die temperature; normal range 25-75°C</span>
- **board\_voltage**<span style="white-space: pre-wrap;"> - 3.3V supply input; should be 3.0-3.6V</span>
- **Embedded sensor states**<span style="white-space: pre-wrap;"> - Each sensor's current SensorState (IDLE/OPERATING/CALIBRATING/ERROR)</span>

---

## Error Code Pattern

All sensor error codes follow a consistent pattern:

```protobuf
enum XXXErrorCode {
  XXX_NO_ERROR = 0;                      // No error
  XXX_COMMUNICATION_FAILURE = 1;         // Hardware interface (I2C/SPI/UART/ADC) failure
  XXX_OUT_OF_RANGE = 2;                  // Reading outside valid sensor range
  XXX_CALIBRATION_REQUIRED = 3;          // Sensor needs calibration
  XXX_INVALID_DATA = 4;                  // Data validation/filtering failed
  XXX_SENSOR_FAULT = 5;                  // Hardware fault or component failure
  // Additional sensor-specific errors...
}
```

**Integration into application:**

```c
if (gps_result.error_code == GPS_COMMUNICATION_FAILURE) {
    LOG_ERROR("GPS UART connection lost");
    diagnostics.gps_sensor_1.state = SENSOR_ERROR;
} else if (gps_result.error_code == GPS_NO_ERROR) {
    LOG_INFO("GPS position: %f, %f", gps_result.latitude, gps_result.longitude);
}
```

---

## Network Transmission

**Main Sensor Data:**

- Sent via individual sensor messages
- Packaged into SensorBoardState (sensor.proto)
- UDP broadcast to 192.168.0.255:7 (configurable)
- Interval: 5 seconds default (configurable)
- Encoding: Nanopb (lightweight protobuf for embedded)

**Diagnostics Data:**

- Sent via SensorBoardDiagnostics message
- UDP broadcast to 192.168.0.255:7 (same port)
- Interval: 5 seconds (synced with main loop)
- Contains snapshot of all sensor states + health metrics

---

## Message Composition

The SensorBoardDiagnostics message is composed of individual sensor message types:

```
SensorBoardDiagnostics
├── state (Board state)
├── SensorBoardPHInfo
│   ├── ph_value
│   ├── voltage
│   ├── temperature
│   ├── state (SensorState)
│   └── error_code (PHErrorCode)
├── SensorBoardIMUInfo
│   ├── accel_x, accel_y, accel_z
│   ├── gyro_x, gyro_y, gyro_z
│   ├── mag_x, mag_y, mag_z
│   ├── is_calibrated
│   ├── state (SensorState)
│   └── error_code (IMUErrorCode)
├── board_temperature
├── board_voltage
└── SensorBoardGPSInfo
    ├── latitude, longitude, altitude
    ├── speed, heading
    ├── hdop, vdop, satellites
    ├── fix_quality
    ├── state (SensorState)
    ├── error_code (GPSErrorCode)
    └── utc_timestamp
```

**Note:**<span style="white-space: pre-wrap;"> Not all proto files define load cells and pressure sensors in diagnostics.proto yet - they are sent as separate messages when available.</span>

# Driving Board Protobuffers

***This page:** *each driving\_board protobuffer explained.**

<p class="callout success"><span style="white-space: pre-wrap;">The protobuffers for the driving\_board are passed between software, control and embedded. </span></p>

<span style="white-space: pre-wrap;">DrivingBoardDiagnostics.proto </span>

<span style="white-space: pre-wrap;">Embedded → Software / Debugging </span>

<span style="white-space: pre-wrap;">This file contains the full diagnostic state of the driving board and all attached motors. </span>

<span style="white-space: pre-wrap;">DrivingBoardDiagnostics is the main status message used to report the system state and motor-level health information. </span>

##### <span style="white-space: pre-wrap;">DrivingBoardDiagnostics </span>

<span style="white-space: pre-wrap;">This message contains: </span>

- <span style="white-space: pre-wrap;">Overall board state (IDLE, OPERATING, CALIBRATING, ERROR) </span>
- <span style="white-space: pre-wrap;">Motor information for all driving and steering motors </span>

<span style="white-space: pre-wrap;">The board state is used to indicate the current operating mode of the driving board. </span>

**MotorInformation fields:**

<span style="white-space: pre-wrap;">Each motor is reported using the MotorInformation protobuf, which includes: </span>

- <span style="white-space: pre-wrap;">motor state </span>
- <span style="white-space: pre-wrap;">motor ID </span>
- <span style="white-space: pre-wrap;">RPM </span>
- <span style="white-space: pre-wrap;">voltage </span>
- encoder angle

<span style="white-space: pre-wrap;">Motors included The diagnostics message explicitly contains 10 motors: front\_left\_motor middle\_left\_motor back\_left\_motor front\_right\_motor middle\_right\_motor back\_right\_motor steering\_front\_left\_motor steering\_back\_left\_motor steering\_front\_right\_motor steering\_back\_right\_motor </span>

<p class="callout info"><span style="white-space: pre-wrap;">Notes: This structure is currently fixed-size, meaning all motors are explicitly defined instead of using a repeated field. </span></p>

##### <span style="white-space: pre-wrap;">DrivingBoardMotorMessage.proto </span>

<span style="white-space: pre-wrap;">Software → Embedded </span>

<span style="white-space: pre-wrap;">This message is used to send motion commands to the driving board. </span>

<span style="white-space: pre-wrap;">DrivingBoardMotorMessage Contains high-level movement instructions: </span>

- <span style="white-space: pre-wrap;">distance\_to\_go → target travel distance </span>
- <span style="white-space: pre-wrap;">turning\_radius </span>

<span style="white-space: pre-wrap;">Purpose: This message defines a movement request from software. The embedded system uses it as input to compute values on the control code. </span>

##### <span style="white-space: pre-wrap;">DrivingBoardMotorPeriodicProgress.proto </span>

<span style="white-space: pre-wrap;">Embedded → Software </span>

<span style="white-space: pre-wrap;">This message provides runtime feedback during movement execution. DrivingBoardMotorPeriodicProgress </span>

<span style="white-space: pre-wrap;">Contains: </span>

- <span style="white-space: pre-wrap;">distance\_left → remaining distance to target </span>

Purpose This message allows software to track progress of an ongoing movement command in real time. It is typically sent periodically while a movement is being executed.