SimplePing not working in QNEthernet

mgolden

Member
I finally got my act together and soldered my ethernet kit for Teensy 4.1. Now I am running into problems. (I am pretty new at soldering and so there's some chance that the problem is hardware.)

When I run the SimplePing program, the ping always fails - it immediately returns with a -1. What is weird is that it _does_ pick up both the MAC address and the DHCP address. Furthermore, other machines can ping the Teensy when the program is running. If some other machine is pinging it, and I unplug the ethernet cable, the pings start to fail, as they should. Also, when I run some other program, e.g. to blink the LED, the Teensy cannot be pinged, again as expected. All of this makes me think it's not a hardware problem.

I have added the line
#define QNETHERNET_ENABLE_PING 1
to ~/projects/Arduino/libraries/QNEthernet/src/qnethernet_opts.h
but I am not sure that's right.

Is SimplePing program known to work at present? If not, should I be writing one with the Ping class, rather than Ethernet.ping?
 
Can you post your code so we can see what is happening.
When you do post the code, use the </> button on the left. Doing it that way preserves indenting etc.
 
Thanks for getting back to me. I should have posted the code with the original question!

Below I have attached simpleping.ino and ... libraries/QNEthernet/src/qnethernet_opts.h

Note that the printf of QNETHERNET_ENABLE_PING I added right before the MAC address is reported is showing 1. I should say that I tried various other versions starting from the verbatim code as downloaded.

Code:
// SPDX-FileCopyrightText: (c) 2025 Shawn Silverman <shawn@pobox.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

// SimplePing demonstrates a simpler Ping program.
//
// In order to use this example, define the QNETHERNET_ENABLE_PING macro.
//
// Note: the configuration macros must either be defined in the
//       project build options or in the qnethernet_opts.h
//       library file.
//
// This file is part of the QNEthernet library.

#include <QNEthernet.h>

using namespace qindesign::network;

// --------------------------------------------------------------------------
//  Configuration
// --------------------------------------------------------------------------

constexpr uint32_t kDHCPTimeout = 15'000;  // 15 seconds

constexpr uint32_t kPingInterval = 10'000;  // 1 second

// constexpr char kHostname[]{"192.168.1.10"};

// --------------------------------------------------------------------------
//  Program State
// --------------------------------------------------------------------------

static elapsedMillis pingTimer = kPingInterval;  // Start expired

static unsigned int pingCounter = 0;

static IPAddress kIP = IPAddress(192, 168, 1, 10);

// --------------------------------------------------------------------------
//  Main Program
// --------------------------------------------------------------------------

// Program setup.
void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {
    // Wait for Serial
  }
  printf("Starting...\r\n");

  uint8_t mac[6];
  Ethernet.macAddress(mac);  // This is informative; it retrieves, not sets
  printf("MAC = %02x:%02x:%02x:%02x:%02x:%02x\r\n",
         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  printf("Starting Ethernet with DHCP...\r\n");
  if (!Ethernet.begin()) {
    printf("Failed to start Ethernet\r\n");
    return;
  }
  if (!Ethernet.waitForLocalIP(kDHCPTimeout)) {
    printf("Failed to get IP address from DHCP\r\n");
    return;
  }

  printf("QNETHERNET_ENABLE_PING = %d\n", QNETHERNET_ENABLE_PING);

  IPAddress ip = Ethernet.localIP();
  printf("    Local IP    = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
  ip = Ethernet.subnetMask();
  printf("    Subnet mask = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
  ip = Ethernet.gatewayIP();
  printf("    Gateway     = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
  ip = Ethernet.dnsServerIP();
  printf("    DNS         = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);

  printf("\r\n");
  printf("Pinging %d.%d.%d.%d...\r\n", kIP[0], kIP[1], kIP[2], kIP[3]);
}

// Main program loop.
void loop() {
  if (pingTimer < kPingInterval) {
    // printf("returning with pingTimer is %ld\n", pingTimer);
    return;
  } else {
    printf("Not returning %ld > %ld\n", (long int) pingTimer, kPingInterval);
  }

  pingCounter++;

  printf("%u. ", pingCounter);
  long rtt = Ethernet.ping(kIP);
  printf("rtt is %ld\n", rtt);
  if (rtt >= 0) {
    printf("Time = %ld ms\r\n", rtt);
    pingTimer = rtt;
  } else {
    printf("Ping failed\r\n");
    pingTimer = 0;
  }
}

... libraries/QNEthernet/src/qnethernet_opts.h

Code:
// SPDX-FileCopyrightText: (c) 2024 Shawn Silverman <shawn@pobox.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

// qnethernet_opts.h defines configuration options for the QNEthernet library.
// Users can modify this file instead of defining project-wide macros.
// This file is part of the QNEthernet library.

#define QNETHERNET_ENABLE_PING 1

#pragma once

// Enables the 'altcp_tls_adapter' functions for easier TLS library integration.
// It's set, by default here, to be enabled if MbedTLS is enabled.
#ifndef QNETHERNET_ALTCP_TLS_ADAPTER
#define QNETHERNET_ALTCP_TLS_ADAPTER LWIP_ALTCP_TLS_MBEDTLS
#endif

...

// Enables default implementations of the altcp interface functions.
#ifndef QNETHERNET_ENABLE_ALTCP_DEFAULT_FUNCTIONS
#define QNETHERNET_ENABLE_ALTCP_DEFAULT_FUNCTIONS 0
#endif

// Enables ping support. (Includes raw IP layer support.)
#ifndef QNETHERNET_ENABLE_PING
#define QNETHERNET_ENABLE_PING 0
#endif

// Enables promiscuous mode.
#ifndef QNETHERNET_ENABLE_PROMISCUOUS_MODE
#define QNETHERNET_ENABLE_PROMISCUOUS_MODE 0
#endif

...
 
I just tested the example and it works fine. Here were my steps:
1. Modify line 86 (library v0.33.1) of qnethernet_opts.h so that QNETHERNET_ENABLE_PING is 1 instead of 0.
2. Open and run the SimplePing example in the Arduino IDE.

Some possible problems:
1. You're not getting a DHCP address.
2. You're not modifying the correct qnethernet_opts.h file. For example, on a Mac, it should be at ~/Documents/Arduino/libraries/QNEthernet/src/qnethernet_opts.h. On Linux, my Ubuntu machine has it at ~/Arduino/libraries/QNEthernet/src/qnethernet_opts.h.
3. You don't have a path to the Internet so that the "arduino.cc" domain lookup is invalid.

Here's something you can try. What does adding the following, after acquiring an IP address, give you?
C++:
IPAddress ip;
printf("Host lookup = %d\r\n", Ethernet.hostByName("arduino.cc", ip));
 
Last edited:
I've improved the SimplePing example, below, to signal any DNS lookup errors. I'll be including it in the next version of the QNEthernet library.

C++:
// SPDX-FileCopyrightText: (c) 2025 Shawn Silverman <shawn@pobox.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

// SimplePing demonstrates a simpler Ping program.
//
// In order to use this example, define the QNETHERNET_ENABLE_PING macro.
//
// Note: the configuration macros must either be defined in the
//       project build options or in the qnethernet_opts.h
//       library file.
//
// This file is part of the QNEthernet library.

#include <QNEthernet.h>

using namespace qindesign::network;

// --------------------------------------------------------------------------
//  Configuration
// --------------------------------------------------------------------------

constexpr uint32_t kDHCPTimeout = 15'000;  // 15 seconds

constexpr uint32_t kPingInterval = 1'000;  // 1 second

constexpr char kHostname[]{"arduino.cc"};

// --------------------------------------------------------------------------
//  Program State
// --------------------------------------------------------------------------

static bool running = false;  // Whether the program is still running

static IPAddress hostIP;
static elapsedMillis pingTimer = kPingInterval;  // Start expired
static unsigned int pingCounter = 0;

// --------------------------------------------------------------------------
//  Main Program
// --------------------------------------------------------------------------

// Program setup.
void setup() {
  Serial.begin(115200);
  while (!Serial && (millis() < 4000)) {
    // Wait for Serial
  }
  printf("Starting...\r\n");

  uint8_t mac[6];
  Ethernet.macAddress(mac);  // This is informative; it retrieves, not sets
  printf("MAC = %02x:%02x:%02x:%02x:%02x:%02x\r\n",
         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  printf("Starting Ethernet with DHCP...\r\n");
  if (!Ethernet.begin()) {
    printf("Failed to start Ethernet\r\n");
    return;
  }
  if (!Ethernet.waitForLocalIP(kDHCPTimeout)) {
    printf("Failed to get IP address from DHCP\r\n");
    return;
  }

  IPAddress ip = Ethernet.localIP();
  printf("    Local IP    = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
  ip = Ethernet.subnetMask();
  printf("    Subnet mask = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
  ip = Ethernet.gatewayIP();
  printf("    Gateway     = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
  ip = Ethernet.dnsServerIP();
  printf("    DNS         = %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);

  printf("\r\n");

  if (!Ethernet.hostByName(kHostname, hostIP)) {
    printf("Failed to look up host (%s)\r\n", kHostname);
    return;
  }

  printf("Pinging %s (%u.%u.%u.%u)...\r\n",
         kHostname, hostIP[0], hostIP[1], hostIP[2], hostIP[3]);

  running = true;
}

// Main program loop.
void loop() {
  if (!running || (pingTimer < kPingInterval)) {
    return;
  }

  pingCounter++;

  printf("%u. ", pingCounter);
  long rtt = Ethernet.ping(hostIP);
  if (rtt >= 0) {
    printf("Time = %ld ms\r\n", rtt);
    pingTimer = rtt;
  } else {
    printf("Ping failed\r\n");
    pingTimer = 0;
  }
}
 
I am not really sure what just happened. I made all the changes that you suggested, but it still wasn't working. I was able to do a DNS lookup, and the print statement was reporting that QNETHERNET_ENABLE_PING was 1. Then I created a new project with the original code and it started to work. Then I went back to the altered sketch and it started working as well.

The only thing I can think of is that creating a new sketch somehow forced a recompile of one of the library files that had been compiled without the QNETHERNET_ENABLE_PING set to 1. I am not expert enough on the Arduino IDE to know how to force a recompile of the library, so maybe I should learn something about that.

Anyway, thanks for your help. This library looks great.
 
Somehow I thought it had...

I just had another thought. I did notice that even when it wasn't willing to _send_ a ping, it was willing to _respond_ to a ping. (That was one of the reasons I didn't think there was a problem with my soldering!) I would think that for security reasons, if pinging is off, it shouldn't respond. Or at least, there should be a separate switch for that.
 
Ah! Thanks for that. I should probably clarify: enabling ping is for sending and processing replies. The stack will always reply to a ping, whether QNETHERNET_ENABLE_PING is on or not.

In other words: there’s no need to enable any features if you want the Teensy to reply to pings.

I’ll clarify that in the project.
 
Is it possible for someone to add a separate flag to the stack to disable responding? I suspect that not having that ability would make the use of this code a hard no in some situations.
 
Is it possible for someone to add a separate flag to the stack to disable responding? I suspect that not having that ability would make the use of this code a hard no in some situations.

Out of curiosity, why do you want to disable ping (echo) replies? I can think of some security reasons, but what are yours?
 
I do not personally have a need for it. However, I have worked at places that don't want their networks scanned to determine what is on them. In such circumstances, I would expect them to want to be able to turn off responses. In fact, I think the use case for turning off responses is probably more common than the one for turning off outbound pings.
 
Last edited:
I’ve added a “QNETHERNET_ENABLE_PING_REPLY” configuration macro and renamed the other one to “QNETHERNET_ENABLE_PING_SEND”. I changed these locally, but they’ll be up on GitHub soon so you can try them.

Thanks for the idea.
 
Last edited:
Great, thanks. I wonder which way should be the default - from my point of view both should be on, but maybe I am wrong about that.
For now, I think leaving only the reply on is the way to go. The reason I don’t want to include sending pings by default is because it includes another part of the library, raw IP, which adds more code. The lwIP library is designed to be smaller, where you can exclude things from the codebase via defines.
 
Back
Top