lwip: add icmp echo example

It piggybacks on the console example to add ping support and CLI.

Merges https://github.com/espressif/esp-idf/pull/4093
This commit is contained in:
Andro Nooh
2019-09-18 22:13:02 -07:00
committed by bot
parent 6c3527e2e6
commit a77e69c408
11 changed files with 254 additions and 7 deletions

View File

@@ -128,7 +128,7 @@ esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t o
return ret;
}
esp_err_t esp_ping_result(uint8_t res_val, uint16_t ping_len, uint32_t ping_time)
esp_err_t esp_ping_result(uint8_t res_val, uint16_t ping_len, uint32_t ping_time, uint32_t seqno)
{
esp_err_t ret = ESP_OK;
@@ -154,6 +154,7 @@ esp_err_t esp_ping_result(uint8_t res_val, uint16_t ping_len, uint32_t ping_time
ping_option_info->ping_res.total_time += ping_time;
ping_option_info->ping_res.recv_count ++;
ping_option_info->ping_res.ping_seqno = seqno;
}
}

View File

@@ -208,7 +208,7 @@ ping_recv(int s)
if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) {
/* do some ping result processing */
#ifdef ESP_PING
esp_ping_result((ICMPH_TYPE(iecho) == ICMP_ER), len, PING_TIME_DIFF_MS(now, ping_time));
esp_ping_result((ICMPH_TYPE(iecho) == ICMP_ER), len, PING_TIME_DIFF_MS(now, ping_time), ntohs(iecho->seqno));
#else
PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER));
#endif
@@ -228,7 +228,7 @@ ping_recv(int s)
/* do some ping result processing */
#ifdef ESP_PING
esp_ping_result(0, len, PING_TIME_DIFF_MS(now, ping_time));
esp_ping_result(0, len, PING_TIME_DIFF_MS(now, ping_time),0);
#else
PING_RESULT(0);
#endif
@@ -317,7 +317,7 @@ _exit:
close(s);
_exit_new_socket_failed:
esp_ping_result(PING_RES_FINISH, 0, 0);
esp_ping_result(PING_RES_FINISH, 0, 0, 0);
SYS_ARCH_PROTECT(lev);
if (ping_init_flag) { /* Ping closed by this thread */
LWIP_DEBUGF( PING_DEBUG, ("ping: closed by self "));

View File

@@ -42,6 +42,7 @@ typedef struct _ping_found {
uint32_t total_time;
uint32_t min_time;
uint32_t max_time;
uint32_t ping_seqno;
int8_t ping_err;
} esp_ping_found;
@@ -102,7 +103,7 @@ esp_err_t esp_ping_get_target(ping_target_id_t opt_id, void *opt_val, uint32_t o
* - ESP_OK
* - ESP_ERR_PING_INVALID_PARAMS
*/
esp_err_t esp_ping_result(uint8_t res_val, uint16_t res_len, uint32_t res_time);
esp_err_t esp_ping_result(uint8_t res_val, uint16_t res_len, uint32_t res_time, uint32_t seqno);
#ifdef __cplusplus
}

View File

@@ -100,6 +100,30 @@ Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
[esp32]>
ping <IP address>
Send an ICMP message to an IPv4/IPv6 address
<IPv4/IPv6> Target IP Address
-c, --count=<n> Number of messages
-t, --timeout=<t> Connection timeout, ms
-d, --delay=<t> Delay between messges, ms
-s, --size=<n> Packet data size, bytes
--tos=<n> Type of service
esp32>
esp32> ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 32(60) bytes of data.
60 bytes from 192.168.1.1: icmp_seq=1 time=13 ms
60 bytes from 192.168.1.1: icmp_seq=2 time=11 ms
60 bytes from 192.168.1.1: icmp_seq=3 time=7 ms
60 bytes from 192.168.1.1: icmp_seq=4 time=13 ms
60 bytes from 192.168.1.1: icmp_seq=5 time=25 ms
--- 192.168.1.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 69ms
rtt min/avg/max = 7/13.80/25 ms
esp32>
```
@@ -148,4 +172,4 @@ Several commands implemented in `cmd_wifi.c` and `cmd_system.c` use the Argtable
### Command history
Each time a new command line is obtained from `linenoise`, it is written into history and the history is saved into a file in flash memory. On reset, history is initialized from that file.
Each time a new command line is obtained from `linenoise`, it is written into history and the history is saved into a file in flash memory. On reset, history is initialized from that file.

View File

@@ -1,3 +1,4 @@
idf_component_register(SRCS "cmd_wifi.c"
"console_example_main.c"
INCLUDE_DIRS ".")
"cmd_ping.c"
INCLUDE_DIRS ".")

View File

@@ -14,6 +14,7 @@ extern "C" {
#include "cmd_system.h"
#include "cmd_wifi.h"
#include "cmd_ping.h"
#include "cmd_nvs.h"
#ifdef __cplusplus

View File

@@ -0,0 +1,194 @@
/* Console example — WiFi 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 "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "tcpip_adapter.h"
#include "esp_event.h"
#include "cmd_wifi.h"
#include "cmd_ping.h"
#include "lwip/inet.h"
#include <netinet/in.h>
#include "esp_ping.h"
#include "ping/ping.h"
#define DEFAULT_COUNT (5)
#define DEFAULT_TIMEOUT (1000) // unit = ms
#define DEFAULT_DELAY (500) // unit = ms
#define DEFAULT_DATA_SIZE (32) // bytes
#define FAILURE (-1)
extern volatile bool is_connected;
/** Arguments used by 'send_icmp' function */
static struct {
struct arg_str *ip_address;
struct arg_int *count;
struct arg_int *timeout;
struct arg_int *delay;
struct arg_int *packet_size;
struct arg_int *tos;
struct arg_end *end;
} ping_args;
static bool parse_ip_address(const char *address, ip4_addr_t *ipv4, ip6_addr_t *ipv6)
{
// Determine if the address is IPv4 or IPv6
char *indicator = (char *) malloc(strlen(address));
strcpy(indicator, address);
while(*indicator++)
{
if (*indicator == '.') {
return inet_aton(address, ipv4);
}
else if (*indicator == ':') {
return inet6_aton(address, &ipv6);
}
}
return false;
}
static esp_err_t parse_args(ip4_addr_t *ip4, ip6_addr_t *ip6, uint32_t *count, uint32_t *timeout,
uint32_t *delay, uint32_t *packet_size, uint32_t *tos)
{
if (ping_args.ip_address->count > 0)
{
// Parse IP address (Handle v4 and v6)
bool rc = parse_ip_address(ping_args.ip_address->sval[0], ip4, ip6);
if (!rc) {
ESP_LOGE("ping","Error parsing provided IP address...aborting!\n");
return ESP_FAIL;
}
}
if (ping_args.count->count > 0)
*count= *ping_args.count->ival;
if (ping_args.timeout->count > 0)
*timeout = *ping_args.timeout->ival;
if (ping_args.delay->count > 0)
*delay = *ping_args.delay->ival;
if (ping_args.packet_size->count > 0)
*packet_size = *ping_args.packet_size->ival;
if (ping_args.tos->count > 0)
*tos = *ping_args.tos->ival;
return ESP_OK;
}
esp_err_t ping_results(ping_target_id_t message_type, esp_ping_found *found_val)
{
static int ctr;
ip4_addr_t target_ipv4;
uint32_t ping_count = 0;
esp_ping_get_target(PING_TARGET_IP_ADDRESS, &target_ipv4.addr, sizeof(uint32_t));
esp_ping_get_target(PING_TARGET_IP_ADDRESS_COUNT, &ping_count, sizeof(uint32_t));
if (found_val->ping_err != 2)
{
fprintf(stdout, "%d bytes from %s: icmp_seq=%d time=%d ms\n",
found_val->bytes, inet_ntoa(target_ipv4), found_val->ping_seqno, found_val->resp_time);
} else {
fprintf(stdout, ".");
fflush(stdout);
}
if (++ctr >= ping_count) {
int32_t packet_loss = ( 1-((found_val->recv_count * 1.0) / found_val->send_count)) * 100;
fprintf(stdout, "\n--- %s ping statistics ---\n", inet_ntoa(target_ipv4));
fprintf(stdout, "%d packets transmitted, %d received, %d%% packet loss, time %dms\n",
found_val->send_count, found_val->recv_count, packet_loss, found_val->total_time);
fprintf(stdout, "rtt min/avg/max = %d/%1.2f/%d ms\n", found_val->min_time,
(found_val->total_time * 1.0) / found_val->recv_count, found_val->max_time);
// clean up time
memset(found_val, 0, sizeof(esp_ping_found));
ctr = 0;
}
ping_deinit();
return ESP_OK;
}
static esp_err_t send_icmp(int argc, char **argv)
{
if (!is_connected)
{
ESP_LOGE("ping", "Device is not connected to any AP. Cannot ping host without an IP address\n"
"Please associate to an SSID and then use ping!\n");
return ESP_FAIL;
}
esp_err_t ret;
int counter;
ip4_addr_t target_ipv4;
// initialize optional arguments to default values
uint32_t ping_count = DEFAULT_COUNT;
uint32_t ping_timeout = DEFAULT_TIMEOUT;
uint32_t ping_delay = DEFAULT_DELAY;
uint32_t packet_size = DEFAULT_DATA_SIZE;
uint32_t ping_tos = -1;
// TO DO: IPv6
ip6_addr_t target_ipv6;
int nerrors = arg_parse(argc, argv, (void **) &ping_args);
if (nerrors != 0) {
arg_print_errors(stderr, ping_args.end, argv[0]);
return FAILURE;
}
ret = parse_args(&target_ipv4, &target_ipv6, &ping_count, &ping_timeout, &ping_delay,
&packet_size, &ping_tos);
if (ret == ESP_OK) {
printf("PING %s (%s) %d(%d) bytes of data.\n", inet_ntoa(target_ipv4),
inet_ntoa(target_ipv4), packet_size, (packet_size + 28)); // ICMP header length is 28 bytes
for (counter = 0; counter < ping_count; counter++)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
esp_ping_set_target(PING_TARGET_IP_ADDRESS_COUNT, &ping_count, sizeof(uint32_t));
esp_ping_set_target(PING_TARGET_RCV_TIMEO, &ping_timeout, sizeof(uint32_t));
esp_ping_set_target(PING_TARGET_DELAY_TIME, &ping_delay, sizeof(uint32_t));
esp_ping_set_target(PING_TARGET_IP_ADDRESS, &target_ipv4.addr, sizeof(uint32_t));
esp_ping_set_target(PING_TARGET_RES_FN, &ping_results, sizeof(ping_results));
esp_ping_set_target(PING_TARGET_DATA_LEN, &packet_size, sizeof(uint32_t));
if (ping_tos != -1)
esp_ping_set_target(PING_TARGET_IP_TOS, &ping_tos, sizeof(uint32_t));
ping_init();
}
} else
return ESP_FAIL;
return ESP_OK;
}
void register_ping(void)
{
ping_args.ip_address = arg_str0(NULL, NULL, "<IPv4/IPv6>", "Target IP Address");
ping_args.timeout = arg_int0("t", "timeout", "<t>", "Connection timeout, ms");
ping_args.count = arg_int0("c", "count", "<n>", "Number of messages");
ping_args.delay= arg_int0("d", "delay", "<t>", "Delay between messges, ms");
ping_args.packet_size = arg_int0("s", "size", "<n>", "Packet data size, bytes");
ping_args.tos = arg_int0(NULL, "tos", "<n>", "Type of service");
ping_args.end = arg_end(1);
const esp_console_cmd_t ping_cmd = {
.command = "ping",
.help = "Send an ICMP message to an IPv4/IPv6 address",
.hint = "<IP address>",
.func = &send_icmp,
.argtable = &ping_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&ping_cmd) );
}

View File

@@ -0,0 +1,21 @@
/* 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 Ping functions
void register_ping(void);
#ifdef __cplusplus
}
#endif

View File

@@ -24,6 +24,7 @@
static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;
volatile bool is_connected = false;
static void event_handler(void* arg, esp_event_base_t event_base,
@@ -106,6 +107,7 @@ static int connect(int argc, char **argv)
return 1;
}
ESP_LOGI(__func__, "Connected");
is_connected = true;
return 0;
}

View File

@@ -12,6 +12,7 @@
extern "C" {
#endif
extern volatile bool is_connected;
// Register WiFi functions
void register_wifi(void);

View File

@@ -130,6 +130,7 @@ void app_main(void)
register_system();
register_wifi();
register_nvs();
register_ping();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.