Skip to content

Commit

Permalink
Add multidrop support to LAN867x tcp transfer example
Browse files Browse the repository at this point in the history
  • Loading branch information
bogdankolendovskyy committed May 2, 2024
1 parent 22ce262 commit bf2c4c6
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 33 deletions.
57 changes: 35 additions & 22 deletions lan867x/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,38 @@ phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
esp_eth_phy_t *phy = esp_eth_phy_new_lan867x(&phy_config);
```

#### You may need to setup the PLCA

Depending on the task you are doing some additional setup may be required.
PLCA configuration is done using `esp_eth_ioctl` function with one of following commands.

| Command | Argument | Action |
|------------------------------------|------------------|-----------------------------------------------------------------------------------------------------------|
| LAN867X_ETH_CMD_S_EN_PLCA | bool* enable | Enable (if true) or disable PLCA |
| LAN867X_ETH_CMD_G_EN_PLCA | bool* enable | Write PLCA status (true if enabled) to the location via pointer |
| LAN867X_ETH_CMD_S_PLCA_NCNT | uint8_t* count | Set node count to the value passed through the pointer |
| LAN867X_ETH_CMD_G_PLCA_NCNT | uint8_t* count | Write node count configured in the PLCA to the location via pointer |
| LAN867X_ETH_CMD_S_PLCA_ID | uint8_t* id | Set ID to the value passed through the pointer |
| LAN867X_ETH_CMD_G_PLCA_ID | uint8_t* id | Write ID configured in the PLCA to the location via pointer |
| LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY | uint8_t* node_id | Add additional transmit opportunity for chosen node |
| LAN867X_ETH_CMD_RM_TX_OPPORTUNITY | uint8_t* node_id | Remove additional transmit opportunity for chosen node |
| LAN867X_ETH_CMD_S_MAX_BURST_COUNT | uint8_t* maxcnt | Set max count of additonal packets transmitted during one frame, or 0 to disable PLCA burst |
| LAN867X_ETH_CMD_G_MAX_BURST_COUNT | uint8_t* maxcnt | Write max count of additonal packets transmitted during one frame to the location via pointer |
| LAN867X_ETH_CMD_S_BURST_TIMER | uint8_t* time | Set time during which additional packets can be sent in incriments of 100ns |
| LAN867X_ETH_CMD_G_BURST_TIMER | uint8_t* time | Write time during which additional packets can be sent in increments of 100ns to the location via pointer |
| LAN768X_ETH_CMD_PLCA_RST | | Perform reset of the PLCA |

and you are ready to use the Ethernet driver as you are used to. For more information of how to use ESP-IDF Ethernet driver, visit [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_eth.html).
#### Collision avoidance methods: CSMA/CD and PLCA

In most cases you can rely on the default CSMA/CD (Carrier Sense Multiple Access / Collision Detection) provided by the MAC layer to avoid collisions when connecting multiple nodes. It requires zero configuration to work and does a good job of coordinating when data is sent. However, due to its nature, the delays between transmissions are random. For timing-critical applications, deterministic PLCA (PHY-Level Collision Avoidance) provides a guaranteed time period between transmissions along with improved throughput. Read more about PLCA and its usage at [ieee802.org](https://www.ieee802.org/3/cg/public/July2018/PLCA%20FAQ.pdf).

PLCA requires setting at least PLCA ID, node count, and selecting one node as a coordinator but ensures precise and repeatable time between transmissions, and due to the flexibility of the transmit opportunity timer, burst mode and additional transmit opportunities you can fine-tune it to your needs.

#### Configuring PLCA

PLCA configuration is done using the `esp_eth_ioctl` function with one of the following commands.

| Command | Argument | Action |
|------------------------------------|------------------|-----------------------------------------------------------------------------------------------|
| LAN867X_ETH_CMD_S_EN_PLCA | bool* enable | Enable (if true) or disable PLCA, PHY will use CDMA/CD if disabled |
| LAN867X_ETH_CMD_G_EN_PLCA | bool* enable | Write PLCA status (true if enabled) to the location via pointer |
| LAN867X_ETH_CMD_S_PLCA_NCNT | uint8_t* count | Set node count to the value passed through the pointer |
| LAN867X_ETH_CMD_G_PLCA_NCNT | uint8_t* count | Write node count configured in the PLCA to the location via pointer |
| LAN867X_ETH_CMD_S_PLCA_ID | uint8_t* id | Set ID to the value passed through the pointer |
| LAN867X_ETH_CMD_G_PLCA_ID | uint8_t* id | Write ID configured in the PLCA to the location via pointer |
| LAN867X_ETH_CMD_S_PLCA_TOT | uint8_t* time | Set PLCA Transmit Opportunity Timer in BTs |
| LAN867X_ETH_CMD_G_PLCA_TOT | uint8_t* time | Write Transmit Opportunity Timer value in BTs |
| LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY | uint8_t* node_id | Add additional transmit opportunity for chosen node |
| LAN867X_ETH_CMD_RM_TX_OPPORTUNITY | uint8_t* node_id | Remove additional transmit opportunity for chosen node |
| LAN867X_ETH_CMD_S_MAX_BURST_COUNT | uint8_t* maxcnt | Set max count of additonal packets transmitted during one frame, or 0 to disable PLCA burst |
| LAN867X_ETH_CMD_G_MAX_BURST_COUNT | uint8_t* maxcnt | Write max count of additonal packets transmitted during one frame to the location via pointer |
| LAN867X_ETH_CMD_S_BURST_TIMER | uint8_t* time | Set time during which additional packets in BTs |
| LAN867X_ETH_CMD_G_BURST_TIMER | uint8_t* time | Write time during which additional packets can be sent in BTs to the location via pointer |
| LAN768X_ETH_CMD_PLCA_RST | | Perform reset of the PLCA |

One of the devices on the network must be a **coordinator** for which you need to set _node count_ to amount of connected nodes, and _ID_ to 0.
On all other nodes, only _ID_ is required, which must be unique for every node.

After that the device is ready and the Ethernet driver can be used as normal. For more information on how to use the ESP-IDF Ethernet driver, visit the [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_eth.html).

---
BT - Bit time with a value of 100 ns
15 changes: 15 additions & 0 deletions lan867x/examples/lan867x_tcp_sockets/client/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
menu "Example Configuration"
config EXAMPLE_LAN867X_USE_PLCA
bool "Use PLCA"
default n
help
Use PLCA istead of CSMA/CD, in some cases PLCA may be more robust against collisions

config EXAMPLE_LAN867X_PLCA_ID
depends on EXAMPLE_LAN867X_USE_PLCA
int "LAN867x PLCA id"
range 1 255
default 1
help
Set PLCA id (each device in the network must have unique PLCA id)
endmenu
21 changes: 18 additions & 3 deletions lan867x/examples/lan867x_tcp_sockets/client/main/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_eth_phy_lan867x.h"
#include "esp_event.h"
#include "esp_log.h"
#include "ethernet_init.h"
#include "lwip/sockets.h"
#include "sdkconfig.h"

#define SOCKET_ADDRESS "192.168.1.1"
#define SOCKET_PORT 5000
Expand Down Expand Up @@ -49,8 +51,21 @@ void app_main(void)
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0])));
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
// Configure PLCA
#if CONFIG_EXAMPLE_LAN867X_USE_PLCA
// Configure PLCA with node number from config
uint8_t plca_id = CONFIG_EXAMPLE_LAN867X_PLCA_ID;
esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_PLCA_ID, &plca_id);
bool plca_en = true;
esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_EN_PLCA, &plca_en);
#endif // otherwise rely on CSMA/CD
// Start Ethernet driver
esp_eth_start(eth_handles[0]);
// Get mac address and save it as a string
char mac_str[18];
uint8_t mac_data[6];
esp_eth_ioctl(eth_handles[0], ETH_CMD_G_MAC_ADDR, &mac_data);
sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", mac_data[0], mac_data[1], mac_data[2], mac_data[3], mac_data[4], mac_data[5]);
// Initialize Berkley socket
char txbuffer[SOCKET_MAX_LENGTH] = {0};
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
Expand All @@ -61,10 +76,10 @@ void app_main(void)
// Wait until IP address is assigned to this device
xSemaphoreTake(xGotIpSemaphore, portMAX_DELAY);
int transmission_cnt = 0;
connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
while (1) {
connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
snprintf(txbuffer, SOCKET_MAX_LENGTH, "Transmission #%d. Hello from ESP32 via LAN867x", ++transmission_cnt);
int bytesSent = send(client_fd, txbuffer, strlen(txbuffer), 0);
snprintf(txbuffer, SOCKET_MAX_LENGTH, "Transmission #%d. Hello from ESP32 (%s) via LAN867x", ++transmission_cnt, mac_str);
int bytesSent = send(client_fd, txbuffer, SOCKET_MAX_LENGTH, 0);
ESP_LOGI(TAG, "Sent tranmission #%d which was %d bytes long.", transmission_cnt, bytesSent);
vTaskDelay(pdMS_TO_TICKS(500));
}
Expand Down
15 changes: 15 additions & 0 deletions lan867x/examples/lan867x_tcp_sockets/server/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
menu "Example Configuration"
config EXAMPLE_LAN867X_USE_PLCA
bool "Use PLCA"
default n
help
Use PLCA istead of CSMA/CD, in some cases PLCA may be more robust against collisions

config EXAMPLE_LAN867X_PLCA_NODE_COUNT
depends on EXAMPLE_LAN867X_USE_PLCA
int "PLCA node count"
range 2 255
default 2
help
Set PLCA node count (amount of nodes in the network including this one)
endmenu
47 changes: 40 additions & 7 deletions lan867x/examples/lan867x_tcp_sockets/server/main/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@
#include "esp_netif.h"
#include "esp_netif_net_stack.h"
#include "esp_eth.h"
#include "esp_eth_phy_lan867x.h"
#include "esp_event.h"
#include "esp_log.h"
#include "ethernet_init.h"
#include "lwip/sockets.h"

#define SOCKET_PORT 5000
#define LISTENER_MAX_QUEUE 1
#define LISTENER_MAX_QUEUE 10
#define SOCKET_MAX_LENGTH 128

static const char *TAG = "lan867x_server";

/** Event handler for IP_EVENT_ETH_GOT_IP */
/* Structure to store information about individual connection */
struct connection_info {
int fd;
struct sockaddr_in address;
};

/* Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *data)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *) data;
Expand Down Expand Up @@ -69,26 +76,52 @@ void app_main(void)
// Stop dhcp client and set a static IP address
esp_netif_dhcpc_stop(eth_netif);
esp_netif_set_ip_info(eth_netif, &ip_info);
#if CONFIG_EXAMPLE_LAN867X_USE_PLCA
// Configure PLCA as coordinator
uint8_t plca_nodes_count = CONFIG_EXAMPLE_LAN867X_PLCA_NODE_COUNT;
esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_PLCA_NCNT, &plca_nodes_count);
uint8_t plca_id = 0;
esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_PLCA_ID, &plca_id);
uint8_t plca_max_burst_count = 0;
esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_MAX_BURST_COUNT, &plca_max_burst_count);
bool plca_en = false;
esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_EN_PLCA, &plca_en);
#endif // otherwise rely on CSMA/CD
// Start Ethernet driver state machine
ESP_ERROR_CHECK(esp_eth_start(eth_handles[0]));
// Initialize Berkley socket which will listen on port SOCKET_PORT for transmission from client
char rxbuffer[SOCKET_MAX_LENGTH] = {0};
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in address;
socklen_t addrlen = sizeof(address);
fd_set ready;
struct timeval to = {.tv_sec = 0, .tv_usec = 500000};
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(SOCKET_PORT);
bind(server_fd, (struct sockaddr *)&address, sizeof(address));
// listen and wait for transmission to come
struct connection_info connections[LISTENER_MAX_QUEUE];
int active_connections_count = 0;
listen(server_fd, LISTENER_MAX_QUEUE);
int new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen);
while (1) {
// when transmission comes - print it
recv(new_socket, rxbuffer, SOCKET_MAX_LENGTH, 0);
printf("Received: \"%s\" from %s.\n", rxbuffer, inet_ntoa(address.sin_addr));
// Check if any connections are pending
FD_ZERO(&ready);
FD_SET(server_fd, &ready);
select(server_fd + 1, &ready, 0, 0, &to);
if (FD_ISSET(server_fd, &ready)) {
// accept new connection
struct sockaddr_in *current_address_ptr = &connections[active_connections_count].address;
connections[active_connections_count].fd = accept(server_fd, (struct sockaddr *) current_address_ptr, &addrlen);
active_connections_count++;
}
for (int i = 0; i < active_connections_count; i++) {
// when transmission comes - print it
memset(rxbuffer, 0, SOCKET_MAX_LENGTH);
read(connections[i].fd, rxbuffer, SOCKET_MAX_LENGTH);
printf("Received: \"%s\" from %s.\n", rxbuffer, inet_ntoa(connections[i].address.sin_addr));
}
}
close(new_socket);
}
2 changes: 1 addition & 1 deletion lan867x/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "0.9.1"
version: "1.0.0"
targets:
- esp32
- esp32p4
Expand Down
2 changes: 2 additions & 0 deletions lan867x/include/esp_eth_phy_lan867x.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ typedef enum {
LAN867X_ETH_CMD_G_PLCA_NCNT, /*!< Get PLCA node count */
LAN867X_ETH_CMD_S_PLCA_ID, /*!< Set PLCA ID */
LAN867X_ETH_CMD_G_PLCA_ID, /*!< Get PLCA ID */
LAN867x_ETH_CMD_S_PLCA_TOT, /*!< Set PLCA Transmit Opportunity Timer in incriments of 100ns */
LAN867x_ETH_CMD_G_PLCA_TOT, /*!< Get PLCA Transmit Opportunity Timer in incriments of 100ns */
LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY, /*!< Add additional transmit opportunity for chosen node */
LAN867X_ETH_CMD_RM_TX_OPPORTUNITY, /*!< Remove additional transmit opportunity for chosen node */
LAN867X_ETH_CMD_S_MAX_BURST_COUNT, /*!< Set max count of additional packets, set to 0 to disable */
Expand Down
19 changes: 19 additions & 0 deletions lan867x/src/esp_eth_phy_lan867x.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ typedef union {
} lan867x_plca_ctrl1_reg_t;
#define ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR (0xCA02)

typedef union {
struct {
uint8_t reserved1; // Reserved
uint8_t totmr; // Transmit Opportunity Timer
};
uint32_t val;
} lan867x_plca_totmr_reg_t;
#define ETH_PHY_PLCA_TOTMR_REG_MMD_ADDR (0xCA04)

typedef union {
struct {
uint8_t maxbc; // Maximum burst count
Expand Down Expand Up @@ -207,6 +216,7 @@ static esp_err_t lan867x_custom_ioctl(esp_eth_phy_t *phy, uint32_t cmd, void *da
esp_eth_mediator_t *eth = phy_802_3->eth;
lan867x_plca_ctrl0_reg_t plca_ctrl0;
lan867x_plca_ctrl1_reg_t plca_ctrl1;
lan867x_plca_totmr_reg_t plca_totmr;
lan867x_plca_burst_reg_t plca_burst_reg;
lan867x_plca_multiple_id_reg_t plca_multiple_id_reg;
switch (cmd) {
Expand Down Expand Up @@ -244,6 +254,15 @@ static esp_err_t lan867x_custom_ioctl(esp_eth_phy_t *phy, uint32_t cmd, void *da
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR, &plca_ctrl1.val), err, TAG, "read PLCA_CTRL1 failed");
*((uint8_t *) data) = plca_ctrl1.id;
break;
case LAN867x_ETH_CMD_S_PLCA_TOT:
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_TOTMR_REG_MMD_ADDR, &plca_totmr.val), err, TAG, "read PLCA_TOTMR failed");
plca_totmr.totmr = *((uint8_t *) data);
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_TOTMR_REG_MMD_ADDR, plca_totmr.val), err, TAG, "write PLCA_TOTMR failed");
break;
case LAN867x_ETH_CMD_G_PLCA_TOT:
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_TOTMR_REG_MMD_ADDR, &plca_totmr.val), err, TAG, "read PLCA_TOTMR failed");
*((uint8_t *) data) = plca_totmr.totmr;
break;
case LAN768X_ETH_CMD_PLCA_RST:
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL0_REG_MMD_ADDR, &plca_ctrl0.val), err, TAG, "read PLCA_CTRL0 failed");
plca_ctrl0.rst = true;
Expand Down

0 comments on commit bf2c4c6

Please sign in to comment.