diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index 5c98b166..94ffee5b 100644
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -1,4 +1,4 @@
-name: .NET Core CI Build
+name: Stable Build
on:
workflow_dispatch:
@@ -24,7 +24,7 @@ jobs:
- name: Checkout Meadow.Core
uses: actions/checkout@v3
with:
- path: root # we have to put this in a sub-folder so we can pull Contracts/Units to a parent folder
+ path: Meadow.Core # we have to put this in a sub-folder so we can pull Contracts/Units to a parent folder
- name: Checkout Meadow.Foundation
# required for the FT232H driver, which is used by Meadow.Windows
@@ -62,4 +62,4 @@ jobs:
7.0.x
- name: Build Meadow.Core
- run: dotnet build -c Release root/source/Meadow.Core.sln
+ run: dotnet build -c Release Meadow.Core/source/Meadow.Core.sln
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..22f4d490
--- /dev/null
+++ b/README.md
@@ -0,0 +1,81 @@
+[](https://www.nuget.org/packages/Meadow)
+[](https://github.com/WildernessLabs/Meadow.Core/actions/workflows/ci-build.yml)
+[](license.txt)
+
+
+
+# Meadow.Core
+
+Welcome to the Meadow Core library GitHub repository! This library is the foundation of the Meadow platform and provides a powerful set of tools for creating connected devices. The Meadow Core library includes APIs for common protocols such as SPI, I2C, and UART, as well as APIs for networking, storage, and more. The library is written in C# and is designed to work with the Meadow F7 Microcontroller. With Meadow Core, you can quickly and easily build connected devices that are powerful, reliable, and secure. Join our community and start building with Meadow Core today!
+
+## Repositories
+
+### Dependencies
+
+If this repo is cloned, it will only build if the following repositories are cloned and stored at the same folder level:
+1. [Meadow.Units](https://github.com/WildernessLabs/Meadow.Units) contains a strong unitization into the entire stack of Meadow.
+2. [Meadow.Logging](https://github.com/WildernessLabs/Meadow.Logging) a lightweight logging library for embedded hardware.
+3. [Meadow.Contracts](https://github.com/WildernessLabs/Meadow.Contracts) contains the interfaces used by the entire Meadow stack.
+4. [MQTTnet](https://github.com/WildernessLabs/MQTTnet) is a high performance .NET library for MQTT based communication.
+
+### Samples
+
+Additionally, you might want to check out our repos with tons of samples with different levels of complexity and hardware requirements:
+1. [Meadow.Core.Samples](https://github.com/WildernessLabs/Meadow.Core.Samples) has all our sample projects that cover every feature Meadow has to offer with no extra peripherals required.
+1. [Meadow.Project.Samples](https://github.com/WildernessLabs/Meadow.Project.Samples) has en extensive collection of Meadow Projects using [Meadow.Foundation](https://github.com/WildernessLabs/Meadow.Foundation), our peripheral driver and hardware control libraries to make .NET IoT development plug-and-play.
+1. [Meadow.ProjectLab.Samples](https://github.com/WildernessLabs/Meadow.ProjectLab.Samples) contains project samples for the breadboardless rapid prototyping board [Project Lab](https://github.com/WildernessLabs/Meadow.ProjectLab).
+
+## Unified GPIO Architecture
+
+### Peripherals Must Support Pin and Port
+
+All peripherals must be able to be constructed with an `IPin` along with a device with the capabilities to configure a proper port (digital IO, analog, PWM, etc.) and a specific type of port such as `IDigitalOutputPort`, `IPwmPort`, `IAnalogInputPort`, etc.
+
+#### Analog Example
+
+```
+public class AnalogSensor
+{
+ protected IAnalogInputPort AnalogInputPort { get; set; }
+
+ public AnalogSensor (IAnalogInputController device, IPin pin)
+ : this (device.CreateAnalogInputPort(pin)) { }
+
+ public AnalogSensor (IAnalogInputPort analogInputPort)
+ {
+ AnalogInputPort = analogInputPort;
+ }
+}
+```
+#### Digital Example
+
+```
+public class Led
+{
+ protected IDigitalOutputPort DigitalOutputPort { get; set; }
+
+ public Led (IDigitalOutputController device, IPin pin)
+ : this(device.CreateDigitalOutputPort(pin)) { }
+
+ public Led (IDigitalPort digitalOutputPort)
+ {
+ DigitalOutputPort = digitalOutputPort;
+ }
+}
+```
+#### PWM Example
+
+```
+public class PwmLed
+{
+ protected IPwmPort PwmPort { get; set; }
+
+ public PwmLed (IPwmOutputController device, IPin pin)
+ : this (device.CreatePwmPort(pin)) { }
+
+ public PwmLed (IPwmPort pwmPort)
+ {
+ PwmPort = pwmPort;
+ }
+}
+```
diff --git a/Readme.md b/Readme.md
index 2242bdb2..22f4d490 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,64 +1,81 @@
-# Meadow Core Library
+[](https://www.nuget.org/packages/Meadow)
+[](https://github.com/WildernessLabs/Meadow.Core/actions/workflows/ci-build.yml)
+[](license.txt)
-## Repo Status
+
-[](https://github.com/WildernessLabs/Meadow.Core/actions/workflows/ci-build.yml)
+# Meadow.Core
-# Framework Design
+Welcome to the Meadow Core library GitHub repository! This library is the foundation of the Meadow platform and provides a powerful set of tools for creating connected devices. The Meadow Core library includes APIs for common protocols such as SPI, I2C, and UART, as well as APIs for networking, storage, and more. The library is written in C# and is designed to work with the Meadow F7 Microcontroller. With Meadow Core, you can quickly and easily build connected devices that are powerful, reliable, and secure. Join our community and start building with Meadow Core today!
-## Unified GPIO Architecture
+## Repositories
-### Peripherals Must Support Pin and Port
+### Dependencies
-All peripherals must be able to be constructed with either a an `IPort` or an `IPin`:
+If this repo is cloned, it will only build if the following repositories are cloned and stored at the same folder level:
+1. [Meadow.Units](https://github.com/WildernessLabs/Meadow.Units) contains a strong unitization into the entire stack of Meadow.
+2. [Meadow.Logging](https://github.com/WildernessLabs/Meadow.Logging) a lightweight logging library for embedded hardware.
+3. [Meadow.Contracts](https://github.com/WildernessLabs/Meadow.Contracts) contains the interfaces used by the entire Meadow stack.
+4. [MQTTnet](https://github.com/WildernessLabs/MQTTnet) is a high performance .NET library for MQTT based communication.
-```
-protected IDigitalPort _port;
+### Samples
-public Relay (IDigitalPin pin)
- : this (new DigitalPort(pin)) { }
-
-public Relay (IDigitalPort port) {
- _port = port;
-}
-```
+Additionally, you might want to check out our repos with tons of samples with different levels of complexity and hardware requirements:
+1. [Meadow.Core.Samples](https://github.com/WildernessLabs/Meadow.Core.Samples) has all our sample projects that cover every feature Meadow has to offer with no extra peripherals required.
+1. [Meadow.Project.Samples](https://github.com/WildernessLabs/Meadow.Project.Samples) has en extensive collection of Meadow Projects using [Meadow.Foundation](https://github.com/WildernessLabs/Meadow.Foundation), our peripheral driver and hardware control libraries to make .NET IoT development plug-and-play.
+1. [Meadow.ProjectLab.Samples](https://github.com/WildernessLabs/Meadow.ProjectLab.Samples) contains project samples for the breadboardless rapid prototyping board [Project Lab](https://github.com/WildernessLabs/Meadow.ProjectLab).
+## Unified GPIO Architecture
-### Peripherals Must Only Accept Correct Port Type
+### Peripherals Must Support Pin and Port
-e.g. `IDigitalPin` or `IPWMPin`
+All peripherals must be able to be constructed with an `IPin` along with a device with the capabilities to configure a proper port (digital IO, analog, PWM, etc.) and a specific type of port such as `IDigitalOutputPort`, `IPwmPort`, `IAnalogInputPort`, etc.
#### Analog Example
```
-public class AnalogSensor {
- public AnalogSensor (IAnalogPin pin) { ... }
- public AnalogSensor (IAnalogPort port) { ... }
+public class AnalogSensor
+{
+ protected IAnalogInputPort AnalogInputPort { get; set; }
+
+ public AnalogSensor (IAnalogInputController device, IPin pin)
+ : this (device.CreateAnalogInputPort(pin)) { }
+
+ public AnalogSensor (IAnalogInputPort analogInputPort)
+ {
+ AnalogInputPort = analogInputPort;
+ }
}
```
#### Digital Example
```
-public class LED {
- public PwmLed (IDigitalPin pin) { ... }
- public PwmLed (IDigitalPort port { ... }
+public class Led
+{
+ protected IDigitalOutputPort DigitalOutputPort { get; set; }
+
+ public Led (IDigitalOutputController device, IPin pin)
+ : this(device.CreateDigitalOutputPort(pin)) { }
+
+ public Led (IDigitalPort digitalOutputPort)
+ {
+ DigitalOutputPort = digitalOutputPort;
+ }
}
```
#### PWM Example
```
-public class PwmLed {
- public PwmLed (IPwmPin pin) { ... }
- public PwmLed (IPwmPort port { ... }
-}
-```
-# Publishing Nuget Packages
-
-CI builds are [setup in Jenkins](http://jenkins.wildernesslabs.co/job/Meadow.Core/).
-To trigger a new build:
-- Go to project properties in VS 2017
-- in the `Package` tab, increment either the MAJOR or MINOR `Package version`.
+public class PwmLed
+{
+ protected IPwmPort PwmPort { get; set; }
-The CI job will pick up the changes, pack, and push the Nuget package.
+ public PwmLed (IPwmOutputController device, IPin pin)
+ : this (device.CreatePwmPort(pin)) { }
-[](http://jenkins.wildernesslabs.co/job/Meadow.Core/)
+ public PwmLed (IPwmPort pwmPort)
+ {
+ PwmPort = pwmPort;
+ }
+}
+```
diff --git a/design/banner.jpg b/design/banner.jpg
new file mode 100644
index 00000000..ccd676ee
Binary files /dev/null and b/design/banner.jpg differ
diff --git a/doc/publish-profile-settings.png b/doc/publish-profile-settings.png
new file mode 100644
index 00000000..88494897
Binary files /dev/null and b/doc/publish-profile-settings.png differ
diff --git a/docfx/.gitignore b/docfx/.gitignore
deleted file mode 100644
index 4378419e..00000000
--- a/docfx/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-###############
-# folder #
-###############
-/**/DROP/
-/**/TEMP/
-/**/packages/
-/**/bin/
-/**/obj/
-_site
diff --git a/docfx/api/.gitignore b/docfx/api/.gitignore
deleted file mode 100644
index e8079a3b..00000000
--- a/docfx/api/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-###############
-# temp file #
-###############
-*.yml
-.manifest
diff --git a/docfx/api/index.md b/docfx/api/index.md
deleted file mode 100644
index 78dc9c00..00000000
--- a/docfx/api/index.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# PLACEHOLDER
-TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*!
diff --git a/docfx/articles/intro.md b/docfx/articles/intro.md
deleted file mode 100644
index c0478ced..00000000
--- a/docfx/articles/intro.md
+++ /dev/null
@@ -1 +0,0 @@
-# Add your introductions here!
diff --git a/docfx/articles/toc.yml b/docfx/articles/toc.yml
deleted file mode 100644
index ff89ef1f..00000000
--- a/docfx/articles/toc.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-- name: Introduction
- href: intro.md
diff --git a/docfx/docfx.json b/docfx/docfx.json
deleted file mode 100644
index ba0b27ae..00000000
--- a/docfx/docfx.json
+++ /dev/null
@@ -1,65 +0,0 @@
-{
- "metadata": [
- {
- "src": [
- {
- "files": [
- "**/**.csproj"
- ],
- "src": "../source"
- }
- ],
- "dest": "api",
- "disableGitFeatures": false,
- "disableDefaultFilter": false
- }
- ],
- "build": {
- "content": [
- {
- "files": [
- "api/**.yml",
- "api/index.md"
- ]
- },
- {
- "files": [
- "articles/**.md",
- "articles/**/toc.yml",
- "toc.yml",
- "*.md"
- ]
- }
- ],
- "resource": [
- {
- "files": [
- "images/**"
- ]
- }
- ],
- "overwrite": [
- {
- "files": [
- "apidoc/**.md"
- ],
- "exclude": [
- "obj/**",
- "_site/**"
- ]
- }
- ],
- "dest": "_site",
- "globalMetadataFiles": [],
- "fileMetadataFiles": [],
- "template": [
- "default"
- ],
- "postProcessors": [],
- "markdownEngineName": "markdig",
- "noLangKeyword": false,
- "keepFileLink": false,
- "cleanupCacheHistory": false,
- "disableGitFeatures": false
- }
-}
\ No newline at end of file
diff --git a/docfx/index.md b/docfx/index.md
deleted file mode 100644
index 3ae25063..00000000
--- a/docfx/index.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# This is the **HOMEPAGE**.
-Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files.
-## Quick Start Notes:
-1. Add images to the *images* folder if the file is referencing an image.
diff --git a/docfx/toc.yml b/docfx/toc.yml
deleted file mode 100644
index 59f80104..00000000
--- a/docfx/toc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-- name: Articles
- href: articles/
-- name: Api Documentation
- href: api/
- homepage: api/index.md
diff --git a/source/Meadow.Core.sln b/source/Meadow.Core.sln
index ec7fa92b..b8e7270d 100644
--- a/source/Meadow.Core.sln
+++ b/source/Meadow.Core.sln
@@ -4,26 +4,52 @@ VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Core", "Meadow.Core\Meadow.Core.csproj", "{B13C7BDD-77F5-45F8-BDB9-6CD368C67410}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.F7", "Meadow.F7\Meadow.F7.csproj", "{68862169-C332-48DF-BE45-8A27AAA365B3}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7BCFC6D6-228E-4C93-8145-EAD121AA8587}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{7BCFC6D6-228E-4C93-8145-EAD121AA8587}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E5D88D38-57E0-46E0-913A-0CCE91826F18}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_refs", "_refs", "{E5D88D38-57E0-46E0-913A-0CCE91826F18}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Contracts", "..\..\Meadow.Contracts\Source\Meadow.Contracts\Meadow.Contracts.csproj", "{E6453AC7-85B7-4F5C-BA23-5DC8D7BB60C8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Units", "..\..\Meadow.Units\Source\Meadow.Units\Meadow.Units.csproj", "{08343570-1C97-429D-A0C9-6A9822412028}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Units.Tests", "..\..\Meadow.Units\Source\Meadow.Units.Tests\Meadow.Units.Tests.csproj", "{A3467CEB-5392-4166-93AF-C0A1C20DF0F8}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Unit.Tests", "Tests\Core.Unit.Tests\Core.Unit.Tests.csproj", "{93DFC566-A413-4039-A0C1-90A42A356386}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Update", "Meadow.Update\Meadow.Update.csproj", "{91A73126-30FF-4A8E-BFC6-750053BD51D5}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "..\..\MQTTnet\Source\MQTTnet\MQTTnet.csproj", "{7E26CCF1-DDB2-4A49-93AA-08F2612A47A8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Logging", "..\..\Meadow.Logging\Source\Meadow.Logging\lib\Meadow.Logging.csproj", "{BC491B8D-D6EF-4AE6-B11D-1A117B6A350D}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "implementations", "implementations", "{CC469899-D08C-417C-A908-C052CA155BEC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.F7", "implementations\f7\Meadow.F7\Meadow.F7.csproj", "{E377CD5F-1E6C-4FFD-BA70-F17620A9273E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Simulation", "implementations\simulation\Meadow.Simulation\Meadow.Simulation.csproj", "{5543D2D5-FA80-43CE-8844-40510A7DBED6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Simulation.Unit.Tests", "implementations\simulation\Meadow.Simulation.Unit.Tests\Meadow.Simulation.Unit.Tests.csproj", "{40647101-F527-478F-888E-8D2041A8A614}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "linux", "linux", "{B187C117-A16E-4A75-B862-3CA7DCA464B8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Linux", "implementations\linux\Meadow.Linux\Meadow.Linux.csproj", "{4DEA563C-50EE-4729-ACA4-C77A251655BC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Gtk", "implementations\linux\Meadow.Gtk\Meadow.Gtk.csproj", "{07AE65D7-86F0-45DD-97E8-F4427E24B031}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Graphics.MicroGraphics", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Libraries_and_Frameworks\Graphics.MicroGraphics\Driver\Graphics.MicroGraphics.csproj", "{A58654BF-7738-4367-844C-234A499BF1F2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "f7", "f7", "{716A47C0-59CB-485F-B6C8-57F073F5B633}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "simulation", "simulation", "{89E8BFD9-BC4E-4409-8AB5-9B1FD0A72CAB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "windows", "windows", "{BF8A3E34-3958-4D8D-975A-353EAC05B87A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Windows", "implementations\windows\Meadow.Windows\Meadow.Windows.csproj", "{893B86CC-CB07-4C75-8D6A-6EC7B95277D7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICs.IOExpanders.Ft232h", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\ICs.IOExpanders.Ft232h\Driver\ICs.IOExpanders.Ft232h.csproj", "{777D9516-489E-45E7-B116-C1F808031B98}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{C064E02A-2AFF-4650-8305-C109214D0FD1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Foundation.Core", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Core\Meadow.Foundation.Core.csproj", "{268778C7-AEFD-42BD-B99C-25954DA67445}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.WinForms", "implementations\windows\Meadow.WinForms\Meadow.WinForms.csproj", "{223B48B5-EBFD-491E-AEFE-8A7B83084A78}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Core|Any CPU = Core|Any CPU
@@ -37,15 +63,6 @@ Global
{B13C7BDD-77F5-45F8-BDB9-6CD368C67410}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B13C7BDD-77F5-45F8-BDB9-6CD368C67410}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B13C7BDD-77F5-45F8-BDB9-6CD368C67410}.Release|Any CPU.Build.0 = Release|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Core|Any CPU.ActiveCfg = Debug|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Core|Any CPU.Build.0 = Debug|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Core|Any CPU.Deploy.0 = Debug|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Release|Any CPU.Build.0 = Release|Any CPU
- {68862169-C332-48DF-BE45-8A27AAA365B3}.Release|Any CPU.Deploy.0 = Release|Any CPU
{E6453AC7-85B7-4F5C-BA23-5DC8D7BB60C8}.Core|Any CPU.ActiveCfg = Debug|Any CPU
{E6453AC7-85B7-4F5C-BA23-5DC8D7BB60C8}.Core|Any CPU.Build.0 = Debug|Any CPU
{E6453AC7-85B7-4F5C-BA23-5DC8D7BB60C8}.Core|Any CPU.Deploy.0 = Debug|Any CPU
@@ -64,24 +81,12 @@ Global
{08343570-1C97-429D-A0C9-6A9822412028}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08343570-1C97-429D-A0C9-6A9822412028}.Release|Any CPU.Build.0 = Release|Any CPU
{08343570-1C97-429D-A0C9-6A9822412028}.Release|Any CPU.Deploy.0 = Release|Any CPU
- {A3467CEB-5392-4166-93AF-C0A1C20DF0F8}.Core|Any CPU.ActiveCfg = Debug|Any CPU
- {A3467CEB-5392-4166-93AF-C0A1C20DF0F8}.Core|Any CPU.Build.0 = Debug|Any CPU
- {A3467CEB-5392-4166-93AF-C0A1C20DF0F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A3467CEB-5392-4166-93AF-C0A1C20DF0F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A3467CEB-5392-4166-93AF-C0A1C20DF0F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A3467CEB-5392-4166-93AF-C0A1C20DF0F8}.Release|Any CPU.Build.0 = Release|Any CPU
{93DFC566-A413-4039-A0C1-90A42A356386}.Core|Any CPU.ActiveCfg = Debug|Any CPU
{93DFC566-A413-4039-A0C1-90A42A356386}.Core|Any CPU.Build.0 = Debug|Any CPU
{93DFC566-A413-4039-A0C1-90A42A356386}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{93DFC566-A413-4039-A0C1-90A42A356386}.Debug|Any CPU.Build.0 = Debug|Any CPU
{93DFC566-A413-4039-A0C1-90A42A356386}.Release|Any CPU.ActiveCfg = Release|Any CPU
{93DFC566-A413-4039-A0C1-90A42A356386}.Release|Any CPU.Build.0 = Release|Any CPU
- {91A73126-30FF-4A8E-BFC6-750053BD51D5}.Core|Any CPU.ActiveCfg = Debug|Any CPU
- {91A73126-30FF-4A8E-BFC6-750053BD51D5}.Core|Any CPU.Build.0 = Debug|Any CPU
- {91A73126-30FF-4A8E-BFC6-750053BD51D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {91A73126-30FF-4A8E-BFC6-750053BD51D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {91A73126-30FF-4A8E-BFC6-750053BD51D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {91A73126-30FF-4A8E-BFC6-750053BD51D5}.Release|Any CPU.Build.0 = Release|Any CPU
{7E26CCF1-DDB2-4A49-93AA-08F2612A47A8}.Core|Any CPU.ActiveCfg = Debug|Any CPU
{7E26CCF1-DDB2-4A49-93AA-08F2612A47A8}.Core|Any CPU.Build.0 = Debug|Any CPU
{7E26CCF1-DDB2-4A49-93AA-08F2612A47A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -94,6 +99,81 @@ Global
{BC491B8D-D6EF-4AE6-B11D-1A117B6A350D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC491B8D-D6EF-4AE6-B11D-1A117B6A350D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC491B8D-D6EF-4AE6-B11D-1A117B6A350D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Core|Any CPU.Deploy.0 = Debug|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {5543D2D5-FA80-43CE-8844-40510A7DBED6}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {5543D2D5-FA80-43CE-8844-40510A7DBED6}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {5543D2D5-FA80-43CE-8844-40510A7DBED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5543D2D5-FA80-43CE-8844-40510A7DBED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5543D2D5-FA80-43CE-8844-40510A7DBED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5543D2D5-FA80-43CE-8844-40510A7DBED6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {40647101-F527-478F-888E-8D2041A8A614}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {40647101-F527-478F-888E-8D2041A8A614}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {40647101-F527-478F-888E-8D2041A8A614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {40647101-F527-478F-888E-8D2041A8A614}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {40647101-F527-478F-888E-8D2041A8A614}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {40647101-F527-478F-888E-8D2041A8A614}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Core|Any CPU.Deploy.0 = Debug|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {07AE65D7-86F0-45DD-97E8-F4427E24B031}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {07AE65D7-86F0-45DD-97E8-F4427E24B031}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {07AE65D7-86F0-45DD-97E8-F4427E24B031}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {07AE65D7-86F0-45DD-97E8-F4427E24B031}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {07AE65D7-86F0-45DD-97E8-F4427E24B031}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {07AE65D7-86F0-45DD-97E8-F4427E24B031}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Core|Any CPU.Deploy.0 = Debug|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A58654BF-7738-4367-844C-234A499BF1F2}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {893B86CC-CB07-4C75-8D6A-6EC7B95277D7}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {893B86CC-CB07-4C75-8D6A-6EC7B95277D7}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {893B86CC-CB07-4C75-8D6A-6EC7B95277D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {893B86CC-CB07-4C75-8D6A-6EC7B95277D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {893B86CC-CB07-4C75-8D6A-6EC7B95277D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {893B86CC-CB07-4C75-8D6A-6EC7B95277D7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Core|Any CPU.Deploy.0 = Debug|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Release|Any CPU.Build.0 = Release|Any CPU
+ {777D9516-489E-45E7-B116-C1F808031B98}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Core|Any CPU.Deploy.0 = Debug|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Release|Any CPU.Build.0 = Release|Any CPU
+ {268778C7-AEFD-42BD-B99C-25954DA67445}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {223B48B5-EBFD-491E-AEFE-8A7B83084A78}.Core|Any CPU.ActiveCfg = Debug|Any CPU
+ {223B48B5-EBFD-491E-AEFE-8A7B83084A78}.Core|Any CPU.Build.0 = Debug|Any CPU
+ {223B48B5-EBFD-491E-AEFE-8A7B83084A78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {223B48B5-EBFD-491E-AEFE-8A7B83084A78}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {223B48B5-EBFD-491E-AEFE-8A7B83084A78}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {223B48B5-EBFD-491E-AEFE-8A7B83084A78}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -101,10 +181,23 @@ Global
GlobalSection(NestedProjects) = preSolution
{E6453AC7-85B7-4F5C-BA23-5DC8D7BB60C8} = {E5D88D38-57E0-46E0-913A-0CCE91826F18}
{08343570-1C97-429D-A0C9-6A9822412028} = {E5D88D38-57E0-46E0-913A-0CCE91826F18}
- {A3467CEB-5392-4166-93AF-C0A1C20DF0F8} = {7BCFC6D6-228E-4C93-8145-EAD121AA8587}
{93DFC566-A413-4039-A0C1-90A42A356386} = {7BCFC6D6-228E-4C93-8145-EAD121AA8587}
{7E26CCF1-DDB2-4A49-93AA-08F2612A47A8} = {E5D88D38-57E0-46E0-913A-0CCE91826F18}
{BC491B8D-D6EF-4AE6-B11D-1A117B6A350D} = {E5D88D38-57E0-46E0-913A-0CCE91826F18}
+ {E377CD5F-1E6C-4FFD-BA70-F17620A9273E} = {716A47C0-59CB-485F-B6C8-57F073F5B633}
+ {5543D2D5-FA80-43CE-8844-40510A7DBED6} = {89E8BFD9-BC4E-4409-8AB5-9B1FD0A72CAB}
+ {40647101-F527-478F-888E-8D2041A8A614} = {7BCFC6D6-228E-4C93-8145-EAD121AA8587}
+ {B187C117-A16E-4A75-B862-3CA7DCA464B8} = {CC469899-D08C-417C-A908-C052CA155BEC}
+ {4DEA563C-50EE-4729-ACA4-C77A251655BC} = {B187C117-A16E-4A75-B862-3CA7DCA464B8}
+ {07AE65D7-86F0-45DD-97E8-F4427E24B031} = {B187C117-A16E-4A75-B862-3CA7DCA464B8}
+ {A58654BF-7738-4367-844C-234A499BF1F2} = {E5D88D38-57E0-46E0-913A-0CCE91826F18}
+ {716A47C0-59CB-485F-B6C8-57F073F5B633} = {CC469899-D08C-417C-A908-C052CA155BEC}
+ {89E8BFD9-BC4E-4409-8AB5-9B1FD0A72CAB} = {CC469899-D08C-417C-A908-C052CA155BEC}
+ {BF8A3E34-3958-4D8D-975A-353EAC05B87A} = {CC469899-D08C-417C-A908-C052CA155BEC}
+ {893B86CC-CB07-4C75-8D6A-6EC7B95277D7} = {BF8A3E34-3958-4D8D-975A-353EAC05B87A}
+ {777D9516-489E-45E7-B116-C1F808031B98} = {E5D88D38-57E0-46E0-913A-0CCE91826F18}
+ {268778C7-AEFD-42BD-B99C-25954DA67445} = {E5D88D38-57E0-46E0-913A-0CCE91826F18}
+ {223B48B5-EBFD-491E-AEFE-8A7B83084A78} = {BF8A3E34-3958-4D8D-975A-353EAC05B87A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {346406ED-19A9-40FB-A3F7-14139F44C0C8}
diff --git a/source/Meadow.Core/Bases/App.cs b/source/Meadow.Core/Bases/App.cs
index ed79fc5b..7e2793ca 100644
--- a/source/Meadow.Core/Bases/App.cs
+++ b/source/Meadow.Core/Bases/App.cs
@@ -68,6 +68,6 @@ public void OnUpdateComplete(Version oldVersion, out bool rollbackUpdate)
///
public static CancellationToken Abort { get; protected set; }
- public async virtual ValueTask DisposeAsync() { return; }
+ public virtual ValueTask DisposeAsync() { return new ValueTask(Task.CompletedTask); }
}
}
\ No newline at end of file
diff --git a/source/Meadow.Core/CircularBuffer.cs b/source/Meadow.Core/CircularBuffer.cs
deleted file mode 100644
index 8c477a72..00000000
--- a/source/Meadow.Core/CircularBuffer.cs
+++ /dev/null
@@ -1,551 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace Meadow
-{
- ///
- ///
- ///
- ///
- public class CircularBuffer : IEnumerable
- {
- public event EventHandler ItemAdded = delegate { };
-
- // TODO: this should probably be Span
- private T[] _list;
- private object _syncRoot = new object();
- private int _head = 0;
- private int _tail = 0;
- private bool _highwaterExceeded = false;
- private bool _lowwaterExceeded = true;
- private AutoResetEvent _addedResetEvent;
-
- ///
- /// Fires when an element is added to the buffer when it is already full
- ///
- public event EventHandler Overrun = delegate { };
- ///
- /// Fires when an attempt is made to remove an item from an empty buffer
- ///
- public event EventHandler Underrun = delegate { };
- ///
- /// Fires when the number of elements reaches a non-zero HighWaterLevel value on an Enqueue call. This event fires only once when passing upward across the boundary.
- ///
- public event EventHandler HighWater = delegate { };
- ///
- /// Fires when the number of elements reaches a non-zero LowWaterLevel value on a Remove call. This event fires only once when passing downward across the boundary.
- ///
- public event EventHandler LowWater = delegate { };
- ///
- /// Gets the maximum number of elements the buffer can hold.
- ///
- public int MaxElements { get; private set; }
- ///
- /// When set to true, overrun conditions will throw an exception. Default is false.
- ///
- public bool ExceptOnOverrun { get; set; }
- ///
- /// When set to true, underrun conditions will throw an exception. Default is false.
- ///
- public bool ExceptOnUnderrun { get; set; }
- ///
- /// Returns true when an overrun condition has occurred.
- ///
- ///
- /// The buffer will never reset this value except when Clear is called. It is up to the consumer to set this back to false if desired.
- ///
- public bool HasOverrun { get; set; }
- ///
- /// Returns true when an underrun condition has occurred.
- ///
- ///
- /// The buffer will never reset this value except when Clear is called. It is up to the consumer to set this back to false if desired.
- ///
- public bool HasUnderrun { get; set; }
- ///
- /// Returns true if the buffer's Count equals its MaxEleemnts.
- ///
- public bool IsFull { get; private set; }
-
- public CircularBuffer(int maxElements)
- {
- _addedResetEvent = new AutoResetEvent(false);
- MaxElements = maxElements;
- _list = new T[MaxElements];
- }
-
- ///
- /// Empties all elements from the buffer
- ///
- public void Clear()
- {
- lock (_syncRoot)
- {
- _head = 0;
- _tail = 0;
- _highwaterExceeded = false;
- _lowwaterExceeded = true;
- IsFull = false;
- HasOverrun = false;
- HasUnderrun = false;
- }
- }
-
- ///
- /// Gets the current count of elements in the buffer
- ///
- public int Count
- {
- get
- {
- lock (_syncRoot)
- {
- if (IsFull) return MaxElements;
-
- if (_head == _tail) return 0;
-
- // special case for head at the "end" (which is also the beginning)
- if (_head == 0)
- {
- return MaxElements - _tail;
- }
-
- if (_head > _tail)
- {
- return _head - _tail;
- }
-
- return MaxElements - _tail + _head;
- }
- }
- }
-
- ///
- /// The HighWater event will fire when the buffer contains this many (or more) elements.
- ///
- ///
- /// Set the value to zero (default) to disable high-water notifications
- ///
- public int HighWaterLevel { get; set; }
-
- ///
- /// The LowWater event will fire when the buffer contains this many (or less) elements.
- ///
- ///
- /// Set the value to zero (default) to disable low-water notifications
- ///
- public int LowWaterLevel { get; set; }
-
- private void IncrementTail()
- {
- _tail++;
- if (_tail >= MaxElements)
- {
- _tail = 0;
- }
- }
-
- private void IncrementHead()
- {
- _head++;
- if (_head >= MaxElements)
- {
- _head = 0;
- }
-
- if (_head == _tail)
- {
- IsFull = true;
- }
- }
-
- public void Append(IEnumerable items)
- {
- foreach (var i in items)
- {
- Append(i);
- }
- }
-
- public void Append(T[] items, int offset, int count)
- {
- for (int i = offset; i < offset + count; i++)
- {
- Append(items[i]);
- }
- }
-
- // TODO: not sure why i can't do this. but LINQ adds Append methods.
- //public void Append(T element)
- //{
- // this.Enqueue(element);
- //}
-
- //public void Append(T element)
- //{
- // this.Enqueue(element);
- //}
-
- //public void Append(IEnumerable items)
- //{
- // this.Enqueue(items);
- //}
-
- ///
- /// Adds an element to the head of the buffer
- ///
- ///
- ///
- /// If the buffer is full and Enqueue is called, the new item will be successfully added to the buffer and the tail (oldest) item will be automatically removed
- ///
- public void Append(T item)
- {
- lock (_syncRoot)
- {
- if (IsFull)
- {
- // drop the tail item
- IncrementTail();
-
- // notify the consumer
- OnOverrun();
- }
-
- // put the new item in the list
- _list[_head] = item;
-
- IncrementHead();
-
- if ((HighWaterLevel > 0) && (Count >= HighWaterLevel))
- {
- if (!_highwaterExceeded)
- {
- _highwaterExceeded = true;
- HighWater?.Invoke(this, EventArgs.Empty);
- }
- }
-
- if ((LowWaterLevel > 0) && (Count > LowWaterLevel))
- {
- _lowwaterExceeded = false;
- }
-
- // do notifications
- _addedResetEvent.Set();
- ItemAdded?.Invoke(this, EventArgs.Empty);
- }
- }
-
- public bool AppendWaitOne(int millisecondsTimeout)
- {
- return _addedResetEvent.WaitOne(millisecondsTimeout);
- }
-
- ///
- /// Removes the element from the tail of the buffer, if one exists
- ///
- ///
- public T? Remove()
- {
- return GetOldest(true);
- }
-
- ///
- /// Returns the element currently at the head of the buffer, if one exists, without removing it
- ///
- ///
- public T? Peek()
- {
- return GetOldest(false);
- }
-
- private T? GetOldest(bool remove)
- {
- lock (_syncRoot)
- {
- if ((Count == 0) && !(IsFull))
- {
- OnUnderrun();
- return default;
- }
-
- T item = _list[_tail];
-
- if (remove)
- {
- IncrementTail();
- IsFull = false;
-
- if ((HighWaterLevel > 0) && (Count < HighWaterLevel))
- {
- _highwaterExceeded = false;
- }
-
- if ((LowWaterLevel > 0) && (Count <= LowWaterLevel))
- {
- if (!_lowwaterExceeded)
- {
- _lowwaterExceeded = true;
- LowWater?.Invoke(this, EventArgs.Empty);
- }
- }
- }
-
- return item;
- }
- }
-
- public virtual void OnOverrun()
- {
- HasOverrun = true;
-
- if (ExceptOnOverrun)
- {
- throw new BufferException("Overrun");
- }
- Overrun?.Invoke(this, EventArgs.Empty);
- }
-
- public virtual void OnUnderrun()
- {
- HasUnderrun = true;
-
- if (ExceptOnUnderrun)
- {
- throw new BufferException("Underrun");
- }
- Underrun?.Invoke(this, EventArgs.Empty);
- }
-
- ///
- /// Find the next element that matches the provided function criteria starting with the head item.
- ///
- ///
- /// The value to return if find function finds nothing
- ///
- public T? Last(Func findFunction, T? defaultValue = default)
- {
- lock (_syncRoot)
- {
- int index = 0;
- if (_head > 0)
- {
- index = _head - 1;
- }
-
- for (int i = 0; i < Count; i++)
- {
- T item = _list[index];
- if (findFunction(item))
- {
- return item;
- }
- if (--index < 0) index = MaxElements - 1;
- }
-
- return defaultValue;
- }
- }
-
- ///
- /// Find the next element that matches the provided function criteria starting with the tail item.
- ///
- ///
- /// The value to return if find function finds nothing
- ///
- public T? First(Func findFunction, T? defaultValue = default)
- {
- lock (_syncRoot)
- {
- int index = _tail;
-
- for (int i = 0; i < Count; i++)
- {
- T item = _list[index];
- if (findFunction(item))
- {
- return item;
- }
- if (++index >= MaxElements - 1) index = 0;
- }
-
- return defaultValue;
- }
- }
-
- ///
- /// Determine if the buffer contains a specified value
- ///
- ///
- public bool Contains(T searchFor)
- {
- if (_list == null) return false;
-
- lock (_syncRoot)
- {
- // we don't want to enumerate values outside of our "valid" range
- for (int i = 0; i < Count; i++)
- {
- int index = _tail + i;
-
- if ((_head <= _tail) && (index >= MaxElements))
- {
- index -= MaxElements;
- }
-
- if (_list[index]?.Equals(searchFor) ?? false) return true;
- }
-
- return false;
- }
- }
-
- //public bool Contains(T[] pattern)
- //{
- // int patternLength = pattern.Length;
- // //int totalLength = Count;
- // T firstMatch = pattern[0];
-
- // for (int i = 0; i < Count; i++) {
- // // calculate the index from the head
- // int index = _tail = i;
- // if ((_head <= _tail) && (index >= MaxElements)) {
- // index -= MaxElements;
- // }
-
- // if (firstMatch == source[i] && Count - i >= patternLength) {
-
- // }
- // }
-
- // return false;
- //}
-
- ///
- /// Removes the requested number of elements from the buffer
- ///
- ///
- ///
- ///
- /// Similar to the Take() Linq method, if the buffer contains less items than requested, and empty array of items is returned and no items are Removed
- ///
- public T?[] Remove(int count)
- {
- if (Count < count) return new T[] { };
-
- var result = new T?[count];
-
- lock (_syncRoot)
- {
- for (int i = 0; i < count; i++)
- {
- result[i] = Remove();
- }
- }
-
- return result;
- }
-
- public int MoveItemsTo(T[] destination, int index, int count)
- {
- if (count <= 0) { return 0; }
-
- try
- {
- lock (_syncRoot)
- {
- // how many are we moving?
- // move from current toward the tail
- var actual = (count > Count) ? Count : count;
- var tailToEnd = _list.Length - _tail;
-
- if ((_tail < _head)
- || (_tail == 0 && IsFull)
- || (tailToEnd >= actual))
- {
- // the data is linear, just copy
- Array.Copy(_list, _tail, destination, index, actual);
-
- // move the tail pointer
- _tail += actual;
- }
- else
- {
- // there's a data wrap
- // copy from here to the end
- Array.Copy(_list, _tail, destination, index, tailToEnd);
- // now copy from the start (tail == 0) the remaining data
- _tail = 0;
- var remaining = actual - tailToEnd;
- Array.Copy(_list, _tail, destination, tailToEnd + index, remaining);
-
- // move the tail pointer
- _tail = remaining;
- }
-
- IsFull = false;
- return actual;
- }
- }
- finally
- {
- if ((LowWaterLevel > 0) && (Count <= LowWaterLevel))
- {
- if (!_lowwaterExceeded)
- {
- _lowwaterExceeded = true;
- LowWater?.Invoke(this, EventArgs.Empty);
- }
- }
- }
- }
-
- public T this[int index]
- {
- get
- {
- lock (_syncRoot)
- {
- int i = _tail + index;
-
- if ((_head <= _tail) && (i >= MaxElements))
- {
- i -= MaxElements;
- }
-
- return _list[i];
- }
- }
- }
-
- public IEnumerator GetEnumerator()
- {
- // we don't want to enumerate values outside of our "valid" range
- for (int i = 0; i < Count; i++)
- {
- int index = _tail + i;
-
- if ((_head <= _tail) && (index >= MaxElements))
- {
- index -= MaxElements;
- }
-
- yield return _list[index];
- }
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- public class BufferException : Exception
- {
- public BufferException(string message)
- : base(message)
- {
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/InterruptGroupInUseException.cs b/source/Meadow.Core/Exceptions/InterruptGroupInUseException.cs
similarity index 100%
rename from source/Meadow.Core/Hardware/InterruptGroupInUseException.cs
rename to source/Meadow.Core/Exceptions/InterruptGroupInUseException.cs
diff --git a/source/Meadow.Core/Hardware/PortInUseException.cs b/source/Meadow.Core/Exceptions/PortInUseException.cs
similarity index 100%
rename from source/Meadow.Core/Hardware/PortInUseException.cs
rename to source/Meadow.Core/Exceptions/PortInUseException.cs
diff --git a/source/Meadow.Core/ExtensionMethods.cs b/source/Meadow.Core/ExtensionMethods.cs
deleted file mode 100644
index b39ae34a..00000000
--- a/source/Meadow.Core/ExtensionMethods.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Meadow
-{
- public static class ExtensionMethods
- {
- ///
- /// TODO: move this into the `CircularBuffer` class? or is it broadly applicable?
- ///
- ///
- ///
- ///
- ///
- static public bool Contains(this IEnumerable source, TSource[] pattern)
- {
- return (source.FirstIndexOf(pattern) != -1);
-
- //int patternLength = pattern.Length;
- //int totalLength = source.Count();
- //TSource firstMatch = pattern[0];
-
- //for (int i = 0; i < totalLength; i++) {
- // // is this the right equality?
- // if ( (firstMatch.Equals (source.ElementAt(i)) ) // begin match?
- // &&
- // (totalLength - i >= patternLength) // can match exist?
- // ) {
- // TSource[] matchTest = new TSource[patternLength];
- // // copy the potential match into the matchTest array.
- // // can't use .Skip() and .Take() because it will actually
- // // enumerate over stuff and can have side effects
- // for (int x = 0; x < patternLength; x++) {
- // matchTest[x] = source.ElementAt(i + x);
- // }
- // // if the pattern pulled from source matches our search pattern
- // // then the pattern exists.
- // if (matchTest.SequenceEqual(pattern)) {
- // return true;
- // }
- // }
- //}
- //// if we go here, no pattern match
- //return false;
- }
-
- /// TODO: move this into the `CircularBuffer` class? or is it broadly applicable?
- static public int FirstIndexOf(this IEnumerable source, TSource[] pattern)
- {
- if (pattern == null) throw new ArgumentNullException();
-
- int patternLength = pattern.Length;
- int totalLength = source.Count();
- TSource firstMatch = pattern[0];
-
- if (firstMatch == null) return -1;
-
- for (int i = 0; i < totalLength; i++)
- {
- // is this the right equality?
- if ((firstMatch.Equals(source.ElementAt(i))) // begin match?
- &&
- (totalLength - i >= patternLength) // can match exist?
- )
- {
- TSource[] matchTest = new TSource[patternLength];
- // copy the potential match into the matchTest array.
- // can't use .Skip() and .Take() because it will actually
- // enumerate over stuff and can have side effects
- for (int x = 0; x < patternLength; x++)
- {
- matchTest[x] = source.ElementAt(i + x);
- }
- // if the pattern pulled from source matches our search pattern
- // then the pattern exists.
- if (matchTest.SequenceEqual(pattern))
- {
- return i;
- }
- }
- }
- // if we go here, doesn't exist
- return -1;
- }
-
- ///
- /// Maps a source value's position within a range of numbers to the same positon
- /// within a another range of numbers. For instance, will map a source value of `30`
- /// in the range of `0` to `100` to a value of `0.3` in a given range of `0.0` to `1.0`.
- ///
- /// The value to map to the new domain.
- /// The minimum value of the source domain.
- /// The maximum value of the source domain.
- /// The minimum value of the destinatino domain.
- /// The maximum value of the destination domain.
- ///
- static public float Map(this float souceValue, float sourceMin, float sourceMax, float targetMin, float targetMax)
- {
- return (souceValue - sourceMin) / (sourceMax - sourceMin) * (targetMax - targetMin) + targetMin;
- }
-
- ///
- /// Maps a source value's position within a range of numbers to the same positon
- /// within a another range of numbers. For instance, will map a source value of `30`
- /// in the range of `0` to `100` to a value of `0.3` in a given range of `0.0` to `1.0`.
- ///
- /// The value to map to the new domain.
- /// The minimum value of the source domain.
- /// The maximum value of the source domain.
- /// The minimum value of the destinatino domain.
- /// The maximum value of the destination domain.
- ///
- static public double Map(this double souceValue, double sourceMin, double sourceMax, double targetMin, double targetMax)
- {
- return (souceValue - sourceMin) / (sourceMax - sourceMin) * (targetMax - targetMin) + targetMin;
- }
-
- ///
- /// Maps a source value's position within a range of numbers to the same positon
- /// within a another range of numbers. For instance, will map a source value of `30`
- /// in the range of `0` to `100` to a value of `0.3` in a given range of `0.0` to `1.0`.
- ///
- /// The value to map to the new domain.
- /// The minimum value of the source domain.
- /// The maximum value of the source domain.
- /// The minimum value of the destinatino domain.
- /// The maximum value of the destination domain.
- ///
- static public int Map(this int souceValue, int sourceMin, int sourceMax, int targetMin, int targetMax)
- {
- return (souceValue - sourceMin) / (sourceMax - sourceMin) * (targetMax - targetMin) + targetMin;
- }
-
- public static void Fire(this Delegate handler, params object[] args)
- {
- if (handler == null) return;
- foreach (var d in handler.GetInvocationList())
- {
- try
- {
- d.DynamicInvoke(args);
- }
- catch (Exception ex)
- {
- Resolver.Log.Error($"Event handler threw {ex.GetType().Name}: {ex.Message}");
- }
- }
- }
-
- public static void Fire(this EventHandler h, object sender)
- {
- Fire(h, sender, EventArgs.Empty);
- }
-
- public static void Fire(this EventHandler handler, object sender, EventArgs args)
- {
- if (handler == null) return;
- foreach (var d in handler.GetInvocationList())
- {
- try
- {
- d.DynamicInvoke(sender, args);
- }
- catch (Exception ex)
- {
- Resolver.Log.Error($"Event handler threw {ex.GetType().Name}: {ex.Message}");
- }
- }
- }
-
- public static void Fire(this EventHandler handler, object sender, T args) where T : EventArgs
- {
- if (handler == null) return;
- foreach (var d in handler.GetInvocationList())
- {
- try
- {
- d.DynamicInvoke(sender, args);
- }
- catch (Exception ex)
- {
- Resolver.Log.Error($"Event handler threw {ex.GetType().Name}: {ex.Message}");
- }
- }
- }
- }
-}
diff --git a/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicBool.cs b/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicBool.cs
index 7c924300..0cc9509e 100644
--- a/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicBool.cs
+++ b/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicBool.cs
@@ -12,7 +12,7 @@ public CharacteristicBool(string name, string uuid, CharacteristicPermission per
public override void HandleDataWrite(byte[] data)
{
- Console.WriteLine($"HandleDataWrite in {this.GetType().Name}");
+ Resolver.Log.Info($"HandleDataWrite in {this.GetType().Name}");
RaiseValueSet(data[0] != 0);
}
diff --git a/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicInt32.cs b/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicInt32.cs
index 4e2590b7..fc4cc3a8 100644
--- a/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicInt32.cs
+++ b/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicInt32.cs
@@ -12,13 +12,13 @@ public CharacteristicInt32(string name, string uuid, CharacteristicPermission pe
public override void HandleDataWrite(byte[] data)
{
- Console.WriteLine($"HandleDataWrite in {this.GetType().Name}");
+ Resolver.Log.Info($"HandleDataWrite in {this.GetType().Name}");
// TODO: if the written data isn't 4 bytes, then what??
// for now I'll right-pad with zeros
if (data.Length < 4)
{
- Console.WriteLine($"HandleDataWrite only got {data.Length} bytes - padding");
+ Resolver.Log.Info($"HandleDataWrite only got {data.Length} bytes - padding");
var temp = new byte[4];
Array.Copy(data, temp, data.Length);
RaiseValueSet(BitConverter.ToInt32(temp));
@@ -27,7 +27,7 @@ public override void HandleDataWrite(byte[] data)
{
if (data.Length > 4)
{
- Console.WriteLine($"HandleDataWrite got {data.Length} bytes - using only the first 4");
+ Resolver.Log.Info($"HandleDataWrite got {data.Length} bytes - using only the first 4");
}
RaiseValueSet(BitConverter.ToInt32(data));
}
diff --git a/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicString.cs b/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicString.cs
index 4e16d433..5a4b343c 100644
--- a/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicString.cs
+++ b/source/Meadow.Core/Gateways/Bluetooth/Definitions/CharacteristicString.cs
@@ -13,7 +13,7 @@ public CharacteristicString(string name, string uuid, CharacteristicPermission p
public override void HandleDataWrite(byte[] data)
{
- Console.WriteLine($"HandleDataWrite in {this.GetType().Name}");
+ Resolver.Log.Info($"HandleDataWrite in {this.GetType().Name}");
RaiseValueSet(Encoding.UTF8.GetString(data));
}
diff --git a/source/Meadow.Core/Hardware/AnalogChannelInfo.cs b/source/Meadow.Core/Hardware/AnalogChannelInfo.cs
deleted file mode 100644
index 91ea267f..00000000
--- a/source/Meadow.Core/Hardware/AnalogChannelInfo.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-namespace Meadow.Hardware
-{
- ///
- /// Information about an analog channel
- ///
- public class AnalogChannelInfo : ChannelInfoBase, IAnalogChannelInfo
- {
- ///
- /// Whether or not the channel is capable of reading input (i.e. ADC).
- ///
- /// true if input capable; otherwise, false.
- public bool InputCapable { get; protected set; }
- ///
- /// Whether or not the channel is capable of writing output (i.e. DAC).
- ///
- /// true if output capable; otherwise, false.
- public bool OutputCapable { get; protected set; }
- ///
- /// Precision (in bits) of the channel
- ///
- public byte Precision { get; protected set; }
-
- ///
- /// Create an AnalogChannelInfo instance
- ///
- /// The channel name
- /// The precision (in bits) of the channel
- /// Whether or not the channel is ADC capable
- /// Whether or not the channel is DAC capable
- public AnalogChannelInfo(string name, byte precision, bool inputCapable, bool outputCapable)
- : base (name)
- {
- this.Precision = precision;
- this.InputCapable = inputCapable;
- this.OutputCapable = outputCapable;
- }
-
- }
-}
diff --git a/source/Meadow.Core/Hardware/AnalogInputPort.cs b/source/Meadow.Core/Hardware/AnalogInputPort.cs
index fa433813..b8dfc430 100644
--- a/source/Meadow.Core/Hardware/AnalogInputPort.cs
+++ b/source/Meadow.Core/Hardware/AnalogInputPort.cs
@@ -86,7 +86,7 @@ protected AnalogInputPort(
}
else
{
- throw new PortInUseException();
+ throw new PortInUseException($"{this.GetType().Name}: Pin {pin.Name} is already in use");
}
}
diff --git a/source/Meadow.Core/Hardware/Bases/AnalogInputPortBase.cs b/source/Meadow.Core/Hardware/Bases/AnalogInputPortBase.cs
deleted file mode 100644
index 269b26d6..00000000
--- a/source/Meadow.Core/Hardware/Bases/AnalogInputPortBase.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Meadow.Units;
-
-namespace Meadow.Hardware
-{
- ///
- /// Provides a base implementation for much of the common tasks of
- /// implementing IAnalogInputPort
- ///
- public abstract class AnalogInputPortBase : AnalogPortBase, IAnalogInputPort
- {
- protected object BufferSyncRoot { get; } = new object();
-
- ///
- /// Raised when the value of the reading changes.
- ///
- public event EventHandler> Updated = delegate { };
-
- ///
- /// Gets the sample buffer. Make sure to call StartUpdating() before
- /// use.
- ///
- /// The sample buffer.
- // TODO: make this a Memory if possible.
- public IList VoltageSampleBuffer { get; } = new List();
-
- ///
- /// A `TimeSpan` that specifies how long to
- /// wait between readings. This value influences how often `*Updated`
- /// events are raised and `IObservable` consumers are notified.
- ///
- public TimeSpan UpdateInterval { get; protected set; }
-
- ///
- /// Number of samples to take per reading. If > `1` then the port will
- /// take multiple readings and These are automatically averaged to
- /// reduce noise, a process known as _oversampling_.
- ///
- public int SampleCount { get; protected set; }
-
- ///
- /// Duration in between samples when oversampling.
- ///
- public TimeSpan SampleInterval { get; protected set; }
-
- ///
- /// The reference voltage being used for the ADC comparison
- ///
- public Voltage ReferenceVoltage { get; protected set; }
-
- ///
- /// Gets the average value of the values in the buffer. Use in conjunction
- /// with StartUpdating() for long-running analog sampling. For occasional
- /// sampling, use Read().
- ///
- /// The average buffer value.
- public Voltage Voltage {
- get
- {
- //heh. may be a faster way to do this.
- lock (BufferSyncRoot)
- {
- return new Voltage((VoltageSampleBuffer.Select(x => x.Volts).Sum() / VoltageSampleBuffer.Count()), Voltage.UnitType.Volts);
- }
- }
- }
-
- // collection of observers
- protected List>> observers { get; set; } = new List>>();
-
- protected AnalogInputPortBase(
- IPin pin, IAnalogChannelInfo channel,
- int sampleCount, TimeSpan sampleInterval,
- Voltage referenceVoltage)
- : base (pin, channel)
- {
- Pin = pin;
- SampleCount = sampleCount;
- SampleInterval = sampleInterval;
- ReferenceVoltage = referenceVoltage;
- }
-
- ///
- /// Convenience method to get the current voltage. For frequent reads, use
- /// StartUpdating() and StopUpdating() in conjunction with the SampleBuffer.
- ///
- public abstract Task Read();
-
- ///
- /// Starts continuously sampling the analog port.
- ///
- /// This method also starts raising `Changed` events and IObservable
- /// subscribers getting notified. Use the `readIntervalDuration` parameter
- /// to specify how often events and notifications are raised/sent.
- ///
- public abstract void StartUpdating(TimeSpan? updateInterval);
-
- ///
- /// Stops sampling the analog port.
- ///
- public abstract void StopUpdating();
-
-
- protected void RaiseChangedAndNotify(IChangeResult changeResult)
- {
- Updated?.Invoke(this, changeResult);
- observers.ForEach(x => x.OnNext(changeResult));
- }
-
- public IDisposable Subscribe(IObserver> observer)
- {
- if (!observers.Contains(observer)) observers.Add(observer);
- return new Unsubscriber(observers, observer);
- }
-
- private class Unsubscriber : IDisposable
- {
- private List>> _observers;
- private IObserver> _observer;
-
- public Unsubscriber(List>> observers, IObserver> observer)
- {
- this._observers = observers;
- this._observer = observer;
- }
-
- public void Dispose()
- {
- if (!(_observer == null)) _observers.Remove(_observer);
- }
- }
-
- }
-}
diff --git a/source/Meadow.Core/Hardware/Bases/AnalogPortBase.cs b/source/Meadow.Core/Hardware/Bases/AnalogPortBase.cs
deleted file mode 100644
index 23b1144f..00000000
--- a/source/Meadow.Core/Hardware/Bases/AnalogPortBase.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-namespace Meadow.Hardware
-{
- ///
- /// Provides a base implementation for much of the common tasks of
- /// implementing IAnalogPort
- ///
- public abstract class AnalogPortBase : PortBase, IAnalogPort
- {
- protected AnalogPortBase(IPin pin, IAnalogChannelInfo channel)
- : base(pin, channel)
- { }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Bases/BiDirectionalPortBase.cs b/source/Meadow.Core/Hardware/Bases/BiDirectionalPortBase.cs
deleted file mode 100644
index a16797a8..00000000
--- a/source/Meadow.Core/Hardware/Bases/BiDirectionalPortBase.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Meadow.Hardware
-{
- ///
- /// Provides a base implementation for BiDirectional Ports; digital ports
- /// that can be both input and output.
- ///
- public abstract class BiDirectionalPortBase : DigitalPortBase, IBiDirectionalPort, IDigitalInterruptPort, IDisposable
- {
- public event EventHandler Changed = delegate { };
-
- // internals
- protected bool _currentState;
-
- public bool InitialState { get; }
- public OutputType InitialOutputType { get; }
- public ResistorMode Resistor { get; }
- protected List> _observers { get; set; } = new List>();
-
- public abstract bool State { get; set; }
- public abstract PortDirectionType Direction { get; set; }
-
- protected TimeSpan _debounceDuration;
- protected TimeSpan _glitchDuration;
-
- public abstract TimeSpan DebounceDuration { get; set; }
- public abstract TimeSpan GlitchDuration { get; set; }
-
- ///
- /// Gets or sets a value indicating the type of interrupt monitoring this input.
- ///
- /// true if interrupt enabled; otherwise, false.
- public InterruptMode InterruptMode { get; protected set; }
-
- protected BiDirectionalPortBase(
- IPin pin,
- IDigitalChannelInfo channel,
- bool initialState,
- InterruptMode interruptMode = InterruptMode.None,
- ResistorMode resistorMode = ResistorMode.Disabled,
- PortDirectionType initialDirection = PortDirectionType.Input)
- : this(pin, channel, initialState, interruptMode, resistorMode, initialDirection, debounceDuration: TimeSpan.Zero, glitchDuration: TimeSpan.Zero, initialOutputType: OutputType.PushPull)
- {
- }
-
- protected BiDirectionalPortBase(
- IPin pin,
- IDigitalChannelInfo channel,
- bool initialState,
- InterruptMode interruptMode,
- ResistorMode resistorMode,
- PortDirectionType initialDirection,
- TimeSpan debounceDuration,
- TimeSpan glitchDuration,
- OutputType initialOutputType)
- : base(pin, channel)
- {
- this.InterruptMode = interruptMode;
- InitialState = initialState;
- Resistor = resistorMode;
- Direction = initialDirection;
- _debounceDuration = debounceDuration; // Don't trigger WireInterrupt call via property
- _glitchDuration = glitchDuration; // Don't trigger WireInterrupt call via property
- InitialOutputType = initialOutputType;
- }
-
- protected void RaiseChangedAndNotify(DigitalPortResult changeResult)
- {
- if (disposed) return;
-
- Changed?.Invoke(this, changeResult);
- // TODO: implement Subscribe patter (see DigitalInputPortBase)
- // _observers.ForEach(x => x.OnNext(changeResult));
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Bases/ChannelInfoBase.cs b/source/Meadow.Core/Hardware/Bases/ChannelInfoBase.cs
deleted file mode 100644
index bd3825a8..00000000
--- a/source/Meadow.Core/Hardware/Bases/ChannelInfoBase.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-namespace Meadow.Hardware
-{
- ///
- /// Provides base functionality for channel types.
- ///
- public abstract class ChannelInfoBase
- {
- public string Name { get; protected set; }
-
- protected ChannelInfoBase(string name)
- {
- this.Name = name;
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Bases/DigitalChannelInfoBase.cs b/source/Meadow.Core/Hardware/Bases/DigitalChannelInfoBase.cs
deleted file mode 100644
index 974d5fa4..00000000
--- a/source/Meadow.Core/Hardware/Bases/DigitalChannelInfoBase.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-
-namespace Meadow.Hardware
-{
- public class DigitalChannelInfoBase : ChannelInfoBase, IDigitalChannelInfo
- {
- public bool InputCapable { get; protected set; }
- public bool OutputCapable { get; protected set; }
- public bool InterruptCapable { get; protected set; }
- public bool PullDownCapable { get; protected set; }
- public bool PullUpCapable { get; protected set; }
- public bool InverseLogic { get; protected set; }
- public int? InterruptGroup { get; protected set; }
-
- protected DigitalChannelInfoBase(
- string name,
- bool inputCapable,
- bool outputCapable,
- bool interruptCapable,
- bool pullDownCapable,
- bool pullUpCapable,
- bool inverseLogic,
- int? interruptGroup = null)
- : base(name)
- {
- this.InputCapable = inputCapable;
- this.OutputCapable = outputCapable;
- this.InterruptCapable = interruptCapable;
- this.PullDownCapable = pullDownCapable;
- this.PullUpCapable = pullUpCapable;
- this.InverseLogic = inverseLogic;
- this.InterruptGroup = interruptGroup;
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Bases/DigitalInputPortBase.cs b/source/Meadow.Core/Hardware/Bases/DigitalInputPortBase.cs
deleted file mode 100644
index 86fb1f05..00000000
--- a/source/Meadow.Core/Hardware/Bases/DigitalInputPortBase.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Meadow.Hardware
-{
- ///
- /// Provides a base implementation for digital input ports.
- ///
- public abstract class DigitalInputPortBase : DigitalPortBase, IDigitalInputPort, IDigitalInterruptPort
- {
- ///
- /// Occurs when the state is changed. To enable this, set the InterruptMode at construction
- ///
- public event EventHandler Changed = delegate { };
-
- ///
- /// Gets or sets a value indicating the type of interrupt monitoring this input.
- ///
- /// true if interrupt enabled; otherwise, false.
- public InterruptMode InterruptMode { get; protected set; }
-
- public abstract bool State { get; }
- public abstract ResistorMode Resistor { get; set; }
- public abstract TimeSpan DebounceDuration { get; set; }
- public abstract TimeSpan GlitchDuration { get; set; }
-
- protected List>> _observers { get; set; } = new List>>();
-
- private List unsubscribers = new List();
-
- protected DigitalInputPortBase(
- IPin pin,
- IDigitalChannelInfo channel,
- InterruptMode interruptMode = InterruptMode.None
- )
- : base(pin, channel)
- {
- // TODO: check interrupt mode (i.e. if != none, make sure channel info agrees)
- this.InterruptMode = interruptMode;
- }
-
- protected void RaiseChangedAndNotify(DigitalPortResult changeResult)
- {
- Changed?.Invoke(this, changeResult);
- _observers.ForEach(x => x.OnNext(changeResult));
- }
-
- public IDisposable Subscribe(IObserver> observer)
- {
- if(!_observers.Contains(observer)) _observers.Add(observer);
- var u = new Unsubscriber(_observers, observer);
- unsubscribers.Add(u);
- return u;
- }
-
- protected override void Dispose(bool disposing)
- {
- foreach(var u in unsubscribers)
- {
- u.Dispose();
- }
-
- base.Dispose(disposing);
- }
-
- private class Unsubscriber : IDisposable
- {
- private List>> _observers;
- private IObserver> _observer;
-
- public Unsubscriber(List>> observers, IObserver> observer)
- {
- this._observers = observers;
- this._observer = observer;
- }
-
- public void Dispose()
- {
- if(!(_observer == null)) _observers.Remove(_observer);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/source/Meadow.Core/Hardware/Bases/DigitalInterruptPortBase.cs b/source/Meadow.Core/Hardware/Bases/DigitalInterruptPortBase.cs
deleted file mode 100644
index f4e4c33d..00000000
--- a/source/Meadow.Core/Hardware/Bases/DigitalInterruptPortBase.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-//using System;
-//namespace Meadow.Hardware
-//{
-// ///
-// /// Provides a base implementation for digital interrupt ports; digital input
-// /// ports that notify on change.
-// ///
-// public class DigitalInterruptPortBase : DigitalInputPortBase, IDigitalInterruptPort
-// {
-// protected DigitalInterruptPortBase(IDigitalChannelInfo channelInfo) : base(channelInfo)
-// {
-// }
-
-
-// bool IDigitalInterruptPort.InterrupEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
-// }
-//}
diff --git a/source/Meadow.Core/Hardware/Bases/DigitalOutputPortBase.cs b/source/Meadow.Core/Hardware/Bases/DigitalOutputPortBase.cs
deleted file mode 100644
index f1f71bff..00000000
--- a/source/Meadow.Core/Hardware/Bases/DigitalOutputPortBase.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Meadow.Hardware
-{
- ///
- /// Provides a base implementation for digital output ports.
- ///
- public abstract class DigitalOutputPortBase : DigitalPortBase, IDigitalOutputPort
- {
- ///
- /// The initial state of the port.
- ///
- public bool InitialState { get; protected set; }
- public OutputType InitialOutputType { get; protected set; }
-
- public abstract bool State { get; set; }
-
- protected DigitalOutputPortBase(IPin pin, IDigitalChannelInfo channel, bool initialState, OutputType initialOutputType)
- : base(pin, channel)
- {
- this.InitialState = initialState;
- this.InitialOutputType = initialOutputType;
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Bases/DigitalPortBase.cs b/source/Meadow.Core/Hardware/Bases/DigitalPortBase.cs
deleted file mode 100644
index 46c5be41..00000000
--- a/source/Meadow.Core/Hardware/Bases/DigitalPortBase.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Meadow.Hardware
-{
- ///
- /// DigitalPortBase provides a base implementation for much of the
- /// common tasks of classes implementing IDigitalPort.
- ///
- public abstract class DigitalPortBase : PortBase, IDigitalPort
- {
- public new IDigitalChannelInfo Channel { get; protected set; }
-
- protected bool InverseLogic { get; }
-
- protected DigitalPortBase(IPin pin, IDigitalChannelInfo channel)
- : base(pin, channel)
- {
- this.InverseLogic = channel.InverseLogic;
- this.Channel = channel;
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Bases/PinBase.cs b/source/Meadow.Core/Hardware/Bases/PinBase.cs
deleted file mode 100644
index a118ccac..00000000
--- a/source/Meadow.Core/Hardware/Bases/PinBase.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System.Collections.Generic;
-
-namespace Meadow.Hardware
-{
-
- ///
- /// Provides base implementation for IO pins.
- ///
- public abstract class PinBase : IPin
- {
- public IList? SupportedChannels { get; protected set; }
-
- public string Name { get; protected set; }
- ///
- /// Identifier that the parent Device can use to identify the I/O (address, port, pin, etc)
- ///
- /// The key.
- public object Key { get; protected set; }
-
- //public abstract IChannelInfo ActiveChannel { get; protected set; }
-
- protected PinBase(string name, object key, IList? supportedChannels)
- {
- this.Name = name;
- this.Key = key;
- this.SupportedChannels = supportedChannels;
- }
-
-#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
- protected PinBase()
- {
- // make default non-callable
- }
-#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
-
- public override string ToString()
- {
- return this.Name;
- }
-
- public void ReserveChannel()
- {
-
- }
- public void ReleaseChannel()
- {
-
- }
-
- public virtual bool Equals(IPin other)
- {
- if (other == null) return false;
-
- return this.Key.Equals(other.Key);
- }
-
- public override bool Equals(object obj)
- {
- if (obj is IPin { } p)
- {
- return p.Equals(this);
- }
- return false;
- }
-
- public override int GetHashCode()
- {
- return Key.GetHashCode();
- }
- }
-}
\ No newline at end of file
diff --git a/source/Meadow.Core/Hardware/Bases/PortBase.cs b/source/Meadow.Core/Hardware/Bases/PortBase.cs
deleted file mode 100644
index 15d1e16a..00000000
--- a/source/Meadow.Core/Hardware/Bases/PortBase.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-namespace Meadow.Hardware
-{
- public abstract class PortBase : IPort where C : class, IChannelInfo
- {
- protected bool disposed = false;
-
- public C Channel { get; }
-
- public IPin Pin { get; protected set; }
-
- protected PortBase(IPin pin, C channel)
- {
- this.Pin = pin ?? throw new ArgumentNullException(nameof(pin));
- this.Channel = channel ?? throw new ArgumentNullException(nameof(channel));
- }
-
- protected virtual void Dispose(bool disposing) { }
-
- public void Dispose()
- {
- Dispose(true);
- disposed = true;
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Bases/PwmPortBase.cs b/source/Meadow.Core/Hardware/Bases/PwmPortBase.cs
deleted file mode 100644
index 0405ac6f..00000000
--- a/source/Meadow.Core/Hardware/Bases/PwmPortBase.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Meadow.Units;
-using System;
-namespace Meadow.Hardware
-{
- public abstract class PwmPortBase : DigitalPortBase, IPwmPort
- {
- public new IPwmChannelInfo Channel {
- get => (IPwmChannelInfo)base.Channel;
- protected set { base.Channel = value; }
- }
-
- protected PwmPortBase(
- IPin pin,
- IPwmChannelInfo channelInfo,
- Frequency frequency,
- float dutyCycle = 0,
- bool inverted = false
- ) : base (pin, channelInfo)
- {
- this.Inverted = inverted;
- this.Frequency = frequency;
- this.DutyCycle = dutyCycle;
- }
-
- ///
- /// The units in which time-based properties (Period and Duration) are expressed
- ///
- public TimeScale TimeScale { get; set; }
-
- public abstract bool Inverted { get; set; }
- public abstract float DutyCycle { get; set; }
- public abstract Frequency Frequency { get; set; }
- public abstract float Duration { get; set; }
- public abstract float Period { get; set; }
- public abstract bool State { get; }
- public abstract void Start();
- public abstract void Stop();
- }
-}
diff --git a/source/Meadow.Core/Hardware/BiDirectionalPort.cs b/source/Meadow.Core/Hardware/BiDirectionalPort.cs
index 4db27dce..4e9c8d7d 100644
--- a/source/Meadow.Core/Hardware/BiDirectionalPort.cs
+++ b/source/Meadow.Core/Hardware/BiDirectionalPort.cs
@@ -109,7 +109,7 @@ OutputType outputType
}
else
{
- throw new PortInUseException();
+ throw new PortInUseException($"{this.GetType().Name}: Pin {pin.Name} is already in use");
}
}
diff --git a/source/Meadow.Core/Hardware/CanChannelInfo.cs b/source/Meadow.Core/Hardware/CanChannelInfo.cs
deleted file mode 100644
index 85fc55b8..00000000
--- a/source/Meadow.Core/Hardware/CanChannelInfo.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-namespace Meadow.Hardware
-{
- public class CanChannelInfo : DigitalChannelInfoBase, ICanChannelInfo
- {
- public SerialDirectionType SerialDirection { get; protected set; }
-
- public CanChannelInfo(string name, SerialDirectionType serialDirection)
- : base(name, true, true, true, true, true, false)
- {
- this.SerialDirection = serialDirection;
- }
-
- }
-}
diff --git a/source/Meadow.Core/Hardware/Communications/I2cBusSpeed.cs b/source/Meadow.Core/Hardware/Communications/I2cBusSpeed.cs
deleted file mode 100644
index 0a4c9d70..00000000
--- a/source/Meadow.Core/Hardware/Communications/I2cBusSpeed.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace Meadow.Hardware
-{
- ///
- /// Standard I2C Bus speeds
- ///
- public enum I2cBusSpeed
- {
- ///
- /// Standard 100 kHz clock frequency
- ///
- Standard = 100000,
- ///
- /// Fast 400 kHz clock frequency
- ///
- Fast = 400000,
- ///
- /// Fast-Plus 1 MHz clock frequency
- ///
- FastPlus = 1000000
- }
-}
diff --git a/source/Meadow.Core/Hardware/Communications/I2cChannelInfo.cs b/source/Meadow.Core/Hardware/Communications/I2cChannelInfo.cs
deleted file mode 100644
index eff82251..00000000
--- a/source/Meadow.Core/Hardware/Communications/I2cChannelInfo.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-namespace Meadow.Hardware
-{
- //TODO: what else should this have? allowed speeds?
- public class I2cChannelInfo : DigitalChannelInfoBase, II2cChannelInfo
- {
- public I2cChannelFunctionType ChannelFunction { get; protected set; }
-
- public I2cChannelInfo(string name,
- I2cChannelFunctionType channelFunction,
- bool pullDownCapable = false,
- bool pullUpCapable = false)
- : base(
- name,
- inputCapable: true,
- outputCapable: true,
- interruptCapable: false, // ?? i mean, technically, yes, but will we have events?
- pullDownCapable: pullDownCapable,
- pullUpCapable: pullUpCapable,
- inverseLogic: false) //TODO: switch to C# 7.2+ to get rid of trailing names
- {
- this.ChannelFunction = channelFunction;
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Communications/I2cPeripheral.cs b/source/Meadow.Core/Hardware/Communications/I2cPeripheral.cs
deleted file mode 100644
index 8993addc..00000000
--- a/source/Meadow.Core/Hardware/Communications/I2cPeripheral.cs
+++ /dev/null
@@ -1,206 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Meadow.Hardware
-{
- ///
- /// Defines a contract for a peripheral that communicates via the IIC/I2C
- /// protocol.
- ///
- public class I2cPeripheral : II2cPeripheral
- {
- public byte Address { get; protected set; }
- public II2cBus Bus { get; protected set; }
-
- ///
- /// Internal write buffer. Used in methods in which the buffers aren't
- /// passed in.
- ///
- protected Memory WriteBuffer { get; }
- ///
- /// Internal read buffer. Used in methods in which the buffers aren't
- /// passed in.
- ///
- protected Memory ReadBuffer { get; }
-
- public I2cPeripheral(II2cBus bus, byte peripheralAddress, int readBufferSize = 8, int writeBufferSize = 8)
- {
- Bus = bus;
- Address = peripheralAddress;
- WriteBuffer = new byte[writeBufferSize];
- ReadBuffer = new byte[readBufferSize];
- }
-
- ///
- /// Reads data from the peripheral.
- ///
- /// The buffer to read from the peripheral into.
- ///
- /// The number of bytes to be read is determined by the length of the
- /// `readBuffer`.
- ///
- public void Read(Span readBuffer)
- {
- Bus.Read(this.Address, readBuffer);
- }
-
- ///
- /// Reads data from the peripheral starting at the specified address.
- ///
- ///
- ///
- public void ReadRegister(byte address, Span readBuffer)
- {
- WriteBuffer.Span[0] = address;
- Bus.Exchange(this.Address, WriteBuffer.Span[0..1], readBuffer);
- }
-
- ///
- /// Read a register from the peripheral.
- ///
- /// Address of the register to read.
- public byte ReadRegister(byte address)
- {
- WriteBuffer.Span[0] = address;
- Bus.Exchange(this.Address, WriteBuffer.Span[0..1], ReadBuffer.Span[0..1]);
- return ReadBuffer.Span[0];
- }
-
- ///
- /// Read an usingned short from a register.
- ///
- /// Register address of the low byte (the high byte will follow).
- /// Order of the bytes in the register (little endian is the default).
- /// Value read from the register.
- public ushort ReadRegisterAsUShort(byte address, ByteOrder order = ByteOrder.LittleEndian)
- {
- WriteBuffer.Span[0] = address;
- Bus.Exchange(this.Address, WriteBuffer[0..1].Span, ReadBuffer[0..2].Span);
- if (order == ByteOrder.LittleEndian) {
- return (ushort)(ReadBuffer.Span[0] | (ReadBuffer.Span[1] << 8));
- } else {
- return (ushort)(ReadBuffer.Span[0] << 8 | ReadBuffer.Span[1]);
- }
- }
-
- ///
- /// Write a single byte to the peripheral.
- ///
- /// Value to be written (8-bits).
- public void Write(byte value)
- {
- // stuff the value into the write buffer
- WriteBuffer.Span[0] = value;
- this.Bus.Write(this.Address, WriteBuffer.Span[0..1]);
- }
-
- ///
- /// Write an array of bytes to the peripheral.
- ///
- /// Values to be written.
- public void Write(Span data)
- {
- this.Bus.Write(this.Address, data);
- }
-
- ///
- /// Write data a register in the peripheral.
- ///
- /// Address of the register to write to.
- /// Data to write into the register.
- public void WriteRegister(byte address, byte value)
- {
- // stuff the address and value into the write buffer
- WriteBuffer.Span[0] = address;
- WriteBuffer.Span[1] = value;
- this.Bus.Write(this.Address, WriteBuffer.Span[0..2]);
- }
-
- ///
- /// Write an unsigned short to the peripheral.
- ///
- /// Address to write the first byte to.
- /// Value to be written (16-bits).
- /// Indicate if the data should be written as big or little endian.
- public void WriteRegister(byte address, ushort value, ByteOrder order = ByteOrder.LittleEndian)
- {
- // split the 16 bit ushort into two bytes
- var bytes = BitConverter.GetBytes(value);
- // call the helper method
- WriteRegister(address, bytes, order);
- }
-
- ///
- /// Write an unsigned integer to the peripheral.
- ///
- /// Address to write the first byte to.
- /// Value to be written.
- /// Indicate if the data should be written as big or little endian.
- public void WriteRegister(byte address, uint value, ByteOrder order = ByteOrder.LittleEndian)
- {
- // split the 32 bit uint into four bytes
- var bytes = BitConverter.GetBytes(value);
- // call the helper method
- WriteRegister(address, bytes, order);
- }
-
- ///
- /// Write an unsigned long to the peripheral.
- ///
- /// Address to write the first byte to.
- /// Value to be written.
- /// Indicate if the data should be written as big or little endian.
- public void WriteRegister(byte address, ulong value, ByteOrder order = ByteOrder.LittleEndian)
- {
- // split the 64 bit ulong into 8 bytes
- var bytes = BitConverter.GetBytes(value);
- // call the helper method
- WriteRegister(address, bytes, order);
- }
-
- ///
- /// Write data to a register in the peripheral.
- ///
- /// Address of the register to write to.
- /// A buffer of byte values to be written.
- /// Indicate if the data should be written as big or little endian.
- public void WriteRegister(byte address, Span writeBuffer, ByteOrder order = ByteOrder.LittleEndian)
- {
- if (WriteBuffer.Length < writeBuffer.Length + 1) {
- throw new ArgumentException("Data to write is too large for the write buffer. " +
- "Must be less than WriteBuffer.Length + 1 (to allow for address). " +
- "Instantiate this class with a larger WriteBuffer, or send a smaller" +
- "amount of data to fix.");
- }
-
- // stuff the register address into the write buffer
- WriteBuffer.Span[0] = address;
-
- // stuff the bytes into the write buffer (starting at `1` index,
- // because `0` is the register address.
- switch (order) {
- case ByteOrder.LittleEndian:
- for (int i = 0; i < writeBuffer.Length; i++) {
- WriteBuffer.Span[i + 1] = writeBuffer[i];
- }
- break;
- case ByteOrder.BigEndian:
- for (int i = 0; i < writeBuffer.Length; i++) {
- // stuff them backwards
- WriteBuffer.Span[i + 1] = writeBuffer[writeBuffer.Length - (i + 1)];
- }
- break;
- }
- // write it
- this.Bus.Write(this.Address, WriteBuffer.Span[0..(writeBuffer.Length + 1)]);
- }
-
- public void Exchange(Span writeBuffer, Span readBuffer, DuplexType duplex = DuplexType.Half)
- {
- if (duplex == DuplexType.Full) { throw new ArgumentException("I2C doesn't support full-duplex communications. Only half-duplex is available because it only has a single data line."); }
-
- Bus.Exchange(this.Address, writeBuffer, readBuffer);
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/Communications/SerialMessagePort.cs b/source/Meadow.Core/Hardware/Communications/SerialMessagePort.cs
index d9096f5f..c647d4ce 100644
--- a/source/Meadow.Core/Hardware/Communications/SerialMessagePort.cs
+++ b/source/Meadow.Core/Hardware/Communications/SerialMessagePort.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Text;
-
-namespace Meadow.Hardware
+namespace Meadow.Hardware
{
// TODO: to optimize, this really should re-implement its own serialport stuff
// rather than using the ClassicSerialPort. That way we don't maintain two
@@ -16,62 +13,48 @@ namespace Meadow.Hardware
/// thread-safe and asynchronous in nature. This is the recommended way to
/// use serial on Meadow for nearly all use cases.
///
- public class SerialMessagePort : ISerialMessagePort// : FilterableObservableBase
+ public class SerialMessagePort : SerialMessageProcessor, ISerialMessagePort
{
///
/// Gets or sets the serial baud rate.
///
public int BaudRate
{
- get => _classicSerialPort.BaudRate;
- set => _classicSerialPort.BaudRate = value;
+ get => classicSerialPort.BaudRate;
+ set => classicSerialPort.BaudRate = value;
}
+
///
/// Gets the port name used for communications.
///
- public string PortName { get => _classicSerialPort.PortName; }
+ public string PortName { get => classicSerialPort.PortName; }
+
///
/// Gets a value indicating the open or closed status of the SerialPort object.
///
- public bool IsOpen { get => _classicSerialPort.IsOpen; }
+ public bool IsOpen { get => classicSerialPort.IsOpen; }
+
///
/// Gets or sets the parity-checking protocol.
///
- public Parity Parity { get => _classicSerialPort.Parity; set => _classicSerialPort.Parity = value; }
+ public Parity Parity { get => classicSerialPort.Parity; set => classicSerialPort.Parity = value; }
+
///
/// Gets or sets the standard length of data bits per byte.
///
- public int DataBits { get => _classicSerialPort.DataBits; set => _classicSerialPort.DataBits = value; }
- ///
- /// Gets or sets the standard number of stopbits per byte.
- ///
- public StopBits StopBits { get => _classicSerialPort.StopBits; set => _classicSerialPort.StopBits = value; }
- ///
- /// The buffer size, in bytes.
- ///
- public int ReceiveBufferSize {
- get => _classicSerialPort.ReceiveBufferSize;
- }
+ public int DataBits { get => classicSerialPort.DataBits; set => classicSerialPort.DataBits = value; }
///
- /// Raised when a message, as defined in the constructor, arrives.
+ /// Gets or sets the standard number of stopbits per byte.
///
- public event EventHandler MessageReceived = delegate { };
+ public StopBits StopBits { get => classicSerialPort.StopBits; set => classicSerialPort.StopBits = value; }
- protected ISerialPort _classicSerialPort;
- protected SerialMessageMode _messageMode;
- protected byte[] _messageDelimiterTokens;
- protected int _messageLength;
- protected bool _preserveDelimiter;
-
- protected CircularBuffer? _readBuffer;
- protected object _msgParseLock = new object();
+ protected ISerialPort classicSerialPort;
public static SerialMessagePort From(
ISerialPort commsPort,
byte[] suffixDelimiter,
- bool preserveDelimiter
- )
+ bool preserveDelimiter)
{
return new SerialMessagePort(commsPort, suffixDelimiter, preserveDelimiter);
}
@@ -80,8 +63,7 @@ public static SerialMessagePort From(
ISerialPort commsPort,
byte[] prefixDelimiter,
bool preserveDelimiter,
- int messageLength
- )
+ int messageLength)
{
return new SerialMessagePort(commsPort, prefixDelimiter, preserveDelimiter, messageLength);
}
@@ -98,13 +80,10 @@ int messageLength
protected SerialMessagePort(
ISerialPort commsPort,
byte[] suffixDelimiter,
- bool preserveDelimiter)
+ bool preserveDelimiter) : base(commsPort.ReceiveBufferSize, suffixDelimiter, preserveDelimiter)
{
- this._messageMode = SerialMessageMode.SuffixDelimited;
- this._preserveDelimiter = preserveDelimiter;
- this._messageDelimiterTokens = suffixDelimiter;
- this._classicSerialPort = commsPort;
- this.Init(commsPort.ReceiveBufferSize);
+ classicSerialPort = commsPort;
+ Init();
}
///
@@ -123,176 +102,55 @@ protected SerialMessagePort(
ISerialPort commsPort,
byte[] prefixDelimiter,
bool preserveDelimiter,
- int messageLength)
+ int messageLength) : base(commsPort.ReceiveBufferSize, prefixDelimiter, preserveDelimiter, messageLength)
{
-
- this._messageMode = SerialMessageMode.PrefixDelimited;
- this._preserveDelimiter = preserveDelimiter;
- this._messageDelimiterTokens = prefixDelimiter;
- this._messageLength = messageLength;
- this._classicSerialPort = commsPort;
- this.Init(commsPort.ReceiveBufferSize);
+ classicSerialPort = commsPort;
+ Init();
}
///
/// Initializes the buffer and underlying serial port
///
///
- protected void Init(int readBufferSize)
+ protected void Init()
{
- _readBuffer = new CircularBuffer(readBufferSize);
- this._classicSerialPort.DataReceived += SerialPort_DataReceived;
- }
-
- private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
- {
- if (_readBuffer == null) return;
-
- // only one message processor at a time
- lock (_msgParseLock) {
-
- if (e.EventType == SerialDataType.Chars) {
-
- // read all the available data from the underlying port
- // HACK: note that this is where this class actually re-implementing
- // serial port comms would be beneifical. we wouldn't have to do
- // these additional allocations (`tempBuffer`)
- byte[] tempBuffer = new byte[_classicSerialPort.BytesToRead];
- _classicSerialPort.ReadAll(tempBuffer);
- this._readBuffer.Append(tempBuffer);
-
- int firstIndex;
- switch (this._messageMode) {
-
- // PREFIX DELIMITED PARSING ROUTINE
- case SerialMessageMode.PrefixDelimited:
-
- // if the buffer contains the prefix
- firstIndex = _readBuffer.FirstIndexOf(_messageDelimiterTokens);
-
- // while there are messages to Remove
- while (firstIndex >= 0) {
- // calculations
- // length of the entire message that needs to be Removed
- int totalMsgLength = _messageDelimiterTokens.Length + _messageLength;
- // length of the message to return (depends on delimiter preservation)
- int returnMsgLength = ( _preserveDelimiter ? totalMsgLength : _messageLength);
-
- byte[] msg = new byte[returnMsgLength];
-
- // throw away anything before the prefix
- for (int i = 0; i < firstIndex; i++) {
- _readBuffer.Remove();
- }
-
- // presever delimiter?
- switch (_preserveDelimiter) {
- case true: // if preserving, dump the whole message in
- for (int i = 0; i < totalMsgLength; i++) {
- msg[i] = _readBuffer.Remove();
- }
- break;
- case false:
- // if tossing away, throw away first part
- for (int i = 0; i < _messageDelimiterTokens.Length; i++) {
- _readBuffer.Remove();
- }
- for (int i = 0; i < returnMsgLength; i++) {
- msg[i] = _readBuffer.Remove();
- }
- break;
- }
-
- //todo: should this run on a new thread?
- // it doesn't seem to return, otherwise
- System.Threading.Tasks.Task.Run(() => {
- //Console.WriteLine($"raising message received, msg.length: {msg.Length}");
- //Console.WriteLine($"Message:{Encoding.ASCII.GetString(msg)}");
- this.RaiseMessageReceivedAndNotify(new SerialMessageData() { Message = msg });
- });
-
- // check if there are any left
- firstIndex = _readBuffer.FirstIndexOf(_messageDelimiterTokens);
- }
-
- break;
-
- // SUFFIX DELIMITED PARSING ROUTINE
- case SerialMessageMode.SuffixDelimited:
- // if the buffer contains the suffix
- firstIndex = _readBuffer.FirstIndexOf(_messageDelimiterTokens);
-
- // while there are valid messages in here (multiple
- // messages can be in a single data event
- while (firstIndex >= 0) {
- var bytesToRemove = firstIndex + _messageDelimiterTokens.Length;
- byte[] msg = new byte[(_preserveDelimiter ? bytesToRemove : (bytesToRemove - _messageDelimiterTokens.Length))];
-
- // deuque the message, sans delimeter
- for (int i = 0; i < firstIndex; i++) {
- msg[i] = _readBuffer.Remove();
- }
- // handle the delimeters. either add to msg or toss away.
- for (int i = firstIndex; i < bytesToRemove; i++) {
- if (_preserveDelimiter) {
- msg[i] = _readBuffer.Remove();
- } else {
- _readBuffer.Remove();
- }
- }
-
- //todo: should this run on a new thread?
- // it doesn't seem to return, otherwise
- System.Threading.Tasks.Task.Run(() => {
- this.RaiseMessageReceivedAndNotify(new SerialMessageData() { Message = msg });
- });
-
- firstIndex = _readBuffer.FirstIndexOf(_messageDelimiterTokens);
- }
- break;
- }
- }
- }
+ classicSerialPort.DataReceived += SerialPortDataReceived;
}
- protected void RaiseMessageReceivedAndNotify(SerialMessageData messageData)
+ private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
- try
- {
- MessageReceived(this, messageData);
- }
- catch(Exception ex)
+ if (e.EventType == SerialDataType.Chars)
{
- Console.WriteLine($"!! {ex.Message}");
+ byte[] data = new byte[classicSerialPort.BytesToRead];
+ classicSerialPort.ReadAll(data);
+ Process(data);
}
- //TODO: figure out the IObservable when there's no change context
- //base.NotifyObservers(messageResult);
}
///
- /// Opens a new serial port connection.
+ /// Opens a new serial port connection
///
public void Open()
{
- this._classicSerialPort.Open();
+ classicSerialPort.Open();
}
///
- /// Closes the port connection and sets the IsOpen property to false.
+ /// Closes the port connection and sets the IsOpen property to false
///
public void Close()
{
- this._classicSerialPort.Close();
+ classicSerialPort.Close();
}
///
- /// Writes data to the serial port.
+ /// Writes data to the serial port
///
///
///
public int Write(byte[] buffer)
{
- return this._classicSerialPort.Write(buffer);
+ return classicSerialPort.Write(buffer);
}
///
@@ -304,25 +162,15 @@ public int Write(byte[] buffer)
///
public int Write(byte[] buffer, int offset, int count)
{
- return this._classicSerialPort.Write(buffer, offset, count);
+ return classicSerialPort.Write(buffer, offset, count);
}
///
- /// Discards all data from the receive buffer.
+ /// Discards all data from the receive buffer
///
public void ClearReceiveBuffer()
{
- _classicSerialPort.ClearReceiveBuffer();
+ classicSerialPort.ClearReceiveBuffer();
}
-
- ///
- /// Whether we're defining messages by prefix + length, or suffix.
- ///
- protected enum SerialMessageMode
- {
- PrefixDelimited,
- SuffixDelimited
- }
-
}
-}
+}
\ No newline at end of file
diff --git a/source/Meadow.Core/Hardware/Communications/SerialPortBase.cs b/source/Meadow.Core/Hardware/Communications/SerialPortBase.cs
index aab6283f..0ee6047b 100644
--- a/source/Meadow.Core/Hardware/Communications/SerialPortBase.cs
+++ b/source/Meadow.Core/Hardware/Communications/SerialPortBase.cs
@@ -222,7 +222,7 @@ private void OnReadBufferOverrun(object sender, EventArgs e)
catch (Exception ex)
{
// ignore errors in the consumer's code
- Console.WriteLine($"Error in BufferOverrun handler: {ex.Message}");
+ Resolver.Log.Error($"Error in BufferOverrun handler: {ex.Message}");
}
}
@@ -258,7 +258,6 @@ public void ClearReceiveBuffer()
if (_readBuffer != null)
{
_readBuffer.Clear();
- //_readBuffer.Overrun -= OnReadBufferOverrun; - Adrian, this shouldn't be needed Aug 12, 2020
}
}
diff --git a/source/Meadow.Core/Hardware/Communications/SpiPeripheral.cs b/source/Meadow.Core/Hardware/Communications/SpiPeripheral.cs
deleted file mode 100644
index c5b7a557..00000000
--- a/source/Meadow.Core/Hardware/Communications/SpiPeripheral.cs
+++ /dev/null
@@ -1,267 +0,0 @@
-using System;
-
-namespace Meadow.Hardware
-{
- ///
- /// Represents an SPI peripheral object
- /// This encapsulates and synchronizes the SPI bus and chip select ports
- ///
- public class SpiPeripheral : ISpiPeripheral
- {
- ///
- /// The SPI chip select port
- ///
- public IDigitalOutputPort? ChipSelect { get; }
-
- ///
- /// The chip select mode (active high or active low)
- ///
- ChipSelectMode chipSelectMode;
-
- ///
- /// the ISpiBus object
- ///
- public ISpiBus Bus { get; }
-
- ///
- /// Internal write buffer. Used in methods in which the buffers aren't
- /// passed in.
- ///
- protected Memory WriteBuffer { get; }
- ///
- /// Internal read buffer. Used in methods in which the buffers aren't
- /// passed in.
- ///
- protected Memory ReadBuffer { get; }
-
- ///
- /// Creates a new SpiPeripheral instance
- ///
- /// The spi bus connected to the peripheral
- /// The chip select port
- /// The size of the read buffer in bytes
- /// The size of the write buffer in bytes
- /// The chip select mode, active high or active low
- public SpiPeripheral(
- ISpiBus bus,
- IDigitalOutputPort? chipSelect,
- int readBufferSize = 8, int writeBufferSize = 8,
- ChipSelectMode csMode = ChipSelectMode.ActiveLow)
- {
- this.Bus = bus;
- this.ChipSelect = chipSelect;
- this.chipSelectMode = csMode;
- WriteBuffer = new byte[writeBufferSize];
- ReadBuffer = new byte[readBufferSize];
- }
-
- ///
- /// Reads data from the peripheral.
- ///
- /// The buffer to read from the peripheral into.
- ///
- /// The number of bytes to be read is determined by the length of the
- /// `readBuffer`.
- ///
- public void Read(Span readBuffer)
- {
- Bus.Read(this.ChipSelect, readBuffer, this.chipSelectMode);
- }
-
- ///
- /// Reads data from the peripheral starting at the specified address.
- ///
- /// The register address
- /// The buffer to hold the data
- public void ReadRegister(byte address, Span readBuffer)
- {
- WriteBuffer.Span[0] = address;
- Bus.Exchange(this.ChipSelect, WriteBuffer.Span[0..readBuffer.Length], readBuffer, this.chipSelectMode);
- }
-
- ///
- /// Reads a single byte from the specified address of the peripheral
- ///
- /// Address to read
- /// The byte read
- public byte ReadRegister(byte address)
- {
- WriteBuffer.Span[0] = address;
- Bus.Exchange(this.ChipSelect, WriteBuffer.Span[0..1], ReadBuffer.Span[0..1], this.chipSelectMode);
- return ReadBuffer.Span[0];
- }
-
- ///
- /// Reads a single ushort value from the specified address of the peripheral
- ///
- /// Address of the read
- /// Endianness of the value read
- /// The value read
- public ushort ReadRegisterAsUShort(byte address, ByteOrder order = ByteOrder.LittleEndian)
- {
- ReadRegister(address, ReadBuffer[0..2].Span);
- if (order == ByteOrder.LittleEndian)
- {
- return (ushort)(ReadBuffer.Span[0] | (ReadBuffer.Span[1] << 8));
- }
- else
- {
- return (ushort)(ReadBuffer.Span[0] << 8 | ReadBuffer.Span[1]);
- }
- }
-
- ///
- /// Writes a single byte to the peripheral
- ///
- /// Value to write
- public void Write(byte value)
- {
- WriteBuffer.Span[0] = value;
- Bus.Write(ChipSelect, WriteBuffer.Span[0..1], this.chipSelectMode);
- }
-
- ///
- /// Write a span of bytes to the peripheral.
- ///
- /// Data to be written.
- public void Write(Span data)
- {
- Bus.Write(this.ChipSelect, data, this.chipSelectMode);
- }
-
- ///
- /// Writes a single byte to the specified address of the peripheral
- ///
- /// The target write register address
- /// Value to write
- public void WriteRegister(byte address, byte value)
- {
- // stuff the address and value into the write buffer
- WriteBuffer.Span[0] = address;
- WriteBuffer.Span[1] = value;
- Bus.Write(ChipSelect, WriteBuffer.Span[0..2], this.chipSelectMode);
- }
-
- ///
- /// Writes a single ushort value to a target register address on the peripheral (i.e. [address][ushort])
- ///
- /// The target write register address
- /// Value to write
- /// Endianness of the value to be written
- public void WriteRegister(byte address, ushort value, ByteOrder order = ByteOrder.LittleEndian)
- {
- // split the 16 bit ushort into two bytes
- var bytes = BitConverter.GetBytes(value);
- // call the helper method
- WriteRegister(address, bytes, order);
- }
-
- ///
- /// Write an unsigned integer to the peripheral.
- ///
- /// Address to write the first byte to.
- /// Value to be written.
- /// Indicate if the data should be written as big or little endian.
- public void WriteRegister(byte address, uint value, ByteOrder order = ByteOrder.LittleEndian)
- {
- // split the 32 bit ushort into four bytes
- var bytes = BitConverter.GetBytes(value);
- // call the helper method
- WriteRegister(address, bytes, order);
- }
-
- ///
- /// Write an unsigned long to the peripheral.
- ///
- /// Address to write the first byte to.
- /// Value to be written.
- /// Indicate if the data should be written as big or little endian.
- public void WriteRegister(byte address, ulong value, ByteOrder order = ByteOrder.LittleEndian)
- {
- // split the 64 bit ushort into eight bytes
- var bytes = BitConverter.GetBytes(value);
- // call the helper method
- WriteRegister(address, bytes, order);
- }
-
- ///
- /// Write data to a register in the peripheral.
- ///
- /// Address of the register to write to.
- /// A buffer of byte values to be written.
- /// Indicate if the data should be written as big or little endian.
- public void WriteRegister(byte address, Span writeBuffer, ByteOrder order = ByteOrder.LittleEndian)
- {
- if (WriteBuffer.Length < writeBuffer.Length + 1)
- {
- throw new ArgumentException("Data to write is too large for the write buffer. " +
- "Must be less than WriteBuffer.Length + 1 (to allow for address). " +
- "Instantiate this class with a larger WriteBuffer, or send a smaller" +
- "amount of data to fix.");
- }
-
- // stuff the register address into the write buffer
- WriteBuffer.Span[0] = address;
-
- // stuff the bytes into the write buffer (starting at `1` index,
- // because `0` is the register address.
- switch (order)
- {
- case ByteOrder.LittleEndian:
- for (int i = 0; i < writeBuffer.Length; i++)
- {
- WriteBuffer.Span[i + 1] = writeBuffer[i];
- }
- break;
- case ByteOrder.BigEndian:
- for (int i = 0; i < writeBuffer.Length; i++)
- {
- // stuff them backwards
- WriteBuffer.Span[i + 1] = writeBuffer[writeBuffer.Length - (i + 1)];
- }
- break;
- }
- // write it
- this.Bus.Write(this.ChipSelect, WriteBuffer.Span[0..(writeBuffer.Length + 1)], this.chipSelectMode);
- }
-
- ///
- /// Exchange data over the SPI bus
- ///
- /// The buffer holding the data to write
- /// The buffer to receieve data
- /// The duplex mode - half or full
- public void Exchange(Span writeBuffer, Span readBuffer, DuplexType duplex = DuplexType.Half)
- {
- if (duplex == DuplexType.Half)
- {
- // Todo: we should move this functionality deeper into the stack
- // and have nuttx write the write buffer, then continue clocking out
- // 0x00's until it's hit writeBuffer.Length + readBuffer.Lenght
- // and ignore the input until it hits writeBuffer.Length, and then
- // start writing directly into the readBuffer starting at 0.
- // that will prevent all the allocations and copying we're doing
- // here.
-
- // clock in and clock out data means that the buffers have to be as
- // long as both tx and rx together
- int length = writeBuffer.Length + readBuffer.Length;
- Span txBuffer = stackalloc byte[length];
- Span rxBuffer = stackalloc byte[length];
-
- // copy the write into tx
- writeBuffer.CopyTo(txBuffer);
-
- // write/read the data
- Bus.Exchange(ChipSelect, txBuffer, rxBuffer, this.chipSelectMode);
-
- // move the rx data into the read buffer, starting it at zero
- rxBuffer[writeBuffer.Length..length].CopyTo(readBuffer);
- }
- else
- {
- Bus.Exchange(ChipSelect, writeBuffer, readBuffer, this.chipSelectMode);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/source/Meadow.Core/Hardware/Counter.cs b/source/Meadow.Core/Hardware/Counter.cs
index 95b7b941..b4ba3887 100644
--- a/source/Meadow.Core/Hardware/Counter.cs
+++ b/source/Meadow.Core/Hardware/Counter.cs
@@ -1,6 +1,4 @@
-using Meadow.Devices;
-using Meadow.Peripherals;
-using System;
+using System;
using System.Threading;
namespace Meadow.Hardware
diff --git a/source/Meadow.Core/Hardware/DigitalChannelInfo.cs b/source/Meadow.Core/Hardware/DigitalChannelInfo.cs
deleted file mode 100644
index a6616505..00000000
--- a/source/Meadow.Core/Hardware/DigitalChannelInfo.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-namespace Meadow.Hardware
-{
- public class DigitalChannelInfo : DigitalChannelInfoBase
- {
- public DigitalChannelInfo(
- string name,
- bool inputCapable = true,
- bool outputCapable = true,
- bool interruptCapable = true,
- bool pullDownCapable = true,
- bool pullUpCapable = true,
- bool inverseLogic = false,
- int? interruptGroup = null
- )
- : base(name, inputCapable, outputCapable, interruptCapable,
- pullDownCapable, pullUpCapable, inverseLogic, interruptGroup)
- {
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/DigitalInputPort.cs b/source/Meadow.Core/Hardware/DigitalInputPort.cs
index 31c5db6b..1cc698c9 100644
--- a/source/Meadow.Core/Hardware/DigitalInputPort.cs
+++ b/source/Meadow.Core/Hardware/DigitalInputPort.cs
@@ -50,7 +50,7 @@ TimeSpan glitchDuration
}
else
{
- throw new PortInUseException($"Pin {pin.Name} is already in use");
+ throw new PortInUseException($"{this.GetType().Name}: Pin {pin.Name} is already in use");
}
}
@@ -63,10 +63,6 @@ public static DigitalInputPort From(
TimeSpan glitchDuration
)
{
- // convert to microseconds
- var debounce = debounceDuration.TotalMilliseconds * 10;
- var glitch = glitchDuration.TotalMilliseconds * 10;
-
var chan = pin.SupportedChannels.OfType().FirstOrDefault();
//TODO: may need other checks here.
if (chan == null)
@@ -77,11 +73,11 @@ TimeSpan glitchDuration
{
throw new Exception("Unable to create input; channel is not capable of interrupts");
}
- if (debounce < 0.0 || debounce > 1000.0)
+ if (debounceDuration.TotalMilliseconds > 1000.0)
{
throw new ArgumentOutOfRangeException(nameof(debounceDuration), "Unable to create an input port, because debounceDuration is out of range (0.1-1000.0)");
}
- if (glitch < 0.0 || glitch > 1000.0)
+ if (glitchDuration.TotalMilliseconds > 1000.0)
{
throw new ArgumentOutOfRangeException(nameof(glitchDuration), "Unable to create an input port, because glitchDuration is out of range (0.1-1000.0)");
}
@@ -110,7 +106,7 @@ void OnInterrupt(IPin pin, bool state)
{
var capturedLastTime = LastEventTime; // note: doing this for latency reasons. kind of. sort of. bad time good time. all time.
this.LastEventTime = DateTime.Now;
- //Console.WriteLine($"event on pin: {pin.Name}, state: {state}");
+ //Resolver.Log.Info($"event on pin: {pin.Name}, state: {state}");
// BC 2021.05.21 b5.0: Changed this to the new result type.
// assuming that old state is just an inversion of the new state if date time isn't min, yeah?
DigitalState? old = (capturedLastTime == DateTime.MinValue) ? null : new DigitalState(!state, capturedLastTime);
diff --git a/source/Meadow.Core/Hardware/DigitalOutputPort.cs b/source/Meadow.Core/Hardware/DigitalOutputPort.cs
index cb32351d..16aba405 100644
--- a/source/Meadow.Core/Hardware/DigitalOutputPort.cs
+++ b/source/Meadow.Core/Hardware/DigitalOutputPort.cs
@@ -51,7 +51,7 @@ protected DigitalOutputPort(
}
else
{
- throw new PortInUseException(success.Item2);
+ throw new PortInUseException($"{this.GetType().Name}: Pin {pin.Name} is already in use");
}
}
diff --git a/source/Meadow.Core/Hardware/Pin.cs b/source/Meadow.Core/Hardware/Pin.cs
deleted file mode 100644
index 70d44c15..00000000
--- a/source/Meadow.Core/Hardware/Pin.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Meadow.Hardware
-{
- ///
- /// Represents a physical pin on the Meadow device.
- ///
- public class Pin : PinBase
- {
- public Pin(string name, object key, IList? supportedChannels = null)
- : base (name, key, supportedChannels)
- {
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/PwmChannelInfo.cs b/source/Meadow.Core/Hardware/PwmChannelInfo.cs
deleted file mode 100644
index 3d2fc816..00000000
--- a/source/Meadow.Core/Hardware/PwmChannelInfo.cs
+++ /dev/null
@@ -1,33 +0,0 @@
- using System;
-namespace Meadow.Hardware
-{
- public class PwmChannelInfo : DigitalChannelInfoBase, IPwmChannelInfo
- {
- public float MinimumFrequency { get; protected set; }
- public float MaximumFrequency { get; protected set; }
- public uint Timer { get; protected set; }
- public uint TimerChannel { get; protected set; }
-
- public PwmChannelInfo(string name,
- uint timer,
- uint timerChannel,
- float minimumFrequency = 0,
- float maximumFrequency = 100000,
- bool pullDownCapable = false, // does this mean anything in PWM?
- bool pullUpCapable = false) // ibid
- : base(
- name,
- inputCapable: true,
- outputCapable: true,
- interruptCapable: false, // ?? i mean, technically, yes, but will we have events?
- pullDownCapable: pullDownCapable,
- pullUpCapable: pullUpCapable,
- inverseLogic: false) //TODO: switch to C# 7.2+ to get rid of trailing names
- {
- this.Timer = timer;
- this.TimerChannel = timerChannel;
- this.MinimumFrequency = minimumFrequency;
- this.MaximumFrequency = maximumFrequency;
- }
- }
-}
diff --git a/source/Meadow.Core/Hardware/SoftPwmPort.cs b/source/Meadow.Core/Hardware/SoftPwmPort.cs
new file mode 100644
index 00000000..031b9617
--- /dev/null
+++ b/source/Meadow.Core/Hardware/SoftPwmPort.cs
@@ -0,0 +1,220 @@
+using Meadow.Units;
+using System;
+using System.Threading;
+
+namespace Meadow.Hardware
+{
+ ///
+ /// A Pulse Width Modulation Generator that can
+ /// generates waveforms in software. The maximum
+ /// Frequency is about 100 Hz.
+ ///
+ /// Note: This class is not yet implemented.
+ ///
+ public class SoftPwmPort : IPwmPort
+ {
+ ///
+ /// Digital output port used for PWM
+ ///
+ protected IDigitalOutputPort Port { get; set; }
+
+ ///
+ /// PWM duration in ms
+ ///
+ public float Duration
+ {
+ get => Period * DutyCycle;
+ set { }
+ }
+
+ ///
+ /// Period of PWM
+ ///
+ public float Period
+ {
+ get => 1 / (float)frequency.Hertz;
+ set => frequency = new Frequency(1 / value, Units.Frequency.UnitType.Hertz);
+ }
+
+ ///
+ /// Is the PWM signal inverted
+ ///
+ public bool Inverted { get; set; }
+
+
+ ///
+ /// Duty cycle of PWM
+ ///
+ public float DutyCycle
+ {
+ get => dutyCycle;
+ set
+ {
+ dutyCycle = value;
+ onTimeMilliseconds = CalculateOnTimeMillis();
+ offTimeMilliseconds = CalculateOffTimeMillis();
+ }
+ }
+ float dutyCycle;
+
+ ///
+ /// Frequency of soft PWM
+ ///
+ public Frequency Frequency
+ {
+ get => frequency;
+ set {
+ frequency = value;
+ onTimeMilliseconds = CalculateOnTimeMillis();
+ offTimeMilliseconds = CalculateOffTimeMillis();
+ }
+ }
+ Frequency frequency = new Frequency(1.0, Units.Frequency.UnitType.Hertz); // in the case it doesn't get set before dutycycle, initialize to 1
+
+ ///
+ /// Channel info for PWM port
+ ///
+ public IPwmChannelInfo Channel {get; protected set;}
+
+ ///
+ /// State of PWM port (running / not running)
+ ///
+ public bool State => running;
+
+ ///
+ /// Pin used for soft PWM
+ ///
+ public IPin Pin => Port.Pin;
+
+ IDigitalChannelInfo IPort.Channel => throw new NotImplementedException();
+
+ ///
+ /// Timescale
+ ///
+ public TimeScale TimeScale
+ {
+ get => TimeScale.Seconds;
+ set { }
+ }
+
+ Thread? thread = null;
+ int onTimeMilliseconds = 0;
+ int offTimeMilliseconds = 0;
+ bool running = false;
+
+ ///
+ /// Instantiate a SoftPwm object that can perform PWM using digital pins
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SoftPwmPort(IMeadowDevice device, IPin outputPin, float dutyCycle = 0.5f, float frequency = 1.0f) :
+ this(device.CreateDigitalOutputPort(outputPin, false), dutyCycle, frequency)
+ {
+ }
+
+ ///
+ /// Instantiate a SoftPwm object that can perform PWM using digital pins
+ ///
+ ///
+ ///
+ ///
+ public SoftPwmPort(IDigitalOutputPort outputPort, float dutyCycle = 0.0f, float frequencyInHertz = 1000)
+ {
+ Port = outputPort;
+ DutyCycle = dutyCycle;
+ frequency = new Frequency(frequencyInHertz, Units.Frequency.UnitType.Hertz);
+
+ Channel = new PwmChannelInfo("SoftPwmChannel", 0, 0, 1000, 1000, false, false);
+ }
+
+ ///
+ /// Start the pulse width modulation
+ ///
+ public void Start()
+ {
+ running = true;
+
+ // create a new thread that actually writes the pwm to the output port
+ thread = new Thread(() =>
+ {
+ while (running)
+ {
+ Port.State = !Inverted;
+ Thread.Sleep(onTimeMilliseconds);
+ Port.State = Inverted;
+ Thread.Sleep(offTimeMilliseconds);
+ }
+ });
+ thread.Start();
+ }
+
+ ///
+ /// Stop the pulse width modulation
+ ///
+ public void Stop()
+ {
+ // setting this will wrap up the thread
+ running = false;
+
+ // need to make sure the port is off, otherwise it can get
+ // stuck in an ON state.
+ Port.State = false;
+ }
+
+ ///
+ /// Calculates the pulse on time in milliseconds
+ ///
+ protected int CalculateOnTimeMillis()
+ {
+ var dc = DutyCycle;
+ // clamp
+ if (dc < 0) dc = 0;
+ if (dc > 1) dc = 1;
+ // on time =
+ return (int)(dc / frequency.Kilohertz);
+ }
+
+ ///
+ /// Calculates the off time of pulse in milliseconds
+ ///
+ ///
+ protected int CalculateOffTimeMillis()
+ {
+ var dc = DutyCycle;
+ // clamp
+ if (dc < 0) dc = 0;
+ if (dc > 1) dc = 1;
+ // off time =
+ return (int)((1 - dc) / frequency.Kilohertz);
+ }
+
+ private bool disposedValue = false; // To detect redundant calls
+
+ ///
+ /// Dispose soft PWM
+ ///
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ }
+
+ disposedValue = true;
+ }
+ }
+
+ ///
+ /// Dispose soft pwm port
+ ///
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/Meadow.Core/Meadow.Core.csproj b/source/Meadow.Core/Meadow.Core.csproj
index eb951831..e9cd1442 100644
--- a/source/Meadow.Core/Meadow.Core.csproj
+++ b/source/Meadow.Core/Meadow.Core.csproj
@@ -28,12 +28,10 @@
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/source/Meadow.Core/MeadowOS.SystemInformation.cs b/source/Meadow.Core/MeadowOS.SystemInformation.cs
index 715118bc..ac85798c 100644
--- a/source/Meadow.Core/MeadowOS.SystemInformation.cs
+++ b/source/Meadow.Core/MeadowOS.SystemInformation.cs
@@ -21,7 +21,7 @@ public static class SystemInformation
/// Get the mono version on the device.
///
/// Mono version.
- public static string MonoVersion => CurrentDevice.PlatformOS.MonoVersion;
+ public static string RuntimeVersion => CurrentDevice.PlatformOS.RuntimeVersion;
///
/// Should the system reboot if an unhandled exception is encounted in the user application?
diff --git a/source/Meadow.Core/MeadowOS.cs b/source/Meadow.Core/MeadowOS.cs
index 0efc4f6e..0fb52c1c 100644
--- a/source/Meadow.Core/MeadowOS.cs
+++ b/source/Meadow.Core/MeadowOS.cs
@@ -14,7 +14,7 @@
public static partial class MeadowOS
{
//==== internals
- private static SynchronizationContext? synchronizationContext;
+ private static bool _startedDirectly = false; // true when this assembly is the entry point
//==== properties
internal static IMeadowDevice CurrentDevice { get; private set; } = null!;
@@ -31,6 +31,16 @@ public static partial class MeadowOS
///
///
public async static Task Main(string[] _)
+ {
+ _startedDirectly = true;
+ await Start();
+ }
+
+ ///
+ /// Initializes and starts up the Meadow Core software stack
+ ///
+ ///
+ public async static Task Start()
{
bool systemInitialized = false;
try
@@ -208,11 +218,21 @@ private static Type FindAppType()
{
Resolver.Log.Trace($"Looking for app assembly...");
- // support app.exe or app.dll
- var assembly = FindByPath(new string[] { "App.dll", "App.exe", "app.dll", "app.exe" });
+ Assembly appAssembly;
- if (assembly == null) throw new Exception("No 'App' assembly found. Expected either App.exe or App.dll");
+ if (_startedDirectly)
+ {
+ // support app.exe or app.dll
+ appAssembly = FindByPath(new string[] { "App.dll", "App.exe", "app.dll", "app.exe" });
+
+ if (appAssembly == null) throw new Exception("No 'App' assembly found. Expected either App.exe or App.dll");
+ }
+ else
+ {
+ appAssembly = Assembly.GetEntryAssembly();
+ }
+ // === LOCAL METHOD ===
Assembly? FindByPath(string[] namesToCheck)
{
var root = AppDomain.CurrentDomain.BaseDirectory;
@@ -240,7 +260,7 @@ private static Type FindAppType()
Resolver.Log.Trace($"Looking for IApp...");
var searchType = typeof(IApp);
Type? appType = null;
- foreach (var type in assembly.GetTypes())
+ foreach (var type in appAssembly.GetTypes())
{
if (searchType.IsAssignableFrom(type) && !type.IsAbstract)
{
@@ -268,7 +288,7 @@ private static bool Initialize()
catch (Exception ex)
{
// must use Console because the logger failed
- Console.WriteLine($"Failed to create Logger: {ex.Message}");
+ Resolver.Log.Error($"Failed to create Logger: {ex.Message}");
return false;
}
@@ -334,7 +354,7 @@ private static bool Initialize()
App = app;
- var updateService = new UpdateService(UpdateSettings);
+ var updateService = new UpdateService(CurrentDevice.PlatformOS.FileSystemRoot, UpdateSettings);
Resolver.Services.Add(updateService);
Resolver.Log.Info($"Update Service is {(UpdateSettings.Enabled ? "enabled" : "disabled")}.");
@@ -347,7 +367,7 @@ private static bool Initialize()
}
catch (Exception e)
{
- Console.WriteLine(e);
+ Resolver.Log.Error(e.ToString());
return false;
}
}
diff --git a/source/Meadow.Core/MeadowSynchronizationContext.cs b/source/Meadow.Core/MeadowSynchronizationContext.cs
index d708eddb..3f3e8dce 100644
--- a/source/Meadow.Core/MeadowSynchronizationContext.cs
+++ b/source/Meadow.Core/MeadowSynchronizationContext.cs
@@ -17,7 +17,7 @@ public override void Post(SendOrPostCallback action, object state)
}
catch (Exception e)
{
- Console.WriteLine ($"!!! {e.GetType()}: {e.Message}");
+ Resolver.Log.Error($"!!! {e.GetType()}: {e.Message}");
}
};
ThreadPool.QueueUserWorkItem(new WaitCallback(action.Invoke));
diff --git a/source/Meadow.Core/Update/UpdateService.cs b/source/Meadow.Core/Update/UpdateService.cs
index fc2c9852..3881a711 100644
--- a/source/Meadow.Core/Update/UpdateService.cs
+++ b/source/Meadow.Core/Update/UpdateService.cs
@@ -36,27 +36,15 @@ public class UpdateService : IUpdateService
private UpdateState _state;
private IUpdateSettings Config { get; }
- private IMqttClient MqttClient { get; set; }
- private IMqttClientOptions ClientOptions { get; set; }
+ private IMqttClient MqttClient { get; set; } = default!;
+ private IMqttClientOptions ClientOptions { get; set; } = default!;
private UpdateStore Store { get; }
private string MqttClientID { get; }
- internal UpdateService(IUpdateSettings config)
+ internal UpdateService(string fsRoot, IUpdateSettings config)
{
- string root;
-
- // NOTE: this is a temporary path for desktop testing right now
- if (Environment.OSVersion.Platform == PlatformID.Win32NT)
- {
- root = Path.Combine(Path.GetTempPath(), "meadow");
- }
- else
- {
- root = "/meadow0";
- }
-
- UpdateStoreDirectory = Path.Combine(root, "update-store");
- UpdateDirectory = Path.Combine(root, "update");
+ UpdateStoreDirectory = Path.Combine(fsRoot, "update-store");
+ UpdateDirectory = Path.Combine(fsRoot, "update");
Store = new UpdateStore(UpdateStoreDirectory);
var id = Resolver.Device?.Information?.UniqueID ?? "simple";
diff --git a/source/Meadow.Core/Utilities/BitHelpers.cs b/source/Meadow.Core/Utilities/BitHelpers.cs
deleted file mode 100644
index ee4cb280..00000000
--- a/source/Meadow.Core/Utilities/BitHelpers.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-namespace Meadow.Utilities
-{
- public static class BitHelpers
- {
- ///
- /// Returns a new byte mask based on the input mask, with a single
- /// bit set. To the passed in value.
- ///
- /// The original byte mask value.
- /// The index of the bit to set.
- /// The value to set the bit. Should be 0 or 1.
- public static byte SetBit(byte mask, byte bitIndex, byte value)
- {
- return SetBit(mask, bitIndex, (value == 0) ? false : true);
- }
-
- ///
- /// Returns a new byte mask based on the input mask, with a single
- /// bit set. To the passed in value.
- ///
- /// The original byte mask value.
- /// The index of the bit to set.
- /// The value to set the bit. true for 1, false for 0.
- ///
- public static byte SetBit(byte mask, byte bitIndex, bool value)
- {
- byte newMask = mask;
-
- if (value)
- {
- newMask |= (byte)(1 << bitIndex);
- }
- else
- {
- newMask &= (byte)~(1 << bitIndex); // tricky to zero
- }
-
- return newMask;
- }
-
- ///
- /// Returns a new 16-bit short with the single bit set or cleared
- ///
- /// The original value
- /// The index of the bit to affect
- /// True to set, False to clear
- ///
- public static short SetBit(short mask, byte bitIndex, bool value)
- {
- short b = mask;
- if (value)
- {
- return (short)(b | (short)(1 << bitIndex));
- }
- return (short)(b & (short)(~(1 << bitIndex)));
- }
-
- ///
- /// Returns the value of the mask at the given ordinal.
- ///
- ///
- ///
- ///
- public static bool GetBitValue(byte mask, byte bitIndex)
- {
-
- return ((mask & (byte)(1 << bitIndex)) != 0) ? true : false;
- }
-
- ///
- /// Determines if a specified bit in a 16-bit short is set
- ///
- /// The value to check
- /// The index of the bit to check
- ///
- public static bool GetBitValue(short mask, byte bitIndex)
- {
- if ((mask & (short)(1 << bitIndex)) == 0)
- {
- return false;
- }
- return true;
- }
-
- ///
- /// Converts the first 2 bytes of an array to a little-endian 16-bit short
- ///
- ///
- ///
- public static short ToInt16(byte[] data)
- {
- return (short)(data[0] | (data[1] << 8));
- }
- }
-}
diff --git a/source/Meadow.Core/Utilities/Output.cs b/source/Meadow.Core/Utilities/Output.cs
index 42b4086e..9c76f1a8 100644
--- a/source/Meadow.Core/Utilities/Output.cs
+++ b/source/Meadow.Core/Utilities/Output.cs
@@ -16,7 +16,7 @@ public static void WriteIf(bool test, string value)
{
if (test)
{
- Console.Write(value);
+ Resolver.Log.Info(value);
}
}
@@ -25,20 +25,20 @@ public static void WriteLineIf(bool test, string value)
{
if (test)
{
- Console.WriteLine(value);
+ Resolver.Log.Info(value);
}
}
[Conditional("DEBUG")]
public static void Write(string value)
{
- Console.Write(value);
+ Resolver.Log.Info(value);
}
[Conditional("DEBUG")]
public static void WriteLine(string value)
{
- Console.WriteLine(value);
+ Resolver.Log.Info(value);
}
///
diff --git a/source/Meadow.F7/Devices/Definitions/F7CoreCompute.Pinout.cs b/source/Meadow.F7/Devices/Definitions/F7CoreCompute.Pinout.cs
deleted file mode 100644
index a9c8c80f..00000000
--- a/source/Meadow.F7/Devices/Definitions/F7CoreCompute.Pinout.cs
+++ /dev/null
@@ -1,396 +0,0 @@
-using Meadow.Hardware;
-using System.Collections;
-using System.Collections.Generic;
-
-namespace Meadow.Devices
-{
- public partial class F7CoreComputeV2
- {
- private const int ADCPrecisionBits = 12;
-
- public class SerialPortNameDefinitions
- {
- public SerialPortName Com1 { get; } = new SerialPortName("COM1", "ttyS0");
- public SerialPortName Com4 { get; } = new SerialPortName("COM4", "ttyS1");
- }
-
- public partial class Pinout : IF7CoreComputePinout
- {
- public IList AllPins => new List
- {
- A00, A01, A02, A03, A04, A05,
- SPI3_SCK, SPI3_CIPO, SPI3_COPI, SPI5_SCK, SPI5_CIPO, SPI5_COPI,
- D00, D01, D02, D03, D04, D05, D06, D07, D08, D09, D10, D11, D12, D13, D14, D15,
- D16, D17, D18, D19, D20,
- ESP_COPI, ESP_CIPO, ESP_CLK, ESP_CS, ESP_BOOT, ESP_RST, ESP_UART_RX, ESP_UART_TX,
- I2C1_SCL, I2C1_SDA, I2C3_SCL, I2C3_SDA,
- };
-
- // ==== SPI ====
- public IPin SPI3_SCK => new Pin(
- "SPI3_SCK", "PC10",
- new List {
- new DigitalChannelInfo("PC10", interruptCapable: false),
- new SpiChannelInfo("SPI3_SCK", SpiLineType.Clock)
- }
- );
-
- public IPin SPI3_CIPO => new Pin(
- "SPI3_CIPO", "PC11",
- new List {
- new DigitalChannelInfo("PC11", interruptGroup: 11),
- new SpiChannelInfo("SPI3_CIPO", SpiLineType.MISO)
- }
- );
-
- public IPin SPI3_COPI => new Pin(
- "SPI3_COPI", "PB5",
- new List {
- new DigitalChannelInfo("PB5", interruptGroup: 5),
- new SpiChannelInfo("SPI3_COPI", SpiLineType.MOSI)
- }
- );
-
- public IPin SPI5_SCK => new Pin(
- "SPI5_SCK", "PH6",
- new List {
- new DigitalChannelInfo("PH6", interruptGroup: 6),
- new SpiChannelInfo("SPI5_SCK", SpiLineType.Clock)
- }
- );
-
- public IPin SPI5_CIPO => new Pin(
- "SPI5_CIPO", "PF8",
- new List {
- new DigitalChannelInfo("PF8", interruptGroup: 8),
- new SpiChannelInfo("SPI5_CIPO", SpiLineType.MISO)
- }
- );
-
- public IPin SPI5_COPI => new Pin(
- "SPI5_COPI", "PF9",
- new List {
- new DigitalChannelInfo("PF9", interruptGroup: 9),
- new SpiChannelInfo("SPI5_COPI", SpiLineType.MOSI)
- }
- );
-
- // ==== A2D ====
- public IPin A00 => new Pin(
- "A00", "PA4",
- new List {
- new DigitalChannelInfo("PA4", interruptGroup: 4),
- new AnalogChannelInfo("ADC1_IN4", ADCPrecisionBits, true, false)
- }
- );
-
- public IPin A01 => new Pin(
- "A01", "PA5",
- new List {
- new DigitalChannelInfo("PA5", interruptGroup: 5),
- new AnalogChannelInfo("ADC1_IN5", ADCPrecisionBits, true, false)
- }
- );
-
- public IPin A02 => new Pin(
- "A02", "PA3",
- new List {
- new DigitalChannelInfo("PA3", interruptGroup: 3),
- new AnalogChannelInfo("ADC1_IN3", ADCPrecisionBits, true, false)
- }
- );
-
- public IPin A03 => new Pin(
- "A03", "PB0",
- new List {
- new DigitalChannelInfo("PB0", interruptGroup: 0),
- new AnalogChannelInfo("ADC1_IN8", ADCPrecisionBits, true, false) // TODO: should we move this to the second ADC?
- }
- );
-
- public IPin A04 => new Pin(
- "A04", "PB1",
- new List {
- new DigitalChannelInfo("PB1", interruptGroup: 1),
- new AnalogChannelInfo("ADC1_IN9", ADCPrecisionBits, true, false)
- }
- );
-
- public IPin A05 => new Pin(
- "A05", "PC0",
- new List {
- new DigitalChannelInfo("PC0", interruptGroup: 0),
- new AnalogChannelInfo("ADC1_IN10", ADCPrecisionBits, true, false)
- }
- );
-
- // ==== DISCRETES ====
-
- public IPin D00 => new Pin(
- "D00", "PI9",
- new List {
- new DigitalChannelInfo("PI9", interruptGroup: 9),
- new UartChannelInfo("UART4_RX", SerialDirectionType.Receive)
- }
- );
-
- // TODO: shared with ETH_IRQ?
- public IPin D01 => new Pin(
- "D01", "PH13",
- new List {
- new DigitalChannelInfo("PH13", interruptGroup: 13),
- new UartChannelInfo("UART4_TX", SerialDirectionType.Transmit)
- }
- );
-
- public IPin D02 => new Pin(
- "D02", "PH10",
- new List {
- new DigitalChannelInfo("PH10", interruptCapable: false),
- new PwmChannelInfo("TIM5_CH1", 5, 1)
- }
- );
-
- public IPin D03 => new Pin(
- "D03", "PB8",
- new List {
- new DigitalChannelInfo("PB8", interruptGroup: 8),
- new PwmChannelInfo("TIM4_CH3", 4, 3)
- }
- );
-
- public IPin D04 => new Pin(
- "D04", "PB9",
- new List {
- new DigitalChannelInfo("PB9", interruptGroup: 9),
- new PwmChannelInfo("TIM4_CH4", 4, 4),
- }
- );
-
- public IPin D05 => new Pin(
- "D05", "PB4",
- new List {
- new DigitalChannelInfo("PB4", interruptGroup: 4),
- new PwmChannelInfo("TIM3_CH1", 3, 1),
- }
- );
-
- public IPin D06 => new Pin(
- "D06", "PB13",
- new List {
- new DigitalChannelInfo("PB13", interruptGroup: 13)
- }
- );
-
- public IPin D07 => new Pin(
- "D07", "PB7",
- new List {
- new DigitalChannelInfo("PB7", interruptGroup: 7),
- new PwmChannelInfo("TIM4_CH2", 4, 2),
- new I2cChannelInfo("I2C1_SDA", I2cChannelFunctionType.Data)
- }
- );
-
- public IPin D08 => new Pin(
- "D08", "PB6",
- new List {
- new DigitalChannelInfo("PB6", interruptGroup: 6),
- new PwmChannelInfo("TIM4_CH1", 4, 1),
- new I2cChannelInfo("I2C1_SCL", I2cChannelFunctionType.Clock)
- }
- );
-
- public IPin D09 => new Pin(
- "D09", "PC6",
- new List {
- new DigitalChannelInfo("PC6", interruptGroup: 6),
- new PwmChannelInfo("TIM8_CH1", 8, 1), // or TIM3_CH1 (see D05)
- }
- );
-
- public IPin D10 => new Pin(
- "D10", "PC7",
- new List {
- new DigitalChannelInfo("PC7", interruptGroup: 7),
- new PwmChannelInfo("TIM3_CH2", 3, 2) // or TIM8_CH2
- }
- );
-
- public IPin D11 => new Pin(
- "D11", "PC9",
- new List {
- new DigitalChannelInfo("PC9", interruptGroup: 9),
- new PwmChannelInfo("TIM8_CH4", 8, 4)
- }
- );
-
- public IPin D12 => new Pin(
- "D12", "PB14",
- new List {
- new DigitalChannelInfo("PB14", interruptGroup: 14),
- new PwmChannelInfo("TIM12_CH1", 12, 1),
- new UartChannelInfo("COM1_TX", SerialDirectionType.Transmit)
- }
- );
-
- public IPin D13 => new Pin(
- "D13", "PB15",
- new List {
- new DigitalChannelInfo("PB15", interruptGroup: 15),
- new PwmChannelInfo("TIM12_CH2", 12, 2),
- new UartChannelInfo("COM11_RX", SerialDirectionType.Receive)
- }
- );
-
- public IPin D14 => new Pin(
- "D14", "PB12",
- new List {
- new DigitalChannelInfo("PB12", interruptGroup: 12)
- }
- );
-
- // TODO: shared with SDMMC2_D3?
- public IPin D15 => new Pin(
- "D15", "PG12",
- new List
- {
- // consumed by SD card, so no capability, but required by the I32PinFeatherBoardPinout interface
- }
- );
-
- // TODO: shared with SDMMC2_D3?
- public IPin D16 => new Pin(
- "D16", "PI11",
- new List {
- new DigitalChannelInfo("PI11", interruptGroup: 11)
- }
- );
-
- public IPin D17 => new Pin(
- "D17", "PD5",
- new List {
- new DigitalChannelInfo("PD5", interruptGroup: 5)
- }
- );
-
- public IPin D18 => new Pin(
- "D18", "PA10",
- new List {
- new DigitalChannelInfo("PA10", interruptCapable: false)
- }
- );
-
- public IPin D19 => new Pin(
- "D19", "PC8",
- new List {
- new DigitalChannelInfo("PC8", interruptGroup: 8)
- }
- );
-
- // ==== BLINKY on breakout ====
- public IPin D20 => new Pin(
- "D20", "PA0",
- new List {
- new DigitalChannelInfo("PA0", interruptGroup: 0),
- new PwmChannelInfo("TIM2_CH1", 2, 1)
- }
- );
-
- public IPin I2C3_SDA => new Pin(
- "I2C3_SDA", "PH7",
- new List {
- new DigitalChannelInfo("PH7", interruptGroup: 7),
- new I2cChannelInfo("I2C3_SDA", I2cChannelFunctionType.Data)
- }
- );
-
- public IPin I2C3_SCL => new Pin(
- "I2C3_SCL", "PH8",
- new List {
- new DigitalChannelInfo("PH8", interruptGroup: 8),
- new I2cChannelInfo("I2C3_SCL", I2cChannelFunctionType.Clock)
- }
- );
-
-
- // ==== ESP32 ====
- public IPin ESP_COPI => new Pin(
- "ESP_MOSI", "PI3",
- new List {
- new DigitalChannelInfo("PI3"),
- new SpiChannelInfo("PI3", SpiLineType.MOSI)
- }
- );
-
- public IPin ESP_CIPO => new Pin(
- "ESP_MISO", "PI2",
- new List {
- new DigitalChannelInfo("PI2", interruptGroup: 12),
- new SpiChannelInfo("PI2", SpiLineType.MISO)
- }
- );
-
- public IPin ESP_CLK => new Pin(
- "ESP_CLK", "PD3",
- new List {
- new DigitalChannelInfo("PD3", interruptGroup: 3),
- new SpiChannelInfo("PD3", SpiLineType.Clock)
- }
- );
-
- public IPin ESP_CS => new Pin(
- "ESP_CS", "PI0",
- new List {
- new DigitalChannelInfo("PI0", interruptGroup: 0),
- new SpiChannelInfo("PI10", SpiLineType.ChipSelect)
- }
- );
-
- public IPin ESP_BOOT => new Pin(
- "ESP_BOOT", "PI10",
- new List {
- new DigitalChannelInfo("PI10", interruptCapable: false),
- }
- );
-
- public IPin ESP_RST => new Pin(
- "ESP_RST", "PF7",
- new List {
- new DigitalChannelInfo("PF7", interruptGroup: 7),
- }
- );
-
- public IPin ESP_UART_RX => new Pin(
- "ESP_UART5_RX", "PD2",
- new List {
- new DigitalChannelInfo("PD2", interruptGroup: 2),
- }
- );
-
- public IPin ESP_UART_TX => new Pin(
- "ESP_UART5_TX", "PC12",
- new List {
- new DigitalChannelInfo("PC12", interruptGroup: 12),
- }
- );
-
- public IPin SD_IN_L => new Pin(
- "SD_IN_L", "PG6",
- new List {
- new DigitalChannelInfo("PG6", interruptCapable: false)
- }
- );
-
-
- // ==== ALIASES ====
- public IPin I2C1_SDA => D07;
- public IPin I2C1_SCL => D08;
- public IPin SCK => SPI3_SCK;
- public IPin COPI => SPI3_COPI;
- public IPin CIPO => SPI3_CIPO;
-
- public IEnumerator GetEnumerator() => AllPins.GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- }
- }
-}
diff --git a/source/Meadow.F7/Devices/Definitions/IF7CoreComputePinout.cs b/source/Meadow.F7/Devices/Definitions/IF7CoreComputePinout.cs
deleted file mode 100644
index 67bf2ac5..00000000
--- a/source/Meadow.F7/Devices/Definitions/IF7CoreComputePinout.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace Meadow.Hardware
-{
- public interface IF7CoreComputePinout : I32PinFeatherBoardPinout, IEspCoprocessorPinout, IPinDefinitions
- {
- IPin I2C1_SCL { get; }
- IPin I2C1_SDA { get; }
- IPin I2C3_SCL { get; }
- IPin I2C3_SDA { get; }
- IPin SPI3_SCK { get; }
- IPin SPI3_COPI { get; }
- IPin SPI3_CIPO { get; }
- IPin SPI5_SCK { get; }
- IPin SPI5_COPI { get; }
- IPin SPI5_CIPO { get; }
- IPin D16 { get; }
- IPin D17 { get; }
- IPin D18 { get; }
- IPin D19 { get; }
- IPin D20 { get; }
- IPin SD_IN_L { get; }
- }
-}
diff --git a/source/Meadow.F7/Hardware/Communications/SpiChannelInfo.cs b/source/Meadow.F7/Hardware/Communications/SpiChannelInfo.cs
deleted file mode 100644
index 089e0e37..00000000
--- a/source/Meadow.F7/Hardware/Communications/SpiChannelInfo.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Meadow.Hardware
-{
- public class SpiChannelInfo : DigitalChannelInfoBase, ISpiChannelInfo
- {
- public SpiLineType LineTypes { get; protected set; }
-
- public SpiChannelInfo(string name,
- SpiLineType lineTypes,
- bool pullDownCapable = false,
- bool pullUpCapable = false)
- : base (
- name,
- inputCapable: true,
- outputCapable: true,
- interruptCapable: false, // ?? i mean, technically, yes, but will we have events?
- pullDownCapable: pullDownCapable,
- pullUpCapable: pullUpCapable,
- inverseLogic: false) //TODO: switch to C# 7.2+ to get rid of trailing names
- {
- LineTypes = lineTypes;
- }
- }
-}
diff --git a/source/Meadow.F7/Hardware/Communications/UartChannelInfo.cs b/source/Meadow.F7/Hardware/Communications/UartChannelInfo.cs
deleted file mode 100644
index 1719868e..00000000
--- a/source/Meadow.F7/Hardware/Communications/UartChannelInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-namespace Meadow.Hardware
-{
- public class UartChannelInfo : DigitalChannelInfoBase, IUartChannelInfo
- {
- public SerialDirectionType SerialDirection { get; protected set; }
-
- public UartChannelInfo(string name,
- SerialDirectionType serialDirection,
- bool pullDownCapable = false,
- bool pullUpCapable = false)
- : base(
- name,
- inputCapable: true,
- outputCapable: true,
- interruptCapable: false, // ?? i mean, technically, yes, but will we have events?
- pullDownCapable: pullDownCapable,
- pullUpCapable: pullUpCapable,
- inverseLogic: false) //TODO: switch to C# 7.2+ to get rid of trailing names
- {
- this.SerialDirection = serialDirection;
- }
- }
-}
diff --git a/source/Tests/Core.Unit.Tests/CircularBuffer_ExtensionMethod_Tests.cs b/source/Tests/Core.Unit.Tests/CircularBuffer_ExtensionMethod_Tests.cs
index 3b864f88..5fde5732 100644
--- a/source/Tests/Core.Unit.Tests/CircularBuffer_ExtensionMethod_Tests.cs
+++ b/source/Tests/Core.Unit.Tests/CircularBuffer_ExtensionMethod_Tests.cs
@@ -21,7 +21,7 @@ public void TestContains()
buffer.Append(b);
}
- //Console.WriteLine($"Buffer.Count():{buffer.Count()}");
+ //Resolver.Log.Info($"Buffer.Count():{buffer.Count()}");
byte[] searchPattern = new byte[] { 3, 4, 5 };
diff --git a/source/implementations/f7/Meadow.F7/Devices/Definitions/F7CoreCompute.Pinout.cs b/source/implementations/f7/Meadow.F7/Devices/Definitions/F7CoreCompute.Pinout.cs
new file mode 100644
index 00000000..f4150a70
--- /dev/null
+++ b/source/implementations/f7/Meadow.F7/Devices/Definitions/F7CoreCompute.Pinout.cs
@@ -0,0 +1,522 @@
+using Meadow.Hardware;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Meadow.Devices
+{
+ public partial class F7CoreComputeV2
+ {
+ private const int ADCPrecisionBits = 12;
+
+ public partial class Pinout : IF7CoreComputePinout
+ {
+ public IList AllPins { get; }
+
+ public Pinout()
+ {
+ AllPins = new List();
+
+ foreach (var pin in this.GetType()
+ .GetProperties()
+ .Where(p => p.PropertyType is IPin)
+ .Select(p => p.GetValue(this) as IPin))
+ {
+ if (pin != null)
+ {
+ AllPins.Add(pin);
+ }
+ }
+ }
+
+ // ==== DISCRETES ====
+
+ public IPin PA0 => new Pin(
+ "PA0", "PA0",
+ new List {
+ new DigitalChannelInfo("PA0", interruptGroup: 0),
+ new PwmChannelInfo("TIM2_CH1", 2, 1)
+ }
+ );
+
+ public IPin PA1_ETH_REF_CLK => new Pin(
+ "PA1", "PA1",
+ new List {
+ new DigitalChannelInfo("PA1", interruptGroup: 1),
+ }
+ );
+
+ public IPin PA2_ETH_MDIO => new Pin(
+ "PA2", "PA2",
+ new List {
+ new DigitalChannelInfo("PA2", interruptGroup: 2),
+ }
+ );
+
+ public IPin PA3 => new Pin(
+ "PA3", "PA3",
+ new List {
+ new DigitalChannelInfo("PA3", interruptGroup: 3),
+ new AnalogChannelInfo("ADC1_IN3", ADCPrecisionBits, true, false)
+ }
+ );
+
+ public IPin PA4 => new Pin(
+ "PA4", "PA4",
+ new List {
+ new DigitalChannelInfo("PA4", interruptGroup: 4),
+ new AnalogChannelInfo("ADC1_IN4", ADCPrecisionBits, true, false)
+ }
+ );
+
+ public IPin PA5 => new Pin(
+ "PA5", "PA5",
+ new List {
+ new DigitalChannelInfo("PA5", interruptGroup: 5),
+ new AnalogChannelInfo("ADC1_IN5", ADCPrecisionBits, true, false)
+ }
+ );
+
+ public IPin PA7_ETH_CRS_DV => new Pin(
+ "PA7", "PA7",
+ new List {
+ new DigitalChannelInfo("PA7", interruptGroup: 7),
+ }
+ );
+
+ public IPin PA9 => new Pin(
+ "PA9", "PA9",
+ new List {
+ new DigitalChannelInfo("PA9", interruptGroup: 9),
+ }
+ );
+
+ public IPin PA10 => new Pin(
+ "PA10", "PA10",
+ new List {
+ new DigitalChannelInfo("PA10", interruptCapable: false),
+ }
+ );
+
+ public IPin PA13 => new Pin(
+ "PA13", "PA13",
+ new List {
+ new DigitalChannelInfo("PA13", interruptGroup: 13),
+ }
+ );
+
+ public IPin PA14 => new Pin(
+ "PA14", "PA14",
+ new List {
+ new DigitalChannelInfo("PA14", interruptGroup: 14),
+ }
+ );
+
+ public IPin PA15 => new Pin(
+ "PA15", "PA15",
+ new List {
+ new DigitalChannelInfo("PA15", interruptGroup: 15),
+ }
+ );
+
+ public IPin PB0 => new Pin(
+ "PB0", "PB0",
+ new List {
+ new DigitalChannelInfo("PB0", interruptGroup: 0),
+ new AnalogChannelInfo("ADC1_IN8", ADCPrecisionBits, true, false)
+ }
+ );
+
+ public IPin PB1 => new Pin(
+ "PB1", "PB1",
+ new List {
+ new DigitalChannelInfo("PB1", interruptGroup: 1),
+ new AnalogChannelInfo("ADC1_IN9", ADCPrecisionBits, true, false)
+ }
+ );
+
+ public IPin PB3 => new Pin(
+ "PB3", "PB3",
+ new List {
+ new DigitalChannelInfo("PB3", interruptGroup: 3),
+ }
+ );
+
+ public IPin PB4 => new Pin(
+ "PB4", "PB4",
+ new List {
+ new DigitalChannelInfo("PB4", interruptGroup: 4),
+ new PwmChannelInfo("TIM3_CH1", 3, 1),
+ }
+ );
+
+ public IPin PB5 => new Pin(
+ "PB5", "PB5",
+ new List {
+ new DigitalChannelInfo("PB5", interruptGroup: 5),
+ new SpiChannelInfo("SPI3_COPI", SpiLineType.COPI)
+ }
+ );
+
+ public IPin PB6 => new Pin(
+ "PB6", "PB6",
+ new List {
+ new DigitalChannelInfo("PB6", interruptGroup: 6),
+ new PwmChannelInfo("TIM4_CH1", 4, 1),
+ new I2cChannelInfo("I2C1_SCL", I2cChannelFunctionType.Clock)
+ }
+ );
+
+ public IPin PB7 => new Pin(
+ "PB7", "PB7",
+ new List {
+ new DigitalChannelInfo("PB7", interruptGroup: 7),
+ new PwmChannelInfo("TIM4_CH2", 4, 2),
+ new I2cChannelInfo("I2C1_SDA", I2cChannelFunctionType.Data)
+ }
+ );
+
+ public IPin PB8 => new Pin(
+ "PB8", "PB8",
+ new List {
+ new DigitalChannelInfo("PB8", interruptGroup: 8),
+ new PwmChannelInfo("TIM4_CH3", 4, 3)
+ }
+ );
+
+ public IPin PB9 => new Pin(
+ "PB9", "PB9",
+ new List {
+ new DigitalChannelInfo("PB9", interruptGroup: 9),
+ new PwmChannelInfo("TIM4_CH4", 4, 4),
+ }
+ );
+
+ public IPin PB11_ETH_TX_EN => new Pin(
+ "PB11", "PB11",
+ new List {
+ new DigitalChannelInfo("PB11", interruptGroup: 11),
+ }
+ );
+
+ public IPin PB12 => new Pin(
+ "PB12", "PB12",
+ new List {
+ new DigitalChannelInfo("PB12", interruptGroup: 12),
+ }
+ );
+
+ public IPin PB13 => new Pin(
+ "PB13", "PB13",
+ new List {
+ new DigitalChannelInfo("PB13", interruptGroup: 13),
+ }
+ );
+
+ public IPin PB14 => new Pin(
+ "PB14", "PB14",
+ new List {
+ new DigitalChannelInfo("PB14", interruptGroup: 14),
+ new PwmChannelInfo("TIM12_CH1", 12, 1),
+ new UartChannelInfo("COM1_TX", SerialDirectionType.Transmit)
+ }
+ );
+
+ public IPin PB15 => new Pin(
+ "PB15", "PB15",
+ new List {
+ new DigitalChannelInfo("PB15", interruptGroup: 15),
+ new PwmChannelInfo("TIM12_CH2", 12, 2),
+ new UartChannelInfo("COM11_RX", SerialDirectionType.Receive)
+ }
+ );
+
+ public IPin PC0 => new Pin(
+ "PC0", "PC0",
+ new List {
+ new DigitalChannelInfo("PC0", interruptGroup: 0),
+ new AnalogChannelInfo("ADC1_IN10", ADCPrecisionBits, true, false)
+ }
+ );
+
+ public IPin PC1_ETH_MDC => new Pin(
+ "PC1", "PC1",
+ new List {
+ new DigitalChannelInfo("PC1", interruptGroup: 1),
+ }
+ );
+
+ public IPin PC2 => new Pin(
+ "PC2", "PC2",
+ new List {
+ new DigitalChannelInfo("PC2", interruptGroup: 2),
+ }
+ );
+
+ public IPin PC4_ETH_RXD0 => new Pin(
+ "PC4", "PC4",
+ new List {
+ new DigitalChannelInfo("PC4", interruptGroup: 4),
+ }
+ );
+
+ public IPin PC5_ETH_RXD1 => new Pin(
+ "PC5", "PC5",
+ new List {
+ new DigitalChannelInfo("PC5", interruptGroup: 5),
+ }
+ );
+
+ public IPin PC6 => new Pin(
+ "PC6", "PC6",
+ new List {
+ new DigitalChannelInfo("PC6", interruptGroup: 6),
+ new PwmChannelInfo("TIM8_CH1", 8, 1), // or TIM3_CH1 (see D05)
+ }
+ );
+
+ public IPin PC7 => new Pin(
+ "PC7", "PC7",
+ new List {
+ new DigitalChannelInfo("PC7", interruptGroup: 7),
+ new PwmChannelInfo("TIM3_CH2", 3, 2) // or TIM8_CH2
+ }
+ );
+
+ public IPin PC8 => new Pin(
+ "PC8", "PC8",
+ new List {
+ new DigitalChannelInfo("PC8", interruptGroup: 8),
+ }
+ );
+
+ public IPin PC9 => new Pin(
+ "PC9", "PC9",
+ new List {
+ new DigitalChannelInfo("PC9", interruptGroup: 9),
+ new PwmChannelInfo("TIM8_CH4", 8, 4)
+ }
+ );
+
+ public IPin PC10 => new Pin(
+ "PC10", "PC10",
+ new List {
+ new DigitalChannelInfo("PC10", inputCapable: false ),
+ new SpiChannelInfo("SPI3_SCK", SpiLineType.Clock)
+ }
+ );
+
+ public IPin PC11 => new Pin(
+ "PC11", "PC11",
+ new List {
+ new DigitalChannelInfo("PC11", interruptGroup: 11),
+ new SpiChannelInfo("SPI3_CIPO", SpiLineType.CIPO)
+ }
+ );
+
+ public IPin PD5 => new Pin(
+ "PD5", "PD5",
+ new List {
+ new DigitalChannelInfo("PD5", interruptGroup: 5),
+ }
+ );
+
+ public IPin PD6_SDMMC_CLK => new Pin(
+ "PD6", "PD6",
+ new List {
+ new DigitalChannelInfo("PD6", interruptGroup: 6),
+ }
+ );
+
+ public IPin PD7_SDMMC_CMD => new Pin(
+ "PD7", "PD7",
+ new List {
+ new DigitalChannelInfo("PD7", interruptGroup: 7),
+ }
+ );
+
+ public IPin PF8 => new Pin(
+ "PF8", "PF8",
+ new List {
+ new DigitalChannelInfo("PF8", interruptGroup: 8),
+ new SpiChannelInfo("SPI5_CIPO", SpiLineType.CIPO)
+ }
+ );
+
+ public IPin PF9 => new Pin(
+ "PF9", "PF9",
+ new List {
+ new DigitalChannelInfo("PF9", interruptGroup: 9),
+ new SpiChannelInfo("SPI5_COPI", SpiLineType.COPI)
+ }
+ );
+
+ public IPin PG6_SDMMC_IN_L => new Pin(
+ "PG6", "PG6",
+ new List {
+ new DigitalChannelInfo("PG6", interruptCapable: false),
+ }
+ );
+
+ public IPin PG9_SDMMC_D0 => new Pin(
+ "PG9", "PG9",
+ new List {
+ new DigitalChannelInfo("PG9", interruptGroup: 9),
+ }
+ );
+
+ public IPin PG10_SDMMC_D1 => new Pin(
+ "PG10", "PG10",
+ new List {
+ new DigitalChannelInfo("PG10", interruptCapable: false),
+ }
+ );
+
+ public IPin PG11_SDMMC_D2 => new Pin(
+ "PG11", "PG11",
+ new List {
+ new DigitalChannelInfo("PG11", interruptGroup: 11),
+ }
+ );
+
+ public IPin PG12_SDMMC_D3 => new Pin(
+ "PG12", "PG12",
+ new List {
+ new DigitalChannelInfo("PG12", interruptGroup: 12),
+ }
+ );
+
+ public IPin PG13_ETH_TXD0 => new Pin(
+ "PG13", "PG13",
+ new List {
+ new DigitalChannelInfo("PG13", interruptGroup: 13),
+ }
+ );
+
+ public IPin PG14_ETH_TXD1 => new Pin(
+ "PG14", "PG14",
+ new List {
+ new DigitalChannelInfo("PG14", interruptGroup: 14),
+ }
+ );
+
+ public IPin PH6 => new Pin(
+ "PH6", "PH6",
+ new List {
+ new DigitalChannelInfo("PH6", interruptGroup: 6),
+ new SpiChannelInfo("SPI5_SCK", SpiLineType.Clock)
+ }
+ );
+
+ public IPin PH7 => new Pin(
+ "PH7", "PH7",
+ new List {
+ new DigitalChannelInfo("PH7", interruptGroup: 7),
+ new I2cChannelInfo("I2C3_SDA", I2cChannelFunctionType.Data)
+ }
+ );
+
+ public IPin PH8 => new Pin(
+ "PH8", "PH8",
+ new List {
+ new DigitalChannelInfo("PH8", interruptGroup: 8),
+ new I2cChannelInfo("I2C3_SCL", I2cChannelFunctionType.Clock)
+ }
+ );
+
+ public IPin PH10 => new Pin(
+ "PH10", "PH10",
+ new List {
+ new DigitalChannelInfo("PH10", interruptCapable: false ),
+ new PwmChannelInfo("TIM5_CH1", 5, 1)
+ }
+ );
+
+ public IPin PH12 => new Pin(
+ "PH12", "PH12",
+ new List {
+ new DigitalChannelInfo("PH12", interruptGroup: 12),
+ }
+ );
+
+ public IPin PH13 => new Pin(
+ "PH13", "PH13",
+ new List {
+ new DigitalChannelInfo("PH13", interruptGroup: 13),
+ }
+ );
+
+ public IPin PH14_ETH_IRQ => new Pin(
+ "PH14", "PH14",
+ new List {
+ new DigitalChannelInfo("PH14", interruptGroup: 14),
+ }
+ );
+
+ public IPin PI9 => new Pin(
+ "PI9", "PI9",
+ new List {
+ new DigitalChannelInfo("PI9", interruptGroup: 9),
+ new UartChannelInfo("UART4_RX", SerialDirectionType.Receive)
+ }
+ );
+
+ public IPin PI11 => new Pin(
+ "PI11", "PI11",
+ new List {
+ new DigitalChannelInfo("PI11", interruptGroup: 11),
+ }
+ );
+
+ // ==== ALIASES ====
+ public IPin I2C1_SDA => D07;
+ public IPin I2C1_SCL => D08;
+ public IPin I2C3_SDA => PH7;
+ public IPin I2C3_SCL => PH8;
+
+ public IPin SCK => SPI3_SCK;
+ public IPin COPI => SPI3_COPI;
+ public IPin CIPO => SPI3_CIPO;
+
+ public IPin SPI3_COPI => PB5;
+ public IPin SPI3_SCK => PC10;
+ public IPin SPI3_CIPO => PC11;
+
+ public IPin SPI5_CIPO => PF8;
+ public IPin SPI5_SCK => PH6;
+ public IPin SPI5_COPI => PF9;
+
+ public IPin A00 => PA4;
+ public IPin A01 => PA5;
+ public IPin A02 => PA3;
+ public IPin A03 => PB0;
+ public IPin A04 => PB1;
+ public IPin A05 => PC0;
+
+ public IPin D00 => PI9;
+ public IPin D01 => PH13;
+ public IPin D02 => PH10;
+ public IPin D03 => PB8;
+ public IPin D04 => PB9;
+ public IPin D05 => PB4;
+ public IPin D06 => PB13;
+ public IPin D07 => PB7;
+ public IPin D08 => PB6;
+ public IPin D09 => PC6;
+ public IPin D10 => PC7;
+ public IPin D11 => PC9;
+ public IPin D12 => PB14;
+ public IPin D13 => PB15;
+ public IPin D14 => PB12;
+ public IPin D15 => PG12_SDMMC_D3;
+ public IPin D16 => PI11;
+ public IPin D17 => PD5;
+ public IPin D18 => PA10;
+ public IPin D19 => PC8;
+ public IPin D20 => PA0; // BLINKY on dev board
+
+ public IEnumerator GetEnumerator() => AllPins.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ }
+ }
+}
diff --git a/source/Meadow.F7/Devices/Definitions/F7FeatherV1.Pinout.cs b/source/implementations/f7/Meadow.F7/Devices/Definitions/F7FeatherV1.Pinout.cs
similarity index 98%
rename from source/Meadow.F7/Devices/Definitions/F7FeatherV1.Pinout.cs
rename to source/implementations/f7/Meadow.F7/Devices/Definitions/F7FeatherV1.Pinout.cs
index c5b4b75d..9e06784c 100644
--- a/source/Meadow.F7/Devices/Definitions/F7FeatherV1.Pinout.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/Definitions/F7FeatherV1.Pinout.cs
@@ -133,7 +133,7 @@ public partial class Pinout : IF7FeatherPinout
"COPI", "PB5",
new List {
new DigitalChannelInfo("PB5", interruptGroup: 5),
- new SpiChannelInfo("PB5", SpiLineType.MOSI)
+ new SpiChannelInfo("PB5", SpiLineType.COPI)
}
);
// CIPO
@@ -143,7 +143,7 @@ public partial class Pinout : IF7FeatherPinout
"CIPO", "PC11",
new List {
new DigitalChannelInfo("PC11", interruptGroup: 11),
- new SpiChannelInfo("PC11", SpiLineType.MISO)
+ new SpiChannelInfo("PC11", SpiLineType.CIPO)
}
);
@@ -310,7 +310,7 @@ public partial class Pinout : IF7FeatherPinout
"ESP_MOSI", "PI3",
new List {
new DigitalChannelInfo("PI3"),
- new SpiChannelInfo("PI3", SpiLineType.MOSI)
+ new SpiChannelInfo("PI3", SpiLineType.COPI)
}
);
// ESP_MISO
@@ -318,7 +318,7 @@ public partial class Pinout : IF7FeatherPinout
"ESP_MISO", "PI2",
new List {
new DigitalChannelInfo("PI2", interruptGroup: 12),
- new SpiChannelInfo("PI2", SpiLineType.MISO)
+ new SpiChannelInfo("PI2", SpiLineType.CIPO)
}
);
// ESP_CLK
diff --git a/source/Meadow.F7/Devices/Definitions/F7FeatherV2.Pinout.cs b/source/implementations/f7/Meadow.F7/Devices/Definitions/F7FeatherV2.Pinout.cs
similarity index 98%
rename from source/Meadow.F7/Devices/Definitions/F7FeatherV2.Pinout.cs
rename to source/implementations/f7/Meadow.F7/Devices/Definitions/F7FeatherV2.Pinout.cs
index 66fc358c..aa9ee473 100644
--- a/source/Meadow.F7/Devices/Definitions/F7FeatherV2.Pinout.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/Definitions/F7FeatherV2.Pinout.cs
@@ -135,7 +135,7 @@ public partial class Pinout : IF7FeatherPinout
"COPI", "PB5",
new List {
new DigitalChannelInfo("PB5", interruptGroup: 5),
- new SpiChannelInfo("PB5", SpiLineType.MOSI)
+ new SpiChannelInfo("PB5", SpiLineType.COPI)
}
);
// CIPO
@@ -145,7 +145,7 @@ public partial class Pinout : IF7FeatherPinout
"CIPO", "PC11",
new List {
new DigitalChannelInfo("PC11", interruptGroup: 11),
- new SpiChannelInfo("PC11", SpiLineType.MISO)
+ new SpiChannelInfo("PC11", SpiLineType.CIPO)
}
);
@@ -351,7 +351,7 @@ public partial class Pinout : IF7FeatherPinout
"ESP_MOSI", "PI3",
new List {
new DigitalChannelInfo("PI3"),
- new SpiChannelInfo("PI3", SpiLineType.MOSI)
+ new SpiChannelInfo("PI3", SpiLineType.COPI)
}
);
// ESP_MISO
@@ -359,7 +359,7 @@ public partial class Pinout : IF7FeatherPinout
"ESP_MISO", "PI2",
new List {
new DigitalChannelInfo("PI2", interruptCapable: false),
- new SpiChannelInfo("PI2", SpiLineType.MISO)
+ new SpiChannelInfo("PI2", SpiLineType.CIPO)
}
);
// ESP_CLK
diff --git a/source/implementations/f7/Meadow.F7/Devices/Definitions/IF7CoreComputePinout.cs b/source/implementations/f7/Meadow.F7/Devices/Definitions/IF7CoreComputePinout.cs
new file mode 100644
index 00000000..c9f8680c
--- /dev/null
+++ b/source/implementations/f7/Meadow.F7/Devices/Definitions/IF7CoreComputePinout.cs
@@ -0,0 +1,80 @@
+namespace Meadow.Hardware
+{
+ public interface IF7CoreComputePinout : I32PinFeatherBoardPinout, IPinDefinitions
+ {
+ IPin I2C1_SCL { get; }
+ IPin I2C1_SDA { get; }
+ IPin I2C3_SCL { get; }
+ IPin I2C3_SDA { get; }
+ IPin SPI3_SCK { get; }
+ IPin SPI3_COPI { get; }
+ IPin SPI3_CIPO { get; }
+ IPin SPI5_SCK { get; }
+ IPin SPI5_COPI { get; }
+ IPin SPI5_CIPO { get; }
+ IPin D16 { get; }
+ IPin D17 { get; }
+ IPin D18 { get; }
+ IPin D19 { get; }
+ IPin D20 { get; }
+
+ IPin PA0 { get; }
+ IPin PA1_ETH_REF_CLK { get; }
+ IPin PA2_ETH_MDIO { get; }
+ IPin PA3 { get; }
+ IPin PA4 { get; }
+ IPin PA5 { get; }
+ IPin PA7_ETH_CRS_DV { get; }
+ IPin PA9 { get; }
+ IPin PA10 { get; }
+ IPin PA13 { get; }
+ IPin PA14 { get; }
+ IPin PA15 { get; }
+ IPin PB0 { get; }
+ IPin PB1 { get; }
+ IPin PB3 { get; }
+ IPin PB4 { get; }
+ IPin PB5 { get; }
+ IPin PB6 { get; }
+ IPin PB7 { get; }
+ IPin PB8 { get; }
+ IPin PB9 { get; }
+ IPin PB11_ETH_TX_EN { get; }
+ IPin PB12 { get; }
+ IPin PB13 { get; }
+ IPin PB14 { get; }
+ IPin PB15 { get; }
+ IPin PC0 { get; }
+ IPin PC1_ETH_MDC { get; }
+ IPin PC2 { get; }
+ IPin PC4_ETH_RXD0 { get; }
+ IPin PC5_ETH_RXD1 { get; }
+ IPin PC6 { get; }
+ IPin PC7 { get; }
+ IPin PC8 { get; }
+ IPin PC9 { get; }
+ IPin PC10 { get; }
+ IPin PC11 { get; }
+ IPin PD5 { get; }
+ IPin PD6_SDMMC_CLK { get; }
+ IPin PD7_SDMMC_CMD { get; }
+ IPin PF8 { get; }
+ IPin PF9 { get; }
+ IPin PG6_SDMMC_IN_L { get; }
+ IPin PG9_SDMMC_D0 { get; }
+ IPin PG10_SDMMC_D1 { get; }
+ IPin PG11_SDMMC_D2 { get; }
+ IPin PG12_SDMMC_D3 { get; }
+ IPin PG13_ETH_TXD0 { get; }
+ IPin PG14_ETH_TXD1 { get; }
+ IPin PH6 { get; }
+ IPin PH7 { get; }
+ IPin PH8 { get; }
+ IPin PH10 { get; }
+ IPin PH12 { get; }
+ IPin PH13 { get; }
+ IPin PH14_ETH_IRQ { get; }
+ IPin PI9 { get; }
+ IPin PI11 { get; }
+ }
+}
diff --git a/source/Meadow.F7/Devices/Definitions/IF7FeatherPinout.cs b/source/implementations/f7/Meadow.F7/Devices/Definitions/IF7FeatherPinout.cs
similarity index 100%
rename from source/Meadow.F7/Devices/Definitions/IF7FeatherPinout.cs
rename to source/implementations/f7/Meadow.F7/Devices/Definitions/IF7FeatherPinout.cs
diff --git a/source/Meadow.F7/Devices/DeviceChannelManager.cs b/source/implementations/f7/Meadow.F7/Devices/DeviceChannelManager.cs
similarity index 100%
rename from source/Meadow.F7/Devices/DeviceChannelManager.cs
rename to source/implementations/f7/Meadow.F7/Devices/DeviceChannelManager.cs
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/Encoders.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Encoders.cs
similarity index 97%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/Encoders.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Encoders.cs
index 6b269981..b042d463 100644
--- a/source/Meadow.F7/Devices/Esp32Coprocessor/Encoders.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Encoders.cs
@@ -543,7 +543,7 @@ public static byte[] EncodeAccessPointInformation(MessagePayloads.AccessPointInf
//
length += (int) (accessPointInformation.NetworkName.Length + 1);
length += (int) (accessPointInformation.Password.Length + 1);
- length += 12;
+ length += 15;
//
// Now allocate a new buffer and copy the data in to the buffer.
@@ -559,6 +559,12 @@ public static byte[] EncodeAccessPointInformation(MessagePayloads.AccessPointInf
EncodeUInt32(accessPointInformation.SubnetMask, buffer, offset);
offset += 4;
EncodeUInt32(accessPointInformation.Gateway, buffer, offset);
+ offset += 4;
+ buffer[offset] = accessPointInformation.WiFiAuthenticationMode;
+ offset += 1;
+ buffer[offset] = accessPointInformation.Channel;
+ offset += 1;
+ buffer[offset] = accessPointInformation.Hidden;
return(buffer);
}
@@ -581,6 +587,12 @@ public static MessagePayloads.AccessPointInformation ExtractAccessPointInformati
accessPointInformation.SubnetMask = ExtractUInt32(buffer, offset);
offset += 4;
accessPointInformation.Gateway = ExtractUInt32(buffer, offset);
+ offset += 4;
+ accessPointInformation.WiFiAuthenticationMode = buffer[offset];
+ offset += 1;
+ accessPointInformation.Channel = buffer[offset];
+ offset += 1;
+ accessPointInformation.Hidden = buffer[offset];
return(accessPointInformation);
}
@@ -594,7 +606,7 @@ public static int EncodedAccessPointInformationBufferSize(MessagePayloads.Access
int result = 0;
result += (int) accessPointInformation.NetworkName.Length;
result += (int) accessPointInformation.Password.Length;
- return(result + 14);
+ return(result + 17);
}
///
@@ -732,6 +744,60 @@ public static int EncodedConnectEventDataBufferSize(MessagePayloads.ConnectEvent
return(57);
}
+ ///
+ /// Encode a NodeConnectionChangeEventData object and return a byte array containing the encoded message.
+ ///
+ /// NodeConnectionChangeEventData object to be encoded.
+ /// Byte array containing the encoded NodeConnectionChangeEventData object.
+ public static byte[] EncodeNodeConnectionChangeEventData(MessagePayloads.NodeConnectionChangeEventData nodeConnectionChangeEventData)
+ {
+ int offset = 0;
+ int length = 0;
+
+ //
+ // Calculate the amount of memory needed.
+ //
+ length += (int) 6;
+ length += 4;
+
+ //
+ // Now allocate a new buffer and copy the data in to the buffer.
+ //
+ byte[] buffer = new byte[length];
+ Array.Clear(buffer, 0, buffer.Length);
+ EncodeUInt32(nodeConnectionChangeEventData.IpAddress, buffer, offset);
+ offset += 4;
+ Array.Copy(nodeConnectionChangeEventData.MacAddress, 0, buffer, offset, 6);
+ return(buffer);
+ }
+
+ ///
+ /// Extract a NodeConnectionChangeEventData object from a byte array.
+ ///
+ /// Byte array containing the encoded data.
+ /// Offset into the buffer where the encoded data can be found.
+ /// NodeConnectionChangeEventData object.
+ public static MessagePayloads.NodeConnectionChangeEventData ExtractNodeConnectionChangeEventData(byte[] buffer, int offset)
+ {
+ NodeConnectionChangeEventData nodeConnectionChangeEventData = new MessagePayloads.NodeConnectionChangeEventData();
+
+ nodeConnectionChangeEventData.IpAddress = ExtractUInt32(buffer, offset);
+ offset += 4;
+ nodeConnectionChangeEventData.MacAddress = new byte[6];
+ Array.Copy(buffer, offset, nodeConnectionChangeEventData.MacAddress, 0, 6);
+ return(nodeConnectionChangeEventData);
+ }
+
+ ///
+ /// Calculate the amount of memory required to hold the given instance of the NodeConnectionChangeEventData object.
+ ///
+ /// NodeConnectionChangeEventData object to be encoded.
+ /// Number of bytes required to hold the encoded NodeConnectionChangeEventData object.
+ public static int EncodedNodeConnectionChangeEventDataBufferSize(MessagePayloads.NodeConnectionChangeEventData nodeConnectionChangeEventData)
+ {
+ return(10);
+ }
+
///
/// Encode a DisconnectEventData object and return a byte array containing the encoded message.
///
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor.cs
similarity index 100%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor.cs
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor_IBlutoothAdapter.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor_IBlutoothAdapter.cs
similarity index 100%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor_IBlutoothAdapter.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor_IBlutoothAdapter.cs
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor_IWiFiAdapter.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor_IWiFiAdapter.cs
similarity index 100%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor_IWiFiAdapter.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Esp32Coprocessor_IWiFiAdapter.cs
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/NtpTimeChangedEventArgs.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/NtpTimeChangedEventArgs.cs
similarity index 100%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/NtpTimeChangedEventArgs.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/NtpTimeChangedEventArgs.cs
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiDisconnectEventArgs.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiDisconnectEventArgs.cs
similarity index 100%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiDisconnectEventArgs.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiDisconnectEventArgs.cs
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiInterfaceStartedEventArgs.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiInterfaceStartedEventArgs.cs
similarity index 100%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiInterfaceStartedEventArgs.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiInterfaceStartedEventArgs.cs
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiInterfaceStoppedEventArgs.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiInterfaceStoppedEventArgs.cs
similarity index 100%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiInterfaceStoppedEventArgs.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/EventArgs/WiFiInterfaceStoppedEventArgs.cs
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/Messages.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Messages.cs
similarity index 97%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/Messages.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Messages.cs
index d1ed2b1c..7270fc76 100644
--- a/source/Meadow.F7/Devices/Esp32Coprocessor/Messages.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/Messages.cs
@@ -172,6 +172,18 @@ public struct AccessPointInformation
/// Gateway element.
///
public UInt32 Gateway;
+ ///
+ /// WiFiAuthenticationMode element.
+ ///
+ public Byte WiFiAuthenticationMode;
+ ///
+ /// Channel element.
+ ///
+ public Byte Channel;
+ ///
+ /// Hidden element.
+ ///
+ public Byte Hidden;
};
///
@@ -224,6 +236,21 @@ public struct ConnectEventData
public UInt32 Reason;
};
+ ///
+ /// Structure to hold NodeConnectionChangeEventData data.
+ ///
+ public struct NodeConnectionChangeEventData
+ {
+ ///
+ /// IpAddress element.
+ ///
+ public UInt32 IpAddress;
+ ///
+ /// MacAddress element.
+ ///
+ public Byte[] MacAddress;
+ };
+
///
/// Structure to hold DisconnectEventData data.
///
diff --git a/source/Meadow.F7/Devices/Esp32Coprocessor/SharedEnums.cs b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/SharedEnums.cs
similarity index 96%
rename from source/Meadow.F7/Devices/Esp32Coprocessor/SharedEnums.cs
rename to source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/SharedEnums.cs
index a5ea122a..3060ce11 100644
--- a/source/Meadow.F7/Devices/Esp32Coprocessor/SharedEnums.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/Esp32Coprocessor/SharedEnums.cs
@@ -161,7 +161,27 @@ public enum StatusCodes
///
/// StatusCodes - UnknownConfigurationItem
///
- UnknownConfigurationItem = 34
+ UnknownConfigurationItem = 34,
+ ///
+ /// StatusCodes - CannotStartAccessPoint
+ ///
+ CannotStartAccessPoint = 35,
+ ///
+ /// StatusCodes - DhcpConfigurationError
+ ///
+ DhcpConfigurationError = 36,
+ ///
+ /// StatusCodes - AccessPointNotStarted
+ ///
+ AccessPointNotStarted = 37,
+ ///
+ /// StatusCodes - AccessPointAlreadyStarted
+ ///
+ AccessPointAlreadyStarted = 38,
+ ///
+ /// StatusCodes - NotImplemented
+ ///
+ NotImplemented = 39
};
///
@@ -398,7 +418,31 @@ public enum WiFiFunction
///
/// WiFiFunction - ErrorEvent
///
- ErrorEvent = 39
+ ErrorEvent = 39,
+ ///
+ /// WiFiFunction - StartAccessPoint
+ ///
+ StartAccessPoint = 40,
+ ///
+ /// WiFiFunction - StopAccessPoint
+ ///
+ StopAccessPoint = 41,
+ ///
+ /// WiFiFunction - AccessPointStartedEvent
+ ///
+ AccessPointStartedEvent = 42,
+ ///
+ /// WiFiFunction - AccessPointStoppedEvent
+ ///
+ AccessPointStoppedEvent = 43,
+ ///
+ /// WiFiFunction - NodeConnectedEvent
+ ///
+ NodeConnectedEvent = 44,
+ ///
+ /// WiFiFunction - NodeDisconnectedEvent
+ ///
+ NodeDisconnectedEvent = 45
};
///
diff --git a/source/Meadow.F7/Devices/F7CoreCompute.cs b/source/implementations/f7/Meadow.F7/Devices/F7CoreCompute.cs
similarity index 93%
rename from source/Meadow.F7/Devices/F7CoreCompute.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7CoreCompute.cs
index 2984df24..73b81fea 100644
--- a/source/Meadow.F7/Devices/F7CoreCompute.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7CoreCompute.cs
@@ -4,8 +4,6 @@ namespace Meadow.Devices
{
public partial class F7CoreComputeV2 : F7CoreComputeBase
{
- public SerialPortNameDefinitions SerialPortNames => new SerialPortNameDefinitions();
-
public F7CoreComputeV2()
: base(new Pinout(),
new F7CoreComputeGpioManager(),
diff --git a/source/Meadow.F7/Devices/F7CoreComputeBase.cs b/source/implementations/f7/Meadow.F7/Devices/F7CoreComputeBase.cs
similarity index 80%
rename from source/Meadow.F7/Devices/F7CoreComputeBase.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7CoreComputeBase.cs
index b7f29afe..b522ad73 100644
--- a/source/Meadow.F7/Devices/F7CoreComputeBase.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7CoreComputeBase.cs
@@ -37,28 +37,11 @@ public override IPin GetPin(string pinName)
///
/// An instance of an I2cBus
public override II2cBus CreateI2cBus(
- I2cBusSpeed busSpeed,
- int busNumber = 1
+ int busNumber = 1,
+ I2cBusSpeed busSpeed = I2cBusSpeed.Standard
)
{
- return CreateI2cBus(busNumber, new Frequency((int)busSpeed, Frequency.UnitType.Hertz));
- }
-
- ///
- /// Creates an I2C bus instance for the default Meadow F7 pins (SCL/D08 and SDA/D07) and the requested bus speed
- ///
- /// The bus speed in (in Hz) defaulting to 100k
- /// An instance of an I2cBus
- public override II2cBus CreateI2cBus(
- int busNumber,
- Frequency frequency
- )
- {
- if (busNumber == 3)
- {
- return CreateI2cBus(Pins.I2C3_SCL, Pins.I2C3_SDA, frequency);
- }
- return CreateI2cBus(Pins.I2C1_SCL, Pins.I2C1_SDA, frequency);
+ return CreateI2cBus(busNumber, busSpeed);
}
protected override int GetI2CBusNumberForPins(IPin clock, IPin data)
@@ -89,8 +72,6 @@ public override ISpiBus CreateSpiBus(
{
switch (busNumber)
{
- case 2:
- return CreateSpiBus(Pins.ESP_CLK, Pins.ESP_COPI, Pins.ESP_CIPO, speed);
case 3:
return CreateSpiBus(Pins.SPI3_SCK, Pins.SPI3_COPI, Pins.SPI3_CIPO, speed);
case 5:
diff --git a/source/Meadow.F7/Devices/F7CoreComputeGpioManager.cs b/source/implementations/f7/Meadow.F7/Devices/F7CoreComputeGpioManager.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7CoreComputeGpioManager.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7CoreComputeGpioManager.cs
diff --git a/source/Meadow.F7/Devices/F7DeviceInformation.cs b/source/implementations/f7/Meadow.F7/Devices/F7DeviceInformation.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7DeviceInformation.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7DeviceInformation.cs
diff --git a/source/Meadow.F7/Devices/F7FeatherBase.cs b/source/implementations/f7/Meadow.F7/Devices/F7FeatherBase.cs
similarity index 74%
rename from source/Meadow.F7/Devices/F7FeatherBase.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7FeatherBase.cs
index 79ae7fc6..247fa61e 100644
--- a/source/Meadow.F7/Devices/F7FeatherBase.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7FeatherBase.cs
@@ -6,16 +6,6 @@ namespace Meadow.Devices
{
public abstract partial class F7FeatherBase : F7MicroBase, IF7FeatherMeadowDevice
{
- public class SerialPortNameDefinitions
- {
- public SerialPortName Com1 { get; } = new SerialPortName("COM1", "ttyS0");
- public SerialPortName Com4 { get; } = new SerialPortName("COM4", "ttyS1");
- // TODO: what is the unix name for this port?
- //public SerialPortName Com7 { get; } = new SerialPortName("COM7", "ttyS?");
- }
-
- public SerialPortNameDefinitions SerialPortNames => new SerialPortNameDefinitions();
-
protected F7FeatherBase(
IF7FeatherPinout pins,
IMeadowIOController ioController,
@@ -37,6 +27,11 @@ public override IPwmPort CreatePwmPort(
return PwmPort.From(pin, this.IoController, frequency, dutyCycle, inverted, isOnboard);
}
+ ///
+ /// Retrives the IPin for the given pin name
+ ///
+ ///
+ ///
public override IPin GetPin(string pinName)
{
return Pins.AllPins.FirstOrDefault(p => p.Name == pinName || p.Key.ToString() == p.Name);
@@ -69,27 +64,33 @@ protected bool IsOnboardLed(IPin pin)
/// Creates an I2C bus instance for the default Meadow F7 pins (SCL/D08 and SDA/D07) and the requested bus speed
///
/// An instance of an I2cBus
- public override II2cBus CreateI2cBus(
- I2cBusSpeed busSpeed,
- int busNumber = 0
+ public II2cBus CreateI2cBus(
+ I2cBusSpeed busSpeed = I2cBusSpeed.Standard
)
{
- return CreateI2cBus(Pins.I2C_SCL, Pins.I2C_SDA, new Frequency((int)busSpeed, Frequency.UnitType.Hertz));
+ return CreateI2cBus(Pins.I2C_SCL, Pins.I2C_SDA, busSpeed);
}
///
/// Creates an I2C bus instance for the default Meadow F7 pins (SCL/D08 and SDA/D07) and the requested bus speed
///
- /// The bus speed in (in Hz) defaulting to 100k
+ /// The bus speed desired
+ /// The hardware bus number
/// An instance of an I2cBus
public override II2cBus CreateI2cBus(
- int busNumber,
- Frequency frequency
+ int busNumber = 0,
+ I2cBusSpeed busSpeed = I2cBusSpeed.Standard
)
{
- return CreateI2cBus(Pins.I2C_SCL, Pins.I2C_SDA, frequency);
+ return CreateI2cBus(Pins.I2C_SCL, Pins.I2C_SDA, busSpeed);
}
+ ///
+ /// Creates an I2C bus instance for the default Meadow F7 pins (SCL/D08 and SDA/D07) and the requested bus speed
+ ///
+ /// The bus speed desired
+ /// The hardware bus number
+ /// An instance of an I2cBus
public override ISpiBus CreateSpiBus(
Units.Frequency speed,
int busNumber = 3
@@ -98,6 +99,13 @@ public override ISpiBus CreateSpiBus(
return CreateSpiBus(Pins.SCK, Pins.COPI, Pins.CIPO, speed);
}
+ ///
+ /// Retrieves the hardware bus number for the provided pins
+ ///
+ ///
+ ///
+ ///
+ ///
protected override int GetSpiBusNumberForPins(IPin clock, IPin copi, IPin cipo)
{
// we're only looking at clock pin.
diff --git a/source/Meadow.F7/Devices/F7FeatherGpioManager.cs b/source/implementations/f7/Meadow.F7/Devices/F7FeatherGpioManager.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7FeatherGpioManager.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7FeatherGpioManager.cs
diff --git a/source/Meadow.F7/Devices/F7FeatherV1.cs b/source/implementations/f7/Meadow.F7/Devices/F7FeatherV1.cs
similarity index 92%
rename from source/Meadow.F7/Devices/F7FeatherV1.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7FeatherV1.cs
index 12fe4a24..2c11aca6 100644
--- a/source/Meadow.F7/Devices/F7FeatherV1.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7FeatherV1.cs
@@ -4,7 +4,10 @@
namespace Meadow.Devices
{
- [Obsolete("Use the F7FeatherV1 class instead.")]
+ ///
+ /// Represents an F7Micro1 device.
+ ///
+ [Obsolete("Use the F7FeatherV1 class instead.", true)]
public class F7Micro : F7FeatherV1
{
}
@@ -38,7 +41,7 @@ public F7FeatherV1()
///
/// Gets a BatteryInfo instance for the current state of the platform
///
- ///
+ /// A BatteryInfo instance
///
public override BatteryInfo GetBatteryInfo()
{
diff --git a/source/Meadow.F7/Devices/F7FeatherV2.cs b/source/implementations/f7/Meadow.F7/Devices/F7FeatherV2.cs
similarity index 70%
rename from source/Meadow.F7/Devices/F7FeatherV2.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7FeatherV2.cs
index e9039ee0..6224aa4b 100644
--- a/source/Meadow.F7/Devices/F7FeatherV2.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7FeatherV2.cs
@@ -3,7 +3,10 @@
namespace Meadow.Devices
{
- [Obsolete("Use the F7FeatherV2 class instead.")]
+ ///
+ /// Represents an F7Micro2 device.
+ ///
+ [Obsolete("Use the F7FeatherV2 class instead.", true)]
public class F7Micro2 : F7FeatherV2
{
}
@@ -14,9 +17,7 @@ public class F7Micro2 : F7FeatherV2
///
public partial class F7FeatherV2 : F7FeatherBase
{
- private Lazy _adc_bat;
-
- public SerialPortNameDefinitions SerialPortNames => new SerialPortNameDefinitions();
+ private Lazy _adc_bat;
public F7FeatherV2()
: base(new Pinout(),
@@ -32,12 +33,18 @@ public F7FeatherV2()
throw new UnsupportedPlatformException(this.Information.Platform, message);
}
- _adc_bat = new Lazy(() =>
+ _adc_bat = new Lazy(() =>
{
- return this.CreateAnalogInputPort((Pins as F7FeatherV2.Pinout).BAT);
+ return this.CreateAnalogInputPort((Pins as F7FeatherV2.Pinout).BAT) ?? null;
});
}
+ ///
+ /// Retrieves the hardware bus number for the provided pins
+ ///
+ ///
+ ///
+ ///
protected override int GetI2CBusNumberForPins(IPin clock, IPin data)
{
if (clock.Name == (Pins as F7FeatherV2.Pinout)?.I2C_SCL.Name)
@@ -49,12 +56,16 @@ protected override int GetI2CBusNumberForPins(IPin clock, IPin data)
return -1;
}
+ ///
+ /// Gets a BatteryInfo instance for the current state of the platform
+ ///
+ /// A BatteryInfo instance
public override BatteryInfo GetBatteryInfo()
{
return new BatteryInfo
{
// on V2 there is a voltage divider 2 x 499R and the voltage is taken from the center of the divider, effectively halving the input
- Voltage = _adc_bat.Value.Voltage * 2
+ Voltage = _adc_bat?.Value?.Voltage * 2
};
}
}
diff --git a/source/Meadow.F7/Devices/F7GPIOManager.cs b/source/implementations/f7/Meadow.F7/Devices/F7GPIOManager.cs
similarity index 99%
rename from source/Meadow.F7/Devices/F7GPIOManager.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7GPIOManager.cs
index eb4b4dc9..34906494 100644
--- a/source/Meadow.F7/Devices/F7GPIOManager.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7GPIOManager.cs
@@ -37,7 +37,7 @@ internal F7GPIOManager()
DebugFeatures = DebugFeature.None;
DeviceChannelManager = new DeviceChannelManager();
#if DEBUG
- //Console.WriteLine($"DirectRegisterAccess = {DirectRegisterAccess}");
+ //Resolver.Log.Info($"DirectRegisterAccess = {DirectRegisterAccess}");
// Adjust this during test and debug for your (developer)'s purposes. The Conditional will turn it all off in a Release build.
//DebugFeatures = DebugFeature.Startup | DebugFeature.PinInitilize | DebugFeature.GpioDetail;
// DebugFeatures = DebugFeature.GpioDetail;
diff --git a/source/Meadow.F7/Devices/F7GPIOManager_adc.cs b/source/implementations/f7/Meadow.F7/Devices/F7GPIOManager_adc.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7GPIOManager_adc.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7GPIOManager_adc.cs
diff --git a/source/Meadow.F7/Devices/F7GPIOManager_interrupts.cs b/source/implementations/f7/Meadow.F7/Devices/F7GPIOManager_interrupts.cs
similarity index 99%
rename from source/Meadow.F7/Devices/F7GPIOManager_interrupts.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7GPIOManager_interrupts.cs
index 8cdfde01..b636a286 100644
--- a/source/Meadow.F7/Devices/F7GPIOManager_interrupts.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7GPIOManager_interrupts.cs
@@ -216,7 +216,7 @@ private void InterruptServiceThreadProc(object o)
}
catch (Exception ex)
{
- Console.WriteLine($"IST: {ex.Message}");
+ Resolver.Log.Error($"IST: {ex.Message}");
Thread.Sleep(5000);
}
}
diff --git a/source/Meadow.F7/Devices/F7GPIOManager_nuttx.cs b/source/implementations/f7/Meadow.F7/Devices/F7GPIOManager_nuttx.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7GPIOManager_nuttx.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7GPIOManager_nuttx.cs
diff --git a/source/Meadow.F7/Devices/F7MicroBase.FileSystem.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.FileSystem.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7MicroBase.FileSystem.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.FileSystem.cs
diff --git a/source/Meadow.F7/Devices/F7MicroBase.IAnalogInputController.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.IAnalogInputController.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7MicroBase.IAnalogInputController.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.IAnalogInputController.cs
diff --git a/source/Meadow.F7/Devices/F7MicroBase.IBiDirectionalController.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.IBiDirectionalController.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7MicroBase.IBiDirectionalController.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.IBiDirectionalController.cs
diff --git a/source/Meadow.F7/Devices/F7MicroBase.IDigitalInputOutputController.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.IDigitalInputOutputController.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7MicroBase.IDigitalInputOutputController.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.IDigitalInputOutputController.cs
diff --git a/source/Meadow.F7/Devices/F7MicroBase.II2cController.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.II2cController.cs
similarity index 63%
rename from source/Meadow.F7/Devices/F7MicroBase.II2cController.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.II2cController.cs
index 22a0a7d5..9e6683a8 100644
--- a/source/Meadow.F7/Devices/F7MicroBase.II2cController.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.II2cController.cs
@@ -1,6 +1,4 @@
-using System;
-using Meadow.Hardware;
-using Meadow.Units;
+using Meadow.Hardware;
namespace Meadow.Devices
{
@@ -20,18 +18,8 @@ public II2cBus CreateI2cBus(int busNumber = 1)
///
/// An instance of an I2cBus
public abstract II2cBus CreateI2cBus(
- I2cBusSpeed busSpeed,
- int busNumber = 0
- );
-
- ///
- /// Creates an I2C bus instance for the default Meadow F7 pins (SCL/D08 and SDA/D07) and the requested bus speed
- ///
- /// The bus speed in (in Hz) defaulting to 100k
- /// An instance of an I2cBus
- public abstract II2cBus CreateI2cBus(
- int busNumber,
- Frequency frequency
+ int busNumber = 0,
+ I2cBusSpeed busSpeed = I2cBusSpeed.Standard
);
protected abstract int GetI2CBusNumberForPins(IPin clock, IPin data);
@@ -39,28 +27,28 @@ Frequency frequency
///
/// Creates an I2C bus instance for the requested pins and bus speed
///
- /// The bus speed in (in Hz) defaulting to 100k
+ /// The bus speed, defaulting to 100k
/// An instance of an I2cBus
public II2cBus CreateI2cBus(
IPin[] pins,
- Frequency frequency
+ I2cBusSpeed busSpeed = I2cBusSpeed.Standard
)
{
- return CreateI2cBus(pins[0], pins[1], frequency);
+ return CreateI2cBus(pins[0], pins[1], busSpeed);
}
///
/// Creates an I2C bus instance for the requested pins and bus speed
///
- /// The bus speed in (in Hz) defaulting to 100k
+ /// The bus speed, defaulting to 100k
/// An instance of an I2cBus
public II2cBus CreateI2cBus(
IPin clock,
IPin data,
- Frequency frequency
+ I2cBusSpeed busSpeed = I2cBusSpeed.Standard
)
{
- var bus = I2cBus.From(this.IoController, clock, data, frequency);
+ var bus = I2cBus.From(this.IoController, clock, data, busSpeed);
bus.BusNumber = GetI2CBusNumberForPins(clock, data);
return bus;
}
diff --git a/source/Meadow.F7/Devices/F7MicroBase.ISerialController.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.ISerialController.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7MicroBase.ISerialController.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.ISerialController.cs
diff --git a/source/Meadow.F7/Devices/F7MicroBase.ISerialMessageController.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.ISerialMessageController.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7MicroBase.ISerialMessageController.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.ISerialMessageController.cs
diff --git a/source/Meadow.F7/Devices/F7MicroBase.ISpiController.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.ISpiController.cs
similarity index 90%
rename from source/Meadow.F7/Devices/F7MicroBase.ISpiController.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.ISpiController.cs
index 1f333b1e..9faa56bd 100644
--- a/source/Meadow.F7/Devices/F7MicroBase.ISpiController.cs
+++ b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.ISpiController.cs
@@ -1,10 +1,16 @@
-using System;
-using Meadow.Hardware;
+using Meadow.Hardware;
namespace Meadow.Devices
{
public abstract partial class F7MicroBase
{
+ ///
+ /// Retrieves the hardware bus number for the provided pins
+ ///
+ ///
+ ///
+ ///
+ ///
protected abstract int GetSpiBusNumberForPins(IPin clock, IPin copi, IPin cipo);
public abstract ISpiBus CreateSpiBus(Units.Frequency speed, int busNumber = 3);
@@ -12,7 +18,7 @@ public abstract partial class F7MicroBase
/// Creates a SPI bus instance for the requested bus speed with the Meadow- default IPins for CLK, COPI and CIPO
///
/// An instance of an IISpiBus
- public ISpiBus CreateSpiBus()
+ public ISpiBus CreateSpiBus()
{
return CreateSpiBus(IMeadowDevice.DefaultSpiBusSpeed);
}
diff --git a/source/Meadow.F7/Devices/F7MicroBase.IWatchdogController.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.IWatchdogController.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7MicroBase.IWatchdogController.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.IWatchdogController.cs
diff --git a/source/Meadow.F7/Devices/F7MicroBase.cs b/source/implementations/f7/Meadow.F7/Devices/F7MicroBase.cs
similarity index 100%
rename from source/Meadow.F7/Devices/F7MicroBase.cs
rename to source/implementations/f7/Meadow.F7/Devices/F7MicroBase.cs
diff --git a/source/Meadow.F7/Devices/NtpClient.cs b/source/implementations/f7/Meadow.F7/Devices/NtpClient.cs
similarity index 100%
rename from source/Meadow.F7/Devices/NtpClient.cs
rename to source/implementations/f7/Meadow.F7/Devices/NtpClient.cs
diff --git a/source/Meadow.F7/F7ExternalStorage.cs b/source/implementations/f7/Meadow.F7/F7ExternalStorage.cs
similarity index 100%
rename from source/Meadow.F7/F7ExternalStorage.cs
rename to source/implementations/f7/Meadow.F7/F7ExternalStorage.cs
diff --git a/source/Meadow.F7/F7PlatformOS.Configuration.cs b/source/implementations/f7/Meadow.F7/F7PlatformOS.Configuration.cs
similarity index 97%
rename from source/Meadow.F7/F7PlatformOS.Configuration.cs
rename to source/implementations/f7/Meadow.F7/F7PlatformOS.Configuration.cs
index 46c9781d..0046898d 100644
--- a/source/Meadow.F7/F7PlatformOS.Configuration.cs
+++ b/source/implementations/f7/Meadow.F7/F7PlatformOS.Configuration.cs
@@ -39,7 +39,7 @@ public void SetConfigurationValue(ConfigurationValues item, T value) where T
/// Get the mono version on the device.
///
/// Mono version.
- public string MonoVersion => GetString(ConfigurationValues.MonoVersion);
+ public string RuntimeVersion => GetString(ConfigurationValues.MonoVersion);
///
/// Should the system reboot if an unhandled exception is encounted in the user application?
@@ -119,13 +119,13 @@ internal static (bool Result, int Length) GetSetValue(ConfigurationValues item,
}
else
{
- Console.WriteLine($"Configuration ioctl failed, result code: {updResult}");
+ Resolver.Log.Error($"Configuration ioctl failed, result code: {updResult}");
result = false;
}
}
catch (Exception ex)
{
- Console.WriteLine($"Configuration ioctl failed: {ex.Message}");
+ Resolver.Log.Error($"Configuration ioctl failed: {ex.Message}");
result = false;
}
finally
diff --git a/source/Meadow.F7/F7PlatformOS.PowerController.cs b/source/implementations/f7/Meadow.F7/F7PlatformOS.PowerController.cs
similarity index 100%
rename from source/Meadow.F7/F7PlatformOS.PowerController.cs
rename to source/implementations/f7/Meadow.F7/F7PlatformOS.PowerController.cs
diff --git a/source/Meadow.F7/F7PlatformOS.Storage.cs b/source/implementations/f7/Meadow.F7/F7PlatformOS.Storage.cs
similarity index 99%
rename from source/Meadow.F7/F7PlatformOS.Storage.cs
rename to source/implementations/f7/Meadow.F7/F7PlatformOS.Storage.cs
index d62952df..c6978c56 100644
--- a/source/Meadow.F7/F7PlatformOS.Storage.cs
+++ b/source/implementations/f7/Meadow.F7/F7PlatformOS.Storage.cs
@@ -79,7 +79,7 @@ private void HandleRemoved()
private void SdMonitorProc(F7CoreComputeBase ccm)
{
- var input = Resolver.Device.CreateDigitalInputPort(ccm.Pins.SD_IN_L);
+ var input = Resolver.Device.CreateDigitalInputPort(ccm.Pins.PG6_SDMMC_IN_L);
var lastState = input.State;
var firstRun = true;
diff --git a/source/Meadow.F7/F7PlatformOS.cs b/source/implementations/f7/Meadow.F7/F7PlatformOS.cs
similarity index 77%
rename from source/Meadow.F7/F7PlatformOS.cs
rename to source/implementations/f7/Meadow.F7/F7PlatformOS.cs
index 568f78fd..eae20416 100644
--- a/source/Meadow.F7/F7PlatformOS.cs
+++ b/source/implementations/f7/Meadow.F7/F7PlatformOS.cs
@@ -1,4 +1,5 @@
-using Meadow.Units;
+using Meadow.Hardware;
+using Meadow.Units;
using System;
namespace Meadow
@@ -35,5 +36,14 @@ public void Initialize(DeviceCapabilities capabilities)
{
InitializeStorage(capabilities.Storage);
}
+
+ public SerialPortName[] GetSerialPortNames()
+ {
+ return new SerialPortName[]
+ {
+ new SerialPortName("COM1", "ttyS0"),
+ new SerialPortName("COM4", "ttyS1")
+ };
+ }
}
}
diff --git a/source/Meadow.F7/Hardware/Communications/F7SerialPort.cs b/source/implementations/f7/Meadow.F7/Hardware/Communications/F7SerialPort.cs
similarity index 97%
rename from source/Meadow.F7/Hardware/Communications/F7SerialPort.cs
rename to source/implementations/f7/Meadow.F7/Hardware/Communications/F7SerialPort.cs
index 2c62eab5..b31792b8 100644
--- a/source/Meadow.F7/Hardware/Communications/F7SerialPort.cs
+++ b/source/implementations/f7/Meadow.F7/Hardware/Communications/F7SerialPort.cs
@@ -126,11 +126,11 @@ protected override int ReadHardwarePort(IntPtr handle, byte[] readBuffer, int co
private void ShowSettings(Nuttx.Termios settings)
{
- Console.WriteLine($" Speed: {settings.c_speed}");
- Console.WriteLine($" Input Flags: 0x{settings.c_iflag:x}");
- Console.WriteLine($" OutputFlags: 0x{settings.c_oflag:x}");
- Console.WriteLine($" Local Flags: 0x{settings.c_lflag:x}");
- Console.WriteLine($" Control Flags: 0x{settings.c_cflag:x}");
+ Resolver.Log.Info($" Speed: {settings.c_speed}");
+ Resolver.Log.Info($" Input Flags: 0x{settings.c_iflag:x}");
+ Resolver.Log.Info($" OutputFlags: 0x{settings.c_oflag:x}");
+ Resolver.Log.Info($" Local Flags: 0x{settings.c_lflag:x}");
+ Resolver.Log.Info($" Control Flags: 0x{settings.c_cflag:x}");
}
protected override unsafe void SetHardwarePortSettings(IntPtr handle)
diff --git a/source/Meadow.F7/Hardware/Communications/I2cBus.cs b/source/implementations/f7/Meadow.F7/Hardware/Communications/I2cBus.cs
similarity index 68%
rename from source/Meadow.F7/Hardware/Communications/I2cBus.cs
rename to source/implementations/f7/Meadow.F7/Hardware/Communications/I2cBus.cs
index 8714a06a..33e33e90 100644
--- a/source/Meadow.F7/Hardware/Communications/I2cBus.cs
+++ b/source/implementations/f7/Meadow.F7/Hardware/Communications/I2cBus.cs
@@ -1,10 +1,7 @@
using Meadow.Devices;
using Meadow.Units;
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Threading;
using static Meadow.Core.Interop;
@@ -18,7 +15,6 @@ public class I2cBus : II2cBus
{
private bool _showI2cDebug = false;
private SemaphoreSlim _busSemaphore = new SemaphoreSlim(1, 1);
- private Frequency _frequency;
private IMeadowIOController IOController { get; }
internal int BusNumber { get; set; } = 1;
@@ -28,44 +24,9 @@ public void Dispose()
}
///
- /// Bus Clock speed in Hz
+ /// Bus Clock speed
///
- public Frequency Frequency
- {
- get => _frequency;
- set
- {
- switch (value.Hertz)
- {
- case 100000:
- case 400000:
- case 1000000:
- _frequency = value;
- break;
- default:
- int actual;
-
- // always round down (except if we're below the floor)
- if (value.Hertz > 1000000)
- {
- actual = 1000000;
- }
- else if (value.Hertz > 400000)
- {
- actual = 400000;
- }
- else
- {
- actual = 100000;
- }
-
- Console.WriteLine($"Warning: Invalid I2C Frequency of {value}. Adjusting to {actual}");
- _frequency = new Frequency(actual, Frequency.UnitType.Hertz);
- break;
-
- }
- }
- }
+ public I2cBusSpeed BusSpeed { get; set; }
///
/// Default constructor for the I2CBus class. This is private to prevent the
@@ -77,11 +38,11 @@ private I2cBus(
II2cChannelInfo clockChannel,
IPin data,
II2cChannelInfo dataChannel,
- Frequency frequency,
+ I2cBusSpeed busSpeed,
ushort transactionTimeout = 100)
{
IOController = ioController;
- Frequency = frequency;
+ BusSpeed = busSpeed;
#if !DEBUG
// ensure this is off in release (in case a dev sets it to true and fogets during check-in
@@ -109,20 +70,6 @@ private void Disable()
/// Bus transaction timeout
/// An I2CBus instance
public static I2cBus From(IMeadowIOController ioController, IPin clock, IPin data, I2cBusSpeed busSpeed, ushort transactionTimeout = 100)
- {
- return From(ioController, clock, data, new Frequency((int)busSpeed, Units.Frequency.UnitType.Hertz), transactionTimeout);
- }
-
- ///
- /// Creates an I2C bus for a set of given pins and parameters
- ///
- /// The Meadow IO Controller
- /// Clock (SCL) pin
- /// Data (SDA) pin
- /// Bus clock speed
- /// Bus transaction timeout
- /// An I2CBus instance
- public static I2cBus From(IMeadowIOController ioController, IPin clock, IPin data, Frequency frequency, ushort transactionTimeout = 100)
{
var clockChannel = clock.SupportedChannels.OfType().FirstOrDefault();
if (clockChannel == null || clockChannel.ChannelFunction != I2cChannelFunctionType.Clock)
@@ -136,11 +83,9 @@ public static I2cBus From(IMeadowIOController ioController, IPin clock, IPin dat
throw new Exception($"Pin {clock.Name} does not have I2C Data capabilities");
}
- return new I2cBus(ioController, clock, clockChannel, data, dataChannel, frequency, transactionTimeout);
+ return new I2cBus(ioController, clock, clockChannel, data, dataChannel, busSpeed, transactionTimeout);
}
- //==== NEW HOTNESS
-
///
/// Reads bytes from a peripheral.
///
@@ -154,11 +99,14 @@ public unsafe void Read(byte peripheralAddress, Span readBuffer)
{
_busSemaphore.Wait();
- try {
- fixed (byte* pData = readBuffer) {
- var command = new Nuttx.UpdI2CCommand() {
+ try
+ {
+ fixed (byte* pData = readBuffer)
+ {
+ var command = new Nuttx.UpdI2CCommand()
+ {
Address = peripheralAddress,
- Frequency = (int)this.Frequency.Hertz,
+ Frequency = (int)BusSpeed,
TxBufferLength = 0,
TxBuffer = IntPtr.Zero,
RxBufferLength = readBuffer.Length,
@@ -166,14 +114,15 @@ public unsafe void Read(byte peripheralAddress, Span readBuffer)
BusNumber = this.BusNumber
};
- Output.WriteIf(_showI2cDebug, " +SendData");
var result = UPD.Ioctl(Nuttx.UpdIoctlFn.I2CData, ref command);
- Output.WriteLineIf(_showI2cDebug, $" returned {result}");
- if (result != 0) {
+ if (result != 0)
+ {
DecipherI2CError(UPD.GetLastError());
}
}
- } finally {
+ }
+ finally
+ {
_busSemaphore.Release();
}
}
@@ -190,11 +139,14 @@ public unsafe void Write(byte peripheralAddress, Span data)
{
_busSemaphore.Wait();
- try {
- fixed (byte* pData = data) {
- var command = new Nuttx.UpdI2CCommand() {
+ try
+ {
+ fixed (byte* pData = data)
+ {
+ var command = new Nuttx.UpdI2CCommand()
+ {
Address = peripheralAddress,
- Frequency = (int)this.Frequency.Hertz,
+ Frequency = (int)this.BusSpeed,
TxBufferLength = data.Length,
TxBuffer = (IntPtr)pData,
RxBufferLength = 0,
@@ -202,14 +154,15 @@ public unsafe void Write(byte peripheralAddress, Span data)
BusNumber = this.BusNumber
};
- Output.WriteIf(_showI2cDebug, " +Write");
var result = UPD.Ioctl(Nuttx.UpdIoctlFn.I2CData, ref command);
- Output.WriteLineIf(_showI2cDebug, $" returned {result}");
- if (result != 0) {
+ if (result != 0)
+ {
DecipherI2CError(UPD.GetLastError());
}
}
- } finally {
+ }
+ finally
+ {
_busSemaphore.Release();
}
}
@@ -224,12 +177,15 @@ public unsafe void Write(byte peripheralAddress, Span data)
public unsafe void Exchange(byte peripheralAddress, Span writeBuffer, Span readBuffer)
{
_busSemaphore.Wait();
- try {
+ try
+ {
fixed (byte* pWrite = writeBuffer)
- fixed (byte* pRead = readBuffer) {
- var command = new Nuttx.UpdI2CCommand() {
+ fixed (byte* pRead = readBuffer)
+ {
+ var command = new Nuttx.UpdI2CCommand()
+ {
Address = peripheralAddress,
- Frequency = (int)this.Frequency.Hertz,
+ Frequency = (int)BusSpeed,
TxBufferLength = writeBuffer.Length,
TxBuffer = (IntPtr)pWrite,
RxBufferLength = readBuffer.Length,
@@ -239,18 +195,21 @@ public unsafe void Exchange(byte peripheralAddress, Span writeBuffer, Span
var result = UPD.Ioctl(Nuttx.UpdIoctlFn.I2CData, ref command);
- if (result != 0) {
+ if (result != 0)
+ {
DecipherI2CError(UPD.GetLastError());
}
}
- } finally {
+ }
+ finally
+ {
_busSemaphore.Release();
}
}
private void DecipherI2CError(Nuttx.ErrorCode ec)
{
- switch(ec)
+ switch (ec)
{
case (Nuttx.ErrorCode)125:
throw new NativeException("Communication error. Verify address and that SCL and SDA are not reversed.");
diff --git a/source/Meadow.F7/Hardware/Communications/OneWire.cs b/source/implementations/f7/Meadow.F7/Hardware/Communications/OneWire.cs
similarity index 100%
rename from source/Meadow.F7/Hardware/Communications/OneWire.cs
rename to source/implementations/f7/Meadow.F7/Hardware/Communications/OneWire.cs
diff --git a/source/Meadow.F7/Hardware/Communications/OneWireBus.cs b/source/implementations/f7/Meadow.F7/Hardware/Communications/OneWireBus.cs
similarity index 100%
rename from source/Meadow.F7/Hardware/Communications/OneWireBus.cs
rename to source/implementations/f7/Meadow.F7/Hardware/Communications/OneWireBus.cs
diff --git a/source/Meadow.F7/Hardware/Communications/SpiBus.cs b/source/implementations/f7/Meadow.F7/Hardware/Communications/SpiBus.cs
similarity index 91%
rename from source/Meadow.F7/Hardware/Communications/SpiBus.cs
rename to source/implementations/f7/Meadow.F7/Hardware/Communications/SpiBus.cs
index e011fe4f..9ee76c8b 100644
--- a/source/Meadow.F7/Hardware/Communications/SpiBus.cs
+++ b/source/implementations/f7/Meadow.F7/Hardware/Communications/SpiBus.cs
@@ -1,8 +1,5 @@
using Meadow.Devices;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
using System.Threading;
using static Meadow.Core.Interop;
@@ -44,15 +41,15 @@ internal static SpiBus From(
{
throw new NotSupportedException($"Pin {clock.Name} does not support SPI Clock capability");
}
- if (!mosi.Supports(p => (p.LineTypes & SpiLineType.MOSI) != SpiLineType.None))
+ if (!mosi.Supports(p => (p.LineTypes & SpiLineType.COPI) != SpiLineType.None))
{
throw new NotSupportedException($"Pin {mosi.Name} does not support SPI MOSI capability");
}
- if (!miso.Supports(p => (p.LineTypes & SpiLineType.MISO) != SpiLineType.None))
+ if (!miso.Supports(p => (p.LineTypes & SpiLineType.CIPO) != SpiLineType.None))
{
throw new NotSupportedException($"Pin {miso.Name} does not support SPI MISO capability");
}
-
+
// we can't set the speed here yet because the caller has to set the bus number first
return new SpiBus();
}
@@ -64,7 +61,7 @@ public SpiClockConfiguration Configuration
{
get
{
- if(_clockConfig == null)
+ if (_clockConfig == null)
{
Configuration = new SpiClockConfiguration(
new Units.Frequency(375, Units.Frequency.UnitType.Kilohertz),
@@ -78,7 +75,7 @@ internal set
{
if (value == null) { throw new ArgumentNullException(); }
- if(_clockConfig != null)
+ if (_clockConfig != null)
{
_clockConfig.Changed -= OnConfigChanged;
}
@@ -132,15 +129,18 @@ public unsafe void Read(
{
_busSemaphore.Wait();
- try {
- if (chipSelect != null) {
+ try
+ {
+ if (chipSelect != null)
+ {
// activate the chip select
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? false : true;
}
fixed (byte* pRead = readBuffer)
{
- var command = new Nuttx.UpdSPIDataCommand() {
+ var command = new Nuttx.UpdSPIDataCommand()
+ {
TxBuffer = IntPtr.Zero,
BufferLength = readBuffer.Length,
RxBuffer = (IntPtr)pRead,
@@ -148,16 +148,20 @@ public unsafe void Read(
};
var result = UPD.Ioctl(Nuttx.UpdIoctlFn.SPIData, ref command);
- if (result != 0) {
+ if (result != 0)
+ {
DecipherSPIError(UPD.GetLastError());
}
- if (chipSelect != null) {
+ if (chipSelect != null)
+ {
// deactivate the chip select
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? true : false;
}
}
- } finally {
+ }
+ finally
+ {
_busSemaphore.Release();
}
}
@@ -176,14 +180,18 @@ public unsafe void Write(
_busSemaphore.Wait();
Output.WriteLineIf(_showSpiDebug, $" +SendData");
- try {
- if (chipSelect != null) {
+ try
+ {
+ if (chipSelect != null)
+ {
// activate the chip select
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? false : true;
}
- fixed (byte* pWrite = writeBuffer) {
- var command = new Nuttx.UpdSPIDataCommand() {
+ fixed (byte* pWrite = writeBuffer)
+ {
+ var command = new Nuttx.UpdSPIDataCommand()
+ {
BufferLength = writeBuffer.Length,
TxBuffer = (IntPtr)pWrite,
RxBuffer = IntPtr.Zero,
@@ -192,17 +200,21 @@ public unsafe void Write(
Output.WriteLineIf(_showSpiDebug, $" sending {writeBuffer.Length} bytes: {BitConverter.ToString(writeBuffer.ToArray())}");
var result = UPD.Ioctl(Nuttx.UpdIoctlFn.SPIData, ref command);
- if (result != 0) {
+ if (result != 0)
+ {
DecipherSPIError(UPD.GetLastError());
}
Output.WriteLineIf(_showSpiDebug, $" send complete");
- if (chipSelect != null) {
+ if (chipSelect != null)
+ {
// deactivate the chip select
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? true : false;
}
}
- } finally {
+ }
+ finally
+ {
_busSemaphore.Release();
Output.WriteLineIf(_showSpiDebug, $" -SendData");
}
@@ -227,15 +239,19 @@ public unsafe void Exchange(
_busSemaphore.Wait();
- try {
- if (chipSelect != null) {
+ try
+ {
+ if (chipSelect != null)
+ {
// activate the chip select
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? false : true;
}
fixed (byte* pWrite = writeBuffer)
- fixed (byte* pRead = readBuffer) {
- var command = new Nuttx.UpdSPIDataCommand() {
+ fixed (byte* pRead = readBuffer)
+ {
+ var command = new Nuttx.UpdSPIDataCommand()
+ {
BufferLength = readBuffer.Length,
TxBuffer = (IntPtr)pWrite,
RxBuffer = (IntPtr)pRead,
@@ -245,17 +261,21 @@ public unsafe void Exchange(
Output.WriteLineIf(_showSpiDebug, "+Exchange");
Output.WriteLineIf(_showSpiDebug, $" Sending {writeBuffer.Length} bytes");
var result = UPD.Ioctl(Nuttx.UpdIoctlFn.SPIData, ref command);
- if (result != 0) {
+ if (result != 0)
+ {
DecipherSPIError(UPD.GetLastError());
}
Output.WriteLineIf(_showSpiDebug, $" Received {readBuffer.Length} bytes");
- if (chipSelect != null) {
+ if (chipSelect != null)
+ {
// deactivate the chip select
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? true : false;
}
}
- } finally {
+ }
+ finally
+ {
_busSemaphore.Release();
}
}
@@ -280,7 +300,7 @@ public Units.Frequency[] SupportedSpeeds
private void SetBitsPerWord(int bitsPerWord)
{
- if(bitsPerWord < 4 || bitsPerWord > 16)
+ if (bitsPerWord < 4 || bitsPerWord > 16)
{
throw new ArgumentOutOfRangeException();
}
diff --git a/source/Meadow.F7/Hardware/PwmPort.cs b/source/implementations/f7/Meadow.F7/Hardware/PwmPort.cs
similarity index 98%
rename from source/Meadow.F7/Hardware/PwmPort.cs
rename to source/implementations/f7/Meadow.F7/Hardware/PwmPort.cs
index cb9ab678..6f4c514d 100644
--- a/source/Meadow.F7/Hardware/PwmPort.cs
+++ b/source/implementations/f7/Meadow.F7/Hardware/PwmPort.cs
@@ -61,7 +61,7 @@ internal static PwmPort From(
}
else
{
- throw new PortInUseException(success.Item2);
+ throw new PortInUseException($"PwmPort: Pin {pin.Name} is already in use");
}
}
else
diff --git a/source/Meadow.F7/IF7CoreComputeMeadowDevice.cs b/source/implementations/f7/Meadow.F7/IF7CoreComputeMeadowDevice.cs
similarity index 100%
rename from source/Meadow.F7/IF7CoreComputeMeadowDevice.cs
rename to source/implementations/f7/Meadow.F7/IF7CoreComputeMeadowDevice.cs
diff --git a/source/Meadow.F7/IF7FeatherMeadowDevice.cs b/source/implementations/f7/Meadow.F7/IF7FeatherMeadowDevice.cs
similarity index 100%
rename from source/Meadow.F7/IF7FeatherMeadowDevice.cs
rename to source/implementations/f7/Meadow.F7/IF7FeatherMeadowDevice.cs
diff --git a/source/Meadow.F7/IF7MeadowDevice.cs b/source/implementations/f7/Meadow.F7/IF7MeadowDevice.cs
similarity index 100%
rename from source/Meadow.F7/IF7MeadowDevice.cs
rename to source/implementations/f7/Meadow.F7/IF7MeadowDevice.cs
diff --git a/source/Meadow.F7/Interop/Interop.clock_gettime.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.clock_gettime.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.clock_gettime.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.clock_gettime.cs
diff --git a/source/Meadow.F7/Interop/Interop.clock_settime.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.clock_settime.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.clock_settime.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.clock_settime.cs
diff --git a/source/Meadow.F7/Interop/Interop.close.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.close.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.close.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.close.cs
diff --git a/source/Meadow.F7/Interop/Interop.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.cs
diff --git a/source/Meadow.F7/Interop/Interop.gpio.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.gpio.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.gpio.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.gpio.cs
diff --git a/source/Meadow.F7/Interop/Interop.ioctl.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.ioctl.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.ioctl.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.ioctl.cs
diff --git a/source/Meadow.F7/Interop/Interop.mount.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.mount.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.mount.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.mount.cs
diff --git a/source/Meadow.F7/Interop/Interop.open.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.open.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.open.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.open.cs
diff --git a/source/Meadow.F7/Interop/Interop.queue.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.queue.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.queue.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.queue.cs
diff --git a/source/Meadow.F7/Interop/Interop.read.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.read.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.read.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.read.cs
diff --git a/source/Meadow.F7/Interop/Interop.signal.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.signal.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.signal.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.signal.cs
diff --git a/source/Meadow.F7/Interop/Interop.stm32.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.stm32.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.stm32.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.stm32.cs
diff --git a/source/Meadow.F7/Interop/Interop.termios.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.termios.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.termios.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.termios.cs
diff --git a/source/Meadow.F7/Interop/Interop.timespec.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.timespec.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.timespec.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.timespec.cs
diff --git a/source/Meadow.F7/Interop/Interop.upd.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.upd.cs
similarity index 98%
rename from source/Meadow.F7/Interop/Interop.upd.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.upd.cs
index d66f2e35..56249d8d 100644
--- a/source/Meadow.F7/Interop/Interop.upd.cs
+++ b/source/implementations/f7/Meadow.F7/Interop/Interop.upd.cs
@@ -366,7 +366,7 @@ public static bool TryGetRegister(IntPtr driverHandle, uint address, out uint va
if (result != 0)
{
var errno = Devices.UPD.GetLastError();
- Console.WriteLine($"GetRegister failed: {errno}");
+ Resolver.Log.Info($"GetRegister failed: {errno}");
value = (uint)result;
return false;
}
@@ -392,7 +392,7 @@ public static bool SetRegister(IntPtr driverHandle, uint address, uint value)
if (result != 0)
{
var errno = Devices.UPD.GetLastError();
- Console.WriteLine($"SetRegister failed: {errno}");
+ Resolver.Log.Error($"SetRegister failed: {errno}");
return false;
}
return true;
@@ -416,7 +416,7 @@ public static bool UpdateRegister(IntPtr driverHandle, uint address, uint clearB
var result = Interop.Nuttx.ioctl(driverHandle, UpdIoctlFn.UpdateRegister, ref update);
if (result != 0)
{
- Console.WriteLine($"Update failed: {result}");
+ Resolver.Log.Error($"Update failed: {result}");
return false;
}
return true;
diff --git a/source/Meadow.F7/Interop/Interop.write.cs b/source/implementations/f7/Meadow.F7/Interop/Interop.write.cs
similarity index 100%
rename from source/Meadow.F7/Interop/Interop.write.cs
rename to source/implementations/f7/Meadow.F7/Interop/Interop.write.cs
diff --git a/source/Meadow.F7/Interop/stm32_gpio.h b/source/implementations/f7/Meadow.F7/Interop/stm32_gpio.h
similarity index 100%
rename from source/Meadow.F7/Interop/stm32_gpio.h
rename to source/implementations/f7/Meadow.F7/Interop/stm32_gpio.h
diff --git a/source/Meadow.F7/Meadow.F7.csproj b/source/implementations/f7/Meadow.F7/Meadow.F7.csproj
similarity index 85%
rename from source/Meadow.F7/Meadow.F7.csproj
rename to source/implementations/f7/Meadow.F7/Meadow.F7.csproj
index aa0574a9..50d7c312 100644
--- a/source/Meadow.F7/Meadow.F7.csproj
+++ b/source/implementations/f7/Meadow.F7/Meadow.F7.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.1
Library
@@ -25,9 +25,9 @@
-
+
-
+
diff --git a/source/Meadow.F7/UPD.cs b/source/implementations/f7/Meadow.F7/UPD.cs
similarity index 87%
rename from source/Meadow.F7/UPD.cs
rename to source/implementations/f7/Meadow.F7/UPD.cs
index 302f39f5..d63bc3da 100644
--- a/source/Meadow.F7/UPD.cs
+++ b/source/implementations/f7/Meadow.F7/UPD.cs
@@ -43,18 +43,18 @@ public static void DumpClockRegisters()
var dckcfg1 = GetRegister(STM32.RCC_BASE + STM32.RCC_DCKCFGR1_OFFSET);
var dckcfg2 = GetRegister(STM32.RCC_BASE + STM32.RCC_DCKCFGR2_OFFSET);
- Console.WriteLine("Clock Registers");
- Console.WriteLine($"\tRCC_CR: {cr:X8}");
- Console.WriteLine($"\tRCC_CFGR: {cfg:X8}");
- Console.WriteLine($"\tRCC_AHB1RSTR: {ahb1rst:X8}");
- Console.WriteLine($"\tRCC_AHB1ENR: {ahb1en:X8}");
- Console.WriteLine($"\tRCC_APB1RSTR: {apb1rst:X8}");
- Console.WriteLine($"\tRCC_APB1ENR: {apb1en:X8}");
- Console.WriteLine($"\tRCC_APB2RSTR: {apb2rst:X8}");
- Console.WriteLine($"\tRCC_APB2ENR: {apb2en:X8}");
- Console.WriteLine($"\tRCC_PLLCFGR: {pllcfg:X8}");
- Console.WriteLine($"\tRCC_DCKCFGR1: {dckcfg1:X8}");
- Console.WriteLine($"\tRCC_DCKCFGR2: {dckcfg2:X8}");
+ Resolver.Log.Info("Clock Registers");
+ Resolver.Log.Info($"\tRCC_CR: {cr:X8}");
+ Resolver.Log.Info($"\tRCC_CFGR: {cfg:X8}");
+ Resolver.Log.Info($"\tRCC_AHB1RSTR: {ahb1rst:X8}");
+ Resolver.Log.Info($"\tRCC_AHB1ENR: {ahb1en:X8}");
+ Resolver.Log.Info($"\tRCC_APB1RSTR: {apb1rst:X8}");
+ Resolver.Log.Info($"\tRCC_APB1ENR: {apb1en:X8}");
+ Resolver.Log.Info($"\tRCC_APB2RSTR: {apb2rst:X8}");
+ Resolver.Log.Info($"\tRCC_APB2ENR: {apb2en:X8}");
+ Resolver.Log.Info($"\tRCC_PLLCFGR: {pllcfg:X8}");
+ Resolver.Log.Info($"\tRCC_DCKCFGR1: {dckcfg1:X8}");
+ Resolver.Log.Info($"\tRCC_DCKCFGR2: {dckcfg2:X8}");
}
public static void DumpI2CRegisters()
@@ -65,12 +65,12 @@ public static void DumpI2CRegisters()
var timing = UPD.GetRegister(STM32.MEADOW_I2C1_BASE + STM32.I2C_TIMINGR_OFFSET);
var timeout = UPD.GetRegister(STM32.MEADOW_I2C1_BASE + STM32.I2C_TIMEOUTR_OFFSET);
- Console.WriteLine("I2C Registers");
- Console.WriteLine($"\tI2C_CR1: {cr1:X8}");
- Console.WriteLine($"\tI2C_CR2: {cr2:X8}");
- Console.WriteLine($"\tI2C_ISR: {isr:X8}");
- Console.WriteLine($"\tI2C_TIMINGR: {timing:X8}");
- Console.WriteLine($"\tI2C_TIMEOUTR: {timeout:X8}");
+ Resolver.Log.Info("I2C Registers");
+ Resolver.Log.Info($"\tI2C_CR1: {cr1:X8}");
+ Resolver.Log.Info($"\tI2C_CR2: {cr2:X8}");
+ Resolver.Log.Info($"\tI2C_ISR: {isr:X8}");
+ Resolver.Log.Info($"\tI2C_TIMINGR: {timing:X8}");
+ Resolver.Log.Info($"\tI2C_TIMEOUTR: {timeout:X8}");
}
public static void SetRegister(uint address, uint value)
@@ -103,7 +103,7 @@ public static int Ioctl(Nuttx.GpioIoctlFn request, ref int pinDesignator)
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -115,7 +115,7 @@ public static int Ioctl(Nuttx.GpioIoctlFn request, ref Nuttx.GPIOPinState pinSta
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -127,7 +127,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdRegisterValue reg
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -139,7 +139,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdRegisterUpdate re
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -151,7 +151,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request)
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -163,7 +163,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, Nuttx.UpdSleepCommand command)
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -176,7 +176,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdGpioInterruptConf
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -237,7 +237,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdSPIBitsCommand sp
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -249,7 +249,7 @@ public static int Ioctl(Nuttx.GpioIoctlFn request, ref Nuttx.GPIOConfigFlags con
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -261,7 +261,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdEnumDirCmd cmd)
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -273,7 +273,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdEsp32Command cmd)
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int) err;
}
return result;
@@ -285,7 +285,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdEsp32EventDataPay
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int) err;
}
return result;
@@ -297,7 +297,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdConfigurationValu
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int) err;
}
return result;
@@ -309,7 +309,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref Nuttx.UpdDeviceInfo device
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -321,7 +321,7 @@ public static int Ioctl(Nuttx.UpdIoctlFn request, ref ulong param)
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"ioctl {request} failed {err}");
+ Resolver.Log.Error($"ioctl {request} failed {err}");
return (int)err;
}
return result;
@@ -337,7 +337,7 @@ public static bool PwmCmd(Nuttx.UpdIoctlFn request, Nuttx.UpdPwmCmd data)
if (result != 0)
{
var err = GetLastError();
- Console.WriteLine($"PWM setup failed {err}");
+ Resolver.Log.Error($"PWM setup failed {err}");
return false;
}
diff --git a/source/implementations/linux/Meadow.Gtk/GtkDisplay.cs b/source/implementations/linux/Meadow.Gtk/GtkDisplay.cs
new file mode 100644
index 00000000..116024fc
--- /dev/null
+++ b/source/implementations/linux/Meadow.Gtk/GtkDisplay.cs
@@ -0,0 +1,226 @@
+using Cairo;
+using Gtk;
+using Meadow.Foundation.Graphics;
+using Meadow.Foundation.Graphics.Buffers;
+using Meadow.Hardware;
+using System;
+using System.Buffers.Binary;
+using System.Threading;
+
+namespace Meadow.Graphics
+{
+ public class GtkDisplay : IGraphicsDisplay, ITouchScreen
+ {
+ public event Hardware.TouchEventHandler TouchDown;
+ public event Hardware.TouchEventHandler TouchUp;
+ public event Hardware.TouchEventHandler TouchClick;
+
+ private Window _window;
+ private IPixelBuffer _pixelBuffer;
+ private Cairo.Format _format;
+ private int _stride;
+ private Action? _bufferConverter = null;
+ private bool _leftButtonState = false;
+
+ private EventWaitHandle ShowComplete { get; } = new EventWaitHandle(true, EventResetMode.ManualReset);
+ public IPixelBuffer PixelBuffer => _pixelBuffer;
+ public ColorMode ColorMode => _pixelBuffer.ColorMode;
+
+ public int Width => _window.Window.Width;
+ public int Height => _window.Window.Height;
+
+ public ColorMode SupportedColorModes => ColorMode.Format24bppRgb888 | ColorMode.Format16bppRgb565 | ColorMode.Format32bppRgba8888;
+
+ static GtkDisplay()
+ {
+ Application.Init();
+ }
+
+ public GtkDisplay(ColorMode mode = ColorMode.Format24bppRgb888)
+ {
+ Initialize(800, 600, mode); // TODO: query screen size and caps
+ }
+
+ public GtkDisplay(int width, int height, ColorMode mode = ColorMode.Format24bppRgb888)
+ {
+ Initialize(width, height, mode);
+ }
+
+ private void Initialize(int width, int height, ColorMode mode)
+ {
+ _window = new Window(WindowType.Popup);
+ _window.WidgetEvent += _window_WidgetEvent;
+
+ switch (mode)
+ {
+ case ColorMode.Format24bppRgb888:
+ _pixelBuffer = new BufferRgb888(width, height);
+ _format = Cairo.Format.Rgb24;
+ break;
+ case ColorMode.Format16bppRgb565:
+ _pixelBuffer = new BufferRgb565(width, height);
+ _format = Cairo.Format.Rgb16565;
+ _bufferConverter = ConvertRGBBufferToBGRBuffer24;
+ break;
+ case ColorMode.Format32bppRgba8888:
+ _pixelBuffer = new BufferRgb8888(width, height);
+ _format = Cairo.Format.Argb32;
+ break;
+ default:
+ throw new Exception($"Mode {mode} not supported");
+ }
+ _stride = GetStrideForBitDepth(width, _pixelBuffer.BitDepth);
+
+ _window.WindowPosition = WindowPosition.Center;
+ _window.DefaultSize = new Gdk.Size(width, height);
+ _window.ShowAll();
+ _window.Drawn += OnWindowDrawn;
+ }
+
+ private void RaiseTouchDown(double x, double y)
+ {
+ _leftButtonState = true;
+ TouchDown?.Invoke((int)x, (int)y);
+ }
+
+ private void RaiseTouchUp(double x, double y)
+ {
+ TouchUp?.Invoke((int)x, (int)y);
+ if (_leftButtonState)
+ {
+ TouchClick?.Invoke((int)x, (int)y);
+ }
+ _leftButtonState = false;
+
+ }
+
+ private void _window_WidgetEvent(object o, WidgetEventArgs args)
+ {
+ if (args.Event is Gdk.EventButton b)
+ {
+ switch (b.Button)
+ {
+ case 1: // left
+ switch (b.State)
+ {
+ case Gdk.ModifierType.None:
+ RaiseTouchDown(b.X, b.Y);
+ break;
+ case Gdk.ModifierType.Button1Mask:
+ RaiseTouchUp(b.X, b.Y);
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ public void Run()
+ {
+ Application.Run();
+ }
+
+ private void OnWindowDrawn(object o, DrawnArgs args)
+ {
+ _bufferConverter?.Invoke(_pixelBuffer.Buffer);
+
+ using (var surface = new ImageSurface(_pixelBuffer.Buffer, _format, Width, Height, _stride))
+ {
+ args.Cr.SetSource(surface);
+ args.Cr.Paint();
+
+ if (args.Cr.GetTarget() is IDisposable d) d.Dispose();
+ if (args.Cr is IDisposable cd) cd.Dispose();
+ args.RetVal = true;
+ }
+ ShowComplete.Set();
+ }
+
+ public void Show()
+ {
+ _window.QueueDraw();
+ ShowComplete.Reset();
+ }
+
+ public void Show(int left, int top, int right, int bottom)
+ {
+ _window.QueueDrawArea(left, top, right - left, bottom - top);
+ ShowComplete.Reset();
+ }
+
+ public void Clear(bool updateDisplay = false)
+ {
+ ShowComplete.WaitOne();
+ _pixelBuffer.Clear();
+ }
+
+ public void Fill(Foundation.Color fillColor, bool updateDisplay = false)
+ {
+ ShowComplete.WaitOne();
+ _pixelBuffer.Fill(fillColor);
+ }
+
+ public void Fill(int x, int y, int width, int height, Foundation.Color fillColor)
+ {
+ ShowComplete.WaitOne();
+ _pixelBuffer.Fill(x, y, width, height, fillColor);
+ }
+
+ public void DrawPixel(int x, int y, Foundation.Color color)
+ {
+ ShowComplete.WaitOne();
+ _pixelBuffer.SetPixel(x, y, color);
+ }
+
+ public void DrawPixel(int x, int y, bool colored)
+ {
+ ShowComplete.WaitOne();
+ DrawPixel(x, y, colored ? Foundation.Color.White : Foundation.Color.Black);
+ }
+
+ public void InvertPixel(int x, int y)
+ {
+ ShowComplete.WaitOne();
+ _pixelBuffer.InvertPixel(x, y);
+ }
+
+ public void WriteBuffer(int x, int y, IPixelBuffer displayBuffer)
+ {
+ ShowComplete.WaitOne();
+ _pixelBuffer.WriteBuffer(x, y, displayBuffer);
+ }
+
+
+ // This attempts to copy the cairo method for getting stride length
+ // as defined here:
+ // https://github.com/freedesktop/cairo/blob/a934fa66dba2b880723f4e5c3fdea92cbe0207e7/src/cairoint.h#L1553
+ // #define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \
+ // ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT)
+ private int GetStrideForBitDepth(int width, int bpp)
+ {
+ var cairo_stride_alignment = sizeof(UInt32);
+ var stride = ((((bpp) * (width) + 7) / 8 + cairo_stride_alignment - 1) & -cairo_stride_alignment);
+ return stride;
+ }
+
+ ///
+ /// 24-bit RGB to BGR converter
+ ///
+ ///
+ private void ConvertRGBBufferToBGRBuffer24(byte[] buffer)
+ {
+ for (int i = 0; i < buffer.Length; i += 2)
+ {
+ // convert two bytes into a short
+ ushort pixel = (ushort)(buffer[i] << 8 | buffer[i + 1]);
+
+ // reverse the endianness because that's what cairo expects
+ BinaryPrimitives.ReverseEndianness(pixel);
+
+ // separate back into two bytes
+ buffer[i] = (byte)pixel;
+ buffer[i + 1] = (byte)(pixel >> 8);
+ }
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Gtk/Meadow.Gtk.csproj b/source/implementations/linux/Meadow.Gtk/Meadow.Gtk.csproj
new file mode 100644
index 00000000..a7cc10ce
--- /dev/null
+++ b/source/implementations/linux/Meadow.Gtk/Meadow.Gtk.csproj
@@ -0,0 +1,33 @@
+
+
+ net6.0
+ Library
+ Meadow.Gtk
+ Wilderness Labs, Inc
+ http://beta-developer.wildernesslabs.co/Meadow/
+ Meadow.Gtk
+ icon.png
+ https://github.com/WildernessLabs/Meadow.Linux
+ Meadow
+ 0.31.0
+ false
+ Meadow
+ enable
+ GTK .NET libraries for Wilderness Labs Meadow
+
+
+ true
+ 9.0
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/implementations/linux/Meadow.Linux.sln b/source/implementations/linux/Meadow.Linux.sln
new file mode 100644
index 00000000..9231e16a
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux.sln
@@ -0,0 +1,157 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.32126.317
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Linux", "lib\Meadow.Linux.csproj", "{D4E5C593-FFB2-427F-82E3-6D9F655243E7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{8436E98A-9974-43FE-ADF2-EC60505E767B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{79CF70C9-E622-45AF-9A58-D954DA93572D}"
+ ProjectSection(SolutionItems) = preProject
+ ..\README.md = ..\README.md
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "M4L.Unit.Tests", "tests\M4L.Unit.Tests\M4L.Unit.Tests.csproj", "{7D66ACC4-3C11-4BE4-8803-204BAABFBA33}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pi", "pi", "{53A3F4CA-E259-45DA-B0F1-B9A54A3A24F7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "krtkl", "krtkl", "{249BB39C-DD2A-4EF7-8DDB-3BEFD0C9EE52}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nvidia", "nvidia", "{5CA85A22-762C-4354-992E-BF0217E6DC67}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalIOSample", "samples\krtkl\DigitalIOSample\DigitalIOSample.csproj", "{6BA83178-2CB0-4B25-B07B-AECBEF1F8190}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XavierI2C_Sample", "samples\nvidia\XavierI2C_Sample\XavierI2C_Sample.csproj", "{D11CA6AD-B4C1-4D57-B74E-81FF23E27C91}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalIOSample", "samples\pi\DigitalIOSample\DigitalIOSample.csproj", "{450CCC0A-93C0-49D0-A692-53D4A67C6653}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ads1015_Sample", "samples\pi\Ads1015_Sample\Ads1015_Sample.csproj", "{10A98F28-C1D8-4572-9738-AB6A4CDD3668}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bme280_Sample", "samples\pi\Bme280_Sample\Bme280_Sample.csproj", "{0FAD6B89-96B8-43B8-AC3D-A542D52F20AE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PushButton_Sample", "samples\pi\PushButton_Sample\PushButton_Sample.csproj", "{5D3B4E9E-D232-4CBD-AEA7-5AE9910F4C81}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ST7789_Sample", "samples\pi\ST7789_Sample\ST7789_Sample.csproj", "{7DBE81E0-5020-470C-8A46-EC65B462C6FD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Gtk", "gtk\Meadow.Gtk.csproj", "{4A1A7A9D-89F6-4BE4-886C-EFE01222F4BB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{608ED7EE-E06F-4FB6-B1B4-C6032E817330}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gtk", "gtk", "{F21963C0-C877-4754-948A-D7F34F861F33}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Gtk_Sample", "samples\gtk\Meadow.Gtk_Sample.csproj", "{27290168-8BCE-470F-96AC-6D72C90624CD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{26BE0786-BFDA-46DA-87D7-7B8FDDE38AEA}"
+ ProjectSection(SolutionItems) = preProject
+ ..\README.md = ..\README.md
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Simulation", "..\..\Meadow.Simulation\src\Meadow.Simulation\Meadow.Simulation.csproj", "{A41A2CB9-6815-454D-B0F4-ACDDEE8E4197}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Core", "..\..\Meadow.Core\source\Meadow.Core\Meadow.Core.csproj", "{C47393AD-6785-45C5-B687-61A0935B691F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "..\..\MQTTnet\Source\MQTTnet\MQTTnet.csproj", "{727ABAB6-DC2F-47F2-93DB-2FBB92419E7F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Contracts", "..\..\Meadow.Contracts\Source\Meadow.Contracts\Meadow.Contracts.csproj", "{6009ADDE-F635-4B0A-B430-B2CED0146DAB}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D4E5C593-FFB2-427F-82E3-6D9F655243E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4E5C593-FFB2-427F-82E3-6D9F655243E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4E5C593-FFB2-427F-82E3-6D9F655243E7}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {D4E5C593-FFB2-427F-82E3-6D9F655243E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4E5C593-FFB2-427F-82E3-6D9F655243E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D4E5C593-FFB2-427F-82E3-6D9F655243E7}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {7D66ACC4-3C11-4BE4-8803-204BAABFBA33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7D66ACC4-3C11-4BE4-8803-204BAABFBA33}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7D66ACC4-3C11-4BE4-8803-204BAABFBA33}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D66ACC4-3C11-4BE4-8803-204BAABFBA33}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6BA83178-2CB0-4B25-B07B-AECBEF1F8190}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6BA83178-2CB0-4B25-B07B-AECBEF1F8190}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6BA83178-2CB0-4B25-B07B-AECBEF1F8190}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6BA83178-2CB0-4B25-B07B-AECBEF1F8190}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D11CA6AD-B4C1-4D57-B74E-81FF23E27C91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D11CA6AD-B4C1-4D57-B74E-81FF23E27C91}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D11CA6AD-B4C1-4D57-B74E-81FF23E27C91}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D11CA6AD-B4C1-4D57-B74E-81FF23E27C91}.Release|Any CPU.Build.0 = Release|Any CPU
+ {450CCC0A-93C0-49D0-A692-53D4A67C6653}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {450CCC0A-93C0-49D0-A692-53D4A67C6653}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {450CCC0A-93C0-49D0-A692-53D4A67C6653}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {450CCC0A-93C0-49D0-A692-53D4A67C6653}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10A98F28-C1D8-4572-9738-AB6A4CDD3668}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10A98F28-C1D8-4572-9738-AB6A4CDD3668}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10A98F28-C1D8-4572-9738-AB6A4CDD3668}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10A98F28-C1D8-4572-9738-AB6A4CDD3668}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0FAD6B89-96B8-43B8-AC3D-A542D52F20AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0FAD6B89-96B8-43B8-AC3D-A542D52F20AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0FAD6B89-96B8-43B8-AC3D-A542D52F20AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0FAD6B89-96B8-43B8-AC3D-A542D52F20AE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5D3B4E9E-D232-4CBD-AEA7-5AE9910F4C81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D3B4E9E-D232-4CBD-AEA7-5AE9910F4C81}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D3B4E9E-D232-4CBD-AEA7-5AE9910F4C81}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D3B4E9E-D232-4CBD-AEA7-5AE9910F4C81}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7DBE81E0-5020-470C-8A46-EC65B462C6FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DBE81E0-5020-470C-8A46-EC65B462C6FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7DBE81E0-5020-470C-8A46-EC65B462C6FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7DBE81E0-5020-470C-8A46-EC65B462C6FD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4A1A7A9D-89F6-4BE4-886C-EFE01222F4BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4A1A7A9D-89F6-4BE4-886C-EFE01222F4BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4A1A7A9D-89F6-4BE4-886C-EFE01222F4BB}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {4A1A7A9D-89F6-4BE4-886C-EFE01222F4BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4A1A7A9D-89F6-4BE4-886C-EFE01222F4BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4A1A7A9D-89F6-4BE4-886C-EFE01222F4BB}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {27290168-8BCE-470F-96AC-6D72C90624CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {27290168-8BCE-470F-96AC-6D72C90624CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {27290168-8BCE-470F-96AC-6D72C90624CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {27290168-8BCE-470F-96AC-6D72C90624CD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A41A2CB9-6815-454D-B0F4-ACDDEE8E4197}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A41A2CB9-6815-454D-B0F4-ACDDEE8E4197}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A41A2CB9-6815-454D-B0F4-ACDDEE8E4197}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A41A2CB9-6815-454D-B0F4-ACDDEE8E4197}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C47393AD-6785-45C5-B687-61A0935B691F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C47393AD-6785-45C5-B687-61A0935B691F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C47393AD-6785-45C5-B687-61A0935B691F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {C47393AD-6785-45C5-B687-61A0935B691F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C47393AD-6785-45C5-B687-61A0935B691F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C47393AD-6785-45C5-B687-61A0935B691F}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {727ABAB6-DC2F-47F2-93DB-2FBB92419E7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {727ABAB6-DC2F-47F2-93DB-2FBB92419E7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {727ABAB6-DC2F-47F2-93DB-2FBB92419E7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {727ABAB6-DC2F-47F2-93DB-2FBB92419E7F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6009ADDE-F635-4B0A-B430-B2CED0146DAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6009ADDE-F635-4B0A-B430-B2CED0146DAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6009ADDE-F635-4B0A-B430-B2CED0146DAB}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {6009ADDE-F635-4B0A-B430-B2CED0146DAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6009ADDE-F635-4B0A-B430-B2CED0146DAB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6009ADDE-F635-4B0A-B430-B2CED0146DAB}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {53A3F4CA-E259-45DA-B0F1-B9A54A3A24F7} = {8436E98A-9974-43FE-ADF2-EC60505E767B}
+ {249BB39C-DD2A-4EF7-8DDB-3BEFD0C9EE52} = {8436E98A-9974-43FE-ADF2-EC60505E767B}
+ {5CA85A22-762C-4354-992E-BF0217E6DC67} = {8436E98A-9974-43FE-ADF2-EC60505E767B}
+ {6BA83178-2CB0-4B25-B07B-AECBEF1F8190} = {249BB39C-DD2A-4EF7-8DDB-3BEFD0C9EE52}
+ {D11CA6AD-B4C1-4D57-B74E-81FF23E27C91} = {5CA85A22-762C-4354-992E-BF0217E6DC67}
+ {450CCC0A-93C0-49D0-A692-53D4A67C6653} = {53A3F4CA-E259-45DA-B0F1-B9A54A3A24F7}
+ {10A98F28-C1D8-4572-9738-AB6A4CDD3668} = {53A3F4CA-E259-45DA-B0F1-B9A54A3A24F7}
+ {0FAD6B89-96B8-43B8-AC3D-A542D52F20AE} = {53A3F4CA-E259-45DA-B0F1-B9A54A3A24F7}
+ {5D3B4E9E-D232-4CBD-AEA7-5AE9910F4C81} = {53A3F4CA-E259-45DA-B0F1-B9A54A3A24F7}
+ {7DBE81E0-5020-470C-8A46-EC65B462C6FD} = {53A3F4CA-E259-45DA-B0F1-B9A54A3A24F7}
+ {F21963C0-C877-4754-948A-D7F34F861F33} = {8436E98A-9974-43FE-ADF2-EC60505E767B}
+ {27290168-8BCE-470F-96AC-6D72C90624CD} = {F21963C0-C877-4754-948A-D7F34F861F33}
+ {A41A2CB9-6815-454D-B0F4-ACDDEE8E4197} = {608ED7EE-E06F-4FB6-B1B4-C6032E817330}
+ {C47393AD-6785-45C5-B687-61A0935B691F} = {608ED7EE-E06F-4FB6-B1B4-C6032E817330}
+ {727ABAB6-DC2F-47F2-93DB-2FBB92419E7F} = {608ED7EE-E06F-4FB6-B1B4-C6032E817330}
+ {6009ADDE-F635-4B0A-B430-B2CED0146DAB} = {608ED7EE-E06F-4FB6-B1B4-C6032E817330}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {6CC77371-5880-4AD7-8506-1FB909E18343}
+ EndGlobalSection
+EndGlobal
diff --git a/source/implementations/linux/Meadow.Linux/AssemblyInfo.cs b/source/implementations/linux/Meadow.Linux/AssemblyInfo.cs
new file mode 100644
index 00000000..60067ed9
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("M4L.Unit.Tests")]
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Communications/I2CBus.cs b/source/implementations/linux/Meadow.Linux/Hardware/Communications/I2CBus.cs
new file mode 100644
index 00000000..e2d060f1
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Communications/I2CBus.cs
@@ -0,0 +1,156 @@
+using Meadow.Hardware;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace Meadow
+{
+ public partial class I2CBus : II2cBus, IDisposable
+ {
+ private class SMBusIoctlData
+ {
+ public SMBusIoctlData(int size)
+ {
+ Size = size;
+ Data = new byte[Size];
+ }
+
+ public byte ReadWrite { get; set; }
+ public byte Command { get; set; }
+ public int Size { get; set; }
+ public byte[] Data { get; set; }
+ }
+
+ private enum I2CIoctl
+ {
+ RETRIES = 0x0701,
+ TIMEOUT = 0x0702,
+ SLAVE = 0x0703,
+ FUNCS = 0x0705,
+ RDWR = 0x0707,
+ SMBUS = 0x0720
+ }
+
+ public int BusNumber { get; set; } = 1;
+ public I2cBusSpeed BusSpeed { get; set; }
+
+ private PeripheralMap InfoMap { get; }
+
+ public I2CBus(int busNumber, I2cBusSpeed busSpeed)
+ {
+ BusNumber = busNumber;
+
+ // TODO: how do we affect frequency on these platforms?
+ BusSpeed = busSpeed;
+
+ InfoMap = new PeripheralMap(BusNumber);
+ }
+
+ public void Dispose()
+ {
+ InfoMap.Dispose();
+ }
+
+ public unsafe void Read(byte peripheralAddress, Span readBuffer)
+ {
+ var handle = InfoMap.GetAddressHandle(BusNumber, peripheralAddress);
+ fixed (byte* pData = readBuffer)
+ {
+ var result = Interop.read(handle, pData, readBuffer.Length);
+ if (result < 0)
+ {
+ var le = (LinuxErrorCode)Marshal.GetLastWin32Error();
+ switch (le)
+ {
+ case LinuxErrorCode.IOError:
+ throw new NativeException("I/O Error. Check your wiring");
+ default:
+ var msg = $"READ ERROR: {le}";
+ throw new NativeException(msg);
+ }
+ }
+ }
+ }
+
+ public unsafe void Write(byte peripheralAddress, Span writeBuffer)
+ {
+ var handle = InfoMap.GetAddressHandle(BusNumber, peripheralAddress);
+ fixed (byte* pData = writeBuffer)
+ {
+ var result = Interop.write(handle, pData, writeBuffer.Length);
+ if (result < 0)
+ {
+ var le = (LinuxErrorCode)Marshal.GetLastWin32Error();
+ switch (le)
+ {
+ case LinuxErrorCode.IOError:
+ throw new NativeException("I/O Error. Check your wiring");
+ default:
+ var msg = $"WRITE ERROR: {le}";
+ throw new NativeException(msg);
+ }
+ }
+ }
+ }
+
+ public unsafe void Exchange(byte peripheralAddress, Span writeBuffer, Span readBuffer)
+ {
+ var handle = InfoMap.GetAddressHandle(BusNumber, peripheralAddress);
+ fixed (byte* pWrite = writeBuffer)
+ fixed (byte* pRead = readBuffer)
+ {
+ var result = Interop.write(handle, pWrite, writeBuffer.Length);
+ if (result < 0)
+ {
+ var le = (LinuxErrorCode)Marshal.GetLastWin32Error();
+ switch (le)
+ {
+ case LinuxErrorCode.IOError:
+ throw new NativeException("I/O Error. Check your wiring");
+ default:
+ var msg = $"WRITE ERROR: {le}";
+ throw new NativeException(msg);
+ }
+ }
+ result = Interop.read(handle, pRead, readBuffer.Length);
+ if (result < 0)
+ {
+ var le = (LinuxErrorCode)Marshal.GetLastWin32Error();
+ switch (le)
+ {
+ case LinuxErrorCode.IOError:
+ throw new NativeException("I/O Error. Check your wiring");
+ default:
+ var msg = $"READ ERROR: {le}";
+ throw new NativeException(msg);
+ }
+ }
+ }
+ }
+
+ public void WriteData(byte peripheralAddress, params byte[] data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WriteData(byte peripheralAddress, byte[] data, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WriteData(byte peripheralAddress, IEnumerable data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte[] WriteReadData(byte peripheralAddress, int byteCountToRead, params byte[] dataToWrite)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte[] ReadData(byte peripheralAddress, int numberOfBytes)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Communications/I2CPeripheralInfo.cs b/source/implementations/linux/Meadow.Linux/Hardware/Communications/I2CPeripheralInfo.cs
new file mode 100644
index 00000000..c21c1ed8
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Communications/I2CPeripheralInfo.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace Meadow
+{
+ public partial class I2CBus
+ {
+ private class PeripheralMap : IDisposable
+ {
+ // the I2C block driver is...interesting. You open it multiple times, once per peripheral address per bus
+ // this is a container of bus, address, info
+ private Dictionary _infoMap = new Dictionary();
+ private bool _disposedValue;
+ private string _driverName;
+
+ public PeripheralMap(int busNumber)
+ {
+ _driverName = $"/dev/i2c-{busNumber}";
+ }
+
+ public int GetAddressHandle(int busNumber, byte busAddress)
+ {
+ I2CPeripheralInfo info;
+
+ if (_infoMap.ContainsKey(busAddress))
+ {
+ info = _infoMap[busAddress];
+ }
+ else
+ {
+ // open the i2c block driver
+ var handle = Interop.open(_driverName, Interop.DriverFlags.O_RDWR);
+ if (handle < 0)
+ {
+ // TODO: maybe try modprobe here?
+ throw new Exception($"Unable to open driver {_driverName}. Is it enabled on your platform?");
+ }
+ info = new I2CPeripheralInfo
+ {
+ DriverHandle = handle,
+ BusAddress = busAddress
+ };
+ _infoMap.Add(busAddress, info);
+ }
+
+ if (!info.IsOpen)
+ {
+ // call the ioctl to set the address for this handle
+ var result = Interop.ioctl(info.DriverHandle, (int)I2CIoctl.SLAVE, info.BusAddress);
+ if (result < 0)
+ {
+ Console.WriteLine($"ERROR: {Marshal.GetLastWin32Error()}");
+ }
+ }
+
+ return info.DriverHandle;
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ foreach (var address in _infoMap.Values)
+ {
+ Interop.close(address.DriverHandle);
+ }
+ }
+
+ _disposedValue = true;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ private class I2CPeripheralInfo
+ {
+ public byte BusAddress { get; set; }
+ public int DriverHandle { get; set; }
+ public bool IsOpen { get; set; }
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Communications/KrtklI2CBus.cs b/source/implementations/linux/Meadow.Linux/Hardware/Communications/KrtklI2CBus.cs
new file mode 100644
index 00000000..843e0c33
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Communications/KrtklI2CBus.cs
@@ -0,0 +1,62 @@
+using Meadow.Hardware;
+using System;
+using System.Collections.Generic;
+
+namespace Meadow
+{
+ public class KrtklI2CBus : II2cBus, IDisposable
+ {
+ public I2cBusSpeed BusSpeed { get; set; }
+
+
+ public KrtklI2CBus(I2cBusSpeed busSpeed)
+ {
+ BusSpeed = busSpeed;
+ }
+
+ public void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Exchange(byte peripheralAddress, Span writeBuffer, Span readBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Read(byte peripheralAddress, Span readBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte[] ReadData(byte peripheralAddress, int numberOfBytes)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Write(byte peripheralAddress, Span writeBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WriteData(byte peripheralAddress, params byte[] data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WriteData(byte peripheralAddress, byte[] data, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WriteData(byte peripheralAddress, IEnumerable data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte[] WriteReadData(byte peripheralAddress, int byteCountToRead, params byte[] dataToWrite)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Communications/LinuxSerialPort.cs b/source/implementations/linux/Meadow.Linux/Hardware/Communications/LinuxSerialPort.cs
new file mode 100644
index 00000000..662a4bda
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Communications/LinuxSerialPort.cs
@@ -0,0 +1,147 @@
+using Meadow.Devices;
+using Meadow.Hardware;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Meadow
+{
+ public class LinuxSerialPort : SerialPortBase
+ {
+ public LinuxSerialPort(
+ SerialPortName portName,
+ int baudRate,
+ int dataBits = 8,
+ Parity parity = Parity.None,
+ StopBits stopBits = StopBits.One,
+ int readBufferSize = 4096)
+ : base(portName, baudRate, dataBits, parity, stopBits, readBufferSize)
+ {
+ }
+
+ protected override void CloseHardwarePort(IntPtr handle)
+ {
+ Interop.close(handle.ToInt32());
+ }
+
+ protected override IntPtr OpenHardwarePort(string portName)
+ {
+ string driverName;
+
+ if(portName.StartsWith("/"))
+ {
+ driverName = portName;
+ }
+ else
+ {
+ driverName = $"/dev/{portName}";
+ }
+
+ Resolver.Log.Trace($"Opening linux serial port {driverName}");
+
+ var handle = Interop.open(driverName, Interop.DriverFlags.O_RDWR | Interop.DriverFlags.O_NOCTTY);
+
+ if(handle < 0)
+ {
+ var err = Marshal.GetLastWin32Error();
+ switch(err)
+ {
+ case 13:
+ throw new NativeException($"Unable to open port '{driverName}'. Permission Denied ({err})");
+ default:
+ throw new NativeException($"Unable to open port '{driverName}'. Native error {err}");
+ }
+ }
+
+ return new IntPtr(handle);
+ }
+
+ protected override int ReadHardwarePort(IntPtr handle, byte[] readBuffer, int count)
+ {
+ var result = Interop.read(handle.ToInt32(), readBuffer, count);
+
+ if(result < 0)
+ {
+ // will we potentially get E_TRY_AGAIN? (11)
+ throw new NativeException($"Unable to read from port. Native error {Marshal.GetLastWin32Error()}");
+ }
+
+ return result;
+ }
+
+ protected override void SetHardwarePortSettings(IntPtr handle)
+ {
+ // get the current settings
+ var settings = new Interop.termios();
+ Interop.tcgetattr(handle.ToInt32(), ref settings);
+
+
+ // clear stuff that should be off
+ settings.c_iflag &= ~(Interop.InputFlags.IGNBRK | Interop.InputFlags.BRKINT | Interop.InputFlags.PARMRK | Interop.InputFlags.ISTRIP | Interop.InputFlags.INLCR | Interop.InputFlags.IGNCR | Interop.InputFlags.ICRNL | Interop.InputFlags.IXON);
+ settings.c_oflag &= ~Interop.OutputFlags.OPOST;
+ settings.c_lflag &= ~(Interop.LocalFlags.ECHO | Interop.LocalFlags.ECHONL | Interop.LocalFlags.ICANON | Interop.LocalFlags.ISIG | Interop.LocalFlags.IEXTEN);
+ settings.c_cflag &= ~(Interop.ControlFlags.CSIZE | Interop.ControlFlags.PARENB | Interop.ControlFlags.CRTSCTS);
+
+ // now set the user-requested settings
+ switch(DataBits)
+ {
+ case 5:
+ settings.c_cflag |= Interop.ControlFlags.CS5;
+ break;
+ case 6:
+ settings.c_cflag |= Interop.ControlFlags.CS6;
+ break;
+ case 7:
+ settings.c_cflag |= Interop.ControlFlags.CS7;
+ break;
+ case 8:
+ settings.c_cflag |= Interop.ControlFlags.CS8;
+ break;
+ }
+
+ settings.c_cflag |= Interop.ControlFlags.CREAD | Interop.ControlFlags.CLOCAL;
+
+ switch(Parity)
+ {
+ case Parity.Odd:
+ settings.c_cflag |= (Interop.ControlFlags.PARENB | Interop.ControlFlags.PARODD);
+ break;
+ case Parity.Even:
+ settings.c_cflag |= Interop.ControlFlags.PARENB;
+ break;
+ }
+
+ switch(StopBits)
+ {
+ case StopBits.Two:
+ settings.c_cflag |= Interop.ControlFlags.CSTOPB;
+ break;
+ }
+
+ Output.WriteLineIf(_showSerialDebug, $" Setting port settings at {BaudRate}...");
+ var result = Interop.cfsetspeed(ref settings, BaudRate);
+ if(result != 0)
+ {
+ throw new NativeException($"cfsetspeed failed: {Marshal.GetLastWin32Error()}");
+ }
+
+ result = Interop.tcsetattr(handle.ToInt32(), Interop.TCSANOW, ref settings);
+
+ if(result != 0)
+ {
+ throw new NativeException($"tcsetattr failed: {Marshal.GetLastWin32Error()}");
+ }
+ }
+
+ protected override int WriteHardwarePort(IntPtr handle, byte[] writeBuffer, int count)
+ {
+ var result = Interop.write(handle.ToInt32(), writeBuffer, count);
+
+ if(result < 0)
+ {
+ throw new NativeException($"Unable to write to port. Native error {Marshal.GetLastWin32Error()}");
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Communications/SpiBus.cs b/source/implementations/linux/Meadow.Linux/Hardware/Communications/SpiBus.cs
new file mode 100644
index 00000000..d971008a
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Communications/SpiBus.cs
@@ -0,0 +1,285 @@
+using Meadow.Devices;
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Meadow
+{
+ public partial class SpiBus : ISpiBus, IDisposable
+ {
+ private SemaphoreSlim _busSemaphore = new SemaphoreSlim(1, 1);
+
+ private const int TransferIoctl = 0;
+ private const int ModeIoctl = 1;
+ private const int JustificationIoctl = 2;
+ private const int LengthIoctl = 3;
+ private const int SpeedIoctl = 4;
+ private const int MAX_TX_BLOCK_SIZE_BYTES = 4096;
+
+ [Flags]
+ public enum SpiMode
+ {
+ CPHA = 0x01,
+ CPOL = 0x02,
+ Mode0 = 0 | 0,
+ Mode1 = 0 | CPHA,
+ Mode2 = CPOL | 0,
+ Mode3 = CPOL | CPHA
+ }
+
+ private int DriverHandle { get; set; }
+
+ public Frequency[] SupportedSpeeds
+ {
+ get => new Frequency[]
+ {
+ new Frequency(375, Frequency.UnitType.Kilohertz),
+ new Frequency(750, Frequency.UnitType.Kilohertz),
+ new Frequency(1500, Frequency.UnitType.Kilohertz),
+ new Frequency(3000, Frequency.UnitType.Kilohertz),
+ new Frequency(6000, Frequency.UnitType.Kilohertz),
+ new Frequency(12000, Frequency.UnitType.Kilohertz),
+ new Frequency(24000, Frequency.UnitType.Kilohertz),
+ new Frequency(48000, Frequency.UnitType.Kilohertz),
+ };
+ }
+
+ public SpiClockConfiguration Configuration { get; private set; }
+
+ public unsafe SpiBus(int chipSelect, SpiClockConfiguration.ClockPhase phase, SpiClockConfiguration.ClockPolarity polarity, Units.Frequency speed)
+ : this(chipSelect, (SpiMode)((int)phase | (int)polarity), speed)
+ {
+ }
+
+ ///
+ ///
+ ///
+ /// Must be 0 or 1
+ public unsafe SpiBus(int chipSelect, SpiMode mode, Units.Frequency speed)
+ {
+ var busNumber = 0;
+
+ var driver = $"/dev/spidev{busNumber}.{chipSelect}";
+ DriverHandle = Interop.open(driver, Interop.DriverFlags.O_RDWR);
+ if (DriverHandle < 0)
+ {
+ // TODO: maybe try modprobe here?
+ throw new NativeException($"Unable to open driver {driver}. Check your user permissions", Marshal.GetLastWin32Error());
+ }
+
+ Mode = mode;
+ BitsPerWord = 8;
+ SpeedHz = Convert.ToInt32(speed.Hertz);
+
+ Configuration = new SpiClockConfiguration(speed)
+ {
+ Phase = SpiClockConfiguration.ClockPhase.Zero,
+ Polarity = SpiClockConfiguration.ClockPolarity.Normal
+ };
+ }
+
+ public unsafe int SpeedHz
+ {
+ set
+ {
+ var status = Interop.ioctl(DriverHandle, Interop._IOW('k', SpeedIoctl, 4), (byte*)&value);
+ if (status < 0)
+ {
+ throw new NativeException($"Could not set SPI speed (WR): {status}", Marshal.GetLastWin32Error());
+ }
+ }
+ get
+ {
+ int speed = 0;
+ var status = Interop.ioctl(DriverHandle, Interop._IOR('k', SpeedIoctl, 4), (byte*)&speed);
+ if (status < 0)
+ {
+ throw new NativeException($"Could not get SPI speed (RD): {status}", Marshal.GetLastWin32Error());
+ }
+ return speed;
+ }
+ }
+
+ public unsafe SpiMode Mode
+ {
+ set
+ {
+ var status = Interop.ioctl(DriverHandle, Interop._IOW('k', ModeIoctl, 1), (byte*)&value);
+ if (status < 0)
+ {
+ throw new NativeException($"Could not set SPIMode (WR): {status}", Marshal.GetLastWin32Error());
+ }
+ }
+ get
+ {
+ byte value = 0;
+ var status = Interop.ioctl(DriverHandle, Interop._IOR('k', ModeIoctl, 1), (byte*)&value);
+ if (status < 0)
+ {
+ throw new NativeException($"Could not get SPIMode (RD): {status}", Marshal.GetLastWin32Error());
+ }
+ return (SpiMode)value;
+ }
+ }
+
+ public unsafe byte BitsPerWord
+ {
+ set
+ {
+ var status = Interop.ioctl(DriverHandle, Interop._IOW('k', LengthIoctl, 1), &value);
+ if (status < 0)
+ {
+ throw new NativeException($"Could not set SPI bits per word (WR): {status}", Marshal.GetLastWin32Error());
+ }
+ }
+ get
+ {
+ byte value = 0;
+ var status = Interop.ioctl(DriverHandle, Interop._IOR('k', LengthIoctl, 1), &value);
+ if (status < 0)
+ {
+ throw new NativeException($"Could not get SPI bits per word (RD): {status}", Marshal.GetLastWin32Error());
+ }
+ return value;
+ }
+ }
+
+ public void Dispose()
+ {
+ if (DriverHandle > 0)
+ {
+ Interop.close(DriverHandle);
+ DriverHandle = 0;
+ }
+ }
+
+ public void Read(IDigitalOutputPort? chipSelect, Span readBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
+ {
+ byte[] writeBuffer = new byte[readBuffer.Length];
+ Exchange(chipSelect, writeBuffer, readBuffer, csMode);
+ }
+
+ public void Write(IDigitalOutputPort? chipSelect, Span writeBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
+ {
+ byte[] readBuffer = new byte[writeBuffer.Length];
+ Exchange(chipSelect, writeBuffer, readBuffer, csMode);
+ }
+
+ private void DecipherSPIError(int status, int errorCode)
+ {
+ switch (errorCode)
+ {
+ case 22:
+ throw new NativeException($"Communication error. {errorCode} (Invalid Argument)");
+ case 90:
+ throw new NativeException($"Communication error. {errorCode} (Message too long)");
+ default:
+ throw new NativeException($"Communication error. Return {status} Error code {errorCode}");
+ }
+ }
+
+ public unsafe void Exchange(IDigitalOutputPort? chipSelect, Span writeBuffer, Span readBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
+ {
+ if (writeBuffer == null) throw new ArgumentNullException("A non-null sendBuffer is required");
+ if (readBuffer == null) throw new ArgumentNullException("A non-null receiveBuffer is required");
+ if (writeBuffer.Length != readBuffer.Length) throw new Exception("Both buffers must be equal size");
+
+ _busSemaphore.Wait();
+
+ try
+ {
+ if (chipSelect != null)
+ {
+ // activate the chip select
+ chipSelect.State = csMode == ChipSelectMode.ActiveLow ? false : true;
+ }
+
+ fixed (byte* pWrite = writeBuffer)
+ fixed (byte* pRead = readBuffer)
+ {
+ byte* p = pWrite;
+
+ // each write can't be bigger than MAX_TX_BLOCK_SIZE_BYTES
+ var offset = 0;
+ while (offset < writeBuffer.Length)
+ {
+ var length = (writeBuffer.Length - offset) > MAX_TX_BLOCK_SIZE_BYTES ? MAX_TX_BLOCK_SIZE_BYTES : (writeBuffer.Length - offset);
+
+ var command = new SpiTransferCommand()
+ {
+ Length = length,
+ TransmitBuffer = (ulong)p, // NOTE: these are always 64-bit for OS compatibility
+ ReceiveBuffer = (ulong)pRead,
+ DelayuSec = 0,
+ BitsPerWord = 0,
+ SpeedHz = 0,
+ ChipSelectChange = 0
+ };
+
+ p += length;
+ offset += length;
+
+ var status = Interop.ioctl(DriverHandle, Interop._IOW('k', TransferIoctl, 32), (byte*)&command);
+ if (status < 0)
+ {
+ DecipherSPIError(status, Marshal.GetLastWin32Error());
+ }
+ }
+
+ if (chipSelect != null)
+ {
+ // deactivate the chip select
+ chipSelect.State = csMode == ChipSelectMode.ActiveLow ? true : false;
+ }
+ }
+ }
+ finally
+ {
+ _busSemaphore.Release();
+ }
+ }
+
+ public void SendData(IDigitalOutputPort chipSelect, params byte[] data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SendData(IDigitalOutputPort chipSelect, IEnumerable data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte[] ReceiveData(IDigitalOutputPort chipSelect, int numberOfBytes)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SendData(IDigitalOutputPort chipSelect, ChipSelectMode csMode, params byte[] data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SendData(IDigitalOutputPort chipSelect, ChipSelectMode csMode, IEnumerable data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte[] ReceiveData(IDigitalOutputPort chipSelect, ChipSelectMode csMode, int numberOfBytes)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void ExchangeData(IDigitalOutputPort chipSelect, ChipSelectMode csMode, byte[] sendBuffer, byte[] receiveBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void ExchangeData(IDigitalOutputPort chipSelect, ChipSelectMode csMode, byte[] sendBuffer, byte[] receiveBuffer, int bytesToExchange)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Communications/SpiTransferCommand.cs b/source/implementations/linux/Meadow.Linux/Hardware/Communications/SpiTransferCommand.cs
new file mode 100644
index 00000000..4318edde
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Communications/SpiTransferCommand.cs
@@ -0,0 +1,35 @@
+using System.Runtime.InteropServices;
+
+namespace Meadow
+{
+ public partial class SpiBus
+ {
+ [StructLayout(LayoutKind.Auto, Pack = 1)]
+ private struct SpiTransferCommand
+ {
+ /*
+ struct spi_ioc_transfer {
+ __u64 tx_buf;
+ __u64 rx_buf;
+
+ __u32 len;
+ __u32 speed_hz;
+
+ __u16 delay_usecs;
+ __u8 bits_per_word;
+ __u8 cs_change;
+ __u32 pad;
+ };
+ */
+
+ public ulong TransmitBuffer { get; set; } // 64-bit pointer to byte array
+ public ulong ReceiveBuffer { get; set; } // 64-bit pointer to byte array
+ public int Length { get; set; }
+ public int SpeedHz { get; set; }
+ public short DelayuSec { get; set; }
+ public byte BitsPerWord { get; set; }
+ public byte ChipSelectChange { get; set; }
+ public int Pad { get; set; }
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/JetsonNano.cs b/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/JetsonNano.cs
new file mode 100644
index 00000000..83371d9e
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/JetsonNano.cs
@@ -0,0 +1,54 @@
+using Meadow.Hardware;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Meadow.Pinouts
+{
+ public class JetsonNano : IPinDefinitions
+ {
+ public IEnumerator GetEnumerator() => AllPins.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ public IList AllPins => new List {
+ I2C_2_SDA, I2C_2_SCL,
+ UART_2_TX, UART_2_RX,
+ I2C_1_SDA, I2C_1_SCL,
+ };
+
+ public IPin I2C_2_SDA => new Pin("I2C_2_SDA", "PIN03", null);
+ public IPin I2C_2_SCL => new Pin("I2C_2_SCL", "PIN05", null);
+
+ public IPin D04 => new SysFsPin("D04", "PIN07", 216);
+
+ public IPin UART_2_TX => new Pin("UART_2_TX", "PIN08", null);
+ public IPin UART_2_RX => new Pin("UART_2_RX", "PIN10", null);
+
+ public IPin D17 => new SysFsPin("D17", "PIN11", 50);
+ public IPin D18 => new SysFsPin("D18", "PIN12", 79);
+
+ public IPin D27 => new SysFsPin("D27", "PIN13", 14);
+ public IPin D22 => new SysFsPin("D22", "PIN15", 194);
+ public IPin D23 => new SysFsPin("D23", "PIN16", 232);
+ public IPin D24 => new SysFsPin("D24", "PIN18", 15);
+ public IPin D10 => new SysFsPin("D10", "PIN19", 16);
+ public IPin D09 => new SysFsPin("D09", "PIN21", 17);
+ public IPin D25 => new SysFsPin("D25", "PIN22", 13);
+ public IPin D11 => new SysFsPin("D11", "PIN23", 18);
+ public IPin D08 => new SysFsPin("D08", "PIN24", 19);
+ public IPin D07 => new SysFsPin("D07", "PIN26", 20);
+
+ public IPin I2C_1_SDA => new Pin("I2C_1_SDA", "PIN27", null);
+ public IPin I2C_1_SCL => new Pin("I2C_1_SCL", "PIN28", null);
+
+ public IPin D05 => new SysFsPin("D05", "PIN29", 149);
+ public IPin D06 => new SysFsPin("D06", "PIN31", 200);
+ public IPin D12 => new SysFsPin("D12", "PIN32", 168);
+ public IPin D13 => new SysFsPin("D13", "PIN33", 38);
+ public IPin D19 => new SysFsPin("D19", "PIN35", 76);
+ public IPin D16 => new SysFsPin("D16", "PIN36", 51);
+ public IPin D26 => new SysFsPin("D26", "PIN37", 12);
+ public IPin D20 => new SysFsPin("D20", "PIN38", 77);
+ public IPin D21 => new SysFsPin("D21", "PIN40", 78);
+
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/JetsonXavierAGX.cs b/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/JetsonXavierAGX.cs
new file mode 100644
index 00000000..67f5a8cc
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/JetsonXavierAGX.cs
@@ -0,0 +1,90 @@
+using Meadow.Hardware;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Meadow.Pinouts
+{
+ public class JetsonXavierAGX : IPinDefinitions
+ {
+ public IEnumerator GetEnumerator() => AllPins.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ public IList AllPins => new List {
+ I2C_GP5_DAT,
+ I2C_GP5_CLK,
+ MCLK05,
+ UART1_RTS,
+ I2S2_CLK,
+ PWM01,
+ GPIO27_PWM2,
+ GPIO8_AO_DMIC_IN_DAT,
+ GPIO35_PWM3,
+ SPI1_MOSI,
+ SPI1_MISO,
+ GPIO17_40HEADER,
+ SPI1_SCLK,
+ SPI1_CS0,
+ SPI1_CS1,
+ CAN0_DIN,
+ CAN0_DOUT,
+ GPIO9_CAN1_GPIO0_DMIC_CLK,
+ CAN1_DOUT,
+ I2S_FS,
+ UART1_CTS,
+ CAN1_DIN,
+ I2S_SDIN,
+ I2S_SDOUT
+ };
+
+ public IPin I2C_GP5_DAT => new Pin("I2C_GP5_DAT", "PIN03", null);
+ public IPin I2C_GP5_CLK => new Pin("I2C_GP5_CLK", "PIN05", null);
+ public IPin MCLK05 => new SysFsPin("MCLK05", "PIN07", 422);
+ public IPin UART1_RTS => new SysFsPin("UART1_RTS", "PIN11", 428);
+ public IPin I2S2_CLK => new SysFsPin("I2S2_CLK", "PIN12", 351);
+ public IPin PWM01 => new SysFsPin("PWM01", "PIN13", 424);
+ public IPin GPIO27_PWM2 => new SysFsPin("GPIO27_PWM2", "PIN15", 393);
+ public IPin GPIO8_AO_DMIC_IN_DAT => new SysFsPin("GPIO8_AO_DMIC_IN_DAT", "PIN16", 256);
+ public IPin GPIO35_PWM3 => new SysFsPin("GPIO35_PWM3", "PIN18", 344);
+ public IPin SPI1_MOSI => new SysFsPin("SPI1_MOSI", "PIN19", 493);
+ public IPin SPI1_MISO => new SysFsPin("SPI1_MISO", "PIN21", 492);
+ public IPin GPIO17_40HEADER => new SysFsPin("GPIO17_40HEADER", "PIN22", 417);
+ public IPin SPI1_SCLK => new SysFsPin("SPI1_SCLK", "PIN23", 491);
+ public IPin SPI1_CS0 => new SysFsPin("SPI1_CS0", "PIN24", 494);
+ public IPin SPI1_CS1 => new SysFsPin("SPI1_CS1", "PIN26", 495);
+ public IPin I2C_GP2_DAT => new Pin("I2C_GP2_DAT", "PIN27", null);
+ public IPin I2C_GP2_CLK => new Pin("I2C_GP2_CLK", "PIN28", null);
+ public IPin CAN0_DIN => new SysFsPin("CAN0_DIN", "PIN29", 251);
+ public IPin CAN0_DOUT => new SysFsPin("CAN0_DOUT", "PIN31", 250);
+ public IPin GPIO9_CAN1_GPIO0_DMIC_CLK => new SysFsPin("GPIO9_CAN1_GPIO0_DMIC_CLK", "PIN32", 257);
+ public IPin CAN1_DOUT => new SysFsPin("CAN1_DOUT", "PIN33", 248);
+ public IPin I2S_FS => new SysFsPin("I2S_FS", "PIN35", 354);
+ public IPin UART1_CTS => new SysFsPin("UART1_CTS", "PIN36", 429);
+ public IPin CAN1_DIN => new SysFsPin("CAN1_DIN", "PIN37", 249);
+ public IPin I2S_SDIN => new SysFsPin("I2S_SDIN", "PIN38", 353);
+ public IPin I2S_SDOUT => new SysFsPin("I2S_SDOUT", "PIN40", 352);
+
+ // aliases for sanity
+ public IPin Pin7 => MCLK05;
+ public IPin Pin11 => UART1_RTS;
+ public IPin Pin12 => I2S2_CLK;
+ public IPin Pin13 => PWM01;
+ public IPin Pin15 => GPIO27_PWM2;
+ public IPin Pin16 => GPIO8_AO_DMIC_IN_DAT;
+ public IPin Pin18 => GPIO35_PWM3;
+ public IPin Pin19 => SPI1_MOSI;
+ public IPin Pin21 => SPI1_MISO;
+ public IPin Pin22 => GPIO17_40HEADER;
+ public IPin Pin23 => SPI1_SCLK;
+ public IPin Pin24 => SPI1_CS0;
+ public IPin Pin26 => SPI1_CS1;
+ public IPin Pin29 => CAN0_DIN;
+ public IPin Pin31 => CAN0_DOUT;
+ public IPin Pin32 => GPIO9_CAN1_GPIO0_DMIC_CLK;
+ public IPin Pin33 => CAN1_DOUT;
+ public IPin Pin35 => I2S_FS;
+ public IPin Pin36 => UART1_CTS;
+ public IPin Pin37 => CAN1_DIN;
+ public IPin Pin38 => I2S_SDIN;
+ public IPin Pin40 => I2S_SDOUT;
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/RaspberryPi.cs b/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/RaspberryPi.cs
new file mode 100644
index 00000000..2e9f9d30
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/RaspberryPi.cs
@@ -0,0 +1,87 @@
+using Meadow.Hardware;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Meadow.Pinouts
+{
+ public class RaspberryPi : IPinDefinitions
+ {
+ public IEnumerator GetEnumerator() => AllPins.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ public IList AllPins => new List
+ {
+ GPIO2, GPIO3, GPIO4, GPIO17, GPIO18, GPIO27, GPIO22, GPIO23,
+ GPIO24, GPIO10, GPIO9, GPIO25, GPIO11, GPIO8, GPIO7, GPIO5,
+ GPIO6, GPIO12, GPIO13, GPIO19, GPIO16, GPIO26, GPIO20, GPIO21
+ };
+
+ public IPin GPIO2 => new LinuxFlexiPin("GPIO2", "PIN03", 2, "gpiochip0", 2);
+ public IPin GPIO3 => new LinuxFlexiPin("GPIO3", "PIN05", 3, "gpiochip0", 3);
+ public IPin GPIO4 => new LinuxFlexiPin("GPIO4", "PIN07", 4, "gpiochip0", 4);
+ public IPin GPIO17 => new LinuxFlexiPin("GPIO17", "PIN11", 17, "gpiochip0", 17);
+ public IPin GPIO18 => new LinuxFlexiPin("GPIO18", "PIN12", 18, "gpiochip0", 18);
+ public IPin GPIO27 => new LinuxFlexiPin("GPIO27", "PIN13", 27, "gpiochip0", 27);
+ public IPin GPIO22 => new LinuxFlexiPin("GPIO22", "PIN15", 22, "gpiochip0", 22);
+
+ // Pi may or may not support GPIOD - depends on OS
+ public IPin GPIO23 => new LinuxFlexiPin("GPIO23", "PIN16", 23, "gpiochip0", 23);
+ public IPin GPIO24 => new LinuxFlexiPin("GPIO24", "PIN18", 24, "gpiochip0", 24);
+
+ public IPin GPIO10 => new LinuxFlexiPin("GPIO10", "PIN19", 10, "gpiochip0", 10);
+ public IPin GPIO9 => new LinuxFlexiPin("GPIO9", "PIN21", 9, "gpiochip0", 9);
+ public IPin GPIO25 => new LinuxFlexiPin("GPIO25", "PIN22", 25, "gpiochip0", 25);
+ public IPin GPIO11 => new LinuxFlexiPin("GPIO11", "PIN23", 11, "gpiochip0", 11);
+ public IPin GPIO8 => new LinuxFlexiPin("GPIO8", "PIN24", 8, "gpiochip0", 8);
+ public IPin GPIO7 => new LinuxFlexiPin("GPIO7", "PIN26", 7, "gpiochip0", 7);
+ public IPin GPIO5 => new LinuxFlexiPin("GPIO5", "PIN29", 5, "gpiochip0", 5);
+ public IPin GPIO6 => new LinuxFlexiPin("GPIO6", "PIN31", 6, "gpiochip0", 6);
+ public IPin GPIO12 => new LinuxFlexiPin("GPIO12", "PIN32", 12, "gpiochip0", 12);
+ public IPin GPIO13 => new LinuxFlexiPin("GPIO13", "PIN33", 13, "gpiochip0", 13);
+ public IPin GPIO19 => new LinuxFlexiPin("GPIO19", "PIN35", 19, "gpiochip0", 19);
+ public IPin GPIO16 => new LinuxFlexiPin("GPIO16", "PIN36", 16, "gpiochip0", 16);
+ public IPin GPIO26 => new LinuxFlexiPin("GPIO26", "PIN37", 26, "gpiochip0", 26);
+ public IPin GPIO20 => new LinuxFlexiPin("GPIO20", "PIN38", 20, "gpiochip0", 20);
+ public IPin GPIO21 => new LinuxFlexiPin("GPIO21", "PIN40", 21, "gpiochip0", 21);
+
+ // aliases for sanity
+ public IPin Pin3 => GPIO2;
+ public IPin Pin5 => GPIO3;
+ public IPin Pin7 => GPIO4;
+ public IPin Pin11 => GPIO17;
+ public IPin Pin12 => GPIO18;
+ public IPin Pin13 => GPIO27;
+ public IPin Pin15 => GPIO22;
+ public IPin Pin16 => GPIO23;
+ public IPin Pin18 => GPIO24;
+ public IPin Pin19 => GPIO10;
+ public IPin Pin21 => GPIO9;
+ public IPin Pin22 => GPIO25;
+ public IPin Pin23 => GPIO11;
+ public IPin Pin24 => GPIO8;
+ public IPin Pin26 => GPIO7;
+ public IPin Pin29 => GPIO5;
+ public IPin Pin31 => GPIO6;
+ public IPin Pin32 => GPIO12;
+ public IPin Pin33 => GPIO13;
+ public IPin Pin35 => GPIO19;
+ public IPin Pin36 => GPIO16;
+ public IPin Pin37 => GPIO26;
+ public IPin Pin38 => GPIO20;
+ public IPin Pin40 => GPIO21;
+
+ public IPin I2C1_SDA => GPIO2;
+ public IPin I2C1_SCL => GPIO3;
+
+ public IPin SPI0_MOSI => GPIO10;
+ public IPin SPI0_MISO => GPIO9;
+ public IPin SPI0_SCLK => GPIO11;
+ public IPin SPI0_CS0 => GPIO8;
+ public IPin SPI0_CS1 => GPIO7;
+
+ public IPin SPI1_MOSI => GPIO20;
+ public IPin SPI1_MISO => GPIO19;
+ public IPin SPI1_SCLK => GPIO21;
+ public IPin SPI1_CS0 => GPIO16;
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/SnickerdoodleBlack.cs b/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/SnickerdoodleBlack.cs
new file mode 100644
index 00000000..d0526797
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Hardware/Pinouts/SnickerdoodleBlack.cs
@@ -0,0 +1,30 @@
+using Meadow.Hardware;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Meadow.Pinouts
+{
+ public class SnickerdoodleBlack : IPinDefinitions
+ {
+ public IEnumerator GetEnumerator() => AllPins.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ public IList AllPins => new List
+ {
+ GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8
+// GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9
+ };
+
+// public IPin GPIO0 => new GpiodPin("GPIO0", "PIN001", "gpiochip0", 0);
+ public IPin GPIO1 => new GpiodPin("GPIO1", "PIN001", "gpiochip0", 1);
+ public IPin GPIO2 => new GpiodPin("GPIO2", "PIN001", "gpiochip0", 2);
+ public IPin GPIO3 => new GpiodPin("GPIO3", "PIN001", "gpiochip0", 3);
+ public IPin GPIO4 => new GpiodPin("GPIO4", "PIN001", "gpiochip0", 4);
+ public IPin GPIO5 => new GpiodPin("GPIO5", "PIN001", "gpiochip0", 5);
+ public IPin GPIO6 => new GpiodPin("GPIO6", "PIN001", "gpiochip0", 6);
+ public IPin GPIO7 => new GpiodPin("GPIO7", "PIN001", "gpiochip0", 7);
+ public IPin GPIO8 => new GpiodPin("GPIO8", "PIN001", "gpiochip0", 8);
+// public IPin GPIO9 => new GpiodPin("GPIO9", "PIN001", "gpiochip0", 9);
+
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.ControlFlags.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.ControlFlags.cs
new file mode 100644
index 00000000..ff6006a2
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.ControlFlags.cs
@@ -0,0 +1,71 @@
+using System;
+
+namespace Meadow
+{
+ internal static partial class Interop
+ {
+ [Flags]
+ public enum ControlFlags : uint
+ {
+ CSIZE = 0x30,
+ CS5 = 0x00,
+ CS6 = 0x10,
+ CS7 = 0x20,
+ CS8 = 0x30,
+ PARENB = 0x100,
+ CREAD = 0x80,
+ CLOCAL = 0x800,
+ PARODD = 0x200,
+ CSTOPB = 0x40,
+
+ /* c_cflag bit meaning */
+ // **** THESE NUMBERS ARE IN OCTAL ****
+ /*
+ CBAUD = 0010017,
+ B0 = 00000000,
+ B50 = 00000001,
+ B75 = 00000002,
+ B110 = 00000003,
+ B134 = 00000004,
+ B150 = 00000005,
+ B200 = 00000006,
+ B300 = 00000007,
+ B600 = 00000010,
+ B1200 = 00000011,
+ B1800 = 00000012,
+ B2400 = 00000013,
+ B4800 = 00000014,
+ B9600 = 00000015,
+ B19200 = 00000016,
+ B38400 = 00000017,
+ EXTA = B19200,
+ EXTB = B38400,
+
+
+ HUPCL = 00002000,
+
+ CBAUDEX = 00010000,
+ BOTHER = 00010000,
+ B57600 = 00010001,
+ B115200 = 00010002,
+ B230400 = 00010003,
+ B460800 = 00010004,
+ B500000 = 00010005,
+ B576000 = 00010006,
+ B921600 = 00010007,
+ B1000000 = 00010010,
+ B1152000 = 00010011,
+ B1500000 = 00010012,
+ B2000000 = 00010013,
+ B2500000 = 00010014,
+ B3000000 = 00010015,
+ B3500000 = 00010016,
+ B4000000 = 00010017,
+ CIBAUD = 002003600000,
+ CMSPAR = 010000000000,
+ */
+ CRTSCTS = 0x80000000 /* flow control */
+
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.DriverFlags.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.DriverFlags.cs
new file mode 100644
index 00000000..31d5e34f
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.DriverFlags.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace Meadow
+{
+ internal static partial class Interop
+ {
+ [Flags]
+ public enum DriverFlags
+ {
+ //#define O_RDONLY 00
+ //#define O_WRONLY 01
+ //#define O_RDWR 02
+ //#define O_NONBLOCK 0x0004 /* no delay */
+ //#define O_FSYNC 0x0080 /* synchronous writes */
+ O_RDONLY = 0x0000,
+ O_WRONLY = 0x0001,
+ O_RDWR = 0x0002,
+ O_NONBLOCK = 0x0004,
+ O_FSYNC = 0x0080,
+ O_NOCTTY = 0x0400,
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.InputFlags.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.InputFlags.cs
new file mode 100644
index 00000000..ef8a4a55
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.InputFlags.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Meadow
+{
+ internal static partial class Interop
+ {
+ [Flags]
+ public enum InputFlags : int
+ {
+ /* c_iflag bits */
+ IGNBRK = 0x01,
+ BRKINT = 0x02,
+ IGNPAR = 0x04,
+ PARMRK = 0x08,
+ INPCK = 0x10,
+ ISTRIP = 0x20,
+ INLCR = 0x40,
+ IGNCR = 0x80,
+ ICRNL = 0x100,
+ IUCLC = 0x200,
+ IXON = 0x400,
+ IXANY = 0x800,
+ /*
+ IXOFF = 0010000,
+ IMAXBEL = 0020000,
+ IUTF8 = 0040000
+ */
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.LocalFlags.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.LocalFlags.cs
new file mode 100644
index 00000000..391f66f4
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.LocalFlags.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Meadow
+{
+ internal static partial class Interop
+ {
+ [Flags]
+ public enum LocalFlags : int
+ {
+ ISIG = 0x01,
+ ICANON = 0x02,
+ XCASE = 0x04,
+ ECHO = 0x08,
+ ECHOE = 0x10,
+ ECHOK = 0x20,
+ ECHONL = 0x40,
+ IEXTEN = 0x8000,
+
+ /* c_lflag bits */
+ // THESE VALUES ARE OCTAL -- IF YOU USE THEM, CONVERT **
+ /*
+ NOFLSH = 0000200,
+ TOSTOP = 0000400,
+ ECHOCTL = 0001000,
+ ECHOPRT = 0002000,
+ ECHOKE = 0004000,
+ FLUSHO = 0010000,
+ PENDIN = 0040000,
+ EXTPROC = 0200000
+ */
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.OutputFlags.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.OutputFlags.cs
new file mode 100644
index 00000000..159f0d50
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.OutputFlags.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Meadow
+{
+ internal static partial class Interop
+ {
+ [Flags]
+ public enum OutputFlags : int
+ {
+ OPOST = 0x01,
+ /* c_oflag bits */
+ // THESE VALUES ARE OCTAL -- IF YOU USE THEM, CONVERT **
+ /*
+ OLCUC = 0000002,
+ ONLCR = 0000004,
+ OCRNL = 0000010,
+ ONOCR = 0000020,
+ ONLRET = 0000040,
+ OFILL = 0000100,
+ OFDEL = 0000200,
+ NLDLY = 0000400,
+ NL0 = 0000000,
+ NL1 = 0000400,
+ CRDLY = 0003000,
+ CR0 = 0000000,
+ CR1 = 0001000,
+ CR2 = 0002000,
+ CR3 = 0003000,
+ TABDLY = 0014000,
+ TAB0 = 0000000,
+ TAB1 = 0004000,
+ TAB2 = 0010000,
+ TAB3 = 0014000,
+ XTABS = 0014000,
+ BSDLY = 0020000,
+ BS0 = 0000000,
+ BS1 = 0020000,
+ VTDLY = 0040000,
+ VT0 = 0000000,
+ VT1 = 0040000,
+ FFDLY = 0100000,
+ FF0 = 0000000,
+ FF1 = 0100000
+ */
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.cs
new file mode 100644
index 00000000..87b9f85d
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.cs
@@ -0,0 +1,61 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace Meadow
+{
+ [SuppressUnmanagedCodeSecurity]
+ internal static partial class Interop
+ {
+ public const string LIBC = "libc";
+
+ public const int TCSANOW = 0;
+ public const int TCSADRAIN = 1;
+ public const int TCSAFLUSH = 2;
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int open(string pathname, DriverFlags flags);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int close(int handle);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int write(int handle, byte[] buf, int count);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public unsafe static extern int write(int handle, byte* buf, int count);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int read(int handle, byte[] buf, int count);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public unsafe static extern int read(int handle, byte* buf, int count);
+
+ //int tcgetattr(int fildes, struct termios *termios_p);
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int tcgetattr(int fd, ref termios termios_p);
+
+ // int tcsetattr(int fildes, int optional_actions, const struct termios *termios_p);
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int tcsetattr(int fd, int optional_actions, ref termios termios_p);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int cfsetspeed(ref termios termiosp, int speed);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int lseek(int fd, int offset, SeekWhence whence);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int poll(pollfd[] fds, int nfds, int timeout);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int poll(ref pollfd fds, int nfds, int timeout);
+
+ public enum SeekWhence
+ {
+ SEEK_SET = 0, /* set file offset to offset */
+ SEEK_CUR = 1, /* set file offset to current plus offset */
+ SEEK_END = 2 /* set file offset to EOF plus offset */
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.ioctl.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.ioctl.cs
new file mode 100644
index 00000000..9a9eb828
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.ioctl.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace Meadow
+{
+ [SuppressUnmanagedCodeSecurity]
+ internal static partial class Interop
+ {
+ private const int _IOC_NRBITS = 8;
+ private const int _IOC_TYPEBITS = 8;
+
+ private const int _IOC_SIZEBITS = 14;
+ private const int _IOC_DIRBITS = 2;
+ private const int _IOC_NRMASK = ((1 << _IOC_NRBITS) - 1);
+ private const int _IOC_TYPEMASK = ((1 << _IOC_TYPEBITS) - 1);
+ private const int _IOC_SIZEMASK = ((1 << _IOC_SIZEBITS) - 1);
+ private const int _IOC_DIRMASK = ((1 << _IOC_DIRBITS) - 1);
+ private const int _IOC_NRSHIFT = 0;
+ private const int _IOC_TYPESHIFT = (_IOC_NRSHIFT + _IOC_NRBITS);
+ private const int _IOC_SIZESHIFT = (_IOC_TYPESHIFT + _IOC_TYPEBITS);
+ private const int _IOC_DIRSHIFT = (_IOC_SIZESHIFT + _IOC_SIZEBITS);
+ private const int _IOC_NONE = 0;
+ private const int _IOC_WRITE = 1;
+ private const int _IOC_READ = 2;
+
+ public static int _IOC(int dir, int type, int nr, int size)
+ {
+ return ((dir) << _IOC_DIRSHIFT) |
+ ((type) << _IOC_TYPESHIFT) |
+ ((nr) << _IOC_NRSHIFT) |
+ ((size) << _IOC_SIZESHIFT);
+ }
+
+ public static int _IO(int type, int nr, int size)
+ {
+ return _IOC(_IOC_NONE, type, nr, 0);
+ }
+
+ public static int _IOR(int type, int nr, int size)
+ {
+ return _IOC(_IOC_READ, type, nr, size);
+ }
+
+ public static int _IOW(int type, int nr, int size)
+ {
+ return _IOC(_IOC_WRITE, type, nr, size);
+ }
+
+ public static int _IOWR(int type, int nr, int size)
+ {
+ return _IOC(_IOC_READ | _IOC_WRITE, type, nr, size);
+ }
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int ioctl(int fd, int request, byte data);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public unsafe static extern int ioctl(int fd, int request, byte* data);
+
+ [DllImport(LIBC, SetLastError = true)]
+ public static extern int ioctl(int fd, int request, IntPtr pData);
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.pollfd.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.pollfd.cs
new file mode 100644
index 00000000..c2e991b9
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.pollfd.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Meadow
+{
+ internal static partial class Interop
+ {
+ [Flags]
+ public enum PollEvent : short
+ {
+ NONE = 0,
+ POLLIN = 0x0001,
+ POLLPRI = 0x0002,
+ POLLOUT = 0x0004,
+ POLLERR = 0x0008,
+ POLLHUP = 0x0010,
+ POLLNVAL = 0x0020
+ }
+
+ public struct pollfd
+ {
+ public int fd;
+ public PollEvent events;
+ public PollEvent revents;
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Interop/Interop.termios.cs b/source/implementations/linux/Meadow.Linux/Interop/Interop.termios.cs
new file mode 100644
index 00000000..0f9f304c
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Interop/Interop.termios.cs
@@ -0,0 +1,27 @@
+using System.Runtime.InteropServices;
+
+namespace Meadow
+{
+ internal static partial class Interop
+ {
+ [StructLayout(LayoutKind.Explicit)]
+ public struct termios
+ {
+ [FieldOffset(0)]
+ public InputFlags c_iflag;
+ [FieldOffset(4)]
+ public OutputFlags c_oflag;
+ [FieldOffset(8)]
+ public ControlFlags c_cflag;
+ [FieldOffset(12)]
+ public LocalFlags c_lflag;
+ [FieldOffset(16)]
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
+ public byte[] c_cc;
+ [FieldOffset(36)] // I *think* this is the right offset (20 bytes of control characters)
+ public int c_ispeed;
+ [FieldOffset(40)]
+ public int c_ospeed;
+ };
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Linux.cs b/source/implementations/linux/Meadow.Linux/Linux.cs
new file mode 100644
index 00000000..351fdd96
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Linux.cs
@@ -0,0 +1,301 @@
+using Meadow.Hardware;
+using Meadow.Pinouts;
+using Meadow.Units;
+using System;
+using System.Linq;
+
+namespace Meadow
+{
+ ///
+ /// Represents an instance of Meadow as a generic Linux process
+ ///
+ ///
+ public class Linux : IMeadowDevice
+ where TPinout : IPinDefinitions, new()
+ {
+ private SysFsGpioDriver _sysfs = null!;
+ private Gpiod _gpiod = null!;
+
+ public event PowerTransitionHandler BeforeReset;
+ public event PowerTransitionHandler BeforeSleep;
+ public event PowerTransitionHandler AfterWake;
+ public event NetworkConnectionHandler NetworkConnected;
+ public event NetworkDisconnectionHandler NetworkDisconnected;
+
+ public TPinout Pins { get; }
+ public DeviceCapabilities Capabilities { get; }
+ public IPlatformOS PlatformOS { get; }
+ public IDeviceInformation Information { get; }
+
+ public INetworkAdapterCollection NetworkAdapters => throw new NotImplementedException();
+
+ ///
+ /// Creates the Meadow on Linux infrastructure instance
+ ///
+ public Linux()
+ {
+ if (typeof(TPinout) == typeof(JetsonNano) || typeof(TPinout) == typeof(JetsonXavierAGX))
+ {
+ PlatformOS = new JetsonPlatformOS();
+ }
+ else
+ {
+ PlatformOS = new LinuxPlatformOS();
+ }
+
+ Information = new LinuxDeviceInfo();
+
+ Pins = new TPinout();
+ Capabilities = new DeviceCapabilities(
+ new AnalogCapabilities(false, null),
+ new NetworkCapabilities(false, true),
+ new StorageCapabilities(true)
+ );
+ }
+
+ public void Initialize()
+ {
+ _sysfs = new SysFsGpioDriver();
+
+ try
+ {
+ _gpiod = new Gpiod(Resolver.Log);
+ }
+ catch
+ {
+ Resolver.Log.Warn("Platform does not support gpiod");
+ }
+ }
+
+ public IPin GetPin(string pinName)
+ {
+ return Pins.AllPins.First(p => string.Compare(p.Name, pinName) == 0);
+ }
+
+ public II2cBus CreateI2cBus(int busNumber = 1)
+ {
+ return CreateI2cBus(busNumber, II2cController.DefaultI2cBusSpeed);
+ }
+
+ public II2cBus CreateI2cBus(int busNumber, I2cBusSpeed busSpeed)
+ {
+ return new I2CBus(busNumber, busSpeed);
+ }
+
+ public II2cBus CreateI2cBus(IPin[] pins, I2cBusSpeed busSpeed)
+ {
+ return CreateI2cBus(pins[0], pins[1], busSpeed);
+ }
+
+ public II2cBus CreateI2cBus(IPin clock, IPin data, I2cBusSpeed busSpeed)
+ {
+ // TODO: implement this based on channel caps (this is platform specific right now)
+
+ if (Pins is JetsonNano)
+ {
+ if (clock == Pins["PIN05"] && data == Pins["PIN03"])
+ {
+ return new I2CBus(1, busSpeed);
+ }
+ else if (clock == Pins["PIN28"] && data == Pins["PIN27"])
+ {
+ return new I2CBus(0, busSpeed);
+ }
+ }
+ if (Pins is JetsonXavierAGX)
+ {
+ if (clock == Pins["I2C_GP2_CLK"] && data == Pins["I2C_GP2_DAT"])
+ {
+ return new I2CBus(1, busSpeed);
+ }
+ else if (clock == Pins["I2C_GP5_CLK"] && data == Pins["I2C_GP5_DAT"])
+ {
+ return new I2CBus(8, busSpeed);
+ }
+ }
+ else if (Pins is RaspberryPi)
+ {
+ if (clock == Pins["PIN05"] && data == Pins["PIN03"])
+ {
+ return new I2CBus(1, busSpeed);
+ }
+ }
+ else if (Pins is SnickerdoodleBlack)
+ {
+ return new KrtklI2CBus(busSpeed);
+ }
+
+ throw new ArgumentOutOfRangeException("Requested pins are not I2C bus pins");
+ }
+
+ public ISerialMessagePort CreateSerialMessagePort(SerialPortName portName, byte[] suffixDelimiter, bool preserveDelimiter, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 512)
+ {
+ var classicPort = CreateSerialPort(portName, baudRate, dataBits, parity, stopBits, readBufferSize);
+ return SerialMessagePort.From(classicPort, suffixDelimiter, preserveDelimiter);
+ }
+
+ public ISerialMessagePort CreateSerialMessagePort(SerialPortName portName, byte[] prefixDelimiter, bool preserveDelimiter, int messageLength, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 512)
+ {
+ var classicPort = CreateSerialPort(portName, baudRate, dataBits, parity, stopBits, readBufferSize);
+ return SerialMessagePort.From(classicPort, prefixDelimiter, preserveDelimiter, messageLength);
+ }
+
+ public ISerialPort CreateSerialPort(SerialPortName portName, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 1024)
+ {
+ return new LinuxSerialPort(portName, baudRate, dataBits, parity, stopBits, readBufferSize);
+ }
+
+ public IDigitalOutputPort CreateDigitalOutputPort(IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull)
+ {
+ if (_gpiod != null)
+ {
+ return new GpiodDigitalOutputPort(_gpiod, pin, initialState);
+ }
+ else
+ {
+ return new SysFsDigitalOutputPort(_sysfs, pin, initialState);
+ }
+ }
+
+ public IDigitalInputPort CreateDigitalInputPort(IPin pin, InterruptMode interruptMode = InterruptMode.None, ResistorMode resistorMode = ResistorMode.Disabled)
+ {
+ return CreateDigitalInputPort(pin, interruptMode, resistorMode);
+ }
+
+ public IDigitalInputPort CreateDigitalInputPort(IPin pin, InterruptMode interruptMode, ResistorMode resistorMode, TimeSpan debounceDuration, TimeSpan glitchDuration)
+ {
+ if (_gpiod != null)
+ {
+ return new GpiodDigitalInputPort(_gpiod, pin, new GpiodDigitalChannelInfo(pin.Name), interruptMode, resistorMode, debounceDuration, glitchDuration);
+ }
+ else
+ {
+ return new SysFsDigitalInputPort(_sysfs, pin, new SysFsDigitalChannelInfo(pin.Name), interruptMode, resistorMode, debounceDuration, glitchDuration);
+ }
+ }
+
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, SpiClockConfiguration config)
+ {
+ return CreateSpiBus(clock, mosi, miso, config.SpiMode, config.Speed);
+ }
+
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, Units.Frequency speed)
+ {
+ return CreateSpiBus(clock, mosi, miso, SpiClockConfiguration.Mode.Mode0, speed);
+ }
+
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, SpiClockConfiguration.Mode mode, Units.Frequency speed)
+ {
+ return new SpiBus(0, (SpiBus.SpiMode)mode, speed);
+ }
+
+ // ----- BELOW HERE ARE NOT YET IMPLEMENTED -----
+
+ public IAnalogInputPort CreateAnalogInputPort(IPin pin, int sampleCount, TimeSpan sampleInterval, float voltageReference = 3.3F)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IAnalogInputPort CreateAnalogInputPort(IPin pin, int sampleCount, TimeSpan sampleInterval, Voltage voltageReference)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IBiDirectionalPort CreateBiDirectionalPort(IPin pin, bool initialState = false, InterruptMode interruptMode = InterruptMode.None, ResistorMode resistorMode = ResistorMode.Disabled, PortDirectionType initialDirection = PortDirectionType.Input, double debounceDuration = 0, double glitchDuration = 0, OutputType output = OutputType.PushPull)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPwmPort CreatePwmPort(IPin pin, Frequency frequency, float dutyCycle = 0.5F, bool invert = false)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnReset()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetClock(DateTime dateTime)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WatchdogEnable(TimeSpan timeout)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WatchdogReset()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnSleep()
+ {
+ throw new NotImplementedException();
+ }
+
+ public BatteryInfo GetBatteryInfo()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Temperature GetProcessorTemperature()
+ {
+ return PlatformOS.GetCpuTemperature();
+ }
+
+ public void Reset()
+ {
+ // TODO: $ sudo reboot
+ throw new NotImplementedException();
+ }
+
+ public void Sleep(TimeSpan duration)
+ {
+ // not supported on RasPi
+ throw new PlatformNotSupportedException();
+ }
+
+ public void OnShutdown(out bool complete, Exception? e = null)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnError(Exception e, out bool recovered)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnResume()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnRecovery(Exception e)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnUpdate(Version newVersion, out bool approveUpdate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void OnUpdateComplete(Version oldVersion, out bool rollbackUpdate)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IBiDirectionalPort CreateBiDirectionalPort(IPin pin, bool initialState, InterruptMode interruptMode, ResistorMode resistorMode, PortDirectionType initialDirection, TimeSpan debounceDuration, TimeSpan glitchDuration, OutputType output = OutputType.PushPull)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ICounter CreateCounter(IPin pin, InterruptMode edge)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/LinuxApp.cs b/source/implementations/linux/Meadow.Linux/LinuxApp.cs
new file mode 100644
index 00000000..8d240679
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/LinuxApp.cs
@@ -0,0 +1,7 @@
+namespace Meadow
+{
+ public class LinuxApp : App
+ where T : class, IMeadowDevice, new()
+ {
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/LinuxDeviceInfo.cs b/source/implementations/linux/Meadow.Linux/LinuxDeviceInfo.cs
new file mode 100644
index 00000000..b63e989d
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/LinuxDeviceInfo.cs
@@ -0,0 +1,30 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow
+{
+ public class LinuxDeviceInfo : IDeviceInformation
+ {
+ internal LinuxDeviceInfo()
+ {
+ }
+
+ public string DeviceName { get => "Meadow.Linux"; set { } }
+
+ public string Model => "[TBD]";
+
+ public MeadowPlatform Platform => MeadowPlatform.MeadowForLinux;
+
+ public string ProcessorType => "[TBD]";
+
+ public string ProcessorSerialNumber => "[TBD]";
+
+ public string UniqueID => "[TBD]";
+
+ public string CoprocessorType => "[TBD]";
+
+ public string? CoprocessorOSVersion => "[TBD]";
+
+ public string OSVersion => throw new NotImplementedException();
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/Meadow.Linux.csproj b/source/implementations/linux/Meadow.Linux/Meadow.Linux.csproj
new file mode 100644
index 00000000..11a77248
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/Meadow.Linux.csproj
@@ -0,0 +1,33 @@
+
+
+ netstandard2.1
+ Library
+ Meadow.Linux
+ Wilderness Labs, Inc
+ http://beta-developer.wildernesslabs.co/Meadow/
+ Meadow.Linux
+ icon.png
+ https://github.com/WildernessLabs/Meadow.Core
+ Meadow
+ 0.31.0
+ false
+ Meadow
+ enable
+ Linux .NET libraries for Wilderness Labs Meadow
+
+
+ true
+ 9.0
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/implementations/linux/Meadow.Linux/PlatformOS/JetsonPlatformOS.cs b/source/implementations/linux/Meadow.Linux/PlatformOS/JetsonPlatformOS.cs
new file mode 100644
index 00000000..b2777dd8
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/PlatformOS/JetsonPlatformOS.cs
@@ -0,0 +1,65 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Meadow
+{
+ public class JetsonPlatformOS : LinuxPlatformOS
+ {
+ private Dictionary _nameToPathLookup = new Dictionary();
+
+ private void PopulatePathLookup()
+ {
+ if (_nameToPathLookup.Count > 0) return;
+
+ var root = new DirectoryInfo("/sys/class/thermal");
+ if (root.Exists)
+ {
+ foreach (var zone in root.GetDirectories("thermal_zone*"))
+ {
+ var name = File.ReadAllText(Path.Combine(zone.FullName, "type")).Trim();
+ _nameToPathLookup.Add(name, Path.Combine(zone.FullName, "temp"));
+ }
+ }
+ }
+
+ public string[] GetTemperatureNames()
+ {
+ PopulatePathLookup();
+
+ return _nameToPathLookup.Keys.ToArray();
+ }
+
+ public Temperature GetTemperature(string name)
+ {
+ PopulatePathLookup();
+
+ if (_nameToPathLookup.ContainsKey(name))
+ {
+ var raw = File.ReadAllText(_nameToPathLookup[name]).Trim();
+ var c = double.Parse(raw);
+ return new Temperature(c / 1000d, Temperature.UnitType.Celsius);
+ }
+ else
+ {
+ throw new ArgumentException($"Temperature name '{name}' not found");
+ }
+ }
+
+ public override Temperature GetCpuTemperature()
+ {
+ return GetTemperature("CPU-therm");
+ }
+
+ public override SerialPortName[] GetSerialPortNames()
+ {
+ return new SerialPortName[]
+ {
+ new SerialPortName("UART2", "/dev/ttyTHS1")
+ };
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/PlatformOS/LinuxNtpClient.cs b/source/implementations/linux/Meadow.Linux/PlatformOS/LinuxNtpClient.cs
new file mode 100644
index 00000000..338b9fd0
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/PlatformOS/LinuxNtpClient.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Meadow
+{
+ public class LinuxNtpClient : INtpClient
+ {
+ public bool Enabled => false;
+
+ public TimeSpan PollPeriod { get; set; }
+
+ public event TimeChangedEventHandler TimeChanged;
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/PlatformOS/LinuxPlatformOS.cs b/source/implementations/linux/Meadow.Linux/PlatformOS/LinuxPlatformOS.cs
new file mode 100644
index 00000000..a9cb6860
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/PlatformOS/LinuxPlatformOS.cs
@@ -0,0 +1,127 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Ports;
+using System.Linq;
+using System.Threading;
+
+namespace Meadow
+{
+ public class LinuxPlatformOS : IPlatformOS
+ {
+ public virtual string OSVersion { get; private set; }
+ public virtual string OSBuildDate { get; private set; }
+ public virtual string RuntimeVersion { get; }
+
+ internal static CancellationTokenSource AppAbort = new();
+
+ public event PowerTransitionHandler BeforeReset;
+ public event PowerTransitionHandler BeforeSleep;
+ public event PowerTransitionHandler AfterWake;
+ public event ExternalStorageEventHandler ExternalStorageEvent;
+
+ public INtpClient NtpClient { get; private set; }
+
+ internal LinuxPlatformOS()
+ {
+ RuntimeVersion = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
+ }
+
+ public void Initialize()
+ {
+ NtpClient = new LinuxNtpClient();
+
+ try
+ {
+ var psi = new ProcessStartInfo("/bin/bash", "-c \"lsb_release -d\"")
+ {
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+ var proc = Process.Start(psi);
+ OSVersion = proc.StandardOutput.ReadToEnd().Trim();
+ }
+ catch (Exception ex)
+ {
+ Resolver.Log.Debug($"Unable to parse lsb_release: {ex.Message}");
+ }
+
+ try
+ {
+ var psi = new ProcessStartInfo("/bin/bash", "-c \"uname -v\"")
+ {
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+ var proc = Process.Start(psi);
+ OSBuildDate = proc.StandardOutput.ReadToEnd().Trim();
+ }
+ catch (Exception ex)
+ {
+ Resolver.Log.Debug($"Unable to parse uname: {ex.Message}");
+ }
+ }
+
+ public virtual SerialPortName[] GetSerialPortNames()
+ {
+ return SerialPort.GetPortNames().Select(n =>
+ new SerialPortName(n, n))
+ .ToArray();
+ }
+
+ public void Initialize(DeviceCapabilities capabilities)
+ {
+ // TODO: deal with capabilities
+ }
+
+ public string FileSystemRoot => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".meadow");
+
+
+ public virtual Temperature GetCpuTemperature()
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public bool RebootOnUnhandledException => false;
+
+ public uint InitializationTimeout => throw new NotImplementedException();
+
+ public IEnumerable ExternalStorage => throw new NotImplementedException();
+
+ public bool AutomaticallyStartNetwork => throw new NotImplementedException();
+
+ public IPlatformOS.NetworkConnectionType SelectedNetwork => throw new NotImplementedException();
+
+ public bool SdStorageSupported => throw new NotImplementedException();
+
+ public T GetConfigurationValue(IPlatformOS.ConfigurationValues item) where T : struct
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetConfigurationValue(IPlatformOS.ConfigurationValues item, T value) where T : struct
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Reset()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Sleep(TimeSpan duration)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RegisterForSleep(ISleepAwarePeripheral peripheral)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/PlatformOS/RaspberryPiPlatformOS.cs b/source/implementations/linux/Meadow.Linux/PlatformOS/RaspberryPiPlatformOS.cs
new file mode 100644
index 00000000..21c138e5
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/PlatformOS/RaspberryPiPlatformOS.cs
@@ -0,0 +1,16 @@
+using Meadow.Hardware;
+
+namespace Meadow
+{
+ public class RaspberryPiPlatformOS : LinuxPlatformOS
+ {
+ public override SerialPortName[] GetSerialPortNames()
+ {
+ return new SerialPortName[]
+ {
+ new SerialPortName("serial0", "/dev/serial0"),
+ new SerialPortName("ttyAMA0", "/dev/ttyAMA0")
+ };
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/SysFs/DeviceBusyException.cs b/source/implementations/linux/Meadow.Linux/SysFs/DeviceBusyException.cs
new file mode 100644
index 00000000..7955e2ff
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/SysFs/DeviceBusyException.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Meadow
+{
+ internal class DeviceBusyException : Exception
+ {
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/SysFs/LinuxErrorCode.cs b/source/implementations/linux/Meadow.Linux/SysFs/LinuxErrorCode.cs
new file mode 100644
index 00000000..90839be4
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/SysFs/LinuxErrorCode.cs
@@ -0,0 +1,9 @@
+namespace Meadow
+{
+ internal enum LinuxErrorCode
+ {
+ PermissionDenied = 13,
+ DeviceBusy = 16,
+ IOError = 121
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalChannelInfo.cs b/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalChannelInfo.cs
new file mode 100644
index 00000000..a7052e68
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalChannelInfo.cs
@@ -0,0 +1,13 @@
+using Meadow.Hardware;
+
+namespace Meadow
+{
+ public class SysFsDigitalChannelInfo : DigitalChannelInfoBase
+ {
+ public SysFsDigitalChannelInfo(
+ string name)
+ : base(name, true, true, true, false, false, false, null)
+ {
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalInputPort.cs b/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalInputPort.cs
new file mode 100644
index 00000000..b1593e17
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalInputPort.cs
@@ -0,0 +1,99 @@
+using Meadow.Hardware;
+using System;
+using System.Threading;
+
+namespace Meadow
+{
+ public class SysFsDigitalInputPort : DigitalInputPortBase, IDigitalInputPort
+ {
+ private int Gpio { get; set; } = -1;
+ private SysFsGpioDriver Driver { get; }
+
+ public override bool State => Driver.GetValue(Gpio);
+
+ internal SysFsDigitalInputPort(
+ SysFsGpioDriver driver,
+ IPin pin,
+ SysFsDigitalChannelInfo channel,
+ InterruptMode interruptMode,
+ ResistorMode resistorMode,
+ TimeSpan debounceDuration,
+ TimeSpan glitchDuration)
+ : base(pin, channel, interruptMode)
+ {
+ if(resistorMode != ResistorMode.Disabled)
+ {
+ throw new NotSupportedException("Resistor Mode not supported on this platform");
+ }
+ if(debounceDuration != TimeSpan.Zero || glitchDuration != TimeSpan.Zero)
+ {
+ throw new NotSupportedException("Glitch filtering and debounce are not currently supported on this platform.");
+ }
+
+ Driver = driver;
+ Pin = pin;
+ if(pin is SysFsPin { } sp)
+ {
+ Gpio = sp.Gpio;
+ }
+ else if(pin is LinuxFlexiPin { } l)
+ {
+ Gpio = l.SysFsGpio;
+ }
+ else
+ {
+ throw new NativeException($"Pin {pin.Name} does not support SYS FS GPIO operations");
+ }
+
+ Driver.Export(Gpio);
+ Thread.Sleep(100); // this seems to be required to prevent an error 13
+ Driver.SetDirection(Gpio, SysFsGpioDriver.GpioDirection.Input);
+ switch(interruptMode)
+ {
+ case InterruptMode.None:
+ // nothing to do
+ break;
+ default:
+ Driver.HookInterrupt(Gpio, interruptMode, InterruptCallback);
+ break;
+ }
+
+ InterruptMode = interruptMode;
+ }
+
+ private void InterruptCallback()
+ {
+ // TODO: implement old/new
+ RaiseChangedAndNotify(new DigitalPortResult());
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if(Gpio >= 0)
+ {
+ Driver.UnhookInterrupt(Gpio);
+ Driver.Unexport(Gpio);
+ }
+
+ base.Dispose(disposing);
+ }
+
+ public override ResistorMode Resistor
+ {
+ get => ResistorMode.Disabled;
+ set => throw new NotSupportedException("Resistor Mode not supported on this platform");
+ }
+
+ public override TimeSpan DebounceDuration
+ {
+ get => TimeSpan.Zero;
+ set => throw new NotSupportedException("Glitch filtering and debounce are not currently supported on this platform.");
+ }
+
+ public override TimeSpan GlitchDuration
+ {
+ get => TimeSpan.Zero;
+ set => throw new NotSupportedException("Glitch filtering and debounce are not currently supported on this platform.");
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalOutputPort.cs b/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalOutputPort.cs
new file mode 100644
index 00000000..622e0900
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/SysFs/SysFsDigitalOutputPort.cs
@@ -0,0 +1,80 @@
+using Meadow.Hardware;
+using System;
+using System.Threading;
+
+namespace Meadow
+{
+ public class SysFsDigitalOutputPort : IDigitalOutputPort
+ {
+ public bool InitialState { get; private set; }
+ public IPin Pin { get; private set; }
+ private bool LastState { get; set; }
+ private int Gpio { get; set; } = -1;
+ private SysFsGpioDriver Driver { get; }
+
+ public IDigitalChannelInfo Channel => throw new NotImplementedException(); // TODO
+
+ internal SysFsDigitalOutputPort(SysFsGpioDriver driver, IPin pin, bool initialState)
+ {
+ Driver = driver;
+ Pin = pin;
+ InitialState = initialState;
+ if(pin is SysFsPin { } sp)
+ {
+ Gpio = sp.Gpio;
+ }
+ else if(pin is LinuxFlexiPin { } l)
+ {
+ Gpio = l.SysFsGpio;
+ }
+ else
+ {
+ throw new NativeException($"Pin {pin.Name} does not support SYS FS GPIO operations");
+ }
+
+ try
+ {
+ Initialize();
+ }
+ catch(DeviceBusyException)
+ {
+ //if the device is busy, it might be that our app tore down before and never Unexported. Try to unexport and retry
+ Driver.Unexport(Gpio);
+ Thread.Sleep(500);
+ Initialize();
+ }
+ }
+
+ private void Initialize()
+ {
+ Driver.Export(Gpio);
+
+ // wait for the sysfs driver to generate the GPIO folder. If we don't we'll get an error 13
+ // TODO: actually look at the filesystem rather than a hard-coded delay
+ Thread.Sleep(500);
+
+ // this may throw if the driver is already open
+ Driver.SetDirection(Gpio, SysFsGpioDriver.GpioDirection.Output);
+
+ State = InitialState;
+ }
+
+ public bool State
+ {
+ get => LastState;
+ set
+ {
+ Driver.SetValue(Gpio, value);
+ LastState = value;
+ }
+ }
+
+ public void Dispose()
+ {
+ if(Gpio >= 0)
+ {
+ Driver.Unexport(Gpio);
+ }
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/SysFs/SysFsGpioDriver.cs b/source/implementations/linux/Meadow.Linux/SysFs/SysFsGpioDriver.cs
new file mode 100644
index 00000000..00d24b76
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/SysFs/SysFsGpioDriver.cs
@@ -0,0 +1,304 @@
+using Meadow.Hardware;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Meadow
+{
+ // error codes:
+ // 13 = permission denied
+ // 16 = device busy
+ internal class SysFsGpioDriver
+ {
+ private const string GpioFolder = "/sys/class/gpio";
+ private const int InterruptCheckPeriodMs = 1000;
+
+ private readonly byte[] GPIO_IN = new byte[] { 0x69, 0x6e, 0x00 }; // the string "in"
+ private readonly byte[] GPIO_OUT = new byte[] { 0x6f, 0x75, 0x74, 0x00 }; // the string "out"
+ private readonly byte[] GPIO_HIGH = new byte[] { 0x31, 0x00 }; // the string "1"
+ private readonly byte[] GPIO_LOW = new byte[] { 0x30, 0x00 }; // the string "0"
+
+ private Dictionary _cancelTokens = new();
+
+ public enum GpioDirection
+ {
+ Input,
+ Output
+ }
+
+ public void Export(int gpio)
+ {
+ try
+ {
+ TryExport(gpio);
+ }
+ catch (DeviceBusyException)
+ {
+ // if the device is busy, it might be that our app tore down before and never Unexported.
+ // Try to unexport and retry
+ Unexport(gpio);
+ Thread.Sleep(500);
+ TryExport(gpio);
+ }
+ }
+
+ private void TryExport(int gpio)
+ {
+ var path = $"{GpioFolder}/export";
+
+ var handle = Interop.open(path, Interop.DriverFlags.O_WRONLY);
+ if (handle < 0)
+ {
+ var ec = (LinuxErrorCode)Marshal.GetLastWin32Error();
+ switch (ec)
+ {
+ case LinuxErrorCode.PermissionDenied:
+ break;
+ }
+ throw new NativeException($"Unable to get handle for GPIO {gpio} (error code {ec})");
+ }
+
+ try
+ {
+ var content = Encoding.ASCII.GetBytes($"{gpio}");
+ var result = Interop.write(handle, content, content.Length);
+ if (result < 0)
+ {
+ var ec = (LinuxErrorCode)Marshal.GetLastWin32Error();
+ switch (ec)
+ {
+ case LinuxErrorCode.DeviceBusy:
+ throw new DeviceBusyException();
+ // DEV NOTE: if a port was not properly disposed, it might still already be exported
+ // Should we unexport it and retry? It could be a bad thing if another app is using the port...
+ }
+ throw new NativeException($"Unable to export GPIO {gpio} (error code {ec})");
+ }
+ }
+ finally
+ {
+ Interop.close(handle);
+ }
+ }
+
+ public void Unexport(int gpio)
+ {
+ var path = $"{GpioFolder}/unexport";
+
+ var handle = Interop.open(path, Interop.DriverFlags.O_WRONLY);
+ if (handle < 0)
+ {
+ throw new NativeException($"Unable to get handle for GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+
+ try
+ {
+ var content = Encoding.ASCII.GetBytes($"{gpio}");
+ var result = Interop.write(handle, content, content.Length);
+ if (result < 0)
+ {
+ throw new NativeException($"Unable to export GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ }
+ finally
+ {
+ Interop.close(handle);
+ }
+ }
+
+ public void SetDirection(int gpio, GpioDirection direction)
+ {
+ var path = $"{GpioFolder}/gpio{gpio}/direction";
+
+ var handle = Interop.open(path, Interop.DriverFlags.O_WRONLY);
+ if(handle < 0)
+ {
+ throw new NativeException($"Unable to get handle for GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ try
+ {
+ var content = direction == GpioDirection.Input ? GPIO_IN : GPIO_OUT;
+ var result = Interop.write(handle, content, content.Length);
+ if (result < 0)
+ {
+ throw new NativeException($"Unable to write to GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ }
+ finally
+ {
+ Interop.close(handle);
+ }
+ }
+
+ public void SetValue(int gpio, bool value)
+ {
+ var path = $"{GpioFolder}/gpio{gpio}/value";
+
+ var handle = Interop.open(path, Interop.DriverFlags.O_WRONLY);
+ if (handle < 0)
+ {
+ throw new NativeException($"Unable to get handle for GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ try
+ {
+ var content = value ? GPIO_HIGH : GPIO_LOW;
+ var result = Interop.write(handle, content, content.Length);
+ if (result < 0)
+ {
+ throw new NativeException($"Unable to write to GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ }
+ finally
+ {
+ Interop.close(handle);
+ }
+ }
+
+ public unsafe bool GetValue(int gpio)
+ {
+ var path = $"{GpioFolder}/gpio{gpio}/value";
+
+ var handle = Interop.open(path, Interop.DriverFlags.O_RDONLY);
+ if (handle < 0)
+ {
+ throw new NativeException($"Unable to get handle for GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ try
+ {
+ var buffer = stackalloc byte[1];
+ var result = Interop.read(handle, buffer, 1);
+ if (result < 0)
+ {
+ throw new NativeException($"Unable to read from GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ return buffer[0] != '0';// 0x30;
+
+ }
+ finally
+ {
+ Interop.close(handle);
+ }
+ }
+
+ private void SetEdge(int gpio, InterruptMode mode)
+ {
+ var path = $"{GpioFolder}/gpio{gpio}/edge";
+
+ var handle = Interop.open(path, Interop.DriverFlags.O_WRONLY);
+ if (handle < 0)
+ {
+ throw new NativeException($"Unable to get handle for GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ try
+ {
+ byte[] content;
+ switch(mode)
+ {
+ case InterruptMode.EdgeBoth:
+ content = Encoding.ASCII.GetBytes("both\0");
+ break;
+ case InterruptMode.EdgeRising:
+ content = Encoding.ASCII.GetBytes("rising\0");
+ break;
+ case InterruptMode.EdgeFalling:
+ content = Encoding.ASCII.GetBytes("falling\0");
+ break;
+ default:
+ content = Encoding.ASCII.GetBytes("none\0");
+ break;
+ }
+
+ var result = Interop.write(handle, content, content.Length);
+ if (result < 0)
+ {
+ throw new NativeException($"Unable to set interrupt edge for GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+ }
+ finally
+ {
+ Interop.close(handle);
+ }
+ }
+
+ public void HookInterrupt(int gpio, InterruptMode mode, Action callback)
+ {
+ // TODO: we need to ensure the interrupt isn't hooked twice
+ SetEdge(gpio, mode);
+
+ var path = $"{GpioFolder}/gpio{gpio}/value";
+ var handle = Interop.open(path, Interop.DriverFlags.O_RDONLY | Interop.DriverFlags.O_NONBLOCK);
+ if (handle < 0)
+ {
+ throw new NativeException($"Unable to get handle for GPIO {gpio} (error code {Marshal.GetLastWin32Error()})");
+ }
+
+ var tokenSource = new CancellationTokenSource();
+
+ _cancelTokens.Add(gpio, tokenSource);
+
+ Task.Run(() =>
+ {
+ InterruptProc(gpio, handle, tokenSource.Token, callback);
+ });
+ }
+
+ public void UnhookInterrupt(int gpio)
+ {
+ if (_cancelTokens.ContainsKey(gpio))
+ {
+ _cancelTokens[gpio].Cancel();
+ _cancelTokens.Remove(gpio);
+ }
+ }
+
+ private void InterruptProc(int gpio, int handle, CancellationToken cancelToken, Action callback)
+ {
+ var readBuffer = new byte[64];
+ var fdset = new Interop.pollfd();
+
+ fdset.fd = handle;
+ fdset.events = Interop.PollEvent.POLLPRI;
+ fdset.revents = Interop.PollEvent.NONE;
+
+ // some platforms (looking at you Jetson!) require we clear the IRQ with a read before we do anything
+ _ = Interop.read(handle, readBuffer, readBuffer.Length);
+
+ while (true)
+ {
+ var result = Interop.poll(ref fdset, 1, InterruptCheckPeriodMs);
+
+ if (cancelToken.IsCancellationRequested)
+ {
+ break;
+ }
+
+ if (result < 0)
+ {
+ throw new NativeException($"Failed to poll for interrupt: {Marshal.GetLastWin32Error()}");
+ }
+ else if(result == 0)
+ {
+ // timeout, NOP
+ }
+ else
+ {
+ // check the received event
+ if ((fdset.revents & Interop.PollEvent.POLLPRI) != Interop.PollEvent.NONE)
+ {
+ // Interrupt Occurred!
+ callback?.Invoke();
+
+ // clear the IRQ with a read
+ Interop.lseek(handle, 0, Interop.SeekWhence.SEEK_SET);
+ _ = Interop.read(handle, readBuffer, readBuffer.Length);
+ }
+ }
+ }
+
+ Interop.close(handle);
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/SysFs/SysFsPin.cs b/source/implementations/linux/Meadow.Linux/SysFs/SysFsPin.cs
new file mode 100644
index 00000000..76dd0d39
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/SysFs/SysFsPin.cs
@@ -0,0 +1,31 @@
+using Meadow.Hardware;
+using System.Collections.Generic;
+
+namespace Meadow
+{
+ public class LinuxFlexiPin : Pin
+ {
+ public int SysFsGpio { get; }
+ public string GpiodChip { get; }
+ public int GpiodOffset { get; }
+
+ public LinuxFlexiPin(string name, object key, int sysfsGpio, string gpiodChip, int gpiodOffset, IList? supportedChannels = null)
+ : base(name, key, supportedChannels)
+ {
+ SysFsGpio = sysfsGpio;
+ GpiodChip = gpiodChip;
+ GpiodOffset = gpiodOffset;
+ }
+ }
+
+ public class SysFsPin : Pin
+ {
+ public int Gpio { get; }
+
+ public SysFsPin(string name, object key, int gpio, IList? supportedChannels = null)
+ : base(name, key, supportedChannels)
+ {
+ Gpio = gpio;
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/ChipCollection.cs b/source/implementations/linux/Meadow.Linux/gpiod/ChipCollection.cs
new file mode 100644
index 00000000..a94f1ca5
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/ChipCollection.cs
@@ -0,0 +1,41 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Meadow
+{
+ internal class ChipCollection : IEnumerable
+ {
+ private List _chips = new List();
+
+ public ChipInfo this[int index]
+ {
+ get => _chips[index];
+ }
+
+ public bool Contains(string name)
+ {
+ return _chips.Any(c => c.Name == name);
+ }
+
+ public void Add(ChipInfo chip)
+ {
+ _chips.Add(chip);
+ }
+
+ public ChipInfo this[string name]
+ {
+ get => _chips.FirstOrDefault(c=> c.Name == name);
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return _chips.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/ChipInfo.cs b/source/implementations/linux/Meadow.Linux/gpiod/ChipInfo.cs
new file mode 100644
index 00000000..4d837375
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/ChipInfo.cs
@@ -0,0 +1,65 @@
+using Meadow.Logging;
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace Meadow
+{
+ internal class ChipInfo : IDisposable
+ {
+ private Logger Logger { get; }
+ public IntPtr Handle { get; private set; }
+ private Gpiod.Interop.gpiod_chip? Chip { get; }
+ public string Name { get; } = string.Empty;
+ public string Label { get; } = string.Empty;
+
+ public LineCollection Lines { get; }
+
+ public bool IsInvalid => Handle.ToInt64() <= 0;
+
+ public static ChipInfo FromIntPtr(Logger logger, IntPtr p)
+ {
+ return new ChipInfo(logger, p);
+ }
+
+ private ChipInfo(Logger logger, IntPtr p)
+ {
+ Logger = logger;
+
+ Handle = p;
+
+ Logger.Debug($"Chip ptr: {p.ToString()}");
+
+ if (!IsInvalid)
+ {
+ Logger.Debug($"Chip ptr VALID");
+
+ Chip = Marshal.PtrToStructure(Handle);
+ Name = Chip.Value.name;
+ Label = Chip.Value.label;
+
+ // Init as an array of nulls. We'll populate as they are accessed
+ Lines = new LineCollection(this, (int)Chip.Value.num_lines);
+ }
+ else
+ {
+ Logger.Error($"errno={Marshal.GetLastWin32Error()}");
+ }
+ }
+
+ public void Dispose()
+ {
+ if (IsInvalid) return;
+
+ Gpiod.Interop.gpiod_chip_close(Handle);
+ Handle = IntPtr.Zero;
+ }
+
+ public override string ToString()
+ {
+ // same format as gpiodetect
+ // gpiochip0 [pinctrl-bcm2711] (58 lines)
+ return $"{Name} [{Label}] ({Lines.Count} lines)";
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/Gpiod.Interop.cs b/source/implementations/linux/Meadow.Linux/gpiod/Gpiod.Interop.cs
new file mode 100644
index 00000000..6623da8c
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/Gpiod.Interop.cs
@@ -0,0 +1,437 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Meadow
+{
+ internal partial class Gpiod
+ {
+ // https://github.com/brgl/libgpiod/blob/master/include/gpiod.h
+
+ internal static class Interop
+ {
+ /*
+ struct gpiod_chip {
+ struct gpiod_line **lines;
+ unsigned int num_lines;
+
+ int fd;
+
+ char name[32];
+ char label[32];
+ };
+
+ struct line_fd_handle {
+ int fd;
+ int refcount;
+ };
+
+ struct gpiod_line {
+ unsigned int offset;
+
+ /* The direction of the GPIO line.
+ int direction;
+
+ /* The active-state configuration.
+ int active_state;
+
+ /* The logical value last written to the line.
+ int output_value;
+
+ /* The GPIOLINE_FLAGs returned by GPIO_GET_LINEINFO_IOCTL.
+ __u32 info_flags;
+
+ /* The GPIOD_LINE_REQUEST_FLAGs provided to request the line.
+ __u32 req_flags;
+
+ * Indicator of LINE_FREE, LINE_REQUESTED_VALUES or
+ * LINE_REQUESTED_EVENTS.
+ int state;
+
+ struct gpiod_chip *chip;
+ struct line_fd_handle *fd_handle;
+
+ char name[32];
+ char consumer[32];
+ };
+ */
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct line_fd_handle
+ {
+ public int fd;
+ public int refcount;
+ }
+
+ public enum line_request
+ {
+ GPIOD_LINE_REQUEST_DIRECTION_AS_IS = 1,
+ /**< Request the line(s), but don't change current direction. */
+ GPIOD_LINE_REQUEST_DIRECTION_INPUT,
+ /**< Request the line(s) for reading the GPIO line state. */
+ GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
+ /**< Request the line(s) for setting the GPIO line state. */
+ GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE,
+ /**< Only watch falling edge events. */
+ GPIOD_LINE_REQUEST_EVENT_RISING_EDGE,
+ /**< Only watch rising edge events. */
+ GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES,
+ /**< Monitor both types of events. */
+ }
+
+ [Flags]
+ public enum line_request_flags
+ {
+ None = 0,
+ GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN = 1 << 0,
+ /**< The line is an open-drain port. */
+ GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE = 1 << 1,
+ /**< The line is an open-source port. */
+ GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW = 1 << 2,
+ /**< The active state of the line is low (high is the default). */
+ GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE = 1 << 3,
+ /**< The line has neither either pull-up nor pull-down resistor. */
+ GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN = 1 << 4,
+ /**< The line has pull-down resistor enabled. */
+ GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP = 1 << 5,
+ /**< The line has pull-up resistor enabled. */
+ }
+
+ public enum line_direction
+ {
+ GPIOD_LINE_DIRECTION_INPUT = 1,
+ /**< Direction is input - we're reading the state of a GPIO line. */
+ GPIOD_LINE_DIRECTION_OUTPUT,
+ /**< Direction is output - we're driving the GPIO line. */
+ };
+
+ public enum line_active_state
+ {
+ GPIOD_LINE_ACTIVE_STATE_HIGH = 1,
+ /**< The active state of a GPIO is active-high. */
+ GPIOD_LINE_ACTIVE_STATE_LOW,
+ /**< The active state of a GPIO is active-low. */
+ };
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ internal unsafe struct gpiod_line
+ {
+ public uint offset;
+
+ // The direction of the GPIO line.
+ public line_direction direction;
+
+ // The active-state configuration.
+ public line_active_state active_state;
+
+ // The logical value last written to the line.
+ public int output_value;
+
+ // The GPIOLINE_FLAGs returned by GPIO_GET_LINEINFO_IOCTL.
+ public uint info_flags;
+
+ // The GPIOD_LINE_REQUEST_FLAGs provided to request the line.
+ public uint req_flags;
+
+ // Indicator of LINE_FREE, LINE_REQUESTED_VALUES or
+ // LINE_REQUESTED_EVENTS.
+ public int state;
+
+ public IntPtr chip; // gpiod_chip *chip;
+ public IntPtr fd_handle; // line_fd_handle *fd_handle;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string name;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string consumer;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ internal struct gpiod_chip
+ {
+ public IntPtr lines; // gpiod_line **lines;
+ public uint num_lines;
+
+ public int fd;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string name;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string label;
+ }
+
+ internal enum gpiod_event_type
+ {
+ GPIOD_LINE_EVENT_RISING_EDGE = 1,
+ /**< Rising edge event. */
+ GPIOD_LINE_EVENT_FALLING_EDGE,
+ /**< Falling edge event. */
+ }
+
+ internal struct gpiod_line_request_config
+ {
+ public string consumer;
+ /**< Name of the consumer. */
+ public int request_type;
+ /**< Request type. */
+ public int flags;
+ /**< Other configuration flags. */
+ };
+
+ internal struct gpiod_line_event
+ {
+ public timespec ts;
+ /**< Best estimate of time of event occurrence. */
+ public gpiod_event_type event_type;
+ /**< Type of the event that occurred. */
+ };
+
+ private const string LIB_GPIOD = "libgpiod.so.2";
+
+ // struct gpiod_chip *gpiod_chip_open_by_name(const char *name)
+ [DllImport(LIB_GPIOD, SetLastError = true, CharSet=CharSet.Ansi)]
+ public static extern IntPtr gpiod_chip_open_by_name([MarshalAs(UnmanagedType.LPStr)] string name);
+
+ // struct gpiod_chip *gpiod_chip_open_by_number(unsigned int num) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern IntPtr gpiod_chip_open_by_number(uint num);
+
+ // void gpiod_chip_close(struct gpiod_chip *chip)
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern void gpiod_chip_close(IntPtr chip);
+
+ // struct gpiod_line *gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset)
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern IntPtr gpiod_chip_get_line(IntPtr chip, int offset);
+
+ // int gpiod_line_update(struct gpiod_line *line)
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_update(IntPtr line);
+
+ /**
+ * @brief Reserve a single line.
+ * @param line GPIO line object.
+ * @param config Request options.
+ * @param default_val Initial line value - only relevant if we're setting
+ * the direction to output.
+ * @return 0 if the line was properly reserved. In case of an error this
+ * routine returns -1 and sets the last error number.
+ *
+ * If this routine succeeds, the caller takes ownership of the GPIO line until
+ * it's released.
+ */
+ // int gpiod_line_request(struct gpiod_line *line, const struct gpiod_line_request_config *config, int default_val) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_request(IntPtr line, IntPtr config, int default_val);
+
+ /**
+ * @brief Reserve a single line, set the direction to input.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @return 0 if the line was properly reserved, -1 on failure.
+ */
+ //int gpiod_line_request_input(struct gpiod_line *line, const char *consumer) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_request_input(IntPtr line, [MarshalAs(UnmanagedType.LPStr)] string consumer);
+
+ /**
+ * @brief Reserve a single line, set the direction to input.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the line was properly reserved, -1 on failure.
+ */
+ // int gpiod_line_request_input_flags(struct gpiod_line *line, const char* consumer, int flags) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_request_input_flags(IntPtr line, [MarshalAs(UnmanagedType.LPStr)] string consumer, line_request_flags flags);
+
+ /**
+ * @brief Reserve a single line, set the direction to output.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param default_val Initial line value.
+ * @return 0 if the line was properly reserved, -1 on failure.
+ */
+ //int gpiod_line_request_output(struct gpiod_line *line, const char *consumer, int default_val) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_request_output(IntPtr line, [MarshalAs(UnmanagedType.LPStr)] string consumer);
+
+
+ /**
+ * @brief Read current value of a single GPIO line.
+ * @param line GPIO line object.
+ * @return 0 or 1 if the operation succeeds. On error this routine returns -1
+ * and sets the last error number.
+ */
+ // int gpiod_line_get_value(struct gpiod_line *line) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_get_value(IntPtr line);
+
+ /**
+ * @brief Set the value of a single GPIO line.
+ * @param line GPIO line object.
+ * @param value New value.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+ // int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_set_value(IntPtr line, int value);
+
+ /**
+ * @brief Release a previously reserved line.
+ * @param line GPIO line object.
+ */
+ // void gpiod_line_release(struct gpiod_line *line) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern void gpiod_line_release(IntPtr line);
+
+ // bool gpiod_line_is_requested(struct gpiod_line *line) GPIOD_API;
+ // bool gpiod_line_is_free(struct gpiod_line *line) GPIOD_API;
+
+ /**
+ * @brief Update the configuration of a single GPIO line.
+ * @param line GPIO line object.
+ * @param direction Updated direction which may be one of
+ * GPIOD_LINE_REQUEST_DIRECTION_AS_IS,
+ * GPIOD_LINE_REQUEST_DIRECTION_INPUT, or
+ * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT.
+ * @param flags Replacement flags.
+ * @param value The new output value for the line when direction is
+ * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+ // int gpiod_line_set_config(struct gpiod_line *line, int direction, int flags, int value) GPIOD_API;
+
+ /**
+ * @brief Set the direction of a single GPIO line to output.
+ * @param line GPIO line object.
+ * @param value The logical value output on the line.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+ //int gpiod_line_set_direction_output(struct gpiod_line *line, int value) GPIOD_API;
+
+ /**
+ * @brief Set the direction of a single GPIO line to input.
+ * @param line GPIO line object.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+ //int gpiod_line_set_direction_input(struct gpiod_line *line) GPIOD_API;
+
+ /**
+ * @brief Update the configuration flags of a single GPIO line.
+ * @param line GPIO line object.
+ * @param flags Replacement flags.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+ // int gpiod_line_set_flags(struct gpiod_line *line, int flags) GPIOD_API;
+
+ internal struct timespec
+ {
+ public ulong tv_sec; // seconds
+ public ulong tv_nsec; // nanoseconds
+ };
+
+ /**
+ * @brief Wait for an event on a single line.
+ * @param line GPIO line object.
+ * @param timeout Wait time limit.
+ * @return 0 if wait timed out, -1 if an error occurred, 1 if an event
+ * occurred.
+ */
+ //int gpiod_line_event_wait(struct gpiod_line *line, const struct timespec *timeout) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_event_wait(IntPtr line, ref timespec timeout);
+
+
+ /**
+ * @brief Request rising edge event notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+ //int gpiod_line_request_rising_edge_events(struct gpiod_line *line, const char* consumer) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_request_rising_edge_events(IntPtr line, [MarshalAs(UnmanagedType.LPStr)] string consumer);
+
+ /**
+ * @brief Request rising edge event notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+ // int gpiod_line_request_rising_edge_events_flags(struct gpiod_line *line, const char* consumer, int flags) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_request_rising_edge_events_flags(IntPtr line, [MarshalAs(UnmanagedType.LPStr)] string consumer, line_request_flags flags);
+
+ /**
+ * @brief Request falling edge event notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+ //int gpiod_line_request_falling_edge_events(struct gpiod_line *line, const char *consumer) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_request_falling_edge_events(IntPtr line, [MarshalAs(UnmanagedType.LPStr)] string consumer);
+
+ /**
+ * @brief Request all event type notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+ //int gpiod_line_request_both_edges_events(struct gpiod_line *line, const char *consumer) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_request_both_edges_events(IntPtr line, [MarshalAs(UnmanagedType.LPStr)] string consumer);
+
+ /**
+ * @brief Request falling edge event notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+ // int gpiod_line_request_falling_edge_events_flags(struct gpiod_line *line, const char* consumer, int flags) GPIOD_API;
+
+ /**
+ * @brief Request all event type notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+ //int gpiod_line_request_both_edges_events_flags(struct gpiod_line *line, const char* consumer, int flags) GPIOD_API;
+
+ /**
+ * @brief Read next pending event from the GPIO line.
+ * @param line GPIO line object.
+ * @param event Buffer to which the event data will be copied.
+ * @return 0 if the event was read correctly, -1 on error.
+ * @note This function will block if no event was queued for this line.
+ */
+ //int gpiod_line_event_read(struct gpiod_line *line, struct gpiod_line_event *event) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern int gpiod_line_event_read(IntPtr line, ref gpiod_line_event evnt);
+
+ // struct gpiod_chip_iter *gpiod_chip_iter_new(void) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern IntPtr gpiod_chip_iter_new();
+
+ // void gpiod_chip_iter_free(struct gpiod_chip_iter *iter) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern void gpiod_chip_iter_free(IntPtr iter);
+
+ // struct gpiod_chip *gpiod_chip_iter_next(struct gpiod_chip_iter *iter) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern IntPtr gpiod_chip_iter_next(IntPtr iter);
+
+ // struct gpiod_chip *gpiod_chip_iter_next_noclose(struct gpiod_chip_iter *iter) GPIOD_API;
+ [DllImport(LIB_GPIOD, SetLastError = true)]
+ public static extern IntPtr gpiod_chip_iter_next_noclose(IntPtr iter);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/Gpiod.cs b/source/implementations/linux/Meadow.Linux/gpiod/Gpiod.cs
new file mode 100644
index 00000000..5c1775c3
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/Gpiod.cs
@@ -0,0 +1,126 @@
+using Meadow.Hardware;
+using Meadow.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Meadow
+{
+ internal partial class Gpiod : IDisposable
+ {
+
+ private class PinInfo
+ {
+ public int FileDescriptor { get; set; }
+ public int ReferenceCount { get; set; }
+ }
+
+ public bool IsDisposed { get; private set; }
+
+ private ChipCollection Chips { get; set; }
+ private Logger Logger { get; }
+
+ public unsafe Gpiod(Logger logger)
+ {
+ Chips = new ChipCollection();
+ Logger = logger;
+
+ var iter = Interop.gpiod_chip_iter_new();
+
+ try
+ {
+ IntPtr p;
+
+ do
+ {
+ p = Interop.gpiod_chip_iter_next_noclose(iter);
+
+ var info = ChipInfo.FromIntPtr(logger, p);
+ if (!info.IsInvalid)
+ {
+ Chips.Add(info);
+
+ Logger.Debug(info.ToString());
+
+ foreach (var line in info.Lines)
+ {
+ Logger.Debug(line.ToString());
+ }
+ }
+ } while(p != IntPtr.Zero);
+ }
+ finally
+ {
+ Interop.gpiod_chip_iter_free(iter);
+ }
+
+ /*
+ var names = Directory.GetFiles("/dev", "gpiochip*");
+
+ foreach (var n in names)
+ {
+ Logger.Debug($"opening {n}");
+
+ var info = ChipInfo.FromIntPtr(logger, Interop.gpiod_chip_open_by_name(n));
+ if (!info.IsInvalid)
+ {
+ Chips.Add(info);
+
+ Logger.Debug(info.ToString());
+
+ foreach (var line in info.Lines)
+ {
+ Logger.Debug(line.ToString());
+ }
+ }
+ else
+ {
+ Console.WriteLine($"ERR: {Marshal.GetLastWin32Error()}");
+
+ Logger.Error($"Unable to get info for chip {n}");
+ }
+ }
+ */
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!IsDisposed)
+ {
+ if (disposing)
+ {
+ // TODO: dispose managed state (managed objects)
+ }
+
+ foreach(var chip in Chips)
+ {
+ chip.Dispose();
+ }
+
+ IsDisposed = true;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ public LineInfo GetLine(GpiodPin pin)
+ {
+ if (!Chips.Contains(pin.Chip))
+ {
+ throw new NativeException($"Unknown GPIO chip {pin.Chip}");
+ }
+
+ var line = Chips[pin.Chip].Lines[pin.Offset];
+
+ // TODO: check availability, check for other reservations
+
+ return line;
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalChannelInfo.cs b/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalChannelInfo.cs
new file mode 100644
index 00000000..558b3fdb
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalChannelInfo.cs
@@ -0,0 +1,13 @@
+using Meadow.Hardware;
+
+namespace Meadow
+{
+ public class GpiodDigitalChannelInfo : DigitalChannelInfoBase
+ {
+ public GpiodDigitalChannelInfo(
+ string name)
+ : base(name, true, true, true, true, true, false, null)
+ {
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalInputPort.cs b/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalInputPort.cs
new file mode 100644
index 00000000..dc576d23
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalInputPort.cs
@@ -0,0 +1,103 @@
+using Meadow.Hardware;
+using System;
+using static Meadow.Gpiod.Interop;
+
+namespace Meadow
+{
+ public class GpiodDigitalInputPort : DigitalInputPortBase
+ {
+ private Gpiod Driver { get; }
+ private LineInfo Line { get; }
+
+ public override bool State => Line.GetValue();
+
+ internal GpiodDigitalInputPort(
+ Gpiod driver,
+ IPin pin,
+ GpiodDigitalChannelInfo channel,
+ InterruptMode interruptMode,
+ ResistorMode resistorMode,
+ TimeSpan debounceDuration,
+ TimeSpan glitchDuration)
+ : base(pin, channel, interruptMode)
+ {
+ if(debounceDuration != TimeSpan.Zero || glitchDuration != TimeSpan.Zero)
+ {
+ throw new NotSupportedException("Glitch filtering and debounce are not currently supported on this platform.");
+ }
+
+ Driver = driver;
+ Pin = pin;
+
+ line_request_flags flags = line_request_flags.None;
+
+ if(pin is GpiodPin { } gp)
+ {
+ Line = Driver.GetLine(gp);
+ switch(resistorMode)
+ {
+ case ResistorMode.InternalPullUp:
+ flags = line_request_flags.GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP;
+ break;
+ case ResistorMode.InternalPullDown:
+ flags = line_request_flags.GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN;
+ break;
+ default:
+ flags = line_request_flags.GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE;
+ break;
+ }
+
+ InterruptMode = interruptMode;
+
+ switch(InterruptMode)
+ {
+ case InterruptMode.EdgeRising:
+ case InterruptMode.EdgeFalling:
+ case InterruptMode.EdgeBoth:
+ Line.InterruptOccurred += OnInterruptOccurred;
+ Line.RequestInterrupts(InterruptMode, flags);
+ break;
+ default:
+ Line.RequestInput(flags);
+ break;
+ }
+
+ }
+ else
+ {
+ throw new NativeException($"Pin {pin.Name} does not support GPIOD operations");
+ }
+ }
+
+ private void OnInterruptOccurred(LineInfo sender, gpiod_line_event e)
+ {
+ var state = e.event_type == gpiod_event_type.GPIOD_LINE_EVENT_RISING_EDGE ? true : false;
+
+ this.RaiseChangedAndNotify(new DigitalPortResult { New = new DigitalState(state, DateTime.UtcNow) }); // TODO: convert event time?
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Line.Release();
+ base.Dispose(disposing);
+ }
+
+ public override ResistorMode Resistor
+ {
+ get => ResistorMode.Disabled;
+ set => throw new NotSupportedException("Resistor Mode not supported on this platform");
+ }
+
+ public override TimeSpan DebounceDuration
+ {
+ get => TimeSpan.Zero;
+ set => throw new NotSupportedException("Glitch filtering and debounce are not currently supported on this platform.");
+ }
+
+ public override TimeSpan GlitchDuration
+ {
+ get => TimeSpan.Zero;
+ set => throw new NotSupportedException("Glitch filtering and debounce are not currently supported on this platform.");
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalOutputPort.cs b/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalOutputPort.cs
new file mode 100644
index 00000000..a5ba6351
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/GpiodDigitalOutputPort.cs
@@ -0,0 +1,53 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow
+{
+
+ public class GpiodDigitalOutputPort : IDigitalOutputPort
+ {
+ public bool InitialState { get; private set; }
+ public IPin Pin { get; private set; }
+ private bool LastState { get; set; }
+
+ private Gpiod Driver { get; }
+ private LineInfo Line { get; }
+
+
+ public IDigitalChannelInfo Channel => throw new NotImplementedException(); // TODO
+
+ internal GpiodDigitalOutputPort(Gpiod driver, IPin pin, bool initialState)
+ {
+ Driver = driver;
+ Pin = pin;
+ InitialState = initialState;
+
+ if (pin is GpiodPin { } gp)
+ {
+ Line = Driver.GetLine(gp);
+ Line.Request(Gpiod.Interop.line_direction.GPIOD_LINE_DIRECTION_OUTPUT);
+ }
+ else
+ {
+ throw new NativeException($"Pin {pin.Name} does not support GPIOD operations");
+ }
+
+ }
+
+ public bool State
+ {
+ get => LastState;
+ set
+ {
+ Line.SetValue(value);
+
+ LastState = value;
+ }
+ }
+
+ public void Dispose()
+ {
+ Line.Release();
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/GpiodPin.cs b/source/implementations/linux/Meadow.Linux/gpiod/GpiodPin.cs
new file mode 100644
index 00000000..bfede3c2
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/GpiodPin.cs
@@ -0,0 +1,18 @@
+using Meadow.Hardware;
+using System.Collections.Generic;
+
+namespace Meadow
+{
+ public class GpiodPin : Pin
+ {
+ public string Chip { get; }
+ public int Offset { get; }
+
+ public GpiodPin(string name, object key, string chip, int offset, IList? supportedChannels = null)
+ : base(name, key, supportedChannels)
+ {
+ Chip = chip;
+ Offset = offset;
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/LineCollection.cs b/source/implementations/linux/Meadow.Linux/gpiod/LineCollection.cs
new file mode 100644
index 00000000..d692d539
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/LineCollection.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Meadow
+{
+ internal class LineCollection : IEnumerable
+ {
+ private LineInfo[] Lines { get; }
+ private ChipInfo Chip { get; }
+
+ internal LineCollection(ChipInfo chip, int count)
+ {
+ Chip = chip;
+ Lines = new LineInfo[count];
+ }
+
+ public int Count => Lines.Length;
+
+ public LineInfo this[int index]
+ {
+ get
+ {
+ if (Lines[index] == null)
+ {
+ Lines[index] = new LineInfo(Chip, index);
+ }
+ return Lines[index];
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ int position = 0; // state
+ while (position < Count)
+ {
+ position++;
+ yield return this[position - 1];
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/LineConfiguration.cs b/source/implementations/linux/Meadow.Linux/gpiod/LineConfiguration.cs
new file mode 100644
index 00000000..0a2c591d
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/LineConfiguration.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Meadow
+{
+ [Flags]
+ internal enum LineConfiguration
+ {
+ /*
+ #define GPIOHANDLE_REQUEST_INPUT (1UL << 0)
+ #define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1)
+ #define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)
+ #define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)
+ #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
+ */
+ Input = 1 << 0,
+ Output = 1 << 1,
+ ActiveLow = 1 << 2,
+ OpenDrain = 1 << 3,
+ OpenSource = 1 << 4
+ }
+}
diff --git a/source/implementations/linux/Meadow.Linux/gpiod/LineInfo.cs b/source/implementations/linux/Meadow.Linux/gpiod/LineInfo.cs
new file mode 100644
index 00000000..129263a7
--- /dev/null
+++ b/source/implementations/linux/Meadow.Linux/gpiod/LineInfo.cs
@@ -0,0 +1,196 @@
+using Meadow.Hardware;
+using System;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using static Meadow.Gpiod.Interop;
+
+namespace Meadow
+{
+ internal delegate void LineEventHandler(LineInfo lineInfo, gpiod_line_event evt);
+
+ internal class LineInfo
+ {
+ private IntPtr Handle { get; set; }
+ private gpiod_line? Line { get; set; }
+ public string Name { get; private set; } = string.Empty;
+ public string Consumer { get; private set; } = string.Empty;
+ public int Offset { get; }
+ public line_direction Direction { get; private set; }
+ public line_active_state ActiveState { get; private set; }
+
+ public bool IsInvalid => Handle.ToInt64() <= 0;
+
+ private bool _istIsRunning = false;
+ private bool _istShouldStop = false;
+ public event LineEventHandler InterruptOccurred = delegate { };
+
+ public LineInfo(ChipInfo chip, int offset)
+ {
+ Offset = offset;
+ Handle = gpiod_chip_get_line(chip.Handle, Offset);
+
+ if (!IsInvalid)
+ {
+ UpdatePropsFromHandle();
+ }
+ }
+
+ private void UpdatePropsFromHandle()
+ {
+ Line = Marshal.PtrToStructure(Handle);
+ Name = Line.Value.name;
+ Consumer = Line.Value.consumer;
+ Direction = Line.Value.direction;
+ ActiveState = Line.Value.active_state;
+ }
+
+ public void Update()
+ {
+ gpiod_line_update(Handle);
+ UpdatePropsFromHandle();
+ }
+
+ private const string MeadowConsumer = "Meadow";
+
+ public void Request(line_direction direction)
+ {
+ // TODO: check for free?
+ int result;
+
+ if (direction == line_direction.GPIOD_LINE_DIRECTION_INPUT)
+ {
+ result = gpiod_line_request_input(Handle, MeadowConsumer);
+ }
+ else
+ {
+ result = gpiod_line_request_output(Handle, MeadowConsumer);
+ }
+
+ if (result == -1)
+ {
+ throw new NativeException("Failed to request line", Marshal.GetLastWin32Error());
+ }
+ }
+
+ public void RequestInput(line_request_flags flags)
+ {
+ // TODO: check for free?
+ var result = gpiod_line_request_input_flags(Handle, MeadowConsumer, flags);
+
+ if (result == -1)
+ {
+ throw new NativeException("Failed to request line", Marshal.GetLastWin32Error());
+ }
+ }
+
+ public void RequestInterrupts(InterruptMode mode, line_request_flags flags)
+ {
+ if (_istIsRunning) return;
+
+ _istShouldStop = false;
+
+ int result;
+
+ switch (mode)
+ {
+ case InterruptMode.EdgeRising:
+ result = gpiod_line_request_rising_edge_events_flags(Handle, MeadowConsumer, flags);
+ break;
+ case InterruptMode.EdgeFalling:
+ result = gpiod_line_request_falling_edge_events(Handle, MeadowConsumer);
+ break;
+ case InterruptMode.EdgeBoth:
+ result = gpiod_line_request_both_edges_events(Handle, MeadowConsumer);
+ break;
+ default:
+ return;
+ }
+
+ if (result == -1)
+ {
+ throw new NativeException($"Failed to request interrupts: {Marshal.GetLastWin32Error()}", Marshal.GetLastWin32Error());
+ }
+ else
+ {
+ Task.Run(() => IST());
+ }
+ }
+
+ private void IST()
+ {
+ _istIsRunning = true;
+ timespec timeout = new timespec { tv_sec = 1 }; // 1-second timeout
+ gpiod_line_event evnt = new gpiod_line_event();
+
+ while (!_istShouldStop)
+ {
+ var result = gpiod_line_event_wait(Handle, ref timeout);
+
+ switch (result)
+ {
+ case 0: // timeout
+ // nop, just wait again
+ break;
+ case 1: // event
+ result = gpiod_line_event_read(Handle, ref evnt);
+
+ if (result == 0)
+ {
+ InterruptOccurred?.Invoke(this, evnt);
+ }
+ else
+ {
+ throw new NativeException("Failed to read interrupt data", Marshal.GetLastWin32Error());
+ }
+ break;
+ case -1: // error
+ default: //undefined
+ throw new NativeException("Waiting for interrupt event failed", Marshal.GetLastWin32Error());
+ }
+
+ }
+
+ _istIsRunning = false;
+ }
+
+ public void Release()
+ {
+ _istShouldStop = true;
+ gpiod_line_release(Handle);
+ }
+
+ public void SetValue(bool state)
+ {
+ var result = gpiod_line_set_value(Handle, state ? 1 : 0);
+
+ if (result == -1)
+ {
+ throw new NativeException("Failed to set line value", Marshal.GetLastWin32Error());
+ }
+ }
+
+ public bool GetValue()
+ {
+ var result = gpiod_line_get_value(Handle);
+
+ if (result == -1)
+ {
+ throw new NativeException("Failed to set line value", Marshal.GetLastWin32Error());
+ }
+
+ return result == 1;
+ }
+
+ public override string ToString()
+ {
+ // same format as gpioinfo
+ // gpiochip0 [pinctrl-bcm2711] (58 lines)
+ var c = string.IsNullOrEmpty(Consumer) ? "unused" : $"\"{Consumer}\"";
+ var n = $"\"{Name}\"";
+ var d = Direction == line_direction.GPIOD_LINE_DIRECTION_INPUT ? " input" : "output";
+ var s = ActiveState == line_active_state.GPIOD_LINE_ACTIVE_STATE_LOW ? "active-low" : "active-high";
+
+ return $"line {Offset:00}: {n,24}{c,24} {d} {s}";
+ }
+ }
+}
diff --git a/source/implementations/linux/README.md b/source/implementations/linux/README.md
new file mode 100644
index 00000000..98debb39
--- /dev/null
+++ b/source/implementations/linux/README.md
@@ -0,0 +1,129 @@
+# Wilderness Labs Meadow.Linux
+
+## Repo Status
+
+[](https://github.com/WildernessLabs/Meadow.Linux/actions/workflows/build.yml)
+[](https://github.com/WildernessLabs/Meadow.Linux/actions/workflows/package.yml)
+
+## Summary
+
+Wilderness Labs Meadow.Linux is a .NET Framework for running IoT applications on Linux devices. A Meadow.Linux application provides developers the ease of quickly creating applications for popular platforms that can use any of the peripheral drivers available in the [Meadow.Foundation](https://github.com/WildernessLabs/Meadow.Foundation) library.
+
+## Supported Platforms and Distributions
+
+Currently tested platforms and distributions:
+
+| Hardware | Distro | Meadow.Core Version tested |
+| :---: | :---: | :---: |
+| Raspberry Pi 4 | Raspberry Pi OS | Beta 6.2 |
+| Raspberry Pi Zero 2 W | Raspberry Pi OS | Beta 6.2 |
+| Jetson Nano | Ubuntu 20.04 | Beta 6.2 |
+| Jetson Xavier AGX | Ubuntu 18.04 | RC-2 |
+| KRTKL Snickerdoodle Black | Ubuntu 20.04 | RC-1 |
+| AMD64 Ubuntu 20.04 under WSL2 | Ubuntu 20.04 | RC-1 |
+
+## License
+
+Apache 2.0
+
+See [LICENSE File](/LICENSE)
+
+## Assumptions
+
+For this documentation, we assume you are developing your application code on a machine *separate* from your target hardware. You can certainly develop and compile your application directly on your target hardware, but the possible variations and permutations make documenting the process more difficult. If you are new to .NET development on IoT devices, we recommend starting with a separate development machine first.
+
+## Prepare Your Target Hardware
+
+`Meadow.Linux` runs applications using the .NET 6.0 Core Runtime, so you must install it on the target.
+
+### Install .NET 7.0
+
+Follow the instructions based on your distro:
+
+https://docs.microsoft.com/en-us/dotnet/core/install/linux
+
+```console
+$ dotnet --list-runtimes
+Microsoft.NETCore.App 7.0.2 [/opt/dotnet/shared/Microsoft.NETCore.App]
+```
+
+### Enable Hardware Access
+
+Different platforms will have different rules for enabling application access to hardware devices such as GPIO, SPI and I2C. Consult your platform documentation for specifics, but as an example Raspberry Pi OS requires that you run `raspi-config` to enable the peripherals you want, and *additionally* add your user to specific groups for the peripherals. If you get permissions errors while first running your application, this is the place to start looking.
+
+## Develop your Application
+
+- Create a .NET 7 Core Application Project
+- Add NuGet references to Meadow.Linux and any other requirements (e.g Meadow.Foundation and peripheral drivers)
+- Develop/Compile
+
+## Deploying
+
+When you are ready to run your application you will need to copy it, and all dependencies, to the target device.
+
+The simplest, most reliable way to collect all of those binaries is by using `dotnet publish`.
+
+For example, the `Bme280_Sample` in this repo can be published to a local folder like this:
+
+```console
+C:\repos\wilderness\Meadow.Linux\src\samples\Bme280_Sample>dotnet publish -c Release -o publish
+Microsoft (R) Build Engine version 17.2.0+41abc5629 for .NET
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+ Determining projects to restore...
+ All projects are up-to-date for restore.
+ Meadow.Linux -> C:\repos\wilderness\Meadow.Linux\src\lib\bin\Release\netstandard2.1\Meadow.Linux.dll
+ Bme280_Sample -> C:\repos\wilderness\Meadow.Linux\src\samples\Bme280_Sample\bin\Release\net7.0\App.dll
+ Bme280_Sample -> C:\repos\wilderness\Meadow.Linux\src\samples\Bme280_Sample\publish\
+```
+
+The output folder contains all of the files and assemblies you need to copy to your target hardware.
+
+Now use a tool such as SCP to copy all of these files to a folder on you target hardware.
+
+[add example command / winscp sceen cap]
+
+[todo: this is a note from my previous work, check if App.deps is a problem]
+- Copy over your application binaries and `App.runtimeconfig.json`. Do *not* copy over App.deps.json
+
+Here's an example of all of the binaries for the Bme280_Sample aaplication after deployment to a target Raspberry Pi:
+
+```console
+$ ls -al
+total 472
+drwxr-xr-x 2 pi pi 4096 Nov 7 17:56 .
+drwxr-xr-x 16 pi pi 4096 Nov 7 17:55 ..
+-rw-r--r-- 1 pi pi 7680 Nov 7 18:09 App.dll
+-rw-r--r-- 1 pi pi 147 Nov 7 17:55 App.runtimeconfig.json
+-rw-r--r-- 1 pi pi 17920 Nov 7 17:58 Bme280.dll
+-rw-r--r-- 1 pi pi 69632 Nov 7 17:23 Meadow.Contracts.dll
+-rw-r--r-- 1 pi pi 127488 Nov 7 17:23 Meadow.dll
+-rw-r--r-- 1 pi pi 111104 Nov 7 17:23 Meadow.Foundation.dll
+-rw-r--r-- 1 pi pi 38400 Nov 7 17:23 Meadow.Linux.dll
+-rw-r--r-- 1 pi pi 83968 Nov 7 17:23 Meadow.Units.dll
+```
+
+## Running
+
+Starting with the `RC1.0` release, Meadow now has a defined lifecycle. This is imposed to allow automated application shutdown by the OtA update service. What this means, practically, is that your Meadow.Linux application must define an entry point in your `App` implementation and and manually start the MeadowOS stack.
+
+```console
+public class MeadowApp : App
+{
+ public static async Task Main(string[] _)
+ {
+ await MeadowOS.Start();
+ }
+ ...
+}
+```
+
+Then launch the application using `dotnet`
+
+```console
+$ dotnet MyAppAssembly.dll
+```
+
+## Work in Progress
+
+`Meadow.Linux` is currently an *Beta* product with several core features that are not yet implemented. Details are available in the [Issues Tab](https://github.com/WildernessLabs/Meadow.Linux/issues) and the source.
diff --git a/source/implementations/linux/tests/Meadow.Linux.Unit.Tests/M4L.Unit.Tests.csproj b/source/implementations/linux/tests/Meadow.Linux.Unit.Tests/M4L.Unit.Tests.csproj
new file mode 100644
index 00000000..4a10f7c0
--- /dev/null
+++ b/source/implementations/linux/tests/Meadow.Linux.Unit.Tests/M4L.Unit.Tests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net5.0
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/source/implementations/linux/tests/Meadow.Linux.Unit.Tests/UnitTest1.cs b/source/implementations/linux/tests/Meadow.Linux.Unit.Tests/UnitTest1.cs
new file mode 100644
index 00000000..fd8b7420
--- /dev/null
+++ b/source/implementations/linux/tests/Meadow.Linux.Unit.Tests/UnitTest1.cs
@@ -0,0 +1,28 @@
+using System;
+using Xunit;
+
+namespace M4L.Unit.Tests
+{
+ public class IoctlEncodingTests
+ {
+ // 1075866368 MSG 4020 6B00
+ // 2147576577 RD_MODE 8001 6B01
+ // 1073834753 WR_MODE 4001 6B01
+ // 2147576578 RD_LSB 8001 6B02
+ // 1073834754 WR_LSB 4001 6B02
+ // 2147576579 RD_BITS 8001 6B03
+ // 1073834755 WR_BITS 4001 6B03
+ // 2147773188 RD_SPD 8004 6B04
+ // 1074031364 WR_SPD 4004 6B04
+ // 2147773189 RD_MD32 8004 6B05
+ // 2147773189 WR_MD32 4004 6B05
+
+ [Fact]
+ public void Test1()
+ {
+ var modeIoctl = Meadow.Interop._IOW('k', 1, 8);
+
+ var ioctl = Meadow.Interop._IOW('k', 3, 32);
+ }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation.Unit.Tests/AnalogInputTests.cs b/source/implementations/simulation/Meadow.Simulation.Unit.Tests/AnalogInputTests.cs
new file mode 100644
index 00000000..723b86d0
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation.Unit.Tests/AnalogInputTests.cs
@@ -0,0 +1,38 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+using Xunit;
+
+namespace Meadow.Simulation.Unit.Tests
+{
+ public class AnalogInputTests
+ {
+ [Fact]
+ public async void TestInputs()
+ {
+ var sim = new SimulatedMeadow();
+
+ foreach (var pin in sim.Pins)
+ {
+ if (pin.Supports())
+ {
+ using (var port = sim.CreateAnalogInputPort(pin, 1, TimeSpan.Zero, new Voltage(3.3f)))
+ {
+ var state = true;
+
+ for (var i = 0; i < 10; i++)
+ {
+ var driven = new Voltage(i * 0.5);
+ sim.DrivePinVoltage(pin, driven);
+ var read = await port.Read();
+
+ Assert.Equal(driven, read);
+
+ state = !state;
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/simulation/Meadow.Simulation.Unit.Tests/DigitalIOTests.cs b/source/implementations/simulation/Meadow.Simulation.Unit.Tests/DigitalIOTests.cs
new file mode 100644
index 00000000..f30922d0
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation.Unit.Tests/DigitalIOTests.cs
@@ -0,0 +1,131 @@
+using Meadow.Hardware;
+using Xunit;
+
+namespace Meadow.Simulation.Unit.Tests
+{
+ public class DigitalIOTests
+ {
+ [Fact]
+ public void TestBiDirectional()
+ {
+ var sim = new SimulatedMeadow();
+
+ foreach (var pin in sim.Pins)
+ {
+ if (pin.Supports(p => p.OutputCapable && p.InputCapable))
+ {
+ using (var port = sim.CreateBiDirectionalPort(pin))
+ {
+ var state = true;
+ port.Direction = PortDirectionType.Output;
+
+ for (var i = 0; i < 10; i++)
+ {
+ port.State = state;
+ var read = sim.ReadPinState(pin);
+
+ Assert.Equal(port.State, read);
+
+ state = !state;
+ }
+
+ port.Direction = PortDirectionType.Input;
+
+ for (var i = 0; i < 10; i++)
+ {
+ sim.DrivePinState(pin, state);
+
+ Assert.Equal(state, port.State);
+
+ state = !state;
+ }
+ }
+ }
+ }
+ }
+
+ [Fact]
+ public void TestOutputs()
+ {
+ var sim = new SimulatedMeadow();
+
+ foreach (var pin in sim.Pins)
+ {
+ if (pin.Supports(p => p.OutputCapable))
+ {
+ using (var port = sim.CreateDigitalOutputPort(pin))
+ {
+ var state = true;
+
+ for (var i = 0; i < 10; i++)
+ {
+ port.State = state;
+ var read = sim.ReadPinState(pin);
+
+ Assert.Equal(port.State, read);
+
+ state = !state;
+ }
+ }
+ }
+ }
+ }
+
+ [Fact]
+ public void TestInputs()
+ {
+ var sim = new SimulatedMeadow();
+
+ foreach (var pin in sim.Pins)
+ {
+ if (pin.Supports(p => p.InputCapable))
+ {
+ using (var port = sim.CreateDigitalInputPort(pin))
+ {
+ var state = true;
+
+ for (var i = 0; i < 10; i++)
+ {
+ sim.DrivePinState(pin, state);
+
+ Assert.Equal(state, port.State);
+
+ state = !state;
+ }
+ }
+ }
+ }
+ }
+
+ [Fact]
+ public void TestInterrupts()
+ {
+ var sim = new SimulatedMeadow();
+
+ foreach (var pin in sim.Pins)
+ {
+ if (pin.Supports(p => p.InputCapable))
+ {
+ var port = sim.CreateDigitalInputPort(pin, Hardware.InterruptMode.EdgeBoth);
+ bool interruptReceived = false;
+ port.Changed += (b, a) =>
+ {
+ interruptReceived = true;
+ };
+
+ var state = true;
+
+ for (var i = 0; i < 10; i++)
+ {
+ Assert.False(interruptReceived);
+ sim.DrivePinState(pin, state);
+ Assert.True(interruptReceived);
+ interruptReceived = false;
+
+ state = !state;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/simulation/Meadow.Simulation.Unit.Tests/Meadow.Simulation.Unit.Tests.csproj b/source/implementations/simulation/Meadow.Simulation.Unit.Tests/Meadow.Simulation.Unit.Tests.csproj
new file mode 100644
index 00000000..a8668a9c
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation.Unit.Tests/Meadow.Simulation.Unit.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net7.0
+ enable
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/source/implementations/simulation/Meadow.Simulation.sln b/source/implementations/simulation/Meadow.Simulation.sln
new file mode 100644
index 00000000..927a7a06
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation.sln
@@ -0,0 +1,59 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32210.308
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Simulation", "Meadow.Simulation\Meadow.Simulation.csproj", "{C2F4AF98-8DB9-4272-9377-9D2E4B19B8E7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Simulation.Unit.Tests", "Meadow.Simulation.Unit.Tests\Meadow.Simulation.Unit.Tests.csproj", "{B82FD7C5-F0C4-4AFE-8041-B39742E9254E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimulatedApp", "SimulatedApp\SimulatedApp.csproj", "{F239AA5C-F804-49EA-B35D-D00964CEE0B7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_ref", "_ref", "{668E863A-0150-4182-8822-02520512AF47}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Core", "..\..\Meadow.Core\source\Meadow.Core\Meadow.Core.csproj", "{E97204F9-EE48-44C1-88C9-2A59BB589FA5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Contracts", "..\..\Meadow.Contracts\Source\Meadow.Contracts\Meadow.Contracts.csproj", "{D1C0A9EB-CD97-4A82-AA14-27C377A199C2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C2F4AF98-8DB9-4272-9377-9D2E4B19B8E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C2F4AF98-8DB9-4272-9377-9D2E4B19B8E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C2F4AF98-8DB9-4272-9377-9D2E4B19B8E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C2F4AF98-8DB9-4272-9377-9D2E4B19B8E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B82FD7C5-F0C4-4AFE-8041-B39742E9254E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B82FD7C5-F0C4-4AFE-8041-B39742E9254E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B82FD7C5-F0C4-4AFE-8041-B39742E9254E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B82FD7C5-F0C4-4AFE-8041-B39742E9254E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F239AA5C-F804-49EA-B35D-D00964CEE0B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F239AA5C-F804-49EA-B35D-D00964CEE0B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F239AA5C-F804-49EA-B35D-D00964CEE0B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F239AA5C-F804-49EA-B35D-D00964CEE0B7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E97204F9-EE48-44C1-88C9-2A59BB589FA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E97204F9-EE48-44C1-88C9-2A59BB589FA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E97204F9-EE48-44C1-88C9-2A59BB589FA5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {E97204F9-EE48-44C1-88C9-2A59BB589FA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E97204F9-EE48-44C1-88C9-2A59BB589FA5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E97204F9-EE48-44C1-88C9-2A59BB589FA5}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {D1C0A9EB-CD97-4A82-AA14-27C377A199C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1C0A9EB-CD97-4A82-AA14-27C377A199C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1C0A9EB-CD97-4A82-AA14-27C377A199C2}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {D1C0A9EB-CD97-4A82-AA14-27C377A199C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1C0A9EB-CD97-4A82-AA14-27C377A199C2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1C0A9EB-CD97-4A82-AA14-27C377A199C2}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {E97204F9-EE48-44C1-88C9-2A59BB589FA5} = {668E863A-0150-4182-8822-02520512AF47}
+ {D1C0A9EB-CD97-4A82-AA14-27C377A199C2} = {668E863A-0150-4182-8822-02520512AF47}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {268DD7BE-115E-4AB6-9115-B2B73364AD0E}
+ EndGlobalSection
+EndGlobal
diff --git a/source/implementations/simulation/Meadow.Simulation/ISimulatedDevice.cs b/source/implementations/simulation/Meadow.Simulation/ISimulatedDevice.cs
new file mode 100644
index 00000000..31e56333
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/ISimulatedDevice.cs
@@ -0,0 +1,11 @@
+using Meadow.Devices;
+using Meadow.Hardware;
+
+namespace Meadow.Simulation
+{
+ public interface ISimulatedDevice : IMeadowDevice
+ where TPinDefinitions : IPinDefinitions
+ {
+ TPinDefinitions Pins { get; }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/Meadow.Simulation.csproj b/source/implementations/simulation/Meadow.Simulation/Meadow.Simulation.csproj
new file mode 100644
index 00000000..1ba3ff5a
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/Meadow.Simulation.csproj
@@ -0,0 +1,31 @@
+
+
+
+ net7.0
+ enable
+ Meadow.Simulation
+ Wilderness Labs, Inc
+ http://beta-developer.wildernesslabs.co/Meadow/
+ Meadow.Simulation
+ https://github.com/WildernessLabs/Meadow.Foundation/blob/master/Source/icon.png?raw=true
+ https://github.com/WildernessLabs/Meadow.Core
+ Meadow
+ 0.1.0
+ false
+ enable
+ A simulation Environment for Wilderness Labs' Meadow Platform
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/implementations/simulation/Meadow.Simulation/SerialPortProxy.cs b/source/implementations/simulation/Meadow.Simulation/SerialPortProxy.cs
new file mode 100644
index 00000000..36d72957
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SerialPortProxy.cs
@@ -0,0 +1,118 @@
+using Meadow.Hardware;
+using System;
+using System.IO.Ports;
+
+namespace Meadow.Simulation
+{
+ public class SerialPortProxy : ISerialPort
+ {
+ public event Hardware.SerialDataReceivedEventHandler DataReceived;
+ public event EventHandler BufferOverrun;
+
+ private SerialPort _port;
+
+ public SerialPortProxy(
+ SerialPortName portName,
+ int baudRate,
+ int dataBits = 8,
+ Hardware.Parity parity = Hardware.Parity.None,
+ Hardware.StopBits stopBits = Hardware.StopBits.One,
+ int readBufferSize = 4096)
+ {
+ _port = new SerialPort(portName.SystemName, baudRate, (System.IO.Ports.Parity)parity, dataBits, (System.IO.Ports.StopBits)stopBits);
+ }
+
+ public int BytesToRead => _port.BytesToRead;
+ public bool IsOpen => _port.IsOpen;
+ public string PortName => _port.PortName;
+ public int ReceiveBufferSize => _port.ReadBufferSize;
+
+ public int BaudRate
+ {
+ get => _port.BaudRate;
+ set => _port.BaudRate = value;
+ }
+
+ public int DataBits
+ {
+ get => _port.DataBits;
+ set => _port.DataBits = value;
+ }
+
+ public Hardware.Parity Parity
+ {
+ get => (Hardware.Parity)_port.Parity;
+ set => _port.Parity = (System.IO.Ports.Parity)value;
+ }
+
+ public Hardware.StopBits StopBits
+ {
+ get => (Hardware.StopBits)_port.StopBits;
+ set => _port.StopBits = (System.IO.Ports.StopBits)value;
+ }
+
+ public TimeSpan ReadTimeout
+ {
+ get => TimeSpan.FromMilliseconds(_port.ReadTimeout);
+ set => _port.ReadTimeout = (int)value.TotalMilliseconds;
+ }
+
+ public TimeSpan WriteTimeout
+ {
+ get => TimeSpan.FromMilliseconds(_port.WriteTimeout);
+ set => _port.WriteTimeout = (int)value.TotalMilliseconds;
+ }
+
+ public void ClearReceiveBuffer()
+ {
+ _port.DiscardOutBuffer();
+ }
+
+ public void Close()
+ {
+ _port.Close();
+ }
+
+ public void Dispose()
+ {
+ _port.Dispose();
+ }
+
+ public void Open()
+ {
+ _port.Open();
+ }
+
+ public int Peek()
+ {
+ throw new NotSupportedException();
+ }
+
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ return _port.Read(buffer, offset, count);
+ }
+
+ public int ReadAll(byte[] buffer)
+ {
+ return _port.Read(buffer, 0, _port.BytesToRead);
+ }
+
+ public int ReadByte()
+ {
+ return _port.ReadByte();
+ }
+
+ public int Write(byte[] buffer)
+ {
+ _port.Write(buffer, 0, buffer.Length);
+ return buffer.Length;
+ }
+
+ public int Write(byte[] buffer, int offset, int count)
+ {
+ _port.Write(buffer, offset, count);
+ return count;
+ }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulatedAnalogInputPort.cs b/source/implementations/simulation/Meadow.Simulation/SimulatedAnalogInputPort.cs
new file mode 100644
index 00000000..1213314e
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulatedAnalogInputPort.cs
@@ -0,0 +1,31 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+using System.Threading.Tasks;
+
+namespace Meadow.Simulation
+{
+ internal class SimulatedAnalogInputPort : AnalogInputPortBase
+ {
+ private SimulatedPin _pin;
+
+ public SimulatedAnalogInputPort(IPin pin, IAnalogChannelInfo channel, int sampleCount, TimeSpan sampleInterval, Voltage referenceVoltage)
+ : base(pin, channel, sampleCount, sampleInterval, referenceVoltage)
+ {
+ _pin = pin as SimulatedPin;
+ }
+
+ public override Task Read()
+ {
+ return Task.FromResult(_pin.Voltage);
+ }
+
+ public override void StartUpdating(TimeSpan? updateInterval)
+ {
+ }
+
+ public override void StopUpdating()
+ {
+ }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulatedBiDirectionalPort.cs b/source/implementations/simulation/Meadow.Simulation/SimulatedBiDirectionalPort.cs
new file mode 100644
index 00000000..7b048256
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulatedBiDirectionalPort.cs
@@ -0,0 +1,46 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow.Simulation
+{
+ internal class SimulatedBiDirectionalPort : BiDirectionalPortBase
+ {
+ private SimulatedPin _pin;
+
+ public SimulatedBiDirectionalPort(IPin pin, IDigitalChannelInfo channel, bool initialState, InterruptMode interruptMode, ResistorMode resistorMode, PortDirectionType initialDirection, TimeSpan debounceDuration, TimeSpan glitchDuration, OutputType initialOutputType = OutputType.PushPull)
+ : base(pin, channel, initialState, interruptMode, resistorMode, initialDirection, debounceDuration, glitchDuration, initialOutputType)
+ {
+ _pin = pin as SimulatedPin;
+ _pin.VoltageChanged += OnPinVoltageChanged;
+ Direction = initialDirection;
+
+ if (initialState)
+ {
+ State = InitialState;
+ }
+ }
+
+ private void OnPinVoltageChanged(object sender, EventArgs e)
+ {
+ RaiseChangedAndNotify(new DigitalPortResult(new DigitalState(State, DateTime.Now), null));
+ }
+
+ public override bool State
+ {
+ get => _pin.Voltage >= SimulationEnvironment.ActiveVoltage;
+ set
+ {
+ if (Direction == PortDirectionType.Input) throw new Exception("Port currently set as Input");
+ _pin.Voltage = value ? SimulationEnvironment.ActiveVoltage : SimulationEnvironment.InactiveVoltage;
+ }
+ }
+
+ public override PortDirectionType Direction { get; set; }
+ public override TimeSpan DebounceDuration { get; set; }
+ public override TimeSpan GlitchDuration { get; set; }
+
+ protected override void Dispose(bool disposing)
+ {
+ }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulatedDigitalInputPort.cs b/source/implementations/simulation/Meadow.Simulation/SimulatedDigitalInputPort.cs
new file mode 100644
index 00000000..22bc7a84
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulatedDigitalInputPort.cs
@@ -0,0 +1,35 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+
+namespace Meadow.Simulation
+{
+ internal class SimulatedDigitalInputPort : DigitalInputPortBase, IDigitalInterruptPort
+ {
+ private SimulatedPin _pin;
+
+ public SimulatedDigitalInputPort(SimulatedPin pin, IDigitalChannelInfo channel, InterruptMode interruptMode = InterruptMode.None)
+ : base(pin, channel, interruptMode)
+ {
+ _pin.VoltageChanged += OnPinVoltageChanged;
+ }
+
+ private void OnPinVoltageChanged(object sender, EventArgs e)
+ {
+ RaiseChangedAndNotify(new DigitalPortResult(new DigitalState(State, DateTime.Now), null));
+ }
+
+ internal void SetVoltage(Voltage voltage)
+ {
+ if (voltage == _pin.Voltage) return;
+
+ _pin.Voltage = voltage;
+ }
+
+ public override bool State { get => _pin.Voltage >= SimulationEnvironment.ActiveVoltage; }
+
+ public override ResistorMode Resistor { get; set; }
+ public override TimeSpan DebounceDuration { get; set; }
+ public override TimeSpan GlitchDuration { get; set; }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulatedDigitalOutputPort.cs b/source/implementations/simulation/Meadow.Simulation/SimulatedDigitalOutputPort.cs
new file mode 100644
index 00000000..53ea0276
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulatedDigitalOutputPort.cs
@@ -0,0 +1,22 @@
+using Meadow.Hardware;
+using Meadow.Units;
+
+namespace Meadow.Simulation
+{
+ internal class SimulatedDigitalOutputPort : DigitalOutputPortBase
+ {
+ private SimulatedPin _pin;
+
+ public SimulatedDigitalOutputPort(IPin pin, IDigitalChannelInfo channel, bool initialState, OutputType initialOutputType)
+ : base(pin, channel, initialState, initialOutputType)
+ {
+ _pin = pin as SimulatedPin;
+ }
+
+ public override bool State
+ {
+ get => _pin.Voltage >= SimulationEnvironment.ActiveVoltage;
+ set => _pin.Voltage = value ? new Voltage(SimulationEnvironment.ActiveVoltage) : new Voltage(SimulationEnvironment.InactiveVoltage);
+ }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulatedMeadow.Pinout.cs b/source/implementations/simulation/Meadow.Simulation/SimulatedMeadow.Pinout.cs
new file mode 100644
index 00000000..addc9065
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulatedMeadow.Pinout.cs
@@ -0,0 +1,80 @@
+using Meadow.Devices;
+using Meadow.Hardware;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Meadow.Simulation
+{
+ public partial class SimulatedPinout : IPinDefinitions
+ {
+ private Dictionary _pins = new Dictionary();
+
+ public SimulatedPinout()
+ {
+ _pins.Add("LED0", new SimulatedPin(
+ "LED0", "LED0",
+ new List {
+ new DigitalChannelInfo("LED0", inputCapable: false, interruptCapable: false)
+ }
+ ));
+
+ _pins.Add("AI0", new SimulatedPin(
+ "AI0", "AI0",
+ new List {
+ new AnalogChannelInfo("AI0", 12, true, false)
+ }
+ ));
+
+ _pins.Add("AI1", new SimulatedPin(
+ "AI1", "AI1",
+ new List {
+ new AnalogChannelInfo("AI0", 12, true, false)
+ }
+ ));
+
+ _pins.Add("D00", new SimulatedPin(
+ "D00", "D00",
+ new List {
+ new DigitalChannelInfo("D00", interruptGroup: 0)
+ }
+ ));
+
+ _pins.Add("D01", new SimulatedPin(
+ "D01", "D01",
+ new List {
+ new DigitalChannelInfo("D01", interruptGroup: 0)
+ }
+ ));
+ _pins.Add("D02", new SimulatedPin(
+ "D02", "D02",
+ new List {
+ new DigitalChannelInfo("D02", interruptGroup: 0)
+ }
+ ));
+ _pins.Add("D03", new SimulatedPin(
+ "D03", "D03",
+ new List {
+ new DigitalChannelInfo("D03", interruptGroup: 0)
+ }
+ ));
+ }
+
+ public IList AllPins => _pins.Values.Cast().ToList();
+
+ public IPin LED0 => _pins["LED0"];
+ public IPin AI0 => _pins["AI0"];
+ public IPin AI1 => _pins["AI1"];
+
+
+ public IPin D00 => _pins["D00"];
+ public IPin D01 => _pins["D01"];
+ public IPin D02 => _pins["D02"];
+ public IPin D03 => _pins["D03"];
+
+ public IEnumerator GetEnumerator() => AllPins.GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ }
+}
+
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulatedMeadow.cs b/source/implementations/simulation/Meadow.Simulation/SimulatedMeadow.cs
new file mode 100644
index 00000000..e392cbcc
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulatedMeadow.cs
@@ -0,0 +1,236 @@
+using Meadow.Hardware;
+using Meadow.Logging;
+using Meadow.Units;
+using System;
+using System.Linq;
+
+namespace Meadow.Simulation
+{
+ public partial class SimulatedMeadow : ISimulatedDevice
+ where TPinDefinitions : IPinDefinitions, new()
+ {
+ private SimulationEngine _simulationEngine;
+ private IPlatformOS _platformOS;
+
+ public event PowerTransitionHandler BeforeReset;
+ public event PowerTransitionHandler BeforeSleep;
+ public event PowerTransitionHandler AfterWake;
+ public event NetworkConnectionHandler NetworkConnected;
+ public event NetworkDisconnectionHandler NetworkDisconnected;
+
+ public Logger Logger { get; }
+
+ public SimulatedMeadow()
+ {
+ Logger = new Logger(new ConsoleLogProvider());
+ Logger.Loglevel = LogLevel.Information;
+
+ Pins = new TPinDefinitions();
+ _platformOS = new SimulatedPlatformOS();
+ _simulationEngine = new SimulationEngine(this, Logger);
+ Information = new SimulationInformation();
+ }
+
+ public TPinDefinitions Pins { get; }
+ public IDeviceInformation Information { get; }
+
+ public IPlatformOS PlatformOS => _platformOS;
+ public DeviceCapabilities Capabilities => throw new NotImplementedException();
+
+ public INetworkAdapterCollection NetworkAdapters => throw new NotImplementedException();
+
+ private void LaunchUI()
+ {
+
+ }
+
+ public void DrivePinVoltage(IPin pin, Voltage voltage)
+ {
+ _simulationEngine.SetPinVoltage(pin, voltage);
+ }
+
+ public void DrivePinState(IPin pin, bool state)
+ {
+ _simulationEngine.SetDiscrete(pin, state);
+ }
+
+ public bool ReadPinState(IPin pin)
+ {
+ return _simulationEngine.GetDiscrete(pin);
+ }
+
+ public IAnalogInputPort CreateAnalogInputPort(IPin pin, int sampleCount, TimeSpan sampleInterval, Meadow.Units.Voltage voltageReference)
+ {
+ var dc = pin.SupportedChannels.FirstOrDefault(i => i is IAnalogChannelInfo) as AnalogChannelInfo;
+ if (dc != null)
+ {
+ return new SimulatedAnalogInputPort(pin, dc, sampleCount, sampleInterval, voltageReference);
+ }
+
+ throw new NotSupportedException();
+ }
+
+ public IBiDirectionalPort CreateBiDirectionalPort(IPin pin, bool initialState = false, InterruptMode interruptMode = InterruptMode.None, ResistorMode resistorMode = ResistorMode.Disabled, PortDirectionType initialDirection = PortDirectionType.Input, OutputType output = OutputType.PushPull)
+ {
+ return CreateBiDirectionalPort(pin, initialState, interruptMode, resistorMode, initialDirection, TimeSpan.Zero, TimeSpan.Zero);
+ }
+
+ public IBiDirectionalPort CreateBiDirectionalPort(IPin pin, bool initialState, InterruptMode interruptMode, ResistorMode resistorMode, PortDirectionType initialDirection, TimeSpan debounceDuration, TimeSpan glitchDuration, OutputType output = OutputType.PushPull)
+ {
+ var dc = pin.SupportedChannels.FirstOrDefault(i => i is IDigitalChannelInfo) as DigitalChannelInfo;
+ if (dc != null)
+ {
+ return new SimulatedBiDirectionalPort(pin, dc, initialState, interruptMode, resistorMode, initialDirection, debounceDuration, glitchDuration);
+ }
+
+ throw new NotSupportedException();
+ }
+
+ public IDigitalInputPort CreateDigitalInputPort(IPin pin, InterruptMode interruptMode = InterruptMode.None, ResistorMode resistorMode = ResistorMode.Disabled)
+ {
+ return CreateDigitalInputPort(pin, interruptMode, resistorMode, TimeSpan.Zero, TimeSpan.Zero);
+ }
+
+ public IDigitalInputPort CreateDigitalInputPort(IPin pin, InterruptMode interruptMode, ResistorMode resistorMode, TimeSpan debounceDuration, TimeSpan glitchDuration)
+ {
+ var dci = pin.SupportedChannels.FirstOrDefault(i => i is IDigitalChannelInfo) as DigitalChannelInfo;
+ if (dci != null)
+ {
+ return new SimulatedDigitalInputPort(pin as SimulatedPin ?? throw new ArgumentException("pin must be a SimulatedPin"), dci, interruptMode);
+ }
+
+ throw new NotSupportedException();
+ }
+
+ public IDigitalOutputPort CreateDigitalOutputPort(IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull)
+ {
+ var dco = pin.SupportedChannels.FirstOrDefault(i => i is IDigitalChannelInfo) as DigitalChannelInfo;
+ if (dco != null)
+ {
+ var p = pin as SimulatedPin;
+ p.VoltageChanged += (s, e) =>
+ {
+ _simulationEngine.SetPinVoltage(pin, p.Voltage);
+ };
+
+ return new SimulatedDigitalOutputPort(pin, dco, false, OutputType.PushPull);
+ }
+
+ throw new NotSupportedException();
+ }
+
+ public ISerialMessagePort CreateSerialMessagePort(SerialPortName portName, byte[] suffixDelimiter, bool preserveDelimiter, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 512)
+ {
+ return SerialMessagePort.From(
+ new SerialPortProxy(portName, baudRate, dataBits, parity, stopBits, readBufferSize),
+ suffixDelimiter,
+ preserveDelimiter);
+ }
+
+ public ISerialMessagePort CreateSerialMessagePort(SerialPortName portName, byte[] prefixDelimiter, bool preserveDelimiter, int messageLength, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 512)
+ {
+ return SerialMessagePort.From(
+ new SerialPortProxy(portName, baudRate, dataBits, parity, stopBits, readBufferSize),
+ prefixDelimiter,
+ preserveDelimiter,
+ messageLength);
+ }
+
+ public ISerialPort CreateSerialPort(SerialPortName portName, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 1024)
+ {
+ return new SerialPortProxy(portName, baudRate, dataBits, parity, stopBits, readBufferSize);
+ }
+
+ public IPin GetPin(string name)
+ {
+ return Pins[name];
+ }
+
+
+ // ========= not implemented below here =========
+
+ public II2cBus CreateI2cBus(int busNumber = 0)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPwmPort CreatePwmPort(IPin pin, float frequency = 100, float dutyCycle = 0.5F, bool invert = false)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, SpiClockConfiguration config)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, Meadow.Units.Frequency speed)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Initialize()
+ {
+ }
+
+ public void Reset()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetClock(DateTime dateTime)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WatchdogEnable(TimeSpan timeout)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WatchdogReset()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Sleep(int seconds = -1)
+ {
+ throw new NotImplementedException();
+ }
+
+ public BatteryInfo GetBatteryInfo()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Temperature GetProcessorTemperature()
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPwmPort CreatePwmPort(IPin pin, Frequency frequency, float dutyCycle = 0.5F, bool invert = false)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ICounter CreateCounter(IPin pin, InterruptMode edge)
+ {
+ throw new NotImplementedException();
+ }
+
+ public II2cBus CreateI2cBus(int busNumber, I2cBusSpeed busSpeed)
+ {
+ throw new NotImplementedException();
+ }
+
+ public II2cBus CreateI2cBus(IPin[] pins, I2cBusSpeed busSpeed)
+ {
+ throw new NotImplementedException();
+ }
+
+ public II2cBus CreateI2cBus(IPin clock, IPin data, I2cBusSpeed busSpeed)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulatedPin.cs b/source/implementations/simulation/Meadow.Simulation/SimulatedPin.cs
new file mode 100644
index 00000000..a211606c
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulatedPin.cs
@@ -0,0 +1,34 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+using System.Collections.Generic;
+
+namespace Meadow.Simulation
+{
+ public class SimulatedPin : Pin
+ {
+ private Voltage _voltage;
+
+ internal event EventHandler VoltageChanged = delegate { };
+
+ internal SimulatedPin(string name, object key, IList? supportedChannels = null)
+ : base(name, key, supportedChannels)
+ {
+ }
+
+ internal Voltage Voltage
+ {
+ get => _voltage;
+ set
+ {
+ if (_voltage != value)
+ {
+ _voltage = value;
+ VoltageChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ }
+ }
+ }
+}
+
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulatedPlatformOS.cs b/source/implementations/simulation/Meadow.Simulation/SimulatedPlatformOS.cs
new file mode 100644
index 00000000..cb66e363
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulatedPlatformOS.cs
@@ -0,0 +1,92 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Linq;
+
+namespace Meadow.Simulation
+{
+ public class SimulatedPlatformOS : IPlatformOS
+ {
+ public event PowerTransitionHandler BeforeReset;
+ public event PowerTransitionHandler BeforeSleep;
+ public event PowerTransitionHandler AfterWake;
+ public event ExternalStorageEventHandler ExternalStorageEvent;
+
+ public string OSVersion => "0.1";
+
+ public string RuntimeVersion { get; }
+
+ internal SimulatedPlatformOS()
+ {
+ RuntimeVersion = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
+ }
+
+ public virtual SerialPortName[] GetSerialPortNames()
+ {
+ return SerialPort.GetPortNames().Select(n =>
+ new SerialPortName(n, n))
+ .ToArray();
+ }
+
+
+
+ public string FileSystemRoot => System.AppDomain.CurrentDomain.BaseDirectory;
+
+ public string OSBuildDate => throw new NotImplementedException();
+
+ public bool RebootOnUnhandledException => throw new NotImplementedException();
+
+ public uint InitializationTimeout => throw new NotImplementedException();
+
+ public INtpClient NtpClient => throw new NotImplementedException();
+
+ public IEnumerable ExternalStorage => throw new NotImplementedException();
+
+ public bool AutomaticallyStartNetwork => throw new NotImplementedException();
+
+ public IPlatformOS.NetworkConnectionType SelectedNetwork => throw new NotImplementedException();
+
+ public bool SdStorageSupported => throw new NotImplementedException();
+
+ public T GetConfigurationValue(IPlatformOS.ConfigurationValues item) where T : struct
+ {
+ throw new NotImplementedException();
+ }
+
+ public Temperature GetCpuTemperature()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Initialize()
+ {
+ }
+
+ public void Reset()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetConfigurationValue(IPlatformOS.ConfigurationValues item, T value) where T : struct
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Sleep(TimeSpan duration)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Initialize(DeviceCapabilities capabilities)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RegisterForSleep(ISleepAwarePeripheral peripheral)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulationEngine.cs b/source/implementations/simulation/Meadow.Simulation/SimulationEngine.cs
new file mode 100644
index 00000000..af97aa49
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulationEngine.cs
@@ -0,0 +1,187 @@
+using Meadow.Hardware;
+using Meadow.Logging;
+using Meadow.Units;
+using System;
+
+namespace Meadow.Simulation
+{
+ internal class SimulationEngine : SimulationEnvironment, IMeadowIOController
+ where TPinDefinitions : IPinDefinitions
+ {
+ private ISimulatedDevice _device;
+ private WebSocketServer _wsServer;
+
+ public SimulationEngine(ISimulatedDevice device, Logger logger)
+ {
+ _device = device;
+
+ _wsServer = new WebSocketServer(logger);
+ _wsServer.MessageReceived += OnWebSocketMessageReceived;
+
+ Initialize();
+ }
+
+ // private Dictionary _discreteStates = new Dictionary();
+ // private Dictionary _analogStates = new Dictionary();
+
+ public IDeviceChannelManager DeviceChannelManager => throw new NotImplementedException();
+
+ public string OSVersion => throw new NotImplementedException();
+
+ public string OSBuildDate => throw new NotImplementedException();
+
+ public string MonoVersion => throw new NotImplementedException();
+
+ public event InterruptHandler Interrupt;
+
+ public void Initialize()
+ {
+ /*
+ foreach (var pin in _device.Pins)
+ {
+ // discretes
+ if (pin.Supports())
+ {
+ _discreteStates.Add(pin, false);
+ }
+
+ // analog inputs
+ if (pin.Supports())
+ {
+ _analogStates.Add(pin, 0d);
+ }
+ }
+ */
+
+ _wsServer.Start();
+
+ PublishState();
+ }
+
+ private void PublishState()
+ {
+ var state = new SimulationState();
+
+ foreach (SimulatedPin pin in _device.Pins)
+ {
+ state.PinStates.Add(pin.Name, pin.Voltage.Volts);
+ }
+
+ var j = System.Text.Json.JsonSerializer.Serialize(state);
+ _wsServer.SendMessage(j);
+ }
+
+ private void OnWebSocketMessageReceived(WebSocketServer source, string message)
+ {
+ switch (message)
+ {
+ case "get_state":
+ // publish full state
+ PublishState();
+ break;
+ }
+ }
+
+ void IMeadowIOController.Initialize()
+ {
+ }
+
+ public void SetDiscrete(IPin pin, bool state)
+ {
+ var voltage = state ? SimulationEnvironment.ActiveVoltage : SimulationEnvironment.InactiveVoltage;
+ SetPinVoltage(pin, voltage);
+ }
+
+ public bool GetDiscrete(IPin pin)
+ {
+ return GetPinVoltage(pin) == SimulationEnvironment.ActiveVoltage; // TODO: do we want an active threshold below 3.3V?
+ }
+
+ public void SetPinVoltage(IPin pin, Voltage voltage)
+ {
+ if (pin is SimulatedPin { } sp)
+ {
+ if (voltage.Volts < 0) throw new ArgumentOutOfRangeException();
+
+ var rising = voltage > sp.Voltage;
+
+ sp.Voltage = voltage;
+
+ _wsServer.SendMessage($"{pin.Name}={voltage.Volts}V");
+
+ Interrupt?.Invoke(pin, rising);
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public Voltage GetPinVoltage(IPin pin)
+ {
+ if (pin is SimulatedPin { } sp)
+ {
+ return sp.Voltage;
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public void SetResistorMode(IPin pin, ResistorMode mode)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void ConfigureOutput(IPin pin, bool initialState, OutputType outputType)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void ConfigureInput(IPin pin, ResistorMode resistorMode, InterruptMode interruptMode, TimeSpan debounceDuration, TimeSpan glitchDuration)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool UnconfigureGpio(IPin pin)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void ConfigureAnalogInput(IPin pin)
+ {
+ throw new NotImplementedException();
+ }
+
+ public int GetAnalogValue(IPin pin)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Temperature GetTemperature()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WireInterrupt(IPin pin, InterruptMode interruptMode, ResistorMode resistorMode, TimeSpan debounceDuration, TimeSpan glitchDuration, bool validateInterruptGroup = true)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void ReassertConfig(IPin pin, bool validateInterruptGroup = true)
+ {
+ throw new NotImplementedException();
+ }
+
+ public T GetConfigurationValue(IPlatformOS.ConfigurationValues item) where T : struct
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetConfigurationValue(IPlatformOS.ConfigurationValues item, T value) where T : struct
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulationEnvironment.cs b/source/implementations/simulation/Meadow.Simulation/SimulationEnvironment.cs
new file mode 100644
index 00000000..169a4d26
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulationEnvironment.cs
@@ -0,0 +1,11 @@
+using Meadow.Hardware;
+using Meadow.Units;
+
+namespace Meadow.Simulation
+{
+ internal abstract class SimulationEnvironment
+ {
+ internal static Voltage ActiveVoltage = new Voltage(3.3d);
+ internal static Voltage InactiveVoltage = new Voltage(0d);
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulationInformation.cs b/source/implementations/simulation/Meadow.Simulation/SimulationInformation.cs
new file mode 100644
index 00000000..673cf211
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulationInformation.cs
@@ -0,0 +1,18 @@
+using Meadow.Hardware;
+
+namespace Meadow.Simulation
+{
+ internal class SimulationInformation : IDeviceInformation
+ {
+ public string DeviceName { get; set; } = "Meadow Simulator";
+ public string Model { get; set; }
+ public MeadowPlatform Platform => MeadowPlatform.MeadowSimulation;
+ public string ProcessorType => "Unknown";
+ public string ProcessorSerialNumber => "SIMULATOR";
+ public string ChipID => "SIM";
+ public string CoprocessorType => "None";
+ public string? CoprocessorOSVersion => null;
+ public string UniqueID => "SIM";
+ public string OSVersion => "SIM";
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/simulation/Meadow.Simulation/SimulationState.cs b/source/implementations/simulation/Meadow.Simulation/SimulationState.cs
new file mode 100644
index 00000000..44dfa8ff
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/SimulationState.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace Meadow.Simulation
+{
+ public class SimulationState
+ {
+ public Dictionary PinStates { get; set; } = new Dictionary();
+ }
+}
diff --git a/source/implementations/simulation/Meadow.Simulation/WebSocketServer.cs b/source/implementations/simulation/Meadow.Simulation/WebSocketServer.cs
new file mode 100644
index 00000000..51adceb4
--- /dev/null
+++ b/source/implementations/simulation/Meadow.Simulation/WebSocketServer.cs
@@ -0,0 +1,182 @@
+using Meadow.Logging;
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Meadow.Simulation
+{
+ internal delegate void MessageHandler(WebSocketServer source, string message);
+
+ internal class WebSocketServer
+ {
+ private Logger _logger;
+ private TcpListener _listener;
+ private List _clients = new List();
+
+ public event MessageHandler MessageReceived = delegate { };
+
+ public WebSocketServer(Logger logger, int port = 8081)
+ {
+ _logger = logger;
+
+ _listener = new TcpListener(IPAddress.Any, port);
+ }
+
+ public void Start()
+ {
+ _listener.Start();
+ var result = _listener.BeginAcceptTcpClient(AcceptProc, _listener);
+ }
+
+ private void AcceptProc(IAsyncResult result)
+ {
+ _logger.Info("Client Connected");
+
+ if (result.AsyncState is TcpListener { } listener)
+ {
+ var client = listener.EndAcceptTcpClient(result);
+ if (NegotiateClientConnection(client))
+ {
+ lock (_clients)
+ {
+ _clients.Add(client);
+ }
+
+ Task.Run(() => MessageReceiverProc(client));
+ }
+
+ // allow multiple clients
+ listener.BeginAcceptTcpClient(AcceptProc, listener);
+ }
+ }
+
+ private void MessageReceiverProc(TcpClient client)
+ {
+ var stream = client.GetStream();
+
+ while (client.Connected)
+ {
+ if (stream.DataAvailable)
+ {
+ byte[] bytes = new byte[client.Available];
+ stream.Read(bytes, 0, client.Available);
+
+ var msgFromClient = (bytes[1] & 0b10000000) != 0;
+ if (!msgFromClient)
+ {
+ // should never happen - all client messages should have that bit set
+ continue;
+ }
+
+ ulong offset = 2;
+ ulong msglen = (ulong)bytes[1] & 0b01111111;
+
+ if (msglen == 126)
+ {
+ // endian swap
+ msglen = BitConverter.ToUInt16(new byte[] { bytes[3], bytes[2] }, 0);
+ offset = 4;
+ }
+ else if (msglen == 127)
+ {
+ throw new Exception("length > 64k not supported");
+ }
+
+ if (msglen > 0)
+ {
+ byte[] decoded = new byte[msglen];
+ byte[] masks = new byte[4] { bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3] };
+ offset += 4;
+
+ for (ulong i = 0; i < msglen; ++i)
+ {
+ decoded[i] = (byte)(bytes[offset + i] ^ masks[i % 4]);
+ }
+
+ var message = Encoding.UTF8.GetString(decoded);
+ _logger.Info($"WS message received: {message}");
+ MessageReceived.Invoke(this, message);
+ }
+ }
+ }
+ }
+
+ public void SendMessage(string message)
+ {
+ var data = Encoding.UTF8.GetBytes(message);
+ var frameData = new List();
+ frameData.Add(0x81); // utf text
+ if (data.Length < 126)
+ {
+ frameData.Add((byte)data.Length);
+ }
+ else if (data.Length < ushort.MaxValue)
+ {
+ frameData.Add((byte)data.Length);
+ frameData.AddRange(BitConverter.GetBytes((ushort)data.Length));
+ }
+ else
+ {
+ throw new Exception("length > 64k not supported");
+ }
+ frameData.AddRange(data);
+
+ lock (_clients)
+ {
+ for (var i = _clients.Count - 1; i >= 0; i--)
+ {
+ if (!_clients[i].Connected)
+ {
+ _clients.RemoveAt(i);
+ }
+ else
+ {
+ var s = _clients[i].GetStream();
+ s.Write(frameData.ToArray(), 0, frameData.Count);
+ }
+ }
+ }
+ }
+
+ private bool NegotiateClientConnection(TcpClient client)
+ {
+ var stream = client.GetStream();
+
+ var buffer = new byte[4096];
+
+ if (client.Connected)
+ {
+ if (stream.DataAvailable)
+ {
+ var read = stream.Read(buffer, 0, buffer.Length);
+ _logger.Info($"WS Received {read} bytes");
+
+ var request = Encoding.UTF8.GetString(buffer, 0, read);
+
+ if (Regex.IsMatch(request, "^GET", RegexOptions.IgnoreCase))
+ {
+ var wskey = $"{Regex.Match(request, "Sec-WebSocket-Key: (.*)").Groups[1].Value.Trim()}258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ var sha = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(wskey));
+
+ var response = Encoding.UTF8.GetBytes(
+ "HTTP/1.1 101 Switching Protocols\r\n" +
+ "Connection: Upgrade\r\n" +
+ "Upgrade: websocket\r\n" +
+ "Sec-WebSocket-Accept: " + Convert.ToBase64String(sha) + "\r\n\r\n");
+
+ stream.Write(response, 0, response.Length);
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/source/implementations/simulation/SimulatedApp/MeadowApp.cs b/source/implementations/simulation/SimulatedApp/MeadowApp.cs
new file mode 100644
index 00000000..8e9f5b76
--- /dev/null
+++ b/source/implementations/simulation/SimulatedApp/MeadowApp.cs
@@ -0,0 +1,42 @@
+
+using Meadow;
+using Meadow.Hardware;
+using Meadow.Modbus;
+using Meadow.Simulation;
+
+public class MeadowApp : App>
+{
+ private IDigitalOutputPort _out1;
+
+ public MeadowApp()
+ {
+ Initialize();
+
+ Run();
+ }
+
+ private void Run()
+ {
+ bool state = false;
+
+ while (true)
+ {
+ Device.Logger.Info($"Setting {_out1.Pin.Name} to {state}");
+ _out1.State = state;
+ state = !state;
+ Thread.Sleep(1000);
+ }
+ }
+
+ void Initialize()
+ {
+ Device.Logger.Info("Initialize hardware...");
+
+ _out1 = Device.CreateDigitalOutputPort(Device.Pins.D00);
+
+ var port = Device.CreateSerialPort(new SerialPortName("foo", "COM10"), 19200);
+ var client = new ModbusRtuClient(port);
+ client.Connect();
+ var register = client.ReadHoldingRegisters(1, 100, 1);
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/simulation/SimulatedApp/Program.cs b/source/implementations/simulation/SimulatedApp/Program.cs
new file mode 100644
index 00000000..5f66c609
--- /dev/null
+++ b/source/implementations/simulation/SimulatedApp/Program.cs
@@ -0,0 +1,5 @@
+using Meadow;
+
+MeadowOS.Main(args);
+
+Thread.Sleep(Timeout.Infinite);
\ No newline at end of file
diff --git a/source/implementations/simulation/SimulatedApp/SimulatedApp.csproj b/source/implementations/simulation/SimulatedApp/SimulatedApp.csproj
new file mode 100644
index 00000000..180e2f58
--- /dev/null
+++ b/source/implementations/simulation/SimulatedApp/SimulatedApp.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ App
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/implementations/windows/Meadow.WinForms/Meadow.WinForms.csproj b/source/implementations/windows/Meadow.WinForms/Meadow.WinForms.csproj
new file mode 100644
index 00000000..228131b6
--- /dev/null
+++ b/source/implementations/windows/Meadow.WinForms/Meadow.WinForms.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net7.0-windows
+ true
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/source/implementations/windows/Meadow.WinForms/WinFormsDisplay.cs b/source/implementations/windows/Meadow.WinForms/WinFormsDisplay.cs
new file mode 100644
index 00000000..e0e52f17
--- /dev/null
+++ b/source/implementations/windows/Meadow.WinForms/WinFormsDisplay.cs
@@ -0,0 +1,89 @@
+using Meadow.Foundation.Graphics;
+using Meadow.Foundation.Graphics.Buffers;
+using Meadow.Hardware;
+
+namespace Meadow.Graphics;
+
+public class WinFormsDisplay : Form, IGraphicsDisplay, ITouchScreen
+{
+ public event TouchEventHandler TouchDown;
+ public event TouchEventHandler TouchUp;
+ public event TouchEventHandler TouchClick;
+
+ private WinFormsPixelBuffer _buffer;
+
+ public ColorMode ColorMode => PixelBuffer.ColorMode;
+ public IPixelBuffer PixelBuffer => _buffer;
+
+ public ColorMode SupportedColorModes => ColorMode.Format24bppRgb888;
+
+ public WinFormsDisplay(int width = 800, int height = 600)
+ {
+ this.Width = width;
+ this.Height = height;
+
+ this.DoubleBuffered = true;
+
+ _buffer = new WinFormsPixelBuffer(Width, Height);
+ }
+
+ void IGraphicsDisplay.Show()
+ {
+ this.Invalidate(true);
+ this.Update();
+ }
+
+ void IGraphicsDisplay.Show(int left, int top, int right, int bottom)
+ {
+ this.Invalidate(new Rectangle(left, top, right - left, bottom - top), true);
+ }
+
+ void IGraphicsDisplay.Clear(bool updateDisplay)
+ {
+ _buffer.Clear();
+ if (updateDisplay)
+ {
+ this.Show();
+ }
+ }
+
+ void IGraphicsDisplay.Fill(Foundation.Color fillColor, bool updateDisplay)
+ {
+ _buffer.Fill(fillColor);
+ if (updateDisplay)
+ {
+ this.Show();
+ }
+ }
+
+ void IGraphicsDisplay.Fill(int x, int y, int width, int height, Foundation.Color fillColor)
+ {
+ _buffer.Fill(x, y, width, height, fillColor);
+ }
+
+ void IGraphicsDisplay.DrawPixel(int x, int y, Foundation.Color color)
+ {
+ _buffer.SetPixel(x, y, color);
+ }
+
+ void IGraphicsDisplay.DrawPixel(int x, int y, bool enabled)
+ {
+ _buffer.SetPixel(x, y, enabled ? Foundation.Color.White : Foundation.Color.Black);
+ }
+
+ void IGraphicsDisplay.InvertPixel(int x, int y)
+ {
+ _buffer.InvertPixel(x, y);
+ }
+
+ void IGraphicsDisplay.WriteBuffer(int x, int y, IPixelBuffer displayBuffer)
+ {
+ _buffer.WriteBuffer(x, y, displayBuffer);
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ e.Graphics.DrawImage(_buffer.Image, 0, 0);
+ base.OnPaint(e);
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/windows/Meadow.WinForms/WinFormsDisplay.resx b/source/implementations/windows/Meadow.WinForms/WinFormsDisplay.resx
new file mode 100644
index 00000000..1af7de15
--- /dev/null
+++ b/source/implementations/windows/Meadow.WinForms/WinFormsDisplay.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/source/implementations/windows/Meadow.WinForms/WinFormsPixelBuffer.cs b/source/implementations/windows/Meadow.WinForms/WinFormsPixelBuffer.cs
new file mode 100644
index 00000000..58e0b62a
--- /dev/null
+++ b/source/implementations/windows/Meadow.WinForms/WinFormsPixelBuffer.cs
@@ -0,0 +1,76 @@
+using Meadow.Foundation.Graphics;
+using Meadow.Foundation.Graphics.Buffers;
+
+namespace Meadow.Graphics;
+
+internal class WinFormsPixelBuffer : IPixelBuffer
+{
+ private Bitmap _bmp;
+ private byte[] _buffer;
+
+ public int Width { get; private set; }
+ public int Height { get; private set; }
+
+ public ColorMode ColorMode => ColorMode.Format24bppRgb888;
+ public int BitDepth => 24;
+ public int ByteCount => _buffer.Length;
+ public byte[] Buffer => _buffer;
+ public Bitmap Image => _bmp;
+
+ public WinFormsPixelBuffer(int width, int height)
+ {
+ Width = width;
+ Height = height;
+
+ _bmp = new Bitmap(Width, Height);
+ var data = _bmp.LockBits(new Rectangle(0, 0, Width, Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
+ _buffer = new byte[Math.Abs(data.Stride * Height)];
+ _bmp.UnlockBits(data);
+ }
+
+ public void Clear()
+ {
+ using (var g = System.Drawing.Graphics.FromImage(_bmp))
+ {
+ g.FillRectangle(new SolidBrush(Color.Black), 0, 0, Width, Height);
+ }
+ }
+
+ public void Fill(Foundation.Color color)
+ {
+ using (var g = System.Drawing.Graphics.FromImage(_bmp))
+ {
+ g.FillRectangle(new SolidBrush(Color.FromArgb(color.R, color.G, color.B)), 0, 0, Width, Height);
+ }
+ }
+
+ public void Fill(int originX, int originY, int width, int height, Foundation.Color color)
+ {
+ using (var g = System.Drawing.Graphics.FromImage(_bmp))
+ {
+ g.FillRectangle(new SolidBrush(Color.FromArgb(color.R, color.G, color.B)), originX, originX, width, height);
+ }
+ }
+
+ public Foundation.Color GetPixel(int x, int y)
+ {
+ var p = _bmp.GetPixel(x, y);
+ return Foundation.Color.FromRgba(p.R, p.G, p.B, p.A);
+ }
+
+ public void InvertPixel(int x, int y)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetPixel(int x, int y, Foundation.Color color)
+ {
+ // TODO: use lockbits and set the _buffer instead
+ _bmp.SetPixel(x, y, Color.FromArgb(color.R, color.G, color.B));
+ }
+
+ public void WriteBuffer(int originX, int originY, IPixelBuffer buffer)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/source/implementations/windows/Meadow.WinUI/Meadow.WinUI.csproj b/source/implementations/windows/Meadow.WinUI/Meadow.WinUI.csproj
new file mode 100644
index 00000000..c206c00f
--- /dev/null
+++ b/source/implementations/windows/Meadow.WinUI/Meadow.WinUI.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0-windows10.0.19041.0
+ 10.0.17763.0
+ enable
+ enable
+ win10-x86;win10-x64;win10-arm64
+ true
+ None
+
+
+
+
+
+
+
+
diff --git a/source/implementations/windows/Meadow.WinUI/WinUIDisplay.cs b/source/implementations/windows/Meadow.WinUI/WinUIDisplay.cs
new file mode 100644
index 00000000..68646e3b
--- /dev/null
+++ b/source/implementations/windows/Meadow.WinUI/WinUIDisplay.cs
@@ -0,0 +1,56 @@
+using Microsoft.UI;
+using System.Numerics;
+using Windows.ApplicationModel.Core;
+using Windows.UI.Composition;
+using Windows.UI.Core;
+
+namespace Meadow.WinUI
+{
+ class WinUIDisplay : IFrameworkView, IFrameworkViewSource
+ {
+ public IFrameworkView CreateView()
+ {
+ return this;
+ }
+ public void SetWindow(CoreWindow window)
+ {
+ this.compositor = new Compositor();
+ this.compositionTarget = this.compositor.CreateTargetForCurrentView();
+
+ // Make a visual which is a container to put other visuals into.
+ ContainerVisual container = this.compositor.CreateContainerVisual();
+ this.compositionTarget.Root = container;
+
+ // Make a visual which paints itself with a brush.
+ SpriteVisual visual = this.compositor.CreateSpriteVisual();
+
+ // Tell it where it is.
+ visual.Size = new Vector2(100, 100);
+ visual.Offset = new Vector3(10, 10, 0);
+
+ // Tell it how to paint itself
+ visual.Brush = this.compositor.CreateColorBrush(Colors.Red);
+
+ // Put it into the container.
+ container.Children.InsertAtTop(visual);
+ }
+ public void Run()
+ {
+ CoreWindow window = CoreWindow.GetForCurrentThread();
+ window.Activate();
+ window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
+ }
+ public void Initialize(CoreApplicationView applicationView)
+ {
+ }
+ public void Load(string entryPoint)
+ {
+ }
+ public void Uninitialize()
+ {
+ }
+ CompositionTarget compositionTarget;
+ Compositor compositor;
+
+ }
+}
\ No newline at end of file
diff --git a/source/implementations/windows/Meadow.Windows/Meadow.Windows.csproj b/source/implementations/windows/Meadow.Windows/Meadow.Windows.csproj
new file mode 100644
index 00000000..6f83d018
--- /dev/null
+++ b/source/implementations/windows/Meadow.Windows/Meadow.Windows.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net7.0
+ enable
+ Meadow.Windows
+ Wilderness Labs, Inc
+ http://beta-developer.wildernesslabs.co/Meadow/
+ Meadow.Simulation
+ https://github.com/WildernessLabs/Meadow.Foundation/blob/master/Source/icon.png?raw=true
+ https://github.com/WildernessLabs/Meadow.Core
+ Meadow
+ 0.1.0
+ false
+ enable
+ A simulation Environment for Wilderness Labs' Meadow Platform
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/implementations/windows/Meadow.Windows/Windows.cs b/source/implementations/windows/Meadow.Windows/Windows.cs
new file mode 100644
index 00000000..6f3f8b23
--- /dev/null
+++ b/source/implementations/windows/Meadow.Windows/Windows.cs
@@ -0,0 +1,152 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+
+namespace Meadow
+{
+ public class Windows : IMeadowDevice
+ {
+ public IPlatformOS PlatformOS { get; }
+ public DeviceCapabilities Capabilities { get; private set; }
+ public IDeviceInformation Information { get; private set; }
+
+ public Windows()
+ {
+ PlatformOS = new WindowsPlatformOS();
+ }
+
+ public void Initialize()
+ {
+ // TODO: populate actual capabilities
+ Capabilities = new DeviceCapabilities(
+ new AnalogCapabilities(false, null),
+ new NetworkCapabilities(false, false),
+ new StorageCapabilities(false));
+
+ // TODO: populate this with appropriate data
+ Information = new WindowsDeviceInformation();
+ }
+
+ public II2cBus CreateI2cBus(int busNumber = 0)
+ {
+ throw new NotSupportedException("Add an IO Expander to your platform");
+ }
+
+ public II2cBus CreateI2cBus(int busNumber, I2cBusSpeed busSpeed)
+ {
+ throw new NotSupportedException("Add an IO Expander to your platform");
+ }
+
+ public II2cBus CreateI2cBus(IPin[] pins, I2cBusSpeed busSpeed)
+ {
+ throw new NotSupportedException("Add an IO Expander to your platform");
+ }
+
+ public II2cBus CreateI2cBus(IPin clock, IPin data, I2cBusSpeed busSpeed)
+ {
+ throw new NotSupportedException("Add an IO Expander to your platform");
+ }
+
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, SpiClockConfiguration config)
+ {
+ throw new NotSupportedException("Add an IO Expander to your platform");
+ }
+
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, Frequency speed)
+ {
+ throw new NotSupportedException("Add an IO Expander to your platform");
+ }
+
+
+
+
+
+
+
+
+
+
+ // TODO: implement everything below here
+
+
+ public INetworkAdapterCollection NetworkAdapters => throw new NotImplementedException();
+
+ public event NetworkConnectionHandler NetworkConnected;
+ public event NetworkDisconnectionHandler NetworkDisconnected;
+
+ public IAnalogInputPort CreateAnalogInputPort(IPin pin, int sampleCount, TimeSpan sampleInterval, Voltage voltageReference)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IBiDirectionalPort CreateBiDirectionalPort(IPin pin, bool initialState, InterruptMode interruptMode, ResistorMode resistorMode, PortDirectionType initialDirection, TimeSpan debounceDuration, TimeSpan glitchDuration, OutputType output = OutputType.PushPull)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ICounter CreateCounter(IPin pin, InterruptMode edge)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IDigitalInputPort CreateDigitalInputPort(IPin pin, InterruptMode interruptMode, ResistorMode resistorMode, TimeSpan debounceDuration, TimeSpan glitchDuration)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IDigitalOutputPort CreateDigitalOutputPort(IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPwmPort CreatePwmPort(IPin pin, Frequency frequency, float dutyCycle = 0.5F, bool invert = false)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISerialMessagePort CreateSerialMessagePort(SerialPortName portName, byte[] suffixDelimiter, bool preserveDelimiter, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 512)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISerialMessagePort CreateSerialMessagePort(SerialPortName portName, byte[] prefixDelimiter, bool preserveDelimiter, int messageLength, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 512)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISerialPort CreateSerialPort(SerialPortName portName, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int readBufferSize = 1024)
+ {
+ throw new NotImplementedException();
+ }
+
+ public BatteryInfo GetBatteryInfo()
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPin GetPin(string name)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Temperature GetProcessorTemperature()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetClock(DateTime dateTime)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WatchdogEnable(TimeSpan timeout)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void WatchdogReset()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/source/implementations/windows/Meadow.Windows/WindowsDeviceInformation.cs b/source/implementations/windows/Meadow.Windows/WindowsDeviceInformation.cs
new file mode 100644
index 00000000..da14c26c
--- /dev/null
+++ b/source/implementations/windows/Meadow.Windows/WindowsDeviceInformation.cs
@@ -0,0 +1,34 @@
+using Meadow.Hardware;
+using Microsoft.Win32;
+using System;
+
+namespace Meadow
+{
+ public class WindowsDeviceInformation : IDeviceInformation
+ {
+ public string DeviceName { get; set; }
+ public string Model { get; }
+ public MeadowPlatform Platform => MeadowPlatform.Windows;
+ public string ProcessorType { get; }
+ public string ProcessorSerialNumber => "Unknown";
+ public string UniqueID { get; }
+ public string CoprocessorType => "None";
+ public string? CoprocessorOSVersion => null;
+ public string OSVersion => Environment.OSVersion.ToString();
+
+ internal WindowsDeviceInformation()
+ {
+ DeviceName = Environment.MachineName;
+ Model = $"{Environment.OSVersion.Platform} {Environment.OSVersion.Version.ToString(2)}";
+
+ using (var key = Registry.LocalMachine.OpenSubKey(@"HARDWARE\DESCRIPTION\System\CentralProcessor\0\"))
+ {
+ ProcessorType = (key?.GetValue("ProcessorNameString")?.ToString() ?? "Unknown").Trim();
+ }
+ using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography\"))
+ {
+ UniqueID = (key?.GetValue("MachineGuid")?.ToString() ?? "Unknown").Trim();
+ }
+ }
+ }
+}
diff --git a/source/implementations/windows/Meadow.Windows/WindowsPlatformOS.cs b/source/implementations/windows/Meadow.Windows/WindowsPlatformOS.cs
new file mode 100644
index 00000000..dd40f80e
--- /dev/null
+++ b/source/implementations/windows/Meadow.Windows/WindowsPlatformOS.cs
@@ -0,0 +1,98 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Ports;
+using System.Linq;
+
+namespace Meadow
+{
+ public class WindowsPlatformOS : IPlatformOS
+ {
+ public event ExternalStorageEventHandler ExternalStorageEvent;
+ public event PowerTransitionHandler BeforeReset;
+ public event PowerTransitionHandler BeforeSleep;
+ public event PowerTransitionHandler AfterWake;
+
+ public string FileSystemRoot { get; private set; }
+
+ public string OSVersion { get; }
+ public string OSBuildDate { get; }
+ public string RuntimeVersion { get; }
+
+ internal WindowsPlatformOS()
+ {
+ OSVersion = Environment.OSVersion.ToString();
+ OSBuildDate = "Unknown";
+ RuntimeVersion = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
+ }
+
+ public void Initialize(DeviceCapabilities capabilities)
+ {
+ // create the Meadow root folder
+ var di = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Meadow"));
+ if (!di.Exists)
+ {
+ di.Create();
+ }
+
+ FileSystemRoot = di.FullName;
+ }
+
+ public SerialPortName[] GetSerialPortNames()
+ {
+ return SerialPort.GetPortNames().Select(n =>
+ new SerialPortName(n, n))
+ .ToArray();
+ }
+
+ public Temperature GetCpuTemperature()
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+
+
+
+
+
+
+
+ // TODO: implement everything below here
+
+ public IEnumerable ExternalStorage => throw new NotImplementedException();
+ public INtpClient NtpClient => throw new NotImplementedException();
+ public bool RebootOnUnhandledException => throw new NotImplementedException();
+ public uint InitializationTimeout => throw new NotImplementedException();
+ public bool AutomaticallyStartNetwork => throw new NotImplementedException();
+ public IPlatformOS.NetworkConnectionType SelectedNetwork => throw new NotImplementedException();
+ public bool SdStorageSupported => throw new NotImplementedException();
+
+ public T GetConfigurationValue(IPlatformOS.ConfigurationValues item) where T : struct
+ {
+ throw new NotImplementedException();
+ }
+
+
+ public void RegisterForSleep(ISleepAwarePeripheral peripheral)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Reset()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetConfigurationValue(IPlatformOS.ConfigurationValues item, T value) where T : struct
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Sleep(TimeSpan duration)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}