# 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);
      }

}
```