├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── report-a-bug.md
│ └── request-a-feature.md
├── dependabot.yml
└── workflows
│ ├── CI.yml
│ ├── CodeQL.yml
│ ├── Sonar.yml
│ └── nuget.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Examples
├── CLI
│ ├── CLI.csproj
│ └── Program.cs
├── Components
│ ├── DotMatrix
│ │ ├── DotMatrix.csproj
│ │ └── Program.cs
│ ├── Motor
│ │ ├── Motor.csproj
│ │ └── Program.cs
│ ├── RGBLED
│ │ ├── Program.cs
│ │ └── RGBLED.csproj
│ ├── RotaryEncoder
│ │ ├── Program.cs
│ │ └── RotaryEncoder.csproj
│ ├── SevenSegmentDisplay
│ │ ├── Program.cs
│ │ └── SevenSegmentDisplay.csproj
│ └── ShiftRegister
│ │ ├── Program.cs
│ │ └── ShiftRegister.csproj
├── Input
│ ├── Input.csproj
│ └── Program.cs
├── PWM
│ ├── PWM.csproj
│ └── Program.cs
└── Web
│ ├── Controllers
│ └── PiController.cs
│ ├── Program.cs
│ ├── Startup.cs
│ ├── Web.csproj
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── wwwroot
│ └── index.html
├── LICENSE
├── README.md
├── SECURITY.md
├── SimpleGPIO.Tests
├── Boards
│ ├── BroadcomBoardTests.cs
│ ├── BroadcomStub.cs
│ └── RaspberryPiTests.cs
├── Components
│ ├── DotMatrixTests.cs
│ ├── MotorTests.cs
│ ├── RGBLEDTests.cs
│ ├── RotaryEncoderTests.cs
│ ├── SevenSegmentDisplayTests.cs
│ └── ShiftRegisterTests.cs
├── GPIO
│ ├── PinStub.cs
│ └── SystemPinInterfaceTests.cs
├── IO
│ └── IOHelpersTests.cs
├── MathTests.cs
├── Power
│ ├── PowerHelpersTests.cs
│ └── PowerModeTests.cs
├── SPI
│ ├── InputStub.cs
│ └── InputTests.cs
└── SimpleGPIO.Tests.csproj
├── SimpleGPIO.sln
└── SimpleGPIO
├── Boards
├── BroadcomBoard.cs
└── RaspberryPi.cs
├── Components
├── DotMatrix.cs
├── Motor.cs
├── RGBLED.cs
├── RotaryEncoder.cs
├── SevenSegmentDisplay.cs
└── ShiftRegister.cs
├── Device
├── GpioControllerWrapper.cs
├── IGpioController.cs
├── IPwmChannel.cs
├── PinInterfaceFactory.cs
└── PwmChannelWrapper.cs
├── GPIO
├── IPinInterface.cs
├── PinInterface.cs
└── SystemPinInterface.cs
├── IO
├── Direction.cs
├── IOHelpers.cs
└── IOMode.cs
├── IsExternalInit.cs
├── Math.cs
├── Power
├── Differential.cs
├── Direct.cs
├── IPowerMode.cs
├── PowerHelpers.cs
├── PowerMode.cs
├── PowerValue.cs
└── Voltage.cs
├── SPI
└── Input.cs
└── SimpleGPIO.csproj
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = tab
6 | trim_trailing_whitespace = true
7 | insert_final_newline = false
8 | max_line_length = off
9 |
10 | [{*.yml,*.yaml}]
11 | indent_style = space
12 | indent_size = 2
13 |
14 | [*.cs]
15 | csharp_prefer_braces = true
16 | csharp_style_expression_bodied_methods = true
17 | csharp_style_expression_bodied_constructors = true
18 | csharp_style_expression_bodied_operators = true
19 | csharp_style_expression_bodied_properties = true
20 | csharp_style_expression_bodied_indexers = true
21 | csharp_style_expression_bodied_accessors = true
22 | csharp_style_expression_bodied_lambdas = true
23 | csharp_style_expression_bodied_local_functions = true
24 | csharp_style_pattern_local_over_anonymous_function = false
25 | csharp_style_namespace_declarations = file_scoped:warning
26 | dotnet_diagnostic.S101.severity = suggestion
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/report-a-bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Report a Bug
3 | about: Describe a problem
4 |
5 | ---
6 |
7 | **Steps to reproduce**
8 | 1. login as X
9 | 1. click X
10 | 1. etc.
11 |
12 | **Expected behavior**
13 | - this should happen
14 | - and this
15 | - etc.
16 |
17 | **Observed behavior**
18 | - this happened instead
19 | - field X got set wrong
20 | - etc.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/request-a-feature.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Request a Feature
3 | about: Suggest new functionality for the app
4 |
5 | ---
6 |
7 | As a ___type_of_user___, I want to ___accomplish_this_goal___, so that ___reason_why_this_would_help___.
8 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "nuget"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 |
--------------------------------------------------------------------------------
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [ push, pull_request ]
3 |
4 | jobs:
5 | Build:
6 | runs-on: ubuntu-latest
7 |
8 | steps:
9 | - uses: actions/checkout@v4
10 |
11 | - name: Setup .NET
12 | uses: actions/setup-dotnet@v4
13 | with:
14 | dotnet-version: 9.0.x
15 |
16 | - name: Restore
17 | run: dotnet restore
18 |
19 | - name: Build
20 | run: dotnet build --no-restore
21 |
22 | - name: Test
23 | run: dotnet test --no-build --verbosity normal
--------------------------------------------------------------------------------
/.github/workflows/CodeQL.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '45 7 * * *'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'csharp' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v4
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v3
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | - name: Setup .NET
54 | uses: actions/setup-dotnet@v4
55 | with:
56 | dotnet-version: 9.0.x
57 |
58 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
59 | # If this step fails, then you should remove it and run the build manually (see below)
60 | - name: Autobuild
61 | uses: github/codeql-action/autobuild@v3
62 |
63 | # ℹ️ Command-line programs to run using the OS shell.
64 | # 📚 https://git.io/JvXDl
65 |
66 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
67 | # and modify them (or add more) to build your code if your project
68 | # uses a compiled language
69 |
70 | #- run: |
71 | # make bootstrap
72 | # make release
73 |
74 | - name: Perform CodeQL Analysis
75 | uses: github/codeql-action/analyze@v3
76 |
--------------------------------------------------------------------------------
/.github/workflows/Sonar.yml:
--------------------------------------------------------------------------------
1 | name: Sonar
2 | on: push
3 |
4 | jobs:
5 | Code-Quality:
6 | runs-on: ubuntu-latest
7 | if: github.actor != 'dependabot[bot]'
8 |
9 | steps:
10 | - name: Checkout code
11 | uses: actions/checkout@v4
12 | with:
13 | fetch-depth: 0
14 |
15 | - name: Setup .NET
16 | uses: actions/setup-dotnet@v4
17 | with:
18 | dotnet-version: 9.0.x
19 |
20 | - name: Install Java
21 | uses: actions/setup-java@v4
22 | with:
23 | distribution: microsoft
24 | java-version: 21
25 |
26 | - name: Install Sonar Scanner
27 | run: dotnet tool install --global dotnet-sonarscanner
28 |
29 | - name: Install dependencies
30 | run: dotnet restore
31 |
32 | - name: Start Sonar Analysis
33 | run: dotnet-sonarscanner begin /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /o:"ecoapm" /k:"ecoAPM_SimpleGPIO" /d:sonar.cs.vstest.reportsPaths="SimpleGPIO.Tests/**/results.trx" /d:sonar.cs.opencover.reportsPaths="SimpleGPIO.Tests/**/coverage.opencover.xml" /d:sonar.coverage.exclusions="Examples/**,SimpleGPIO/Device/**"
34 |
35 | - name: Build
36 | run: dotnet build --no-restore
37 | env:
38 | SONAR_DOTNET_ENABLE_CONCURRENT_EXECUTION: true
39 |
40 | - name: Test
41 | run: dotnet test --no-build --logger "trx;LogFileName=results.trx" --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
42 |
43 | - name: Finish Sonar Analysis
44 | run: dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
45 | env:
46 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
--------------------------------------------------------------------------------
/.github/workflows/nuget.yml:
--------------------------------------------------------------------------------
1 | name: NuGet
2 | on:
3 | push:
4 | tags:
5 | - '*'
6 |
7 | jobs:
8 | Publish:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | with:
13 | ref: ${{ github.ref }}
14 |
15 | - uses: actions/setup-dotnet@v4
16 | with:
17 | dotnet-version: 9.0.x
18 |
19 | - name: Run tests
20 | run: dotnet test
21 |
22 | - name: Package
23 | run: dotnet pack -c Release -p:ContinuousIntegrationBuild=true
24 |
25 | - name: Publish
26 | run: dotnet nuget push SimpleGPIO/bin/Release/SimpleGPIO.$(echo ${{ github.ref }} | sed 's/refs\/tags\///').nupkg -k ${{ secrets.NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 | /.vscode
332 | *.DotSettings
333 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | Steve@ecoAPM.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # ecoAPM Contribution Guidelines
2 |
3 | First of all, thank you for your interest in contributing!
4 |
5 | This document represents a general set of guidelines to help make the process of community contributions as smooth as possible for all parties involved.
6 |
7 | #### Please read the [Code of Conduct](CODE_OF_CONDUCT.md) prior to participating
8 | - This is the standard "Contributor Covenant" used throughout all ecoAPM codebases, and widely across the OSS landscape
9 | - Building a strong, professional, caring, and empathetic community is paramount in our goal as an OSS company
10 |
11 | #### Discussions about changes should happen in an issue before creating a pull request
12 | - While a change may make sense for a specific use case, it may not match the larger goals of the project as initially formulated by the original contributor
13 | - Prior discussion can help give direction to how a feature or bug fix could best be implemented to meet everyone's needs
14 |
15 | #### Follow the standard issue template formats for reporting bugs and requesting new features
16 | - These make reading, understanding, and triaging issues much easier
17 |
18 | #### Commit quality code with detailed documentation to help maximize PR review effectiveness
19 | - All new or modified functionality should have unit tests covering the logic involved
20 | - All PR checks (e.g. automated tests, code quality analysis, etc.) should be passing before a PR is reviewed
21 | - Commit messages should be English (Canadian/UK/US are all acceptable) in the present tense using an imperative form (see existing commits for examples)
22 | - Please do not reference GitHub issue numbers or PR numbers in git commit messages
23 |
24 | #### Multiple smaller, atomic PRs are preferable to single larger monolithic PRs
25 | - This may take longer to get the full changeset merged, but will provide for a much smoother feedback process
26 | - Please reference any related issue numbers in the body of all PR descriptions so that GitHub links them together
27 |
--------------------------------------------------------------------------------
/Examples/CLI/CLI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | linux-arm
7 | latest
8 | enable
9 | enable
10 | SimpleGPIO.Examples.CLI
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Examples/CLI/Program.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 |
3 | var pi = new RaspberryPi();
4 | var redLED = pi.Pin16;
5 | redLED.TurnOn();
6 |
7 | await Task.Delay(TimeSpan.FromSeconds(5));
8 |
9 | // I always pick up my playthings, and so
10 | // I will show you another good trick that I know
11 | pi.Dispose();
--------------------------------------------------------------------------------
/Examples/Components/DotMatrix/DotMatrix.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net9.0
5 | enable
6 | enable
7 | SimpleGPIO.Examples.Components.DotMatrix
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Examples/Components/DotMatrix/Program.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 | using SimpleGPIO.Components;
3 | using SimpleGPIO.Power;
4 |
5 | using var pi = new RaspberryPi();
6 | var matrix = new DotMatrix(
7 | new DotMatrix.PinSet
8 | {
9 | //rows
10 | Pin1 = pi.GPIO5, Pin2 = pi.GPIO7, Pin3 = pi.GPIO12, Pin4 = pi.GPIO13, Pin5 = pi.GPIO8, Pin6 = pi.GPIO15, Pin7 = pi.GPIO6, Pin8 = pi.GPIO3,
11 |
12 | //columns
13 | Pin9 = pi.GPIO1, Pin10 = pi.GPIO14, Pin11 = pi.GPIO16, Pin12 = pi.GPIO4, Pin13 = pi.GPIO11, Pin14 = pi.GPIO2, Pin15 = pi.GPIO17, Pin16 = pi.GPIO18
14 | }
15 | );
16 |
17 | matrix.SetAllRows(PowerValue.Off);
18 | matrix.SetAllColumns(PowerValue.On);
19 |
20 | for (var x = 0; x < 8; x++)
21 | {
22 | if (x > 0)
23 | matrix.Row[x - 1].TurnOff();
24 |
25 | matrix.Row[x].TurnOn();
26 | await Task.Delay(100);
27 | }
28 |
29 | matrix.SetAllColumns(PowerValue.Off);
30 | matrix.SetAllRows(PowerValue.On);
31 |
32 | for (var x = 0; x < 8; x++)
33 | {
34 | if (x > 0)
35 | matrix.Column[x - 1].TurnOff();
36 |
37 | matrix.Column[x].TurnOn();
38 | await Task.Delay(100);
39 | }
40 |
41 | for (var x = 0; x < 3; x++)
42 | {
43 | matrix.SetAll(PowerValue.Off);
44 | await Task.Delay(100);
45 |
46 | matrix.SetRows(new DotMatrix.PowerSet { Item1 = PowerValue.Off, Item2 = PowerValue.Off, Item3 = PowerValue.Off, Item4 = PowerValue.On, Item5 = PowerValue.On, Item6 = PowerValue.Off, Item7 = PowerValue.Off, Item8 = PowerValue.Off });
47 | matrix.SetColumns(new DotMatrix.PowerSet { Item1 = PowerValue.Off, Item2 = PowerValue.Off, Item3 = PowerValue.Off, Item4 = PowerValue.On, Item5 = PowerValue.On, Item6 = PowerValue.Off, Item7 = PowerValue.Off, Item8 = PowerValue.Off });
48 | await Task.Delay(100);
49 |
50 | matrix.SetRows(new DotMatrix.PowerSet { Item1 = PowerValue.Off, Item2 = PowerValue.Off, Item3 = PowerValue.On, Item4 = PowerValue.On, Item5 = PowerValue.On, Item6 = PowerValue.On, Item7 = PowerValue.Off, Item8 = PowerValue.Off });
51 | matrix.SetColumns(new DotMatrix.PowerSet { Item1 = PowerValue.Off, Item2 = PowerValue.Off, Item3 = PowerValue.On, Item4 = PowerValue.On, Item5 = PowerValue.On, Item6 = PowerValue.On, Item7 = PowerValue.Off, Item8 = PowerValue.Off });
52 | await Task.Delay(100);
53 |
54 | matrix.SetRows(new DotMatrix.PowerSet { Item1 = PowerValue.Off, Item2 = PowerValue.On, Item3 = PowerValue.On, Item4 = PowerValue.On, Item5 = PowerValue.On, Item6 = PowerValue.On, Item7 = PowerValue.On, Item8 = PowerValue.Off });
55 | matrix.SetColumns(new DotMatrix.PowerSet { Item1 = PowerValue.Off, Item2 = PowerValue.On, Item3 = PowerValue.On, Item4 = PowerValue.On, Item5 = PowerValue.On, Item6 = PowerValue.On, Item7 = PowerValue.On, Item8 = PowerValue.Off });
56 | await Task.Delay(100);
57 |
58 | matrix.SetRows(new DotMatrix.PowerSet { Item1 = PowerValue.On, Item2 = PowerValue.On, Item3 = PowerValue.On, Item4 = PowerValue.On, Item5 = PowerValue.On, Item6 = PowerValue.On, Item7 = PowerValue.On, Item8 = PowerValue.On });
59 | matrix.SetColumns(new DotMatrix.PowerSet { Item1 = PowerValue.On, Item2 = PowerValue.On, Item3 = PowerValue.On, Item4 = PowerValue.On, Item5 = PowerValue.On, Item6 = PowerValue.On, Item7 = PowerValue.On, Item8 = PowerValue.On });
60 | await Task.Delay(100);
61 | }
62 |
63 | await Task.Delay(100);
64 | matrix.SetAll(PowerValue.Off);
--------------------------------------------------------------------------------
/Examples/Components/Motor/Motor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net9.0
5 | enable
6 | enable
7 | SimpleGPIO.Examples.Components.Motor
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Examples/Components/Motor/Program.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 | using SimpleGPIO.Components;
3 |
4 | using var pi = new RaspberryPi();
5 | var enabledPin = pi.Pin11;
6 | var clockwisePin = pi.Pin13;
7 | var counterclockwisePin = pi.Pin15;
8 | var motor = new Motor(enabledPin, clockwisePin, counterclockwisePin);
9 |
10 | await motor.TurnClockwiseFor(TimeSpan.FromSeconds(2));
11 | //give it a second to fully stop before reversing
12 | await Task.Delay(TimeSpan.FromSeconds(1));
13 |
14 | await motor.TurnCounterclockwiseFor(TimeSpan.FromSeconds(1), true);
15 | //give it some cooldown time before disposing,
16 | //as counterclockwisePin turning off will abruptly stop the motor
17 | await Task.Delay(TimeSpan.FromSeconds(2));
--------------------------------------------------------------------------------
/Examples/Components/RGBLED/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Drawing;
2 | using SimpleGPIO.Boards;
3 | using SimpleGPIO.Components;
4 |
5 | using var pi = new RaspberryPi();
6 | var led = new RGBLED(pi.GPIO5, pi.GPIO6, pi.GPIO13);
7 | var delay = TimeSpan.FromSeconds(0.5);
8 |
9 | led.SetColor(Color.Red);
10 | await Task.Delay(delay);
11 |
12 | led.SetColor(Color.Lime);
13 | await Task.Delay(delay);
14 |
15 | led.SetColor(Color.Blue);
16 | await Task.Delay(delay);
17 |
18 | await led.FadeTo(Color.Yellow, delay);
19 | await led.FadeTo(Color.Magenta, delay);
20 | await led.FadeOut(delay);
--------------------------------------------------------------------------------
/Examples/Components/RGBLED/RGBLED.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net9.0
5 | enable
6 | enable
7 | SimpleGPIO.Examples.Components.RGBLED
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Examples/Components/RotaryEncoder/Program.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 | using SimpleGPIO.Components;
3 |
4 | using var pi = new RaspberryPi();
5 | var dial = new RotaryEncoder(pi.Pin11, pi.Pin13);
6 |
7 | var x = 0;
8 | dial.OnIncrease(() => Console.WriteLine(++x));
9 | dial.OnDecrease(() => Console.WriteLine(--x));
10 |
11 | Console.WriteLine("Press any key to exit...");
12 | Console.ReadKey();
--------------------------------------------------------------------------------
/Examples/Components/RotaryEncoder/RotaryEncoder.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net9.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Examples/Components/SevenSegmentDisplay/Program.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 | using SimpleGPIO.Components;
3 |
4 | using var pi = new RaspberryPi();
5 |
6 | var segments = new SevenSegmentDisplay.PinSet
7 | {
8 | Center = pi.GPIO12,
9 | UpperLeft = pi.GPIO23,
10 | Top = pi.GPIO24,
11 | UpperRight = pi.GPIO25,
12 | LowerLeft = pi.GPIO17,
13 | Bottom = pi.GPIO27,
14 | LowerRight = pi.GPIO22,
15 | Decimal = pi.GPIO5
16 | };
17 | var display = new SevenSegmentDisplay(segments);
18 |
19 | Console.WriteLine("Enter characters, or press enter to exit");
20 | var keyInfo = new ConsoleKeyInfo();
21 | do
22 | {
23 | display.Show(keyInfo.KeyChar);
24 | keyInfo = Console.ReadKey();
25 | } while (keyInfo.Key != ConsoleKey.Enter);
26 |
27 | Console.WriteLine();
--------------------------------------------------------------------------------
/Examples/Components/SevenSegmentDisplay/SevenSegmentDisplay.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net9.0
5 | enable
6 | enable
7 | SimpleGPIO.Examples.Components.SevenSegmentDisplay
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Examples/Components/ShiftRegister/Program.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 | using SimpleGPIO.Components;
3 |
4 | using var pi = new RaspberryPi();
5 | var register = new ShiftRegister(pi.Pin13, pi.Pin11, pi.Pin15, pi.Pin16, pi.Pin18);
6 |
7 | for (byte x = 0; x < byte.MaxValue; x++)
8 | {
9 | Console.WriteLine(x);
10 | register.SetValue(x);
11 | await Task.Delay(200);
12 | }
13 |
14 | await Task.Delay(400);
15 | register.Clear();
--------------------------------------------------------------------------------
/Examples/Components/ShiftRegister/ShiftRegister.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net9.0
5 | enable
6 | enable
7 | SimpleGPIO.Examples.Components.ShiftRegister
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Examples/Input/Input.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net9.0
5 | enable
6 | enable
7 | SimpleGPIO.Examples.Input
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Examples/Input/Program.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 |
3 | using var pi = new RaspberryPi();
4 | var button = pi.Pin11;
5 | var led = pi.Pin16;
6 | button.OnPowerOn(() => led.Toggle());
7 |
8 | Console.WriteLine("Press any key to exit...");
9 | Console.ReadKey();
--------------------------------------------------------------------------------
/Examples/PWM/PWM.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 | enable
8 | SimpleGPIO.Examples.PWM
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Examples/PWM/Program.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 |
3 | var pi = new RaspberryPi();
4 | var led = pi.GPIO17;
5 | var delay = TimeSpan.FromSeconds(1);
6 |
7 | await led.FadeIn(delay);
8 |
9 | led.Strength = 25;
10 | await Task.Delay(delay);
11 |
12 | await led.FadeTo(75, delay);
13 |
14 | led.Strength = 50;
15 | await Task.Delay(delay);
16 |
17 | await led.FadeOut(delay);
18 | pi.Dispose();
--------------------------------------------------------------------------------
/Examples/Web/Controllers/PiController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using SimpleGPIO.Boards;
3 | using SimpleGPIO.GPIO;
4 |
5 | namespace SimpleGPIO.Examples.Web.Controllers;
6 |
7 | [ApiController]
8 | [Route("")]
9 | public sealed class PiController : ControllerBase
10 | {
11 | private readonly IPinInterface _redLED;
12 | private readonly IPinInterface _yellowLED;
13 | private readonly IPinInterface _greenLED;
14 |
15 | public PiController(RaspberryPi pi)
16 | {
17 | _redLED = pi.Pin16;
18 | _yellowLED = pi.Pin18;
19 | _greenLED = pi.Pin22;
20 | }
21 |
22 | [HttpPost("red")]
23 | public void Red() => _redLED.Toggle();
24 |
25 | [HttpPost("yellow")]
26 | public void Yellow() => _yellowLED.Toggle();
27 |
28 | [HttpPost("green")]
29 | public void Green() => _greenLED.Toggle();
30 | }
--------------------------------------------------------------------------------
/Examples/Web/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 |
3 | namespace SimpleGPIO.Examples.Web;
4 |
5 | public static class Program
6 | {
7 | public static void Main(string[] args)
8 | {
9 | WebHost.CreateDefaultBuilder(args)
10 | .UseStartup().Build().Run();
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/Web/Startup.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 |
3 | namespace SimpleGPIO.Examples.Web;
4 |
5 | public sealed class Startup
6 | {
7 | public IConfiguration Configuration { get; }
8 |
9 | public Startup(IConfiguration configuration) => Configuration = configuration;
10 |
11 | public static void ConfigureServices(IServiceCollection services)
12 | {
13 | services.AddMvc();
14 | services.AddSingleton();
15 | }
16 |
17 | public static void Configure(IApplicationBuilder app)
18 | {
19 | app.UseDefaultFiles();
20 | app.UseStaticFiles();
21 | app.UseRouting();
22 | app.UseEndpoints(c => c.MapControllers());
23 | }
24 | }
--------------------------------------------------------------------------------
/Examples/Web/Web.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net9.0
4 | enable
5 | enable
6 | SimpleGPIO.Examples.Web
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Examples/Web/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Examples/Web/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*"
8 | }
9 |
--------------------------------------------------------------------------------
/Examples/Web/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SimpleGPIO Web Example
6 |
7 |
8 |
9 |
10 |
11 |
SimpleGPIO Web Example
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 ecoAPM LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SimpleGPIO
2 |
3 | A simple, low-ceremony GPIO library for all your IoT needs
4 |
5 | [](https://nuget.org/packages/SimpleGPIO)
6 | [](https://github.com/ecoAPM/SimpleGPIO/actions/workflows/CI.yml)
7 | [](https://sonarcloud.io/dashboard?id=ecoAPM_SimpleGPIO)
8 |
9 | [](https://sonarcloud.io/dashboard?id=ecoAPM_SimpleGPIO)
10 | [](https://sonarcloud.io/dashboard?id=ecoAPM_SimpleGPIO)
11 | [](https://sonarcloud.io/dashboard?id=ecoAPM_SimpleGPIO)
12 |
13 | ## Overview
14 |
15 | `SimpleGPIO` takes a high-level, object-oriented approach to IoT programming, in the same way that high-level programming languages provide features that help abstract what's happening on the metal away from the code.
16 |
17 | ## Requirements
18 |
19 | `SimpleGPIO` is a .NET Standard 2.0 library, and therefore should work with all actively supported .NET runtimes.
20 |
21 | ## Installation
22 |
23 | Simply add the `SimpleGPIO` library to your project from NuGet.
24 |
25 | ## Initialization
26 |
27 | Instantiate a new board to be able to access its GPIO header:
28 | ```C#
29 | var pi = new RaspberryPi();
30 | ```
31 |
32 | If you're using a dependency injection container, you can register the board as a singleton to be used elsewhere in the application:
33 | ```C#
34 | services.AddSingleton();
35 | ```
36 |
37 | ## Accessing GPIO pins
38 |
39 | GPIO pins can be accessed by both their physical location on the board, and/or their Broadcom identifier GPIO#.
40 | ```C#
41 | var redLED = pi.Pin16;
42 | var sameRedLED = pi.GPIO23;
43 | ```
44 |
45 | ## Moving electrons
46 |
47 | `SimpleGPIO` provides many ways to turn power on or off, depending on your preferences.
48 |
49 | The simplest way is to use the built-in helper methods:
50 | ```C#
51 | redLED.TurnOn();
52 | redLED.TurnOff();
53 | ```
54 |
55 | If you prefer assigning values:
56 | ```C#
57 | redLED.Power = PowerValue.On;
58 | redLED.Power = PowerValue.Off;
59 | ```
60 |
61 | At the lowest level, you can directly set the voltage going out of the pin:
62 | ```C#
63 | redLED.Voltage = Voltage.High; //on
64 | redLED.Voltage = Voltage.Low; //off
65 | ```
66 |
67 | ### Power Modes
68 |
69 | All of the above examples assume the default `Direct` power mode, where the positive terminal of the LED is connected to the GPIO pin, and the negative terminal is connected to the ground pin.
70 |
71 | If, instead, you want to supply constant power by, e.g. the 3v3 pin, and the have the GPIO pin supply (or not supply) resistance, you can use the `Differential` power mode, where `PowerValue.On == Voltage.Low` and `PowerValue.Off == Voltage.High`:
72 | ```C#
73 | var yellowLED = pi.Pin18;
74 | yellowLED.PowerMode = PowerMode.Differential;
75 | yellowLED.TurnOn();
76 | ```
77 |
78 | ## Timed Power
79 |
80 | Pins can be turned on or off for specific lengths of time via the following:
81 | ```C#
82 | var led = pi.Pin18;
83 | led.TurnOnFor(TimeSpan.FromSeconds(1)); //will turn off after 1 second
84 |
85 | led.TurnOn();
86 | led.TurnOffFor(TimeSpan.FromSeconds(0.5)); //will turn back on after 0.5 seconds
87 | ```
88 |
89 | ## Techno Dance Parties
90 |
91 | There are some helper methods for toggling values. If power is currently on, toggling it will turn it off; if power is off, toggling will turn it on:
92 | ```C#
93 | redLED.Toggle();
94 | ```
95 |
96 | If you want to repeat the toggle at a given frequency, for a set amount of time, pass in the frequency and a `TimeSpan` as parameters:
97 | ```C#
98 | redLED.Toggle(3, TimeSpan.FromSeconds(5));
99 | ```
100 | This will flash the red LED 3 times per second, for 5 seconds.
101 |
102 | Alternatively, you can toggle power a set number of times by passing in a number as the second parameter. The following will flash the red LED 3 times over 1.5 seconds:
103 | ```C#
104 | redLED.Toggle(2, 3);
105 | ```
106 |
107 | ### Pulse Width Modulation
108 |
109 | Pins can have their "strength" set using Pulse Width Modulation (PWM) via the `Strength` property on the pin:
110 | ```C#
111 | var led = pi.Pin18;
112 | led.Strength = 50; //valid range: 0-100
113 | ```
114 |
115 | There are also several helper methods to make smooth transitions easier:
116 | ```C#
117 | led.FadeIn(TimeSpan.FromSeconds(1));
118 | led.FadeOut(TimeSpan.FromSeconds(2));
119 | led.FadeTo(50,TimeSpan.FromSeconds(0.5));
120 |
121 | led.Pulse(TimeSpan.FromSeconds(1));
122 | led.Pulse(50, TimeSpan.FromSeconds(1));
123 | ```
124 |
125 | ## What about inputs?
126 |
127 | Input components such as buttons can be declared the same way as output components, and the `Power` and `Voltage` can be read from the new variable:
128 | ```C#
129 | var button = pi.Pin11;
130 | var isPressed = button.Power == PowerValue.On;
131 | ```
132 |
133 | The `Direct` Power Mode for an input component expects power from e.g. the 3v3 pin, so that electricity flows through to the GPIO pin when the button is depressed.
134 |
135 | ## Reacting to Change
136 |
137 | Three methods are provided on a pin that accept an `Action` as a parameter, so that when that pin's state changes, some subsequent steps can be performed:
138 | ```C#
139 | var button = pi.Pin11;
140 | var redLED = pi.Pin16;
141 | var buzzer = pi.Pin18;
142 |
143 | button.OnPowerOn(() => redLED.TurnOn());
144 | button.OnPowerOff(() => redLED.TurnOff());
145 | redLED.OnPowerChange(() => buzzer.Toggle(1, 1));
146 | ```
147 |
148 | Whenever the button is pressed down, the LED will turn on. When the button is released, the LED will turn off. Whenever the LED turns on or off, the buzzer will beep for half a second (reminder why: because Toggle will complete a single cycle at 1Hz, which means 0.5s on, then 0.5s off).
149 |
150 | ## Cleaning up
151 |
152 | If you want to turn off everything that was turned on while your application was running, simply `Dispose()` of your `RaspberryPi` at the end of your code.
153 |
154 | ```C#
155 | pi.Dispose();
156 | ```
157 |
158 | This will turn off and close all open GPIO pins. As with all `IDisposable`s, this also works if you wrap the `RaspberryPi` you're using in a `using(){}` block.
159 |
160 | ## Components
161 |
162 | Several components have been implemented to help make development easier.
163 |
164 | ### RGB LED
165 |
166 | The RGB LED contains a separate pin for red, green, and blue, which can be combined to show different colors.
167 |
168 | ```C#
169 | var redPin = pi.Pin11;
170 | var greenPin = pi.Pin16;
171 | var bluePin = pi.Pin18;
172 |
173 | var rgbLED = new RGBLED(redPin, greenPin, bluePin);
174 | ```
175 |
176 | Colors can then be set using the `SetColor()` method:
177 | ```C#
178 | rgbLED.SetColor(Color.Red);
179 | rgbLED.SetColor(Color.Yellow);
180 | rgbLED.SetColor(Color.Purple);
181 | ```
182 |
183 | Several helpers also exist:
184 | ```C#
185 | rgbLED.FadeTo(Color.White, TimeSpan.FromSeconds(1));
186 | rgbLED.Pulse(Color.Green, TimeSpan.FromSeconds(0.5));
187 | rgbLED.TurnOff(); // same as rgbLED.SetColor(Color.Black);
188 | ```
189 |
190 | See [the example](Examples/Components/RGBLED/Program.cs) for more details.
191 |
192 | ### Rotary Encoder
193 |
194 | Rotary encoders have actions that can be performed when the dial is turned.
195 |
196 | ```C#
197 | var dial = new RotaryEncoder(clockPin, dataPin);
198 | dial.OnIncrease(() => Console.WriteLine("up"));
199 | dial.OnDecrease(() => Console.WriteLine("down"));
200 | ```
201 |
202 | Built-in button functionality is not yet supported.
203 |
204 | See [the example](Examples/Components/RotartEncoder/Program.cs) for more details.
205 |
206 | ### Seven-Segment Display
207 |
208 | Seven-segment displays are currently supported for direct connections to GPIO pins (support for shift register input coming soon) and can be passed a character (all ASCII letters, numbers, and several other symbols)
209 |
210 | ```C#
211 | var segments = new PinSet
212 | {
213 | Center = centerPin,
214 | UpperLeft = upperLeftPin,
215 | Top = topPin,
216 | UpperRight = upperRightPin,
217 | LowerLeft = lowerLeftPin,
218 | Bottom = bottomPin,
219 | LowerRight = lowerRightPin,
220 | Decimal = decimalPin //optional
221 | };
222 | var display = new SevenSegmentDisplay(segments);
223 | display.Show('A');
224 | display.Show('B');
225 | display.Show('C');
226 | display.Show('1');
227 | display.Show('2');
228 | display.Show('3');
229 | ```
230 |
231 | Custom characters can also be displayed passing a `PowerSet` to `SetPowerValues()`:
232 |
233 | ```C#
234 | var custom = new PowerSet
235 | {
236 | Center = PowerValue.On,
237 | UpperLeft = PowerValue.Off,
238 | Top = PowerValue.On,
239 | UpperRight = PowerValue.Off,
240 | LowerLeft = PowerValue.On,
241 | Bottom = PowerValue.Off,
242 | LowerRight = PowerValue.On,
243 | Decimal = PowerValue.Off //optional
244 | };
245 | display.SetPowerValues(custom);
246 | ```
247 |
248 | See [the example](Examples/Components/SevenSegmentDisplay/Program.cs) for more details.
249 |
250 | ### Dot Matrix Display
251 |
252 | The dot matrix display required 16 inputs, one for each row and one for each column. Like the seven segment display, these are initialized via a `PinSet`, with pins numbered counter-clockwise starting from the bottom left:
253 | ```C#
254 | var set = new DotMatrix.PinSet
255 | {
256 | //rows
257 | Pin1 = pi.GPIO5, Pin2 = pi.GPIO7, Pin3 = pi.GPIO12, Pin4 = pi.GPIO13, Pin5 = pi.GPIO8, Pin6 = pi.GPIO15, Pin7 = pi.GPIO6, Pin8 = pi.GPIO3,
258 |
259 | //columns
260 | Pin9 = pi.GPIO1, Pin10 = pi.GPIO14, Pin11 = pi.GPIO16, Pin12 = pi.GPIO4, Pin13 = pi.GPIO11, Pin14 = pi.GPIO2, Pin15 = pi.GPIO17, Pin16 = pi.GPIO18
261 | };
262 | var matrix = new DotMatrix(set);
263 | ```
264 |
265 | You can then set rows or columns individually or all together:
266 | ```C#
267 | matrix.SetAllRows(PowerValue.On);
268 | matrix.SetAllColumns(PowerValue.Off);
269 |
270 | matrix.SetRows(new DotMatrix.PowerSet { ... });
271 | matrix.SetColumns(new DotMatrix.PowerSet { ... });
272 | ```
273 |
274 | See [the example](Examples/Components/DotMatrix/Program.cs) for more details.
275 |
276 | ### Bidirectional Motor
277 |
278 | The wiring required to safely run a motor is rather complicated. The code, however, can be quite eloquent. The `Motor` component assumes an L293D-compatible driver.
279 |
280 | ```C#
281 | var enabledPin = pi.Pin11;
282 | var clockwisePin = pi.Pin13; //name assumes connected to L293D pin 1A
283 | var counterclockwisePin = pi.Pin15; // name assumes connected to L293D pin 2A
284 | var motor = new Motor(enabledPin, clockwisePin, counterclockwisePin);
285 | motor.Direction = Rotation.Clockwise;
286 | motor.Start();
287 | motor.Stop();
288 |
289 | motor.Direction = Rotation.Counterclockwise;
290 | motor.Start();
291 | motor.Coast();
292 |
293 | motor.TurnClockwise();
294 | motor.TurnCounterclockwise();
295 | motor.TurnClockwiseFor(TimeSpan.FromSeconds(1));
296 | motor.TurnCounterclockwiseFor(TimeSpan.FromSeconds(2), true); //optional parameter to coast instead of stop
297 | ```
298 |
299 | If you never need to coast (only stop) and your `enabled` pin is always on (e.g. 3.3 or 5V), you can pass `null` as the first constructor parameter.
300 |
301 | If using all 4 inputs on a single driver, declare another `Motor` to handle inputs 3 and 4.
302 |
303 | To drive a single-direction motor (by only having input 1 connected), simply pass `null` as the `counterclockwisePin` to the `Motor` constructor. Counterclockwise methods are not expected to function under this condition.
304 |
305 | See [the example](Examples/Components/Motor/Program.cs) for more details.
306 |
307 | ### Shift Register
308 |
309 | A shift register allows you to control more outputs than you have inputs. The `ShiftRegister` component abstracts the implementation details of the 595-style integrated circuit away, so you can simply send the data you want as a `byte`!
310 |
311 | ```C#
312 | var enabledPin = pi.Pin11;
313 | var dataPin = pi.Pin13;
314 | var shiftPin = pi.Pin15;
315 | var outputPin = pi.Pin16; //aka "latch"
316 | var clearPin = pi.Pin18;
317 | var register = new ShiftRegister(enabledPin, dataPin, shiftPin, outputPin, clearPin);
318 |
319 | register.SetValue(255); //sets all 8 bits to On
320 | register.SetValue(0b11111111); //does the same
321 |
322 | //these two are also identical to each other
323 | register.SetValue(0b10101010);
324 | var values = new PowerSet
325 | {
326 | A = PowerValue.On,
327 | B = PowerValue.Off,
328 | C = PowerValue.On,
329 | D = PowerValue.Off,
330 | E = PowerValue.On,
331 | F = PowerValue.Off,
332 | G = PowerValue.On,
333 | H = PowerValue.Off
334 | };
335 | register.SetPowerValues(values);
336 |
337 | //you can also accomplish this more manually
338 | dataPin.TurnOn();
339 | shiftPin.Spike();
340 | dataPin.TurnOff();
341 | shiftPin.Spike();
342 | outputPin.Spike();
343 | ```
344 |
345 | Similar to the `Motor` component, the `enabled` and `clear` parameters are optional; if you choose to have the register always on/enabled by connecting it to the 3.3 or 5V rail, set the first param to `null`. If you never need to clear, and that pin is also connected to 3.3 or 5V, just leave the last param out.
346 |
347 | See [the example](Examples/Components/ShiftRegister/Program.cs) for more details.
348 |
349 | ## "Wow, this is great! How can I help?"
350 |
351 | First, thank you for your enthusiasm! I'd love feedback on how you felt using this. If you had an awesome experience, let me know on [Twitter](https://twitter.com/intent/tweet?text=.@stevedesmond_ca&hashtags=SimpleGPIO). If you had any problems, feel free to [file an issue](https://github.com/stevedesmond-ca/SimpleGPIO/issues/new).
352 |
353 | If you're looking to contribute code, but don't have any ideas of your own, there are some specific things I'd love help with over in the Issues tab!
354 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Security updates are generally only applied to the newest release, and backported on an as-needed basis where appropriate.
6 |
7 | ## Reporting a Vulnerability
8 |
9 | Unless a vulnerability is deemed critical or exposes PII, please create an issue in this repository using the "Report a Bug" template.
10 |
11 | For critical vulnerabilities, or those that expose PII, please email info@ecoAPM.com so that the issue can be fixed confidentially, prior to public disclosure.
12 |
--------------------------------------------------------------------------------
/SimpleGPIO.Tests/Boards/BroadcomBoardTests.cs:
--------------------------------------------------------------------------------
1 | using NSubstitute;
2 | using SimpleGPIO.GPIO;
3 | using SimpleGPIO.Tests.GPIO;
4 | using Xunit;
5 |
6 | namespace SimpleGPIO.Tests.Boards;
7 |
8 | public sealed class BroadcomBoardTests
9 | {
10 | [Fact]
11 | public void CanGetPinInterface()
12 | {
13 | //arrange
14 | var newPin = Substitute.For>();
15 | var board = new BroadcomStub(newPin);
16 |
17 | //act
18 | var pin0 = board.GPIO0;
19 |
20 | //assert
21 | Assert.IsAssignableFrom(pin0);
22 | }
23 |
24 | [Fact]
25 | public void PinInterfaceIsCached()
26 | {
27 | //arrange
28 | var newPin = Substitute.For>();
29 | var board = new BroadcomStub(newPin);
30 | var pin0 = board.GPIO0;
31 |
32 | //act
33 | var pin1 = board.GPIO0;
34 |
35 | //assert
36 | Assert.Equal(pin0, pin1);
37 | newPin.Received(1).Invoke(0);
38 | }
39 |
40 | [Fact]
41 | public void PinsAreMappedCorrectly()
42 | {
43 | //arrange
44 | var newPin = Substitute.For>();
45 | newPin.Invoke(Arg.Any()).Returns(p => new PinStub(p.Arg()));
46 | var board = new BroadcomStub(newPin);
47 | var pins = new PinStub[28];
48 |
49 | //act
50 | for (var x = 0; x < 28; x++)
51 | pins[x] = (PinStub)board.GetType().GetProperty($"GPIO{x}")?.GetValue(board)!;
52 |
53 | //assert
54 | for (var x = 0; x < 28; x++)
55 | Assert.Equal(x, pins[x].Pin);
56 | }
57 |
58 | [Fact]
59 | public void DisposesAllPins()
60 | {
61 | //arrange
62 | var newPin = Substitute.For>();
63 | var pin = Substitute.For();
64 | newPin.Invoke(Arg.Any()).Returns(_ => pin);
65 | var board = new BroadcomStub(newPin);
66 | for (var x = 0; x < 28; x++)
67 | board.GetType().GetProperty($"GPIO{x}")?.GetValue(board);
68 |
69 | //act
70 | board.Dispose();
71 |
72 | //assert
73 | pin.Received(28).Dispose();
74 | }
75 | }
--------------------------------------------------------------------------------
/SimpleGPIO.Tests/Boards/BroadcomStub.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Boards;
2 | using SimpleGPIO.GPIO;
3 |
4 | namespace SimpleGPIO.Tests.Boards;
5 |
6 | internal sealed class BroadcomStub : BroadcomBoard
7 | {
8 | public BroadcomStub(Func pinInterfaceFactory) : base(pinInterfaceFactory)
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/SimpleGPIO.Tests/Boards/RaspberryPiTests.cs:
--------------------------------------------------------------------------------
1 | using NSubstitute;
2 | using SimpleGPIO.Boards;
3 | using SimpleGPIO.GPIO;
4 | using SimpleGPIO.Tests.GPIO;
5 | using Xunit;
6 |
7 | namespace SimpleGPIO.Tests.Boards;
8 |
9 | public sealed class RaspberryPiTests
10 | {
11 | // courtesy of the "pinout" python command
12 | // with some regex magic
13 | [Theory]
14 | [InlineData(3, 2)]
15 | [InlineData(5, 3)]
16 | [InlineData(7, 4)]
17 | [InlineData(8, 14)]
18 | [InlineData(10, 15)]
19 | [InlineData(11, 17)]
20 | [InlineData(12, 18)]
21 | [InlineData(13, 27)]
22 | [InlineData(15, 22)]
23 | [InlineData(16, 23)]
24 | [InlineData(18, 24)]
25 | [InlineData(19, 10)]
26 | [InlineData(21, 9)]
27 | [InlineData(22, 25)]
28 | [InlineData(23, 11)]
29 | [InlineData(24, 8)]
30 | [InlineData(26, 7)]
31 | [InlineData(27, 0)]
32 | [InlineData(28, 1)]
33 | [InlineData(29, 5)]
34 | [InlineData(31, 6)]
35 | [InlineData(32, 12)]
36 | [InlineData(33, 13)]
37 | [InlineData(35, 19)]
38 | [InlineData(36, 16)]
39 | [InlineData(37, 26)]
40 | [InlineData(38, 20)]
41 | [InlineData(40, 21)]
42 | public void PinsAreMappedCorrectly(byte physical, byte bcm)
43 | {
44 | //arrange
45 | var newPin = Substitute.For>();
46 | newPin.Invoke(Arg.Any()).Returns(p => new PinStub(p.Arg()));
47 | var board = new RaspberryPi(newPin);
48 |
49 | //act
50 | var pin = (PinStub)board.GetType().GetProperty($"Pin{physical}")?.GetValue(board)!;
51 |
52 | //assert
53 | Assert.Equal(bcm, pin.Pin);
54 | }
55 | }
--------------------------------------------------------------------------------
/SimpleGPIO.Tests/Components/DotMatrixTests.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Components;
2 | using SimpleGPIO.Power;
3 | using SimpleGPIO.Tests.GPIO;
4 | using Xunit;
5 |
6 | namespace SimpleGPIO.Tests.Components;
7 |
8 | public sealed class DotMatrixTests
9 | {
10 | private static DotMatrix.PinSet StubPinSet(PinStub[] pins)
11 | {
12 | return new DotMatrix.PinSet
13 | {
14 | Pin1 = pins[1],
15 | Pin2 = pins[2],
16 | Pin3 = pins[3],
17 | Pin4 = pins[4],
18 | Pin5 = pins[5],
19 | Pin6 = pins[6],
20 | Pin7 = pins[7],
21 | Pin8 = pins[8],
22 |
23 | Pin9 = pins[9],
24 | Pin10 = pins[10],
25 | Pin11 = pins[11],
26 | Pin12 = pins[12],
27 | Pin13 = pins[13],
28 | Pin14 = pins[14],
29 | Pin15 = pins[15],
30 | Pin16 = pins[16]
31 | };
32 | }
33 |
34 | private static PinStub[] StubPins()
35 | {
36 | const byte numPins = 16;
37 | var pins = new PinStub[numPins + 1];
38 | for (byte x = 1; x <= numPins; x++)
39 | pins[x] = new PinStub(x);
40 |
41 | return pins;
42 | }
43 |
44 | [Fact]
45 | public void RowsAndColumnsAreSetFromInput()
46 | {
47 | //arrange
48 | var pins = StubPins();
49 | var pinSet = StubPinSet(pins);
50 |
51 | //act
52 | var matrix = new DotMatrix(pinSet);
53 |
54 | //assert
55 | Assert.Equal(pins[9], matrix.Row[0]);
56 | Assert.Equal(pins[14], matrix.Row[1]);
57 | Assert.Equal(pins[8], matrix.Row[2]);
58 | Assert.Equal(pins[12], matrix.Row[3]);
59 | Assert.Equal(pins[1], matrix.Row[4]);
60 | Assert.Equal(pins[7], matrix.Row[5]);
61 | Assert.Equal(pins[2], matrix.Row[6]);
62 | Assert.Equal(pins[5], matrix.Row[7]);
63 |
64 | Assert.Equal(pins[13], matrix.Column[0]);
65 | Assert.Equal(pins[3], matrix.Column[1]);
66 | Assert.Equal(pins[4], matrix.Column[2]);
67 | Assert.Equal(pins[10], matrix.Column[3]);
68 | Assert.Equal(pins[6], matrix.Column[4]);
69 | Assert.Equal(pins[11], matrix.Column[5]);
70 | Assert.Equal(pins[15], matrix.Column[6]);
71 | Assert.Equal(pins[16], matrix.Column[7]);
72 | }
73 |
74 | [Fact]
75 | public void CanTurnOnAllRows()
76 | {
77 | //arrange
78 | var pins = StubPins();
79 | var pinSet = StubPinSet(pins);
80 | var matrix = new DotMatrix(pinSet);
81 |
82 | //act
83 | matrix.SetAllRows(PowerValue.On);
84 |
85 | //assert
86 | for (var x = 0; x < 8; x++)
87 | Assert.Equal(PowerValue.On, matrix.Row[x].Power);
88 | }
89 |
90 | [Fact]
91 | public void CanTurnOnAllColumns()
92 | {
93 | //arrange
94 | var pins = StubPins();
95 | var pinSet = StubPinSet(pins);
96 | var matrix = new DotMatrix(pinSet);
97 |
98 | //act
99 | matrix.SetAllColumns(PowerValue.On);
100 |
101 | //assert
102 | for (var x = 0; x < 8; x++)
103 | Assert.Equal(PowerValue.On, matrix.Column[x].Power);
104 | }
105 |
106 | [Fact]
107 | public void CanTurnOnAllPins()
108 | {
109 | //arrange
110 | var pins = StubPins();
111 | var pinSet = StubPinSet(pins);
112 | var matrix = new DotMatrix(pinSet);
113 |
114 | //act
115 | matrix.SetAll(PowerValue.On);
116 |
117 | //assert
118 | for (var x = 0; x < 8; x++)
119 | {
120 | Assert.Equal(PowerValue.On, matrix.Row[x].Power);
121 | Assert.Equal(PowerValue.On, matrix.Column[x].Power);
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/SimpleGPIO.Tests/Components/MotorTests.cs:
--------------------------------------------------------------------------------
1 | using SimpleGPIO.Components;
2 | using SimpleGPIO.Power;
3 | using SimpleGPIO.Tests.GPIO;
4 | using Xunit;
5 |
6 | namespace SimpleGPIO.Tests.Components;
7 |
8 | public sealed class MotorTests
9 | {
10 | [Fact]
11 | public void StartWhenClockwiseSetsInputsCorrectly()
12 | {
13 | //arrange
14 | var enabled = new PinStub(1);
15 | var clockwise = new PinStub(2);
16 | var counterclockwise = new PinStub(3);
17 | var motor = new Motor(enabled, clockwise, counterclockwise)
18 | {
19 | Direction = Motor.Rotation.Clockwise
20 | };
21 |
22 | //act
23 | motor.Start();
24 |
25 | //assert
26 | Assert.Equal(PowerValue.On, enabled.Power);
27 | Assert.Equal(PowerValue.On, clockwise.Power);
28 | Assert.Equal(PowerValue.Off, counterclockwise.Power);
29 | }
30 |
31 | [Fact]
32 | public void StartWhenCounterclockwiseSetsInputsCorrectly()
33 | {
34 | //arrange
35 | var enabled = new PinStub(1);
36 | var clockwise = new PinStub(2);
37 | var counterclockwise = new PinStub(3);
38 | var motor = new Motor(enabled, clockwise, counterclockwise)
39 | {
40 | Direction = Motor.Rotation.Counterclockwise
41 | };
42 |
43 | //act
44 | motor.Start();
45 |
46 | //assert
47 | Assert.Equal(PowerValue.On, enabled.Power);
48 | Assert.Equal(PowerValue.Off, clockwise.Power);
49 | Assert.Equal(PowerValue.On, counterclockwise.Power);
50 | }
51 |
52 | [Fact]
53 | public void TurnClockwiseSetsDirection()
54 | {
55 | //arrange
56 | var enabled = new PinStub(1);
57 | var clockwise = new PinStub(2);
58 | var counterclockwise = new PinStub(3);
59 | var motor = new Motor(enabled, clockwise, counterclockwise)
60 | {
61 | Direction = Motor.Rotation.Counterclockwise
62 | };
63 |
64 | //act
65 | motor.TurnClockwise();
66 |
67 | //assert
68 | Assert.Equal(Motor.Rotation.Clockwise, motor.Direction);
69 | }
70 |
71 | [Fact]
72 | public void TurnCounterclockwiseSetsDirection()
73 | {
74 | //arrange
75 | var enabled = new PinStub(1);
76 | var clockwise = new PinStub(2);
77 | var counterclockwise = new PinStub(3);
78 | var motor = new Motor(enabled, clockwise, counterclockwise)
79 | {
80 | Direction = Motor.Rotation.Clockwise
81 | };
82 |
83 | //act
84 | motor.TurnCounterclockwise();
85 |
86 | //assert
87 | Assert.Equal(Motor.Rotation.Counterclockwise, motor.Direction);
88 | }
89 |
90 | [Fact]
91 | public async Task RunForStopsWhenDone()
92 | {
93 | //arrange
94 | var enabled = new PinStub(1);
95 | var clockwise = new PinStub(2);
96 | var counterclockwise = new PinStub(3);
97 | var motor = new Motor(enabled, clockwise, counterclockwise);
98 |
99 | //act
100 | await motor.RunFor(TimeSpan.Zero);
101 |
102 | //assert
103 | Assert.Equal(PowerValue.Off, clockwise.Power);
104 | Assert.Equal(PowerValue.Off, counterclockwise.Power);
105 | }
106 |
107 | [Fact]
108 | public async Task RunForCanCoastWhenDone()
109 | {
110 | //arrange
111 | var enabled = new PinStub(1);
112 | var clockwise = new PinStub(2);
113 | var counterclockwise = new PinStub(3);
114 | var motor = new Motor(enabled, clockwise, counterclockwise);
115 |
116 | //act
117 | await motor.RunFor(TimeSpan.Zero, true);
118 |
119 | //assert
120 | Assert.Equal(PowerValue.Off, enabled.Power);
121 | Assert.Equal(PowerValue.On, clockwise.Power);
122 | Assert.Equal(PowerValue.Off, counterclockwise.Power);
123 | }
124 |
125 | [Fact]
126 | public async Task TurnClockwiseForSetsDirection()
127 | {
128 | //arrange
129 | var enabled = new PinStub(1);
130 | var clockwise = new PinStub(2);
131 | var counterclockwise = new PinStub(3);
132 | var motor = new Motor(enabled, clockwise, counterclockwise);
133 |
134 | //act
135 | await motor.TurnClockwiseFor(TimeSpan.Zero);
136 |
137 | //assert
138 | Assert.Equal(Motor.Rotation.Clockwise, motor.Direction);
139 | }
140 |
141 | [Fact]
142 | public async Task TurnCounterclockwiseForSetsDirection()
143 | {
144 | //arrange
145 | var enabled = new PinStub(1);
146 | var clockwise = new PinStub(2);
147 | var counterclockwise = new PinStub(3);
148 | var motor = new Motor(enabled, clockwise, counterclockwise);
149 |
150 | //act
151 | await motor.TurnCounterclockwiseFor(TimeSpan.Zero);
152 |
153 | //assert
154 | Assert.Equal(Motor.Rotation.Counterclockwise, motor.Direction);
155 | }
156 |
157 | [Fact]
158 | public void StopTurnsOffAllPins()
159 | {
160 | //arrange
161 | var enabled = new PinStub(1);
162 | var clockwise = new PinStub(2);
163 | var counterclockwise = new PinStub(3);
164 | var motor = new Motor(enabled, clockwise, counterclockwise);
165 | motor.Start();
166 |
167 | //act
168 | motor.Stop();
169 |
170 | //assert
171 | Assert.Equal(PowerValue.Off, enabled.Power);
172 | Assert.Equal(PowerValue.Off, clockwise.Power);
173 | Assert.Equal(PowerValue.Off, counterclockwise.Power);
174 | }
175 |
176 | [Fact]
177 | public void CoastKeepsInputsOn()
178 | {
179 | //arrange
180 | var enabled = new PinStub(1);
181 | var clockwise = new PinStub(2);
182 | var counterclockwise = new PinStub(3);
183 | var motor = new Motor(enabled, clockwise, counterclockwise);
184 | motor.Start();
185 |
186 | //act
187 | motor.Coast();
188 |
189 | //assert
190 | Assert.Equal(PowerValue.Off, enabled.Power);
191 | Assert.Equal(PowerValue.On, clockwise.Power);
192 | Assert.Equal(PowerValue.Off, counterclockwise.Power);
193 | }
194 | }
--------------------------------------------------------------------------------
/SimpleGPIO.Tests/Components/RGBLEDTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Drawing;
3 | using SimpleGPIO.Components;
4 | using SimpleGPIO.Power;
5 | using SimpleGPIO.Tests.GPIO;
6 | using Xunit;
7 |
8 | namespace SimpleGPIO.Tests.Components;
9 |
10 | public sealed class RGBLEDTests
11 | {
12 | [Theory]
13 | [ClassData(typeof(ColorData))]
14 | public void ColorsMatch(Color color, double r, double g, double b)
15 | {
16 | //arrange
17 | var red = new PinStub(1);
18 | var green = new PinStub(2);
19 | var blue = new PinStub(3);
20 | var led = new RGBLED(red, green, blue);
21 |
22 | //act
23 | led.SetColor(color);
24 |
25 | //assert
26 | Assert.Equal(r, red.Strength);
27 | Assert.Equal(g, green.Strength);
28 | Assert.Equal(b, blue.Strength);
29 |
30 | Assert.Equal(r > 0 ? PowerValue.On : PowerValue.Off, red.Power);
31 | Assert.Equal(g > 0 ? PowerValue.On : PowerValue.Off, green.Power);
32 | Assert.Equal(b > 0 ? PowerValue.On : PowerValue.Off, blue.Power);
33 | }
34 |
35 | [Fact]
36 | public void TurnOffTurnsOffAllPins()
37 | {
38 | //arrange
39 | var red = new PinStub(1);
40 | var green = new PinStub(2);
41 | var blue = new PinStub(3);
42 | var led = new RGBLED(red, green, blue);
43 |
44 | //act
45 | led.TurnOff();
46 |
47 | //assert
48 | Assert.Equal(PowerValue.Off, red.Power);
49 | Assert.Equal(PowerValue.Off, green.Power);
50 | Assert.Equal(PowerValue.Off, blue.Power);
51 | }
52 |
53 | [Fact]
54 | public async Task CanFadeToColor()
55 | {
56 | //arrange
57 | var red = new PinStub(1);
58 | var green = new PinStub(2);
59 | var blue = new PinStub(3);
60 | var led = new RGBLED(red, green, blue);
61 |
62 | //act
63 | var ecoGreen = Color.FromArgb(83, 141, 67);
64 | await led.FadeTo(ecoGreen, TimeSpan.Zero);
65 |
66 | //assert
67 | Assert.Equal(32.5, red.Strength, 1);
68 | Assert.Equal(55.3, green.Strength, 1);
69 | Assert.Equal(26.3, blue.Strength, 1);
70 | }
71 |
72 | [Fact]
73 | public async Task CanFadeOutToBlack()
74 | {
75 | //arrange
76 | var red = new PinStub(1);
77 | var green = new PinStub(2);
78 | var blue = new PinStub(3);
79 | var led = new RGBLED(red, green, blue);
80 |
81 | //act
82 | await led.FadeOut(TimeSpan.Zero);
83 |
84 | //assert
85 | Assert.Equal(PowerValue.Off, red.Power);
86 | Assert.Equal(PowerValue.Off, green.Power);
87 | Assert.Equal(PowerValue.Off, blue.Power);
88 | }
89 |
90 | [Fact]
91 | public async Task PulseFadesOutCompletely()
92 | {
93 | //arrange
94 | var red = new PinStub(1);
95 | var green = new PinStub(2);
96 | var blue = new PinStub(3);
97 | var led = new RGBLED(red, green, blue);
98 |
99 | //act
100 | await led.Pulse(Color.White, TimeSpan.Zero);
101 |
102 | //assert
103 | Assert.Equal(PowerValue.Off, red.Power);
104 | Assert.Equal(PowerValue.Off, green.Power);
105 | Assert.Equal(PowerValue.Off, blue.Power);
106 | }
107 |
108 | private sealed class ColorData : IEnumerable