Skip to content

Instantly share code, notes, and snippets.

@smdn
Created April 6, 2024 15:46
Show Gist options
  • Save smdn/667662d7f761a327ab670a7103bea23c to your computer and use it in GitHub Desktop.
Save smdn/667662d7f761a327ab670a7103bea23c to your computer and use it in GitHub Desktop.
Smdn.Devices.BP35XX 1.0.0 Release Notes
diff --git a/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-net6.0.apilist.cs b/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-net6.0.apilist.cs
new file mode 100644
index 0000000..b86796d
--- /dev/null
+++ b/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-net6.0.apilist.cs
@@ -0,0 +1,110 @@
+// Smdn.Devices.BP35XX.dll (Smdn.Devices.BP35XX-1.0.0)
+// Name: Smdn.Devices.BP35XX
+// AssemblyVersion: 1.0.0.0
+// InformationalVersion: 1.0.0+e4163ada5b034b45c5cc0dac179b412cf54198cd
+// TargetFramework: .NETCoreApp,Version=v6.0
+// Configuration: Release
+// Referenced assemblies:
+// Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+// Microsoft.Extensions.Logging.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+// Smdn.Fundamental.PrintableEncoding.Hexadecimal, Version=3.0.0.0, Culture=neutral
+// Smdn.Net.SkStackIP, Version=1.0.0.0, Culture=neutral
+// System.ComponentModel, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.IO.Ports, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+// System.Memory, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+// System.Net.NetworkInformation, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.Net.Primitives, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+#nullable enable annotations
+
+using System;
+using System.IO;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Threading;
+using System.Threading.Tasks;
+using Smdn.Devices.BP35XX;
+using Smdn.Net.SkStackIP;
+
+namespace Smdn.Devices.BP35XX {
+ public interface IBP35Configurations {
+ BP35UartBaudRate BaudRate { get; }
+ SkStackERXUDPDataFormat ERXUDPDataFormat { get; }
+ string? SerialPortName { get; }
+ bool TryLoadFlashMemory { get; }
+ }
+
+ public interface IBP35SerialPortStreamFactory {
+ Stream CreateSerialPortStream(IBP35Configurations configurations);
+ }
+
+ public enum BP35UartBaudRate : byte {
+ Baud115200 = 0,
+ Baud19200 = 4,
+ Baud2400 = 1,
+ Baud38400 = 5,
+ Baud4800 = 2,
+ Baud57600 = 6,
+ Baud9600 = 3,
+ }
+
+ public enum BP35UartCharacterInterval : byte {
+ Microseconds100 = 16,
+ Microseconds200 = 32,
+ Microseconds300 = 48,
+ Microseconds400 = 64,
+ Microseconds50 = 80,
+ None = 0,
+ }
+
+ public enum BP35UartFlowControl : byte {
+ Disabled = 0,
+ Enabled = 128,
+ }
+
+ public enum BP35UdpReceiveDataFormat : byte {
+ Binary = 0,
+ HexAscii = 1,
+ }
+
+ public class BP35A1 : BP35Base {
+ public static ValueTask<BP35A1> CreateAsync(BP35A1Configurations configurations, IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default) {}
+ public static ValueTask<BP35A1> CreateAsync(string? serialPortName, IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default) {}
+ }
+
+ public sealed class BP35A1Configurations : IBP35Configurations {
+ public BP35A1Configurations() {}
+
+ public BP35UartBaudRate BaudRate { get; set; }
+ public string? SerialPortName { get; set; }
+ SkStackERXUDPDataFormat IBP35Configurations.ERXUDPDataFormat { get; }
+ public bool TryLoadFlashMemory { get; set; }
+ }
+
+ public abstract class BP35Base : SkStackClient {
+ public IPAddress LinkLocalAddress { get; }
+ public PhysicalAddress MacAddress { get; }
+ public string RohmPassword { get; }
+ public string RohmUserId { get; }
+ public string SkStackAppVersion { get; }
+ public Version SkStackVersion { get; }
+
+ public async ValueTask<BP35UartConfigurations> GetUartOptionsAsync(CancellationToken cancellationToken = default) {}
+ public async ValueTask<BP35UdpReceiveDataFormat> GetUdpDataFormatAsync(CancellationToken cancellationToken = default) {}
+ public ValueTask SetUartOptionsAsync(BP35UartBaudRate baudRate, BP35UartCharacterInterval characterInterval = BP35UartCharacterInterval.None, BP35UartFlowControl flowControl = BP35UartFlowControl.Disabled, CancellationToken cancellationToken = default) {}
+ public ValueTask SetUartOptionsAsync(BP35UartConfigurations uartConfigurations, CancellationToken cancellationToken = default) {}
+ public ValueTask SetUdpDataFormatAsync(BP35UdpReceiveDataFormat format, CancellationToken cancellationToken = default) {}
+ }
+
+ public readonly struct BP35UartConfigurations {
+ public BP35UartConfigurations(BP35UartBaudRate baudRate, BP35UartCharacterInterval characterInterval, BP35UartFlowControl flowControl) {}
+
+ public BP35UartBaudRate BaudRate { get; }
+ public BP35UartCharacterInterval CharacterInterval { get; }
+ public BP35UartFlowControl FlowControl { get; }
+
+ public void Deconstruct(out BP35UartBaudRate baudRate, out BP35UartCharacterInterval characterInterval, out BP35UartFlowControl flowControl) {}
+ }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.4.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.3.1.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-net8.0.apilist.cs b/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-net8.0.apilist.cs
new file mode 100644
index 0000000..d0204eb
--- /dev/null
+++ b/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-net8.0.apilist.cs
@@ -0,0 +1,110 @@
+// Smdn.Devices.BP35XX.dll (Smdn.Devices.BP35XX-1.0.0)
+// Name: Smdn.Devices.BP35XX
+// AssemblyVersion: 1.0.0.0
+// InformationalVersion: 1.0.0+e4163ada5b034b45c5cc0dac179b412cf54198cd
+// TargetFramework: .NETCoreApp,Version=v8.0
+// Configuration: Release
+// Referenced assemblies:
+// Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+// Microsoft.Extensions.Logging.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+// Smdn.Fundamental.PrintableEncoding.Hexadecimal, Version=3.0.0.0, Culture=neutral
+// Smdn.Net.SkStackIP, Version=1.0.0.0, Culture=neutral
+// System.ComponentModel, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.IO.Ports, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+// System.Memory, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+// System.Net.NetworkInformation, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.Net.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+#nullable enable annotations
+
+using System;
+using System.IO;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Threading;
+using System.Threading.Tasks;
+using Smdn.Devices.BP35XX;
+using Smdn.Net.SkStackIP;
+
+namespace Smdn.Devices.BP35XX {
+ public interface IBP35Configurations {
+ BP35UartBaudRate BaudRate { get; }
+ SkStackERXUDPDataFormat ERXUDPDataFormat { get; }
+ string? SerialPortName { get; }
+ bool TryLoadFlashMemory { get; }
+ }
+
+ public interface IBP35SerialPortStreamFactory {
+ Stream CreateSerialPortStream(IBP35Configurations configurations);
+ }
+
+ public enum BP35UartBaudRate : byte {
+ Baud115200 = 0,
+ Baud19200 = 4,
+ Baud2400 = 1,
+ Baud38400 = 5,
+ Baud4800 = 2,
+ Baud57600 = 6,
+ Baud9600 = 3,
+ }
+
+ public enum BP35UartCharacterInterval : byte {
+ Microseconds100 = 16,
+ Microseconds200 = 32,
+ Microseconds300 = 48,
+ Microseconds400 = 64,
+ Microseconds50 = 80,
+ None = 0,
+ }
+
+ public enum BP35UartFlowControl : byte {
+ Disabled = 0,
+ Enabled = 128,
+ }
+
+ public enum BP35UdpReceiveDataFormat : byte {
+ Binary = 0,
+ HexAscii = 1,
+ }
+
+ public class BP35A1 : BP35Base {
+ public static ValueTask<BP35A1> CreateAsync(BP35A1Configurations configurations, IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default) {}
+ public static ValueTask<BP35A1> CreateAsync(string? serialPortName, IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default) {}
+ }
+
+ public sealed class BP35A1Configurations : IBP35Configurations {
+ public BP35A1Configurations() {}
+
+ public BP35UartBaudRate BaudRate { get; set; }
+ public string? SerialPortName { get; set; }
+ SkStackERXUDPDataFormat IBP35Configurations.ERXUDPDataFormat { get; }
+ public bool TryLoadFlashMemory { get; set; }
+ }
+
+ public abstract class BP35Base : SkStackClient {
+ public IPAddress LinkLocalAddress { get; }
+ public PhysicalAddress MacAddress { get; }
+ public string RohmPassword { get; }
+ public string RohmUserId { get; }
+ public string SkStackAppVersion { get; }
+ public Version SkStackVersion { get; }
+
+ public async ValueTask<BP35UartConfigurations> GetUartOptionsAsync(CancellationToken cancellationToken = default) {}
+ public async ValueTask<BP35UdpReceiveDataFormat> GetUdpDataFormatAsync(CancellationToken cancellationToken = default) {}
+ public ValueTask SetUartOptionsAsync(BP35UartBaudRate baudRate, BP35UartCharacterInterval characterInterval = BP35UartCharacterInterval.None, BP35UartFlowControl flowControl = BP35UartFlowControl.Disabled, CancellationToken cancellationToken = default) {}
+ public ValueTask SetUartOptionsAsync(BP35UartConfigurations uartConfigurations, CancellationToken cancellationToken = default) {}
+ public ValueTask SetUdpDataFormatAsync(BP35UdpReceiveDataFormat format, CancellationToken cancellationToken = default) {}
+ }
+
+ public readonly struct BP35UartConfigurations {
+ public BP35UartConfigurations(BP35UartBaudRate baudRate, BP35UartCharacterInterval characterInterval, BP35UartFlowControl flowControl) {}
+
+ public BP35UartBaudRate BaudRate { get; }
+ public BP35UartCharacterInterval CharacterInterval { get; }
+ public BP35UartFlowControl FlowControl { get; }
+
+ public void Deconstruct(out BP35UartBaudRate baudRate, out BP35UartCharacterInterval characterInterval, out BP35UartFlowControl flowControl) {}
+ }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.4.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.3.1.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-netstandard2.1.apilist.cs b/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-netstandard2.1.apilist.cs
new file mode 100644
index 0000000..e9184a4
--- /dev/null
+++ b/doc/api-list/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX-netstandard2.1.apilist.cs
@@ -0,0 +1,106 @@
+// Smdn.Devices.BP35XX.dll (Smdn.Devices.BP35XX-1.0.0)
+// Name: Smdn.Devices.BP35XX
+// AssemblyVersion: 1.0.0.0
+// InformationalVersion: 1.0.0+e4163ada5b034b45c5cc0dac179b412cf54198cd
+// TargetFramework: .NETStandard,Version=v2.1
+// Configuration: Release
+// Referenced assemblies:
+// Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+// Microsoft.Extensions.Logging.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+// Smdn.Fundamental.PrintableEncoding.Hexadecimal, Version=3.0.0.0, Culture=neutral
+// Smdn.Net.SkStackIP, Version=1.0.0.0, Culture=neutral
+// System.IO.Ports, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+// netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+#nullable enable annotations
+
+using System;
+using System.IO;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Threading;
+using System.Threading.Tasks;
+using Smdn.Devices.BP35XX;
+using Smdn.Net.SkStackIP;
+
+namespace Smdn.Devices.BP35XX {
+ public interface IBP35Configurations {
+ BP35UartBaudRate BaudRate { get; }
+ SkStackERXUDPDataFormat ERXUDPDataFormat { get; }
+ string? SerialPortName { get; }
+ bool TryLoadFlashMemory { get; }
+ }
+
+ public interface IBP35SerialPortStreamFactory {
+ Stream CreateSerialPortStream(IBP35Configurations configurations);
+ }
+
+ public enum BP35UartBaudRate : byte {
+ Baud115200 = 0,
+ Baud19200 = 4,
+ Baud2400 = 1,
+ Baud38400 = 5,
+ Baud4800 = 2,
+ Baud57600 = 6,
+ Baud9600 = 3,
+ }
+
+ public enum BP35UartCharacterInterval : byte {
+ Microseconds100 = 16,
+ Microseconds200 = 32,
+ Microseconds300 = 48,
+ Microseconds400 = 64,
+ Microseconds50 = 80,
+ None = 0,
+ }
+
+ public enum BP35UartFlowControl : byte {
+ Disabled = 0,
+ Enabled = 128,
+ }
+
+ public enum BP35UdpReceiveDataFormat : byte {
+ Binary = 0,
+ HexAscii = 1,
+ }
+
+ public class BP35A1 : BP35Base {
+ public static ValueTask<BP35A1> CreateAsync(BP35A1Configurations configurations, IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default) {}
+ public static ValueTask<BP35A1> CreateAsync(string? serialPortName, IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default) {}
+ }
+
+ public sealed class BP35A1Configurations : IBP35Configurations {
+ public BP35A1Configurations() {}
+
+ public BP35UartBaudRate BaudRate { get; set; }
+ public string? SerialPortName { get; set; }
+ SkStackERXUDPDataFormat IBP35Configurations.ERXUDPDataFormat { get; }
+ public bool TryLoadFlashMemory { get; set; }
+ }
+
+ public abstract class BP35Base : SkStackClient {
+ public IPAddress LinkLocalAddress { get; }
+ public PhysicalAddress MacAddress { get; }
+ public string RohmPassword { get; }
+ public string RohmUserId { get; }
+ public string SkStackAppVersion { get; }
+ public Version SkStackVersion { get; }
+
+ public async ValueTask<BP35UartConfigurations> GetUartOptionsAsync(CancellationToken cancellationToken = default) {}
+ public async ValueTask<BP35UdpReceiveDataFormat> GetUdpDataFormatAsync(CancellationToken cancellationToken = default) {}
+ public ValueTask SetUartOptionsAsync(BP35UartBaudRate baudRate, BP35UartCharacterInterval characterInterval = BP35UartCharacterInterval.None, BP35UartFlowControl flowControl = BP35UartFlowControl.Disabled, CancellationToken cancellationToken = default) {}
+ public ValueTask SetUartOptionsAsync(BP35UartConfigurations uartConfigurations, CancellationToken cancellationToken = default) {}
+ public ValueTask SetUdpDataFormatAsync(BP35UdpReceiveDataFormat format, CancellationToken cancellationToken = default) {}
+ }
+
+ public readonly struct BP35UartConfigurations {
+ public BP35UartConfigurations(BP35UartBaudRate baudRate, BP35UartCharacterInterval characterInterval, BP35UartFlowControl flowControl) {}
+
+ public BP35UartBaudRate BaudRate { get; }
+ public BP35UartCharacterInterval CharacterInterval { get; }
+ public BP35UartFlowControl FlowControl { get; }
+
+ public void Deconstruct(out BP35UartBaudRate baudRate, out BP35UartCharacterInterval characterInterval, out BP35UartFlowControl flowControl) {}
+ }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.4.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.3.1.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX.csproj b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX.csproj
new file mode 100644
index 0000000..2551c0f
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX.csproj
@@ -0,0 +1,59 @@
+<!--
+SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+SPDX-License-Identifier: MIT
+-->
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFrameworks>net8.0;net6.0;netstandard2.1</TargetFrameworks>
+ <VersionPrefix>1.0.0</VersionPrefix>
+ <VersionSuffix></VersionSuffix>
+ <!-- <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion> -->
+ <Nullable>enable</Nullable>
+ <RootNamespace/> <!-- empty the root namespace so that the namespace is determined only by the directory name, for code style rule IDE0030 -->
+ <NoWarn>CS1591;$(NoWarn)</NoWarn> <!-- CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' -->
+ </PropertyGroup>
+
+ <PropertyGroup Label="metadata">
+ <Description>Provides APIs to operate ROHM BP35A1 and other ROHM Wi-SUN modules using the SKSTACK-IP command.</Description>
+ <CopyrightYear>2021</CopyrightYear>
+ </PropertyGroup>
+
+ <PropertyGroup Label="package properties">
+ <PackageTags>SKSTACK,SKSTACK-IP,BP35A1,ROHM-BP35A1,Wi-SUN</PackageTags>
+ <GenerateNupkgReadmeFileDependsOnTargets>$(GenerateNupkgReadmeFileDependsOnTargets);GenerateReadmeFileContent</GenerateNupkgReadmeFileDependsOnTargets>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="System.IO.Ports" Version="8.0.0" />
+ <ProjectOrPackageReference ReferencePackageVersion="[1.0.0,2.0.0)" Include="..\Smdn.Net.SkStackIP\Smdn.Net.SkStackIP.csproj" />
+ </ItemGroup>
+
+ <Target Name="GenerateReadmeFileContent" DependsOnTargets="ReadReadmeFileNoticeSectionContent">
+ <PropertyGroup>
+ <PackageReadmeFileContent><![CDATA[# $(PackageId) $(PackageVersion)
+`$(PackageId)` is a library that provides APIs to operate [ROHM BP35A1](https://www.rohm.co.jp/products/wireless-communication/specified-low-power-radio-modules/bp35a1-product) and other [ROHM Wi-SUN modules](https://www.rohm.co.jp/products/wireless-communication/specified-low-power-radio-modules) using the [Skyley Networks](https://www.skyley.com/)' [SKSTACK IP](https://www.skyley.com/wiki/?SKSTACK+IP+for+HAN) command.
+
+## Getting started
+First, add package [$(PackageId)](https://www.nuget.org/packages/$(PackageId)) to the project file.
+
+```
+dotnet add package $(PackageId)
+```
+
+For using BP35A1, use the `BP35A1.CreateAsync` method to create an instance of the device.
+
+```cs
+$([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\..\examples\$(PackageId)\getting-started\Program.cs').TrimEnd())
+```
+
+More examples can be found on the [GitHub repository]($(RepositoryUrl)/tree/main/examples/$(PackageId)/), including examples of using library features.
+
+## Contributing
+This project welcomes contributions, feedbacks and suggestions. You can contribute to this project by submitting [Issues]($(RepositoryUrl)/issues/new/choose) or [Pull Requests]($(RepositoryUrl)/pulls/) on the [GitHub repository]($(RepositoryUrl)).
+
+## Notice
+$(ReadmeFileNoticeSectionContent)
+]]></PackageReadmeFileContent>
+ </PropertyGroup>
+ </Target>
+</Project>
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35A1.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35A1.cs
new file mode 100644
index 0000000..29ceb35
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35A1.cs
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Smdn.Devices.BP35XX;
+
+public class BP35A1 : BP35Base {
+ /// <summary>
+ /// Refer to the initial value of baud rate for UART setting in the BP35A1.
+ /// </summary>
+ /// <remarks>
+ /// See 'BP35A1コマンドリファレンス 3.32. WUART (プロダクト設定コマンド)' for detailed specifications.
+ /// </remarks>
+ internal const BP35UartBaudRate DefaultValueForBP35UartBaudRate = BP35UartBaudRate.Baud115200;
+
+ public static ValueTask<BP35A1> CreateAsync(
+ string? serialPortName,
+ IServiceProvider? serviceProvider = null,
+ CancellationToken cancellationToken = default
+ )
+ => CreateAsync(
+ configurations: new BP35A1Configurations() {
+ SerialPortName = serialPortName,
+ },
+ serviceProvider: serviceProvider,
+ cancellationToken: cancellationToken
+ );
+
+ public static ValueTask<BP35A1> CreateAsync(
+ BP35A1Configurations configurations,
+ IServiceProvider? serviceProvider = null,
+ CancellationToken cancellationToken = default
+ )
+ => InitializeAsync(
+#pragma warning disable CA2000
+ device: new BP35A1(
+ configurations: configurations ?? throw new ArgumentNullException(nameof(configurations)),
+ serviceProvider: serviceProvider
+ ),
+#pragma warning restore CA2000
+ tryLoadFlashMemory: configurations.TryLoadFlashMemory,
+ serviceProvider: serviceProvider,
+ cancellationToken: cancellationToken
+ );
+
+ private BP35A1(
+ IBP35Configurations configurations,
+ IServiceProvider? serviceProvider = null
+ )
+ : base(
+ configurations: configurations,
+ serialPortStreamFactory: serviceProvider?.GetService<IBP35SerialPortStreamFactory>(),
+ logger: serviceProvider?.GetService<ILoggerFactory>()?.CreateLogger<BP35A1>()
+ )
+ {
+ }
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35A1Configurations.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35A1Configurations.cs
new file mode 100644
index 0000000..eeffe6a
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35A1Configurations.cs
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+using Smdn.Net.SkStackIP;
+
+namespace Smdn.Devices.BP35XX;
+
+public sealed class BP35A1Configurations : IBP35Configurations {
+ /// <inheritdoc cref="IBP35Configurations.SerialPortName"/>
+ public string? SerialPortName { get; set; }
+
+ /// <inheritdoc cref="IBP35Configurations.BaudRate"/>
+ public BP35UartBaudRate BaudRate { get; set; } = BP35A1.DefaultValueForBP35UartBaudRate;
+
+ /// <inheritdoc cref="IBP35Configurations.TryLoadFlashMemory"/>
+ public bool TryLoadFlashMemory { get; set; } = BP35Base.DefaultValueForTryLoadFlashMemory;
+
+ SkStackERXUDPDataFormat IBP35Configurations.ERXUDPDataFormat => SkStackERXUDPDataFormat.Binary;
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.DefaultSerialPortStreamFactory.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.DefaultSerialPortStreamFactory.cs
new file mode 100644
index 0000000..2b62f11
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.DefaultSerialPortStreamFactory.cs
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+using System;
+using System.IO;
+using System.IO.Ports;
+
+namespace Smdn.Devices.BP35XX;
+
+#pragma warning disable IDE0040
+partial class BP35Base {
+#pragma warning restore IDE0040
+ private class DefaultSerialPortStreamFactory : IBP35SerialPortStreamFactory {
+ public static DefaultSerialPortStreamFactory Instance { get; } = new();
+
+ public Stream CreateSerialPortStream(IBP35Configurations configurations)
+ {
+ if (string.IsNullOrEmpty(configurations.SerialPortName)) {
+ throw new ArgumentException(
+ message: $"The {nameof(configurations.SerialPortName)} is not set for the {configurations.GetType().Name}",
+ paramName: nameof(configurations)
+ );
+ }
+
+ const string CRLF = "\r\n";
+
+#pragma warning disable CA2000
+ var port = new SerialPort(
+ portName: configurations.SerialPortName,
+ baudRate: configurations.BaudRate switch {
+ BP35UartBaudRate.Baud2400 => 2_400,
+ BP35UartBaudRate.Baud4800 => 4_800,
+ BP35UartBaudRate.Baud9600 => 9_600,
+ BP35UartBaudRate.Baud19200 => 19_200,
+ BP35UartBaudRate.Baud38400 => 38_400,
+ BP35UartBaudRate.Baud57600 => 57_600,
+ BP35UartBaudRate.Baud115200 => 115_200,
+ _ => throw new ArgumentException(
+ message: $"A valid {nameof(BP35UartBaudRate)} value is not set for the {configurations.GetType().Name}",
+ paramName: nameof(configurations)
+ ),
+ },
+ parity: Parity.None,
+ dataBits: 8,
+ stopBits: StopBits.One
+ ) {
+ Handshake = Handshake.None, // TODO: RequestToSend
+ DtrEnable = false,
+ RtsEnable = false,
+ NewLine = CRLF,
+ };
+#pragma warning restore CA2000
+
+ port.Open();
+
+ // discard input buffer to avoid reading previously received data
+ port.DiscardInBuffer();
+
+ return port.BaseStream;
+ }
+ }
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.Functions.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.Functions.cs
new file mode 100644
index 0000000..a04999c
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.Functions.cs
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Smdn.Devices.BP35XX;
+
+#pragma warning disable IDE0040
+partial class BP35Base {
+#pragma warning restore IDE0040
+ public ValueTask SetUdpDataFormatAsync(
+ BP35UdpReceiveDataFormat format,
+ CancellationToken cancellationToken = default
+ )
+ {
+ return SendWOPTAsync(
+ mode: format switch {
+ BP35UdpReceiveDataFormat.Binary => BP35ERXUDPFormatBinary,
+ BP35UdpReceiveDataFormat.HexAscii => BP35ERXUDPFormatHexAscii,
+ _ => throw new ArgumentException($"undefined value of {nameof(BP35UdpReceiveDataFormat)}", nameof(format)),
+ },
+ cancellationToken: cancellationToken
+ );
+ }
+
+ public async ValueTask<BP35UdpReceiveDataFormat> GetUdpDataFormatAsync(
+ CancellationToken cancellationToken = default
+ )
+ {
+ var mode = await SendROPTAsync(
+ cancellationToken: cancellationToken
+ ).ConfigureAwait(false);
+
+ return (mode & BP35ERXUDPFormatMask) switch {
+ BP35ERXUDPFormatBinary => BP35UdpReceiveDataFormat.Binary,
+ BP35ERXUDPFormatHexAscii => BP35UdpReceiveDataFormat.HexAscii,
+ _ => BP35UdpReceiveDataFormat.Binary, // XXX
+ };
+ }
+
+ public ValueTask SetUartOptionsAsync(
+ BP35UartBaudRate baudRate,
+ BP35UartCharacterInterval characterInterval = default,
+ BP35UartFlowControl flowControl = default,
+ CancellationToken cancellationToken = default
+ )
+ => SetUartOptionsAsync(
+ uartConfigurations: new(baudRate, characterInterval, flowControl),
+ cancellationToken: cancellationToken
+ );
+
+ public ValueTask SetUartOptionsAsync(
+ BP35UartConfigurations uartConfigurations,
+ CancellationToken cancellationToken = default
+ )
+ => SendWUARTAsync(
+ mode: uartConfigurations.Mode,
+ cancellationToken: cancellationToken
+ );
+
+ public async ValueTask<BP35UartConfigurations> GetUartOptionsAsync(
+ CancellationToken cancellationToken = default
+ )
+ {
+ var mode = await SendRUARTAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ return new(mode);
+ }
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.SkStackIP.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.SkStackIP.cs
new file mode 100644
index 0000000..ad77ea4
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.SkStackIP.cs
@@ -0,0 +1,140 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+using System;
+using System.Buffers;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Smdn.Formats;
+using Smdn.Net.SkStackIP.Protocol;
+
+namespace Smdn.Devices.BP35XX;
+
+#pragma warning disable IDE0040
+partial class BP35Base {
+#pragma warning restore IDE0040
+ private static readonly SkStackProtocolSyntax RMCommandSyntax = new BP35CommandSyntax();
+
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.30. WOPT (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ private const byte BP35ERXUDPFormatMask = 0b_0000000_1;
+
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.30. WOPT (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ private const byte BP35ERXUDPFormatBinary = 0b_0000000_0;
+
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.30. WOPT (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ private const byte BP35ERXUDPFormatHexAscii = 0b_0000000_1;
+
+ /// <summary>
+ /// <para>Sends a command <c>WOPT</c>.</para>
+ /// </summary>
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.30. WOPT (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ private protected async ValueTask SendWOPTAsync(
+ byte mode,
+ CancellationToken cancellationToken = default
+ )
+ {
+ byte[]? modeBytes = null;
+
+ try {
+ modeBytes = ArrayPool<byte>.Shared.Rent(2);
+
+ _ = Hexadecimal.TryEncodeUpperCase(mode, modeBytes.AsSpan(), out var lengthOfMODE);
+
+ _ = await SendCommandAsync(
+ command: BP35CommandNames.WOPT,
+ writeArguments: writer => writer.WriteToken(modeBytes.AsSpan(0, lengthOfMODE)),
+ syntax: RMCommandSyntax,
+ cancellationToken: cancellationToken,
+ throwIfErrorStatus: true
+ ).ConfigureAwait(false);
+ }
+ finally {
+ if (modeBytes is not null)
+ ArrayPool<byte>.Shared.Return(modeBytes);
+ }
+ }
+
+ /// <summary>
+ /// <para>Sends a command <c>ROPT</c>.</para>
+ /// </summary>
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.31. ROPT (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ private protected async ValueTask<byte> SendROPTAsync(
+ CancellationToken cancellationToken = default
+ )
+ {
+ var resp = await SendCommandAsync(
+ command: BP35CommandNames.ROPT,
+ writeArguments: null,
+ syntax: RMCommandSyntax,
+ cancellationToken: cancellationToken,
+ throwIfErrorStatus: true
+ ).ConfigureAwait(false);
+
+ return Convert.ToByte(Encoding.ASCII.GetString(resp.StatusText.Span), 16);
+ }
+
+ /// <summary>
+ /// <para>Sends a command <c>WUART</c>.</para>
+ /// </summary>
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.32. WUART (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ private protected async ValueTask SendWUARTAsync(
+ byte mode,
+ CancellationToken cancellationToken = default
+ )
+ {
+ byte[]? modeBytes = null;
+
+ try {
+ modeBytes = ArrayPool<byte>.Shared.Rent(2);
+
+ _ = Hexadecimal.TryEncodeUpperCase(mode, modeBytes.AsSpan(), out var lengthOfMODE);
+
+ _ = await SendCommandAsync(
+ command: BP35CommandNames.WUART,
+ writeArguments: writer => writer.WriteToken(modeBytes.AsSpan(0, lengthOfMODE)),
+ syntax: RMCommandSyntax,
+ cancellationToken: cancellationToken,
+ throwIfErrorStatus: true
+ ).ConfigureAwait(false);
+ }
+ finally {
+ if (modeBytes is not null)
+ ArrayPool<byte>.Shared.Return(modeBytes);
+ }
+ }
+
+ /// <summary>
+ /// <para>Sends a command <c>RUART</c>.</para>
+ /// </summary>
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.33. RUART (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ private protected async ValueTask<byte> SendRUARTAsync(
+ CancellationToken cancellationToken = default
+ )
+ {
+ var resp = await SendCommandAsync(
+ command: BP35CommandNames.RUART,
+ writeArguments: null,
+ syntax: RMCommandSyntax,
+ cancellationToken: cancellationToken,
+ throwIfErrorStatus: true
+ ).ConfigureAwait(false);
+
+ return Convert.ToByte(Encoding.ASCII.GetString(resp.StatusText.Span), 16);
+ }
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.cs
new file mode 100644
index 0000000..a498b91
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35Base.cs
@@ -0,0 +1,225 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+#pragma warning disable CA1848
+
+using System;
+#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
+using System.Diagnostics.CodeAnalysis;
+#endif
+#if !SYSTEM_CONVERT_TOHEXSTRING
+using System.Buffers; // ArrayPool
+#endif
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.Extensions.Logging;
+
+using Smdn.Net.SkStackIP;
+
+namespace Smdn.Devices.BP35XX;
+
+public abstract partial class BP35Base : SkStackClient {
+ internal const bool DefaultValueForTryLoadFlashMemory = true;
+
+ private protected static async ValueTask<TBP35XX> InitializeAsync<TBP35XX>(
+ TBP35XX device,
+ bool tryLoadFlashMemory = DefaultValueForTryLoadFlashMemory,
+ IServiceProvider? serviceProvider = null,
+ CancellationToken cancellationToken = default
+ )
+ where TBP35XX : BP35Base
+ {
+#pragma warning disable CA1510
+ if (device is null)
+ throw new ArgumentNullException(nameof(device));
+#pragma warning disable CA1510
+
+ try {
+ await device.InitializeAsync(
+ tryLoadFlashMemory,
+ serviceProvider,
+ cancellationToken
+ ).ConfigureAwait(false);
+
+ return device;
+ }
+ catch {
+ device.Dispose();
+
+ throw;
+ }
+ }
+
+ private protected static InvalidOperationException CreateNotInitializedException()
+ => new(message: "not initialized");
+
+ /*
+ * instance members
+ */
+#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
+ [MemberNotNullWhen(true, nameof(skstackVersion))]
+ [MemberNotNullWhen(true, nameof(skstackAppVersion))]
+ [MemberNotNullWhen(true, nameof(linkLocalAddress))]
+ [MemberNotNullWhen(true, nameof(macAddress))]
+ [MemberNotNullWhen(true, nameof(rohmUserId))]
+ [MemberNotNullWhen(true, nameof(rohmPassword))]
+#endif
+ private protected bool IsInitialized { get; private set; }
+
+#if !SYSTEM_DIAGNOSTICS_CODEANALYSIS_MEMBERNOTNULLWHENATTRIBUTE
+#pragma warning disable CS8603
+#endif
+ private Version? skstackVersion;
+ public Version SkStackVersion => IsInitialized ? skstackVersion : throw CreateNotInitializedException();
+
+ private string? skstackAppVersion;
+ public string SkStackAppVersion => IsInitialized ? skstackAppVersion : throw CreateNotInitializedException();
+
+ private IPAddress? linkLocalAddress;
+ public IPAddress LinkLocalAddress => IsInitialized ? linkLocalAddress : throw CreateNotInitializedException();
+
+ private PhysicalAddress? macAddress;
+ public PhysicalAddress MacAddress => IsInitialized ? macAddress : throw CreateNotInitializedException();
+
+ private string? rohmUserId;
+ public string RohmUserId => IsInitialized ? rohmUserId : throw CreateNotInitializedException();
+
+ private string? rohmPassword;
+ public string RohmPassword => IsInitialized ? rohmPassword : throw CreateNotInitializedException();
+#pragma warning restore CS8603
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BP35Base"/> class with specifying the serial port name.
+ /// </summary>
+ /// <param name="configurations">
+ /// A <see cref="IBP35Configurations"/> that holds the configurations to the <see cref="BP35Base"/> instance.
+ /// </param>
+ /// <param name="serialPortStreamFactory">
+ /// A <see cref="IBP35SerialPortStreamFactory"/> that provides the function to create the serial port stream according to the <paramref name="configurations"/>.
+ /// </param>
+ /// <param name="logger">The <see cref="ILogger"/> to report the situation.</param>
+#pragma warning disable IDE0290
+ private protected BP35Base(
+ IBP35Configurations configurations,
+ IBP35SerialPortStreamFactory? serialPortStreamFactory,
+ ILogger? logger
+ )
+#pragma warning restore IDE0290
+ : base(
+ stream: (serialPortStreamFactory ?? DefaultSerialPortStreamFactory.Instance).CreateSerialPortStream(
+ configurations ?? throw new ArgumentNullException(nameof(configurations))
+ ),
+ leaveStreamOpen: false, // should close the opened stream
+ erxudpDataFormat: configurations.ERXUDPDataFormat,
+ logger: logger
+ )
+ {
+ }
+
+ private async ValueTask InitializeAsync(
+ bool tryLoadFlashMemory,
+ IServiceProvider? serviceProvider,
+ CancellationToken cancellationToken
+ )
+ {
+ // retrieve firmware version
+ skstackVersion = (await SendSKVERAsync(cancellationToken).ConfigureAwait(false)).Payload;
+
+ Logger?.LogInformation("{Name}: {Value}", nameof(SkStackVersion), skstackVersion);
+
+ skstackAppVersion = (await SendSKAPPVERAsync(cancellationToken).ConfigureAwait(false)).Payload;
+
+ Logger?.LogInformation("{Name}: {Value}", nameof(SkStackAppVersion), skstackAppVersion);
+
+ // retrieve EINFO
+ var respInfo = await SendSKINFOAsync(cancellationToken).ConfigureAwait(false);
+ var einfo = respInfo.Payload!;
+
+ linkLocalAddress = einfo.LinkLocalAddress;
+ macAddress = einfo.MacAddress;
+
+ Logger?.LogInformation("{Name}: {Value}", nameof(LinkLocalAddress), linkLocalAddress);
+ Logger?.LogInformation("{Name}: {Value}", nameof(MacAddress), macAddress);
+
+ Logger?.LogInformation("{Name}: {Value}", nameof(einfo.Channel), einfo.Channel);
+ Logger?.LogInformation("{Name}: {Value} (0x{ValueToBeDisplayedInHex:X4})", nameof(einfo.PanId), einfo.PanId, einfo.PanId);
+
+ // parse ROHM user ID and password
+ (rohmUserId, rohmPassword) = ParseRohmUserIdAndPassword(linkLocalAddress);
+
+ // try load configuration from flash memory
+ if (tryLoadFlashMemory) {
+ try {
+ await SendSKLOADAsync(cancellationToken).ConfigureAwait(false);
+ }
+ catch (SkStackFlashMemoryIOException) {
+ Logger?.LogWarning("Could not load configuration from flash memory.");
+ }
+ }
+
+ // disable echoback (override loaded configuration)
+ await SendSKSREGAsync(
+ register: SkStackRegister.EnableEchoback,
+ value: false,
+ cancellationToken: cancellationToken
+ ).ConfigureAwait(false);
+
+ // set ERXUDP data format
+ var udpDataFormat = await GetUdpDataFormatAsync(cancellationToken).ConfigureAwait(false);
+
+#pragma warning disable IDE0055, IDE0072
+ ERXUDPDataFormat = udpDataFormat switch {
+ BP35UdpReceiveDataFormat.HexAscii => SkStackERXUDPDataFormat.HexAsciiText,
+ /*BP35UdpReceiveDataFormat.Binary,*/ _ => SkStackERXUDPDataFormat.Binary,
+ };
+#pragma warning restore IDE0055, IDE0072
+
+ await InitializeAsyncCore(serviceProvider, cancellationToken).ConfigureAwait(false);
+
+ IsInitialized = true;
+
+ static (string, string) ParseRohmUserIdAndPassword(IPAddress linkLocalAddress)
+ {
+#if SYSTEM_CONVERT_TOHEXSTRING
+ Span<byte> addressBytes = stackalloc byte[16];
+
+ if (linkLocalAddress.TryWriteBytes(addressBytes, out var bytesWritten) && (8 + 2) <= bytesWritten) {
+ return (
+ Convert.ToHexString(addressBytes.Slice(0, 2)),
+ Convert.ToHexString(addressBytes.Slice(8, 2))
+ );
+ }
+#else
+ byte[]? addressBytes = null;
+
+ try {
+ addressBytes = ArrayPool<byte>.Shared.Rent(16);
+
+ if (linkLocalAddress.TryWriteBytes(addressBytes, out var bytesWritten) && (8 + 2) <= bytesWritten) {
+ return (
+ $"{addressBytes[0]:X2}{addressBytes[1]:X2}",
+ $"{addressBytes[8]:X2}{addressBytes[9]:X2}"
+ );
+ }
+ }
+ finally {
+ if (addressBytes is not null)
+ ArrayPool<byte>.Shared.Return(addressBytes);
+ }
+#endif
+
+ return default; // or throw exception?
+ }
+ }
+
+ private protected virtual ValueTask InitializeAsyncCore(
+ IServiceProvider? serviceProvider,
+ CancellationToken cancellationToken
+ )
+ {
+ // nothing to do in this class
+ return default;
+ }
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35CommandNames.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35CommandNames.cs
new file mode 100644
index 0000000..e940474
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35CommandNames.cs
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+using System;
+using System.Text;
+
+namespace Smdn.Devices.BP35XX;
+
+/// <remarks>
+/// <para>See 'BP35A1コマンドリファレンス 3. コマンドリファレンス' for detailed specifications.</para>
+/// </remarks>
+internal class BP35CommandNames {
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.30. WOPT (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ public static ReadOnlyMemory<byte> WOPT { get; } = Encoding.ASCII.GetBytes(nameof(WOPT));
+
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.31. ROPT (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ public static ReadOnlyMemory<byte> ROPT { get; } = Encoding.ASCII.GetBytes(nameof(ROPT));
+
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.32. WUART (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ public static ReadOnlyMemory<byte> WUART { get; } = Encoding.ASCII.GetBytes(nameof(WUART));
+
+ /// <remarks>
+ /// <para>See 'BP35A1コマンドリファレンス 3.33. RUART (プロダクト設定コマンド)' for detailed specifications.</para>
+ /// </remarks>
+ public static ReadOnlyMemory<byte> RUART { get; } = Encoding.ASCII.GetBytes(nameof(RUART));
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35CommandSyntax.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35CommandSyntax.cs
new file mode 100644
index 0000000..b522260
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35CommandSyntax.cs
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+using System;
+
+using Smdn.Net.SkStackIP.Protocol;
+
+namespace Smdn.Devices.BP35XX;
+
+/// <remarks>
+/// <para>See below for detailed specifications.</para>
+/// <list type="bullet">
+/// <item><description>'BP35A1コマンドリファレンス 3.30. WOPT (プロダクト設定コマンド)'</description></item>
+/// <item><description>'BP35A1コマンドリファレンス 3.31. ROPT (プロダクト設定コマンド)'</description></item>
+/// <item><description>'BP35A1コマンドリファレンス 3.32. WUART (プロダクト設定コマンド)'</description></item>
+/// <item><description>'BP35A1コマンドリファレンス 3.33. RUART (プロダクト設定コマンド)'</description></item>
+/// </list>
+/// </remarks>
+/// <seealso cref="SkStackProtocolSyntax"/>
+internal sealed class BP35CommandSyntax : SkStackProtocolSyntax {
+ /// <summary>
+ /// Gets the newline character used in the product configuration commands (プロダクト設定コマンド).
+ /// Only <c>CR</c> is used as a newline character in the product configuration command and its response.
+ /// </summary>
+ public override ReadOnlySpan<byte> EndOfCommandLine => "\r"u8;
+ public override bool ExpectStatusLine => true;
+ public override ReadOnlySpan<byte> EndOfStatusLine => "\r"u8;
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartBaudRate.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartBaudRate.cs
new file mode 100644
index 0000000..6b3e9d7
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartBaudRate.cs
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+namespace Smdn.Devices.BP35XX;
+
+/// <summary>
+/// An enumeration type representing the configuration values for the UART baud rate, to be set or get by the <c>WUART</c> and <c>RUART</c> commands.
+/// </summary>
+/// <remarks>
+/// <para>See below for detailed specifications.</para>
+/// <list type="bullet">
+/// <item><description>'BP35A1コマンドリファレンス 3.32. WUART (プロダクト設定コマンド)'</description></item>
+/// <item><description>'BP35A1コマンドリファレンス 3.33. RUART (プロダクト設定コマンド)'</description></item>
+/// </list>
+/// </remarks>
+#pragma warning disable CA1027
+public enum BP35UartBaudRate : byte {
+#pragma warning restore CA1027
+ Baud115200 = 0b_0_000_0_000, // default(BP35UartBaudRate)
+ Baud2400 = 0b_0_000_0_001,
+ Baud4800 = 0b_0_000_0_010,
+ Baud9600 = 0b_0_000_0_011,
+ Baud19200 = 0b_0_000_0_100,
+ Baud38400 = 0b_0_000_0_101,
+ Baud57600 = 0b_0_000_0_110,
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartCharacterInterval.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartCharacterInterval.cs
new file mode 100644
index 0000000..7be4179
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartCharacterInterval.cs
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+namespace Smdn.Devices.BP35XX;
+
+/// <summary>
+/// An enumeration type representing the configuration values for the inter-character intervals in UART, to be set or get by the <c>WUART</c> and <c>RUART</c> commands.
+/// </summary>
+/// <remarks>
+/// <para>See below for detailed specifications.</para>
+/// <list type="bullet">
+/// <item><description>'BP35A1コマンドリファレンス 3.32. WUART (プロダクト設定コマンド)'</description></item>
+/// <item><description>'BP35A1コマンドリファレンス 3.33. RUART (プロダクト設定コマンド)'</description></item>
+/// </list>
+/// </remarks>
+#pragma warning disable CA1027
+public enum BP35UartCharacterInterval : byte {
+#pragma warning restore CA1027
+ None = 0b_0_000_0_000, // default(BP35UartCharacterInterval)
+ Microseconds100 = 0b_0_001_0_000,
+ Microseconds200 = 0b_0_010_0_000,
+ Microseconds300 = 0b_0_011_0_000,
+ Microseconds400 = 0b_0_100_0_000,
+ Microseconds50 = 0b_0_101_0_000, // or may be 500μsecs?
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartConfigurations.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartConfigurations.cs
new file mode 100644
index 0000000..8211e61
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartConfigurations.cs
@@ -0,0 +1,76 @@
+// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+using System;
+
+namespace Smdn.Devices.BP35XX;
+
+/// <summary>
+/// A read-only structure that represents the configuration values relevant to UART, set and get by the <c>WUART</c> and <c>RUART</c> commands.
+/// </summary>
+/// <remarks>
+/// <para>See below for detailed specifications.</para>
+/// <list type="bullet">
+/// <item><description>'BP35A1コマンドリファレンス 3.32. WUART (プロダクト設定コマンド)'</description></item>
+/// <item><description>'BP35A1コマンドリファレンス 3.33. RUART (プロダクト設定コマンド)'</description></item>
+/// </list>
+/// </remarks>
+public readonly struct BP35UartConfigurations {
+ private const byte BaudRateMask = 0b_0_000_0_111;
+ // private const byte ReservedBitMask = 0b_0_000_1_000;
+ private const byte CharacterIntervalMask = 0b_0_111_0_000;
+ private const byte FlowControlMask = 0b_1_000_0_000;
+
+ internal byte Mode { get; }
+
+ public BP35UartBaudRate BaudRate => (BP35UartBaudRate)(Mode & BaudRateMask);
+ public BP35UartCharacterInterval CharacterInterval => (BP35UartCharacterInterval)(Mode & CharacterIntervalMask);
+ public BP35UartFlowControl FlowControl => (BP35UartFlowControl)(Mode & FlowControlMask);
+
+ internal BP35UartConfigurations(
+ byte mode
+ )
+ {
+ Mode = mode;
+ }
+
+ public BP35UartConfigurations(
+ BP35UartBaudRate baudRate,
+ BP35UartCharacterInterval characterInterval,
+ BP35UartFlowControl flowControl
+ )
+ {
+#if SYSTEM_ENUM_ISDEFINED_OF_TENUM
+ if (!Enum.IsDefined(baudRate))
+#else
+ if (!Enum.IsDefined(typeof(BP35UartBaudRate), baudRate))
+#endif
+ throw new ArgumentException($"undefined value of {nameof(BP35UartBaudRate)}", nameof(baudRate));
+
+#if SYSTEM_ENUM_ISDEFINED_OF_TENUM
+ if (!Enum.IsDefined(flowControl))
+#else
+ if (!Enum.IsDefined(typeof(BP35UartFlowControl), flowControl))
+#endif
+ throw new ArgumentException($"undefined value of {nameof(BP35UartFlowControl)}", nameof(flowControl));
+
+#if SYSTEM_ENUM_ISDEFINED_OF_TENUM
+ if (!Enum.IsDefined(characterInterval))
+#else
+ if (!Enum.IsDefined(typeof(BP35UartCharacterInterval), characterInterval))
+#endif
+ throw new ArgumentException($"undefined value of {nameof(BP35UartCharacterInterval)}", nameof(characterInterval));
+
+ Mode = (byte)((byte)baudRate | (byte)characterInterval | (byte)flowControl);
+ }
+
+ public void Deconstruct(
+ out BP35UartBaudRate baudRate,
+ out BP35UartCharacterInterval characterInterval,
+ out BP35UartFlowControl flowControl
+ )
+ {
+ baudRate = BaudRate;
+ characterInterval = CharacterInterval;
+ flowControl = FlowControl;
+ }
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartFlowControl.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartFlowControl.cs
new file mode 100644
index 0000000..b69bc38
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UartFlowControl.cs
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+namespace Smdn.Devices.BP35XX;
+
+/// <summary>
+/// An enumeration type representing the configuration values for the flow control in UART, to be set or get by the <c>WUART</c> and <c>RUART</c> commands.
+/// </summary>
+/// <remarks>
+/// <para>See below for detailed specifications.</para>
+/// <list type="bullet">
+/// <item><description>'BP35A1コマンドリファレンス 3.32. WUART (プロダクト設定コマンド)'</description></item>
+/// <item><description>'BP35A1コマンドリファレンス 3.33. RUART (プロダクト設定コマンド)'</description></item>
+/// </list>
+/// </remarks>
+#pragma warning disable CA1027
+public enum BP35UartFlowControl : byte {
+#pragma warning restore CA1027
+ Disabled = 0b_0_000_0_000, // default(BP35UartFlowControl)
+ Enabled = 0b_1_000_0_000,
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UdpReceiveDataFormat.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UdpReceiveDataFormat.cs
new file mode 100644
index 0000000..cd4e642
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/BP35UdpReceiveDataFormat.cs
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: 2021 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+
+namespace Smdn.Devices.BP35XX;
+
+/// <summary>
+/// An enumeration type representing the configuration values for the display format of the data part in ERXUDP event, to be set or get by the <c>WOPT</c> and <c>ROPT</c> commands.
+/// </summary>
+/// <remarks>
+/// <para>See below for detailed specifications.</para>
+/// <list type="bullet">
+/// <item><description>'BP35A1コマンドリファレンス 3.30. WOPT (プロダクト設定コマンド)'</description></item>
+/// <item><description>'BP35A1コマンドリファレンス 3.31. ROPT (プロダクト設定コマンド)'</description></item>
+/// </list>
+/// </remarks>
+#pragma warning disable CA1027
+public enum BP35UdpReceiveDataFormat : byte {
+#pragma warning restore CA1027
+ Binary = 0b_0000_0000,
+ HexAscii = 0b_0000_0001,
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/IBP35Configurations.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/IBP35Configurations.cs
new file mode 100644
index 0000000..457e941
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/IBP35Configurations.cs
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+using Smdn.Net.SkStackIP;
+
+namespace Smdn.Devices.BP35XX;
+
+public interface IBP35Configurations {
+ /// <summary>
+ /// Gets the <see cref="string"/> value that holds the serial port name for communicating with the device that implements the SKSTACK-IP protocol.
+ /// </summary>
+ string? SerialPortName { get; }
+
+ /// <summary>
+ /// Gets the <see cref="BP35UartBaudRate"/> value that specifies the baud rate of the serial port for communicating with the device.
+ /// </summary>
+ BP35UartBaudRate BaudRate { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether or not to attempt to load the configuration from flash memory during initialization.
+ /// </summary>
+ bool TryLoadFlashMemory { get; }
+
+ /// <summary>
+ /// Gets the value that specifies the format of the data part received in the event <c>ERXUDP</c>. See <see cref="SkStackClient.ERXUDPDataFormat"/>.
+ /// </summary>
+ /// <seealso cref="SkStackClient.ERXUDPDataFormat"/>
+ /// <seealso cref="SkStackERXUDPDataFormat"/>
+ SkStackERXUDPDataFormat ERXUDPDataFormat { get; }
+}
diff --git a/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/IBP35SerialPortStreamFactory.cs b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/IBP35SerialPortStreamFactory.cs
new file mode 100644
index 0000000..b6a192f
--- /dev/null
+++ b/src/Smdn.Devices.BP35XX/Smdn.Devices.BP35XX/IBP35SerialPortStreamFactory.cs
@@ -0,0 +1,9 @@
+// SPDX-FileCopyrightText: 2024 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+using System.IO;
+
+namespace Smdn.Devices.BP35XX;
+
+public interface IBP35SerialPortStreamFactory {
+ Stream CreateSerialPortStream(IBP35Configurations configurations);
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment