Bug in Gadgeteer.Modules.GHIElectronics.EthernetENC28 in 4.3?

Hi,

I’m testing networking in the new 4.3 SDK and suspect a bug in the ENC28 driver.
I have a ENC28 module connected to port1 of a Cerbuino Bee board with the underneath (very simple) code :

    public partial class Program
    {
        void ProgramStarted()
        {
            Debug.Print("Program Started");
            ethernetModule.UseDHCP();
            ethernetModule.NetworkUp += EthernetModuleOnNetworkUp;
        }

        private void EthernetModuleOnNetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state)
        {
            Debug.Print("Event nwInterface.IPAddress : " + ethernetModule.NetworkInterface.IPAddress);
        }

When executing the code without breakpoints, the reported IP address is always 0.0.0.0
To get a correct IP address (192.168.0.16 in my case), I need to set a breakpoint on the Debug.Print statement in the OnNetworkUp event and simply click continue when the program breaks. Works every time with the breakpoint, never without the breakpoint.
I tried adding a sleep statement and while loops in different places, but only get success when breaking and continuing the program.

Is this a bug or am I doing something wrong ? To be completely honest, the documentation on the new SDK is more confusing me than it is helping me.

For completeness :
FEZ config reports 4.3.3.0 firmware on the device

Code generated by the designer surface is

namespace GadgeteerTestAppOnCerbuinoBee1 {
    using Gadgeteer;
    using GTM = Gadgeteer.Modules;
    
    
    public partial class Program : Gadgeteer.Program {
        
        /// <summary>The Ethernet ENC28 module using socket 1 of the mainboard.</summary>
        private Gadgeteer.Modules.GHIElectronics.EthernetENC28 ethernetModule;
        
        /// <summary>This property provides access to the Mainboard API. This is normally not necessary for an end user program.</summary>
        protected new static GHIElectronics.Gadgeteer.FEZCerbuinoBee Mainboard {
            get {
                return ((GHIElectronics.Gadgeteer.FEZCerbuinoBee)(Gadgeteer.Program.Mainboard));
            }
            set {
                Gadgeteer.Program.Mainboard = value;
            }
        }
        
        /// <summary>This method runs automatically when the device is powered, and calls ProgramStarted.</summary>
        public static void Main() {
            // Important to initialize the Mainboard first
            Program.Mainboard = new GHIElectronics.Gadgeteer.FEZCerbuinoBee();
            Program p = new Program();
            p.InitializeModules();
            p.ProgramStarted();
            // Starts Dispatcher
            p.Run();
        }
        
        private void InitializeModules() {
            this.ethernetModule = new GTM.GHIElectronics.EthernetENC28(1);
        }
    }
}

@ StefaanV - Can you subscribe to the Microsoft.SPOT.Net.NetworkInformation.NetworkChange.NetworkAddressChanged event and print out the IP in it? I was able to reproduce getting an IP of “0.0.0.0” using your code, but the breakpoint did not fix it. Do you have the cable plugged in when the system starts? Instead of calling UseDHCP, can you call ethernetModule.NetworkInterface.EnableDhcp?

Hello,

I am having the exact same problem using FEZ Cerberus.

I subscribed to these events:

            NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
ethernetENC28.NetworkUp += ethernetENC28_NetworkUp;
ethernetENC28.NetworkDown += ethernetENC28_NetworkDown;

NetworkDown gets called.
NetworkUp does not gets called (It does if I add a breakpoint).
NetworkAddressChanged gets called. (And it gets a valid IP address from DHCP Server)
NetworkAvailabilityChanged does NOT (breakpoint here does not help)

ethernetENC28.NetworkInterface.[b]EnableDhcp/b Did not help neither… even if add the breakpoint to NetworkUp, it does not get called.

What is even more perplexing… It is that if I use UseStaticIp and ran it ONCE (right after testing UseDhcp), the events get call in this sequence:

  • NetworkAddressChanged
  • NetworkUp
  • NetworkDown (wrong order!)

NetworkAvailabilityChanged DOES NOT get called.

I ran the program again (still using Static IP) and the events get call properly:

  • NetworkAvailabilityChanged
  • NetworkAddressChanged
  • NetworkDown
  • NetworkUp

If I ran the program again using UseDhcp, it works, but it is using the information used by UseStaticIP, I have to use [em]FEZ Config[/em] to remove the IP info.

Ideas?

This is the code I am using to test it:

    public partial class Program
    {
        private object _lock = new object();
        private bool _isNetworkAvailable = false;

        void ProgramStarted()
        {
            NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
            NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;

            ethernetENC28.NetworkUp += ethernetENC28_NetworkUp;
            ethernetENC28.NetworkDown += ethernetENC28_NetworkDown;

            Debug.Print("Warming up...");
            Thread.Sleep(5000);

            //foreach (var item in NetworkInterface.GetAllNetworkInterfaces())
            //{
            //    try
            //    {
            //        item.EnableDhcp();
            //        item.EnableDynamicDns();    
            //    }
            //    catch (Exception)
            //    {
            //        // Ignore
            //    }

            //}

            ethernetENC28.DebugPrintEnabled = true;
            //ethernetENC28.UseThisNetworkInterface();
            
            ethernetENC28.UseDHCP();
            //ethernetENC28.NetworkInterface.EnableDhcp();
//            ethernetENC28.NetworkSettings.EnableDynamicDns();

            //ethernetENC28.UseStaticIP("192.168.10.72", "255.255.255.0", "192.168.10.1", new string[] { "8.8.8.8", "8.8.4.4" });

        }

        void ethernetENC28_NetworkDown(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state)
        {
            Debug.Print("Network Down");
        }

        void ethernetENC28_NetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state)
        {
            Debug.Print("Network Up: " + ethernetENC28.NetworkInterface.IPAddress);
        }

        void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
        {
            lock (_lock)
            {
                Debug.Print("==============================================================");
                Debug.Print("NetworkChange_NetworkAddressChanged");
                Debug.Print("_isNetworkAvailable: " + _isNetworkAvailable);

                foreach (var item in NetworkInterface.GetAllNetworkInterfaces())
                {
                    Debug.Print("Interface   : " + GetNetworkInterfaceTypeName(item.NetworkInterfaceType));
                    Debug.Print("IP          : " + item.IPAddress);
                    Debug.Print("..............................................................");
                }

                Debug.Print("==============================================================");
            }
        }

        void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
        {
            lock (_lock)
            {
                Debug.Print("--------------------------------------------------------------");
                Debug.Print("NetworkChange_NetworkAvailabilityChanged: " + e.IsAvailable);
                _isNetworkAvailable = e.IsAvailable;

                if (e.IsAvailable)
                {
                    foreach (var item in NetworkInterface.GetAllNetworkInterfaces())
                    {
                        Debug.Print("Interface   : " + GetNetworkInterfaceTypeName(item.NetworkInterfaceType));
                        Debug.Print("IP          : " + item.IPAddress);
                        Debug.Print("Mask        : " + item.SubnetMask);
                        Debug.Print("Gateway     : " + item.GatewayAddress);
                        Debug.Print("DCHP Enable : " + item.IsDhcpEnabled);
                        foreach (var dns in item.DnsAddresses)
                        {
                            Debug.Print("DNS         : " + dns);
                        }
                        Debug.Print("..............................................................");
                    }
                }

                Debug.Print("--------------------------------------------------------------");
            }
        }

        private string GetNetworkInterfaceTypeName(NetworkInterfaceType t) {
            switch (t)
            {
                case NetworkInterfaceType.Unknown:
                    return "Unknown";
                case NetworkInterfaceType.Ethernet:
                    return "Ethernet";
                case NetworkInterfaceType.Wireless80211:
                    return "Wireless80211";
            }

            return "NULL";
        }
    }

@ John,

Quick test shows that your suggestion solves the problem when the network cable is already plugged in at program startup. When the network cable is plugged in while the program is running, neither of the events fires.
I’m a bit surprised that you use an event that isn’t a member of the ethernet interface class. After all, controllers can have multiple network interfaces.

Where can I find out which events fire in which circumstances and which I should use ? Same thing with the two ways to indicate HDCP should be used : where can I get information about which alternative to use ?

Thanks for you help,
Stefaan

@ StefaanV - Can you try to run the examples on https://www.ghielectronics.com/docs/30/networking and remove the Ethernet module from the Gadgeteer designer? Using our networking classes outside of a Gadgeteer module is generally more stable. All of those examples have been verified to work on every board.

As for why the two events I mentioned are not part of the interface class: where possible we try to use the plain NETMF functionality instead of extending it with our own classes. It is a shortcoming of the two events I gave that they do not pass the interface the event occurred on as a parameter or, better yet, are members of the interface class.

John,

Thanks for your input. I tried the example in the webpage you reference.

I get [em] “An unhandled exception of type ‘System.Exception’ occurred in GHI.Networking.dll” [/em]on the instantiation of the EthernetENC28J60 object (without further explanation on the exception).

Are the Cpu.Pin.GPIO_PinXX settings appropriate for the CerbuinoBee mainboard ?

mvg,
Stefaan

PS. Here’s the code I used - I removed the ENC28 module from the designer and left port 1 unconnected

    public partial class Program
    {
        private static EthernetENC28J60 netif;

        void ProgramStarted()
        {
            //NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
            //NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;

            netif = new EthernetENC28J60(SPI.SPI_module.SPI1, Cpu.Pin., Cpu.Pin.GPIO_Pin2, Cpu.Pin.GPIO_Pin3);
            netif.Open();
            netif.EnableDhcp();
            netif.EnableDynamicDns();

            while (netif.NetworkInterface.IPAddress == "0.0.0.0")
            {
                Debug.Print("Waiting for DHCP");
                Thread.Sleep(250);
            }

            Debug.Print("Network IPAddress : " + netif.NetworkInterface.IPAddress);
        }

@ StefaanV - you’ll want to update the pins and SPI module in the parameters to the constructor with the actual values for the socket you are using, those are just placeholders.

@ John

I tried your sample in https://www.ghielectronics.com/docs/30/networking with this code and the events still not been called properly.


NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;

// FEZ Cerberus Socket and Pins assigments
var socket = Socket.GetSocket(6, true, null, null);
var pinINT = socket.CpuPins[3];
var pinRESET = socket.CpuPins[4];
var pinSPI_CS = socket.CpuPins[6];

ethernetENC28 = new EthernetENC28J60(SPI.SPI_module.SPI1, pinSPI_CS, pinINT, pinRESET);
ethernetENC28.Open();
ethernetENC28.EnableDhcp();
ethernetENC28.EnableDynamicDns();

With the network cable connected before starting the app:

  • [em]NetworkAvailabilityChanged [/em]DOES NOT get called. (It should, because there was NO network and now it does)
  • [em]NetworkAddressChanged [/em] gets called. (And I can get the IP address)

while the app still running, if I disconnect the cable the events get called properly

  • [em]NetworkAvailabilityChanged [/em] gets called with IsAvailable = FALSE (GOOD!)
  • [em]NetworkAddressChanged [/em] gets called and IP Address = 0.0.0.0 (GOOD!)

I connect the cable again and the events are:

  • [em]NetworkAvailabilityChanged [/em][strong ]DOES [/b]get called. (Now it gets called with IsAvailable = TRUE)
  • [em]NetworkAddressChanged [/em] gets called. (And I can get the IP address)

Why is the event [em]NetworkAvailabilityChanged [/em]not been called on first boot?

If the cable IS NOT connected on Boot, neither events get called, which is OK… and if connect the cable while it is running, everything gets call fine.

Thanks for your help.

To add to @ Dulfe… In 4.2. we had to option to check IsCableConnected. If i am right this seems to be removed in 4.3

@ RobvanSchelven

I did not know that existed, thanks!..

After checking into it I found it (in Net 4.3) in the Ethernet Gadgeteer Module and in the Ethernet NetworkInterface is called CableConnected… but it only tells me if the cable is connected but not if the network is available (it has an IP Address).

If the network is down and the device has not received an IP Address, but the cable is connected… the property CableConnected returns TRUE.

That is better than nothing!.. but I want to detect network. (I know I can try pinging some host, but that takes precious cycles :))

Thanks.

1 Like

@ dulfe - The NetworkAvailablityChanged event tells you if a cable is connected or you are joined to a WiFi network. It does not tell you if you are able to communicate over the network. It essentially tells you the same thing that CableConnected tells you.

If you want to be sure that the network is functioning properly and you can reach a remote host, after you receive the NetworkAddressChanged event with a proper address (if using DHCP), you have to open a socket and attempt communication.

NetworkAvailablityChanged is not raised when you first open an interface because the status of the cable connection hasn’t yet changed. If you are unsure of the starting state of a cable, you can check the CableConnected property after opening the interface. The event will only be raised when the cable is physically inserted or removed after calling open.

Thank you… that explains it.

Now that I know that is the way it works, I can handle it properly.

I really appreciate your help.

Thanks.

@ John,

I looked up the pin numbers but still no success ! I just get an endless list of “Waiting for DHCP”. Disconnecting and reconnecting the network cable doesn’t make any difference.

According to the documentation found om your website, for the cerberusBee,
Chipselect = PA13 = Pin6,
External Interrupt = PA14 = Pin3
Reset = PB10 = Pin4

Or am I still missing something ?

So I adapted the code accordingly :


    public partial class Program
    {
        private static EthernetENC28J60 netif;

        void ProgramStarted()
        {
            netif = new EthernetENC28J60(SPI.SPI_module.SPI1, Cpu.Pin.GPIO_Pin6, Cpu.Pin.GPIO_Pin3, Cpu.Pin.GPIO_Pin4);
            netif.Open();
            netif.EnableDhcp();
            netif.EnableDynamicDns();
            while (netif.NetworkInterface.IPAddress == "0.0.0.0")
            {
                Debug.Print("Waiting for DHCP");
                Thread.Sleep(250);
            }

            Debug.Print("Network IPAddress : " + netif.NetworkInterface.IPAddress);
        }
    }

@ StefaanV

You are using Cerbuino Bee, right?.. if so… I think those ports (3,4 and 6) are from socket 1 (S), so you need to do:


// FEZ Cerbuino Bee Socket and Pins assigments
 var socket = Socket.GetSocket(1, true, null, null);
 var pinINT = socket.CpuPins[3];
 var pinRESET = socket.CpuPins[4];
 var pinSPI_CS = socket.CpuPins[6];

 ethernetENC28 = new EthernetENC28J60(SPI.SPI_module.SPI1, pinSPI_CS, pinINT, pinRESET);

Hope it helps.

@ StefaanV - Pin 6, 4, and 3 just represent the physical pin number on the socket. You still want the low level number of the pin like: PA13, PA14, and PB10. If you have the ENC28 connected to socket 1 on the Cerbuino Bee, the code dulfe gave should work, though I would use the spi module property on the socket object to get the spi module instead of hardcoding it.

@ John,

Sounds like double-Dutch to me. Can you give me the code that represents ?
I searched really hard to get something like Pin.PA13, but found no enum that have PA13 in it. Sorry, but you’ll have to come down to my level for this to work.

Stefaan

@ StefaanV - Assuming you are using socket 1 on the Cerbuino Bee, you want something similar to dulfe’s code:


var socket = GT.Socket.GetSocket(1, true, null, null);

var intPin = socket.CpuPins[3];
var resetPin = socket.CpuPins[4];
var csPin = socket.CpuPins[6];
var spiModule = socket.SPIModule;

var ethernetENC28 = new EthernetENC28J60(spiModule, csPin, intPin, resetPin);

I faced same problem - can not get network to work. Symptoms like described earlier.
I tried your code in console application, but get the error at the first line:

[quote]An unhandled exception of type ‘Gadgeteer.Socket.InvalidSocketException’ occurred in Gadgeteer.dll

Additional information: Invalid socket number 1 specified.[/quote]

If I put this code in Gadgeteer application, I get another error at the last line:

[quote]An unhandled exception of type ‘System.InvalidOperationException’ occurred in GHI.Networking.dll

Additional information: This interface type is already created.[/quote]

@ Skyblade - The code I listed is only meant to be used in a Gadgeteer application. The exception you are getting in Gadgeteer is likely because you have already connected the ENC28 in the designer to the Cerbuino Bee.

Thanks for instant response!

Right, that was the reason. However, this did not help in any way - I still can not obtain IP address.
Before submitting my code I want to check my network configuration which seems to be incorrect.
I attached 2 screenshots of my network configuration + part of ipconfig:

Ethernet adapter Ethernet:

   Connection-specific DNS Suffix  . :
   Description . . . . . . . . . . . : Realtek PCI GBE Family Controller
   Physical Address. . . . . . . . . : 10-FE-ED-03-66-2A
   DHCP Enabled. . . . . . . . . . . : Yes
   Autoconfiguration Enabled . . . . : Yes
   Link-local IPv6 Address . . . . . : fe80::10e0:35bc:a81a:dbff%6(Preferred)
   Autoconfiguration IPv4 Address. . : 169.254.219.255(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.0.0
   Default Gateway . . . . . . . . . :
   DHCPv6 IAID . . . . . . . . . . . : 101777133
   DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-1A-BD-0B-C4-10-FE-ED-03-66-2A

   DNS Servers . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                       fec0:0:0:ffff::2%1
                                       fec0:0:0:ffff::3%1
   NetBIOS over Tcpip. . . . . . . . : Enabled

What really confuses me is that Network Configuration from .NET Micro Framework Deployment Tool shows me IP address 192.168.1.202, but this is not related to any of existing network interfaces in my system.