TinyCLR OS 2.1 Preview 4 Network Static IP

Hi Guys, I was super excited to see the non-blocking ethernet addition to the preview and got some time today to attempt to use it. I updated everything (vsix, firmware, config and local nugets). Everything is flashing and running, and the network says it is coming up with the static IP I assign. However, I can’t connect to the board with my PC side application or RealTerm. I have the exact same board running a G400D with all the same settings (obviously different IP and MAC) and on the same switch and it connects fine. Wireshark output:
G400D

SC20260D

As you can see, the G400D is responding to the ARP and handing back the MAC of the G400D module. The SC20260D is not responding at all. The networking code is essentially identical to the sample (and is the code I have been using to test networking since we started the insider program and has worked with prior releases) with the exception of using the static IP shown in the screen grabs and assigning a MAC from a block my company purchased. I don’t have a router for my test setup so I can’t see if DHCP would fare better, though my guess is it does. Could something have been messed up in ARP for this update? Could you run a quick test of static IP with your network example?

Are we sure we have a valid and unique MAC address?

100% I bought a block from the IEEE registration authority. I am also running on a local network with only the PC and the board on it, so not much room for conflict. I have also tried a few different MACs in my block. I also went back and tried the MAC I used for the G400D while disconnecting that from the network and no joy.

Strange. We didn’t change anything that would effect static ip but we will check.

Have you tried dynamic ip?
Have you tried this before with previous releases?

… And please share a simple code example that we can use to test what your are doing?

Are you running native ethernet or ENC28J60?

I can use static IP with the ENC28J60 no problem

@LucaP I am using onboard ethernet.

@Gus_Issa I have not tried DHCP because I don’t have a router for my test setup. I am looking into getting one. I have tried this exact code with previous releases and it worked. As for a code sample, I ran in my full solution. I am rebuilding my network test on preview 4 and will send along the results. If it doesn’t work there, I’ll share the solution, though Dat should already have it as I have sent it to him previously.

Pulled the module off my board and put it back on the dev board. Rebuilt my network only test to ensure it wasnt something strange with the new vsix. Reran and still cannot get it to connect with a static IP using the same exact settings and network infrastructure as the G400D version. Here is the code:

Program.cs

using System;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Threading;

namespace NetworkTest2_1Prev4
{
    public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs e);
    public delegate void ConnectionLostEventHandler(object sender);

    class Program
    {

        //Network channel
        public NetworkInterface Host;
        //Threads
        Thread ServerThread;

        static void Main()
        {
            Program myApplication = new Program();
            myApplication.initialize();
        }

        void initialize()
        {
            CreateNetworkInterface();
        }
        private void CreateNetworkInterface()
        {
            Host = new NetworkInterface(64010);
            ServerThread = new Thread(() => Host.StartServer());
            ServerThread.Priority = ThreadPriority.Highest;
            ServerThread.Start();

            Host.DataReceived += Host_DataReceived;
        }

        private void Host_DataReceived(object sender, DataReceivedEventArgs e)
        {
            Debug.WriteLine(e.Data);
        }
    }
}

NetworkInterface.cs

using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.Network;
using GHIElectronics.TinyCLR.Pins;
using System;
using System.Collections;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace NetworkTest2_1Prev4
{
    public class NetworkInterface : IDisposable
    {
        string resend = "";
        static Socket serverSocket;
        static Socket clientSocket;
        static EndPoint ep;
        //EthernetENC28J60 ethernet;
        public bool IsConnected = false;
        public event DataReceivedEventHandler DataReceived;
        public event ConnectionLostEventHandler ConnectionLost;
        public NetworkStream ns;
        bool IsOpen = false;
        private ManualResetEvent waitForCommandEvent;
        private byte[] buffer;
        private string carryOver;
        private DateTime lastRead;
        private DateTime lastWrite;
        private Thread processingThread;
        private ManualResetEvent networkRefreshEvent;

        const int RESET = SC20260.GpioPin.PG3;
        const string EMAC_API_NAME = "GHIElectronics.TinyCLR.NativeApis.STM32H7.EthernetEmacController\\0";
        static bool phyReady = false;
        public static bool linkReady = false;

        public Queue CommandQueue { get; set; }

        public static byte dash = 45;
        public static byte LF = 10;
        public static byte CR = 13;
        public static byte space = 32;
        private int port;
        private readonly object locker;
        private string resendInfo = "";

        private int resendCount = 0;

        public NetworkInterface(int port)
        {
            locker = new object();
            CommandQueue = new Queue();
            this.port = port;
            waitForCommandEvent = new ManualResetEvent(false);
            networkRefreshEvent = new ManualResetEvent(true);
            buffer = new byte[100];


            // Do external Phy reset
            var gpioController = GpioController.GetDefault();
            var resetPin = gpioController.OpenPin(RESET);
            resetPin.SetDriveMode(GpioPinDriveMode.Output);

            resetPin.Write(GpioPinValue.Low);
            Thread.Sleep(100);

            resetPin.Write(GpioPinValue.High);
            Thread.Sleep(100);


            var networkController = NetworkController.FromName(EMAC_API_NAME);

            EthernetNetworkInterfaceSettings networkInterfaceSetting = new EthernetNetworkInterfaceSettings();
            BuiltInNetworkCommunicationInterfaceSettings networkCommunicationInterfaceSettings = new BuiltInNetworkCommunicationInterfaceSettings();

            networkInterfaceSetting.Address = new IPAddress(new byte[] { 172, 16, 0, 106 });
            networkInterfaceSetting.SubnetMask = new IPAddress(new byte[] { 255, 255, 255, 0 });
            networkInterfaceSetting.GatewayAddress = new IPAddress(new byte[] { 172, 16, 0, 255 });
            networkInterfaceSetting.DnsAddresses = new IPAddress[] { new IPAddress(new byte[] { 8, 8, 8, 8 }), new IPAddress(new byte[] { 8, 8, 4, 4 }) };
            networkInterfaceSetting.TlsEntropy = new byte[] { 0, 1, 2, 3 };

            networkInterfaceSetting.MacAddress = new byte[] { 0x70, 0xB3, 0xD5, 0xFA, 0xC0, 0x0F };
            //networkInterfaceSetting.MacAddress = new byte[] { 0x00, 0x4, 0x00, 0x00, 0x00, 0x00 };
            networkInterfaceSetting.DhcpEnable = false;
            networkInterfaceSetting.DynamicDnsEnable = false;

            //networkInterfaceSetting.Address = new IPAddress(config.network.IPAddress);
            //networkInterfaceSetting.SubnetMask = new IPAddress(config.network.SubnetMask);
            //networkInterfaceSetting.GatewayAddress = new IPAddress(config.network.DefaultGateway);
            //networkInterfaceSetting.DnsAddresses = new IPAddress[] { new IPAddress(config.network.DNSAddress1), new IPAddress(config.network.DNSAddress2) };
            //networkInterfaceSetting.TlsEntropy = config.network.TlsEntropy;

            //networkInterfaceSetting.MacAddress = config.network.MACAddress;
            //networkInterfaceSetting.DhcpEnable = config.network.IsDHCP;
            //networkInterfaceSetting.DynamicDnsEnable = config.network.IsDynamicDNS;



            networkController.SetInterfaceSettings(networkInterfaceSetting);
            networkController.SetCommunicationInterfaceSettings(networkCommunicationInterfaceSettings);
            networkController.SetAsDefaultController();

            networkController.NetworkAddressChanged += NetworkController_NetworkAddressChanged;
            networkController.NetworkLinkConnectedChanged += NetworkController_NetworkLinkConnectedChanged;

            networkController.Enable();

            while (linkReady == false) ;

            System.Diagnostics.Debug.WriteLine("Network is ready to use");
        }

        private static void NetworkController_NetworkLinkConnectedChanged(NetworkController sender, NetworkLinkConnectedChangedEventArgs e)
        {
            phyReady = e.Connected;
            System.Diagnostics.Debug.WriteLine("Phy status " + phyReady);
        }

        private static void NetworkController_NetworkAddressChanged(NetworkController sender, NetworkAddressChangedEventArgs e)
        {
            var ipProperties = sender.GetIPProperties();

            var address = ipProperties.Address.GetAddressBytes();
            var subnet = ipProperties.SubnetMask.GetAddressBytes();
            var gw = ipProperties.GatewayAddress.GetAddressBytes();

            var interfaceProperties = sender.GetInterfaceProperties();

            System.Diagnostics.Debug.WriteLine("Mac: " + interfaceProperties.MacAddress[0].ToString("x") + ":" + interfaceProperties.MacAddress[1].ToString("x") + ":" + interfaceProperties.MacAddress[2].ToString("x") + ":" + interfaceProperties.MacAddress[3].ToString("x") + ":" + interfaceProperties.MacAddress[4].ToString("x") + ":" + interfaceProperties.MacAddress[5].ToString("x"));

            System.Diagnostics.Debug.WriteLine("ip address :" + address[0] + "." + address[1] + "." + address[2] + "." + address[3]);
            System.Diagnostics.Debug.WriteLine("subnetmask :" + subnet[0] + "." + subnet[1] + "." + subnet[2] + "." + subnet[3]);
            System.Diagnostics.Debug.WriteLine("gate way :" + gw[0] + "." + gw[1] + "." + gw[2] + "." + gw[3]);

            var dnsCount = ipProperties.DnsAddresses.Length;

            for (int i = 0; i < dnsCount; i++)
            {
                var dns = ipProperties.DnsAddresses[i].GetAddressBytes();

                System.Diagnostics.Debug.WriteLine("dns[" + i + "] :" + dns[0] + "." + dns[1] + "." + dns[2] + "." + dns[3]);
            }

            linkReady = address[0] != 0 ? true : false;
        }

        public void StartServer()
        {
            try
            {
                //IsOpen = false;
                //create server socket
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                serverSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
                serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                //serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
                serverSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.Debug, true);

                //bind socket to all available endpoints (should only be the ethernet on the board)

                ep = new IPEndPoint(IPAddress.Any, port);
                serverSocket.Bind(ep);

                //start listening for connections
                Debug.WriteLine("Listening for connections on port " + port.ToString());

                serverSocket.Listen(5);

                while (true)
                {
                    clientSocket = serverSocket.Accept();

                    clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    //clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
                    clientSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);

                    IsOpen = true;
                    IsConnected = true;
                    if (resend != "")
                    {
                        Send(resend);
                    }

                    ns = new NetworkStream(clientSocket);
                    ns.ReadTimeout = Timeout.Infinite;
                    //set writeTimeout to 2 sec
                    ns.WriteTimeout = 2000;
                    Debug.WriteLine(DateTime.Now.ToString() + "          Accepted New Connection");

                    if (processingThread == null)
                    {

                        StartProcessingResponses();
                    }
                    else if (!processingThread.IsAlive)
                    {
                        StartProcessingResponses();
                    }
                }


            }
            catch (Exception ex)
            {
                Debug.WriteLine("Unable to start server!");
                Debug.WriteLine(ex.Message);
                serverSocket.Close();
                serverSocket = null;
            }
        }

        private void StartProcessingResponses()
        {
            processingThread = new Thread(() =>
            {
                //ensure the network is up
                while (IsOpen)
                {
                    ArrayList commands = null;
                    try
                    {
                        commands = ReadCommand();
                    }
                    catch (Exception e)
                    {
                        IsOpen = false;
                        Debug.WriteLine(e.Message);
                    }



                    //check to make sure there is an ID, otherwise ignore
                    if (commands == null)
                    {
                        continue;
                    }

                    // put the repsonse into the dictionary for the corresponding command
                    //Debug.Print("Got Command: " + command);

                    foreach (string command in commands)
                    {
                        DataReceived(this, new DataReceivedEventArgs(command));
                        //CommandQueue.Enqueue(command);
                        //Debug.Print(command);
                    }

                }
            });

            processingThread.Start();
        }

        private ArrayList ReadCommand()
        {
            //Debugger.Launch();
            //clear the buffer byte[]
            Array.Clear(buffer, 0, buffer.Length);
            ArrayList response = new ArrayList();

            int readAll = 0;
            int read;

            networkRefreshEvent.WaitOne();
            //read data from the network stream byte by byte
            try
            {
                while (ns.DataAvailable)
                {
                    lastRead = DateTime.Now;

                    read = ns.Read(buffer, readAll, buffer.Length - readAll);


                    readAll += read;
                    //ReadBytes = readAll;
                    string rawMessage = new string(Encoding.UTF8.GetChars(buffer));
                    //string rawMessage = carryOver + new string(Encoding.UTF8.GetChars(buffer));
                    //Debug.Print("Raw Read = " + rawMessage);
                    response = processData(buffer);
                    return response;
                    //    indexOfEnd = rawMessage.IndexOf("\r\n", 0) + 2;

                    //    if (rawMessage.Length > indexOfEnd)
                    //    {
                    //        var stringArray = rawMessage.Split('\n');
                    //        for (int i = 0; i < stringArray.Length - 1; i++)
                    //        {
                    //            if(stringArray[i].IndexOf('-') != -1)
                    //            {
                    //                response.Add(stringArray[i] + '\n');
                    //                carryOver = "";
                    //            }
                    //            else
                    //            {
                    //                carryOver = stringArray[i];
                    //            }

                    //        }
                    //        break;
                    //    }
                    //    else
                    //    {
                    //        if(rawMessage.IndexOf("\r\n")>2)
                    //        {
                    //            if (rawMessage.IndexOf('-') != -1)
                    //            {
                    //                response.Add(new string(Encoding.UTF8.GetChars(buffer)) + '\n');
                    //                carryOver = "";
                    //                break;
                    //            }
                    //            else
                    //            {
                    //                carryOver = rawMessage;
                    //            }
                    //        }
                    //        else
                    //        {
                    //            carryOver = rawMessage;
                    //        }



                    //    }
                }
                Thread.Sleep(20);
                return response;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Error: " + ex.Message);
                return response;
            }
        }

        private ArrayList processData(byte[] buffer)
        {
            ArrayList response = new ArrayList();
            string commandString = carryOver + new string(Encoding.UTF8.GetChars(buffer));

            int startChar = commandString.IndexOf('-');
            int endChar = commandString.IndexOf("\r\n");

            if (startChar != -1 && endChar != -1)
            {
                //make sure framing chars are in the right order
                if (startChar < endChar)
                {
                    //string split to see if multiple commands exist
                    var commandStrings = commandString.Split('\n');

                    foreach (string com in commandStrings)
                    {
                        //if com has remaining framing chars, add the response
                        if (com.IndexOf('-') != -1 && com.IndexOf('\r') != -1 && com.IndexOf('-') < com.IndexOf('\r'))
                        {
                            response.Add(com + "\n");
                            //Debug.Print("adding " + com);
                            carryOver = "";
                        }
                        // if the com doesn't have the framing chars or they are in the wrong order, add the data to carryover
                        else
                        {
                            carryOver = com;
                        }
                    }

                }
                else
                {
                    carryOver = commandString;
                }
            }
            else if (startChar != -1 && endChar == -1)
            {
                carryOver = commandString;
            }
            else if (startChar == -1 && endChar != -1)
            {
                Debug.WriteLine("Error occured in parsing");
            }
            else
            {
                carryOver = commandString;
            }

            return response;
        }

        //wrapper for socket send
        public void Send(string sendInfo)
        {

            lock (locker)
            {
                networkRefreshEvent.WaitOne();
                byte[] message = UTF8Encoding.UTF8.GetBytes(sendInfo);
                byte[] resendMessage = UTF8Encoding.UTF8.GetBytes(resendInfo);

                try
                {
                    lastWrite = DateTime.Now;
                    //if there is a resend message, send it first
                    if (string.Compare(resendInfo, "") != 0)
                    {
                        ns.Write(resendMessage, 0, resendMessage.Length);
                        //clear resend info here so we know if this send went through
                        resendInfo = "";
                        //clear the count
                        resendCount = 0;
                        Debug.WriteLine("Finished resend");
                    }

                    ns.Write(message, 0, message.Length);
                    //ns.Flush();
                    //Thread.Sleep(20);


                    Debug.WriteLine("Round trip time: " + (lastWrite - lastRead));

                    if ((lastWrite - lastRead) > new TimeSpan(0, 0, 1))
                    {
                        Debug.WriteLine("******************************************************************************************************");
                    }
                }
                catch
                {
                    //if we timeout on a send, remember what was trying to be sent and cache it.
                    //Increment the counter.  If we get to 5, do something drastic.
                    //set resend;
                    resendInfo = sendInfo;
                    //increment counter
                    resendCount += 1;
                    //set resend info so when we reconnect, we send again
                    Debug.WriteLine("setting resend to: " + resendInfo);
                }
            }
        }


        private void OnDataReceived(DataReceivedEventArgs e)
        {
            if (DataReceived != null)
                DataReceived(this, e);
        }

        public void Dispose()
        {
            clientSocket.Close();
            serverSocket.Close();
        }
    }
}

DataReceivedEventArgs.cs

using System.Threading;

namespace NetworkTest2_1Prev4
{
    public class DataReceivedEventArgs : EventArgs
    {
        //public EndPoint LocalEndPoint { get; private set; }
        //public EndPoint RemoteEndPoint { get; private set; }
        //public byte[] Data { get; private set; }
        public string Data { get; set; }
        public bool Close { get; set; }
        public byte[] ResponseData { get; set; }

        //public DataReceivedEventArgs(EndPoint localEndPoint, EndPoint remoteEndPoint, byte[] data)
        public DataReceivedEventArgs(string data)
        {
            //LocalEndPoint = localEndPoint;
            //RemoteEndPoint = remoteEndPoint;
            if (data != null)
            {
                //Data = new byte[data.Length];
                Data = data;
                //data.CopyTo(Data, 0);
            }
        }
    }
}

What do you mean doesn’t work?

Couldn’t ping, or doesn’t run as expected?

we just tried your MAC with Preview4 static IP, we can talk to azure just fine.

Yeah, I mean I can’t ping the board and cant connect with a terminal. Hmm, well, if you can connect and this code was working on the previous builds, then I have to suspect maybe something happened to my module. It’s really the only thing left at this point. I’ll get a new one and try again.

No, we didn’t try your code yet, we just tried simple our code with is similar to our document.

Yeah, my code is essentially identical to the example, and it worked with previous versions, so I can’t imagine there is a problem there. It has to be something hardware at this point.

Got a new module in from digikey today and I can connect with a static IP. Not sure what I did to make this current module angry, but I will forge onward and test the non-blocking network imminently.

2 Likes

Can you please do one more thing for us please?

Try to set slow clock to see if it work.

         if (Power.GetSystemClock() == SystemClock.High)
            {
                Power.SetSystemClock(SystemClock.Low, false);
                Power.Reset();
            }

You mean on the old module? I will try, but I suspect the module is flaky because I read back some sensors through a MUX’d ADC and the values it is reading back are incorrect. However, my UARTs and SD card and whatnot still work on it, so it looks like its a pretty localized issue. I’ll see if the clock corrects it.

yes, the old one. Thanks

No change after modifying the system clock rate. The defunct module is Rev. A and the new ones are Rev. C if that makes any difference.

Rev A from insider? You should not be using that!

Ha, well I’m not anymore!

It’s alive! Networking seems to be running in a non-blocking mode. I am still getting some random timeouts, so there is a bit of investigation to be done, but I haven’t been able to work with this module much because of the blocking network issue. I’ll dig in and see what else I can find. Thanks guys for taking care of this one.

1 Like