NetMF + Esp8266 = UPnP :)

here is a small demo getting NetMF talking to a PC though the UPnP protocol…

allJoyn is next :slight_smile:

https://mix.office.com/watch/1uu43r1n7rxi6

Cheers,
Jay

15 Likes

Great stuff @ Jay Jay. :clap:

Nice! I can’t wait to learn more and see AllJoyn in action!

@ Jay Jay - Is it too much to ask for some codeshare ?

@ PiWi - its al about that tease

of course i can share the code…

first to get this to work it does require an extension to the current ESP8266 firmware… once i get that clean up a bit i will share the firmware and th code :wink:

Cheers,
jay.

1 Like

@ Jay Jay - I’m more interrested in the upnp mo …

Mine Doesn’t get recognised … The examples from the sdk do work but i’d like to get down to the udp level of sending the gena messages for discovery etc. And device docs but some how it doesn’t fly …

are you using ESP8266 for the connection? if yes then that is the reason why you need the extension of the firmware I’ve built because the current AT doesn’t give you a way to join the Multicast Group…

if you are using other means to connect to the network… let me know i’ll post the what you need to get it done :slight_smile:

also you mentioned an example from the sdk… which one are you talking about?

No, no, just regular stuff like wifi thru an rs21 or even by eth28 on various mainboards, raptor, spider, cerb family but got nothing working so far. I think I’ve messed too much with some guid/settings in the xml stuff.

The examples from the netmf sdk with those web services for devices approach, stripped down a bit to get a very basic working model.

I just need a basic discovery communicating well, once that is working, that with the device/services/etc is not that difficult, I hope.

But, by all means, if you wanna post a complete piece of code, that’s totally up to you of course…

TIA

ahhh, in that case here you are:

to generate GUID compatible I’ve created the following which give the same GUID ID even on reboot and power down :wink:


        private static String _uuid;

        private static String _udn;

        /// <summary>
        /// Generates a unique UPnP Compatible GUID
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public static string GenerateUuid(string token)
        {
            var macAddr = new byte[0];
            var inets = NetworkInterface.GetAllNetworkInterfaces();

            foreach (var n in inets)
            {
                if (n.NetworkInterfaceType != NetworkInterfaceType.Ethernet) continue;     
                macAddr = n.PhysicalAddress;
                if (macAddr.Length > 0) break;           
            }
            var tmpToken = new byte[16];
            var r = new Random((token.GetHashCode() + macAddr.GetHashCode()));
            r.NextBytes(tmpToken);
            return new Guid(tmpToken).ToString();
        }

        /// <summary>
        /// Return the UUID
        /// required
        /// uuid:UUID
        /// </summary>
        public static String Uuid
        {
            get
            {
                return _uuid ?? (_uuid = UDN);
            }
            // set
            // {
            // Usn = value;

            // }
        }


Example code would be a property called UDN:



        /// <summary>
        /// required
        /// REQUIRED. Unique Device Name. Universally-unique identifier for the device, 
        /// whether root or embedded. MUST be the same over time for a specific device instance 
        /// (i.e., MUST survive reboots). MUST match the field value of the NT header field in 
        /// device discovery messages. MUST match the prefix of the USN header field in all 
        /// discovery messages. (Section 1, "Discovery" explains the NT and USN header fields.)
        /// MUST begin with "uuid:" followed by a UUID suffix specified by a UPnP vendor. 
        /// See section 1.1.4, "UUID format and RECOMMENDED generation algorithms" for 
        /// the MANDATORY UUID format.
        /// TODO: how to make sure this survives the reboot ans stays the same
        /// </summary>
        public static string UDN
        {
            get { return _udn ?? (_udn = Utilities.Tools.GenerateUuid("friendlyName, should be a unique string per board")); }
        }

now to reply to Notify calls in the UDP: usually in your UDP Data receive event you would look for “M-SEARCH” string and if found you send this below:

this code should be in your UDP receive event.


                     var data = HtppOk(1800, "upnp:rootdevice"); //stay alive for 1800sec

                     udpClient.SendTo(data, remoteEndpoint);


        /// <summary>
        /// Build the Http 200 OK string
        /// </summary>
        /// <returns></returns>
        internal static byte[] HtppOk(int lmaxAge, string st)
        {

            return Encoding.UTF8.GetBytes("HTTP/1.1 200 OK\r\n"
            + "CACHE-CONTROL: max-age = " + lmaxAge + "\r\n"
            + "DATE: " + DateTime.Now + " \r\n"
            + "EXT: \r\n"
            + "LOCATION: http://" + LocalIpAddress + ":" + LocalTCPPort + "/DeviceDescription.xml\r\n" //you should already have a TCP Server started listening on this PORT
            + "SERVER: " + Server + " UPnP/1.1 \r\n"
            + "ST: " + st + "\r\n"
            + "USN: uuid:" + Uuid + "::upnp:rootdevice\r\n";
        }


once the above is sent, you should of course have already started a TCP server listening on your IP address at the above specified LocalTCPPort , and look for DeviceDescription.xml string when data comes and serve that xml file or string, should look like this below:


var resp =                                                    
                                                    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
                                                    + "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
                                                    + "   <specVersion>" + "      <major>" + Major + "</major>"
                                                    + "      <minor>" + Minor + "</minor>" + "   </specVersion>"
                                                    + "   <URLBase>" + UrlBase + "</URLBase>" + "   <device>"
                                                    + "      <deviceType>urn:schemas-upnp-org:device:" + Devicetype
                                                    + ":1</deviceType>" + "      <presentationURL>" + PresentationUrl
                                                    + "</presentationURL>" + "      <friendlyName>" + FriendlyName
                                                    + "</friendlyName>" + "      <manufacturer>" + Manufacturer
                                                    + "</manufacturer>" + "      <manufacturerURL>" + ManufacturerUrl
                                                    + "</manufacturerURL>" + "      <modelDescription>"
                                                    + ModelDescription + "</modelDescription>" + "      <modelName>"
                                                    + ModelName + "</modelName>" + "      <modelNumber>" + ModelNumber
                                                    + "</modelNumber>" + "      <modelURL>" + ModelUrl + "</modelURL>"
                                                    + "      <UPC>" + ProductCode + "</UPC>" + "      <serialNumber>"
                                                    + SerialNumber + "</serialNumber>"
                                                    //+ "      <UDN>uuid:" + UpnpT.USN + "</UDN>"
                                                    + "      <UDN>uuid:" + UDN + "</UDN>" + "      <iconList>"
                                                    + "         <icon>" + "            <mimetype>image/png</mimetype>"
                                                    + "            <width>32</width>"
                                                    + "            <height>32</height>"
                                                    + "            <depth>32</depth>"
                                                    + "            <url>/icon.png</url>" + "         </icon>"
                                                    + "         <icon>" + "            <mimetype>image/jpg</mimetype>"
                                                    + "            <width>32</width>"
                                                    + "            <height>32</height>"
                                                    + "            <depth>32</depth>"
                                                    + "            <url>/icon.jpg</url>" + "         </icon>"
                                                    + "      </iconList>" + "      <serviceList>"
                                                    + "      </serviceList>" + "   </device>" + "</root>";
                                                //buf = Encoding.UTF8.GetBytes(Tools.ReplaceString(resp.Trim(), " ", ""));
                                             byte[]   buf = Encoding.UTF8.GetBytes(resp);


 const string contentType = "text/xml; charset=\"utf-8\"";

                                        var header =
                                            Encoding.UTF8.GetBytes(
                                                "HTTP/1.0 200 OK\r\nCache-Control: no-cache\r\nConnection: Close\r\nContent-Length: "
                                                + buf.Length + "\r\nContent-Type: " + contentType.Trim()
                                                + "\r\n\r\n");
                                        //var header = Encoding.UTF8.GetBytes("HTTP/1.0 200 OK\r\nCache-Control: no-cache\r\nConnection: Close\r\nRefresh: " + -1 + "\r\nContent-Length: " + buf.Length + "\r\nContent-Type: " + contentType.Trim() + "\r\n\r\n");

                                        clientSocket.Send(header, 0, header.Length, SocketFlags.None);
                                        clientSocket.Send(buf, 0, buf.Length, SocketFlags.None);
                                        clientSocket.Close();


to resume your code when it starts it whould work as follow:

Create and start TCP Server at some random port:
Create and start a UDP Socket and Join the Multicast Group and start listening to data.
once the above is successful send a UDP NOTIFY Call to let everyone in the local network know of your presence: notify call looks like this:



            var ipAddress = IPAddress.Parse(BroadcastIp);
            IPEndPoint ipe = new IPEndPoint(ipAddress, BroadcastPort); 
            var data = Notify(notifyType);
            m_udpReceiveClient.SendTo(data, ipe);
            Thread.Sleep(20);
            m_udpReceiveClient.SendTo(data, ipe);
            Thread.Sleep(20);
            m_udpReceiveClient.SendTo(data, ipe);



        /// <summary>
        /// Build the Notify String
        /// ssdp:byebye //when you leave the network
        /// ssdp:alive //when you first join the network
        /// ssdp:update //when renewing the age and letting everyone know you are still present and alive
        /// </summary>
        /// <returns></returns>
        internal static byte[] Notify(string notifyType)
        {
            return
                            Encoding.UTF8.GetBytes("NOTIFY * HTTP/1.1\r\n" +
                            "HOST: " + BroadcastIp + ":" + BroadcastPort + "\r\n" +
                            "CACHE-CONTROL: max-age = " + MaxAge + "\r\n" +
                            "LOCATION: http://" + LocalIpAddress + ":" + LocalPort + "/DeviceDescription.xml\r\n" +
                            "NT: upnp:rootdevice\r\n" +
                            "NTS: " + notifyType + "\r\n" +
                            "SERVER: " + Server + "\r\n" +
                            "USN: uuid:" + Uuid + "::upnp:rootdevice\r\n" +                     
                            "\r\n\r\n"); //important to keep these!
        }

by the time you sent the notification your TCP should be listening it and as soon as it gets a request for Devicedescription.xml page you should serve it.

If you have any questions about the above please let me know…

3 Likes

one more thing, anytime you send a UDP broadcast always make sure you send it at least three times, as with UDP you get no guarantee of delivery :slight_smile:

1 Like

Oh, that might even be the trick since I send it, ahum, once and I had it working very rarely but not anymore. So I saw it working but since it didn’'t anymore, I started to change bits and pieces to probably mess it up totally.

I’ll have a go again and report back. It will take some time though, I’m a little bit off track at the moment.

Thanks for the advice.

@ PiWi so did it work?

@ Jay - WIP !!!

@ Jay Jay - wow :open_mouth: alljoyn on ESP8266, look for it everywhere and even I tryed to port it, but i couldn´t. I think was not smart enought to do it. ??? :
i will waiting for updates ;D

Hi,
very interesting post, thanks!
How did you extend the AT firmware for the ESP8266 in order to allow multicast? Do you have some code to share for that?

cheers

@ farno -
I rebuild the esp firmware and added the required code.

1 Like

Did you ever get any further on this? Any progress with alljoyn? :slight_smile:

1 Like