From f7f62809531282f41ab3dbde5096c3b35efef78b Mon Sep 17 00:00:00 2001 From: Adrian Soundy Date: Thu, 18 Jul 2024 00:33:02 +1200 Subject: [PATCH] Add first sample for OpenThread (#371) --- samples/OpenThread/Display.cs | 59 ++++++ samples/OpenThread/Led.cs | 90 +++++++++ samples/OpenThread/README.md | 76 ++++++++ samples/OpenThread/SocketUtils.cs | 105 +++++++++++ samples/OpenThread/UdpThreadClient/Program.cs | 175 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 33 ++++ .../UdpThreadClient/UdpThreadClient.nfproj | 92 +++++++++ .../UdpThreadClient/packages.config | 19 ++ samples/OpenThread/UdpThreadSamples.sln | 48 +++++ samples/OpenThread/UdpThreadServer/Program.cs | 153 +++++++++++++++ .../Properties/AssemblyInfo.cs | 33 ++++ .../UdpThreadServer/UdpThreadServer.nfproj | 92 +++++++++ .../UdpThreadServer/packages.config | 19 ++ samples/OpenThread/category.txt | 1 + 14 files changed, 995 insertions(+) create mode 100644 samples/OpenThread/Display.cs create mode 100644 samples/OpenThread/Led.cs create mode 100644 samples/OpenThread/README.md create mode 100644 samples/OpenThread/SocketUtils.cs create mode 100644 samples/OpenThread/UdpThreadClient/Program.cs create mode 100644 samples/OpenThread/UdpThreadClient/Properties/AssemblyInfo.cs create mode 100644 samples/OpenThread/UdpThreadClient/UdpThreadClient.nfproj create mode 100644 samples/OpenThread/UdpThreadClient/packages.config create mode 100644 samples/OpenThread/UdpThreadSamples.sln create mode 100644 samples/OpenThread/UdpThreadServer/Program.cs create mode 100644 samples/OpenThread/UdpThreadServer/Properties/AssemblyInfo.cs create mode 100644 samples/OpenThread/UdpThreadServer/UdpThreadServer.nfproj create mode 100644 samples/OpenThread/UdpThreadServer/packages.config create mode 100644 samples/OpenThread/category.txt diff --git a/samples/OpenThread/Display.cs b/samples/OpenThread/Display.cs new file mode 100644 index 00000000..dbafe1fa --- /dev/null +++ b/samples/OpenThread/Display.cs @@ -0,0 +1,59 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Networking.Thread; +using System; +using System.Text; + +namespace Samples +{ + internal class Display + { + public static string LH + { + get { return DateTime.UtcNow.ToString("HH:mm:ss") + "-"; } + } + + public static void Log(string str) + { + Console.WriteLine($"{LH} {str}"); + } + + public static void Log(string[] strings) + { + foreach (string line in strings) + { + Log(line); + } + } + + public static void Role(ThreadDeviceRole role) + { + switch (role) + { + case ThreadDeviceRole.Child: Log("Role = Child"); break; + case ThreadDeviceRole.Router: Log("Role = Router"); break; + case ThreadDeviceRole.Leader: Log("Role = Leader"); break; + case ThreadDeviceRole.Detached: Log("Role = Detached"); break; + case ThreadDeviceRole.Disabled: Log("Role = Disabled"); break; + default: + Log($"Role is {role}"); + break; + } + } + + public static void LogMemoryStats(string info) + { + uint manMem = nanoFramework.Runtime.Native.GC.Run(true); + + uint total; + uint free; + uint largest; + + nanoFramework.Hardware.Esp32.NativeMemory.GetMemoryInfo(nanoFramework.Hardware.Esp32.NativeMemory.MemoryType.All, out total, out free, out largest); + Console.WriteLine($"{LH} Memory All ({info}) Managed:{manMem} Native total:{total}/Free:{free}/Largest:{largest}"); + } + } +} diff --git a/samples/OpenThread/Led.cs b/samples/OpenThread/Led.cs new file mode 100644 index 00000000..5e3b514c --- /dev/null +++ b/samples/OpenThread/Led.cs @@ -0,0 +1,90 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Drawing; +using System.Threading; +using nanoFramework.Runtime.Native; +using CCSWE.nanoFramework.NeoPixel; +using CCSWE.nanoFramework.NeoPixel.Drivers; +using nanoFramework.Networking.Thread; + +namespace Samples +{ + public class Led + { + private NeoPixelStrip _led; + private ThreadDeviceRole _role; + + /// + /// Open _led for inbuilt Neopixel + /// + public Led() + { + if (SystemInfo.TargetName.Contains("ESP32_H2") || SystemInfo.TargetName.Contains("ESP32_C6")) + { + var driver = new Ws2812B(CCSWE.nanoFramework.NeoPixel.ColorOrder.GRB); + _led = new NeoPixelStrip(8, 1, driver); + } + else + { + _led = null; + } + } + + public void SetRxTX() + { + Set(_role, 0.2f); + Thread.Sleep(50); + Set(_role, 0.1f); + } + + public void Set(ThreadDeviceRole role) + { + Set(role, 0.1f); + } + + private void Set(ThreadDeviceRole role, float brightness) + { + Color col; + + if (_led == null) + { + return; + } + + // Save it for RXTX + _role = role; + + switch (role) + { + case ThreadDeviceRole.Detached: + col = Color.White; + break; + + case ThreadDeviceRole.Child: + col = Color.Green; + break; + + case ThreadDeviceRole.Router: + col = Color.Blue; + break; + + case ThreadDeviceRole.Leader: + col = Color.Red; + break; + + case ThreadDeviceRole.Disabled: + default: + col = Color.Black; + brightness = 0; + break; + } + + _led.SetLed(0, col, brightness); + _led.Update(); + } + } +} diff --git a/samples/OpenThread/README.md b/samples/OpenThread/README.md new file mode 100644 index 00000000..733464a6 --- /dev/null +++ b/samples/OpenThread/README.md @@ -0,0 +1,76 @@ +# 🌶️🌶️ - OpenTHread Networking sample pack + +Shows how to use OpenThread Networking API. + +## Samples + +- [🌶️🌶️🌶️ - UPD OpenThread Client using sockets](UdpThreadClient/) +- [🌶️🌶️🌶️ - UPD OpenThread Server using sockets](UdpThreadServer/) + +Shows how to use various APIs related to OpenThread. + +## Hardware requirements + +These project are for the ESP32_C6 and ESP32_H2 Espressif devkit boards with a Ws2812B Neopixel on pin 8. +This can be easily disabled or Ws2812B added to pin 8 for other boards. + +## Sample description + +### Upd socket samples + +These 2 sample work together to create a client / server communications over OpenThread. +They use UPD sockets over the IPV6 networking of the OpenThread stack. + +The neopixel shows the current role of the node. + +- White -> Detached from network +- Green -> Child +- Blue -> Router +- Red -> Leader + + The led will flash when message is transmitted or received. + + The samples use the CCSWE.nanoFramework.Neopixel for driving the neopixels. + + There is currently a problem driving the RMT on the ESP32_H2 devices as the core frequency for RMT is + 32Mhz instead of 80Mhz. This will be fixed shortly. + +#### UdpThreadClient + +This sample will send a broadcast message every 5 seconds using the built-in mesh broadcast address "ff03::1" and port 12324. +Any UdpThreadServer running on the same mesh network will receive message and respond back to sender. +Any received messages are logged on console. + +#### UdpThreadServer + +Sample opens sockets and waits for any messages on port 1234. If any message is received to is echoed back to sending address. + +## Related topics + +### Reference + +- [nanoFramework.Networking.Thread](http://docs.nanoframework.net/api/nanoFramework.Networking.Thread) + +## Build the sample + +1. Start Microsoft Visual Studio 2022 or Visual Studio 2019 (Visual Studio 2017 should be OK too) and select `File > Open > Project/Solution`. +1. Starting in the folder where you unzipped the samples/cloned the repository, go to the subfolder for this specific sample. Double-click the Visual Studio Solution (.sln) file. +1. Press `Ctrl+Shift+B`, or select `Build > Build Solution`. + +## Run the sample + +The next steps depend on whether you just want to deploy the sample or you want to both deploy and run it. + +### Deploying the sample + +- Select `Build > Deploy Solution`. + +### Deploying and running the sample + +- To debug the sample and then run it, press F5 or select `Debug > Start Debugging`. + +> [!NOTE] +> +> **Important**: Before deploying or running the sample, please make sure your device is visible in the Device Explorer. +> +> **Tip**: To display the Device Explorer, go to Visual Studio menus: `View > Other Windows > Device Explorer`. diff --git a/samples/OpenThread/SocketUtils.cs b/samples/OpenThread/SocketUtils.cs new file mode 100644 index 00000000..1b4b6139 --- /dev/null +++ b/samples/OpenThread/SocketUtils.cs @@ -0,0 +1,105 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Net.Sockets; +using System.Net; +using System.Text; + +namespace Samples +{ + internal class NetUtils + { + private static Socket socket; + + /// + /// Open a new UDP socket + /// + /// + /// + public static void OpenUdpSocket(String remoteAdr, int port, IPAddress endpoint) + { + socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); + + // Interface / port to receive on + IPEndPoint ep = new IPEndPoint(endpoint, port); + socket.Bind(ep); + + if (remoteAdr.Length > 0) + { + // Set remote address + var address = IPAddress.Parse(remoteAdr); + IPEndPoint rep = new IPEndPoint(address, port); + socket.Connect(rep); + } + } + + /// + /// Close open socket + /// + public static void CloseUdpSocket() + { + socket.Close(); + socket = null; + } + + /// + /// Send message to specific target / port + /// + /// Port number to send to + /// Target IP address + /// MEssage to send + public static void SendMessageSocketTo(int port, string targetAdr, string message) + { + var data = Encoding.UTF8.GetBytes(message); + + var address = IPAddress.Parse(targetAdr); + IPEndPoint ep = new IPEndPoint(address, port); + + socket.SendTo(data, ep); + } + + /// + /// Send message to connected target, target specified in Open + /// + /// + public static void SendMessage(string message) + { + var data = Encoding.UTF8.GetBytes(message); + socket.Send(data); + } + + /// + /// Method for receiving and displaying messages from UDP socket. + /// If respond param true will respond with generic message (like a server) + /// + /// + public static void ReceiveUdpMessages(bool respond = false) + { + Display.Log($"Receive thread for UDP messages started"); + + while (true) + { + byte[] data = new byte[256]; + EndPoint remoteEp = new IPEndPoint(0, 0); + + int length = socket.ReceiveFrom(data, ref remoteEp); + + var message = Encoding.UTF8.GetString(data, 0, length); + + Display.Log($"UDP message(sock) >{message}< received from {remoteEp}"); + + Program._led.SetRxTX(); + + if (respond) + { + IPEndPoint rp = remoteEp as IPEndPoint; + SendMessageSocketTo(rp.Port, rp.Address.ToString(), $"Server response {DateTime.UtcNow}"); + Display.Log($"UDP message(sock) >{message}< respond to {rp.Address} {rp.Port}"); + } + } + } + } +} diff --git a/samples/OpenThread/UdpThreadClient/Program.cs b/samples/OpenThread/UdpThreadClient/Program.cs new file mode 100644 index 00000000..cf8d7341 --- /dev/null +++ b/samples/OpenThread/UdpThreadClient/Program.cs @@ -0,0 +1,175 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Threading; +using System.Net; +using nanoFramework.Networking.Thread; + +namespace Samples +{ + public class Program + { + private const int UDP_PORT = 1234; + + private static OpenThread _ot; + private static AutoResetEvent _waitNetAttached = new AutoResetEvent(false); + + public static Led _led = new Led(); + + public static void Main() + { + Console.WriteLine(); + Display.Log("Sample UDP thread UDP client"); + + try + { + // Target is mesh broadcast address + // this will be received by all mesh devices except sleepy devices. + // If you use a specific mesh local address here it will only received by 1 target + string remoteAdress = "ff03::1"; + + _led.Set(ThreadDeviceRole.Disabled); + + // Initialize OpenThread stack + InitThread(); + + Display.Log("Wait for OpenThread to be attached..."); + _waitNetAttached.WaitOne(); + + IPAddress meshLocal = _ot.MeshLocalAddress; + Display.Log($"Own mesh local IPV6 address {meshLocal}"); + + Display.Log("Display current active dataset"); + CommandAndResult("dataset active"); + + Display.Log("Display interface IP addresses"); + CommandAndResult("ipaddr"); + + Display.Log("Open UDP socket for communication"); + NetUtils.OpenUdpSocket("", UDP_PORT, _ot.MeshLocalAddress); + + Display.Log("Start a receive thread for UDP message responses"); + Thread ReceiveUdpthread = new Thread(() => NetUtils.ReceiveUdpMessages()); + ReceiveUdpthread.Start(); + + while (true) + { + Display.Log($"Send (broadcast) messages on port:{UDP_PORT}"); + NetUtils.SendMessageSocketTo(UDP_PORT, remoteAdress, $"Test message via socket @ {DateTime.UtcNow}"); + _led.SetRxTX(); + + Thread.Sleep(5000); + } + } + catch (Exception e) + { + Display.Log($"Exception : {e.Message}"); + Display.Log($"Stack : {e.StackTrace}"); + } + + Thread.Sleep(Timeout.Infinite); + } + + /// + /// Initialize the OpenThread + /// + static void InitThread() + { + OpenThreadDataset data = new OpenThreadDataset() + { + // Minimum data required to set up/connect to Thread network + NetworkName = "nanoFramework", + // 000102030405060708090A0B0C0D0E0F + NetworkKey = new byte[16] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + PanId = 0x1234, + Channel = 15 + }; + + Display.Log("---- Thread Dataset ------"); + Display.Log($"Network name {data.NetworkName}"); + Display.Log($"NetworkKey {BitConverter.ToString(data.NetworkKey)}"); + Display.Log($"Channel {data.Channel}"); + Display.Log("---- Thread Dataset end ------"); + + // Use local radio, ESP32_C6 or ESP32_H2 + _ot = OpenThread.CreateThreadWithNativeRadio(ThreadDeviceType.Router); + + // Set up event handlers + _ot.OnStatusChanged += Ot_OnStatusChanged; + _ot.OnRoleChanged += Ot_OnRoleChanged; + _ot.OnConsoleOutputAvailable += Ot_OnConsoleOutputAvailable; + + _ot.Dataset = data; + + Display.Log($"Starting OpenThread stack"); + _ot.Start(); + + Display.Log($"Current Role"); + Display.Role(_ot.Role); + } + + static void CommandAndResult(string cmd) + { + Console.WriteLine($"{Display.LH} command>{cmd}"); + string[] results = _ot.CommandLineInputAndWaitResponse(cmd); + Display.Log(results); + } + + #region OpenThread events handlers + + private static void Ot_OnConsoleOutputAvailable(OpenThread sender, OpenThreadConsoleOutputAvailableArgs args) + { + Display.Log(args.consoleLines); + } + + private static void Ot_OnRoleChanged(OpenThread sender, OpenThreadRoleChangeEventArgs args) + { + Display.Role(args.currentRole); + _led.Set(args.currentRole); + } + + private static void Ot_OnStatusChanged(OpenThread sender, OpenThreadStateChangeEventArgs args) + { + switch ((ThreadDeviceState)args.currentState) + { + case ThreadDeviceState.Detached: + Display.Log("Status - Detached"); + break; + + case ThreadDeviceState.Attached: + Display.Log("Status - Attached"); + _waitNetAttached.Set(); + break; + + case ThreadDeviceState.GotIpv6: + Display.Log("Status - Got IPV6 address"); + break; + + case ThreadDeviceState.Start: + Display.Log("Status - Started"); + break; + + case ThreadDeviceState.Stop: + Display.Log("Status - Stopped"); + break; + + case ThreadDeviceState.InterfaceUp: + Display.Log("Status - Interface UP"); + break; + + case ThreadDeviceState.InterfaceDown: + Display.Log("Status - Interface DOWN"); + break; + + default: + Display.Log($"Status - changed to {args.currentState}"); + break; + } + } + + #endregion + } +} diff --git a/samples/OpenThread/UdpThreadClient/Properties/AssemblyInfo.cs b/samples/OpenThread/UdpThreadClient/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..f268976d --- /dev/null +++ b/samples/OpenThread/UdpThreadClient/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UdpThreadClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UdpThreadClient")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/OpenThread/UdpThreadClient/UdpThreadClient.nfproj b/samples/OpenThread/UdpThreadClient/UdpThreadClient.nfproj new file mode 100644 index 00000000..70cf5bf2 --- /dev/null +++ b/samples/OpenThread/UdpThreadClient/UdpThreadClient.nfproj @@ -0,0 +1,92 @@ + + + + $(MSBuildExtensionsPath)\nanoFramework\v1.0\ + + + + Debug + AnyCPU + {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + c679edd8-8104-4188-b1e3-a7b84c5b7e7b + Exe + Properties + 512 + UdpThreadSamples + UdpThreadSamples + v1.0 + + + + + Display.cs + + + Led.cs + + + SocketUtils.cs + + + + + + + ..\packages\CCSWE.nanoFramework.Math.0.1.32\lib\CCSWE.nanoFramework.Math.dll + + + ..\packages\CCSWE.nanoFramework.NeoPixel.1.0.41\lib\CCSWE.nanoFramework.NeoPixel.dll + + + ..\packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll + + + ..\packages\nanoFramework.Graphics.Core.1.2.15\lib\nanoFramework.Graphics.Core.dll + + + ..\packages\nanoFramework.Hardware.Esp32.1.6.15\lib\nanoFramework.Hardware.Esp32.dll + + + ..\packages\nanoFramework.Hardware.Esp32.Rmt.2.0.10\lib\nanoFramework.Hardware.Esp32.Rmt.dll + + + ..\packages\nanoFramework.Networking.Thread.1.0.10\lib\nanoFramework.Networking.Thread.dll + + + ..\packages\nanoFramework.Runtime.Events.1.11.18\lib\nanoFramework.Runtime.Events.dll + + + ..\packages\nanoFramework.Runtime.Native.1.6.12\lib\nanoFramework.Runtime.Native.dll + + + ..\packages\nanoFramework.System.Collections.1.5.31\lib\nanoFramework.System.Collections.dll + + + ..\packages\nanoFramework.System.Text.1.2.54\lib\nanoFramework.System.Text.dll + + + ..\packages\nanoFramework.System.Device.Gpio.1.1.41\lib\System.Device.Gpio.dll + + + ..\packages\nanoFramework.System.IO.Streams.1.1.59\lib\System.IO.Streams.dll + + + ..\packages\nanoFramework.System.Math.1.5.43\lib\System.Math.dll + + + ..\packages\nanoFramework.System.Net.1.10.79\lib\System.Net.dll + + + ..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/OpenThread/UdpThreadClient/packages.config b/samples/OpenThread/UdpThreadClient/packages.config new file mode 100644 index 00000000..d0fed97a --- /dev/null +++ b/samples/OpenThread/UdpThreadClient/packages.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/OpenThread/UdpThreadSamples.sln b/samples/OpenThread/UdpThreadSamples.sln new file mode 100644 index 00000000..0d422017 --- /dev/null +++ b/samples/OpenThread/UdpThreadSamples.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34221.43 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "UdpThreadClient", "UdpThreadClient\UdpThreadClient.nfproj", "{C679EDD8-8104-4188-B1E3-A7B84C5B7E7B}" +EndProject +Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "UdpThreadServer", "UdpThreadServer\UdpThreadServer.nfproj", "{EA37527A-FC52-4269-A9B1-ACC366ECD0C5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{2B0FD5A8-FC86-4111-8B70-2B0D7090F468}" + ProjectSection(SolutionItems) = preProject + Display.cs = Display.cs + Led.cs = Led.cs + SocketUtils.cs = SocketUtils.cs + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{581E3239-0AE5-4403-B31C-C91265E4AA5E}" + ProjectSection(SolutionItems) = preProject + category.txt = category.txt + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C679EDD8-8104-4188-B1E3-A7B84C5B7E7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C679EDD8-8104-4188-B1E3-A7B84C5B7E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C679EDD8-8104-4188-B1E3-A7B84C5B7E7B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {C679EDD8-8104-4188-B1E3-A7B84C5B7E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C679EDD8-8104-4188-B1E3-A7B84C5B7E7B}.Release|Any CPU.Build.0 = Release|Any CPU + {C679EDD8-8104-4188-B1E3-A7B84C5B7E7B}.Release|Any CPU.Deploy.0 = Release|Any CPU + {EA37527A-FC52-4269-A9B1-ACC366ECD0C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA37527A-FC52-4269-A9B1-ACC366ECD0C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA37527A-FC52-4269-A9B1-ACC366ECD0C5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {EA37527A-FC52-4269-A9B1-ACC366ECD0C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA37527A-FC52-4269-A9B1-ACC366ECD0C5}.Release|Any CPU.Build.0 = Release|Any CPU + {EA37527A-FC52-4269-A9B1-ACC366ECD0C5}.Release|Any CPU.Deploy.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5BE944EC-96F1-4350-88E2-C133969F1FC8} + EndGlobalSection +EndGlobal diff --git a/samples/OpenThread/UdpThreadServer/Program.cs b/samples/OpenThread/UdpThreadServer/Program.cs new file mode 100644 index 00000000..2a58dc7d --- /dev/null +++ b/samples/OpenThread/UdpThreadServer/Program.cs @@ -0,0 +1,153 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Net; +using System.Threading; +using nanoFramework.Networking.Thread; + +namespace Samples +{ + public class Program + { + private const int UDP_PORT = 1234; + + private static OpenThread _ot; + private static AutoResetEvent _waitNetAttached = new AutoResetEvent(false); + + public static Led _led = new Led(); + + public static void Main() + { + Console.WriteLine(); + Display.Log("Sample UDP thread UDP server"); + + Display.LogMemoryStats("Start up"); + + _led.Set(ThreadDeviceRole.Disabled); + + // Init OpenThread stack + InitThread(); + + Display.Log("Wait for OpenThread to be attached..."); + _waitNetAttached.WaitOne(); + + Display.Log("== Demonstrate some CLI commands"); + Display.Log("- Display current active dataset"); + CommandAndResult("dataset active"); + + Display.Log("Display interface IP addresses"); + CommandAndResult("ipaddr"); + + IPAddress adr = _ot.MeshLocalAddress; + Display.Log($"Local Mesh address {adr}"); + + Display.Log("Open UDP socket for communication"); + NetUtils.OpenUdpSocket("", UDP_PORT, IPAddress.IPv6Any); + + Display.Log("Start a receive thread to respond to UDP messages"); + Thread ReceiveUdpThread = new Thread(() => NetUtils.ReceiveUdpMessages(true)); + ReceiveUdpThread.Start(); + + Thread.Sleep(Timeout.Infinite); + } + + static void CommandAndResult(string cmd) + { + Console.WriteLine($"{Display.LH} command>{cmd}"); + string[] results = _ot.CommandLineInputAndWaitResponse(cmd); + Display.Log(results); + } + + #region OpenThread + + /// + /// Initialize the OpenThread + /// + static void InitThread() + { + OpenThreadDataset data = new OpenThreadDataset() + { + // Minimum data required to set up/connect to Thread network + NetworkName = "nanoFramework", + // 000102030405060708090A0B0C0D0E0F + NetworkKey = new byte[16] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + PanId = 0x1234, + Channel = 15 + }; + + Display.Log("---- Thread Dataset ------"); + Display.Log($"Network name {data.NetworkName}"); + Display.Log($"NetworkKey {BitConverter.ToString(data.NetworkKey)}"); + Display.Log($"Channel {data.Channel}"); + Display.Log("---- Thread Dataset end ------"); + + // Use local radio, ESP32_C6 or ESP32_H2 + _ot = OpenThread.CreateThreadWithNativeRadio(ThreadDeviceType.Router); + + // Set up event handlers + _ot.OnStatusChanged += Ot_OnStatusChanged; + _ot.OnRoleChanged += Ot_OnRoleChanged; + _ot.OnConsoleOutputAvailable += Ot_OnConsoleOutputAvailable; + + _ot.Dataset = data; + + Display.Log($"Starting OpenThread stack"); + _ot.Start(); + } + + private static void Ot_OnRoleChanged(OpenThread sender, OpenThreadRoleChangeEventArgs args) + { + Display.Role(args.currentRole); + _led.Set(args.currentRole); + } + + private static void Ot_OnStatusChanged(OpenThread sender, OpenThreadStateChangeEventArgs args) + { + switch ((ThreadDeviceState)args.currentState) + { + case ThreadDeviceState.Detached: + Display.Log("Status - Detached"); + _led.Set(ThreadDeviceRole.Disabled); + break; + + case ThreadDeviceState.Attached: + Display.Log("Status - Attached"); + _waitNetAttached.Set(); + break; + + case ThreadDeviceState.GotIpv6: + Display.Log("Status - Got IPV6 address"); + break; + + case ThreadDeviceState.Start: + Display.Log("Status - Started"); + break; + + case ThreadDeviceState.Stop: + Display.Log("Status - Stopped"); + break; + + case ThreadDeviceState.InterfaceUp: + Display.Log("Status - Interface UP"); + break; + + case ThreadDeviceState.InterfaceDown: + Display.Log("Status - Interface DOWN"); + break; + + default: + Display.Log($"Status - changed to {args.currentState}"); + break; + } + } + private static void Ot_OnConsoleOutputAvailable(OpenThread sender, OpenThreadConsoleOutputAvailableArgs args) + { + Display.Log(args.consoleLines); + } + + #endregion + } +} diff --git a/samples/OpenThread/UdpThreadServer/Properties/AssemblyInfo.cs b/samples/OpenThread/UdpThreadServer/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..c8a75779 --- /dev/null +++ b/samples/OpenThread/UdpThreadServer/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UdpThreadServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UdpThreadServer")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/OpenThread/UdpThreadServer/UdpThreadServer.nfproj b/samples/OpenThread/UdpThreadServer/UdpThreadServer.nfproj new file mode 100644 index 00000000..4a4e56f7 --- /dev/null +++ b/samples/OpenThread/UdpThreadServer/UdpThreadServer.nfproj @@ -0,0 +1,92 @@ + + + + $(MSBuildExtensionsPath)\nanoFramework\v1.0\ + + + + Debug + AnyCPU + {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ea37527a-fc52-4269-a9b1-acc366ecd0c5 + Exe + Properties + 512 + UdpThreadServer + UdpThreadServer + v1.0 + + + + + Display.cs + + + Led.cs + + + SocketUtils.cs + + + + + + + ..\packages\CCSWE.nanoFramework.Math.0.1.32\lib\CCSWE.nanoFramework.Math.dll + + + ..\packages\CCSWE.nanoFramework.NeoPixel.1.0.41\lib\CCSWE.nanoFramework.NeoPixel.dll + + + ..\packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll + + + ..\packages\nanoFramework.Graphics.Core.1.2.15\lib\nanoFramework.Graphics.Core.dll + + + ..\packages\nanoFramework.Hardware.Esp32.1.6.15\lib\nanoFramework.Hardware.Esp32.dll + + + ..\packages\nanoFramework.Hardware.Esp32.Rmt.2.0.10\lib\nanoFramework.Hardware.Esp32.Rmt.dll + + + ..\packages\nanoFramework.Networking.Thread.1.0.10\lib\nanoFramework.Networking.Thread.dll + + + ..\packages\nanoFramework.Runtime.Events.1.11.18\lib\nanoFramework.Runtime.Events.dll + + + ..\packages\nanoFramework.Runtime.Native.1.6.12\lib\nanoFramework.Runtime.Native.dll + + + ..\packages\nanoFramework.System.Collections.1.5.31\lib\nanoFramework.System.Collections.dll + + + ..\packages\nanoFramework.System.Text.1.2.54\lib\nanoFramework.System.Text.dll + + + ..\packages\nanoFramework.System.Device.Gpio.1.1.41\lib\System.Device.Gpio.dll + + + ..\packages\nanoFramework.System.IO.Streams.1.1.59\lib\System.IO.Streams.dll + + + ..\packages\nanoFramework.System.Math.1.5.43\lib\System.Math.dll + + + ..\packages\nanoFramework.System.Net.1.10.79\lib\System.Net.dll + + + ..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/OpenThread/UdpThreadServer/packages.config b/samples/OpenThread/UdpThreadServer/packages.config new file mode 100644 index 00000000..d0fed97a --- /dev/null +++ b/samples/OpenThread/UdpThreadServer/packages.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/OpenThread/category.txt b/samples/OpenThread/category.txt new file mode 100644 index 00000000..cdb10927 --- /dev/null +++ b/samples/OpenThread/category.txt @@ -0,0 +1 @@ +networking \ No newline at end of file