ethernet: add iperf example to test real bandwidth

1. Add command for Ethernet based on console component.
2. Make cmd_system and iperf a component that can be referenced by other examples
3. Add "version" command to cmd_system.c
4. Clean up the README.md in all ethernet examples[TW#26525]
This commit is contained in:
morris
2018-09-20 19:26:14 +08:00
parent 7313e39fde
commit 7bc36d23e1
36 changed files with 1264 additions and 279 deletions

View File

@@ -1,73 +1,108 @@
# Ethernet Example
Initialises the Ethernet interface and enables it, then sends DHCP requests and tries to obtain a DHCP lease. If successful then you will be able to ping the device.
(See the README.md file in the upper level 'examples' directory for more information about examples.)
# PHY Configuration
Use `make menuconfig` to set the PHY model and the PHY address, and configure the SMI I/O pins (see below). These configuration items will vary depending on the hardware configuration you are using.
## Overview
The default example configuration is correct for Espressif's Ethernet board with TLK110 PHY. Other hardware will require different configuration and/or changes to the example.
This example demonstrates basic usage of `Ethernet driver` together with `tcpip_adapter`. The work flow of the example could be as follow:
## PHY Address
The PHY address depends on the hardware and the PHY configuration. Consult the documentation/datasheet for the PHY hardware you have.
1. Initialises the Ethernet interface and enables it
2. Send DHCP requests and tries to obtain a DHCP lease
3. If successful then you will be able to ping the device
* Address 31 (default) for Espressif's Ethernet board with TLK110 PHY
* Address 1 for the common Waveshare LAN8720 PHY breakout
* Address 0 for other LAN8720 breakouts
If you have a new Ethernet applicaion to go (for example, connect to IoT cloud via Ethernet), try this as a basic template, then add your own code.
If the PHY address is incorrect then the EMAC will initialise, but all attempts to read/write configuration registers on the PHY will fail.
## How to use example
## PHY Clock Wiring
The ESP32 and the Ethernet PHY need a common 50MHz reference clock. This clock can either be be provided externally by a crystal oscillator (e.g. crystal connected to the PHY or a seperate crystal oscillator) or internally by using the EPS32's APLL.
### Hardware Required
Because of its freqency the signal integrity has to be observed (ringing, capacitive load, resisitive load, skew, length of PCB trace). It is recommended to add a 33Ω resistor in series to reduce ringing.
To run this example, you should have one ESP32 dev board integrated with an Ethernet interface, or just connect your ESP32 core board to a breakout board which featured in RMII Etherent PHY. Currently esp-idf officially support only two Ethernet PHY: `TLK110` from Texas Instruments and `LAN8720` from microchip.
Possible configurations of the 50MHz clock signal:
### Configure the project
| Mode | GPIO Pin | Signal name | Notes |
| -------- | -------- | -------------- | -------------------------------------------------------------------------------------------------- |
| external | `GPIO0` | `EMAC_TX_CLK` | Input of 50MHz PHY clock |
| internal | `GPIO16` | `EMAC_CLK_OUT` | Output of 50MHz APLL clock. |
| internal | `GPIO17` | `EMAC_CLK_180` | Inverted output of 50MHz APLL clock. Found to be best suitable for LAN8720 with long signal lines. |
Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system. Then go into `Example Configuration` menu.
* Choose PHY device under `Ethernet PHY option` option
#### External PHY Clock
The external reference clock of 50MHz must be supplied on `GPIO0`. See note about `GPIO0` below.
* Set PHY address under `PHY address` option, this address depends on the hardware and the PHY configuration. Consult the documentation/datasheet for the PHY hardware you have.
* Address 31 (default) for Espressif's Ethernet board with TLK110 PHY by default
* Address 1 for the common Waveshare LAN8720 PHY breakout
* Address 0 for other LAN8720 breakouts
#### Internal PHY Clock
The ESP32 can generate a 50MHz clock using its APLL. When the APLL is already used as clock source for other purposes (most likely I²S) external PHY has to be used.
* Check whether or not to control PHY's power (if true, you need also to set PHY Power GPIO number later)
The inverted clock signal `EMAC_CLK_180` was found working best with a LAN8720 PHY.
* Set SMI MDC/MDIO GPIO number according to board schemetic, default these two GPIOs are set as below:
## RMII PHY Wiring
The following PHY connections are required for RMII PHY data connections. These `GPIO` pin assignments cannot be changed.
| Default Example GPIO | RMII Signal | Notes |
| -------------------- | ----------- | ------------- |
| GPIO23 | MDC | Output to PHY |
| GPIO18 | MDIO | Bidirectional |
| GPIO | RMII Signal | ESP32 EMAC Function | Notes |
| -------- | ----------- | ------------------- | ----- |
| `GPIO21` | `TX_EN` | `EMAC_TX_EN` | |
| `GPIO19` | `TX0` | `EMAC_TXD0` | |
| `GPIO22` | `TX1` | `EMAC_TXD1` | |
| `GPIO25` | `RX0` | `EMAC_RXD0` | |
| `GPIO26` | `RX1` | `EMAC_RXD1` | |
| `GPIO27` | `CRS_DV` | `EMAC_RX_DRV` | |
* Select one kind of EMAC clock mode under `EMAC clock mode` option. Possible configurations of the clock are listed as below:
## RMII PHY SMI Wiring
| Mode | GPIO Pin | Signal name | Notes |
| -------- | -------- | ------------ | ------------------------------------------------------------ |
| external | GPIO0 | EMAC_TX_CLK | Input of 50MHz PHY clock |
| internal | GPIO16 | EMAC_CLK_OUT | Output of 50MHz APLL clock |
| internal | GPIO17 | EMAC_CLK_180 | Inverted output of 50MHz APLL clock (suitable for long clock trace) |
The following PHY connections are required for RMII PHY SMI (aka MDIO) management interface. These GPIO pin assignments can be changed to any unused GPIO pin.
* The external reference clock of 50MHz must be supplied on `GPIO0`.
* The ESP32 can generate a 50MHz clock using its APLL. When the APLL is already used as clock source for other purposes (most likely I²S), you have no choice but choose external clock.
For the example, these pins are configured via `make menuconfig` under the Example configuration.
### Build and Flash
| Default Example GPIO | RMII Signal | Notes |
| -------------------- | ----------- | ------------- |
| `GPIO23` | `MDC` | Output to PHY |
| `GPIO18` | `MDIO` | Bidirectional |
Enter `make -j4 flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you' are using CMake based build system.
The defaults in the example are correct for Espressif's Ethernet development board.
(To exit the serial monitor, type ``Ctrl-]``.)
## Note about `GPIO0`
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
Because `GPIO0` is a strapping pin for entering UART flashing mode on reset, care must be taken when also using this pin as `EMAC_TX_CLK`. If the clock output from the PHY is oscillating during reset, the ESP32 may randomly enter UART flashing mode.
## Example Output
One solution is to use an additional GPIO as a "power pin", which either powers the PHY on/off or enables/disables the PHY's own oscillator. This prevents the clock signal from being active during a system reset. For this configuration to work, `GPIO0` also needs a pullup resistor and the "power pin" GPIO will need a pullup/pulldown resistor - as appropriate in order to keep the PHY clock disabled when the ESP32 is in reset.
```bash
I (0) cpu_start: App cpu up.
I (281) heap_init: Initializing. RAM available for dynamic allocation:
I (288) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (294) heap_init: At 3FFB4358 len 0002BCA8 (175 KiB): DRAM
I (300) heap_init: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM
I (307) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (313) heap_init: At 4008966C len 00016994 (90 KiB): IRAM
I (319) cpu_start: Pro cpu start user code
I (113) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (116) eth_example: Power On Ethernet PHY
I (126) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (126) emac: emac reset done
I (126) eth_example: Ethernet Started
I (2126) eth_example: Ethernet Link Up
I (11616) event: eth ip: 192.168.2.156, mask: 255.255.255.0, gw: 192.168.2.2
I (11616) eth_example: Ethernet Got IP Addr
I (11616) eth_example: ~~~~~~~~~~~
I (11616) eth_example: ETHIP:192.168.2.156
I (11626) eth_example: ETHMASK:255.255.255.0
I (11626) eth_example: ETHGW:192.168.2.2
I (11636) eth_example: ~~~~~~~~~~~
```
See the example source code to see how the "power pin" GPIO can be managed in software.
## Troubleshooting
* If the PHY address is incorrect then the EMAC will still be initialised, but all attempts to read/write configuration registers in the PHY's register will fail, for example, waiting for auto-negotiation done.
* Check PHY Clock
> The ESP32's MAC and the External PHY device need a common 50MHz reference clock. This clock can either be provided externally by a crystal oscillator (e.g. crystal connected to the PHY or a seperate crystal oscillator) or internally by generating from EPS32's APLL. The signal integrity of this clock is strict, so it is highly recommended to add a 33Ω resistor in series to reduce ringing.
* Check GPIO connections, the RMII PHY wiring is fixed which can not be changed through either IOMUX or GPIO Matrix. They're described as below:
| GPIO | RMII Signal | ESP32 EMAC Function |
| ------ | ----------- | ------------------- |
| GPIO21 | TX_EN | EMAC_TX_EN |
| GPIO19 | TX0 | EMAC_TXD0 |
| GPIO22 | TX1 | EMAC_TXD1 |
| GPIO25 | RX0 | EMAC_RXD0 |
| GPIO26 | RX1 | EMAC_RXD1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV |
* Check GPIO0
> GPIO0 is a strapping pin for entering UART flashing mode on reset, care must be taken when using this pin as `EMAC_TX_CLK`. If the clock output from the PHY is oscillating during reset, the ESP32 may randomly enter UART flashing mode. One solution is to use an additional GPIO as a "power pin", which either powers the PHY on/off or enables/disables the PHY's own oscillator. This prevents the clock signal from being active during a system reset. For this configuration to work, `GPIO0` also needs a pullup resistor and the "power pin" GPIO will need a pullup/pulldown resistor - as appropriate in order to keep the PHY clock disabled when the ESP32 is in reset. See the example source code to see how the "power pin" GPIO can be managed in software. The example defaults to using `GPIO17` for this function, but it can be overriden. On Espressif's Ethernet development board, `GPIO17` is the power pin used to enable/disable the PHY oscillator.
The example defaults to using `GPIO17` for this function, but it can be overriden. On Espressif's Ethernet development board, `GPIO17` is the power pin used to enable/disable the PHY oscillator.

View File

@@ -33,7 +33,7 @@ choice PHY_CLOCK_MODE
prompt "EMAC clock mode"
default PHY_CLOCK_GPIO0_IN
help
Select external (input on GPIO0) or internal (output on GPIO0, GPIO16 or GPIO17) clock
Select external (input on GPIO0) or internal (output on GPIO16 or GPIO17) clock
config PHY_CLOCK_GPIO0_IN

View File

@@ -0,0 +1,9 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/components
$ENV{IDF_PATH}/examples/wifi/iperf/components)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ethernet-iperf)

View File

@@ -0,0 +1,13 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := ethernet-iperf
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/system/console/components
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/wifi/iperf/components
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,176 @@
# Ethernet iperf example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates basic usage of [iperf](https://iperf.fr/) protocol to measure the throughout/bandwidth of Ethernet.
This example is based on esp-idf's console component. For more information about console you can read the [guide](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html).
## How to use example
### Prepare work
1. Install iperf tool on PC
* Debian/Ubuntu: `sudo apt-get install iperf`
* macOS: `brew install iperf`(if using Homebrew) or `sudo port install iperf`(if using MacPorts)
* Windows(MSYS2): Downloads binaries from [here]( https://iperf.fr/iperf-download.php#windows)
2. Initialise Ethernet on ESP32, run `ethernet start`
3. Get ip information(optional), run `ethernet info`
### Configure the project
Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system. Then go into `Example Configuration` menu.
* Check whether or not to store the history command into flash under `Store command history in flash` option
* Set PHY address under `PHY address` option, this address depends on the hardware and the PHY configuration. Consult the documentation/datasheet for the PHY hardware you have.
- Address 31 (default) for Espressif's Ethernet board with TLK110 PHY by default
- Address 1 for the common Waveshare LAN8720 PHY breakout
- Address 0 for other LAN8720 breakouts
* Check whether or not to control PHY's power (if true, you need also to set PHY Power GPIO number later)
* Set SMI MDC/MDIO GPIO number according to board schemetic, default these two GPIOs are set as below:
| Default Example GPIO | RMII Signal | Notes |
| -------------------- | ----------- | ------------- |
| GPIO23 | MDC | Output to PHY |
| GPIO18 | MDIO | Bidirectional |
* Select one kind of EMAC clock mode under `EMAC clock mode` option. Possible configurations of the clock are listed as below:
| Mode | GPIO Pin | Signal name | Notes |
| -------- | -------- | ------------ | ------------------------------------------------------------ |
| external | GPIO0 | EMAC_TX_CLK | Input of 50MHz PHY clock |
| internal | GPIO16 | EMAC_CLK_OUT | Output of 50MHz APLL clock |
| internal | GPIO17 | EMAC_CLK_180 | Inverted output of 50MHz APLL clock (suitable for long clock trace) |
- The external reference clock of 50MHz must be supplied on `GPIO0`.
- The ESP32 can generate a 50MHz clock using its APLL. When the APLL is already used as clock source for other purposes (most likely I²S), you have no choice but choose external clock.
### Build and Flash
Enter `make -j4 flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you' are using CMake based build system.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
### Test uplink bandwidth
* PC: run command: `iperf -u -s -i 3` to start iperf server in UDP mode, and report interval is 3 seconds.
* ESP32: run command: `iperf -u -c PC_IP -i 3 -t 30` to start iperf client in UDP mode, and the test will last 30 seconds.
#### PC output
```bash
------------------------------------------------------------
Server listening on UDP port 5001
Receiving 1470 byte datagrams
UDP buffer size: 208 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.2.160 port 5001 connected with 192.168.2.156 port 49154
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 3] 0.0- 3.0 sec 24.8 MBytes 69.5 Mbits/sec 0.172 ms 1/17721 (0.0056%)
[ 3] 3.0- 6.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17719 (0%)
[ 3] 6.0- 9.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17719 (0%)
[ 3] 9.0-12.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17718 (0%)
[ 3] 12.0-15.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17717 (0%)
[ 3] 15.0-18.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17720 (0%)
[ 3] 18.0-21.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17721 (0%)
[ 3] 21.0-24.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17720 (0%)
[ 3] 24.0-27.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17723 (0%)
```
#### ESP32 output
```bash
mode=udp-client sip=192.168.2.156:5001, dip=192.168.2.160:5001, interval=3, time=30
Interval Bandwidth
0- 3 sec 69.34 Mbits/sec
3- 6 sec 69.55 Mbits/sec
6- 9 sec 69.55 Mbits/sec
9- 12 sec 69.55 Mbits/sec
12- 15 sec 69.55 Mbits/sec
15- 18 sec 69.56 Mbits/sec
18- 21 sec 69.56 Mbits/sec
21- 24 sec 69.56 Mbits/sec
24- 27 sec 69.56 Mbits/sec
27- 30 sec 69.56 Mbits/sec
```
### Test downlink bandwidth
* PC: run command: `iperf -u -c ESP_IP -b 80M -t 30 -i 3` to start iperf client in UDP mode with estimated bandwidth 100M, and report interval is 3 seconds.
* ESP32: run command: `iperf -u -s -t 30 -i 3` to start iperf server in UDP mode, and the test will last 30 seconds.
#### PC output
```bash
------------------------------------------------------------
Client connecting to 192.168.2.156, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size: 208 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.2.160 port 59581 connected with 192.168.2.156 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 3.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 3.0- 6.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 6.0- 9.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 9.0-12.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 12.0-15.0 sec 28.6 MBytes 79.9 Mbits/sec
[ 3] 15.0-18.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 18.0-21.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 21.0-24.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 24.0-27.0 sec 28.6 MBytes 80.0 Mbits/sec
```
#### ESP32 output
```bash
mode=udp-server sip=192.168.2.156:5001, dip=0.0.0.0:5001, interval=3, time=30
Interval Bandwidth
I (2534456) iperf: want recv=16384
0- 30 sec 72.67 Mbits/sec
3- 6 sec 74.18 Mbits/sec
6- 9 sec 73.14 Mbits/sec
9- 12 sec 73.65 Mbits/sec
12- 15 sec 72.87 Mbits/sec
15- 18 sec 73.29 Mbits/sec
18- 21 sec 74.35 Mbits/sec
21- 24 sec 72.28 Mbits/sec
24- 27 sec 73.39 Mbits/sec
27- 30 sec 73.49 Mbits/sec
```
## Suggestions of getting higher bandwidth
1. Higher MCU working frequency will get higher bandwidth
2. Put frequency invoked function into IRAM via macro `IRAM_ATTR` in code.
3. Priority of iperf task may also have effect.
## Troubleshooting
- If the PHY address is incorrect then the EMAC will still be initialised, but all attempts to read/write configuration registers in the PHY's register will fail, for example, waiting for auto-negotiation done.
- Check PHY Clock
> The ESP32's MAC and the External PHY device need a common 50MHz reference clock. This clock can either be provided externally by a crystal oscillator (e.g. crystal connected to the PHY or a seperate crystal oscillator) or internally by generating from EPS32's APLL. The signal integrity of this clock is strict, so it is highly recommended to add a 33Ω resistor in series to reduce ringing.
- Check GPIO connections, the RMII PHY wiring is fixed which can not be changed through either IOMUX or GPIO Matrix. They're described as below:
| GPIO | RMII Signal | ESP32 EMAC Function |
| ------ | ----------- | ------------------- |
| GPIO21 | TX_EN | EMAC_TX_EN |
| GPIO19 | TX0 | EMAC_TXD0 |
| GPIO22 | TX1 | EMAC_TXD1 |
| GPIO25 | RX0 | EMAC_RXD0 |
| GPIO26 | RX1 | EMAC_RXD1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV |
- Check GPIO0
> GPIO0 is a strapping pin for entering UART flashing mode on reset, care must be taken when using this pin as `EMAC_TX_CLK`. If the clock output from the PHY is oscillating during reset, the ESP32 may randomly enter UART flashing mode. One solution is to use an additional GPIO as a "power pin", which either powers the PHY on/off or enables/disables the PHY's own oscillator. This prevents the clock signal from being active during a system reset. For this configuration to work, `GPIO0` also needs a pullup resistor and the "power pin" GPIO will need a pullup/pulldown resistor - as appropriate in order to keep the PHY clock disabled when the ESP32 is in reset. See the example source code to see how the "power pin" GPIO can be managed in software. The example defaults to using `GPIO17` for this function, but it can be overriden. On Espressif's Ethernet development board, `GPIO17` is the power pin used to enable/disable the PHY oscillator.

View File

@@ -0,0 +1,5 @@
set(COMPONENT_SRCS "cmd_ethernet.c"
"iperf_example_main.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@@ -0,0 +1,91 @@
menu "Example Configuration"
config STORE_HISTORY
bool "Store command history in flash"
default y
help
Linenoise line editing library provides functions to save and load
command history. If this option is enabled, initalizes a FAT filesystem
and uses it to store command history.
menu "Etherent PHY Device"
choice PHY_MODEL
prompt "Ethernet PHY"
default CONFIG_PHY_TLK110
help
Select the PHY driver to use for the example.
config PHY_TLK110
bool "TI TLK110 PHY"
help
Select this to use the TI TLK110 PHY
config PHY_LAN8720
bool "Microchip LAN8720 PHY"
help
Select this to use the Microchip LAN8720 PHY
endchoice
config PHY_ADDRESS
int "PHY Address (0-31)"
default 31
range 0 31
help
Set the PHY Address (0-31) for the hardware configuration.
choice PHY_CLOCK_MODE
prompt "EMAC clock mode"
default PHY_CLOCK_GPIO0_IN
help
Select external (input on GPIO0) or internal (output on GPIO16 or GPIO17) clock
config PHY_CLOCK_GPIO0_IN
bool "GPIO0 input"
help
Input of 50MHz refclock on GPIO0
config PHY_CLOCK_GPIO16_OUT
bool "GPIO16 output"
help
Output the internal 50MHz APLL clock on GPIO16
config PHY_CLOCK_GPIO17_OUT
bool "GPIO17 output (inverted)"
help
Output the internal 50MHz APLL clock on GPIO17 (inverted signal)
endchoice
config PHY_CLOCK_MODE
int
default 0 if PHY_CLOCK_GPIO0_IN
default 2 if PHY_CLOCK_GPIO16_OUT
default 3 if PHY_CLOCK_GPIO17_OUT
config PHY_USE_POWER_PIN
bool "Use PHY Power (enable/disable) pin"
default y
help
Use a GPIO "power pin" to power the PHY on/off during operation.
if PHY_USE_POWER_PIN
config PHY_POWER_PIN
int "PHY Power GPIO"
default 17
range 0 33
help
GPIO number to use for powering on/off the PHY.
endif
endmenu
menu "Etherent SMI interface"
config PHY_SMI_MDC_PIN
int "SMI MDC Pin"
default 23
range 0 33
help
GPIO number to use for SMI clock output MDC to PHY.
config PHY_SMI_MDIO_PIN
int "SMI MDIO Pin"
default 18
range 0 33
help
GPIO number to use for SMI data pin MDIO to/from PHY.
endmenu
endmenu

View File

@@ -0,0 +1,20 @@
/* Console example — declarations of command registration functions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "cmd_system.h"
#include "cmd_ethernet.h"
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,286 @@
/* Console example — Ethernet commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "tcpip_adapter.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_event_loop.h"
#include "esp_eth.h"
#include "argtable3/argtable3.h"
#include "iperf.h"
#include "sdkconfig.h"
#ifdef CONFIG_PHY_LAN8720
#include "eth_phy/phy_lan8720.h"
#define DEFAULT_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config
#endif
#ifdef CONFIG_PHY_TLK110
#include "eth_phy/phy_tlk110.h"
#define DEFAULT_ETHERNET_PHY_CONFIG phy_tlk110_default_ethernet_config
#endif
static tcpip_adapter_ip_info_t ip;
static bool started = false;
static EventGroupHandle_t eth_event_group;
static const int GOTIP_BIT = BIT0;
#define PIN_PHY_POWER CONFIG_PHY_POWER_PIN
#define PIN_SMI_MDC CONFIG_PHY_SMI_MDC_PIN
#define PIN_SMI_MDIO CONFIG_PHY_SMI_MDIO_PIN
#ifdef CONFIG_PHY_USE_POWER_PIN
static void phy_device_power_enable_via_gpio(bool enable)
{
assert(DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable);
if (!enable) {
DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(false);
}
gpio_pad_select_gpio(PIN_PHY_POWER);
gpio_set_direction(PIN_PHY_POWER, GPIO_MODE_OUTPUT);
if (enable == true) {
gpio_set_level(PIN_PHY_POWER, 1);
ESP_LOGI(__func__, "Power On Ethernet PHY");
} else {
gpio_set_level(PIN_PHY_POWER, 0);
ESP_LOGI(__func__, "Power Off Ethernet PHY");
}
vTaskDelay(1); // Allow the power up/down to take effect, min 300us
if (enable) {
/* call the default PHY-specific power on function */
DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true);
}
}
#endif
static void eth_gpio_config_rmii(void)
{
phy_rmii_configure_data_interface_pins();
phy_rmii_smi_configure_pins(PIN_SMI_MDC, PIN_SMI_MDIO);
}
/* "ethernet" command */
static struct {
struct arg_str *control;
struct arg_end *end;
} eth_control_args;
static int eth_cmd_control(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&eth_control_args);
if (nerrors != 0) {
arg_print_errors(stderr, eth_control_args.end, argv[0]);
return 1;
}
if (!strncmp(eth_control_args.control->sval[0], "start", 5) && !started) {
ESP_ERROR_CHECK(esp_eth_enable());
started = true;
}
if (!strncmp(eth_control_args.control->sval[0], "stop", 4) && started) {
ESP_ERROR_CHECK(esp_eth_disable());
started = false;
}
if (!strncmp(eth_control_args.control->sval[0], "info", 4)) {
uint8_t mac_addr[6];
esp_eth_get_mac(mac_addr);
printf("HW ADDR: " MACSTR "\r\n", MAC2STR(mac_addr));
tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip);
printf("ETHIP: " IPSTR "\r\n", IP2STR(&ip.ip));
printf("ETHMASK: " IPSTR "\r\n", IP2STR(&ip.netmask));
printf("ETHGW: " IPSTR "\r\n", IP2STR(&ip.gw));
}
return 0;
}
/* "iperf" command */
static struct {
struct arg_str *ip;
struct arg_lit *server;
struct arg_lit *udp;
struct arg_int *port;
struct arg_int *interval;
struct arg_int *time;
struct arg_lit *abort;
struct arg_end *end;
} iperf_args;
static int eth_cmd_iperf(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
iperf_cfg_t cfg;
if (nerrors != 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]);
return 0;
}
memset(&cfg, 0, sizeof(cfg));
/* iperf -a */
if (iperf_args.abort->count != 0) {
iperf_stop();
return 0;
}
if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0))) {
ESP_LOGE(__func__, "Wrong mode! ESP32 should run in client or server mode");
return 0;
}
/* iperf -s */
if (iperf_args.ip->count == 0) {
cfg.flag |= IPERF_FLAG_SERVER;
}
/* iperf -c SERVER_ADDRESS */
else {
cfg.dip = ipaddr_addr(iperf_args.ip->sval[0]);
cfg.flag |= IPERF_FLAG_CLIENT;
}
/* acquiring for ip, could blocked here */
xEventGroupWaitBits(eth_event_group, GOTIP_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
cfg.sip = ip.ip.addr;
if (cfg.sip == 0) {
return 0;
}
/* iperf -u */
if (iperf_args.udp->count == 0) {
cfg.flag |= IPERF_FLAG_TCP;
} else {
cfg.flag |= IPERF_FLAG_UDP;
}
/* iperf -p */
if (iperf_args.port->count == 0) {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = IPERF_DEFAULT_PORT;
} else {
if (cfg.flag & IPERF_FLAG_SERVER) {
cfg.sport = iperf_args.port->ival[0];
cfg.dport = IPERF_DEFAULT_PORT;
} else {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = iperf_args.port->ival[0];
}
}
/* iperf -i */
if (iperf_args.interval->count == 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
} else {
cfg.interval = iperf_args.interval->ival[0];
if (cfg.interval <= 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
}
}
/* iperf -t */
if (iperf_args.time->count == 0) {
cfg.time = IPERF_DEFAULT_TIME;
} else {
cfg.time = iperf_args.time->ival[0];
if (cfg.time <= cfg.interval) {
cfg.time = cfg.interval;
}
}
printf("mode=%s-%s sip=%d.%d.%d.%d:%d, dip=%d.%d.%d.%d:%d, interval=%d, time=%d\r\n",
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
cfg.sip & 0xFF, (cfg.sip >> 8) & 0xFF, (cfg.sip >> 16) & 0xFF, (cfg.sip >> 24) & 0xFF, cfg.sport,
cfg.dip & 0xFF, (cfg.dip >> 8) & 0xFF, (cfg.dip >> 16) & 0xFF, (cfg.dip >> 24) & 0xFF, cfg.dport,
cfg.interval, cfg.time);
iperf_start(&cfg);
return 0;
}
static esp_err_t eth_event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_ETH_START:
started = true;
break;
case SYSTEM_EVENT_ETH_GOT_IP:
memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t));
ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip));
xEventGroupSetBits(eth_event_group, GOTIP_BIT);
break;
case SYSTEM_EVENT_ETH_STOP:
xEventGroupClearBits(eth_event_group, GOTIP_BIT);
started = false;
default:
break;
}
return ESP_OK;
}
void register_ethernet()
{
eth_event_group = xEventGroupCreate();
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_init(eth_event_handler, NULL));
eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG;
config.phy_addr = CONFIG_PHY_ADDRESS;
config.gpio_config = eth_gpio_config_rmii;
config.tcpip_input = tcpip_adapter_eth_input;
config.clock_mode = CONFIG_PHY_CLOCK_MODE;
#ifdef CONFIG_PHY_USE_POWER_PIN
/* Replace the default 'power enable' function with an example-specific one
that toggles a power GPIO. */
config.phy_power_enable = phy_device_power_enable_via_gpio;
#endif
ESP_ERROR_CHECK(esp_eth_init(&config));
eth_control_args.control = arg_str1(NULL, NULL, "<start|stop|info>", "Start/Stop Ethernet or Get info of Ethernet");
eth_control_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "ethernet",
.help = "Control Ethernet interface",
.hint = NULL,
.func = eth_cmd_control,
.argtable = &eth_control_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
iperf_args.ip = arg_str0("c", "client", "<ip>",
"run in client mode, connecting to <host>");
iperf_args.server = arg_lit0("s", "server", "run in server mode");
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
iperf_args.port = arg_int0("p", "port", "<port>",
"server port to listen on/connect to");
iperf_args.interval = arg_int0("i", "interval", "<interval>",
"seconds between periodic bandwidth reports");
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
iperf_args.end = arg_end(1);
const esp_console_cmd_t iperf_cmd = {
.command = "iperf",
.help = "iperf command",
.hint = NULL,
.func = &eth_cmd_iperf,
.argtable = &iperf_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
}

View File

@@ -0,0 +1,20 @@
/* Console example — declarations of command registration functions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Register Ethernet functions
void register_ethernet();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,194 @@
/* Console example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "esp_vfs_fat.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
static const char *TAG = "ethernet-console";
#if CONFIG_STORE_HISTORY
#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"
static void initialize_filesystem()
{
static wl_handle_t wl_handle;
const esp_vfs_fat_mount_config_t mount_config = {
.max_files = 4,
.format_if_mount_failed = true
};
esp_err_t err = esp_vfs_fat_spiflash_mount(MOUNT_PATH, "storage", &mount_config, &wl_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
return;
}
}
#endif // CONFIG_STORE_HISTORY
static void initialize_nvs()
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
}
static void initialize_console()
{
/* Disable buffering on stdin and stdout */
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
* correct while APB frequency is changing in light sleep mode.
*/
const uart_config_t uart_config = {
.baud_rate = CONFIG_CONSOLE_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.use_ref_tick = true
};
ESP_ERROR_CHECK(uart_param_config(CONFIG_CONSOLE_UART_NUM, &uart_config));
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK(uart_driver_install(CONFIG_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0));
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
/* Initialize the console */
esp_console_config_t console_config = {
.max_cmdline_args = 8,
.max_cmdline_length = 256,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK(esp_console_init(&console_config));
/* Configure linenoise line completion library */
linenoiseSetMultiLine(1);
/* Tell linenoise where to get command completions and hints */
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint);
/* Set command history size */
linenoiseHistorySetMaxLen(100);
#if CONFIG_STORE_HISTORY
/* Load command history from filesystem */
linenoiseHistoryLoad(HISTORY_PATH);
#endif
}
void app_main()
{
initialize_nvs();
#if CONFIG_STORE_HISTORY
initialize_filesystem();
#endif
initialize_console();
/* Register commands */
esp_console_register_help_command();
register_system();
register_ethernet();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char *prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR;
printf("\n =======================================================\n");
printf(" | Steps to Test Ethernet Bandwidth |\n");
printf(" | |\n");
printf(" | 1. Enter 'help', check all supported commands |\n");
printf(" | 2. Enter 'ethernet start' |\n");
printf(" | 3. Wait ESP32 to get IP from DHCP |\n");
printf(" | 4. Enter 'ethernet info', optional |\n");
printf(" | 4. Server: 'iperf -u -s -i 3' |\n");
printf(" | 4. Client: 'iperf -u -c SERVER_IP -t 60 -i 3' |\n");
printf(" | |\n");
printf(" =======================================================\n\n");
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) {
/* zero indicates success */
printf("\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
prompt = "esp32> ";
#endif //CONFIG_LOG_COLORS
}
/* Main loop */
while (true) {
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
char *line = linenoise(prompt);
if (line == NULL) {
/* Ignore empty lines */
continue;
}
/* Add the command to the history */
linenoiseHistoryAdd(line);
#if CONFIG_STORE_HISTORY
/* Save command history to filesystem */
linenoiseHistorySave(HISTORY_PATH);
#endif
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(err));
} else if (err != ESP_OK) {
printf("Internal error: %s\n", esp_err_to_name(err));
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
}
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, fat, , 1M,

View File

@@ -0,0 +1,27 @@
# Reduce bootloader log verbosity
CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y
CONFIG_LOG_BOOTLOADER_LEVEL=2
# Increase main task stack size
CONFIG_MAIN_TASK_STACK_SIZE=7168
# Enable filesystem
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
CONFIG_APP_OFFSET=0x10000
# Enable FreeRTOS stats formatting functions, needed for 'tasks' command
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# ESP32-specific
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
# Ethernet
CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE=y