├── .github └── workflows │ └── develop-ci.yml ├── .gitignore ├── Contributing.md ├── Design ├── Enclosure │ ├── STLs_For_Printing │ │ ├── PL_D-Pad_2.stl │ │ ├── PL_Enclosure_Bottom.stl │ │ ├── PL_Enclosure_Bottom_w_DIN_Mount.stl │ │ └── PL_Enclosure_Top.stl │ └── Source │ │ ├── PLv3 Enclosure w DIN Mounts.f3z │ │ └── PLv3 Enclosure.f3z ├── meadow-projectlab.jpg ├── project-lab-samples.png ├── project-lab-specs.jpg ├── project-lab-v3-enclosure.jpg ├── projectlab-pcb.jpg ├── projectlab-pinout-v1.jpg ├── projectlab-pinout-v2.jpg ├── projectlab-pinout-v3.jpg └── projectlab-store.jpg ├── Hardware ├── v1.e │ └── Schematic_v1.e.pdf ├── v2.e │ ├── BOM_v2.e.csv │ ├── Gerbers_v2.e.zip │ └── Schematic_v2.e.pdf ├── v3.d │ └── Schematic.pdf ├── v3.e │ ├── BOM.csv │ ├── EasyEDA_Source.json │ └── Schematic.pdf ├── v4.a │ └── Schematic_v4a.pdf └── v4.b │ └── Schematic_v4b.pdf ├── LICENSE ├── README.md └── Source ├── Directory.Packages.props ├── Meadow.ProjectLab.sln ├── Meadow.ProjectLab ├── ConnectorProviderV3.cs ├── ConnectorProviderV3e.cs ├── Constants.cs ├── DisplayConnector.cs ├── IConnectorProvider.cs ├── IOTerminalConnector.cs ├── IProjectLabHardware.cs ├── Meadow.ProjectLab.csproj ├── ProjectLab.cs ├── ProjectLabHardwareBase.cs ├── ProjectLabHardwareV1.cs ├── ProjectLabHardwareV2.cs ├── ProjectLabHardwareV3.cs ├── ProjectLabHardwareV4.cs ├── ProjectLabModbusRtuClient.cs └── icon.png └── ProjectLab_Demo ├── .vscode └── launch.config ├── DisplayController.cs ├── MeadowApp.cs ├── ProjectLab_Demo.csproj ├── app.build.yaml └── app.config.yaml /.github/workflows/develop-ci.yml: -------------------------------------------------------------------------------- 1 | name: Develop Build 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: [ develop ] 7 | push: 8 | branches: [ develop ] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: windows-latest 14 | 15 | steps: 16 | - name: Checkout Meadow.Logging 17 | uses: actions/checkout@v4 18 | with: 19 | repository: WildernessLabs/Meadow.Logging 20 | path: Meadow.Logging 21 | ref: develop 22 | 23 | - name: Checkout Meadow.Units 24 | uses: actions/checkout@v4 25 | with: 26 | repository: WildernessLabs/Meadow.Units 27 | path: Meadow.Units 28 | ref: develop 29 | 30 | - name: Checkout Meadow.Contracts 31 | uses: actions/checkout@v4 32 | with: 33 | repository: WildernessLabs/Meadow.Contracts 34 | path: Meadow.Contracts 35 | ref: develop 36 | 37 | - name: Checkout Meadow.Core 38 | uses: actions/checkout@v4 39 | with: 40 | repository: WildernessLabs/Meadow.Core 41 | path: Meadow.Core 42 | ref: develop 43 | 44 | - name: Checkout MQTTnet 45 | uses: actions/checkout@v4 46 | with: 47 | repository: WildernessLabs/MQTTnet 48 | path: MQTTnet 49 | ref: develop 50 | 51 | - name: Checkout Meadow.Modbus 52 | uses: actions/checkout@v4 53 | with: 54 | repository: WildernessLabs/Meadow.Modbus 55 | path: Meadow.Modbus 56 | ref: develop 57 | 58 | - name: Checkout Meadow.Foundation 59 | uses: actions/checkout@v4 60 | with: 61 | repository: WildernessLabs/Meadow.Foundation 62 | path: Meadow.Foundation 63 | ref: develop 64 | 65 | - name: Checkout Meadow.ProjectLab 66 | uses: actions/checkout@v4 67 | with: 68 | path: Meadow.ProjectLab 69 | 70 | - name: Setup .NET SDK 71 | uses: actions/setup-dotnet@v1 72 | with: 73 | dotnet-version: 74 | 8.0.x 75 | 76 | - name: Build Meadow.ProjectLab 77 | run: dotnet build -c Release Meadow.ProjectLab/Source/Meadow.ProjectLab.sln 78 | -------------------------------------------------------------------------------- /.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/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | **/.idea/ 399 | *.sln.iml 400 | 401 | # BeatPulse healthcheck temp database 402 | healthchecksdb 403 | 404 | # MacOS Stuff 405 | .DS_Store 406 | *.DS_Store 407 | **/.DS_Store 408 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contribute to Meadow.ProjectLab 2 | 3 | **Meadow.ProjectLab** is an open-source project by [Wilderness Labs](https://www.wildernesslabs.co/) and we encourage community feedback and contributions. 4 | 5 | ## How to Contribute 6 | 7 | - **Found a bug?** [Report an issue](https://github.com/WildernessLabs/Meadow_Issues/issues) 8 | - Have a **feature idea or driver request?** [Open a new feature request](https://github.com/WildernessLabs/Meadow_Issues/issues) 9 | - Want to **contribute code?** Fork the [Meadow.ProjectLab](https://github.com/WildernessLabs/Meadow.ProjectLab) repository and submit a pull request against the `develop` branch 10 | 11 | ## Pull Requests 12 | 13 | 1. All PRs should target the `develop` branch on the Meadow.ProjectLab repository. 14 | 2. All new public or protected classes, methods, and properties need XML comment documentation. 15 | 3. Please try to follow the existing coding patterns and practices. 16 | 17 | ## Pull Request Steps 18 | 19 | 1. Fork the repository 20 | 2. Clone your fork locally: `git clone https://github.com/WildernessLabs/Meadow.ProjectLab` 21 | 3. Switch to the `develop` branch 22 | 4. Create a new branch: `git checkout -b feature/your-contribution` 23 | 5. Make your changes and commit: `git commit -m 'Added/Updated [feature/fix]` 24 | 6. Push to your fork: `git push origin feature/your-contribution` 25 | 7. Open a pull request at [Meadow.ProjectLab/pulls](https://github.com/WildernessLabs/Meadow.ProjectLab/pulls) targetting the `develop` branch 26 | ## Need Help? 27 | 28 | If you have questions or need assistance, please join the Wilderness Labs [community on Slack](http://slackinvite.wildernesslabs.co/). 29 | -------------------------------------------------------------------------------- /Design/Enclosure/STLs_For_Printing/PL_D-Pad_2.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/Enclosure/STLs_For_Printing/PL_D-Pad_2.stl -------------------------------------------------------------------------------- /Design/Enclosure/STLs_For_Printing/PL_Enclosure_Bottom.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/Enclosure/STLs_For_Printing/PL_Enclosure_Bottom.stl -------------------------------------------------------------------------------- /Design/Enclosure/STLs_For_Printing/PL_Enclosure_Bottom_w_DIN_Mount.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/Enclosure/STLs_For_Printing/PL_Enclosure_Bottom_w_DIN_Mount.stl -------------------------------------------------------------------------------- /Design/Enclosure/STLs_For_Printing/PL_Enclosure_Top.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/Enclosure/STLs_For_Printing/PL_Enclosure_Top.stl -------------------------------------------------------------------------------- /Design/Enclosure/Source/PLv3 Enclosure w DIN Mounts.f3z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/Enclosure/Source/PLv3 Enclosure w DIN Mounts.f3z -------------------------------------------------------------------------------- /Design/Enclosure/Source/PLv3 Enclosure.f3z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/Enclosure/Source/PLv3 Enclosure.f3z -------------------------------------------------------------------------------- /Design/meadow-projectlab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/meadow-projectlab.jpg -------------------------------------------------------------------------------- /Design/project-lab-samples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/project-lab-samples.png -------------------------------------------------------------------------------- /Design/project-lab-specs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/project-lab-specs.jpg -------------------------------------------------------------------------------- /Design/project-lab-v3-enclosure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/project-lab-v3-enclosure.jpg -------------------------------------------------------------------------------- /Design/projectlab-pcb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/projectlab-pcb.jpg -------------------------------------------------------------------------------- /Design/projectlab-pinout-v1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/projectlab-pinout-v1.jpg -------------------------------------------------------------------------------- /Design/projectlab-pinout-v2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/projectlab-pinout-v2.jpg -------------------------------------------------------------------------------- /Design/projectlab-pinout-v3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/projectlab-pinout-v3.jpg -------------------------------------------------------------------------------- /Design/projectlab-store.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Design/projectlab-store.jpg -------------------------------------------------------------------------------- /Hardware/v1.e/Schematic_v1.e.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v1.e/Schematic_v1.e.pdf -------------------------------------------------------------------------------- /Hardware/v2.e/BOM_v2.e.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v2.e/BOM_v2.e.csv -------------------------------------------------------------------------------- /Hardware/v2.e/Gerbers_v2.e.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v2.e/Gerbers_v2.e.zip -------------------------------------------------------------------------------- /Hardware/v2.e/Schematic_v2.e.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v2.e/Schematic_v2.e.pdf -------------------------------------------------------------------------------- /Hardware/v3.d/Schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v3.d/Schematic.pdf -------------------------------------------------------------------------------- /Hardware/v3.e/BOM.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v3.e/BOM.csv -------------------------------------------------------------------------------- /Hardware/v3.e/Schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v3.e/Schematic.pdf -------------------------------------------------------------------------------- /Hardware/v4.a/Schematic_v4a.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v4.a/Schematic_v4a.pdf -------------------------------------------------------------------------------- /Hardware/v4.b/Schematic_v4b.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Hardware/v4.b/Schematic_v4b.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Meadow.ProjectLab, C#, iot 2 | 3 | # Meadow.ProjectLab 4 | 5 | Project Lab is the most functional IoT prototyping platform on the planet. No more breadboards, complicated wiring, or soldering. Project Lab was built from the ground up using the industry's most powerful, capable, and reliable sensors, components, and connectors. 6 | 7 | ## Contents 8 | * [Purchasing or Building](#purchasing-or-building) 9 | * [Getting Started](#getting-started) 10 | * [Hardware Specifications](#hardware-specifications) 11 | * [Pinout Diagram](#pinout-diagram) 12 | * [Project Lab v3.e](#project-lab-v3e) 13 | * [Project Lab v2.e](#project-lab-v2e) 14 | * [Project Lab v1.e](#project-lab-v1e) 15 | * [Industrial Enclosure Design](#industrial-enclosure-design) 16 | * [Additional Samples](#additional-samples) 17 | * [Support](#support) 18 | 19 | ## Purchasing or Building 20 | 21 | 22 | 23 | 26 | 29 | 30 | 31 | 34 | 37 | 38 |
24 | ProjectLab, C#, iot 25 | 27 | ProjectLab, C#, iot 28 |
32 | You can get a Project Lab fully assembled from the Wilderness Labs store. 33 | 35 | It's also designed so that it can be assembled at home for the adventurous. All design files can be found in the Hardware Design folder. 36 |
39 | 40 | ## Getting Started 41 | 42 | To make using the hardware even simpler, we've created a Nuget package that instantiates and encapsulates the onboard hardware into a `ProjectLab` class. 43 | 44 | 1. Add the ProjectLab Nuget package your project: 45 | - `dotnet add package Meadow.ProjectLab`, or 46 | - [Meadow.ProjectLab Nuget Package](https://www.nuget.org/packages/Meadow.ProjectLab) 47 | - [Explore in Fuget.org](https://www.fuget.org/packages/Meadow.ProjectLab/0.1.0/lib/netstandard2.1/ProjectLab.dll/Meadow.Devices/ProjectLab) 48 | 49 | 2. Instantiate the `ProjectLab` class: 50 | ```csharp 51 | public class MeadowApp : App 52 | { 53 | IProjectLabHardware projLab; 54 | 55 | public override Task Initialize() 56 | { 57 | projLab = ProjectLab.Create(); 58 | ... 59 | ``` 60 | 61 | 3. To Access the `Project Lab` onboard peripherals: 62 | ```csharp 63 | if (projLab.EnvironmentalSensor is { } bme688) 64 | { 65 | bme688.Updated += Bme688Updated; 66 | bme688.StartUpdating(TimeSpan.FromSeconds(5)); 67 | } 68 | ``` 69 | 70 | 4. To use an I2C peripheral (with a [Grove Character display](https://wiki.seeedstudio.com/Grove-16x2_LCD_Series) as an example): 71 | ```csharp 72 | var display = new CharacterDisplay 73 | ( 74 | i2cBus: projLab.I2cBus, 75 | address: (byte)I2cCharacterDisplay.Addresses.Grove, 76 | rows: 2, columns: 16, 77 | isGroveDisplay: true 78 | ); 79 | ``` 80 | 81 | ## Hardware Specifications 82 | 83 | project-lab, specs, iot, dotnet 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
Onboard PeripheralsConnectivity
ILI9341 - SPI 320x240 color displayMikroBUS - Two sets of MikroBUS pin headers
BMI270 - I2C motion and acceleration sensorQwiic - Stemma QT I2C connector
BH1750 - I2C light sensorGrove - Analog header
BME688 - I2C atmospheric sensorGrove - GPIO/serial header
Push Button - 4 momentary buttonsRS-485 - Serial
Magnetic Audio Transducer - High quality piezo speakerPorts - 3.3V, 5V, ground, one analog and two GPIO ports
115 | 116 | You can find the schematics and other design files in the [Hardware folder](/Hardware). 117 | 118 | ## Pinout Diagram 119 | 120 | Check the diagrams below to see what pins on the Meadow are connected to every peripheral on board and its connectors: 121 |   122 | 123 | ### Project Lab v3.e 124 | 125 | project-lab-v3, pinout, iot, dotnet 126 | 127 | ### Project Lab v2.e 128 | 129 | project-lab-v2, specs, iot, dotnet 130 | 131 | ### Project Lab v1.e 132 | 133 | project-lab-v1, specs, iot, dotnet 134 | 135 | ## Industrial Enclosure Design 136 | 137 | The enclosure was designed in Autodesk Fusion 360. The source file can be found and STL files can be found [here](Design/Enclosure/STLs_For_Printing/). 138 | 139 | project-lab, enclosure, 3d-print, iot 140 | 141 | ## Additional Samples 142 | 143 | 1. **[Setup your Meadow Build Environment](http://developer.wildernesslabs.co/Meadow/Getting_Started/Deploying_Meadow/)** - If you haven't deployed a Meadow app before, you'll need to setup your IDE extension(s), deploy Meadow.OS, etc. 144 | 2. **[Run the Demo App](Source/ProjectLab_Demo)** - Deploy the Project Lab demonstration app to see the built in peripherals at work. 145 | 3. **[Check out the Project Lab Samples](https://github.com/WildernessLabs/Meadow.ProjectLab.Samples)** - We recommend cloning the [Meadow.ProjectLab.Samples](https://github.com/WildernessLabs/Meadow.ProjectLab.Samples) repo. There you'll find a bunch of awesome samples that you can run right out-of-the box! 146 | 147 | project-lab, iot, project, samples 148 | 149 | 150 | ## Support 151 | 152 | Having trouble building/running these projects? 153 | * File an [issue](https://github.com/WildernessLabs/Meadow.Desktop.Samples/issues) with a repro case to investigate, and/or 154 | * Join our [public Slack](http://slackinvite.wildernesslabs.co/), where we have an awesome community helping, sharing and building amazing things using Meadow. -------------------------------------------------------------------------------- /Source/Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32929.385 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.ProjectLab", "Meadow.ProjectLab\Meadow.ProjectLab.csproj", "{A263ECD8-AA98-4E1C-B404-EC360584362B}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectLab_Demo", "ProjectLab_Demo\ProjectLab_Demo.csproj", "{C9A1FF4C-7210-4EA6-874E-44BE198B2790}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_External", "_External", "{580CAC8F-38C5-4DEA-AAEA-415F5D08982B}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Foundation.Core", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Core\Meadow.Foundation.Core.csproj", "{37AAB07A-1F40-4660-97A5-D24890ED7F27}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Contracts", "..\..\Meadow.Contracts\Source\Meadow.Contracts\Meadow.Contracts.csproj", "{5FA51D52-0F2C-460A-B0E7-3168A11A1CF0}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Displays.TftSpi", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\Displays.TftSpi\Driver\Displays.TftSpi.csproj", "{2ECCF2AC-44A7-436E-A832-E2A370C3317D}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICs.IOExpanders.Mcp23xxx", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\ICs.IOExpanders.Mcp23xxx\Driver\ICs.IOExpanders.Mcp23xxx.csproj", "{FD0FF1E2-C933-4CFD-84A3-6F4F72C7F048}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Atmospheric.Bme68x", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\Sensors.Atmospheric.Bme68x\Driver\Sensors.Atmospheric.Bme68x.csproj", "{F0217A3C-41FC-49A0-8481-64853464D5AB}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Light.Bh1750", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\Sensors.Light.Bh1750\Driver\Sensors.Light.Bh1750.csproj", "{CD675B1D-BC88-4DB5-AF45-8079435F52CA}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Motion.Bmi270", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\Sensors.Motion.Bmi270\Driver\Sensors.Motion.Bmi270.csproj", "{95770BEF-6864-493C-A8DA-660D3AC582A0}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Graphics.MicroGraphics", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Libraries_and_Frameworks\Graphics.MicroGraphics\Driver\Graphics.MicroGraphics.csproj", "{DB4C8FC0-18A8-4735-933B-D1FCE59FC1E3}" 27 | EndProject 28 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Modbus", "..\..\Meadow.Modbus\src\Meadow.Modbus\Meadow.Modbus.csproj", "{2D1FC26C-C80D-42E8-92BD-25BE517AC663}" 29 | EndProject 30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Core", "..\..\Meadow.Core\Source\Meadow.Core\Meadow.Core.csproj", "{18EE7297-3E8D-4B94-B6E3-319991927D58}" 31 | EndProject 32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Logging", "..\..\Meadow.Logging\Source\Meadow.Logging\lib\Meadow.Logging.csproj", "{E56BD47C-4997-4831-833C-EA35C83F89B7}" 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Units", "..\..\Meadow.Units\Source\Meadow.Units\Meadow.Units.csproj", "{35B3DDB4-71EF-4A68-B242-982C71126F3E}" 35 | EndProject 36 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.F7", "..\..\Meadow.Core\Source\implementations\f7\Meadow.F7\Meadow.F7.csproj", "{EF2050F4-6422-4AB2-BF60-9E5428E474AF}" 37 | EndProject 38 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Audio.MicroAudio", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Libraries_and_Frameworks\Audio.MicroAudio\Driver\Audio.MicroAudio.csproj", "{12C58A2F-9D0E-417F-B138-BA25B166C56B}" 39 | EndProject 40 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "..\..\MQTTnet\Source\MQTTnet\MQTTnet.csproj", "{B51B10FB-6577-4C06-B7B2-82D2ACCB31B2}" 41 | EndProject 42 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICs.IOExpanders.Sc16is7x2", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\ICs.IOExpanders.Sc16is7x2\Driver\ICs.IOExpanders.Sc16is7x2.csproj", "{20AFC6E4-46E9-4CBB-A8E2-69BF4CD2039C}" 43 | EndProject 44 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Hid.Xpt2046", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\Sensors.Hid.Xpt2046\Driver\Sensors.Hid.Xpt2046.csproj", "{6089ABC8-BCBB-4B81-BC6A-D6B4963E5BD2}" 45 | EndProject 46 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Graphics.MicroLayout", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Libraries_and_Frameworks\Graphics.MicroLayout\Driver\Graphics.MicroLayout.csproj", "{04A557DE-8136-4AE9-88F0-159D4AB085B8}" 47 | EndProject 48 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serialization.MicroJson", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Libraries_and_Frameworks\Serialization.MicroJson\Driver\Serialization.MicroJson.csproj", "{888F0CDA-2D29-49F2-AF31-898998B3D8F4}" 49 | EndProject 50 | Global 51 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 52 | Debug|Any CPU = Debug|Any CPU 53 | Release|Any CPU = Release|Any CPU 54 | EndGlobalSection 55 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 56 | {A263ECD8-AA98-4E1C-B404-EC360584362B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {A263ECD8-AA98-4E1C-B404-EC360584362B}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {A263ECD8-AA98-4E1C-B404-EC360584362B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 59 | {A263ECD8-AA98-4E1C-B404-EC360584362B}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {A263ECD8-AA98-4E1C-B404-EC360584362B}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {A263ECD8-AA98-4E1C-B404-EC360584362B}.Release|Any CPU.Deploy.0 = Release|Any CPU 62 | {C9A1FF4C-7210-4EA6-874E-44BE198B2790}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {C9A1FF4C-7210-4EA6-874E-44BE198B2790}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {C9A1FF4C-7210-4EA6-874E-44BE198B2790}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 65 | {C9A1FF4C-7210-4EA6-874E-44BE198B2790}.Release|Any CPU.ActiveCfg = Debug|Any CPU 66 | {C9A1FF4C-7210-4EA6-874E-44BE198B2790}.Release|Any CPU.Build.0 = Debug|Any CPU 67 | {C9A1FF4C-7210-4EA6-874E-44BE198B2790}.Release|Any CPU.Deploy.0 = Debug|Any CPU 68 | {37AAB07A-1F40-4660-97A5-D24890ED7F27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {37AAB07A-1F40-4660-97A5-D24890ED7F27}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {37AAB07A-1F40-4660-97A5-D24890ED7F27}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 71 | {37AAB07A-1F40-4660-97A5-D24890ED7F27}.Release|Any CPU.ActiveCfg = Release|Any CPU 72 | {37AAB07A-1F40-4660-97A5-D24890ED7F27}.Release|Any CPU.Build.0 = Release|Any CPU 73 | {37AAB07A-1F40-4660-97A5-D24890ED7F27}.Release|Any CPU.Deploy.0 = Release|Any CPU 74 | {5FA51D52-0F2C-460A-B0E7-3168A11A1CF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 75 | {5FA51D52-0F2C-460A-B0E7-3168A11A1CF0}.Debug|Any CPU.Build.0 = Debug|Any CPU 76 | {5FA51D52-0F2C-460A-B0E7-3168A11A1CF0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 77 | {5FA51D52-0F2C-460A-B0E7-3168A11A1CF0}.Release|Any CPU.ActiveCfg = Release|Any CPU 78 | {5FA51D52-0F2C-460A-B0E7-3168A11A1CF0}.Release|Any CPU.Build.0 = Release|Any CPU 79 | {5FA51D52-0F2C-460A-B0E7-3168A11A1CF0}.Release|Any CPU.Deploy.0 = Release|Any CPU 80 | {2ECCF2AC-44A7-436E-A832-E2A370C3317D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 81 | {2ECCF2AC-44A7-436E-A832-E2A370C3317D}.Debug|Any CPU.Build.0 = Debug|Any CPU 82 | {2ECCF2AC-44A7-436E-A832-E2A370C3317D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 83 | {2ECCF2AC-44A7-436E-A832-E2A370C3317D}.Release|Any CPU.ActiveCfg = Release|Any CPU 84 | {2ECCF2AC-44A7-436E-A832-E2A370C3317D}.Release|Any CPU.Build.0 = Release|Any CPU 85 | {2ECCF2AC-44A7-436E-A832-E2A370C3317D}.Release|Any CPU.Deploy.0 = Release|Any CPU 86 | {FD0FF1E2-C933-4CFD-84A3-6F4F72C7F048}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 87 | {FD0FF1E2-C933-4CFD-84A3-6F4F72C7F048}.Debug|Any CPU.Build.0 = Debug|Any CPU 88 | {FD0FF1E2-C933-4CFD-84A3-6F4F72C7F048}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 89 | {FD0FF1E2-C933-4CFD-84A3-6F4F72C7F048}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {FD0FF1E2-C933-4CFD-84A3-6F4F72C7F048}.Release|Any CPU.Build.0 = Release|Any CPU 91 | {FD0FF1E2-C933-4CFD-84A3-6F4F72C7F048}.Release|Any CPU.Deploy.0 = Release|Any CPU 92 | {F0217A3C-41FC-49A0-8481-64853464D5AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 93 | {F0217A3C-41FC-49A0-8481-64853464D5AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 94 | {F0217A3C-41FC-49A0-8481-64853464D5AB}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 95 | {F0217A3C-41FC-49A0-8481-64853464D5AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 96 | {F0217A3C-41FC-49A0-8481-64853464D5AB}.Release|Any CPU.Build.0 = Release|Any CPU 97 | {F0217A3C-41FC-49A0-8481-64853464D5AB}.Release|Any CPU.Deploy.0 = Release|Any CPU 98 | {CD675B1D-BC88-4DB5-AF45-8079435F52CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 99 | {CD675B1D-BC88-4DB5-AF45-8079435F52CA}.Debug|Any CPU.Build.0 = Debug|Any CPU 100 | {CD675B1D-BC88-4DB5-AF45-8079435F52CA}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 101 | {CD675B1D-BC88-4DB5-AF45-8079435F52CA}.Release|Any CPU.ActiveCfg = Release|Any CPU 102 | {CD675B1D-BC88-4DB5-AF45-8079435F52CA}.Release|Any CPU.Build.0 = Release|Any CPU 103 | {CD675B1D-BC88-4DB5-AF45-8079435F52CA}.Release|Any CPU.Deploy.0 = Release|Any CPU 104 | {95770BEF-6864-493C-A8DA-660D3AC582A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 105 | {95770BEF-6864-493C-A8DA-660D3AC582A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 106 | {95770BEF-6864-493C-A8DA-660D3AC582A0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 107 | {95770BEF-6864-493C-A8DA-660D3AC582A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 108 | {95770BEF-6864-493C-A8DA-660D3AC582A0}.Release|Any CPU.Build.0 = Release|Any CPU 109 | {95770BEF-6864-493C-A8DA-660D3AC582A0}.Release|Any CPU.Deploy.0 = Release|Any CPU 110 | {DB4C8FC0-18A8-4735-933B-D1FCE59FC1E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 111 | {DB4C8FC0-18A8-4735-933B-D1FCE59FC1E3}.Debug|Any CPU.Build.0 = Debug|Any CPU 112 | {DB4C8FC0-18A8-4735-933B-D1FCE59FC1E3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 113 | {DB4C8FC0-18A8-4735-933B-D1FCE59FC1E3}.Release|Any CPU.ActiveCfg = Release|Any CPU 114 | {DB4C8FC0-18A8-4735-933B-D1FCE59FC1E3}.Release|Any CPU.Build.0 = Release|Any CPU 115 | {DB4C8FC0-18A8-4735-933B-D1FCE59FC1E3}.Release|Any CPU.Deploy.0 = Release|Any CPU 116 | {2D1FC26C-C80D-42E8-92BD-25BE517AC663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 117 | {2D1FC26C-C80D-42E8-92BD-25BE517AC663}.Debug|Any CPU.Build.0 = Debug|Any CPU 118 | {2D1FC26C-C80D-42E8-92BD-25BE517AC663}.Release|Any CPU.ActiveCfg = Release|Any CPU 119 | {2D1FC26C-C80D-42E8-92BD-25BE517AC663}.Release|Any CPU.Build.0 = Release|Any CPU 120 | {18EE7297-3E8D-4B94-B6E3-319991927D58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 121 | {18EE7297-3E8D-4B94-B6E3-319991927D58}.Debug|Any CPU.Build.0 = Debug|Any CPU 122 | {18EE7297-3E8D-4B94-B6E3-319991927D58}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 123 | {18EE7297-3E8D-4B94-B6E3-319991927D58}.Release|Any CPU.ActiveCfg = Release|Any CPU 124 | {18EE7297-3E8D-4B94-B6E3-319991927D58}.Release|Any CPU.Build.0 = Release|Any CPU 125 | {18EE7297-3E8D-4B94-B6E3-319991927D58}.Release|Any CPU.Deploy.0 = Release|Any CPU 126 | {E56BD47C-4997-4831-833C-EA35C83F89B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 127 | {E56BD47C-4997-4831-833C-EA35C83F89B7}.Debug|Any CPU.Build.0 = Debug|Any CPU 128 | {E56BD47C-4997-4831-833C-EA35C83F89B7}.Release|Any CPU.ActiveCfg = Release|Any CPU 129 | {E56BD47C-4997-4831-833C-EA35C83F89B7}.Release|Any CPU.Build.0 = Release|Any CPU 130 | {35B3DDB4-71EF-4A68-B242-982C71126F3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 131 | {35B3DDB4-71EF-4A68-B242-982C71126F3E}.Debug|Any CPU.Build.0 = Debug|Any CPU 132 | {35B3DDB4-71EF-4A68-B242-982C71126F3E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 133 | {35B3DDB4-71EF-4A68-B242-982C71126F3E}.Release|Any CPU.ActiveCfg = Release|Any CPU 134 | {35B3DDB4-71EF-4A68-B242-982C71126F3E}.Release|Any CPU.Build.0 = Release|Any CPU 135 | {35B3DDB4-71EF-4A68-B242-982C71126F3E}.Release|Any CPU.Deploy.0 = Release|Any CPU 136 | {EF2050F4-6422-4AB2-BF60-9E5428E474AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 137 | {EF2050F4-6422-4AB2-BF60-9E5428E474AF}.Debug|Any CPU.Build.0 = Debug|Any CPU 138 | {EF2050F4-6422-4AB2-BF60-9E5428E474AF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 139 | {EF2050F4-6422-4AB2-BF60-9E5428E474AF}.Release|Any CPU.ActiveCfg = Release|Any CPU 140 | {EF2050F4-6422-4AB2-BF60-9E5428E474AF}.Release|Any CPU.Build.0 = Release|Any CPU 141 | {EF2050F4-6422-4AB2-BF60-9E5428E474AF}.Release|Any CPU.Deploy.0 = Release|Any CPU 142 | {12C58A2F-9D0E-417F-B138-BA25B166C56B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 143 | {12C58A2F-9D0E-417F-B138-BA25B166C56B}.Debug|Any CPU.Build.0 = Debug|Any CPU 144 | {12C58A2F-9D0E-417F-B138-BA25B166C56B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 145 | {12C58A2F-9D0E-417F-B138-BA25B166C56B}.Release|Any CPU.ActiveCfg = Release|Any CPU 146 | {12C58A2F-9D0E-417F-B138-BA25B166C56B}.Release|Any CPU.Build.0 = Release|Any CPU 147 | {12C58A2F-9D0E-417F-B138-BA25B166C56B}.Release|Any CPU.Deploy.0 = Release|Any CPU 148 | {B51B10FB-6577-4C06-B7B2-82D2ACCB31B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 149 | {B51B10FB-6577-4C06-B7B2-82D2ACCB31B2}.Debug|Any CPU.Build.0 = Debug|Any CPU 150 | {B51B10FB-6577-4C06-B7B2-82D2ACCB31B2}.Release|Any CPU.ActiveCfg = Release|Any CPU 151 | {B51B10FB-6577-4C06-B7B2-82D2ACCB31B2}.Release|Any CPU.Build.0 = Release|Any CPU 152 | {20AFC6E4-46E9-4CBB-A8E2-69BF4CD2039C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 153 | {20AFC6E4-46E9-4CBB-A8E2-69BF4CD2039C}.Debug|Any CPU.Build.0 = Debug|Any CPU 154 | {20AFC6E4-46E9-4CBB-A8E2-69BF4CD2039C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 155 | {20AFC6E4-46E9-4CBB-A8E2-69BF4CD2039C}.Release|Any CPU.ActiveCfg = Release|Any CPU 156 | {20AFC6E4-46E9-4CBB-A8E2-69BF4CD2039C}.Release|Any CPU.Build.0 = Release|Any CPU 157 | {20AFC6E4-46E9-4CBB-A8E2-69BF4CD2039C}.Release|Any CPU.Deploy.0 = Release|Any CPU 158 | {6089ABC8-BCBB-4B81-BC6A-D6B4963E5BD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 159 | {6089ABC8-BCBB-4B81-BC6A-D6B4963E5BD2}.Debug|Any CPU.Build.0 = Debug|Any CPU 160 | {6089ABC8-BCBB-4B81-BC6A-D6B4963E5BD2}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 161 | {6089ABC8-BCBB-4B81-BC6A-D6B4963E5BD2}.Release|Any CPU.ActiveCfg = Release|Any CPU 162 | {6089ABC8-BCBB-4B81-BC6A-D6B4963E5BD2}.Release|Any CPU.Build.0 = Release|Any CPU 163 | {6089ABC8-BCBB-4B81-BC6A-D6B4963E5BD2}.Release|Any CPU.Deploy.0 = Release|Any CPU 164 | {04A557DE-8136-4AE9-88F0-159D4AB085B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 165 | {04A557DE-8136-4AE9-88F0-159D4AB085B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 166 | {04A557DE-8136-4AE9-88F0-159D4AB085B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 167 | {04A557DE-8136-4AE9-88F0-159D4AB085B8}.Release|Any CPU.Build.0 = Release|Any CPU 168 | {888F0CDA-2D29-49F2-AF31-898998B3D8F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 169 | {888F0CDA-2D29-49F2-AF31-898998B3D8F4}.Debug|Any CPU.Build.0 = Debug|Any CPU 170 | {888F0CDA-2D29-49F2-AF31-898998B3D8F4}.Release|Any CPU.ActiveCfg = Release|Any CPU 171 | {888F0CDA-2D29-49F2-AF31-898998B3D8F4}.Release|Any CPU.Build.0 = Release|Any CPU 172 | EndGlobalSection 173 | GlobalSection(SolutionProperties) = preSolution 174 | HideSolutionNode = FALSE 175 | EndGlobalSection 176 | GlobalSection(NestedProjects) = preSolution 177 | {37AAB07A-1F40-4660-97A5-D24890ED7F27} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 178 | {5FA51D52-0F2C-460A-B0E7-3168A11A1CF0} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 179 | {2ECCF2AC-44A7-436E-A832-E2A370C3317D} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 180 | {FD0FF1E2-C933-4CFD-84A3-6F4F72C7F048} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 181 | {F0217A3C-41FC-49A0-8481-64853464D5AB} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 182 | {CD675B1D-BC88-4DB5-AF45-8079435F52CA} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 183 | {95770BEF-6864-493C-A8DA-660D3AC582A0} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 184 | {DB4C8FC0-18A8-4735-933B-D1FCE59FC1E3} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 185 | {2D1FC26C-C80D-42E8-92BD-25BE517AC663} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 186 | {18EE7297-3E8D-4B94-B6E3-319991927D58} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 187 | {E56BD47C-4997-4831-833C-EA35C83F89B7} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 188 | {35B3DDB4-71EF-4A68-B242-982C71126F3E} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 189 | {EF2050F4-6422-4AB2-BF60-9E5428E474AF} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 190 | {12C58A2F-9D0E-417F-B138-BA25B166C56B} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 191 | {B51B10FB-6577-4C06-B7B2-82D2ACCB31B2} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 192 | {20AFC6E4-46E9-4CBB-A8E2-69BF4CD2039C} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 193 | {6089ABC8-BCBB-4B81-BC6A-D6B4963E5BD2} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 194 | {04A557DE-8136-4AE9-88F0-159D4AB085B8} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 195 | {888F0CDA-2D29-49F2-AF31-898998B3D8F4} = {580CAC8F-38C5-4DEA-AAEA-415F5D08982B} 196 | EndGlobalSection 197 | GlobalSection(ExtensibilityGlobals) = postSolution 198 | SolutionGuid = {C1A24E3C-F7F9-4EB3-8A7F-5C6AB7C2A43D} 199 | EndGlobalSection 200 | EndGlobal 201 | -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ConnectorProviderV3.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.ICs.IOExpanders; 2 | using Meadow.Hardware; 3 | using Meadow.Modbus; 4 | using System; 5 | 6 | namespace Meadow.Devices; 7 | 8 | internal class ConnectorProviderV3 : IConnectorProvider 9 | { 10 | public ModbusRtuClient GetModbusRtuClient(ProjectLabHardwareBase projLab, int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One) 11 | { 12 | if (Resolver.Device is F7CoreComputeV2) 13 | { 14 | throw new PlatformNotSupportedException("RS485 is not supported on hardware revisions before 3.e"); 15 | } 16 | 17 | throw new NotSupportedException(); 18 | } 19 | 20 | public MikroBusConnector CreateMikroBus1(IF7CoreComputeMeadowDevice device, Mcp23008 mcp2) 21 | { 22 | return new MikroBusConnector( 23 | "MikroBus1", 24 | new PinMapping 25 | { 26 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, device.Pins.PA3), 27 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RST, device.Pins.PH10), 28 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, device.Pins.PB12), 29 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, device.Pins.SPI5_SCK), 30 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, device.Pins.SPI5_CIPO), 31 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, device.Pins.SPI5_COPI), 32 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, device.Pins.PB8), 33 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, device.Pins.PC2), 34 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, device.Pins.PB15), 35 | new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, device.Pins.PB14), 36 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, device.Pins.I2C3_SCL), 37 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, device.Pins.I2C3_SDA), 38 | }, 39 | device.PlatformOS.GetSerialPortName("com1")!, 40 | new I2cBusMapping(device, 3), 41 | new SpiBusMapping(device, device.Pins.SPI5_SCK, device.Pins.SPI5_COPI, device.Pins.SPI5_CIPO) 42 | ); 43 | } 44 | 45 | public MikroBusConnector CreateMikroBus2(IF7CoreComputeMeadowDevice device, Mcp23008 mcp2) 46 | { 47 | return new MikroBusConnector( 48 | "MikroBus2", 49 | new PinMapping 50 | { 51 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, device.Pins.PB0), 52 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RST, mcp2.Pins.GP1), 53 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, mcp2.Pins.GP2), 54 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, device.Pins.SCK), 55 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, device.Pins.CIPO), 56 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, device.Pins.COPI), 57 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, device.Pins.PB9), 58 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, mcp2.Pins.GP3), 59 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, device.Pins.PB15), 60 | new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, device.Pins.PB14), 61 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, device.Pins.I2C1_SCL), 62 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, device.Pins.I2C1_SDA), 63 | }, 64 | device.PlatformOS.GetSerialPortName("com1")!, 65 | new I2cBusMapping(device, 1), 66 | new SpiBusMapping(device, device.Pins.SPI5_SCK, device.Pins.SPI5_COPI, device.Pins.SPI5_CIPO) 67 | ); 68 | } 69 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ConnectorProviderV3e.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.ICs.IOExpanders; 2 | using Meadow.Hardware; 3 | using Meadow.Modbus; 4 | using Meadow.Units; 5 | using System; 6 | 7 | namespace Meadow.Devices; 8 | 9 | internal class ConnectorProviderV3e : IConnectorProvider 10 | { 11 | private readonly Sc16is752? _uartExpander; 12 | private object _mobusSyncRoot = new(); 13 | private ModbusRtuClient? _client; 14 | 15 | public ConnectorProviderV3e(ProjectLabHardwareBase projLab, II2cBus i2CBus) 16 | { 17 | try 18 | { 19 | _uartExpander = new Sc16is752(i2CBus, new Frequency(1.8432, Frequency.UnitType.Megahertz), Sc16is7x2.Addresses.Address_0x4D); 20 | } 21 | catch (Exception ex) 22 | { 23 | Resolver.Log.Error($"Unable to connect to UART expander: {ex.Message}", Constants.LogGroup); 24 | } 25 | } 26 | 27 | public ModbusRtuClient GetModbusRtuClient(ProjectLabHardwareBase projLab, int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One) 28 | { 29 | if (Resolver.Device is not F7CoreComputeV2) throw new NotSupportedException(); 30 | 31 | lock (_mobusSyncRoot) 32 | { 33 | if (_uartExpander == null) 34 | { 35 | throw new Exception("No UART expander available"); 36 | } 37 | 38 | if (_client == null) 39 | { 40 | try 41 | { 42 | Resolver.Log.Info($"Creating 485 port...", Constants.LogGroup); 43 | // v3.e+ uses an SC16is I2C UART expander for the RS485 44 | var port = _uartExpander.PortB.CreateRs485SerialPort(baudRate, dataBits, parity, stopBits, false); 45 | Resolver.Log.Trace($"485 port created", Constants.LogGroup); 46 | _client = new ModbusRtuClient(port); 47 | } 48 | catch (Exception ex) 49 | { 50 | Resolver.Log.Warn($"Error creating 485 port: {ex.Message}", Constants.LogGroup); 51 | throw new Exception("Unable to connect to UART expander"); 52 | } 53 | } 54 | } 55 | 56 | return _client; 57 | } 58 | 59 | public MikroBusConnector CreateMikroBus1(IF7CoreComputeMeadowDevice device, Mcp23008 mcp2) 60 | { 61 | // todo: verify 3.e and later 62 | return new MikroBusConnector( 63 | "MikroBus1", 64 | new PinMapping 65 | { 66 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, device.Pins.PA3), 67 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RST, device.Pins.PH10), 68 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, device.Pins.PB12), 69 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, device.Pins.SPI5_SCK), 70 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, device.Pins.SPI5_CIPO), 71 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, device.Pins.SPI5_COPI), 72 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, device.Pins.PB8), 73 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, device.Pins.PC2), 74 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, device.Pins.PB15), 75 | new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, device.Pins.PB14), 76 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, device.Pins.I2C3_SCL), 77 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, device.Pins.I2C3_SDA), 78 | }, 79 | device.PlatformOS.GetSerialPortName("com1")!, 80 | new I2cBusMapping(device, 3), 81 | new SpiBusMapping(device, device.Pins.SPI5_SCK, device.Pins.SPI5_COPI, device.Pins.SPI5_CIPO) 82 | ); 83 | } 84 | 85 | public MikroBusConnector CreateMikroBus2(IF7CoreComputeMeadowDevice device, Mcp23008 mcp2) 86 | { 87 | return new MikroBusConnector( 88 | "MikroBus2", 89 | new PinMapping 90 | { 91 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, device.Pins.PB0), 92 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RST, mcp2.Pins.GP1), 93 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, mcp2.Pins.GP2), 94 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, device.Pins.SCK), 95 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, device.Pins.CIPO), 96 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, device.Pins.COPI), 97 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, device.Pins.PB9), 98 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, mcp2.Pins.GP3), 99 | // new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, uart1rx), // on the I2C uart and not usable for anything else 100 | // new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, uart1tx), // on the I2C uart and not usable for anything else 101 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, device.Pins.I2C1_SCL), 102 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, device.Pins.I2C1_SDA), 103 | }, 104 | _uartExpander.PortA, 105 | new I2cBusMapping(device, 1), 106 | new SpiBusMapping(device, device.Pins.SCK, device.Pins.COPI, device.Pins.CIPO) 107 | ); 108 | } 109 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Meadow.Devices; 2 | 3 | internal static class Constants 4 | { 5 | public const string LogGroup = "proj_lab"; 6 | } 7 | -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/DisplayConnector.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Units; 2 | using System; 3 | using static Meadow.Hardware.DisplayConnector; 4 | 5 | namespace Meadow.Hardware; 6 | 7 | /// 8 | /// Represents the display connector on Project Lab 9 | /// 10 | public class DisplayConnector : Connector 11 | { 12 | /// 13 | /// The set of Display connector connector pins 14 | /// 15 | public static class PinNames 16 | { 17 | /// 18 | /// Display Chip Select pin 19 | /// 20 | public const string DISPLAY_CS = "DISPLAY_CS"; 21 | /// 22 | /// Display Reset pin 23 | /// 24 | public const string DISPLAY_RST = "DISPLAY_RST"; 25 | /// 26 | /// Display Data/Command pin 27 | /// 28 | public const string DISPLAY_DC = "DISPLAY_DC"; 29 | /// 30 | /// Display SPI Clock pin 31 | /// 32 | public const string DISPLAY_CLK = "DISPLAY_CLK"; 33 | /// 34 | /// Display SPI controller out, peripheral in pin 35 | /// 36 | public const string DISPLAY_COPI = "DISPLAY_COPI"; 37 | /// 38 | /// Display LED (backlight) pin 39 | /// 40 | public const string DISPLAY_LED = "DISPLAY_LED"; 41 | /// 42 | /// Touch screen interrupt pin 43 | /// 44 | public const string TOUCH_INT = "TOUCH_INT"; 45 | /// 46 | /// Touch Chip Select pin 47 | /// 48 | public const string TOUCH_CS = "TOUCH_CS"; 49 | /// 50 | /// Touch SPI Clock pin 51 | /// 52 | public const string TOUCH_CLK = "TOUCH_CLK"; 53 | /// 54 | /// Touch SPI controller out, peripheral in pin 55 | /// 56 | public const string TOUCH_COPI = "TOUCH_COPI"; 57 | /// 58 | /// Touch SPI controller in, peripheral out pin 59 | /// 60 | public const string TOUCH_CIPO = "TOUCH_CIPO"; 61 | } 62 | 63 | /// 64 | /// Represents the pins definitions for the Display connector on Project Lab 65 | /// 66 | public class DisplayConnectorPinDefinitions : PinDefinitionBase 67 | { 68 | private readonly IPin? _csDisplay; 69 | private readonly IPin? _rstDisplay; 70 | private readonly IPin? _dcDisplay; 71 | private readonly IPin? _clkDisplay; 72 | private readonly IPin? _copiDisplay; 73 | private readonly IPin? _ledDisplay; 74 | private readonly IPin? _intTouch; 75 | private readonly IPin? _csTouch; 76 | private readonly IPin? _clkTouch; 77 | private readonly IPin? _copiTouch; 78 | private readonly IPin? _cipoTouch; 79 | 80 | /// 81 | /// Display Chip Select pin 82 | /// 83 | public IPin DISPLAY_CS => _csDisplay ?? throw new PlatformNotSupportedException("Pin not connected"); 84 | /// 85 | /// Display Reset pin 86 | /// 87 | public IPin DISPLAY_RST => _rstDisplay ?? throw new PlatformNotSupportedException("Pin not connected"); 88 | /// 89 | /// Display Data/Command pin 90 | /// 91 | public IPin DISPLAY_DC => _dcDisplay ?? throw new PlatformNotSupportedException("Pin not connected"); 92 | /// 93 | /// Display SPI Clock pin 94 | /// 95 | public IPin DISPLAY_CLK => _clkDisplay ?? throw new PlatformNotSupportedException("Pin not connected"); 96 | /// 97 | /// Display SPI controller out, peripheral in pin 98 | /// 99 | public IPin DISPLAY_COPI => _copiDisplay ?? throw new PlatformNotSupportedException("Pin not connected"); 100 | /// 101 | /// Display LED (backlight) pin 102 | /// 103 | public IPin DISPLAY_LED => _ledDisplay ?? throw new PlatformNotSupportedException("Pin not connected"); 104 | /// 105 | /// Touch interrupt pin 106 | /// 107 | public IPin TOUCH_INT => _intTouch ?? throw new PlatformNotSupportedException("Pin not connected"); 108 | /// 109 | /// Touch chip select pin 110 | /// 111 | public IPin TOUCH_CS => _csTouch ?? throw new PlatformNotSupportedException("Pin not connected"); 112 | /// 113 | /// Touch SPI Clock pin 114 | /// 115 | public IPin TOUCH_CLK => _clkTouch ?? throw new PlatformNotSupportedException("Pin not connected"); 116 | /// 117 | /// Touch SPI controller out, peripheral in pin 118 | /// 119 | public IPin TOUCH_COPI => _copiTouch ?? throw new PlatformNotSupportedException("Pin not connected"); 120 | /// 121 | /// Touch SPI controller in, peripheral out pin 122 | /// 123 | public IPin TOUCH_CIPO => _cipoTouch ?? throw new PlatformNotSupportedException("Pin not connected"); 124 | 125 | internal DisplayConnectorPinDefinitions(PinMapping mapping) 126 | { 127 | foreach (var m in mapping) 128 | { 129 | switch (m.PinName) 130 | { 131 | case PinNames.DISPLAY_CS: 132 | _csDisplay = m.ConnectsTo; 133 | break; 134 | case PinNames.DISPLAY_RST: 135 | _rstDisplay = m.ConnectsTo; 136 | break; 137 | case PinNames.DISPLAY_DC: 138 | _dcDisplay = m.ConnectsTo; 139 | break; 140 | case PinNames.DISPLAY_CLK: 141 | _clkDisplay = m.ConnectsTo; 142 | break; 143 | case PinNames.DISPLAY_COPI: 144 | _copiDisplay = m.ConnectsTo; 145 | break; 146 | case PinNames.DISPLAY_LED: 147 | _ledDisplay = m.ConnectsTo; 148 | break; 149 | case PinNames.TOUCH_INT: 150 | _intTouch = m.ConnectsTo; 151 | break; 152 | case PinNames.TOUCH_CS: 153 | _csTouch = m.ConnectsTo; 154 | break; 155 | case PinNames.TOUCH_CLK: 156 | _clkTouch = m.ConnectsTo; 157 | break; 158 | case PinNames.TOUCH_COPI: 159 | _copiTouch = m.ConnectsTo; 160 | break; 161 | case PinNames.TOUCH_CIPO: 162 | _cipoTouch = m.ConnectsTo; 163 | break; 164 | } 165 | } 166 | } 167 | } 168 | 169 | private readonly SpiBusMapping _spiBusMappingDisplay; 170 | private readonly SpiBusMapping? _spiBusMappingTouch; 171 | private ISpiBus? _spiDisplay; 172 | private readonly ISpiBus? _spiTouch; 173 | 174 | /// The connector name 175 | /// The mappings to the host controller 176 | /// The mapping for the display connector's SPI bus 177 | /// The mapping for the touch connector's SPI bus 178 | public DisplayConnector(string name, PinMapping mapping, SpiBusMapping spiBusMappingDisplay, SpiBusMapping? spiBusMappingTouch = null) 179 | : base(name, new DisplayConnectorPinDefinitions(mapping)) 180 | { 181 | _spiBusMappingDisplay = spiBusMappingDisplay; 182 | _spiBusMappingTouch = spiBusMappingTouch; 183 | } 184 | 185 | /// 186 | /// Gets the display SPI bus 187 | /// 188 | public ISpiBus SpiBusDisplay 189 | => _spiDisplay ??= _spiBusMappingDisplay.Controller.CreateSpiBus(_spiBusMappingDisplay.Clock, _spiBusMappingDisplay.Copi, _spiBusMappingDisplay.Cipo, new Frequency(1, Frequency.UnitType.Megahertz)); 190 | 191 | /// 192 | /// Gets the touch screen SPI bus 193 | /// 194 | public ISpiBus? SpiBusTouch 195 | { 196 | get 197 | { 198 | if (_spiBusMappingTouch == null || _spiTouch == null) 199 | { 200 | return null; 201 | } 202 | return _spiBusMappingTouch.Controller.CreateSpiBus(_spiBusMappingTouch.Clock, _spiBusMappingTouch.Copi, _spiBusMappingTouch.Cipo, new Frequency(1, Frequency.UnitType.Megahertz)); 203 | } 204 | } 205 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/IConnectorProvider.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.ICs.IOExpanders; 2 | using Meadow.Hardware; 3 | using Meadow.Modbus; 4 | 5 | namespace Meadow.Devices; 6 | 7 | internal interface IConnectorProvider 8 | { 9 | ModbusRtuClient GetModbusRtuClient(ProjectLabHardwareBase projLab, int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One); 10 | MikroBusConnector CreateMikroBus1(IF7CoreComputeMeadowDevice device, Mcp23008 mcp2); 11 | MikroBusConnector CreateMikroBus2(IF7CoreComputeMeadowDevice device, Mcp23008 mcp2); 12 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/IOTerminalConnector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static Meadow.Hardware.IOTerminalConnector; 3 | 4 | namespace Meadow.Hardware; 5 | 6 | /// 7 | /// Represents the IO Terminal connector on Project Lab 8 | /// 9 | public class IOTerminalConnector : Connector 10 | { 11 | /// 12 | /// The set of IO terminal connector connector pins 13 | /// 14 | public static class PinNames 15 | { 16 | /// 17 | /// Pin A1 18 | /// 19 | public const string A1 = "A1"; 20 | /// 21 | /// Pin D2 22 | /// 23 | public const string D2 = "D2"; 24 | /// 25 | /// Pin D3 26 | /// 27 | public const string D3 = "D3"; 28 | } 29 | 30 | /// 31 | /// Represents the pins definitions for the IO Terminal connector on Project Lab 32 | /// 33 | public class IOTerminalConnectorPinDefinitions : PinDefinitionBase 34 | { 35 | private readonly IPin? _a1; 36 | private readonly IPin? _d2; 37 | private readonly IPin? _d3; 38 | 39 | /// 40 | /// Pin A1 41 | /// 42 | public IPin A1 => _a1 ?? throw new PlatformNotSupportedException("Pin not connected"); 43 | /// 44 | /// Pin D2 45 | /// 46 | public IPin D2 => _d2 ?? throw new PlatformNotSupportedException("Pin not connected"); 47 | /// 48 | /// Pin D3 49 | /// 50 | public IPin D3 => _d3 ?? throw new PlatformNotSupportedException("Pin not connected"); 51 | 52 | internal IOTerminalConnectorPinDefinitions(PinMapping mapping) 53 | { 54 | foreach (var m in mapping) 55 | { 56 | switch (m.PinName) 57 | { 58 | case PinNames.A1: 59 | _a1 = m.ConnectsTo; 60 | break; 61 | case PinNames.D2: 62 | _d2 = m.ConnectsTo; 63 | break; 64 | case PinNames.D3: 65 | _d3 = m.ConnectsTo; 66 | break; 67 | } 68 | } 69 | } 70 | } 71 | 72 | /// The connector name 73 | /// The mappings to the host controller 74 | public IOTerminalConnector(string name, PinMapping mapping) 75 | : base(name, new IOTerminalConnectorPinDefinitions(mapping)) 76 | { 77 | } 78 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/IProjectLabHardware.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Hardware; 2 | using Meadow.Modbus; 3 | using Meadow.Peripherals.Displays; 4 | using Meadow.Peripherals.Leds; 5 | using Meadow.Peripherals.Sensors; 6 | using Meadow.Peripherals.Sensors.Atmospheric; 7 | using Meadow.Peripherals.Sensors.Buttons; 8 | using Meadow.Peripherals.Sensors.Environmental; 9 | using Meadow.Peripherals.Sensors.Light; 10 | using Meadow.Peripherals.Sensors.Motion; 11 | using Meadow.Peripherals.Speakers; 12 | 13 | namespace Meadow.Devices; 14 | 15 | /// 16 | /// Interface for ProjectLab hardware 17 | /// 18 | public interface IProjectLabHardware : IMeadowAppEmbeddedHardware 19 | { 20 | /// 21 | /// Gets the up button on the Project Lab board. 22 | /// 23 | public IButton? UpButton { get; } 24 | 25 | /// 26 | /// Gets the down button on the Project Lab board. 27 | /// 28 | public IButton? DownButton { get; } 29 | 30 | /// 31 | /// Gets the left button on the Project Lab board. 32 | /// 33 | public IButton? LeftButton { get; } 34 | 35 | /// 36 | /// Gets the right button on the Project Lab board. 37 | /// 38 | public IButton? RightButton { get; } 39 | 40 | /// 41 | /// Gets the piezo speaker on the Project Lab board. 42 | /// 43 | public IToneGenerator? Speaker { get; } 44 | 45 | /// 46 | /// Gets the RGB PWM LED on the Project Lab board. 47 | /// 48 | public IRgbPwmLed? RgbLed { get; } 49 | 50 | /// 51 | /// Gets the light sensor on the Project Lab board. 52 | /// 53 | public ILightSensor? LightSensor { get; } 54 | 55 | /// 56 | /// Gets the ITemperatureSensor on the Project Lab board. 57 | /// 58 | public ISamplingTemperatureSensor? TemperatureSensor { get; } 59 | 60 | /// 61 | /// Gets the second/alternate ITemperatureSensor on the Project Lab board. 62 | /// 63 | public ISamplingTemperatureSensor? TemperatureSensor2 { get; } 64 | 65 | /// 66 | /// Gets the IHumiditySensor on the Project Lab board. 67 | /// 68 | public IHumiditySensor? HumiditySensor { get; } 69 | 70 | /// 71 | /// Gets the IBarometricPressureSensor on the Project Lab board. 72 | /// 73 | public IBarometricPressureSensor? BarometricPressureSensor { get; } 74 | 75 | /// 76 | /// Gets the IGasResistanceSensor on the Project Lab board. 77 | /// 78 | public IGasResistanceSensor? GasResistanceSensor { get; } 79 | 80 | /// 81 | /// Gets the IGyroscope on the Project Lab board 82 | /// 83 | public IGyroscope? Gyroscope { get; } 84 | 85 | /// 86 | /// Gets the IAccelerometer on the Project Lab board 87 | /// 88 | public IAccelerometer? Accelerometer { get; } 89 | 90 | /// 91 | /// Gets the graphics display on the Project Lab board. 92 | /// 93 | public IPixelDisplay? Display { get; } 94 | 95 | /// 96 | /// Gets the revision string of the Project Lab board. 97 | /// 98 | public string RevisionString { get; } 99 | 100 | /// 101 | /// Gets MikroBus connector 1 on the Project Lab board. 102 | /// 103 | public MikroBusConnector MikroBus1 { get; } 104 | 105 | /// 106 | /// Gets MikroBus connector 2 on the Project Lab board. 107 | /// 108 | public MikroBusConnector MikroBus2 { get; } 109 | 110 | /// 111 | /// Gets the Grove Digital connector on the Project Lab board. 112 | /// 113 | public GroveDigitalConnector? GroveDigital { get; } 114 | 115 | /// 116 | /// Gets the Grove Analog connector on the Project Lab board. 117 | /// 118 | public GroveDigitalConnector GroveAnalog { get; } 119 | 120 | /// 121 | /// Gets the Grove UART connector on the Project Lab board. 122 | /// 123 | public UartConnector GroveUart { get; } 124 | 125 | /// 126 | /// Gets the Qwiic connector on the Project Lab board. 127 | /// 128 | public I2cConnector Qwiic { get; } 129 | 130 | /// 131 | /// Gets the IO Terminal connector on the Project Lab board. 132 | /// 133 | public IOTerminalConnector IOTerminal { get; } 134 | 135 | /// 136 | /// Gets the display header connector on the Project Lab board. 137 | /// 138 | public DisplayConnector DisplayHeader { get; } 139 | 140 | /// 141 | /// Gets the touchscreen on the Project Lab display 142 | /// 143 | public ITouchScreen? Touchscreen { get; } 144 | 145 | /// 146 | /// Get a Modbus RTU client with optional parameters. 147 | /// 148 | /// The baud rate. 149 | /// The number of data bits. 150 | /// The parity setting. 151 | /// The stop bits setting. 152 | /// A Modbus RTU client. 153 | public ModbusRtuClient GetModbusRtuClient(int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One); 154 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/Meadow.ProjectLab.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | icon.png 5 | Wilderness Labs, Inc 6 | netstandard2.1 7 | Library 8 | ProjectLab 9 | Wilderness Labs, Inc 10 | https://github.com/WildernessLabs/Meadow.ProjectLab 11 | Meadow.ProjectLab 12 | https://github.com/WildernessLabs/Meadow.ProjectLab 13 | Meadow.ProjectLab,Meadow,ProjectLab,accelerator 14 | true 15 | Library for the Meadow ProjectLab IoT accelerator 16 | enable 17 | 18 | 19 | Apache-2.0 20 | 12 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ProjectLab.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.ICs.IOExpanders; 2 | using Meadow.Hardware; 3 | using Meadow.Logging; 4 | using System; 5 | 6 | namespace Meadow.Devices; 7 | 8 | /// 9 | /// A base class for Feather-based, Project Lab-targeted applications 10 | /// 11 | public abstract class ProjectLabFeatherApp : App 12 | { 13 | } 14 | 15 | /// 16 | /// A base class for F7 Core Compute-based, Project Lab-targeted applications 17 | /// 18 | public abstract class ProjectLabCoreComputeApp : App 19 | { 20 | } 21 | 22 | /// 23 | /// Represents Project Lab hardware and exposes its peripherals 24 | /// 25 | public class ProjectLab : IMeadowAppEmbeddedHardwareProvider 26 | { 27 | private ProjectLab() { } 28 | 29 | /// 30 | /// Create an instance of the ProjectLab class 31 | /// 32 | public static IProjectLabHardware Create() 33 | { 34 | return new ProjectLab() 35 | .Create(Resolver.Services.Get()!); 36 | } 37 | 38 | /// 39 | /// Create an instance of the ProjectLab class 40 | /// 41 | /// ProjectLab instance 42 | /// ProjectLab instance must be created after App.Initialize() 43 | /// Couldn't detect known ProjectLab hardware 44 | public IProjectLabHardware Create(IMeadowDevice device) 45 | { 46 | IProjectLabHardware hardware; 47 | Logger? logger = Resolver.Log; 48 | 49 | Mcp23008? mcp = null; 50 | 51 | logger?.Trace("Initializing Project Lab..."); 52 | 53 | // make sure not getting instantiated before the App Initialize method 54 | if (device == null) 55 | { 56 | var msg = "ProjectLab instance must be created after App.Initialize()"; 57 | logger?.Error(msg); 58 | throw new Exception(msg); 59 | } 60 | 61 | var i2cBus = device.CreateI2cBus(); 62 | logger?.Debug("I2C Bus instantiated"); 63 | 64 | IDigitalInterruptPort? mcpInterrupt = null; 65 | IDigitalOutputPort? mcpReset = null; 66 | bool isV3 = false; 67 | 68 | if (device is IF7FeatherMeadowDevice f) 69 | { 70 | try 71 | { 72 | mcpInterrupt = device.CreateDigitalInterruptPort(f.Pins.D09, InterruptMode.EdgeRising, ResistorMode.InternalPullDown); 73 | mcpReset = device.CreateDigitalOutputPort(f.Pins.D14); 74 | 75 | mcp = new Mcp23008(i2cBus, address: 0x20, mcpInterrupt, mcpReset); 76 | 77 | logger?.Trace("Mcp_1 up"); 78 | } 79 | catch 80 | { 81 | logger?.Debug("Failed to create MCP1: could be a v1 board"); 82 | mcpInterrupt?.Dispose(); 83 | mcpReset?.Dispose(); 84 | } 85 | } 86 | else if (device is IF7CoreComputeMeadowDevice c) 87 | { 88 | try 89 | { 90 | mcpReset = device.CreateDigitalOutputPort(c.Pins.PA10); 91 | 92 | mcp = new Mcp23008(i2cBus, address: 0x27, resetPort: mcpReset); 93 | 94 | logger?.Trace("Mcp_version up"); 95 | isV3 = mcp.ReadFromPorts() < 17; 96 | } 97 | catch 98 | { 99 | logger?.Debug("Failed to create version MCP: could be a v3 board"); 100 | isV3 = true; 101 | } 102 | finally 103 | { 104 | mcpReset?.Dispose(); 105 | mcp = null; 106 | } 107 | } 108 | 109 | switch (device) 110 | { 111 | case IF7FeatherMeadowDevice feather when mcp is null: 112 | logger?.Info("Instantiating Project Lab v1 specific hardware"); 113 | hardware = new ProjectLabHardwareV1(feather, i2cBus); 114 | break; 115 | case IF7FeatherMeadowDevice feather: 116 | logger?.Info("Instantiating Project Lab v2 specific hardware"); 117 | hardware = new ProjectLabHardwareV2(feather, i2cBus, mcp); 118 | break; 119 | case IF7CoreComputeMeadowDevice ccm when isV3 == true: 120 | logger?.Info($"Instantiating Project Lab v3 specific hardware"); 121 | hardware = new ProjectLabHardwareV3(ccm, i2cBus); 122 | break; 123 | case IF7CoreComputeMeadowDevice ccm: 124 | logger?.Info($"Instantiating Project Lab v4 specific hardware"); 125 | hardware = new ProjectLabHardwareV4(ccm, i2cBus); 126 | break; 127 | default: 128 | throw new NotSupportedException(); 129 | } 130 | 131 | return hardware; 132 | } 133 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ProjectLabHardwareBase.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.Sensors.Atmospheric; 2 | using Meadow.Foundation.Sensors.Light; 3 | using Meadow.Foundation.Sensors.Motion; 4 | using Meadow.Hardware; 5 | using Meadow.Logging; 6 | using Meadow.Modbus; 7 | using Meadow.Peripherals.Displays; 8 | using Meadow.Peripherals.Leds; 9 | using Meadow.Peripherals.Sensors; 10 | using Meadow.Peripherals.Sensors.Atmospheric; 11 | using Meadow.Peripherals.Sensors.Buttons; 12 | using Meadow.Peripherals.Sensors.Environmental; 13 | using Meadow.Peripherals.Sensors.Light; 14 | using Meadow.Peripherals.Sensors.Motion; 15 | using Meadow.Peripherals.Speakers; 16 | using System; 17 | 18 | namespace Meadow.Devices; 19 | 20 | /// 21 | /// Contains common elements of Project Lab hardware 22 | /// 23 | public abstract class ProjectLabHardwareBase : IProjectLabHardware 24 | { 25 | private IConnector?[]? _connectors; 26 | private IPixelDisplay? _display; 27 | private ILightSensor? _lightSensor; 28 | private Bme688? _atmosphericSensor; 29 | private Bmi270? _motionSensor; 30 | private IGyroscope? _gyroscope; 31 | private IAccelerometer? _accelerometer; 32 | private ISamplingTemperatureSensor? _temperatureSensor; 33 | private ISamplingTemperatureSensor? _temperatureSensor2; 34 | private IHumiditySensor? _humiditySensor; 35 | private IBarometricPressureSensor? _barometricPressureSensor; 36 | private IGasResistanceSensor? _gasResistanceSensor; 37 | 38 | /// 39 | /// Get a reference to Meadow Logger 40 | /// 41 | protected Logger? Logger { get; } = Resolver.Log; 42 | 43 | /// 44 | public abstract IButton? UpButton { get; } 45 | 46 | /// 47 | public IMeadowDevice ComputeModule { get; } 48 | 49 | /// 50 | public abstract IButton? DownButton { get; } 51 | 52 | /// 53 | public abstract IButton? LeftButton { get; } 54 | 55 | /// 56 | public abstract IButton? RightButton { get; } 57 | 58 | /// 59 | public abstract IToneGenerator? Speaker { get; } 60 | 61 | /// 62 | public abstract IRgbPwmLed? RgbLed { get; } 63 | 64 | /// 65 | public virtual ITouchScreen? Touchscreen => null; 66 | 67 | /// 68 | public ILightSensor? LightSensor => GetLightSensor(); 69 | 70 | /// 71 | public Bme688? AtmosphericSensor => GetAtmosphericSensor(); 72 | 73 | /// 74 | public Bmi270? MotionSensor => GetMotionSensor(); 75 | 76 | /// 77 | public IGyroscope? Gyroscope => GetGyroscope(); 78 | 79 | /// 80 | public IAccelerometer? Accelerometer => GetAccelerometer(); 81 | 82 | /// 83 | public ISamplingTemperatureSensor? TemperatureSensor => GetTemperatureSensor(); 84 | 85 | /// 86 | public ISamplingTemperatureSensor? TemperatureSensor2 => GetTemperatureSensor2(); 87 | 88 | /// 89 | public IHumiditySensor? HumiditySensor => GetHumiditySensor(); 90 | 91 | /// 92 | public IBarometricPressureSensor? BarometricPressureSensor => GetBarometricPressureSensor(); 93 | 94 | /// 95 | public IGasResistanceSensor? GasResistanceSensor => GetGasResistanceSensor(); 96 | 97 | /// 98 | public IPixelDisplay? Display 99 | { 100 | get 101 | { 102 | _display ??= GetDefaultDisplay(); 103 | return _display; 104 | } 105 | set => _display = value; 106 | } 107 | 108 | /// 109 | /// Gets the default display for the Project Lab board. 110 | /// 111 | protected abstract IPixelDisplay? GetDefaultDisplay(); 112 | 113 | /// 114 | public virtual string RevisionString { get; set; } = "unknown"; 115 | 116 | /// 117 | public MikroBusConnector MikroBus1 => (MikroBusConnector)Connectors[0]!; 118 | 119 | /// 120 | public MikroBusConnector MikroBus2 => (MikroBusConnector)Connectors[1]!; 121 | 122 | /// 123 | public GroveDigitalConnector? GroveDigital => (GroveDigitalConnector?)Connectors[2]; 124 | 125 | /// 126 | public GroveDigitalConnector GroveAnalog => (GroveDigitalConnector)Connectors[3]!; 127 | 128 | /// 129 | public UartConnector GroveUart => (UartConnector)Connectors[4]!; 130 | 131 | /// 132 | public I2cConnector Qwiic => (I2cConnector)Connectors[5]!; 133 | 134 | /// 135 | public IOTerminalConnector IOTerminal => (IOTerminalConnector)Connectors[6]!; 136 | 137 | /// 138 | public DisplayConnector DisplayHeader => (DisplayConnector)Connectors[7]!; 139 | 140 | internal abstract MikroBusConnector CreateMikroBus1(); 141 | internal abstract MikroBusConnector CreateMikroBus2(); 142 | internal virtual GroveDigitalConnector? CreateGroveDigitalConnector() 143 | { 144 | return null; 145 | } 146 | 147 | internal abstract GroveDigitalConnector CreateGroveAnalogConnector(); 148 | 149 | internal abstract UartConnector CreateGroveUartConnector(); 150 | 151 | internal abstract I2cConnector CreateQwiicConnector(); 152 | 153 | internal abstract IOTerminalConnector CreateIOTerminalConnector(); 154 | 155 | internal abstract DisplayConnector CreateDisplayConnector(); 156 | 157 | /// 158 | /// Collection of connectors on the Project Lab board 159 | /// 160 | public IConnector?[] Connectors 161 | { 162 | get 163 | { 164 | if (_connectors == null) 165 | { 166 | _connectors = new IConnector[8]; 167 | _connectors[0] = CreateMikroBus1(); 168 | _connectors[1] = CreateMikroBus2(); 169 | _connectors[2] = CreateGroveDigitalConnector(); 170 | _connectors[3] = CreateGroveAnalogConnector(); 171 | _connectors[4] = CreateGroveUartConnector(); 172 | _connectors[5] = CreateQwiicConnector(); 173 | _connectors[6] = CreateIOTerminalConnector(); 174 | _connectors[7] = CreateDisplayConnector(); 175 | } 176 | 177 | return _connectors; 178 | } 179 | } 180 | 181 | private readonly II2cBus _peripheralI2cBus; 182 | 183 | internal ProjectLabHardwareBase(IMeadowDevice compute, II2cBus peripheralI2cBus) 184 | { 185 | ComputeModule = compute; 186 | 187 | _peripheralI2cBus = peripheralI2cBus; 188 | } 189 | 190 | private IAccelerometer? GetAccelerometer() 191 | { 192 | if (_accelerometer == null) 193 | { 194 | InitializeBmi270(); 195 | } 196 | 197 | return _accelerometer; 198 | } 199 | 200 | private IGyroscope? GetGyroscope() 201 | { 202 | if (_gyroscope == null) 203 | { 204 | InitializeBmi270(); 205 | } 206 | 207 | return _gyroscope; 208 | } 209 | 210 | private ISamplingTemperatureSensor? GetTemperatureSensor() 211 | { 212 | if (_temperatureSensor == null) 213 | { 214 | InitializeBmi270(); 215 | } 216 | 217 | return _temperatureSensor; 218 | } 219 | 220 | private ISamplingTemperatureSensor? GetTemperatureSensor2() 221 | { 222 | if (_temperatureSensor2 == null) 223 | { 224 | InitializeBme688(); 225 | } 226 | 227 | return _temperatureSensor2; 228 | } 229 | 230 | private void InitializeBmi270() 231 | { 232 | try 233 | { 234 | Logger?.Trace("Instantiating motion sensor"); 235 | var bmi = new Bmi270(_peripheralI2cBus); 236 | _motionSensor = bmi; 237 | _gyroscope = bmi; 238 | _accelerometer = bmi; 239 | // we use the BMI270 because, I believe, the 688 is closer to an on-board heat source and reads high 240 | _temperatureSensor = bmi; 241 | Resolver.SensorService.RegisterSensor(_motionSensor); 242 | Logger?.Trace("Motion sensor up"); 243 | } 244 | catch (Exception ex) 245 | { 246 | Logger?.Error($"Unable to create the BMI270 IMU: {ex.Message}"); 247 | } 248 | } 249 | 250 | private Bmi270? GetMotionSensor() 251 | { 252 | if (_motionSensor == null) 253 | { 254 | InitializeBmi270(); 255 | } 256 | 257 | return _motionSensor; 258 | } 259 | 260 | private ILightSensor? GetLightSensor() 261 | { 262 | if (_lightSensor == null) 263 | { 264 | try 265 | { 266 | Logger?.Trace("Instantiating light sensor"); 267 | _lightSensor = new Bh1750( 268 | i2cBus: _peripheralI2cBus, 269 | measuringMode: Bh1750.MeasuringModes.ContinuouslyHighResolutionMode, // the various modes take differing amounts of time. 270 | lightTransmittance: 0.5, // lower this to increase sensitivity, for instance, if it's behind a semi opaque window 271 | address: (byte)Bh1750.Addresses.Address_0x23); 272 | Resolver.SensorService.RegisterSensor(_lightSensor); 273 | Logger?.Trace("Light sensor up"); 274 | } 275 | catch (Exception ex) 276 | { 277 | Logger?.Error($"Unable to create the BH1750 light sensor: {ex.Message}"); 278 | } 279 | } 280 | 281 | return _lightSensor; 282 | } 283 | 284 | private Bme688? GetAtmosphericSensor() 285 | { 286 | if (_atmosphericSensor == null) 287 | { 288 | InitializeBme688(); 289 | } 290 | 291 | return _atmosphericSensor; 292 | } 293 | 294 | private IHumiditySensor? GetHumiditySensor() 295 | { 296 | if (_humiditySensor == null) 297 | { 298 | InitializeBme688(); 299 | } 300 | 301 | return _humiditySensor; 302 | } 303 | 304 | private IBarometricPressureSensor? GetBarometricPressureSensor() 305 | { 306 | if (_barometricPressureSensor == null) 307 | { 308 | InitializeBme688(); 309 | } 310 | 311 | return _barometricPressureSensor; 312 | } 313 | 314 | private IGasResistanceSensor? GetGasResistanceSensor() 315 | { 316 | if (_gasResistanceSensor == null) 317 | { 318 | InitializeBme688(); 319 | } 320 | 321 | return _gasResistanceSensor; 322 | } 323 | 324 | private void InitializeBme688() 325 | { 326 | try 327 | { 328 | Logger?.Trace("Instantiating atmospheric sensor"); 329 | var bme = new Bme688(_peripheralI2cBus, (byte)Bme68x.Addresses.Address_0x76); 330 | _atmosphericSensor = bme; 331 | _humiditySensor = bme; 332 | _barometricPressureSensor = bme; 333 | _gasResistanceSensor = bme; 334 | _temperatureSensor2 = bme; 335 | Resolver.SensorService.RegisterSensor(bme); 336 | Logger?.Trace("Atmospheric sensor up"); 337 | } 338 | catch (Exception ex) 339 | { 340 | Logger?.Error($"Unable to create the BME688 atmospheric sensor: {ex.Message}"); 341 | } 342 | } 343 | 344 | /// 345 | /// Gets a ModbusRtuClient for the on-board RS485 connector 346 | /// 347 | public abstract ModbusRtuClient GetModbusRtuClient(int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One); 348 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ProjectLabHardwareV1.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.Audio; 2 | using Meadow.Foundation.Displays; 3 | using Meadow.Foundation.Leds; 4 | using Meadow.Foundation.Sensors.Buttons; 5 | using Meadow.Hardware; 6 | using Meadow.Modbus; 7 | using Meadow.Peripherals.Displays; 8 | using Meadow.Peripherals.Leds; 9 | using Meadow.Peripherals.Sensors.Buttons; 10 | using Meadow.Peripherals.Speakers; 11 | using Meadow.Units; 12 | using System; 13 | using System.Threading; 14 | 15 | namespace Meadow.Devices; 16 | 17 | /// 18 | /// Represents Project Lab V1 hardware and exposes its peripherals 19 | /// 20 | public class ProjectLabHardwareV1 : ProjectLabHardwareBase 21 | { 22 | private readonly IF7FeatherMeadowDevice _device; 23 | private IToneGenerator? _speaker; 24 | private IRgbPwmLed? _rgbled; 25 | private IPixelDisplay? _display; 26 | 27 | private readonly string revision = "v1.x"; 28 | 29 | /// 30 | public override IButton UpButton { get; } 31 | 32 | /// 33 | public override IButton DownButton { get; } 34 | 35 | /// 36 | public override IButton LeftButton { get; } 37 | 38 | /// 39 | public override IButton RightButton { get; } 40 | 41 | /// 42 | public override IToneGenerator? Speaker => GetSpeaker(); 43 | 44 | /// 45 | public override IRgbPwmLed? RgbLed => GetRgbLed(); 46 | 47 | internal ProjectLabHardwareV1(IF7FeatherMeadowDevice device, II2cBus i2cBus) 48 | : base(device, i2cBus) 49 | { 50 | _device = device; 51 | 52 | Logger?.Trace("Instantiating buttons"); 53 | LeftButton = GetPushButton(device.Pins.D10); 54 | RightButton = GetPushButton(device.Pins.D05); 55 | UpButton = GetPushButton(device.Pins.D15); 56 | DownButton = GetPushButton(device.Pins.D02); 57 | Logger?.Trace("Buttons up"); 58 | } 59 | 60 | /// 61 | protected override IPixelDisplay? GetDefaultDisplay() 62 | { 63 | if (_display == null) 64 | { 65 | Logger?.Trace("Instantiating display"); 66 | 67 | var chipSelectPort = DisplayHeader.Pins.DISPLAY_CS.CreateDigitalOutputPort(); 68 | var dcPort = DisplayHeader.Pins.DISPLAY_DC.CreateDigitalOutputPort(); 69 | var resetPort = DisplayHeader.Pins.DISPLAY_RST.CreateDigitalOutputPort(); 70 | Thread.Sleep(50); 71 | 72 | _display = new St7789( 73 | spiBus: DisplayHeader.SpiBusDisplay, 74 | chipSelectPort: chipSelectPort, 75 | dataCommandPort: dcPort, 76 | resetPort: resetPort, 77 | width: 240, height: 240, 78 | colorMode: ColorMode.Format16bppRgb565) 79 | { 80 | SpiBusMode = SpiClockConfiguration.Mode.Mode3, 81 | SpiBusSpeed = new Frequency(24000, Frequency.UnitType.Kilohertz) 82 | }; 83 | ((St7789)_display).SetRotation(RotationType._270Degrees); 84 | 85 | Logger?.Trace("Display up"); 86 | } 87 | 88 | return _display; 89 | } 90 | 91 | private IToneGenerator? GetSpeaker() 92 | { 93 | if (_speaker == null) 94 | { 95 | try 96 | { 97 | Logger?.Trace("Instantiating speaker"); 98 | _speaker = new PiezoSpeaker(_device.Pins.D11); 99 | Logger?.Trace("Speaker up"); 100 | } 101 | catch (Exception ex) 102 | { 103 | Logger?.Error($"Unable to create the Piezo Speaker: {ex.Message}"); 104 | } 105 | } 106 | 107 | return _speaker; 108 | } 109 | 110 | private IRgbPwmLed? GetRgbLed() 111 | { 112 | if (_rgbled == null) 113 | { 114 | try 115 | { 116 | Logger?.Trace("Instantiating RGB LED"); 117 | _rgbled = new RgbPwmLed( 118 | redPwmPin: _device.Pins.OnboardLedRed, 119 | greenPwmPin: _device.Pins.OnboardLedGreen, 120 | bluePwmPin: _device.Pins.OnboardLedBlue, 121 | CommonType.CommonAnode); 122 | Logger?.Trace("RGB LED up"); 123 | } 124 | catch (Exception ex) 125 | { 126 | Logger?.Error($"Unable to create the RGB LED: {ex.Message}"); 127 | } 128 | } 129 | 130 | return _rgbled; 131 | } 132 | 133 | internal override MikroBusConnector CreateMikroBus1() 134 | { 135 | Logger?.Trace("Creating MikroBus1 connector"); 136 | return new MikroBusConnector( 137 | "MikroBus1", 138 | new PinMapping 139 | { 140 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, _device.Pins.A00), 141 | // no DISPLAY_RST connected 142 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, _device.Pins.D14), 143 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, _device.Pins.SCK), 144 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, _device.Pins.CIPO), 145 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, _device.Pins.COPI), 146 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, _device.Pins.D04), 147 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, _device.Pins.D03), 148 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, _device.Pins.D12), 149 | new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, _device.Pins.D13), 150 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, _device.Pins.I2C_SCL), 151 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, _device.Pins.I2C_SDA), 152 | }, 153 | _device.PlatformOS.GetSerialPortName("com1")!, 154 | new I2cBusMapping(_device, 1), 155 | new SpiBusMapping(_device, _device.Pins.SCK, _device.Pins.COPI, _device.Pins.CIPO) 156 | ); 157 | } 158 | 159 | internal override MikroBusConnector CreateMikroBus2() 160 | { 161 | Logger?.Trace("Creating MikroBus2 connector"); 162 | return new MikroBusConnector( 163 | "MikroBus2", 164 | new PinMapping 165 | { 166 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, _device.Pins.A01), 167 | // no RST connected 168 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, _device.Pins.A02), 169 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, _device.Pins.SCK), 170 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, _device.Pins.CIPO), 171 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, _device.Pins.COPI), 172 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, _device.Pins.D03), 173 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, _device.Pins.D04), 174 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, _device.Pins.D12), 175 | new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, _device.Pins.D13), 176 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, _device.Pins.I2C_SCL), 177 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, _device.Pins.I2C_SDA), 178 | }, 179 | _device.PlatformOS.GetSerialPortName("com1")!, 180 | new I2cBusMapping(_device, 1), 181 | new SpiBusMapping(_device, _device.Pins.SCK, _device.Pins.COPI, _device.Pins.CIPO) 182 | ); 183 | } 184 | 185 | internal override GroveDigitalConnector CreateGroveAnalogConnector() 186 | { 187 | Logger?.Trace("Creating Grove analog connector"); 188 | 189 | return new GroveDigitalConnector( 190 | nameof(GroveAnalog), 191 | new PinMapping 192 | { 193 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D0, _device.Pins.A01), 194 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D1, _device.Pins.A02), 195 | }); 196 | } 197 | 198 | internal override UartConnector CreateGroveUartConnector() 199 | { 200 | Logger?.Trace("Creating Grove UART connector"); 201 | 202 | return new UartConnector( 203 | nameof(GroveUart), 204 | new PinMapping 205 | { 206 | new PinMapping.PinAlias(UartConnector.PinNames.RX, _device.Pins.D13), 207 | new PinMapping.PinAlias(UartConnector.PinNames.TX, _device.Pins.D12), 208 | }, 209 | _device.PlatformOS.GetSerialPortName("com1")!); 210 | } 211 | 212 | internal override I2cConnector CreateQwiicConnector() 213 | { 214 | Logger?.Trace("Creating Qwiic I2C connector"); 215 | 216 | return new I2cConnector( 217 | nameof(Qwiic), 218 | new PinMapping 219 | { 220 | new PinMapping.PinAlias(I2cConnector.PinNames.SCL, _device.Pins.D08), 221 | new PinMapping.PinAlias(I2cConnector.PinNames.SDA, _device.Pins.D07), 222 | }, 223 | new I2cBusMapping(_device, 1)); 224 | } 225 | 226 | internal override IOTerminalConnector CreateIOTerminalConnector() 227 | { 228 | Logger?.Trace("Creating IO terminal connector"); 229 | 230 | return new IOTerminalConnector( 231 | nameof(IOTerminal), 232 | new PinMapping 233 | { 234 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.A1, _device.Pins.A00), 235 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.D2, _device.Pins.D03), 236 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.D3, _device.Pins.D04), 237 | }); 238 | } 239 | 240 | internal override DisplayConnector CreateDisplayConnector() 241 | { 242 | Logger?.Trace("Creating display connector"); 243 | 244 | return new DisplayConnector( 245 | nameof(Display), 246 | new PinMapping 247 | { 248 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_CS, _device.Pins.A03), 249 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_RST, _device.Pins.A05), 250 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_DC, _device.Pins.A04), 251 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_CLK, _device.Pins.SCK), 252 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_COPI, _device.Pins.COPI), 253 | }, 254 | new SpiBusMapping(_device, _device.Pins.SCK, _device.Pins.COPI, _device.Pins.CIPO)); 255 | } 256 | 257 | /// 258 | public override string RevisionString => revision; 259 | 260 | private IButton GetPushButton(IPin pin) 261 | { 262 | if (pin.Supports(c => c.InterruptCapable)) 263 | { 264 | return new PushButton(pin, ResistorMode.InternalPullDown); 265 | } 266 | else 267 | { 268 | return new PollingPushButton(pin, ResistorMode.InternalPullDown); 269 | } 270 | } 271 | 272 | /// 273 | public override ModbusRtuClient GetModbusRtuClient(int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One) 274 | { 275 | if (Resolver.Device is not F7FeatherBase device) throw new NotSupportedException(); 276 | var portName = device.PlatformOS.GetSerialPortName("com4")!; 277 | var port = device.CreateSerialPort(portName, baudRate, dataBits, parity, stopBits); 278 | port.WriteTimeout = port.ReadTimeout = TimeSpan.FromSeconds(5); 279 | var serialEnable = device.CreateDigitalOutputPort(device.Pins.D09, false); 280 | return new ProjectLabModbusRtuClient(port, serialEnable); 281 | } 282 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ProjectLabHardwareV2.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.Audio; 2 | using Meadow.Foundation.Displays; 3 | using Meadow.Foundation.ICs.IOExpanders; 4 | using Meadow.Foundation.Leds; 5 | using Meadow.Foundation.Sensors.Buttons; 6 | using Meadow.Hardware; 7 | using Meadow.Modbus; 8 | using Meadow.Peripherals.Displays; 9 | using Meadow.Peripherals.Leds; 10 | using Meadow.Peripherals.Sensors.Buttons; 11 | using Meadow.Peripherals.Speakers; 12 | using Meadow.Units; 13 | using System; 14 | using System.Diagnostics; 15 | using System.Threading; 16 | 17 | namespace Meadow.Devices; 18 | 19 | /// 20 | /// Represents Project Lab V2 hardware and exposes its peripherals 21 | /// 22 | public class ProjectLabHardwareV2 : ProjectLabHardwareBase 23 | { 24 | private readonly IF7FeatherMeadowDevice _device; 25 | private IToneGenerator? _speaker; 26 | private IRgbPwmLed? _rgbled; 27 | private IPixelDisplay? _display; 28 | 29 | /// 30 | /// The MCP23008 IO expander connected to internal peripherals 31 | /// 32 | public Mcp23008 Mcp_1 { get; protected set; } 33 | 34 | /// 35 | /// The MCP23008 IO expander connected to IO headers and terminals on Project Lab 36 | /// 37 | public Mcp23008? Mcp_2 { get; protected set; } 38 | 39 | /// 40 | /// The MCP23008 IO expander that contains the ProjectLab hardware version 41 | /// 42 | private Mcp23008? Mcp_Version { get; } 43 | 44 | /// 45 | public override IButton UpButton { get; } 46 | 47 | /// 48 | public override IButton DownButton { get; } 49 | 50 | /// 51 | public override IButton LeftButton { get; } 52 | 53 | /// 54 | public override IButton RightButton { get; } 55 | 56 | /// 57 | public override IToneGenerator? Speaker => GetSpeaker(); 58 | 59 | /// 60 | public override IRgbPwmLed? RgbLed => GetRgbLed(); 61 | 62 | internal ProjectLabHardwareV2(IF7FeatherMeadowDevice device, II2cBus i2cBus, Mcp23008 mcp1) 63 | : base(device, i2cBus) 64 | { 65 | _device = device; 66 | 67 | Mcp_1 = mcp1; 68 | IDigitalInterruptPort? mcp2_int = null; 69 | 70 | try 71 | { 72 | // MCP the Second 73 | if (device.Pins.D10.Supports(c => c.InterruptCapable)) 74 | { 75 | mcp2_int = device.CreateDigitalInterruptPort( 76 | device.Pins.D10, InterruptMode.EdgeRising, ResistorMode.InternalPullDown); 77 | } 78 | 79 | Mcp_2 = new Mcp23008(i2cBus, address: 0x21, mcp2_int); 80 | 81 | Logger?.Info("Mcp_2 up"); 82 | } 83 | catch (Exception e) 84 | { 85 | Logger?.Trace($"Failed to create MCP2: {e.Message}"); 86 | mcp2_int?.Dispose(); 87 | } 88 | 89 | try 90 | { 91 | Mcp_Version = new Mcp23008(i2cBus, address: 0x27); 92 | Logger?.Info("Mcp_Version up"); 93 | } 94 | catch (Exception e) 95 | { 96 | Logger?.Trace($"ERR creating the MCP that has version information: {e.Message}"); 97 | } 98 | 99 | Logger?.Trace("Instantiating buttons"); 100 | var leftPort = mcp1.CreateDigitalInterruptPort(mcp1.Pins.GP2, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 101 | LeftButton = new PushButton(leftPort); 102 | var rightPort = mcp1.CreateDigitalInterruptPort(mcp1.Pins.GP1, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 103 | RightButton = new PushButton(rightPort); 104 | var upPort = mcp1.CreateDigitalInterruptPort(mcp1.Pins.GP0, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 105 | UpButton = new PushButton(upPort); 106 | var downPort = mcp1.CreateDigitalInterruptPort(mcp1.Pins.GP3, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 107 | DownButton = new PushButton(downPort); 108 | Logger?.Trace("Buttons up"); 109 | } 110 | 111 | /// 112 | protected override IPixelDisplay? GetDefaultDisplay() 113 | { 114 | if (_display == null) 115 | { 116 | Logger?.Trace("Instantiating display"); 117 | 118 | var chipSelectPort = DisplayHeader.Pins.DISPLAY_CS.CreateDigitalOutputPort(); 119 | var dcPort = DisplayHeader.Pins.DISPLAY_DC.CreateDigitalOutputPort(); 120 | var resetPort = DisplayHeader.Pins.DISPLAY_RST.CreateDigitalOutputPort(); 121 | Thread.Sleep(50); 122 | 123 | _display = new St7789( 124 | spiBus: DisplayHeader.SpiBusDisplay, 125 | chipSelectPort: chipSelectPort, 126 | dataCommandPort: dcPort, 127 | resetPort: resetPort, 128 | width: 240, height: 240, 129 | colorMode: ColorMode.Format16bppRgb565) 130 | { 131 | SpiBusMode = SpiClockConfiguration.Mode.Mode3, 132 | SpiBusSpeed = new Frequency(24000, Frequency.UnitType.Kilohertz) 133 | }; 134 | ((St7789)_display).SetRotation(RotationType._270Degrees); 135 | 136 | Logger?.Trace("Display up"); 137 | } 138 | 139 | return _display; 140 | } 141 | 142 | private IToneGenerator? GetSpeaker() 143 | { 144 | if (_speaker == null) 145 | { 146 | try 147 | { 148 | Logger?.Trace("Instantiating speaker"); 149 | _speaker = new PiezoSpeaker(_device.Pins.D11); 150 | Logger?.Trace("Speaker up"); 151 | } 152 | catch (Exception ex) 153 | { 154 | Logger?.Error($"Unable to create the Piezo Speaker: {ex.Message}"); 155 | } 156 | } 157 | 158 | return _speaker; 159 | } 160 | 161 | private IRgbPwmLed? GetRgbLed() 162 | { 163 | if (_rgbled == null) 164 | { 165 | try 166 | { 167 | Logger?.Trace("Instantiating RGB LED"); 168 | _rgbled = new RgbPwmLed( 169 | redPwmPin: _device.Pins.OnboardLedRed, 170 | greenPwmPin: _device.Pins.OnboardLedGreen, 171 | bluePwmPin: _device.Pins.OnboardLedBlue, 172 | CommonType.CommonAnode); 173 | Logger?.Trace("RGB LED up"); 174 | } 175 | catch (Exception ex) 176 | { 177 | Logger?.Error($"Unable to create the RGB LED: {ex.Message}"); 178 | } 179 | } 180 | 181 | return _rgbled; 182 | } 183 | 184 | internal override MikroBusConnector CreateMikroBus1() 185 | { 186 | Logger?.Trace("Creating MikroBus1 connector"); 187 | Debug.Assert(Mcp_2 != null, nameof(Mcp_2) + " != null"); 188 | return new MikroBusConnector( 189 | "MikroBus1", 190 | new PinMapping 191 | { 192 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, _device.Pins.A02), 193 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, Mcp_2.Pins.GP4), 194 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RST, Mcp_2.Pins.GP5), 195 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, _device.Pins.SCK), 196 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, _device.Pins.CIPO), 197 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, _device.Pins.COPI), 198 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, _device.Pins.D03), 199 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, Mcp_2.Pins.GP6), 200 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, _device.Pins.D13), 201 | new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, _device.Pins.D12), 202 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, _device.Pins.I2C_SCL), 203 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, _device.Pins.I2C_SDA), 204 | }, 205 | _device.PlatformOS.GetSerialPortName("com1")!, 206 | new I2cBusMapping(_device, 1), 207 | new SpiBusMapping(_device, _device.Pins.SCK, _device.Pins.COPI, _device.Pins.CIPO) 208 | ); 209 | } 210 | 211 | internal override MikroBusConnector CreateMikroBus2() 212 | { 213 | Logger?.Trace("Creating MikroBus2 connector"); 214 | Debug.Assert(Mcp_2 != null, nameof(Mcp_2) + " != null"); 215 | return new MikroBusConnector( 216 | "MikroBus2", 217 | new PinMapping 218 | { 219 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, _device.Pins.A03), 220 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, Mcp_2.Pins.GP1), 221 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RST, Mcp_2.Pins.GP2), 222 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, _device.Pins.SCK), 223 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, _device.Pins.CIPO), 224 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, _device.Pins.COPI), 225 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, _device.Pins.D04), 226 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, Mcp_2.Pins.GP3), 227 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, _device.Pins.D13), 228 | new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, _device.Pins.D12), 229 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, _device.Pins.I2C_SCL), 230 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, _device.Pins.I2C_SDA), 231 | }, 232 | _device.PlatformOS.GetSerialPortName("com1")!, 233 | new I2cBusMapping(_device, 1), 234 | new SpiBusMapping(_device, _device.Pins.SCK, _device.Pins.COPI, _device.Pins.CIPO) 235 | ); 236 | } 237 | 238 | internal override GroveDigitalConnector CreateGroveAnalogConnector() 239 | { 240 | Logger?.Trace("Creating Grove analog connector"); 241 | 242 | return new GroveDigitalConnector( 243 | nameof(GroveAnalog), 244 | new PinMapping 245 | { 246 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D0, _device.Pins.A00), 247 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D1, _device.Pins.A01), 248 | }); 249 | } 250 | 251 | internal override UartConnector CreateGroveUartConnector() 252 | { 253 | Logger?.Trace("Creating Grove UART connector"); 254 | 255 | return new UartConnector( 256 | nameof(GroveUart), 257 | new PinMapping 258 | { 259 | new PinMapping.PinAlias(UartConnector.PinNames.RX, _device.Pins.D13), 260 | new PinMapping.PinAlias(UartConnector.PinNames.TX, _device.Pins.D12), 261 | }, 262 | _device.PlatformOS.GetSerialPortName("com1")!); 263 | } 264 | 265 | internal override I2cConnector CreateQwiicConnector() 266 | { 267 | Logger?.Trace("Creating Qwiic I2C connector"); 268 | 269 | return new I2cConnector( 270 | nameof(Qwiic), 271 | new PinMapping 272 | { 273 | new PinMapping.PinAlias(I2cConnector.PinNames.SCL, _device.Pins.D08), 274 | new PinMapping.PinAlias(I2cConnector.PinNames.SDA, _device.Pins.D07), 275 | }, 276 | new I2cBusMapping(_device, 1)); 277 | } 278 | 279 | internal override IOTerminalConnector CreateIOTerminalConnector() 280 | { 281 | Logger?.Trace("Creating IO terminal connector"); 282 | 283 | return new IOTerminalConnector( 284 | nameof(IOTerminal), 285 | new PinMapping 286 | { 287 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.A1, _device.Pins.A04), 288 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.D2, _device.Pins.D03), 289 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.D3, _device.Pins.D04), 290 | }); 291 | } 292 | 293 | internal override DisplayConnector CreateDisplayConnector() 294 | { 295 | Logger?.Trace("Creating display connector"); 296 | 297 | return new DisplayConnector( 298 | nameof(Display), 299 | new PinMapping 300 | { 301 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_CS, Mcp_1.Pins.GP5), 302 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_RST, Mcp_1.Pins.GP7), 303 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_DC, Mcp_1.Pins.GP6), 304 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_CLK, _device.Pins.SCK), 305 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_COPI, _device.Pins.COPI), 306 | }, 307 | new SpiBusMapping(_device, _device.Pins.SCK, _device.Pins.COPI, _device.Pins.CIPO)); 308 | } 309 | 310 | private byte? _revisionNumber; 311 | /// 312 | /// The hardware revision number, read from the on-board MCP 313 | /// 314 | protected byte RevisionNumber 315 | { 316 | get 317 | { 318 | _revisionNumber ??= Mcp_Version?.ReadFromPorts(Mcp23xxx.PortBank.A) ?? 0; 319 | return _revisionNumber.Value; 320 | } 321 | } 322 | 323 | private string? _revisionString; 324 | /// 325 | public override string RevisionString 326 | { 327 | get 328 | { 329 | return _revisionString ??= $"v2.{(Mcp_Version == null ? "x" : RevisionNumber)}"; 330 | } 331 | } 332 | 333 | 334 | /// 335 | public override ModbusRtuClient GetModbusRtuClient(int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One) 336 | { 337 | if (Resolver.Device is not F7FeatherBase device) throw new NotSupportedException(); 338 | 339 | var portName = device.PlatformOS.GetSerialPortName("com4")!; 340 | var port = device.CreateSerialPort(portName, baudRate, dataBits, parity, stopBits); 341 | port.WriteTimeout = port.ReadTimeout = TimeSpan.FromSeconds(5); 342 | Debug.Assert(Mcp_2 != null, nameof(Mcp_2) + " != null"); 343 | var serialEnable = Mcp_2.CreateDigitalOutputPort(Mcp_2.Pins.GP0, false); 344 | 345 | return new ProjectLabModbusRtuClient(port, serialEnable); 346 | } 347 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ProjectLabHardwareV3.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.Audio; 2 | using Meadow.Foundation.Displays; 3 | using Meadow.Foundation.ICs.IOExpanders; 4 | using Meadow.Foundation.Leds; 5 | using Meadow.Foundation.Sensors.Buttons; 6 | using Meadow.Hardware; 7 | using Meadow.Modbus; 8 | using Meadow.Peripherals.Displays; 9 | using Meadow.Peripherals.Leds; 10 | using Meadow.Peripherals.Sensors.Buttons; 11 | using Meadow.Peripherals.Speakers; 12 | using Meadow.Units; 13 | using System; 14 | using System.Threading; 15 | 16 | namespace Meadow.Devices; 17 | 18 | /// 19 | /// Represents Project Lab V3 hardware and exposes its peripherals 20 | /// 21 | public class ProjectLabHardwareV3 : ProjectLabHardwareBase 22 | { 23 | private readonly IF7CoreComputeMeadowDevice _device; 24 | private readonly IConnectorProvider _connectors; 25 | private IToneGenerator? _speaker; 26 | private IRgbPwmLed? _rgbled; 27 | private IPixelDisplay? _display; 28 | 29 | /// 30 | /// The MCP23008 IO expander connected to internal peripherals on Project Lab 31 | /// 32 | public Mcp23008? Mcp_1 { get; protected set; } 33 | 34 | /// 35 | /// The MCP23008 IO expander connected to IO headers and terminals on Project Lab 36 | /// 37 | public Mcp23008? Mcp_2 { get; protected set; } 38 | 39 | /// 40 | /// The MCP23008 IO expander that contains the ProjectLab hardware version 41 | /// 42 | private Mcp23008? Mcp_Version { get; set; } 43 | 44 | /// 45 | public override IButton? UpButton { get; } 46 | 47 | /// 48 | public override IButton? DownButton { get; } 49 | 50 | /// 51 | public override IButton? LeftButton { get; } 52 | 53 | /// 54 | public override IButton? RightButton { get; } 55 | 56 | /// 57 | public override IToneGenerator? Speaker => GetSpeaker(); 58 | 59 | /// 60 | public override IRgbPwmLed? RgbLed => GetRgbLed(); 61 | 62 | /// 63 | /// Display enable port for backlight control 64 | /// 65 | public IDigitalOutputPort? DisplayEnablePort { get; protected set; } 66 | 67 | internal ProjectLabHardwareV3(IF7CoreComputeMeadowDevice device, II2cBus i2cBus) 68 | : base(device, i2cBus) 69 | { 70 | _device = device; 71 | 72 | IDigitalInterruptPort? mcp1Interrupt = null; 73 | IDigitalOutputPort? mcp1Reset = null; 74 | 75 | try 76 | { 77 | mcp1Interrupt = device.CreateDigitalInterruptPort( 78 | device.Pins.A05, 79 | InterruptMode.EdgeRising, 80 | ResistorMode.InternalPullDown); 81 | 82 | mcp1Reset = device.CreateDigitalOutputPort(device.Pins.PB4); 83 | 84 | Mcp_1 = new Mcp23008(i2cBus, address: 0x20, mcp1Interrupt, mcp1Reset); 85 | 86 | Logger?.Trace("Mcp_1 up"); 87 | } 88 | catch (Exception e) 89 | { 90 | Logger?.Trace($"Failed to create MCP1: {e.Message}"); 91 | mcp1Interrupt?.Dispose(); 92 | } 93 | 94 | IDigitalInterruptPort? mcp2Interrupt = null; 95 | 96 | try 97 | { 98 | mcp2Interrupt = device.CreateDigitalInterruptPort(device.Pins.PC8, InterruptMode.EdgeRising); 99 | 100 | Mcp_2 = new Mcp23008(i2cBus, address: 0x21, mcp2Interrupt); 101 | 102 | Logger?.Trace("Mcp_2 up"); 103 | } 104 | catch (Exception e) 105 | { 106 | Logger?.Trace($"Failed to create MCP2: {e.Message}"); 107 | mcp2Interrupt?.Dispose(); 108 | } 109 | 110 | try 111 | { 112 | Mcp_Version = new Mcp23008(i2cBus, address: 0x27); 113 | Logger?.Trace("Mcp_Version up"); 114 | } 115 | catch (Exception e) 116 | { 117 | Logger?.Trace($"ERR creating the MCP that has version information: {e.Message}"); 118 | } 119 | 120 | Logger?.Trace("Instantiating buttons"); 121 | var leftPort = Mcp_1?.CreateDigitalInterruptPort(Mcp_1.Pins.GP2, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 122 | if (leftPort != null) LeftButton = new PushButton(leftPort); 123 | var rightPort = Mcp_1?.CreateDigitalInterruptPort(Mcp_1.Pins.GP1, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 124 | if (rightPort != null) RightButton = new PushButton(rightPort); 125 | var upPort = Mcp_1?.CreateDigitalInterruptPort(Mcp_1.Pins.GP0, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 126 | if (upPort != null) UpButton = new PushButton(upPort); 127 | var downPort = Mcp_1?.CreateDigitalInterruptPort(Mcp_1.Pins.GP3, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 128 | if (downPort != null) DownButton = new PushButton(downPort); 129 | Logger?.Trace("Buttons up"); 130 | 131 | if (RevisionNumber < 15) // before 3.e 132 | { 133 | Logger?.Trace("Hardware is 3.d or earlier"); 134 | _connectors = new ConnectorProviderV3(); 135 | } 136 | else 137 | { 138 | Logger?.Trace("Hardware is 3.e or later"); 139 | _connectors = new ConnectorProviderV3e(this, i2cBus); 140 | } 141 | } 142 | 143 | /// 144 | protected override IPixelDisplay? GetDefaultDisplay() 145 | { 146 | DisplayEnablePort ??= Mcp_1?.CreateDigitalOutputPort(Mcp_1.Pins.GP4, false); 147 | 148 | if (_display == null) 149 | { 150 | Logger?.Trace("Instantiating display"); 151 | 152 | var chipSelectPort = DisplayHeader.Pins.DISPLAY_CS.CreateDigitalOutputPort(); 153 | var dcPort = DisplayHeader.Pins.DISPLAY_DC.CreateDigitalOutputPort(); 154 | var resetPort = DisplayHeader.Pins.DISPLAY_RST.CreateDigitalOutputPort(); 155 | 156 | Thread.Sleep(50); 157 | 158 | _display = new Ili9341( 159 | spiBus: DisplayHeader.SpiBusDisplay, 160 | chipSelectPort: chipSelectPort, 161 | dataCommandPort: dcPort, 162 | resetPort: resetPort, 163 | width: 240, height: 320, 164 | colorMode: ColorMode.Format12bppRgb444) 165 | { 166 | SpiBusMode = SpiClockConfiguration.Mode.Mode3, 167 | SpiBusSpeed = new Frequency(24000, Frequency.UnitType.Kilohertz) 168 | }; 169 | 170 | ((Ili9341)_display).SetRotation(RotationType._270Degrees); 171 | 172 | Logger?.Trace("Display up"); 173 | } 174 | 175 | return _display; 176 | } 177 | 178 | private IToneGenerator? GetSpeaker() 179 | { 180 | if (_speaker == null) 181 | { 182 | try 183 | { 184 | Logger?.Trace("Instantiating speaker"); 185 | _speaker = new PiezoSpeaker(_device.Pins.PA0); 186 | Logger?.Trace("Speaker up"); 187 | } 188 | catch (Exception ex) 189 | { 190 | Logger?.Error($"Unable to create the Piezo Speaker: {ex.Message}"); 191 | } 192 | } 193 | 194 | return _speaker; 195 | } 196 | 197 | private IRgbPwmLed? GetRgbLed() 198 | { 199 | if (_rgbled == null) 200 | { 201 | try 202 | { 203 | Logger?.Trace("Instantiating RGB LED"); 204 | _rgbled = new RgbPwmLed( 205 | redPwmPin: _device.Pins.PC6, 206 | greenPwmPin: _device.Pins.PC7, 207 | bluePwmPin: _device.Pins.PC9, 208 | CommonType.CommonAnode); 209 | Logger?.Trace("RGB LED up"); 210 | } 211 | catch (Exception ex) 212 | { 213 | Logger?.Error($"Unable to create the RGB LED: {ex.Message}"); 214 | } 215 | } 216 | 217 | return _rgbled; 218 | } 219 | 220 | internal override MikroBusConnector CreateMikroBus1() 221 | { 222 | Logger?.Trace("Creating MikroBus1 connector"); 223 | return _connectors.CreateMikroBus1(_device, Mcp_2!); 224 | } 225 | 226 | internal override MikroBusConnector CreateMikroBus2() 227 | { 228 | Logger?.Trace("Creating MikroBus2 connector"); 229 | return _connectors.CreateMikroBus2(_device, Mcp_2!); 230 | } 231 | 232 | internal override GroveDigitalConnector? CreateGroveDigitalConnector() 233 | { 234 | Logger?.Trace("Creating Grove digital connector"); 235 | 236 | return new GroveDigitalConnector( 237 | nameof(GroveDigital), 238 | new PinMapping 239 | { 240 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D0, _device.Pins.D16), 241 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D1, _device.Pins.D17), 242 | }); 243 | } 244 | 245 | internal override GroveDigitalConnector CreateGroveAnalogConnector() 246 | { 247 | Logger?.Trace("Creating Grove analog connector"); 248 | 249 | return new GroveDigitalConnector( 250 | nameof(GroveAnalog), 251 | new PinMapping 252 | { 253 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D0, _device.Pins.PA4), 254 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D1, _device.Pins.PA5), 255 | }); 256 | } 257 | 258 | internal override UartConnector CreateGroveUartConnector() 259 | { 260 | Logger?.Trace("Creating Grove UART connector"); 261 | 262 | return new UartConnector( 263 | nameof(GroveUart), 264 | new PinMapping 265 | { 266 | new PinMapping.PinAlias(UartConnector.PinNames.RX, _device.Pins.PI9), 267 | new PinMapping.PinAlias(UartConnector.PinNames.TX, _device.Pins.PH13), 268 | }, 269 | _device.PlatformOS.GetSerialPortName("com4")!); 270 | } 271 | 272 | internal override I2cConnector CreateQwiicConnector() 273 | { 274 | Logger?.Trace("Creating Qwiic I2C connector"); 275 | 276 | return new I2cConnector( 277 | nameof(Qwiic), 278 | new PinMapping 279 | { 280 | new PinMapping.PinAlias(I2cConnector.PinNames.SCL, _device.Pins.PB6), 281 | new PinMapping.PinAlias(I2cConnector.PinNames.SDA, _device.Pins.PB7), 282 | }, 283 | new I2cBusMapping(_device, 1)); 284 | } 285 | 286 | internal override IOTerminalConnector CreateIOTerminalConnector() 287 | { 288 | Logger?.Trace("Creating IO terminal connector"); 289 | 290 | return new IOTerminalConnector( 291 | nameof(IOTerminal), 292 | new PinMapping 293 | { 294 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.A1, _device.Pins.PB1), 295 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.D2, Mcp_2!.Pins.GP6), 296 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.D3, Mcp_2.Pins.GP5), 297 | }); 298 | } 299 | 300 | internal override DisplayConnector CreateDisplayConnector() 301 | { 302 | Logger?.Trace("Creating display connector"); 303 | 304 | return new DisplayConnector( 305 | nameof(Display), 306 | new PinMapping 307 | { 308 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_CS, Mcp_1!.Pins.GP5), 309 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_RST, Mcp_1.Pins.GP7), 310 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_DC, Mcp_1.Pins.GP6), 311 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_CLK, _device.Pins.SCK), 312 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_COPI, _device.Pins.COPI), 313 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_LED, Mcp_1!.Pins.GP4), 314 | }, 315 | new SpiBusMapping(_device, _device.Pins.SCK, _device.Pins.COPI, _device.Pins.CIPO)); 316 | } 317 | 318 | private byte? _revisionNumber; 319 | /// 320 | /// The hardware revision number, read from the on-board MCP 321 | /// 322 | protected byte RevisionNumber 323 | { 324 | get 325 | { 326 | _revisionNumber ??= Mcp_Version?.ReadFromPorts(Mcp23xxx.PortBank.A) ?? 0; 327 | return _revisionNumber.Value; 328 | } 329 | } 330 | 331 | private string? _revisionString; 332 | /// 333 | public override string RevisionString 334 | { 335 | get 336 | { 337 | return _revisionString ??= $"v3.{(Mcp_Version == null ? "x" : RevisionNumber)}"; 338 | } 339 | } 340 | 341 | /// 342 | public override ModbusRtuClient GetModbusRtuClient(int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One) 343 | { 344 | return _connectors.GetModbusRtuClient(this, baudRate, dataBits, parity, stopBits); 345 | } 346 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ProjectLabHardwareV4.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Foundation.Audio; 2 | using Meadow.Foundation.Displays; 3 | using Meadow.Foundation.ICs.IOExpanders; 4 | using Meadow.Foundation.Leds; 5 | using Meadow.Foundation.Sensors.Buttons; 6 | using Meadow.Foundation.Sensors.Hid; 7 | using Meadow.Hardware; 8 | using Meadow.Modbus; 9 | using Meadow.Peripherals.Displays; 10 | using Meadow.Peripherals.Leds; 11 | using Meadow.Peripherals.Sensors.Buttons; 12 | using Meadow.Peripherals.Speakers; 13 | using Meadow.Units; 14 | using System; 15 | using System.Diagnostics; 16 | using System.Threading; 17 | 18 | namespace Meadow.Devices; 19 | 20 | /// 21 | /// Represents Project Lab V4 hardware and exposes its peripherals 22 | /// 23 | public class ProjectLabHardwareV4 : ProjectLabHardwareBase 24 | { 25 | private readonly IF7CoreComputeMeadowDevice _device; 26 | private IToneGenerator? _speaker; 27 | private IRgbPwmLed? _rgbled; 28 | private IPixelDisplay? _display; 29 | private ITouchScreen? _touchscreen; 30 | 31 | /// 32 | /// The MCP23008 IO expander connected to internal peripherals on Project Lab 33 | /// 34 | public Mcp23008? Mcp_1 { get; protected set; } 35 | 36 | /// 37 | /// The MCP23008 IO expander connected to IO headers and terminals on Project Lab 38 | /// 39 | public Mcp23008? Mcp_2 { get; protected set; } 40 | 41 | /// 42 | /// The MCP23008 IO expander that contains the ProjectLab hardware version 43 | /// 44 | private Mcp23008? Mcp_Version { get; set; } 45 | 46 | /// 47 | public override IButton? UpButton { get; } 48 | 49 | /// 50 | public override IButton? DownButton { get; } 51 | 52 | /// 53 | public override IButton? LeftButton { get; } 54 | 55 | /// 56 | public override IButton? RightButton { get; } 57 | 58 | /// 59 | public override IToneGenerator? Speaker => GetSpeaker(); 60 | 61 | /// 62 | public override IRgbPwmLed? RgbLed => GetRgbLed(); 63 | 64 | private readonly Sc16is752 _uartExpander; 65 | 66 | /// 67 | /// Display enable port 68 | /// 69 | public IDigitalOutputPort? DisplayEnablePort { get; protected set; } 70 | 71 | /// 72 | /// Display backliight port for backlight control 73 | /// 74 | public IDigitalOutputPort? DisplayLedPort { get; protected set; } 75 | 76 | internal ProjectLabHardwareV4(IF7CoreComputeMeadowDevice device, II2cBus i2cBus) 77 | : base(device, i2cBus) 78 | { 79 | _device = device; 80 | 81 | IDigitalInterruptPort? mcp1Interrupt = null; 82 | IDigitalOutputPort? mcp1Reset = null; 83 | 84 | try 85 | { 86 | mcp1Interrupt = device.CreateDigitalInterruptPort(device.Pins.PC0, InterruptMode.EdgeRising); 87 | 88 | mcp1Reset = device.CreateDigitalOutputPort(device.Pins.PA10); 89 | 90 | Mcp_1 = new Mcp23008(i2cBus, address: 0x20, mcp1Interrupt, mcp1Reset); 91 | 92 | Logger?.Trace("Mcp_1 up"); 93 | } 94 | catch (Exception e) 95 | { 96 | Logger?.Trace($"Failed to create MCP1: {e.Message}"); 97 | mcp1Interrupt?.Dispose(); 98 | } 99 | 100 | IDigitalInterruptPort? mcp2Interrupt = null; 101 | 102 | try 103 | { 104 | mcp2Interrupt = device.CreateDigitalInterruptPort(device.Pins.PC8, InterruptMode.EdgeRising); 105 | 106 | Mcp_2 = new Mcp23008(i2cBus, address: 0x21, mcp2Interrupt); 107 | 108 | Logger?.Trace("Mcp_2 up"); 109 | } 110 | catch (Exception e) 111 | { 112 | Logger?.Trace($"Failed to create MCP2: {e.Message}"); 113 | mcp2Interrupt?.Dispose(); 114 | } 115 | 116 | try 117 | { 118 | Mcp_Version = new Mcp23008(i2cBus, address: 0x27); 119 | Logger?.Trace("Mcp_Version up"); 120 | } 121 | catch (Exception e) 122 | { 123 | Logger?.Trace($"ERR creating the MCP that has version information: {e.Message}"); 124 | } 125 | 126 | Logger?.Trace("Instantiating buttons"); 127 | var leftPort = Mcp_1?.CreateDigitalInterruptPort(Mcp_1.Pins.GP2, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 128 | if (leftPort != null) LeftButton = new PushButton(leftPort); 129 | var rightPort = Mcp_1?.CreateDigitalInterruptPort(Mcp_1.Pins.GP1, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 130 | if (rightPort != null) RightButton = new PushButton(rightPort); 131 | var upPort = Mcp_1?.CreateDigitalInterruptPort(Mcp_1.Pins.GP0, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 132 | if (upPort != null) UpButton = new PushButton(upPort); 133 | var downPort = Mcp_1?.CreateDigitalInterruptPort(Mcp_1.Pins.GP3, InterruptMode.EdgeBoth, ResistorMode.InternalPullUp); 134 | if (downPort != null) DownButton = new PushButton(downPort); 135 | Logger?.Trace("Buttons up"); 136 | 137 | _uartExpander = new Sc16is752(i2cBus, new Frequency(1.8432, Frequency.UnitType.Megahertz), Sc16is7x2.Addresses.Address_0x4D); 138 | } 139 | 140 | /// 141 | protected override IPixelDisplay? GetDefaultDisplay() 142 | { 143 | DisplayEnablePort ??= Mcp_1?.CreateDigitalOutputPort(Mcp_1.Pins.GP4, false); 144 | DisplayLedPort ??= Mcp_1?.CreateDigitalOutputPort(DisplayHeader.Pins.DISPLAY_LED, true); 145 | 146 | if (_display == null) 147 | { 148 | Logger?.Trace("Instantiating display"); 149 | 150 | var chipSelectPort = DisplayHeader.Pins.DISPLAY_CS.CreateDigitalOutputPort(); 151 | var dcPort = DisplayHeader.Pins.DISPLAY_DC.CreateDigitalOutputPort(); 152 | var resetPort = DisplayHeader.Pins.DISPLAY_RST.CreateDigitalOutputPort(); 153 | Thread.Sleep(50); 154 | 155 | var spiBus5 = _device.CreateSpiBus( 156 | _device.Pins.SPI5_SCK, 157 | _device.Pins.SPI5_COPI, 158 | _device.Pins.SPI5_CIPO, 159 | new Frequency(24000, Frequency.UnitType.Kilohertz)); 160 | 161 | _display = new Ili9341( 162 | spiBus: spiBus5, 163 | chipSelectPort: chipSelectPort, 164 | dataCommandPort: dcPort, 165 | resetPort: resetPort, 166 | width: 240, height: 320, 167 | colorMode: ColorMode.Format12bppRgb444) 168 | { 169 | SpiBusMode = SpiClockConfiguration.Mode.Mode3, 170 | SpiBusSpeed = new Frequency(24000, Frequency.UnitType.Kilohertz) 171 | }; 172 | 173 | ((Ili9341)_display).SetRotation(RotationType._270Degrees); 174 | ((Ili9341)_display).InvertDisplayColor(true); 175 | 176 | Logger?.Trace("Display up"); 177 | } 178 | 179 | return _display; 180 | } 181 | 182 | private IToneGenerator? GetSpeaker() 183 | { 184 | if (_speaker == null) 185 | { 186 | try 187 | { 188 | Logger?.Trace("Instantiating speaker"); 189 | _speaker = new PiezoSpeaker(_device.Pins.PA0); 190 | Logger?.Trace("Speaker up"); 191 | } 192 | catch (Exception ex) 193 | { 194 | Logger?.Error($"Unable to create the Piezo Speaker: {ex.Message}"); 195 | } 196 | } 197 | 198 | return _speaker; 199 | } 200 | 201 | private IRgbPwmLed? GetRgbLed() 202 | { 203 | if (_rgbled == null) 204 | { 205 | try 206 | { 207 | Logger?.Trace("Instantiating RGB LED"); 208 | _rgbled = new RgbPwmLed( 209 | redPwmPin: _device.Pins.PC6, 210 | greenPwmPin: _device.Pins.PC7, 211 | bluePwmPin: _device.Pins.PC9, 212 | CommonType.CommonAnode); 213 | Logger?.Trace("RGB LED up"); 214 | } 215 | catch (Exception ex) 216 | { 217 | Logger?.Error($"Unable to create the RGB LED: {ex.Message}"); 218 | } 219 | } 220 | 221 | return _rgbled; 222 | } 223 | 224 | internal override MikroBusConnector CreateMikroBus1() 225 | { 226 | Logger?.Trace("Creating MikroBus1 connector"); 227 | Debug.Assert(Mcp_2 != null, nameof(Mcp_2) + " != null"); 228 | return new MikroBusConnector( 229 | "MikroBus1", 230 | new PinMapping 231 | { 232 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, _device.Pins.PA3), 233 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RST, _device.Pins.PH10), 234 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, _device.Pins.PB12), 235 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, _device.Pins.SPI5_SCK), 236 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, _device.Pins.SPI5_CIPO), 237 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, _device.Pins.SPI5_COPI), 238 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, _device.Pins.PB8), 239 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, _device.Pins.PC2), 240 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, _device.Pins.PB15), 241 | new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, _device.Pins.PB14), 242 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, _device.Pins.I2C3_SCL), 243 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, _device.Pins.I2C3_SDA), 244 | }, 245 | _device.PlatformOS.GetSerialPortName("com1")!, 246 | new I2cBusMapping(_device, 3), 247 | new SpiBusMapping(_device, _device.Pins.SPI5_SCK, _device.Pins.SPI5_COPI, _device.Pins.SPI5_CIPO) 248 | ); 249 | } 250 | 251 | internal override MikroBusConnector CreateMikroBus2() 252 | { 253 | Logger?.Trace("Creating MikroBus2 connector"); 254 | Debug.Assert(Mcp_2 != null, nameof(Mcp_2) + " != null"); 255 | return new MikroBusConnector( 256 | "MikroBus2", 257 | new PinMapping 258 | { 259 | new PinMapping.PinAlias(MikroBusConnector.PinNames.AN, _device.Pins.PB0), 260 | new PinMapping.PinAlias(MikroBusConnector.PinNames.RST, Mcp_2.Pins.GP1), 261 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CS, Mcp_2.Pins.GP2), 262 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCK, _device.Pins.SPI3_SCK), 263 | new PinMapping.PinAlias(MikroBusConnector.PinNames.CIPO, _device.Pins.SPI3_CIPO), 264 | new PinMapping.PinAlias(MikroBusConnector.PinNames.COPI, _device.Pins.SPI3_COPI), 265 | new PinMapping.PinAlias(MikroBusConnector.PinNames.PWM, _device.Pins.PB9), 266 | new PinMapping.PinAlias(MikroBusConnector.PinNames.INT, Mcp_2.Pins.GP3), 267 | // new PinMapping.PinAlias(MikroBusConnector.PinNames.RX, uart1rx), // on the I2C uart and not usable for anything else 268 | // new PinMapping.PinAlias(MikroBusConnector.PinNames.TX, uart1tx), // on the I2C uart and not usable for anything else 269 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SCL, _device.Pins.I2C1_SCL), 270 | new PinMapping.PinAlias(MikroBusConnector.PinNames.SDA, _device.Pins.I2C1_SDA), 271 | }, 272 | _device.PlatformOS.GetSerialPortName("com1")!, 273 | new I2cBusMapping(_device, 1), 274 | new SpiBusMapping(_device, _device.Pins.SPI3_SCK, _device.Pins.SPI3_COPI, _device.Pins.SPI3_CIPO) 275 | ); 276 | } 277 | 278 | internal override GroveDigitalConnector? CreateGroveDigitalConnector() 279 | { 280 | Logger?.Trace("Creating Grove digital connector"); 281 | 282 | return new GroveDigitalConnector( 283 | nameof(GroveDigital), 284 | new PinMapping 285 | { 286 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D0, _device.Pins.PB4), 287 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D1, Mcp_2!.Pins.GP4), 288 | }); 289 | } 290 | 291 | internal override GroveDigitalConnector CreateGroveAnalogConnector() 292 | { 293 | Logger?.Trace("Creating Grove analog connector"); 294 | 295 | return new GroveDigitalConnector( 296 | nameof(GroveAnalog), 297 | new PinMapping 298 | { 299 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D0, _device.Pins.PA4), 300 | new PinMapping.PinAlias(GroveDigitalConnector.PinNames.D1, _device.Pins.PA5), 301 | }); 302 | } 303 | 304 | internal override UartConnector CreateGroveUartConnector() 305 | { 306 | Logger?.Trace("Creating Grove UART connector"); 307 | 308 | return new UartConnector( 309 | nameof(GroveUart), 310 | new PinMapping 311 | { 312 | new PinMapping.PinAlias(UartConnector.PinNames.RX, _device.Pins.PI9), 313 | new PinMapping.PinAlias(UartConnector.PinNames.TX, _device.Pins.PH13), 314 | }, 315 | _device.PlatformOS.GetSerialPortName("com4")!); 316 | } 317 | 318 | internal override I2cConnector CreateQwiicConnector() 319 | { 320 | Logger?.Trace("Creating Qwiic I2C connector"); 321 | 322 | return new I2cConnector( 323 | nameof(Qwiic), 324 | new PinMapping 325 | { 326 | new PinMapping.PinAlias(I2cConnector.PinNames.SCL, _device.Pins.PB6), 327 | new PinMapping.PinAlias(I2cConnector.PinNames.SDA, _device.Pins.PB7), 328 | }, 329 | new I2cBusMapping(_device, 1)); 330 | } 331 | 332 | internal override IOTerminalConnector CreateIOTerminalConnector() 333 | { 334 | Logger?.Trace("Creating IO terminal connector"); 335 | 336 | return new IOTerminalConnector( 337 | nameof(IOTerminal), 338 | new PinMapping 339 | { 340 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.A1, _device.Pins.PB1), 341 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.D2, Mcp_2!.Pins.GP6), 342 | new PinMapping.PinAlias(IOTerminalConnector.PinNames.D3, Mcp_2.Pins.GP5), 343 | }); 344 | } 345 | 346 | internal override DisplayConnector CreateDisplayConnector() 347 | { 348 | Logger?.Trace("Creating display connector"); 349 | 350 | return new DisplayConnector( 351 | nameof(Display), 352 | new PinMapping 353 | { 354 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_CS, _device.Pins.PD5), 355 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_RST, _device.Pins.PB13), 356 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_DC, _device.Pins.PI11), 357 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_CLK, _device.Pins.SPI5_SCK), 358 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_COPI, _device.Pins.SPI5_COPI), 359 | new PinMapping.PinAlias(DisplayConnector.PinNames.DISPLAY_LED, Mcp_1!.Pins.GP5), 360 | new PinMapping.PinAlias(DisplayConnector.PinNames.TOUCH_INT, Mcp_2!.Pins.GP0), 361 | new PinMapping.PinAlias(DisplayConnector.PinNames.TOUCH_CS, Mcp_1.Pins.GP6), 362 | new PinMapping.PinAlias(DisplayConnector.PinNames.TOUCH_CLK, _device.Pins.SPI3_SCK), 363 | new PinMapping.PinAlias(DisplayConnector.PinNames.TOUCH_COPI, _device.Pins.SPI3_COPI), 364 | new PinMapping.PinAlias(DisplayConnector.PinNames.TOUCH_CIPO, _device.Pins.SPI3_CIPO), 365 | }, 366 | new SpiBusMapping(_device, _device.Pins.SPI5_SCK, _device.Pins.SPI5_COPI, _device.Pins.SPI5_CIPO), 367 | new SpiBusMapping(_device, _device.Pins.SPI3_SCK, _device.Pins.SPI3_COPI, _device.Pins.SPI3_CIPO)); 368 | } 369 | 370 | private byte? _revisionNumber; 371 | /// 372 | /// The hardware revision number, read from the on-board MCP 373 | /// 374 | protected byte RevisionNumber 375 | { 376 | get 377 | { 378 | _revisionNumber ??= Mcp_Version?.ReadFromPorts(Mcp23xxx.PortBank.A) ?? 0; 379 | return _revisionNumber.Value; 380 | } 381 | } 382 | 383 | private string? _revisionString; 384 | /// 385 | public override string RevisionString 386 | { 387 | get 388 | { 389 | return _revisionString ??= $"v4.{(Mcp_Version == null ? "x" : RevisionNumber)}"; 390 | } 391 | } 392 | 393 | private ModbusRtuClient? _client; 394 | 395 | /// 396 | public override ModbusRtuClient GetModbusRtuClient(int baudRate = 19200, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One) 397 | { 398 | if (_client == null) 399 | { 400 | try 401 | { 402 | Resolver.Log.Info($"Creating 485 port...", Constants.LogGroup); 403 | 404 | // v3.e+ uses an SC16is I2C UART expander for the RS485 405 | var port = _uartExpander.PortB.CreateRs485SerialPort(baudRate, dataBits, parity, stopBits, false); 406 | Resolver.Log.Trace($"485 port created", Constants.LogGroup); 407 | _client = new ModbusRtuClient(port); 408 | } 409 | catch (Exception ex) 410 | { 411 | Resolver.Log.Warn($"Error creating 485 port: {ex.Message}", Constants.LogGroup); 412 | throw new Exception("Unable to connect to UART expander"); 413 | } 414 | } 415 | return _client; 416 | } 417 | 418 | /// 419 | public override ITouchScreen? Touchscreen 420 | { 421 | get 422 | { 423 | return _touchscreen ??= new Xpt2046( 424 | DisplayHeader.SpiBusDisplay, 425 | DisplayHeader.Pins.TOUCH_INT.CreateDigitalInterruptPort(InterruptMode.EdgeBoth, ResistorMode.Disabled), 426 | DisplayHeader.Pins.TOUCH_CS.CreateDigitalOutputPort(true), 427 | RotationType.Normal 428 | ); 429 | } 430 | } 431 | } -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/ProjectLabModbusRtuClient.cs: -------------------------------------------------------------------------------- 1 | using Meadow.Hardware; 2 | using Meadow.Modbus; 3 | using System.Threading; 4 | 5 | namespace Meadow.Devices; 6 | 7 | /// 8 | /// Represents a Modbus RTU client customized for ProjectLab. 9 | /// 10 | public class ProjectLabModbusRtuClient : ModbusRtuClient 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// The serial port for communication. 16 | /// The digital output port used for enable control. 17 | public ProjectLabModbusRtuClient(ISerialPort port, IDigitalOutputPort enablePort) 18 | : base(port, enablePort) 19 | { 20 | // this forces meadow to compile the serial pipeline. Without it, there's a big delay on sending the first byte 21 | PostOpenAction = () => { port.Write(new byte[] { 0x00 }); }; 22 | 23 | // meadow is not-so-fast, and data will not all get transmitted before the call to the port Write() returns 24 | PostWriteDelayAction = (m) => 25 | { 26 | var delay = (int)(1d / port.BaudRate * port.DataBits * 1000d * m.Length) + 3; // +3 to add just a little extra for clients who are a little slow to turn off the enable pin 27 | Thread.Sleep(delay); 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/Meadow.ProjectLab/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildernessLabs/Meadow.ProjectLab/9ab658716c63b681e298b3fcb7ad5041677d1d7a/Source/Meadow.ProjectLab/icon.png -------------------------------------------------------------------------------- /Source/ProjectLab_Demo/.vscode/launch.config: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Deploy", 9 | "type": "meadow", 10 | "request": "launch", 11 | "preLaunchTask": "meadow: Build" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /Source/ProjectLab_Demo/DisplayController.cs: -------------------------------------------------------------------------------- 1 | using Meadow; 2 | using Meadow.Foundation.Graphics; 3 | using Meadow.Foundation.Graphics.MicroLayout; 4 | using Meadow.Peripherals.Displays; 5 | using Meadow.Units; 6 | 7 | namespace ProjectLab_Demo; 8 | 9 | public class DisplayController 10 | { 11 | private readonly int rowOffset = 29; 12 | private readonly int rowHeight = 21; 13 | private readonly int rowMargin = 2; 14 | 15 | private Color foregroundColor = Color.White; 16 | private Color atmosphericColor = Color.White; 17 | private Color motionColor = Color.FromHex("23ABE3"); 18 | private Color buttonColor = Color.FromHex("EF7D3B"); 19 | 20 | private readonly Font12x20 font12X20 = new(); 21 | 22 | private readonly DisplayScreen displayScreen; 23 | 24 | private readonly Label temperature; 25 | private readonly Label humidity; 26 | private readonly Label pressure; 27 | private readonly Label iluminance; 28 | private readonly Label acceleration3D; 29 | private readonly Label angularVelocity3D; 30 | private readonly Label buttonUp; 31 | private readonly Label buttonDown; 32 | private readonly Label buttonLeft; 33 | private readonly Label buttonRight; 34 | 35 | public DisplayController(IPixelDisplay display, string revisionVersion) 36 | { 37 | displayScreen = new DisplayScreen(display, RotationType._270Degrees) 38 | { 39 | BackgroundColor = Color.FromHex("0B3749") 40 | }; 41 | 42 | displayScreen.Controls.Add(new Label(rowMargin, 4, displayScreen.Width, rowHeight) 43 | { 44 | Text = $"Hello PROJ LAB {revisionVersion}!", 45 | TextColor = foregroundColor, 46 | Font = font12X20, 47 | HorizontalAlignment = HorizontalAlignment.Center 48 | }); 49 | 50 | displayScreen.Controls.Add(CreateLeftLabel("Temperature:", atmosphericColor, rowMargin, rowOffset, displayScreen.Width, rowHeight)); 51 | displayScreen.Controls.Add(CreateLeftLabel("Pressure:", atmosphericColor, rowMargin, rowOffset + rowHeight, displayScreen.Width, rowHeight)); 52 | displayScreen.Controls.Add(CreateLeftLabel("Humidity:", atmosphericColor, rowMargin, rowOffset + rowHeight * 2, displayScreen.Width, rowHeight)); 53 | displayScreen.Controls.Add(CreateLeftLabel("Illuminance:", atmosphericColor, rowMargin, rowOffset + rowHeight * 3, displayScreen.Width, rowHeight)); 54 | displayScreen.Controls.Add(CreateLeftLabel("Accel:", motionColor, rowMargin, rowOffset + rowHeight * 4, displayScreen.Width, rowHeight)); 55 | displayScreen.Controls.Add(CreateLeftLabel("Gyro:", motionColor, rowMargin, rowOffset + rowHeight * 5, displayScreen.Width, rowHeight)); 56 | displayScreen.Controls.Add(CreateLeftLabel("Up:", buttonColor, rowMargin, rowOffset + rowHeight * 6, displayScreen.Width, rowHeight)); 57 | displayScreen.Controls.Add(CreateLeftLabel("Down:", buttonColor, rowMargin, rowOffset + rowHeight * 7, displayScreen.Width, rowHeight)); 58 | displayScreen.Controls.Add(CreateLeftLabel("Left:", buttonColor, rowMargin, rowOffset + rowHeight * 8, displayScreen.Width, rowHeight)); 59 | displayScreen.Controls.Add(CreateLeftLabel("Right:", buttonColor, rowMargin, rowOffset + rowHeight * 9, displayScreen.Width, rowHeight)); 60 | 61 | temperature = CreateRightLabel("0ºC", atmosphericColor, rowMargin, rowOffset, displayScreen.Width - rowMargin * 2, rowHeight); 62 | pressure = CreateRightLabel("0atm", atmosphericColor, rowMargin, rowOffset + rowHeight, displayScreen.Width - rowMargin * 2, rowHeight); 63 | humidity = CreateRightLabel("0%", atmosphericColor, rowMargin, rowOffset + rowHeight * 2, displayScreen.Width - rowMargin * 2, rowHeight); 64 | iluminance = CreateRightLabel("0Lux", atmosphericColor, rowMargin, rowOffset + rowHeight * 3, displayScreen.Width - rowMargin * 2, rowHeight); 65 | acceleration3D = CreateRightLabel("0,0,0g", motionColor, rowMargin, rowOffset + rowHeight * 4, displayScreen.Width - rowMargin * 2, rowHeight); 66 | angularVelocity3D = CreateRightLabel("0,0,0rpm", motionColor, rowMargin, rowOffset + rowHeight * 5, displayScreen.Width - rowMargin * 2, rowHeight); 67 | buttonUp = CreateRightLabel("Released", buttonColor, rowMargin, rowOffset + rowHeight * 6, displayScreen.Width - rowMargin * 2, rowHeight); 68 | buttonDown = CreateRightLabel("Released", buttonColor, rowMargin, rowOffset + rowHeight * 7, displayScreen.Width - rowMargin * 2, rowHeight); 69 | buttonLeft = CreateRightLabel("Released", buttonColor, rowMargin, rowOffset + rowHeight * 8, displayScreen.Width - rowMargin * 2, rowHeight); 70 | buttonRight = CreateRightLabel("Released", buttonColor, rowMargin, rowOffset + rowHeight * 9, displayScreen.Width - rowMargin * 2, rowHeight); 71 | 72 | displayScreen.Controls.Add(new[] { temperature, pressure, humidity, iluminance, acceleration3D, angularVelocity3D, buttonUp, buttonDown, buttonLeft, buttonRight }); 73 | } 74 | 75 | private Label CreateLeftLabel(string text, Color color, int left, int top, int width, int height) 76 | { 77 | return new Label(left, top, width, height) 78 | { 79 | Text = text, 80 | TextColor = color, 81 | Font = font12X20, 82 | HorizontalAlignment = HorizontalAlignment.Left 83 | }; 84 | } 85 | 86 | private Label CreateRightLabel(string text, Color color, int left, int top, int width, int height) 87 | { 88 | return new Label(left, top, width, height) 89 | { 90 | Text = text, 91 | TextColor = color, 92 | Font = font12X20, 93 | HorizontalAlignment = HorizontalAlignment.Right 94 | }; 95 | } 96 | 97 | public void UpdateTemperatureValue(Temperature temperature, Temperature temperature2) 98 | { 99 | this.temperature.Text = $"{temperature2.Celsius:N1}ºC/{temperature.Celsius:N1}ºC"; 100 | } 101 | 102 | public void UpdatePressureValue(Pressure pressure) 103 | { 104 | this.pressure.Text = $"{pressure.StandardAtmosphere:N1}atm"; 105 | } 106 | 107 | public void UpdateHumidityValue(RelativeHumidity humidity) 108 | { 109 | this.humidity.Text = $"{humidity.Percent:N1}%"; 110 | } 111 | 112 | public void UpdateIluminanceValue(Illuminance iluminance) 113 | { 114 | this.iluminance.Text = $"{iluminance.Lux:N1}Lux"; 115 | } 116 | 117 | public void UpdateAcceleration3DValue(Acceleration3D acceleration3D) 118 | { 119 | this.acceleration3D.Text = $"{acceleration3D.X.Gravity:N1},{acceleration3D.Y.Gravity:N1},{acceleration3D.Z.Gravity:N1}g"; 120 | } 121 | 122 | public void UpdateAngularVelocity3DValue(AngularVelocity3D angularVelocity3D) 123 | { 124 | this.angularVelocity3D.Text = $"{angularVelocity3D.X.DegreesPerSecond:N0},{angularVelocity3D.Y.DegreesPerSecond:N0},{angularVelocity3D.Z.DegreesPerSecond:N0}º/s"; 125 | } 126 | 127 | public void UpdateButtonUp(bool isPressed) 128 | { 129 | Resolver.Log.Info($"Button Up: {isPressed}"); 130 | buttonUp.Text = isPressed ? "Pressed" : "Released"; 131 | } 132 | 133 | public void UpdateButtonDown(bool isPressed) 134 | { 135 | Resolver.Log.Info($"Button Down: {isPressed}"); 136 | buttonDown.Text = isPressed ? "Pressed" : "Released"; 137 | } 138 | 139 | public void UpdateButtonLeft(bool isPressed) 140 | { 141 | Resolver.Log.Info($"Button Left: {isPressed}"); 142 | buttonLeft.Text = isPressed ? "Pressed" : "Released"; 143 | } 144 | 145 | public void UpdateButtonRight(bool isPressed) 146 | { 147 | Resolver.Log.Info($"Button Right: {isPressed}"); 148 | buttonRight.Text = isPressed ? "Pressed" : "Released"; 149 | } 150 | } -------------------------------------------------------------------------------- /Source/ProjectLab_Demo/MeadowApp.cs: -------------------------------------------------------------------------------- 1 | using Meadow; 2 | using Meadow.Devices; 3 | using Meadow.Foundation; 4 | using Meadow.Foundation.Audio; 5 | using Meadow.Units; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace ProjectLab_Demo; 10 | 11 | // Change to ProjectLabFeatherApp for Project Lab V1 or V2 12 | // Change to ProjectLabCoreComputeApp for Project Lab V3+ 13 | public class MeadowApp : ProjectLabCoreComputeApp 14 | { 15 | private DisplayController? displayController; 16 | 17 | private MicroAudio? audio; 18 | 19 | public override Task Initialize() 20 | { 21 | Resolver.Log.LogLevel = Meadow.Logging.LogLevel.Trace; 22 | 23 | Resolver.Log.Info($"Running on ProjectLab Hardware {Hardware.RevisionString}"); 24 | 25 | if (Hardware.RgbLed is { } rgbLed) 26 | { 27 | rgbLed.SetColor(Color.Blue); 28 | } 29 | 30 | if (Hardware.Display is { } display) 31 | { 32 | Resolver.Log.Trace("Creating DisplayController"); 33 | displayController = new DisplayController(display, Hardware.RevisionString); 34 | Resolver.Log.Trace("DisplayController up"); 35 | } 36 | 37 | if (Hardware.Speaker is { } speaker) 38 | { 39 | speaker.SetVolume(0.2f); 40 | audio = new MicroAudio(speaker); 41 | } 42 | 43 | if (Hardware.TemperatureSensor is { } temperatureSensor) 44 | { 45 | temperatureSensor.Updated += OnTemperatureSensorUpdated; 46 | } 47 | if (Hardware.BarometricPressureSensor is { } pressureSensor) 48 | { 49 | pressureSensor.Updated += OnPressureSensorUpdated; 50 | } 51 | if (Hardware.HumiditySensor is { } humiditySensor) 52 | { 53 | humiditySensor.Updated += OnHumiditySensorUpdated; 54 | } 55 | if (Hardware.LightSensor is { } lightSensor) 56 | { 57 | lightSensor.Updated += OnLightSensorUpdated; 58 | } 59 | 60 | if (Hardware.Accelerometer is { } accelerometer) 61 | { 62 | accelerometer.Updated += OnAccelerometerUpdated; 63 | } 64 | if (Hardware.Gyroscope is { } gyroscope) 65 | { 66 | gyroscope.Updated += OnGyroscopeUpdated; 67 | } 68 | 69 | if (Hardware.UpButton is { } upButton) 70 | { 71 | upButton.PressStarted += (s, e) => displayController!.UpdateButtonUp(true); 72 | upButton.PressEnded += (s, e) => displayController!.UpdateButtonUp(false); 73 | } 74 | if (Hardware.DownButton is { } downButton) 75 | { 76 | downButton.PressStarted += (s, e) => displayController!.UpdateButtonDown(true); 77 | downButton.PressEnded += (s, e) => displayController!.UpdateButtonDown(false); 78 | } 79 | if (Hardware.LeftButton is { } leftButton) 80 | { 81 | leftButton.PressStarted += (s, e) => displayController!.UpdateButtonLeft(true); 82 | leftButton.PressEnded += (s, e) => displayController!.UpdateButtonLeft(false); 83 | } 84 | if (Hardware.RightButton is { } rightButton) 85 | { 86 | rightButton.PressStarted += (s, e) => displayController!.UpdateButtonRight(true); 87 | rightButton.PressEnded += (s, e) => displayController!.UpdateButtonRight(false); 88 | } 89 | 90 | if (Hardware.Touchscreen is { } touchScreen) 91 | { 92 | touchScreen.TouchDown += (s, e) => 93 | { 94 | Resolver.Log.Info("touch screen down"); 95 | }; 96 | touchScreen.TouchUp += (s, e) => 97 | { 98 | Resolver.Log.Info("touch screen up"); 99 | }; 100 | } 101 | 102 | Resolver.Log.Info("Initialization complete"); 103 | 104 | return base.Initialize(); 105 | } 106 | 107 | private void OnTemperatureSensorUpdated(object sender, IChangeResult e) 108 | { 109 | Resolver.Log.Info($"TEMPERATURE: {e.New.Celsius:N1}C"); 110 | displayController!.UpdateTemperatureValue(e.New, Hardware.TemperatureSensor2?.Temperature ?? new Temperature(0)); 111 | } 112 | 113 | private void OnPressureSensorUpdated(object sender, IChangeResult e) 114 | { 115 | Resolver.Log.Info($"PRESSURE: {e.New.Millibar:N1}mbar"); 116 | displayController!.UpdatePressureValue(e.New); 117 | } 118 | 119 | private void OnHumiditySensorUpdated(object sender, IChangeResult e) 120 | { 121 | Resolver.Log.Info($"HUMIDITY: {e.New.Percent:N1}%"); 122 | displayController!.UpdateHumidityValue(e.New); 123 | } 124 | 125 | private void OnLightSensorUpdated(object sender, IChangeResult e) 126 | { 127 | Resolver.Log.Info($"LIGHT: {e.New.Lux:N1}lux"); 128 | displayController!.UpdateIluminanceValue(e.New); 129 | } 130 | 131 | private void OnAccelerometerUpdated(object sender, IChangeResult e) 132 | { 133 | Resolver.Log.Info($"ACCEL: {e.New.X.Gravity:N1}, {e.New.Y.Gravity:N1}, {e.New.Z.Gravity:N1}g"); 134 | displayController!.UpdateAcceleration3DValue(e.New); 135 | } 136 | 137 | private void OnGyroscopeUpdated(object sender, IChangeResult e) 138 | { 139 | Resolver.Log.Info($"GYRO: {e.New.X.DegreesPerSecond:N0}, {e.New.Y.DegreesPerSecond:N0}, {e.New.Z.DegreesPerSecond:N0}deg/s"); 140 | displayController!.UpdateAngularVelocity3DValue(e.New); 141 | } 142 | 143 | public override async Task Run() 144 | { 145 | Resolver.Log.Info("Run..."); 146 | 147 | var updateInterveral = TimeSpan.FromSeconds(5); 148 | 149 | if (audio != null) 150 | { 151 | await audio.PlaySystemSound(SystemSoundEffect.Success); 152 | } 153 | 154 | if (Hardware?.TemperatureSensor is { } temperature) 155 | { 156 | temperature.StartUpdating(updateInterveral); 157 | } 158 | if (Hardware?.BarometricPressureSensor is { } barometer) 159 | { 160 | barometer.StartUpdating(updateInterveral); 161 | } 162 | if (Hardware?.HumiditySensor is { } humidity) 163 | { 164 | humidity.StartUpdating(updateInterveral); 165 | } 166 | if (Hardware?.LightSensor is { } luminance) 167 | { 168 | luminance.StartUpdating(updateInterveral); 169 | } 170 | 171 | if (Hardware?.Accelerometer is { } accelerometer) 172 | { 173 | accelerometer.StartUpdating(updateInterveral); 174 | } 175 | if (Hardware?.Gyroscope is { } gyroscope) 176 | { 177 | gyroscope.StartUpdating(updateInterveral); 178 | } 179 | 180 | if (Hardware?.RgbLed is { } rgbLed) 181 | { 182 | Resolver.Log.Info("starting blink"); 183 | _ = rgbLed.StartBlink(WildernessLabsColors.PearGreen, TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(2000), 0.5f); 184 | } 185 | } 186 | } -------------------------------------------------------------------------------- /Source/ProjectLab_Demo/ProjectLab_Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.1 4 | true 5 | Library 6 | App 7 | Wilderness Labs, Inc 8 | Wilderness Labs, Inc 9 | Apache-2.0 10 | enable 11 | 10.0 12 | 13 | 14 | 15 | Always 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Source/ProjectLab_Demo/app.build.yaml: -------------------------------------------------------------------------------- 1 | Deploy: 2 | NoLink: [ ProjectLab ] -------------------------------------------------------------------------------- /Source/ProjectLab_Demo/app.config.yaml: -------------------------------------------------------------------------------- 1 | # Uncomment additional options as needed. 2 | # To learn more about these config options, including custom application configuration settings, check out the Application Settings Configuration documentation. 3 | # http://developer.wildernesslabs.co/Meadow/Meadow.OS/Configuration/Application_Settings_Configuration/ 4 | 5 | # App lifecycle configuration. 6 | Lifecycle: 7 | 8 | # Control whether Meadow will restart when an unhandled app exception occurs. Combine with Lifecycle > AppFailureRestartDelaySeconds to control restart timing. 9 | RestartOnAppFailure: false 10 | 11 | # When app set to restart automatically on app failure, 12 | # AppFailureRestartDelaySeconds: 15 13 | 14 | # Logging configuration. 15 | Logging: 16 | 17 | # Adjust the level of logging detail. 18 | LogLevel: 19 | 20 | # Trace, Debug, Information, Warning, or Error 21 | Default: Trace 22 | --------------------------------------------------------------------------------