Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ethernet GPIO config #2616

Merged
merged 7 commits into from Feb 9, 2023
Merged

Conversation

technobly
Copy link
Member

@technobly technobly commented Feb 2, 2023

Problem

Devices that support the WizNet Ethernet controller have 3 hard coded GPIO pins in system firmware that collide with SPI1 usage. If a user wants to use Ethernet which requires SPI and D3, D4 and D5 to be reserved on Argon/Boron/P2/Photon2/TrackerM for example. If they also need to use hardware SPI1 for some other peripheral, they cannot.

Solution

  • Make Ethernet GPIO pins configurable for all platforms Argon/Boron/BSoM/B5SoM/ESoMX/P2/Tracker/TrackerM.
  • The API is not official and will change in a future release, but once set the device shall remember the configuration and the API need not be called on every boot.
  • The system must be reset after setting the pin configuration, it will not work in STARTUP without a reset so it's best to use it as in the example below
  • System.enableFeature(FEATURE_ETHERNET_DETECTION) must be used, and if Ethernet is detected, Ethernet.isOn() will return true. If the system boots with FEATURE_ETHERNET_DETECTION disabled (default), the Ethernet API's will not work and Ethernet.isOn() will return false, which can be used to set the correct pin configuration, enabled FEATURE_ETHERNET_DETECTION and reset the system. Upon successive boots the Ethernet controller will be found and pin config and reset will not need to be called again. NOTE: ensure the device does not get caught up in a reset loop with a retained variable, or some other NVM method. Another important note is when following this sequence of DISABLED, pin config change, ENABLE, the Ethernet GPIO pins will not inadvertently be toggled upon first boot. Namely these are the CS and RESET OUTPUT pins, whereas the INT pin is an INPUT with external 10k pull-up.
  • Pins defined as PIN_INVALID will use their default value.

Steps to Test

  • Run TEST=app/ethernet with an Ethernet Wing and Argon/Boron/Photon2.
  • Test moving all 3 pins
  • Test moving only 1 or 2 pins while keeping the others set to PIN_INVALID
  • Test cold booting the device in Safe Mode
  • Test OTA updates on the device

How to change the pins:

Set the pins to whatever available GPIO is required. All can be explicitly set, or if you would like one or more to be in their default location you may use PIN_INVALID. Enable the Ethernet detection feature, and reset the system. Avoid calling this again on successive boots by using the Ethernet.isOn() API. (See example snippet and full example below)

if_wiznet_pin_remap remap = {};
remap.base.type = IF_WIZNET_DRIVER_SPECIFIC_PIN_REMAP;
remap.cs_pin = D2;
remap.reset_pin = D0;
remap.int_pin = D1;
if_request(nullptr, IF_REQ_DRIVER_SPECIFIC, &remap, sizeof(remap), nullptr);
System.enableFeature(FEATURE_ETHERNET_DETECTION);
System.reset();

Example App

#include "Particle.h"
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);
Serial1LogHandler log1Handler(115200, LOG_LEVEL_ALL);

retained uint8_t resetRetry = 0;

void setup() {
#if HAL_PLATFORM_WIFI && !HAL_PLATFORM_WIFI_SCAN_ONLY
    // To force Ethernet only, clear Wi-Fi credentials
    Log.info("Clear Wi-Fi credentionals...");
    WiFi.clearCredentials();
#endif // HAL_PLATFORM_WIFI && !HAL_PLATFORM_WIFI_SCAN_ONLY

    // Disable Listening Mode if not required
    if (System.featureEnabled(FEATURE_DISABLE_LISTENING_MODE)) {
        Log.info("FEATURE_DISABLE_LISTENING_MODE enabled");
    } else {
        Log.info("Disabling Listening Mode...");
        System.enableFeature(FEATURE_DISABLE_LISTENING_MODE);
    }

    Log.info("Checking if Ethernet is on...");
    if (Ethernet.isOn()) {
        Log.info("Ethernet is on");
        uint8_t macAddrBuf[8] = {};
        uint8_t* macAddr = Ethernet.macAddress(macAddrBuf);
        if (macAddr != nullptr) {
            Log.info("Ethernet MAC: %02x %02x %02x %02x %02x %02x",
                    macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);
        }
        Ethernet.connect();
        waitFor(Ethernet.ready, 30000);
        Log.info("Ethernet.ready: %d", Ethernet.ready());
        resetRetry = 0;
    } else if (++resetRetry <= 3) {
        Log.info("Ethernet is off or not detected, attmpting to remap pins: %d/3", resetRetry);

        if_wiznet_pin_remap remap = {};
        remap.base.type = IF_WIZNET_DRIVER_SPECIFIC_PIN_REMAP;

        remap.cs_pin = PIN_INVALID; // default
        remap.reset_pin = PIN_INVALID; // default
        remap.int_pin = PIN_INVALID; // default

        // remap.cs_pin = D5;
        // remap.reset_pin = D3;
        // remap.int_pin = D4;

        // remap.cs_pin = D2;
        // remap.reset_pin = D0;
        // remap.int_pin = D1;

        // remap.cs_pin = D10;
        // remap.reset_pin = D6;
        // remap.int_pin = D7;

        // remap.cs_pin = A0;
        // remap.reset_pin = A2;
        // remap.int_pin = A1;

        // remap.cs_pin = 50; // bad pin config
        // remap.reset_pin = 100;
        // remap.int_pin = 150;

        auto ret = if_request(nullptr, IF_REQ_DRIVER_SPECIFIC, &remap, sizeof(remap), nullptr);
        if (ret != SYSTEM_ERROR_NONE) {
            Log.error("Ethernet GPIO config error: %d", ret);
        } else {
            if (System.featureEnabled(FEATURE_ETHERNET_DETECTION)) {
                Log.info("FEATURE_ETHERNET_DETECTION enabled");
            } else {
                Log.info("Enabling Ethernet...");
                System.enableFeature(FEATURE_ETHERNET_DETECTION);
            }
            delay(500);
            System.reset();
        }
    }

    Particle.connect();
}

void loop() {
    static system_tick_t lastPublish = millis();
    static int count = 0;
    static bool reconnect = false;

    if (Particle.connected()) {
        reconnect = false;
        if (millis() - lastPublish >= 10000UL) {
            Particle.publish("mytest", String(++count), PRIVATE, WITH_ACK);
            lastPublish = millis();
        }
    }

    // Detect a network dropout and reconnect quickly
    if (!reconnect && !Ethernet.ready()) {
        Log.info("Particle disconnect...");
        Particle.disconnect();
        waitFor(Particle.disconnected, 5000);
        Log.info("Network disconnect...");
        Network.disconnect();
        Particle.connect();
        reconnect = true;
    }
}

References

  • sc-105146

Completeness

  • User is totes amazing for contributing!
  • Contributor has signed CLA (Info here)
  • Problem and Solution clearly stated
  • Run unit/integration/application tests on device
  • Added documentation
  • Added to CHANGELOG.md after merging (add links to docs and issues)

hal/network/lwip/wiznet/wiznetif_config.cpp Show resolved Hide resolved
hal/network/lwip/ifapi.cpp Outdated Show resolved Hide resolved
hal/network/api/ifapi.h Show resolved Hide resolved
hal/src/tron/network/network.cpp Outdated Show resolved Hide resolved
@technobly technobly force-pushed the feature/sc-105146-ethernet-gpio-config branch from d7c54bb to dbc170c Compare February 9, 2023 06:27
@technobly technobly changed the title [p2] Ethernet GPIO config Ethernet GPIO config Feb 9, 2023
hal/network/lwip/wiznet/wiznetif_config.cpp Outdated Show resolved Hide resolved
hal/network/lwip/wiznet/wiznetif_config.cpp Outdated Show resolved Hide resolved
hal/network/lwip/wiznet/wiznetif_config.h Outdated Show resolved Hide resolved
@technobly technobly merged commit a3368a3 into develop Feb 9, 2023
@technobly technobly deleted the feature/sc-105146-ethernet-gpio-config branch February 9, 2023 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants