Forum Rule: Always post complete source code & details to reproduce any issue!
Page 4 of 6 FirstFirst ... 2 3 4 5 6 LastLast
Results 76 to 100 of 136

Thread: New lwIP-based Ethernet library for Teensy 4.1

  1. #76
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    @jdredd: There’s code in the NativeEthernet library that blocks (a spinning `while` loop) unless a cable is plugged in. QNEthernet can tolerate having a cable unplugged, even when the cable is plugged into a different network or unplugged for a long time. See the ServerWithAddressListener example.

    See also: https://github.com/vjmuzik/NativeEthernet/issues/12

  2. #77
    Senior Member
    Join Date
    Feb 2020
    Posts
    139
    Quote Originally Posted by shawn View Post
    Itís been on my mind the last few days. When I have some time, Iíll maybe look into whatís required to implement it from an API perspective. What would a good API look like to you? I know what 1588 is, and even how itís implemented, Iíve just never used it, so an external perspective from someone who isnít as close to the internal plumbing would be nice.

    P.S. Thank you for the nice words.
    I am happy with any interface, simple amd that has example code. I'm a brute force programmer, and was hoping others would chime in here.

  3. #78
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    I just released v0.10.0. The changes:

    Code:
    ### Added
    * Added a way to send raw Ethernet frames. The new function
      is `EthernetClass::sendRaw(frame, len)`.
    * Added new sections to the README:
      1. "Sending raw Ethernet frames", and
      2. "How to implement VLAN tagging".
    * Added calls to `loop()` in `EthernetClient::connected()`
      and `operator bool()`.
    * Added `EthernetUDP::beginMulticast(ip, localPort, reuse)`, where `reuse`
      controls the SO_REUSEADDR socket option.
    * Added `EthernetClass::joinGroup(ip)` and `leaveGroup(ip)` for joining and
      leaving a multicast group.
    * Added a "How to use multicast" section to the README.
    
    ### Changed
    * Changed `kMTU` type to be `size_t` everywhere.
    * Added `stdPrint` as an `extern` variable to `QNEthernet.h` and moved it to the
      `qindesign::network` namespace.
    * Changed transmit data buffers to be 64-byte aligned, for
      "optimal performance".\
      See: IMXRT1060RM_rev2.pdf, "Table 41-38. Enhanced transmit buffer descriptor
      field definitions", page 2186.
    * Updated lwIP to v2.1.3.
    * Changed `EthernetUDP::beginMulticast` to release resources if joining the
      group failed.
    * Increased MEMP_NUM_IGMP_GROUP to 9 to allow 8 multicast groups.
    
    ### Removed
    * Removed mention of the need to re-announce mDNS and adjusted the
      docs accordingly.
    
    ### Fixed
    * Changed receive data buffers to be 64-byte aligned.\
      See: IMXRT1060RM_rev2.pdf, "Table 41-36. Receive buffer descriptor field
      definitions", page 2183.
    * Changed TA value in ENET_MMFR register to 2, per the chip docs.
    * Multicast reception now works. Had to set the ENET_GAUR and ENET_GALR
      registers appropriately.
    Multicast receive now works!

    Link: https://github.com/ssilverman/QNEthe...es/tag/v0.10.0

  4. #79
    Junior Member
    Join Date
    Jul 2020
    Posts
    13
    shawn, thank you for great library.

    I tried to migrate my application where server talked with multiple clients by similar way as at AdvancedChatServer example.
    When I replaced Ethernet library on QNEthernet the application runs fine for some time (~30seconds) when multiple clients attached. However, the clients give network error later and server could not accept clients anymore.

    I found that your FixedWidthServer example uses C++ vector approach instead. I would like to ask some questions.

    Is it possible to use multiple EthernetClient instances - sockets into your library?

    Could you explain what does next code mean at your example?

    // Keeps track of state for a single client.
    struct ClientState {
    ClientState(EthernetClient client)
    : client(std::move(client)) {}

    EthernetClient client;

    ...
    }

    Thank you.

  5. #80
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    Can you show the code you’re using? It’s easy to have resource leaks. Yes, you can use multiple clients.

  6. #81
    Junior Member
    Join Date
    Jul 2020
    Posts
    13
    shawn, please find adjusted AdvancedCharServer example at attachments.

    Something happened at the example. I just attached 2 clients. The clients lost connection after approximately 1min even when ones stay passive. The putty provides fatal error.

    Thank you.
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	fatal error.JPG 
Views:	18 
Size:	16.5 KB 
ID:	26609  
    Attached Files Attached Files

  7. #82
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    I tried the program and it seems to work fine. Some notes and questions:

    1. Do you have the latest QNEthernet installed in your Arduino libraries folder? (The latest is v0.10.0, and not available yet from the Library Manager.)
    2. Are you using the latest Teensyduino? (1.55 as of this writing.)
    3. For configuring a static IP without starting the DHCP client, use `Ethernet.begin(ip, subnet, gateway)` instead of `Ethernet.begin()`.
    4. For the differences between the boolean operator and `connected()` for `EthernetClient`s, see https://github.com/ssilverman/QNEthe...netclient-work.
    5. For connection detection and data availability, use `if (clients[i].available() > 0)` and `if (!clients[i].connected())` instead of `if (clients[i] && clients[i].available() > 0)` and `if (clients[i] && !clients[i].connected())`. In other words, no need to use the boolean operator form in addition to the `connected()` call. They both check for connection, but `connected()` also checks for data available after a disconnect.
    Last edited by shawn; 11-19-2021 at 05:40 PM.

  8. #83
    Junior Member
    Join Date
    Jul 2020
    Posts
    13
    Thank you shawn for advices

    I am using 0.9.0 version of library with 1.55 version of teensyduino.

    The issue with fatal error appeared even with one client after some time.
    I tried some changes:
    replace USB to Ethernet converter for another model to exclude hardware issue. It does not help.
    Use different declaration. The declaration Ethernet.begin(ip, subnet, gateway); without Ethernet.begin(); helps to eliminate the issue. It exactly what you suggested at #3.

    My original application tried to receive IP from DHCP first and assign static IP if fail. The code:
    Ethernet.begin();
    if (!Ethernet.waitForLocalIP(10000)){
    Ethernet.begin(ip, subnet, gateway);
    // or
    // Ethernet.setLocalIP(ip);
    // Ethernet.setSubnetMask(subnet);
    // Ethernet.setGatewayIP(gateway);
    }
    The fatal error appeared in such case again. Is it possible to resolve this issue?

    shawn, thank you for boolean, connected() and available() explanation.
    I would like to ask a question: The library description said the resources cleaned up by library automatically after disconnected clients. Is it right? Could I omit client[i].stop(); or client[i].close(); statements?

    Thank you

  9. #84
    Junior Member
    Join Date
    Jul 2020
    Posts
    13
    Looks that "Ethernet" should be closed before opening again. The statement Ethernet.end(); in front of new opening Ethernet.begin(ip, subnet, gateway) solves the issue for DHCP case.

  10. #85
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    A few more comments
    Also, you've asked great questions.

    1. I was wrong about replacing `if (clients[i] && !clients[i].connected())` with `if (!clients[i].connected())`. As your code is structured currently, `clients[i].connected()` will always return false if never-been or not connected (with no data available), and so you'll see a continuous stream of "disconnect client #" messages without that `clients[i]` boolean check. However, internally, when the client has been connected and then gets disconnected, `clients[i]` might return true if the stack hasn't yet updated, but then inside `operator bool()`, the stack gets updated and then `clients[i].connected()` will return false, causing that `if` block to execute. This gets confusing and is why I use a ClientState object in the example code. On the simplest level, it tracks whether a client has no more data available, and when that's true, the associated ClientState instance gets removed from the list (the vector). If you don't use a list removal scheme then you'll still need an associated `bool` variable in some ClientState object to know whether to stop() or close() the client socket. Otherwise, there's no obvious way to know, just by looking at an instance of EthernetClient whether to release it or not; it might need to be released or it might not have been connected in the first place (see also point #2 below). Does that all make sense?

    1.5. To expand on that last thought in the previous point, it might appear that `(clients[i] && !clients[i].connected())` can never be true because both indicate whether the socket is connected (and empty in the case of the second function). Walking through it, `clients[i].connected()`will be false when disconnected and empty. `clients[i]` will be false when disconnected. Therefore, `!clients[i].connected()`, when true, must indicate that the socket is disconnected, meaning `clients[i]` will be false. However, both `operator bool()` (i.e. what `clients[i]` calls) and `connected()` move the stack along, and so internal state can be changed after each of those calls, invalidating the "connected" state.

    2. It's always a good idea to clean up resources when you own and are done with them, and not calling release functions when not needed, as "good practice". Having said that, the resources will be cleaned up as needed inside: connected(), operator bool(), available(), read(), read(buf, size), and peek(). (Also in stop() and close(), of course.) This means that some call somewhere needs to be made to release the resources. For example, in the FixedWidthServer example, you'll note the ClientState's `closed` variable is set to true when a call to `connected()` returns false. In that case (if you look at the source code in QNEthernetClient.cpp:connected()), `conn_`, the shared pointer, gets set to nullptr when that function returns false (or it's already nullptr). You'll also note that the example never calls `close()` or `stop()` on the client. Also note that the calls to `clients[i].available() > 0` in your code will clean up if necessary, so you don't need to call `stop()` below.

    3. I don't love how the `operator bool()` and `connected()` API is defined because it's confusing, but I'm trying to maintain at least rudimentary compatibility with the Arduino API.

    4. You don't need to obtain your own MAC address (eg. your `teensyMAC(mac)` function). By default, both `Ethernet.begin` functions use the Teensy's built-in MAC address. To access this, you can call `Ethernet.macAddress(mac)` (or using the Arduino-style `Ethernet.MACAddress(mac)`). If you really want to use your own MAC address then you can use one of the other `begin` functions. (`setMACAddress(mac)` isn't yet implemented).

    5. I'm working on letting the `begin` functions be called without having to call `end()` in between, for robustness. In your example, what's happening might be: 1. Set via DHCP, so the DHCP client is started. 2. Your specified timeout happens before DHCP gets a chance to vend an address, but the DHCP client is still running. 3. You set the static IP and then start the server. 4. The DHCP client gets an address and replaces the static address. ----- In my experiments, where I time out after 1 second (DHCP for me takes about 5-6 seconds), the server, even though the address has changed, continues to work and accepts connections on the newly-DHCP-assigned address. I don't see Teensy crashes. To avoid having to call `end()` (because it spins down the clock and doesn't need to), I'm calling `dhcp_release_and_stop(netif_);` at the top of `EthernetClass::begin(ip, mask, gateway)`, just after `netif_ = enet_netif();`.

    6. Could you try adding that `dhcp_release_and_stop(netif_);` line and see if that works for you instead of calling `end()`? Also be sure to get the latest version from GitHub.

  11. #86
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    @rya I've attached a different way to do the AdvancedChatServer; it just shows some other approaches. I've also made some changes to the library in the repo, so you might need to download the latest.

    See: AdvancedChatServer_take1.ino

  12. #87
    Junior Member
    Join Date
    Jul 2020
    Posts
    13
    Thank you @shawn

  13. #88
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    I just released v0.11.0. The changes:

    Code:
    ### Added
    * Implemented `EthernetClass::setMACAddress(mac)`.
    * Added `EthernetServer::maxListeners()`, `EthernetClient::maxSockets()`, and
      `EthernetUDP::maxSockets()` so user code doesn't need to guess. These are
      `constexpr` functions that return the compile-time constants from the
      lwIP configuration.
    * Added `EthernetServer::port()` for returning the server's port.
    * Added `EthernetClass::setHostname(hostname)` and `hostname()` for setting and
      getting the DHCP client option 12 hostname.
    * Added `EthernetClass::maxMulticastGroups()` `constexpr` function.
    * Added a "Write immediacy" subsection to the README that addresses when data is
      sent over a connection. It's under the "How to write data to
      connections" section.
    
    ### Changed
    * Changed the default DHCP client option 12 hostname to "teensy-lwip".
    
    ### Fixed
    * Stop the DHCP client when restarting `Ethernet` (in `begin(ip, mask, gateway)`
      and `setMACAddress(mac)`) to ensure that a static IP won't get overwritten by
      any previously running DHCP client. This also obviates the need to call
      `Ethernet.end()` before re-calling `begin`.
    This is the first release that I'll add to the Arduino Library Manager.

    Link: https://github.com/ssilverman/QNEthe...es/tag/v0.11.0

  14. #89
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    @TeensyWolf: I made a new "ieee1588" branch in the project: https://github.com/ssilverman/QNEthernet/tree/ieee1588
    Do a diff with the "master" branch to see what changed in the API. Let me know if it works for you. Note that neither the README nor the CHANGELOG show complete changes for this.

  15. #90
    Senior Member
    Join Date
    Feb 2020
    Posts
    139
    @shawn, that's fantastic. I'll take a look over the weekend. Happy New Year and thanks!

  16. #91
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    See the following post for more IEEE 1588 details for the library. Thereís lots of posts discussing or asking about PTP, so I just chose one.
    https://forum.pjrc.com/threads/27467...l=1#post296909

  17. #92
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    I just released v0.12.0. The changes:

    Code:
    ## [0.12.0]
    
    ### Added
    * Added a way to disable and enable Nagle's algorithm. The new functions are
      `EthernetClient::setNoDelay(flag)` and `isNoDelay()`.
    * Implemented `EthernetServer::availableForWrite()` as the minimum availability
      of all the connections, or zero if there's no connections.
    * New `AppWithListenersTemplate` example.
    * Added `EthernetClass::operator bool()` for testing whether Ethernet
      is initialized.
    * Added a new way to send and receive raw Ethernet frames. There's a new
      `EthernetFrame` instance (of `EthernetFrameClass`) that is used similarly
      to `EthernetUDP`.
    * New `RawFrameMonitor` example.
    * New `EthernetUDP::send(data, len)` function for sending a packet without
      having to use `beginPacket()`/`write()`/`endPacket()`. It causes
      less overhead.
    
    ### Changed
    * Changed `EthernetUDP::flush()` to be a no-op.
    * Reduced lwIP's `MEM_SIZE` to 16KiB from 24000.
    * Split `MDNSClass::addService()` into two overloaded functions: one with three
      arguments and one with four. No more defaulted TXT record function parameter;
      the three-argument version calls the four-argument version with NULL for
      that function.
    * Updated `keywords.txt`.
    * Updated `SNTPClient` example: Removed unneeded includes, made the packet
      buffer a global variable, and added setting the RTC and time.
    * Changed `EthernetClass::mtu()` to `static size_t`. It was non-static
      and `int`.
    * Updated `enet_output_frame(frame, len)` to check if the system is initialized.
    
    ### Removed
    * Removed `EthernetClass::sendRaw(frame, len)` because there's a new
      `EthernetFrame` API with a `send(frame, len)` function.
    
    ### Fixed
    * Fixed the length check when sending raw Ethernet frames to exclude the FCS
      field. It checks that the length is in the range 60-1518 instead of 64-1522.
    * Fixed `check_link_status()` to check if Ethernet is initialized before trying
      to access the PHY.
    Highlights:
    * New Ethernet raw frame API, `EthernetFrame`,
    * Nagle's algorithm access,
    * Reduction of pre-allocated memory by 8k, and
    * Two new examples.

    Link: https://github.com/ssilverman/QNEthe...es/tag/v0.12.0
    Last edited by shawn; 01-08-2022 at 06:04 PM.

  18. #93
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    I just released v0.13.0. The changes:

    Code:
    ### Added
    * `EthernetFrame` convenience functions that also write the header:
      * `beginFrame(dstAddr, srcAddr, typeOrLen)`
      * `beginVLANFrame(dstAddr, srcAddr, vlanInfo, typeOrLen)`
    * `qindesign::network::util` Print utility functions. The `breakf` function
      parameter is used as the stopping condition in `writeFully()`.
      * `writeFully(Print &, buf, size, breakf = nullptr)`
      * `writeMagic(Print &, mac, breakf = nullptr)`
    * `enet_deinit()` now gracefully stops any transmission in progress before
      shutting down Ethernet.
    * `EthernetClass` functions:
      * `linkIsFullDuplex()`: Returns whether the link is full duplex (`true`) or
        half duplex (`false`).
      * `broadcastIP()`: Returns the broadcast IP address associated with the
        current local IP and subnet mask.
    * Functions that return a pointer to the received data:
      * `EthernetUDP::data()`
      * `EthernetFrame::data()`
    * `DNSClient::getServer(index)` function for retrieving a specific DNS
      server address.
    * `EthernetUDP::localPort()`: Returns the port to which the socket is bound.
    * Three new examples:
      1. `IPerfServer`
      2. `OSCPrinter`
      3. `PixelPusherServer`
    
    ### Changed
    * The `EthernetClient::writeFully()` functions were changed to return the number
      of bytes actually written. These can break early if the connection was closed
      while attempting to send the bytes.
    * Changed `EthernetClient::writeFully()` functions to use the new `writeFully()`
      Print utility function.
    * Changed the `Ethernet` object to be a reference to a singleton. This matches
      how the `EthernetFrame` object works.
    * Changed the `read(buf, len)` functions to allow a NULL buffer so that the
      caller can skip data without having to read into a buffer.
    * Moved internal classes and structs into an "internal" namespace to avoid any
      potential contflicts with user declarations.
    
    ### Removed
    * Removed IEEE 1588 initialization and timer read.
    
    ### Fixed
    * Fixed `EthernetClient::availableForWrite()` to re-check the state after the
      call to `EthernetClass::loop()`.
    Highlights:
    * More convenience functions, including `Ethernet.broadcastIP()`, `Ethernet.linkIsFullDuplex()`, and some `EthernetFrame` functions,
    * A way to get the direct pointer to received UDP and Frame data,
    * A way to skip received data by passing in a NULL buffer to the read() functions, and
    * Three new examples: IPerfServer, OSCPrinter, and PixelPusherServer.

    Link: https://github.com/ssilverman/QNEthe...es/tag/v0.13.0

  19. #94
    Quote Originally Posted by shawn View Post
    I just released v0.13.0.
    Sounds great, @shawn. Thanks for your continuing work on this.

  20. #95
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    Thanks, Joe!

  21. #96
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    Who's up for testing PTP support and IPv6 support (they're separate)? I'm gauging interest in actual testing before spending much more time on them. If at least two people per "thing" might actually utilize it and provide me some feedback, then I'll clean some stuff up and push the branches.

    To whet your appetite, here's some code that watches address changes:

    Code:
    #include <QNEthernet.h>
    
    using namespace qindesign::network;
    
    constexpr uint32_t kDHCPTimeout = 10000;  // 10 seconds
    
    // Main program setup.
    void setup() {
      Serial.begin(115200);
      if (CrashReport) {
        Serial.println(CrashReport);
        CrashReport.clear();
      }
      stdPrint = &Serial;  // Make printf work (a QNEthernet feature)
      printf("Starting...\n");
    
      uint8_t mac[6];
      Ethernet.macAddress(mac);
      printf("MAC = %02x:%02x:%02x:%02x:%02x:%02x\n",
             mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    
      // Listen for link changes
      Ethernet.onLinkState([](bool state) {
        printf("[Ethernet] Link %s\n", state ? "ON" : "OFF");
        // Optionally tell something about the new state
      });
    
      // Listen for IPv4 address changes
      Ethernet.onAddressChanged([]() {
        IPAddress ip = Ethernet.localIP();
        bool hasIP = !(ip == INADDR_NONE);  // IPAddress has no operator!=()
        if (hasIP) {
          IPAddress ip = Ethernet.localIP();
          IPAddress subnet = Ethernet.subnetMask();
          IPAddress gw = Ethernet.gatewayIP();
          IPAddress dns = Ethernet.dnsServerIP();
    
          printf("[Ethernet] Address changed:\n"
                 "    Local IP    = %u.%u.%u.%u\n"
                 "    Subnet mask = %u.%u.%u.%u\n"
                 "    Gateway     = %u.%u.%u.%u\n"
                 "    DNS         = %u.%u.%u.%u\n",
                 ip[0], ip[1], ip[2], ip[3],
                 subnet[0], subnet[1], subnet[2], subnet[3],
                 gw[0], gw[1], gw[2], gw[3],
                 dns[0], dns[1], dns[2], dns[3]);
        } else {
          printf("[Ethernet] Address changed: No IP address\n");
        }
    
        // Tell something about the new state
        //addressChanged(hasIP);
      });
    
      // Listen for IPv6 address changes
      Ethernet.onAddress6Changed([](int which,
                                    const IPv6Address &oldIP,
                                    uint8_t oldState) {
        printf("[Ethernet] Address6 changed: \n"
               "    IP: ");
        IPv6Address ip6 = Ethernet.localIPv6(which);
        uint8_t state = Ethernet.localIPv6State(which);
        if (oldIP != ip6) {
          stdoutPrint.print(oldIP);
          printf(" -> ");
        }
    
        if (ip6.isLinkLocal())        { printf("(link-local)"); }
        else if (ip6.isUniqueLocal()) { printf("(unique local)"); }
        else if (ip6.isGlobal())      { printf("(global)"); }
        else if (ip6.isMulticast())   { printf("(multicast)"); }
        else if (ip6.isLoopback())    { printf("(loopback)"); }
        else if (ip6.isAny())         { printf("(any)"); }
        stdoutPrint.println(ip6);
    
        printf("    State: ");
        if (oldState != state) {
          printState(oldState);
          printf(" -> ");
        }
        printState(state);
        printf("\n");
    
        // Tell something about the new state
        //IPv6AddressStates s = IPv6Address::state(state);
        //if ((s == IPv6AddressStates::kPreferred) ||
        //    (s == IPv6AddressStates::kDeprecated)) {
        //  address6Changed(ip6, true);
        //} else if ((s == IPv6AddressStates::kDuplicated) ||
        //           (s == IPv6AddressStates::kInvalid)) {
        //  address6Changed(ip6, false);
        //}
      });
    
      printf("Starting Ethernet with DHCP...\n");
      if (!Ethernet.begin()) {
        printf("Failed to start Ethernet\n");
        return;
      }
    
      // Waiting is an alternative to a listener-only approach, where the
      // listeners start and stop things, depending on what happens
      if (!Ethernet.waitForLocalIP(kDHCPTimeout)) {
        printf("Failed to get IP address from DHCP\n");
      } else {
        printf("Got IP address from DHCP\n");
      }
      if (!Ethernet.waitForLocalIPv6(kDHCPTimeout)) {
        printf("Failed to get local IPv6 address\n");
      } else {
        printf("Got local IPv6 address\n");
      }
      if (!Ethernet.waitForGlobalIPv6(kDHCPTimeout)) {
        printf("Failed to get global IPv6 address\n");
      } else {
        printf("Got global IPv6 address\n");
      }
    
      printf("IPv6 addresses:\n");
      for (int i = 0; i < Ethernet.numIPv6Addresses(); i++) {
        printf("    %d: ", i);
        IPv6Address ip6 = Ethernet.localIPv6(i);
        stdoutPrint.println(ip6);
      }
    
      IPv6Address ip6 = Ethernet.linkLocalIPv6();
      printf("Link local:   ");
      stdoutPrint.println(ip6);
      ip6 = Ethernet.uniqueLocalIPv6();
      printf("Unique local: ");
      stdoutPrint.println(ip6);
      ip6 = Ethernet.globalIPv6();
      printf("Global:       ");
      stdoutPrint.println(ip6);
    }
    
    // Prints the given IPv6 address state.
    void printState(uint8_t state) {
      IPv6AddressStates s = IPv6Address::state(state);
      switch (s) {
        case IPv6AddressStates::kInvalid:
          printf("Invalid");
          break;
        case IPv6AddressStates::kPreferred:
          printf("Preferred");
          break;
        case IPv6AddressStates::kDeprecated:
          printf("Deprecated");
          break;
        case IPv6AddressStates::kDuplicated:
          printf("Duplicated");
          break;
        case IPv6AddressStates::kTentative:
          printf("Tentative(%d)", IPv6Address::tentativeCount(state));
          break;
        default:
          printf("Unknown");
      }
    }
    
    // Main program loop.
    void loop() {
    }
    Last edited by shawn; 02-08-2022 at 03:25 AM.

  22. #97
    Hi Shawn. I see you did not get any replies. I'm interested in PTP. Not sure how well I can support you with testing, but I'll try.

  23. #98
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    @joepasquariello thanks. I sent you an email via the “Send Email” feature here.

  24. #99
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    490
    I just released v0.14.0 of the library. The changes:

    Code:
    ## [0.14.0]
    
    ### Added
    * Added a `util::StdioPrint` class, a `Print` decorator for stdio output files.
      It routes `Print` functions to a specific `FILE*`. This exists mainly to make
      it easy to use `Printable` objects without needing a prior call to `fflush()`.
    * Added `MDNSClass::maxServices()`.
    * Added `operator==()` and `operator!=()` operators for `const IPAddress`. They
      are in the usual namespace. These allow `==` to be used with `const IPAddress`
      values without having to use `const_cast`, and also introduce the completely
      missing `!=` operator.
    * Added a way to declare the `_write()` function as weak via a new
      `QNETHERNET_WEAK_WRITE` macro. Defining this macro will cause the function to
      be declared as weak.
    * Implemented `EthernetFrameClass::availableForWrite()`.
    * Added size limiting to `EthernetFrameClass` write functions.
    * New `EthernetClass::waitForLink(timeout)` function that waits for a link to
      be detected.
    * Added a way to allow or disallow receiving frames addressed to specific MAC
      addresses: `EthernetClass::setMACAddressAllowed(mac, flag)`
    
    ### Changed
    * Updated `SNTPClient` example to use `EthernetUDP::send()` instead of
      `beginPacket()`/`write()`/`endPacket()`.
    * Updated `PixelPusherServer` example to use the frame average for the
      update period.
    * Implemented `EthernetHardwareStatus` enum for the deprecated
      `Ethernet.hardwareStatus()` function. This replaces the zero return value with
      the new non-zero `EthernetOtherHardware`.
    * Cleaned up how internal IP addresses are used.
    * Changed `_write()` (stdio) to do nothing if the requested length is zero
      because that's what `fwrite()` is specified to do.
    * Updated examples to use new `operator!=()` for `IPAddress`.
    * Moved lwIP's heap to RAM2 (DMAMEM).
    * Updated `EthernetFrame`-related documentation to explain that the API doesn't
      receive any known Ethernet frame types, including IPv4, ARP, and IPv6
      (if enabled).
    * Clarified in the examples that `Ethernet.macAddress()` retrieves, not sets.
    * Changed `EthernetClass::setMACAddress(mac)` parameter to `const`.
    * Moved CRC-32 lookup table to RAM2 (DMAMEM).
    * Made const those functions which could be made const.
    * Updated examples and README to consider listeners and their relationship with
      a static IP and link detection.
    
    ### Fixed
    
    * Fixed `EthernetUDP::send()` function to take the host and port as arguments,
      per its description. There's now two of them: one that takes an `IPAddress`
      and another that takes a `char *` hostname.
    * Fixed `enet_output_frame()` to correctly return `false` if Ethernet is
      not initialized.
    * Fixed not being able to set the DNS server IP before starting Ethernet.
    * Fixed raw frame API to consider any padding bytes.
    Highlights:
    * "==" and "!=" operators for `const IPAddress`.
    * A way to wait for link up.
    * A way to allow specific MAC addresses past the system filter, for use with the raw frame API. (Short of enabling promiscuous mode.)
    * Smaller memory usage by moving lwIP's heap and the CRC-32 lookup table to RAM2 (DMAMEM).
    * Updated the README and examples to consider listeners and their relationship with static IPs and link detection.
    * Fixed the raw frame API to consider any padding bytes.

    Link: https://github.com/ssilverman/QNEthe...es/tag/v0.14.0

  25. #100
    Senior Member wwatson's Avatar
    Join Date
    Aug 2017
    Posts
    744
    @shawn - I have been working with a T4.1, Arduino 1.8.19 and TD1.56. I am redoing an FTP client that was working with WiFiSPI on an esp8266. I now kind of have working with QNEthernet. There are a couple of issues though. For some reason that I can't figure out I cannot connect to an FTP server using a host name but can connect if using a remote IP address. I am having the same problem with NativeEthernet as well. I have three different computers using UBuntu 20.04 setup with vsftpd. Using gFTP or Filezilla I can connect to any of the other computers using a hostname.local. They all three will connect to each other.

    Here is a trimmed down sketch that reproduces the problem with the T4.1:
    Code:
    #include "Arduino.h"
    #include "QNEthernet.h"
    #include "QNDNSClient.h"
    
    using namespace qindesign::network;
    
    EthernetClient sclient;
    IPAddress ip;
    constexpr uint32_t kDHCPTimeout = 10000;  // 10 seconds
    
    void setup(void) {
      Serial.begin(115200);
      while (!Serial && millis() < 4000) {
        // Wait for Serial to initialize
      }
      stdPrint = &Serial;  // Make printf work (a QNEthernet feature)
    
    #if defined(ARDUINO_TEENSY41) || defined(ARDUINO_TEENSY40)
      if(CrashReport)
    	Serial.print(CrashReport);
    #endif
    
      printf("\nFTP Client\n\n");
    
      uint8_t mac[6];
      Ethernet.macAddress(mac);
      printf("MAC = %02x:%02x:%02x:%02x:%02x:%02x\n",
             mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
      printf("Starting Ethernet with DHCP...\n");
      if (!Ethernet.begin()) {
        printf("Failed to start Ethernet\n");
        return;
      }
      if (!Ethernet.waitForLocalIP(kDHCPTimeout)) {
        printf("Failed to get IP address from DHCP\n");
        return;
      }
      IPAddress ip = Ethernet.localIP();
      printf("    Local IP    = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
      ip = Ethernet.subnetMask();
      printf("    Subnet mask = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
      ip = Ethernet.gatewayIP();
      printf("    Gateway     = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
      ip = Ethernet.dnsServerIP();
      printf("    DNS         = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
    }
    
    void loop(void) {
    
    delay(1000);
    
      //Try with hostname even without .local still fails.
      if(!DNSClient::getHostByName((const char *)"wwatsond1.local", ip, kDHCPTimeout))
        printf("getHostByName((const char *)wwatsond1.local, ip, kDHCPTimeout) FAILED\n");
      else
        printf("getHostByName((const char *)wwatsond1.local, ip, kDHCPTimeout) WORKED\n");
    
      //Try with IP
      if(!DNSClient::getHostByName((const char *)"192.168.0.104", ip, kDHCPTimeout))
        printf("getHostByName((const char *)192.168.0.104, ip, kDHCPTimeout) FAILED\n");
      else
        printf("getHostByName((const char *)192.168.0.104, ip, kDHCPTimeout) WORKED\n");
    
      //Try with hostname even without .local still fails.
      if (sclient.connect("wwatsond1.local", 21)) {  // 21 = FTP server
       printf((const char *)"Connect with Host name: connected\n");
      } else {
        printf((const char *)"Connect with Host name: failed\n");
      }
    
      //Try with IP
      if (sclient.connect("192.168.0.104", 21)) {  // 21 = FTP server
       printf((const char *)"Connect with IP: Connected\n");
      } else {
        printf((const char *)"Connect with IP: failed\n");
      }
    
    }
    The resulting output:
    Code:
    FTP Client
    
    MAC = 04:e9:e5:0b:b8:0f
    Starting Ethernet with DHCP...
        Local IP    = 192.168.0.106
        Subnet mask = 255.255.255.0
        Gateway     = 192.168.0.1
        DNS         = 192.168.0.1
    getHostByName((const char *)wwatsond1.local, ip, kDHCPTimeout) FAILED
    getHostByName((const char *)192.168.0.104, ip, kDHCPTimeout) WORKED
    Connect with Host name: failed
    Connect with IP: Connected
    I am also testing directly with 'getHostByName()' which also fails using a hostname. I am looping the code with a delay a the beginning of loop().
    Eventually the T4. stalls with this output:
    Code:
    getHostByName((const char *)wwatsond1.local, ip, kDHCPTimeout) FAILED
    getHostByName((const char *)192.168.0.104, ip, kDHCPTimeout) WORKED
    Connect with Host name: failed
    Connect with IP: Connected
    getHostByName((const char *)wwatsond1.local, ip, kDHCPTimeout) WORKED
    getHostByName((const char *)192.168.0.104, ip, kDHCPTimeout) WORKED
    Assertion "tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT" failed at line 1442 in /home/wwatson/Arduino/libraries/QNEthernet/src/lwip/tcp.c
    Notice that at that point getHostByName() worked?

    when using the full version of the FTP client and connecting with a remote IP I can use it for a while but eventually It will stall with the same Assertion error shown above.

    Any advice is appreciated
    Thanks

    EDIT: I have tried changing the T4.1 clock speed and optimizations. Results were the same...
    Last edited by wwatson; 04-24-2022 at 03:31 PM. Reason: More info...

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •