├── .editorconfig ├── .gitignore ├── .travis.yml ├── .vscode └── settings.json ├── CHANGELOG.md ├── Examples ├── Microsoft.PowerShell.IoT.ADXL345 │ ├── Microsoft.PowerShell.IoT.ADXL345.psd1 │ ├── Microsoft.PowerShell.IoT.ADXL345.psm1 │ └── README.md ├── Microsoft.PowerShell.IoT.BME280 │ ├── Microsoft.PowerShell.IoT.BME280.psd1 │ ├── Microsoft.PowerShell.IoT.BME280.psm1 │ └── README.md ├── Microsoft.PowerShell.IoT.Fan │ ├── Microsoft.PowerShell.IoT.Fan.psd1 │ ├── Microsoft.PowerShell.IoT.Fan.psm1 │ ├── README.md │ └── SmartFan.ps1 ├── Microsoft.PowerShell.IoT.LED │ ├── Microsoft.PowerShell.IoT.LED.psd1 │ ├── Microsoft.PowerShell.IoT.LED.psm1 │ └── README.md ├── Microsoft.PowerShell.IoT.Plant │ ├── Microsoft.PowerShell.IoT.Plant.psd1 │ ├── Microsoft.PowerShell.IoT.Plant.psm1 │ ├── README.md │ └── full-plant-demo.ps1 ├── Microsoft.PowerShell.IoT.SSD1306 │ ├── Microsoft.PowerShell.IoT.SSD1306.psd1 │ ├── Microsoft.PowerShell.IoT.SSD1306.psm1 │ └── README.md ├── Microsoft.PowerShell.IoT.Scroll_pHat │ ├── Microsoft.PowerShell.IoT.SadJoey.ps1 │ └── README.md └── README.md ├── LICENSE.txt ├── Move-PSIoTBuild.ps1 ├── NuGet.config ├── README.md ├── SimpleBuild.ps1 ├── appveyor.yml ├── azure-pipelines-release.yml ├── build.ps1 ├── docs ├── README.md ├── help │ ├── Get-GpioPin.md │ ├── Get-I2CDevice.md │ ├── Get-I2CRegister.md │ ├── Microsoft.PowerShell.IoT.md │ ├── Send-SPIData.md │ ├── Set-GpioPin.md │ └── Set-I2CRegister.md ├── remoting.md └── rpi3_pin_layout.md ├── psiot.build.ps1 ├── src ├── Microsoft.PowerShell.IoT.sln ├── Microsoft.PowerShell.IoT │ ├── Gpio │ │ ├── Get │ │ │ └── GetGpioPin.cs │ │ ├── GpioCmdletBase.cs │ │ ├── GpioPinData.cs │ │ └── Set │ │ │ └── SetGpioPin.cs │ ├── I2c │ │ ├── Get │ │ │ ├── GetI2cDevice.cs │ │ │ └── GetI2cRegister.cs │ │ ├── I2cDevice.cs │ │ ├── I2cRegisterData.cs │ │ └── Set │ │ │ └── SetI2cRegister.cs │ ├── Microsoft.PowerShell.IoT.csproj │ ├── Microsoft.PowerShell.IoT.psd1 │ ├── SPI │ │ ├── SPIData.cs │ │ └── Send │ │ │ └── SendSPIData.cs │ ├── gen │ │ └── Resources.cs │ └── resources │ │ └── Resources.resx └── ResGen │ ├── Program.cs │ ├── README.md │ └── ResGen.csproj ├── test └── psiot.Tests.ps1 ├── tools ├── vsts.ps1 └── vstsBuild.psm1 └── vsts.yml /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | insert_final_newline = true 6 | 7 | [*.{cs,ps1,psd1,psm1}] 8 | file_header_template = Copyright (c) Microsoft Corporation.\nLicensed under the MIT License. 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/visualstudio 3 | 4 | ### VisualStudio ### 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | [Ll]og/ 30 | 31 | # Visual Studio 2015 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | TestsResults.xml 44 | 45 | # Build Results of an ATL Project 46 | [Dd]ebugPS/ 47 | [Rr]eleasePS/ 48 | dlldata.c 49 | 50 | # .NET Core 51 | project.lock.json 52 | project.fragment.lock.json 53 | artifacts/ 54 | **/Properties/launchSettings.json 55 | 56 | *_i.c 57 | *_p.c 58 | *_i.h 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.svclog 79 | *.scc 80 | 81 | # Chutzpah Test files 82 | _Chutzpah* 83 | 84 | # Visual C++ cache files 85 | ipch/ 86 | *.aps 87 | *.ncb 88 | *.opendb 89 | *.opensdf 90 | *.sdf 91 | *.cachefile 92 | *.VC.db 93 | *.VC.VC.opendb 94 | 95 | # Visual Studio profiler 96 | *.psess 97 | *.vsp 98 | *.vspx 99 | *.sap 100 | 101 | # TFS 2012 Local Workspace 102 | $tf/ 103 | 104 | # Guidance Automation Toolkit 105 | *.gpState 106 | 107 | # ReSharper is a .NET coding add-in 108 | _ReSharper*/ 109 | *.[Rr]e[Ss]harper 110 | *.DotSettings.user 111 | 112 | # JustCode is a .NET coding add-in 113 | .JustCode 114 | 115 | # TeamCity is a build add-in 116 | _TeamCity* 117 | 118 | # DotCover is a Code Coverage Tool 119 | *.dotCover 120 | 121 | # Visual Studio code coverage results 122 | *.coverage 123 | *.coveragexml 124 | 125 | # NCrunch 126 | _NCrunch_* 127 | .*crunch*.local.xml 128 | nCrunchTemp_* 129 | 130 | # MightyMoose 131 | *.mm.* 132 | AutoTest.Net/ 133 | 134 | # Web workbench (sass) 135 | .sass-cache/ 136 | 137 | # Installshield output folder 138 | [Ee]xpress/ 139 | 140 | # DocProject is a documentation generator add-in 141 | DocProject/buildhelp/ 142 | DocProject/Help/*.HxT 143 | DocProject/Help/*.HxC 144 | DocProject/Help/*.hhc 145 | DocProject/Help/*.hhk 146 | DocProject/Help/*.hhp 147 | DocProject/Help/Html2 148 | DocProject/Help/html 149 | 150 | # Click-Once directory 151 | publish/ 152 | 153 | # Publish Web Output 154 | *.[Pp]ublish.xml 155 | *.azurePubxml 156 | # TODO: Uncomment the next line to ignore your web deploy settings. 157 | # By default, sensitive information, such as encrypted password 158 | # should be stored in the .pubxml.user file. 159 | #*.pubxml 160 | *.pubxml.user 161 | *.publishproj 162 | 163 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 164 | # checkin your Azure Web App publish settings, but sensitive information contained 165 | # in these scripts will be unencrypted 166 | PublishScripts/ 167 | 168 | # NuGet Packages 169 | *.nupkg 170 | # The packages folder can be ignored because of Package Restore 171 | **/packages/* 172 | # except build/, which is used as an MSBuild target. 173 | !**/packages/build/ 174 | # Uncomment if necessary however generally it will be regenerated when needed 175 | #!**/packages/repositories.config 176 | # NuGet v3's project.json files produces more ignorable files 177 | *.nuget.props 178 | *.nuget.targets 179 | 180 | # Microsoft Azure Build Output 181 | csx/ 182 | *.build.csdef 183 | 184 | # Microsoft Azure Emulator 185 | ecf/ 186 | rcf/ 187 | 188 | # Windows Store app package directories and files 189 | AppPackages/ 190 | BundleArtifacts/ 191 | Package.StoreAssociation.xml 192 | _pkginfo.txt 193 | 194 | # Visual Studio cache files 195 | # files ending in .cache can be ignored 196 | *.[Cc]ache 197 | # but keep track of directories ending in .cache 198 | !*.[Cc]ache/ 199 | 200 | # Others 201 | ClientBin/ 202 | ~$* 203 | *~ 204 | *.dbmdl 205 | *.dbproj.schemaview 206 | *.jfm 207 | *.pfx 208 | *.publishsettings 209 | orleans.codegen.cs 210 | 211 | # Since there are multiple workflows, uncomment next line to ignore bower_components 212 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 213 | #bower_components/ 214 | 215 | # RIA/Silverlight projects 216 | Generated_Code/ 217 | 218 | # Backup & report files from converting an old project file 219 | # to a newer Visual Studio version. Backup files are not needed, 220 | # because we have git ;-) 221 | _UpgradeReport_Files/ 222 | Backup*/ 223 | UpgradeLog*.XML 224 | UpgradeLog*.htm 225 | 226 | # SQL Server files 227 | *.mdf 228 | *.ldf 229 | *.ndf 230 | 231 | # Business Intelligence projects 232 | *.rdl.data 233 | *.bim.layout 234 | *.bim_*.settings 235 | 236 | # Microsoft Fakes 237 | FakesAssemblies/ 238 | 239 | # GhostDoc plugin setting file 240 | *.GhostDoc.xml 241 | 242 | # Node.js Tools for Visual Studio 243 | .ntvs_analysis.dat 244 | node_modules/ 245 | 246 | # Typescript v1 declaration files 247 | typings/ 248 | 249 | # Visual Studio 6 build log 250 | *.plg 251 | 252 | # Visual Studio 6 workspace options file 253 | *.opt 254 | 255 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 256 | *.vbw 257 | 258 | # Visual Studio LightSwitch build output 259 | **/*.HTMLClient/GeneratedArtifacts 260 | **/*.DesktopClient/GeneratedArtifacts 261 | **/*.DesktopClient/ModelManifest.xml 262 | **/*.Server/GeneratedArtifacts 263 | **/*.Server/ModelManifest.xml 264 | _Pvt_Extensions 265 | 266 | # Paket dependency manager 267 | .paket/paket.exe 268 | paket-files/ 269 | 270 | # FAKE - F# Make 271 | .fake/ 272 | 273 | # JetBrains Rider 274 | .idea/ 275 | *.sln.iml 276 | 277 | # CodeRush 278 | .cr/ 279 | 280 | # Python Tools for Visual Studio (PTVS) 281 | __pycache__/ 282 | *.pyc 283 | 284 | # Cake - Uncomment if you are using it 285 | # tools/** 286 | # !tools/packages.config 287 | 288 | # Telerik's JustMock configuration file 289 | *.jmconfig 290 | 291 | # BizTalk build output 292 | *.btp.cs 293 | *.btm.cs 294 | *.odx.cs 295 | *.xsd.cs 296 | 297 | ### VisualStudio Patch ### 298 | # By default, sensitive information, such as encrypted password 299 | # should be stored in the .pubxml.user file. 300 | 301 | 302 | # End of https://www.gitignore.io/api/visualstudio 303 | 304 | out -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | 3 | matrix: 4 | include: 5 | - os: osx 6 | dotnet: 2.1.4 7 | mono: none 8 | osx_image: xcode9.1 9 | before_install: 10 | - brew update 11 | - brew tap caskroom/cask 12 | - brew cask install powershell 13 | - os: linux 14 | dotnet: 2.1.4 15 | mono: none 16 | dist: trusty 17 | sudo: required 18 | addons: 19 | apt: 20 | sources: 21 | - sourceline: deb [arch=amd64] https://packages.microsoft.com/ubuntu/14.04/prod trusty main 22 | key_url: https://packages.microsoft.com/keys/microsoft.asc 23 | packages: 24 | - powershell 25 | 26 | script: 27 | - pwsh -c 'Install-Module InvokeBuild -Force -Scope CurrentUser; Invoke-Build' 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.detectIndentation": false, 3 | "editor.tabSize": 4, 4 | "editor.insertSpaces": true, 5 | "[csharp]": { 6 | "editor.rulers": [140] 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.1.0 4 | 5 | Initial Preview of PowerShell IoT -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.ADXL345/Microsoft.PowerShell.IoT.ADXL345.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | 6 | # Root module file 7 | RootModule = 'Microsoft.PowerShell.IoT.ADXL345.psm1' 8 | 9 | # Version number of this module. 10 | ModuleVersion = '0.1.0' 11 | 12 | # ID used to uniquely identify this module 13 | GUID = '78f2d4bb-195e-4143-8f54-0a6d8c68612e' 14 | 15 | # Author of this module 16 | Author = 'Julien Nury' 17 | 18 | # Description of the functionality provided by this module 19 | Description = 'A set of functions to interact with ADXL345s accelerometer thru I2C' 20 | 21 | # Modules that must be imported into the global environment prior to importing this module 22 | RequiredModules = @('Microsoft.PowerShell.IoT') 23 | 24 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 25 | FunctionsToExport = 'Get-ADXL345Device', 'Get-ADXL345Data' 26 | 27 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 28 | CmdletsToExport = @() 29 | 30 | # Variables to export from this module 31 | VariablesToExport = @() 32 | 33 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 34 | AliasesToExport = @() 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.ADXL345/Microsoft.PowerShell.IoT.ADXL345.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # Inspired from https://github.com/sparkfun/SparkFun_ADXL345_Arduino_Library 5 | 6 | # Device registers and parameters 7 | 8 | $script:ADXL345_ADDRESS = 0x53 # I2c address 9 | $script:ADXL345_POWER_CTL = 0x2D # Power-Saving Features Control 10 | $script:ADXL345_DATAX0 = 0x32 # X-Axis Data 0 11 | $script:ADXL345_DATAX1 = 0x33 # X-Axis Data 1 12 | $script:ADXL345_DATAY0 = 0x34 # Y-Axis Data 0 13 | $script:ADXL345_DATAY1 = 0x35 # Y-Axis Data 1 14 | $script:ADXL345_DATAZ0 = 0x36 # Z-Axis Data 0 15 | $script:ADXL345_DATAZ1 = 0x37 # Z-Axis Data 1 16 | $script:ADXL345_GAINX = 0.00376390 # Gain to convert X value in g 17 | $script:ADXL345_GAINY = 0.00376009 # Gain to convert Y value in g 18 | $script:ADXL345_GAINZ = 0.00349265 # Gain to convert Z value in g 19 | 20 | # Published functions 21 | function Get-ADXL345Device { 22 | [CmdletBinding()] 23 | param 24 | ( 25 | [ValidateNotNullOrEmpty()] 26 | [int] 27 | $Id = $script:ADXL345_ADDRESS, 28 | 29 | [ValidateNotNullOrEmpty()] 30 | [string] 31 | $FriendlyName = "ADXL345" 32 | ) 33 | 34 | $Device = Get-I2CDevice -Id $Id -FriendlyName $FriendlyName 35 | InitializeDevice -Device $Device 36 | return $Device 37 | } 38 | 39 | function Get-ADXL345Data { 40 | param ( 41 | [Parameter(ValueFromPipeline)] 42 | [ValidateNotNullOrEmpty()] 43 | [Microsoft.PowerShell.IoT.I2CDevice] 44 | $Device = (Get-ADXL345Device), 45 | 46 | [Parameter()] 47 | [Double] 48 | $Limit, 49 | 50 | [Switch] 51 | $Raw 52 | ) 53 | 54 | try { 55 | $xValue0 = Get-I2CRegister -Device $Device -Register $script:ADXL345_DATAX0 -ByteCount 1 56 | $xValue1 = Get-I2CRegister -Device $Device -Register $script:ADXL345_DATAX1 -ByteCount 1 57 | $xValue = [int16]($xValue1.Data[0]) -shl 8 -bor [int16]($xValue0.Data[0]) 58 | 59 | $yValue0 = Get-I2CRegister -Device $Device -Register $script:ADXL345_DATAY0 -ByteCount 1 60 | $yValue1 = Get-I2CRegister -Device $Device -Register $script:ADXL345_DATAY1 -ByteCount 1 61 | $yValue = [int16]($yValue1.Data[0]) -shl 8 -bor [int16]($yValue0.Data[0]) 62 | 63 | $zValue0 = Get-I2CRegister -Device $Device -Register $script:ADXL345_DATAZ0 -ByteCount 1 64 | $zValue1 = Get-I2CRegister -Device $Device -Register $script:ADXL345_DATAZ1 -ByteCount 1 65 | $zValue = [int16]($zValue1.Data[0]) -shl 8 -bor [int16]($zValue0.Data[0]) 66 | } 67 | catch { 68 | Throw "Unable to retreive data from device '$($Device.FriendlyName)'. Message: $($_.Exception.Message)" 69 | } 70 | 71 | if (-not $Raw) { 72 | $xValue = $xValue * $script:ADXL345_GAINX 73 | $yValue = $yValue * $script:ADXL345_GAINY 74 | $zValue = $zValue * $script:ADXL345_GAINZ 75 | } 76 | 77 | if ($Limit) { 78 | $xValue = FilterValue -Value $xValue -Maximum $Limit 79 | $yValue = FilterValue -Value $yValue -Maximum $Limit 80 | $zValue = FilterValue -Value $zValue -Maximum $Limit 81 | } 82 | 83 | return [PSObject]@{x = $xValue; y = $yValue; z = $zvalue} 84 | } 85 | 86 | # Internal functions 87 | function InitializeDevice { 88 | Param ( 89 | [Parameter(Mandatory)] 90 | [Microsoft.PowerShell.IoT.I2CDevice] 91 | $Device 92 | ) 93 | 94 | try { 95 | Set-I2CRegister -Device $Device -Register $script:ADXL345_POWER_CTL -Data 0x00 96 | Set-I2CRegister -Device $Device -Register $script:ADXL345_POWER_CTL -Data 0x10 97 | Set-I2CRegister -Device $Device -Register $script:ADXL345_POWER_CTL -Data 0x08 98 | } 99 | catch { 100 | Throw "Unable to initialize device '$($Device.FriendlyName)'. Message: $($_.Exception.Message)" 101 | } 102 | } 103 | 104 | function FilterValue { 105 | param ( 106 | [Parameter(Mandatory)] 107 | [Double] 108 | $Value, 109 | 110 | [Parameter(Mandatory)] 111 | [Double] 112 | $Maximum 113 | ) 114 | 115 | if ($Value -ge 0) { 116 | return [Math]::Min($value, $Maximum) 117 | } else { 118 | return [Math]::Max($Value, -$Maximum) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.ADXL345/README.md: -------------------------------------------------------------------------------- 1 | # Example module Microsoft.PowerShell.IoT.ADXL345 2 | 3 | This PowerShell module is to interact with [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) for reading acceleration on 3 axis. 4 | 5 | ## Hardware setup 6 | 7 | Several vendors have breakout boards with ADXL345 sensor. In this example we'll use [SparkFun Triple Axis Accelerometer Breakout](https://www.sparkfun.com/products/9836). 8 | 9 | ADXL345 sensor supports both I2C and SPI interfaces; here we'll use I2C. 10 | 11 | Wiring diagram with Raspberry Pi 3 looks like this: 12 | 13 | ![ADXL345_Wiring](https://user-images.githubusercontent.com/9315492/39673576-40f7e8b4-513f-11e8-8b69-314237f99bd1.png) 14 | 15 | ## Software setup 16 | 17 | ### Install PowerShell Core on Raspberry Pi 18 | 19 | Installation instructions can be found [here](https://github.com/PowerShell/PowerShell/tree/master/docs/installation/linux.md#raspbian). 20 | 21 | ### Enable I2C interface on Raspberry Pi 22 | 23 | 1. `sudo raspi-config` 24 | 2. `5 Interfacing options` 25 | 3. `P5 I2C` 26 | 4. `Would you like ARM I2C interface to be enabled -> Yes` 27 | 28 | ### Start Powershell and install modules 29 | 30 | **Don't forget to start PowerShell with sudo** or you'll be unable to access I2C bus. 31 | 32 | ```powershell 33 | sudo pwsh 34 | Install-Module -Name Microsoft.PowerShell.IoT 35 | git clone https://github.com/PowerShell/PowerShell-IoT.git 36 | Import-Module ./PowerShell-IoT/Examples/Microsoft.PowerShell.IoT.ADXL345 37 | ``` 38 | 39 | ### Collect Data 40 | 41 | To simply collect acceleration values in g: 42 | 43 | ```powershell 44 | PS /home/pi> $accelerometer = Get-ADXL345Device 45 | PS /home/pi> Get-ADXL345Data -Device $accelerometer 46 | 47 | Name Value 48 | ---- ----- 49 | y -0.03008072 50 | x 0.0828058 51 | z 0.86966985 52 | ``` 53 | 54 | To represent current acceleration on the 3 axis with bargraphs: 55 | 56 | ```powershell 57 | PS /home/pi> $accelerometer = Get-ADXL345Device 58 | PS /home/pi> while ($true) { 59 | $data = Get-ADXL345Data -Device $accelerometer -Limit 1 60 | Write-Progress -id 1 -Activity 'X axis' -Status 'Acceleration' -PercentComplete ($data.x * 50 + 50) 61 | Write-Progress -id 2 -Activity 'Y axis' -Status 'Acceleration' -PercentComplete ($data.y * 50 + 50) 62 | Write-Progress -id 3 -Activity 'Z axis' -Status 'Acceleration' -PercentComplete ($data.z * 50 + 50) 63 | } 64 | 65 | X axis 66 | Acceleration 67 | [ooooooooooooooooooooooooooooooooooooooooooooooooooo ] 68 | Y axis 69 | Acceleration 70 | [ooooooooooooooooooooooooooooooooooooooooooooooooo ] 71 | Z axis 72 | Acceleration 73 | [ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ] 74 | ``` -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.BME280/Microsoft.PowerShell.IoT.BME280.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | GUID="eb74e8da-9ae2-482a-a648-e96550fb8739" 6 | Author="Microsoft Corporation" 7 | CompanyName="Microsoft Corporation" 8 | Copyright="© Microsoft Corporation. All rights reserved." 9 | Description='PowerShell module for working with Bosch Sensortec BME280 sensor.' 10 | ModuleVersion="0.1.0" 11 | FunctionsToExport = @('Get-BME280ChipID','Get-BME280Data','Get-BME280Device') 12 | CmdletsToExport = '*' 13 | AliasesToExport = @() 14 | NestedModules=@('Microsoft.PowerShell.IoT','Microsoft.PowerShell.IoT.BME280.psm1') 15 | HelpInfoURI = 'https://github.com/PowerShell/PowerShell-IoT' 16 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 17 | PrivateData = @{ 18 | PSData = @{ 19 | # Tags applied to this module. These help with module discovery in online galleries. 20 | Tags = 'IoT','RaspberryPi','Raspbian','BME280' 21 | 22 | # A URL to the license for this module. 23 | LicenseUri = 'https://github.com/PowerShell/PowerShell-IoT/blob/master/LICENSE.txt' 24 | 25 | # A URL to the main website for this project. 26 | ProjectUri = 'https://github.com/PowerShell/PowerShell-IoT' 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.BME280/Microsoft.PowerShell.IoT.BME280.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | $script:CalibrationData = @{} 5 | $script:Device = $null 6 | 7 | # EXPORTED 8 | 9 | function Get-BME280Device 10 | { 11 | param 12 | ( 13 | [ValidateNotNullOrEmpty()] 14 | [int] 15 | $Id = 0x77, 16 | 17 | [ValidateNotNullOrEmpty()] 18 | [string] 19 | $FriendlyName = "BME280" 20 | ) 21 | $script:Device = Get-I2CDevice -Id $Id -FriendlyName $FriendlyName 22 | return $script:Device 23 | } 24 | 25 | function Get-BME280ChipID 26 | { 27 | param 28 | ( 29 | [ValidateNotNullOrEmpty()] 30 | [Microsoft.PowerShell.IoT.I2CDevice] 31 | $Device = $script:Device 32 | ) 33 | $Device = CreateDeviceIfNotExist -Device $Device 34 | 35 | return @{ 36 | Device = $Device 37 | ChipId = Get-I2CRegister -Device $Device -Register 0xD0 -Raw 38 | } 39 | } 40 | 41 | # Units of returned data: temperature in degrees Celsius, pressure in hPa, relative humidity in % 42 | function Get-BME280Data 43 | { 44 | param 45 | ( 46 | [Parameter(ValueFromPipeline=$true)] 47 | [ValidateNotNullOrEmpty()] 48 | [Microsoft.PowerShell.IoT.I2CDevice] $Device = $script:Device, 49 | 50 | [Parameter(Mandatory=$false)] 51 | [string] $Mode, 52 | 53 | [Parameter(Mandatory=$false)] 54 | [string] $Oversampling, 55 | 56 | [Parameter(Mandatory=$false)] 57 | [switch] $Fahrenheit 58 | ) 59 | $Device = CreateDeviceIfNotExist -Device $Device 60 | 61 | Read-CalibrationData $Device 62 | Set-BME280Config $Device $Mode $Oversampling 63 | 64 | # read raw temperature, humidity and pressure measurement output data 65 | $adc = Get-I2CRegister -Device $Device -Register 0xF7 -ByteCount 8 -Raw 66 | 67 | [int] $adc_P = $adc[0] 68 | $adc_P = ($adc_P -shl 8) -bor $adc[1] 69 | $adc_P = ($adc_P -shl 4) -bor ($adc[2] -shr 4) 70 | 71 | [int] $adc_T = $adc[3] 72 | $adc_T = ($adc_T -shl 8) -bor $adc[4] 73 | $adc_T = ($adc_T -shl 4) -bor ($adc[5] -shr 4) 74 | 75 | [int] $adc_H = $adc[6] 76 | $adc_H = ($adc_H -shl 8) -bor $adc[7] 77 | 78 | [int] $TFine = Calc-T-Fine $adc_T $script:CalibrationData[$Device] 79 | [float] $Temperature = (($TFine * 5 + 128) -shr 8) / [float]100; 80 | if ($Fahrenheit) 81 | { 82 | $Temperature = $Temperature * 1.8 + 32 83 | } 84 | 85 | [float] $Pressure = Compensate_P $adc_P $script:CalibrationData[$Device] $TFine 86 | $Pressure = $Pressure / 100 87 | 88 | [float] $Humidity = Compensate_H $adc_H $script:CalibrationData[$Device] $TFine 89 | $Humidity = $Humidity / 100 90 | 91 | $result = [pscustomobject]@{ 92 | Temperature=$Temperature; 93 | Pressure=$Pressure; 94 | Humidity=$Humidity} 95 | 96 | $result 97 | } 98 | 99 | # INTERNAL 100 | 101 | function CreateDeviceIfNotExist 102 | { 103 | param 104 | ( 105 | [Microsoft.PowerShell.IoT.I2CDevice] $Device 106 | ) 107 | 108 | if (-not $Device) 109 | { 110 | $script:Device = Get-BME280Device 111 | return $script:Device 112 | } 113 | return $Device 114 | } 115 | 116 | function Read-CalibrationData 117 | { 118 | param 119 | ( 120 | [Parameter(Mandatory=$true)] 121 | [ValidateNotNullOrEmpty()] 122 | [Microsoft.PowerShell.IoT.I2CDevice] $Device 123 | ) 124 | 125 | # read calibration data from sensor 126 | $cd1 = Get-I2CRegister -Device $Device -Register 0x88 -ByteCount 25 -Raw 127 | $cd2 = Get-I2CRegister -Device $Device -Register 0xE1 -ByteCount 7 -Raw 128 | 129 | [uint16] $T1 = $cd1[1] 130 | $T1 = ($T1 -shl 8) -bor $cd1[0] 131 | 132 | [int16] $T2 = $cd1[3] 133 | $T2 = ($T2 -shl 8) -bor $cd1[2] 134 | 135 | [int16] $T3 = $cd1[5] 136 | $T3 = ($T3 -shl 8) -bor $cd1[4] 137 | 138 | [uint16] $P1 = $cd1[7] 139 | $P1 = ($P1 -shl 8) -bor $cd1[6] 140 | 141 | [int16] $P2 = $cd1[9] 142 | $P2 = ($P2 -shl 8) -bor $cd1[8] 143 | 144 | [int16] $P3 = $cd1[11] 145 | $P3 = ($P3 -shl 8) -bor $cd1[10] 146 | 147 | [int16] $P4 = $cd1[13] 148 | $P4 = ($P4 -shl 8) -bor $cd1[12] 149 | 150 | [int16] $P5 = $cd1[15] 151 | $P5 = ($P5 -shl 8) -bor $cd1[14] 152 | 153 | [int16] $P6 = $cd1[17] 154 | $P6 = ($P6 -shl 8) -bor $cd1[16] 155 | 156 | [int16] $P7 = $cd1[19] 157 | $P7 = ($P7 -shl 8) -bor $cd1[18] 158 | 159 | [int16] $P8 = $cd1[21] 160 | $P8 = ($P8 -shl 8) -bor $cd1[20] 161 | 162 | [int16] $P9 = $cd1[23] 163 | $P9 = ($P9 -shl 8) -bor $cd1[22] 164 | 165 | [byte] $H1 = $cd1[24] 166 | 167 | [int16] $H2 = $cd2[1] 168 | $H2 = ($H2 -shl 8) -bor $cd2[0] 169 | 170 | [byte] $H3 = $cd2[2] 171 | 172 | [int16] $H4 = $cd2[3] 173 | $H4 = ($H4 -shl 4) -bor ($cd2[4] -band 0x00FF) 174 | 175 | [int16] $H5 = $cd2[5] 176 | $H5 = ($H5 -shl 4) -bor ($cd2[4] -band 0xFF00) 177 | 178 | [sbyte] $H6 = $cd2[6] 179 | 180 | $cd_final = [pscustomobject]@{ 181 | T1=$T1; 182 | T2=$T2; 183 | T3=$T3; 184 | 185 | P1=$P1; 186 | P2=$P2; 187 | P3=$P3; 188 | P4=$P4; 189 | P5=$P5; 190 | P6=$P6; 191 | P7=$P7; 192 | P8=$P8; 193 | P9=$P9; 194 | 195 | H1=$H1; 196 | H2=$H2; 197 | H3=$H3; 198 | H4=$H4; 199 | H5=$H5; 200 | H6=$H6} 201 | 202 | $script:CalibrationData[$Device] = $cd_final 203 | } 204 | 205 | function Set-BME280Config 206 | { 207 | param 208 | ( 209 | [Parameter(Mandatory=$true)] 210 | [ValidateNotNullOrEmpty()] 211 | [Microsoft.PowerShell.IoT.I2CDevice] $Device, 212 | 213 | [Parameter(Mandatory=$false)] 214 | [string] $Mode, 215 | 216 | [Parameter(Mandatory=$false)] 217 | [string] $Oversampling 218 | ) 219 | 220 | # set oversampling of humidity data to ×2 221 | [byte] $reg_ctrl_hum = 0xF2 222 | [byte] $reg_ctrl_hum_value = [Convert]::ToByte("00000010",2) 223 | 224 | # set forced mode, set oversampling for temperature and pressure data to x2 225 | [byte] $reg_ctrl_meas = 0xF4 226 | [byte] $reg_ctrl_meas_value = [Convert]::ToByte("01001001",2) 227 | 228 | Set-I2CRegister -Device $Device -Register $reg_ctrl_hum -Data $reg_ctrl_hum_value 229 | Set-I2CRegister -Device $Device -Register $reg_ctrl_meas -Data $reg_ctrl_meas_value 230 | } 231 | 232 | # Calculate variable TFine (signed 32 bit) that carries a fine resolution temperature value over to the pressure and humidity compensation formula 233 | function Calc-T-Fine 234 | { 235 | param 236 | ( 237 | [ValidateNotNullOrEmpty()] 238 | [int] $adc_T, 239 | 240 | [ValidateNotNullOrEmpty()] 241 | [pscustomobject] $cd 242 | ) 243 | 244 | [int] $var1 = (((($adc_T -shr 3) - ([int]$cd.T1 -shl 1))) * ([int]$cd.T2)) -shr 11 245 | [int] $var2 = ((((($adc_T -shr 4) - ([int]$cd.T1)) * (($adc_T -shr 4) - ([int]$cd.T1))) -shr 12) * ([int]$cd.T3)) -shr 14 246 | [int] $TFine = $var1 + $var2 247 | $TFine 248 | } 249 | 250 | # Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits). 251 | # Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa 252 | function Compensate_P 253 | { 254 | param 255 | ( 256 | [ValidateNotNullOrEmpty()] 257 | [int] $adc_P, 258 | 259 | [ValidateNotNullOrEmpty()] 260 | [pscustomobject] $cd, 261 | 262 | [ValidateNotNullOrEmpty()] 263 | [int] $t_fine 264 | ) 265 | 266 | [int64] $var1 = ([int64]$t_fine) - 128000; 267 | [int64] $var2 = $var1 * $var1 * [int64]$cd.P6; 268 | $var2 = $var2 + (($var1*[int64]$cd.P5) -shl 17); 269 | $var2 = $var2 + (([int64]$cd.P4) -shl 35); 270 | $var1 = (($var1 * $var1 * [int64]$cd.P3) -shr 8) + (($var1 * [int64]$cd.P2) -shl 12); 271 | $var1 = (((([int64]1) -shl 47)+$var1))*([int64]$cd.P1) -shr 33; 272 | if ($var1 -eq 0) 273 | { 274 | return 0 # avoid exception caused by division by zero 275 | } 276 | [int64] $p = 1048576 - $adc_P; 277 | $p = ((($p -shl 31)-$var2)*3125)/$var1; 278 | $var1 = (([int64]$cd.P9) * ($p -shr 13) * ($p -shr 13)) -shr 25; 279 | $var2 = (([int64]$cd.P8) * $p) -shr 19; 280 | $p = (($p + $var1 + $var2) -shr 8) + (([int64]$cd.P7) -shl 4); 281 | $p = [uint32]$p/256; 282 | $p 283 | } 284 | 285 | # Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits). 286 | # Output value of “47445” represents 47445/1024 = 46.333 %RH 287 | function Compensate_H 288 | { 289 | param 290 | ( 291 | [ValidateNotNullOrEmpty()] 292 | [int] $adc_H, 293 | 294 | [ValidateNotNullOrEmpty()] 295 | [pscustomobject] $cd, 296 | 297 | [ValidateNotNullOrEmpty()] 298 | [int] $t_fine 299 | ) 300 | 301 | [int32] $v_x1_u32r = ($t_fine - ([int32]76800)) 302 | $v_x1_u32r = ((((($adc_H -shl 14) - (([int32]$cd.H4) -shl 20) - (([int32]$cd.H5) * $v_x1_u32r)) + 303 | ([int32]16384)) -shr 15) * ((((((($v_x1_u32r * ([int32]$cd.H6)) -shr 10) * ((($v_x1_u32r * 304 | ([int32]$cd.H3)) -shr 11) + ([int32]32768))) -shr 10) + ([int32]2097152)) * 305 | ([int32]$cd.H2) + 8192) -shr 14)) 306 | $v_x1_u32r = ($v_x1_u32r - ((((($v_x1_u32r -shr 15) * ($v_x1_u32r -shr 15)) -shr 7) * ([int32]$cd.H1)) -shr 4)) 307 | if ($v_x1_u32r -lt 0) { $v_x1_u32r = 0 } 308 | if ($v_x1_u32r -gt 419430400) {$v_x1_u32r = 419430400} 309 | $v_x1_u32r = ($v_x1_u32r -shr 12)*100 / 1024 310 | $v_x1_u32r 311 | } 312 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.BME280/README.md: -------------------------------------------------------------------------------- 1 | # Example module Microsoft.PowerShell.IoT.BME280 2 | 3 | This PowerShell module is for interacting with [BME280 environmental sensor](https://www.bosch-sensortec.com/bst/products/all_products/bme280) for reading temperature, pressure and humidity. 4 | 5 | ## Hardware setup 6 | 7 | Several vendors have breakout boards with BME280 sensor. In this example we'll use [BME280 breakout board from Adafruit](https://www.adafruit.com/product/2652). 8 | 9 | BME280 sensor supports both I2C and SPI interfaces; in this example we'll use I2C. 10 | 11 | Later, in the software section, we'll need to use I2C address of the BME280 sensor. 12 | The default I2C address is 0x77. It can be changed to 0x76 by connecting SDO to GND. 13 | 14 | Wiring diagram with Raspberry Pi 3 is like this: 15 | 16 | ![bme280_schema](https://user-images.githubusercontent.com/11860095/38394384-1899c3d4-38e3-11e8-8f8d-a93918971773.png) 17 | 18 | ## Software setup 19 | 20 | ### Install PowerShell Core on Raspberry Pi 21 | 22 | Installation instructions can be found [here](https://github.com/PowerShell/PowerShell/tree/master/docs/installation/linux.md#raspbian). 23 | 24 | ### Enable I2C interface on Raspberry Pi 25 | 26 | 1. `sudo raspi-config` 27 | 2. `5 Interfacing options` 28 | 3. `P5 I2C` 29 | 4. `Would you like ARM I2C interface to be enabled -> Yes` 30 | 31 | ### Start Powershell and install modules 32 | 33 | ```powershell 34 | sudo pwsh 35 | Install-Module -Name Microsoft.PowerShell.IoT 36 | git clone https://github.com/PowerShell/PowerShell-IoT.git 37 | Import-Module ./PowerShell-IoT/Examples/Microsoft.PowerShell.IoT.BME280 38 | ``` 39 | 40 | ### Collect Data 41 | ```powershell 42 | PS /home/pi> $device = Get-BME280Device -Id 0x77 # I2C address of the sensor 43 | PS /home/pi> $data = Get-BME280Data -Device $device 44 | PS /home/pi> $data 45 | 46 | Temperature Pressure Humidity 47 | ----------- -------- -------- 48 | 26.08 1009.41 29.21 49 | 50 | PS /home/pi> "Temperature in Fahrenheit = $($data.Temperature * 1.8 + 32)" 51 | Temperature in Fahrenheit = 76.69 52 | ``` -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Fan/Microsoft.PowerShell.IoT.Fan.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | GUID="0432ee36-7e87-4a21-814f-8feb17974647" 6 | Author="Microsoft Corporation" 7 | CompanyName="Microsoft Corporation" 8 | Copyright="© Microsoft Corporation. All rights reserved." 9 | Description='PowerShell module for controling a fan over GPIO.' 10 | ModuleVersion="0.1.0" 11 | FunctionsToExport = @('Enable-Fan','Disable-Fan') 12 | CmdletsToExport = @() 13 | AliasesToExport = @() 14 | RootModule = 'Microsoft.PowerShell.IoT.Fan.psm1' 15 | NestedModules=@('Microsoft.PowerShell.IoT') 16 | HelpInfoURI = 'https://github.com/PowerShell/PowerShell-IoT' 17 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 18 | PrivateData = @{ 19 | PSData = @{ 20 | # Tags applied to this module. These help with module discovery in online galleries. 21 | Tags = 'IoT','RaspberryPi','Raspbian' 22 | 23 | # A URL to the license for this module. 24 | LicenseUri = 'https://github.com/PowerShell/PowerShell-IoT/blob/master/LICENSE.txt' 25 | 26 | # A URL to the main website for this project. 27 | ProjectUri = 'https://github.com/PowerShell/PowerShell-IoT' 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Fan/Microsoft.PowerShell.IoT.Fan.psm1: -------------------------------------------------------------------------------- 1 | function Enable-Fan 2 | { 3 | [CmdletBinding()] 4 | param 5 | ( 6 | [Parameter(Mandatory=$true, Position=0)] 7 | [ValidateNotNullOrEmpty()] 8 | [int] $Pin 9 | ) 10 | 11 | Set-GpioPin -Id $Pin -Value High 12 | } 13 | 14 | function Disable-Fan 15 | { 16 | [CmdletBinding()] 17 | param 18 | ( 19 | [Parameter(Mandatory=$true, Position=0)] 20 | [ValidateNotNullOrEmpty()] 21 | [int] $Pin 22 | ) 23 | 24 | Set-GpioPin -Id $Pin -Value Low 25 | } 26 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Fan/README.md: -------------------------------------------------------------------------------- 1 | # Example module Microsoft.PowerShell.IoT.Fan 2 | 3 | This PowerShell module is for turning on/off a fan on Raspberry Pi 4 case enclosure. 4 | This showcases GPIO functionality of [the Microsoft.PowerShell.IoT module](../../README.md). 5 | 6 | ## Hardware setup 7 | 8 | [This Raspberry Pi 4 case enclosure](https://www.amazon.com/gp/product/B07XTRK8D4) comes with a 5V fan that can be connected to Raspberry Pi 5V and GND pins. 9 | This fan is nice but a little noisy so we can use this example module to turn it off when the CPU temperature is relatively low. 10 | An [IRLB8721 transistor](https://www.adafruit.com/product/355) can be used to switch power to the fan based on GPIO line of Raspberry Pi. 11 | 12 | ## Wiring 13 | 14 | Insert IRLB8721 transistor into the break of the negative wire of the fan. 15 | Connect transistor gate to GPIO 17 (BCM schema) on Raspberry Pi. 16 | 17 | ![Mossfet1](https://user-images.githubusercontent.com/11860095/79925701-d6a85580-83ef-11ea-894d-93507507df5e.jpg) 18 | ![Mossfet2](https://user-images.githubusercontent.com/11860095/79925725-e6c03500-83ef-11ea-9abd-7dd39be69bd1.jpg) 19 | 20 | ## Software setup 21 | 22 | ### Install PowerShell Core on Raspberry Pi 23 | 24 | Installation instructions can be found [here](https://github.com/PowerShell/PowerShell/tree/master/docs/installation/linux.md#raspbian). 25 | 26 | ### Start Powershell and install modules 27 | 28 | ```powershell 29 | pwsh 30 | 31 | Install-Module -Name Microsoft.PowerShell.IoT 32 | 33 | git clone https://github.com/PowerShell/PowerShell-IoT.git 34 | ``` 35 | 36 | ### Usage 37 | 38 | ```powershell 39 | # Start monitoring CPU temperature and turn on the fan when it reaches 71 degrees; turn fan off when CPU temperature drops below 55 degrees 40 | pwsh ./PowerShell-IoT/Examples/Microsoft.PowerShell.IoT.Fan/SmartFan.ps1 -Pin 17 -OnTemperature 71 -OffTemperature 55 -TemperatureScale Celsius 41 | VERBOSE: 1:36:05 PM: CPU temperature = 71.575 C | 160.835 F 42 | VERBOSE: Starting fan... 43 | VERBOSE: 1:36:10 PM: CPU temperature = 70.601 C | 159.0818 F 44 | VERBOSE: 1:36:16 PM: CPU temperature = 70.114 C | 158.2052 F 45 | VERBOSE: 1:36:21 PM: CPU temperature = 68.653 C | 155.5754 F 46 | #... 47 | VERBOSE: 1:39:01 PM: CPU temperature = 55.504 C | 131.9072 F 48 | VERBOSE: 1:39:06 PM: CPU temperature = 55.504 C | 131.9072 F 49 | VERBOSE: 1:39:11 PM: CPU temperature = 54.043 C | 129.2774 F 50 | VERBOSE: Stopping fan... 51 | VERBOSE: 1:39:17 PM: CPU temperature = 55.991 C | 132.7838 F 52 | #... 53 | ``` 54 | 55 | This produces following CPU temperature graph: 56 | ![CPU-Temp-graph](https://user-images.githubusercontent.com/11860095/79926138-fbe99380-83f0-11ea-8705-b1336fc7bd7d.png) -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Fan/SmartFan.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | [int] $Pin = 17, 4 | [int] $OnTemperature = 70, 5 | [int] $OffTemperature = 50, 6 | [ValidateSet("Fahrenheit","Celsius")] 7 | [string]$TemperatureScale = "Celsius", 8 | [int] $PollPeriodSeconds = 5 9 | ) 10 | 11 | Import-Module $PSScriptRoot/Microsoft.PowerShell.IoT.Fan.psd1 12 | 13 | $OnTemperatureC = $OnTemperature 14 | $OffTemperatureC = $OffTemperature 15 | if ($TemperatureScale -eq "Fahrenheit") 16 | { 17 | $OnTemperatureC = ($OnTemperature - 32) * 5 / 9 18 | $OffTemperatureC = ($OffTemperature - 32) * 5 / 9 19 | } 20 | 21 | while($true) 22 | { 23 | $CpuTemperatureC = (Get-Content /sys/class/thermal/thermal_zone0/temp) / 1000 24 | $CpuTemperatureF = ($CpuTemperatureC * 9 / 5) + 32 25 | 26 | (Get-Date).ToString() + ": CPU temperature = $CpuTemperatureC C | $CpuTemperatureF F" | Write-Verbose 27 | 28 | if ($CpuTemperatureC -gt $OnTemperatureC) 29 | { 30 | "Starting fan..." | Write-Verbose 31 | Enable-Fan -Pin $Pin 32 | } 33 | elseif ($CpuTemperatureC -lt $OffTemperatureC) 34 | { 35 | "Stopping fan..." | Write-Verbose 36 | Disable-Fan -Pin $Pin 37 | } 38 | 39 | Start-Sleep -Seconds $PollPeriodSeconds 40 | } 41 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.LED/Microsoft.PowerShell.IoT.LED.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | GUID="0432ee36-7e87-4a21-814f-8feb17974641" 6 | Author="Microsoft Corporation" 7 | CompanyName="Microsoft Corporation" 8 | Copyright="© Microsoft Corporation. All rights reserved." 9 | Description='PowerShell module for working with a single-color LED.' 10 | ModuleVersion="0.1.0" 11 | FunctionsToExport = @('Set-Led') 12 | CmdletsToExport = '*' 13 | AliasesToExport = @() 14 | NestedModules=@('Microsoft.PowerShell.IoT','Microsoft.PowerShell.IoT.LED.psm1') 15 | HelpInfoURI = 'https://github.com/PowerShell/PowerShell-IoT' 16 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 17 | PrivateData = @{ 18 | PSData = @{ 19 | # Tags applied to this module. These help with module discovery in online galleries. 20 | Tags = 'IoT','RaspberryPi','Raspbian','LED' 21 | 22 | # A URL to the license for this module. 23 | LicenseUri = 'https://github.com/PowerShell/PowerShell-IoT/blob/master/LICENSE.txt' 24 | 25 | # A URL to the main website for this project. 26 | ProjectUri = 'https://github.com/PowerShell/PowerShell-IoT' 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.LED/Microsoft.PowerShell.IoT.LED.psm1: -------------------------------------------------------------------------------- 1 | function Set-Led 2 | { 3 | [CmdletBinding()] 4 | param 5 | ( 6 | [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName="Pin")] 7 | [ValidateNotNullOrEmpty()] 8 | [string] $Pin, 9 | 10 | [Parameter(Mandatory=$true, Position=1, ValueFromPipelineByPropertyName="State")] 11 | [ValidateSet('On','Off',ignorecase=$true)] 12 | [string] $State 13 | ) 14 | if ($State -eq 'On') 15 | { 16 | $value = "High" 17 | } 18 | else 19 | { 20 | $value = "Low" 21 | } 22 | 23 | Set-GpioPin -Id $Pin -Value $value 24 | } 25 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.LED/README.md: -------------------------------------------------------------------------------- 1 | # Example module Microsoft.PowerShell.IoT.LED 2 | 3 | This simple PowerShell module is for turning on/off a single color LED. 4 | 5 | ![An LED is on](https://i.imgur.com/nJPJ9Vk.jpg) 6 | 7 | This showcases GPIO functionality of [the Microsoft.PowerShell.IoT module](../../README.md). 8 | 9 | ## Hardware setup 10 | 11 | Hardware pieces: 12 | 13 | * [Breadboard](https://en.wikipedia.org/wiki/Breadboard) (Optional) 14 | * Male to female [jumper wires](https://en.wikipedia.org/wiki/Jump_wire) 15 | * 1 270-330Ω resistor 16 | * A [single-color LED](http://upload.wikimedia.org/wikipedia/commons/e/e8/LEDs.jpg) 17 | 18 | ## Wiring diagram 19 | 20 | ![wiring](https://i.imgur.com/lCaxMWZ.png) 21 | 22 | ## Software setup 23 | 24 | ### Install PowerShell Core on Raspberry Pi 25 | 26 | Installation instructions can be found [here](https://github.com/PowerShell/PowerShell/tree/master/docs/installation/linux.md#raspbian). 27 | 28 | ### Start Powershell and install modules 29 | 30 | ```powershell 31 | sudo pwsh 32 | 33 | Install-Module -Name Microsoft.PowerShell.IoT 34 | 35 | git clone https://github.com/PowerShell/PowerShell-IoT.git 36 | 37 | Import-Module ./PowerShell-IoT/Examples/Microsoft.PowerShell.IoT.LED 38 | ``` 39 | 40 | ### Usage 41 | 42 | ```powershell 43 | # Turn LED on 44 | Set-Led -Pin 1 -State On 45 | # or 46 | Set-Led 1 On 47 | # or 48 | [PSCustomObject]@{Pin=1; State="On"} | Set-Led 49 | 50 | # Turn LED off 51 | Set-Led 1 Off 52 | ``` 53 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Plant/Microsoft.PowerShell.IoT.Plant.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | GUID="eb74e8da-9ae2-482a-a648-e96550fb8731" 6 | Author="Microsoft Corporation" 7 | CompanyName="Microsoft Corporation" 8 | Copyright="© Microsoft Corporation. All rights reserved." 9 | Description='PowerShell module for plant growth' 10 | ModuleVersion="0.1.0" 11 | FunctionsToExport = '*' 12 | CmdletsToExport = '*' 13 | AliasesToExport = @() 14 | NestedModules=@('Microsoft.PowerShell.IoT','Microsoft.PowerShell.IoT.Plant.psm1') 15 | HelpInfoURI = 'https://github.com/PowerShell/PowerShell-IoT' 16 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 17 | PrivateData = @{ 18 | PSData = @{ 19 | # Tags applied to this module. These help with module discovery in online galleries. 20 | Tags = 'IoT','RaspberryPi','Raspbian','Plant' 21 | 22 | # A URL to the license for this module. 23 | LicenseUri = 'https://github.com/PowerShell/PowerShell-IoT/blob/master/LICENSE.txt' 24 | 25 | # A URL to the main website for this project. 26 | ProjectUri = 'https://github.com/PowerShell/PowerShell-IoT' 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Plant/Microsoft.PowerShell.IoT.Plant.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | function Read-SoilIsDry 5 | { 6 | $raw = Get-GpioPin -Id 5 -Raw 7 | if($raw -eq "High") 8 | { 9 | return $true 10 | } 11 | else 12 | { 13 | return $false 14 | } 15 | } 16 | 17 | function Set-Light 18 | { 19 | param 20 | ( 21 | [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] 22 | [ValidateSet('On','Off',ignorecase=$true)] 23 | [string] $State 24 | ) 25 | if ($State -eq 'On') 26 | { 27 | $value = "High" 28 | } 29 | else 30 | { 31 | $value = "Low" 32 | } 33 | 34 | Set-GpioPin -Id 2 -Value $value 35 | } 36 | 37 | function Start-Water 38 | { 39 | Set-GpioPin -Id 0 -Value "Low" 40 | } 41 | 42 | function Stop-Water 43 | { 44 | Set-GpioPin -Id 0 -Value "High" 45 | } 46 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Plant/README.md: -------------------------------------------------------------------------------- 1 | # Example module Microsoft.PowerShell.IoT.Plant 2 | 3 | This PowerShell module is for automating taking care of potted indoor plants. 4 | 5 | This module allows a PowerShell script to control lights and water a plant when needed based on soil moisture level. 6 | 7 | This showcases GPIO functionality of [the Microsoft.PowerShell.IoT module](../../README.md). 8 | 9 | ## Hardware setup 10 | 11 | Hardware pieces: 12 | 13 | * For lights: 14 | * Desktop lamp 15 | * [Plant-specific light bulb](https://www.amazon.com/dp/B07567BPVH/ref=sspa_dk_detail_5?psc=1&pd_rd_i=B07567BPVH&pd_rd_wg=5iTv6&pd_rd_r=WYCAF6212XNJ9NQ14E5Q&pd_rd_w=2u0Gj) 16 | * [Power relay for power mains (for the lamp)](https://www.amazon.com/POWERSWITCHTAIL-COM-PowerSwitch-Tail-II/dp/B00B888VHM/ref=sr_1_1?ie=UTF8&qid=1518818881&sr=8-1) 17 | * For watering: 18 | * [Small water pump](https://www.adafruit.com/product/1150) 19 | * [Power Relay for 12V DC](https://www.ebay.com/itm/5V-2-Channel-Relay-Module-Shield-For-Arduino-PIC-ARM-DSP-AVR-Electronic-US/162876526032) 20 | * [12V DC PowerAdapter (for water pump)](https://www.adafruit.com/product/798) 21 | * [Soil moisture sensor](https://www.ebay.com/i/122408308563?chn=ps) 22 | 23 | Default pin configuration of Microsoft.PowerShell.IoT.Plant module: 24 | 25 | * Water pump relay connected to GPIO pin 0. 26 | * Light relay connected to GPIO pin 2. 27 | * Soil moisture sensor sends data to GPIO pin 5. 28 | 29 | Wiring diagram will be published shortly. 30 | 31 | ## Software setup 32 | 33 | ### Install PowerShell Core on Raspberry Pi 34 | 35 | Installation instructions can be found [here](https://github.com/PowerShell/PowerShell/tree/master/docs/installation/linux.md#raspbian). 36 | 37 | ### Start Powershell and install modules 38 | 39 | ```powershell 40 | sudo pwsh 41 | Install-Module -Name Microsoft.PowerShell.IoT 42 | git clone https://github.com/PowerShell/PowerShell-IoT.git 43 | Import-Module ./PowerShell-IoT/Examples/Microsoft.PowerShell.IoT.Plant 44 | ``` 45 | 46 | ### Manual operation 47 | 48 | ```powershell 49 | PS /home/pi> # working with light 50 | PS /home/pi> Set-Light On 51 | PS /home/pi> Set-Light Off 52 | PS /home/pi> 53 | PS /home/pi> # working with water 54 | PS /home/pi> Start-Water 55 | PS /home/pi> Stop-Water 56 | PS /home/pi> 57 | PS /home/pi> # reading soil moisture level 58 | PS /home/pi> Read-SoilIsDry 59 | ``` 60 | 61 | ### Automated operation 62 | 63 | See `full-plant-demo.ps1`. 64 | 65 | This script runs 2 PS jobs - one controls light, the other - water. 66 | 67 | For demo purposes script runs for 2 minutes. Adjust timeouts in the script for your scenario. 68 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Plant/full-plant-demo.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # A demo that uses Jobs to toggle lights and turn on the pump 5 | 6 | Import-Module Microsoft.PowerShell.IoT.Plant 7 | 8 | $lightjob = Start-Job { 9 | Import-Module Microsoft.PowerShell.IoT.Plant 10 | 11 | # start with lights off 12 | Set-Light Off 13 | $lightsOn = $false 14 | 15 | while ($true) { 16 | # toggle lights 17 | $lightsOn = -not $lightsOn 18 | if ($lightsOn) { 19 | Set-Light On 20 | } else { 21 | Set-Light Off 22 | } 23 | 24 | # wait some amount of time before toggling again 25 | Start-Sleep -s 20 26 | } 27 | } 28 | 29 | $waterJob = Start-Job { 30 | Import-Module Microsoft.PowerShell.IoT.Plant 31 | 32 | # start with water off 33 | Stop-Water 34 | 35 | while ($true) { 36 | 37 | if (Read-SoilIsDry) { 38 | 39 | # turn on the water for some amount of time 40 | Start-Water 41 | Start-Sleep -s 20 42 | Stop-Water 43 | 44 | # wait some amount of time before checking again 45 | Start-Sleep -s 20 46 | } 47 | 48 | # check sensor every 10 seconds 49 | Start-Sleep -s 10 50 | } 51 | } 52 | 53 | # clean up 54 | Start-Sleep -s 120 55 | Stop-Job $lightJob 56 | Stop-Job $waterJob 57 | 58 | Stop-Water 59 | Set-Light Off 60 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.SSD1306/Microsoft.PowerShell.IoT.SSD1306.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | GUID="eb74e8da-9ae2-482a-a648-e96550fb8741" 6 | Author="Microsoft Corporation" 7 | CompanyName="Microsoft Corporation" 8 | Copyright="© Microsoft Corporation. All rights reserved." 9 | Description='PowerShell module for working with SSD1306 I2C OLED display.' 10 | ModuleVersion="0.1.0" 11 | FunctionsToExport = @('New-OledDisplay','Set-OledText') 12 | CmdletsToExport = '*' 13 | AliasesToExport = @() 14 | NestedModules=@('Microsoft.PowerShell.IoT','Microsoft.PowerShell.IoT.SSD1306.psm1') 15 | HelpInfoURI = 'https://github.com/PowerShell/PowerShell-IoT' 16 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 17 | PrivateData = @{ 18 | PSData = @{ 19 | # Tags applied to this module. These help with module discovery in online galleries. 20 | Tags = 'IoT','RaspberryPi','Raspbian','SSD1306' 21 | 22 | # A URL to the license for this module. 23 | LicenseUri = 'https://github.com/PowerShell/PowerShell-IoT/blob/master/LICENSE.txt' 24 | 25 | # A URL to the main website for this project. 26 | ProjectUri = 'https://github.com/PowerShell/PowerShell-IoT' 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.SSD1306/Microsoft.PowerShell.IoT.SSD1306.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # Example usage: 5 | # New-OledDisplay | Set-OledText -Value "Hello from PowerShell" 6 | 7 | # SSD1306 datasheet can be found here: 8 | # https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf 9 | 10 | 11 | # Several screen sizes are supported; select the right one 12 | $SSD1306_128_64 = 1 13 | $SSD1306_128_32 = 0 14 | $SSD1306_96_16 = 0 15 | 16 | $BLACK = 0 17 | $WHITE = 1 18 | $INVERSE = 2 19 | 20 | $SSD1306_SETCONTRAST = 0x81 21 | $SSD1306_DISPLAYALLOW_RESUME = 0xA4 22 | $SSD1306_DISPLAYALLON = 0xA5 23 | $SSD1306_NORMALDISPLAY = 0xA6 24 | $SSD1306_INVERTDISPLAY = 0xA7 25 | $SSD1306_DISPLAYOFF = 0xAE 26 | $SSD1306_DISPLAYON = 0xAF 27 | $SSD1306_SETDISPLAYOFFSET = 0xD3 28 | $SSD1306_SETCOMPINS = 0xDA 29 | $SSD1306_SETVCOMDETECT = 0xDB 30 | $SSD1306_SETDISPLAYCLOCKDIV = 0xD5 31 | $SSD1306_SETPRECHARGE = 0xD9 32 | $SSD1306_SETMULTIPLEX = 0xA8 33 | $SSD1306_SETLOWCOLUMN = 0x00 34 | $SSD1306_SETHIGHCOLUMN = 0x10 35 | $SSD1306_SETSTARTLINE = 0x40 36 | $SSD1306_MEMORYMODE = 0x20 37 | $SSD1306_COLUMNADDR = 0x21 38 | $SSD1306_PAGEADDR = 0x22 39 | $SSD1306_COMSCANINC = 0xC0 40 | $SSD1306_COMSCANDEC = 0xC8 41 | $SSD1306_SEGREMAP = 0xA0 42 | $SSD1306_CHARGEPUMP = 0x8D 43 | $SSD1306_EXTERNALVCC = 0x1 44 | $SSD1306_SWITCHCAPVCC = 0x2 45 | $SSD1306_ACTIVATE_SCROLL = 0x2F 46 | $SSD1306_DEACTIVATE_SCROLL = 0x2E 47 | $SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3 48 | $SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26 49 | $SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27 50 | $SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29 51 | $SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A 52 | 53 | if ($SSD1306_128_64) 54 | { 55 | $SSD1306_LCDWIDTH = 128 56 | $SSD1306_LCDHEIGHT = 64 57 | $WIDTH = 128 58 | $HEIGHT = 64 59 | } 60 | if ($SSD1306_128_32) 61 | { 62 | $SSD1306_LCDWIDTH = 128 63 | $SSD1306_LCDHEIGHT = 32 64 | $WIDTH = 128 65 | $HEIGHT = 32 66 | } 67 | if ($SSD1306_96_16) 68 | { 69 | $SSD1306_LCDWIDTH = 96 70 | $SSD1306_LCDHEIGHT = 16 71 | $WIDTH = 96 72 | $HEIGHT = 16 73 | } 74 | 75 | [int] $script:cursor_y = 0 76 | [int] $script:cursor_x = 0 77 | [int] $script:textsize = 1 78 | [int] $script:wrap = 1 79 | 80 | # memory buffer for the LCD 81 | $buffer = New-Object byte[] ($SSD1306_LCDWIDTH * $SSD1306_LCDHEIGHT / 8) 82 | 83 | function New-OledDisplay 84 | { 85 | param 86 | ( 87 | [Parameter(Mandatory=$false)] 88 | [int] $Id = 0x3c, 89 | 90 | [Parameter(Mandatory=$false)] 91 | [string] $Name = "OLED" 92 | ) 93 | 94 | $Device = Get-I2CDevice -Id $Id -FriendlyName $Name 95 | 96 | $vccstate = $SSD1306_SWITCHCAPVCC; 97 | 98 | # Init process 99 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_DISPLAYOFF 100 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETDISPLAYCLOCKDIV 101 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x80 # the suggested ratio 0x80 102 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETMULTIPLEX 103 | Set-I2CRegister -Device $Device -Register 0x00 -Data ($SSD1306_LCDHEIGHT - 1) 104 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETDISPLAYOFFSET # 0xD3 105 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x0 106 | Set-I2CRegister -Device $Device -Register 0x00 -Data ($SSD1306_SETSTARTLINE -bor 0x0) 107 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_CHARGEPUMP 108 | if ($vccstate -eq $SSD1306_EXTERNALVCC) 109 | { 110 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x10 111 | } else 112 | { 113 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x14 114 | } 115 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_MEMORYMODE 116 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x00 117 | Set-I2CRegister -Device $Device -Register 0x00 -Data ($SSD1306_SEGREMAP -bor 0x1) 118 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_COMSCANDEC 119 | 120 | if ($SSD1306_128_32) 121 | { 122 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETCOMPINS 123 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x02 124 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETCONTRAST 125 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x8F 126 | } 127 | 128 | if ($SSD1306_128_64) 129 | { 130 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETCOMPINS 131 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x12 132 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETCONTRAST 133 | if ($vccstate -eq $SSD1306_EXTERNALVCC) 134 | { 135 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x9F 136 | } else 137 | { 138 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0xCF 139 | } 140 | } 141 | 142 | if ($SSD1306_96_16) 143 | { 144 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETCOMPINS 145 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x2 146 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETCONTRAST 147 | if ($vccstate -eq $SSD1306_EXTERNALVCC) 148 | { 149 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x10 150 | } else 151 | { 152 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0xAF 153 | } 154 | } 155 | 156 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETPRECHARGE 157 | if ($vccstate -eq $SSD1306_EXTERNALVCC) 158 | { 159 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x22 160 | } else 161 | { 162 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0xF1 163 | } 164 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_SETVCOMDETECT 165 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0x40 166 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_DISPLAYALLOW_RESUME 167 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_NORMALDISPLAY 168 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_DEACTIVATE_SCROLL 169 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_DISPLAYON 170 | 171 | $Device 172 | } 173 | 174 | function ClearDisplay 175 | { 176 | [Array]::Clear($buffer, 0, $buffer.Length) 177 | $script:cursor_y = 0; 178 | $script:cursor_x = 0; 179 | } 180 | 181 | function Display 182 | { 183 | param 184 | ( 185 | [Parameter(Mandatory=$true, ValueFromPipeline=$true)] 186 | [ValidateNotNullOrEmpty()] 187 | [Microsoft.PowerShell.IoT.I2CDevice] $Device 188 | ) 189 | 190 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_COLUMNADDR 191 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0 # Column start address (0 = reset) 192 | Set-I2CRegister -Device $Device -Register 0x00 -Data ($SSD1306_LCDWIDTH - 1) # Column end address 193 | 194 | Set-I2CRegister -Device $Device -Register 0x00 -Data $SSD1306_PAGEADDR 195 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0 # Page start address (0 = reset) 196 | if ($SSD1306_LCDHEIGHT -eq 64) 197 | { 198 | Set-I2CRegister -Device $Device -Register 0x00 -Data 7 # Page end address 199 | } 200 | if ($SSD1306_LCDHEIGHT -eq 32) 201 | { 202 | Set-I2CRegister -Device $Device -Register 0x00 -Data 3 # Page end address 203 | } 204 | if ($SSD1306_LCDHEIGHT -eq 16) 205 | { 206 | Set-I2CRegister -Device $Device -Register 0x00 -Data 1 # Page end address 207 | } 208 | 209 | for ($i = 0; $i -lt ($SSD1306_LCDWIDTH * $SSD1306_LCDHEIGHT / 8); $i++) 210 | { 211 | Set-I2CRegister -Device $Device -Register 0x40 -Data $buffer[$i] 212 | } 213 | } 214 | 215 | 216 | function DrawPixel 217 | { 218 | param 219 | ( 220 | [int] $x, 221 | [int] $y, 222 | [int] $color 223 | ) 224 | 225 | if (($x -lt 0) -or ($x -ge $WIDTH) -or ($y -lt 0) -or ($y -ge $HEIGHT)) 226 | { 227 | return 228 | } 229 | 230 | # x column 231 | $i = $x + [math]::truncate($y / 8) * $SSD1306_LCDWIDTH 232 | if ($color -eq $WHITE) 233 | { 234 | $buffer[$i] = $buffer[$i] -bor (1 -shl ($y -band 7)) 235 | } 236 | if ($color -eq $BLACK) 237 | { 238 | $buffer[$i] = $buffer[$i] -band -not(1 -shl ($y -band 7)) 239 | } 240 | if ($color -eq $INVERSE) 241 | { 242 | $buffer[$i] = $buffer[$i] -bxor (1 -shl ($y -band 7)); 243 | } 244 | } 245 | 246 | function DrawChar 247 | { 248 | param 249 | ( 250 | [int] $x, 251 | [int] $y, 252 | [byte] $c, 253 | [int] $color, 254 | [int] $size 255 | ) 256 | 257 | 258 | if (($x -ge $WIDTH) -or 259 | ($y -ge $HEIGHT) -or 260 | (($x + 6 * $size - 1) -lt 0) -or 261 | (($y + 8 * $size - 1) -lt 0)) 262 | { 263 | return 264 | } 265 | 266 | for ($i = 0; $i -lt 6; $i++) 267 | { 268 | [byte] $line = 0 269 | if ($i -eq 5) 270 | { 271 | $line = 0x0 272 | } 273 | else 274 | { 275 | $line = $font[($c * 5) + $i] 276 | } 277 | 278 | for ($j = 0; $j -lt 8; $j++) 279 | { 280 | if ($line -band 0x1) 281 | { 282 | if ($size -eq 1) # default size 283 | { 284 | DrawPixel ($x + $i) ($y + $j) $color 285 | } 286 | } 287 | $line = $line -shr 1 288 | } 289 | } 290 | } 291 | 292 | function WriteChar 293 | { 294 | param 295 | ( 296 | [byte] $c 297 | ) 298 | 299 | 300 | if ($c -eq '\n') 301 | { 302 | $script:cursor_y += $script:textsize * 8 303 | $script:cursor_x = 0 304 | } 305 | else 306 | { 307 | if ($c -eq '\r') 308 | { 309 | // skip 310 | } 311 | else 312 | { 313 | DrawChar $script:cursor_x $script:cursor_y $c $WHITE $script:textsize 314 | $script:cursor_x += $script:textsize * 6 315 | if ($script:wrap -and ($script:cursor_x -gt ($WIDTH - $script:textsize * 6))) 316 | { 317 | $script:cursor_y += $script:textsize * 8 318 | $script:cursor_x = 0 319 | } 320 | } 321 | } 322 | } 323 | 324 | function Set-OledText 325 | { 326 | param 327 | ( 328 | [Parameter(Mandatory=$true, ValueFromPipeline=$true)] 329 | [ValidateNotNullOrEmpty()] 330 | [Microsoft.PowerShell.IoT.I2CDevice] $Device, 331 | 332 | [string] $Value 333 | ) 334 | 335 | ClearDisplay 336 | $Value.ToCharArray() | %{ WriteChar $_ } 337 | Display $Device 338 | } 339 | 340 | # Standard ASCII 5x7 font Adaf 341 | [byte[]] $font = @( 342 | 0x00, 0x00, 0x00, 0x00, 0x00, 343 | 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 344 | 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 345 | 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 346 | 0x18, 0x3C, 0x7E, 0x3C, 0x18, 347 | 0x1C, 0x57, 0x7D, 0x57, 0x1C, 348 | 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 349 | 0x00, 0x18, 0x3C, 0x18, 0x00, 350 | 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 351 | 0x00, 0x18, 0x24, 0x18, 0x00, 352 | 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 353 | 0x30, 0x48, 0x3A, 0x06, 0x0E, 354 | 0x26, 0x29, 0x79, 0x29, 0x26, 355 | 0x40, 0x7F, 0x05, 0x05, 0x07, 356 | 0x40, 0x7F, 0x05, 0x25, 0x3F, 357 | 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 358 | 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 359 | 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 360 | 0x14, 0x22, 0x7F, 0x22, 0x14, 361 | 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 362 | 0x06, 0x09, 0x7F, 0x01, 0x7F, 363 | 0x00, 0x66, 0x89, 0x95, 0x6A, 364 | 0x60, 0x60, 0x60, 0x60, 0x60, 365 | 0x94, 0xA2, 0xFF, 0xA2, 0x94, 366 | 0x08, 0x04, 0x7E, 0x04, 0x08, 367 | 0x10, 0x20, 0x7E, 0x20, 0x10, 368 | 0x08, 0x08, 0x2A, 0x1C, 0x08, 369 | 0x08, 0x1C, 0x2A, 0x08, 0x08, 370 | 0x1E, 0x10, 0x10, 0x10, 0x10, 371 | 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 372 | 0x30, 0x38, 0x3E, 0x38, 0x30, 373 | 0x06, 0x0E, 0x3E, 0x0E, 0x06, 374 | 0x00, 0x00, 0x00, 0x00, 0x00, 375 | 0x00, 0x00, 0x5F, 0x00, 0x00, 376 | 0x00, 0x07, 0x00, 0x07, 0x00, 377 | 0x14, 0x7F, 0x14, 0x7F, 0x14, 378 | 0x24, 0x2A, 0x7F, 0x2A, 0x12, 379 | 0x23, 0x13, 0x08, 0x64, 0x62, 380 | 0x36, 0x49, 0x56, 0x20, 0x50, 381 | 0x00, 0x08, 0x07, 0x03, 0x00, 382 | 0x00, 0x1C, 0x22, 0x41, 0x00, 383 | 0x00, 0x41, 0x22, 0x1C, 0x00, 384 | 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 385 | 0x08, 0x08, 0x3E, 0x08, 0x08, 386 | 0x00, 0x80, 0x70, 0x30, 0x00, 387 | 0x08, 0x08, 0x08, 0x08, 0x08, 388 | 0x00, 0x00, 0x60, 0x60, 0x00, 389 | 0x20, 0x10, 0x08, 0x04, 0x02, 390 | 0x3E, 0x51, 0x49, 0x45, 0x3E, 391 | 0x00, 0x42, 0x7F, 0x40, 0x00, 392 | 0x72, 0x49, 0x49, 0x49, 0x46, 393 | 0x21, 0x41, 0x49, 0x4D, 0x33, 394 | 0x18, 0x14, 0x12, 0x7F, 0x10, 395 | 0x27, 0x45, 0x45, 0x45, 0x39, 396 | 0x3C, 0x4A, 0x49, 0x49, 0x31, 397 | 0x41, 0x21, 0x11, 0x09, 0x07, 398 | 0x36, 0x49, 0x49, 0x49, 0x36, 399 | 0x46, 0x49, 0x49, 0x29, 0x1E, 400 | 0x00, 0x00, 0x14, 0x00, 0x00, 401 | 0x00, 0x40, 0x34, 0x00, 0x00, 402 | 0x00, 0x08, 0x14, 0x22, 0x41, 403 | 0x14, 0x14, 0x14, 0x14, 0x14, 404 | 0x00, 0x41, 0x22, 0x14, 0x08, 405 | 0x02, 0x01, 0x59, 0x09, 0x06, 406 | 0x3E, 0x41, 0x5D, 0x59, 0x4E, 407 | 0x7C, 0x12, 0x11, 0x12, 0x7C, 408 | 0x7F, 0x49, 0x49, 0x49, 0x36, 409 | 0x3E, 0x41, 0x41, 0x41, 0x22, 410 | 0x7F, 0x41, 0x41, 0x41, 0x3E, 411 | 0x7F, 0x49, 0x49, 0x49, 0x41, 412 | 0x7F, 0x09, 0x09, 0x09, 0x01, 413 | 0x3E, 0x41, 0x41, 0x51, 0x73, 414 | 0x7F, 0x08, 0x08, 0x08, 0x7F, 415 | 0x00, 0x41, 0x7F, 0x41, 0x00, 416 | 0x20, 0x40, 0x41, 0x3F, 0x01, 417 | 0x7F, 0x08, 0x14, 0x22, 0x41, 418 | 0x7F, 0x40, 0x40, 0x40, 0x40, 419 | 0x7F, 0x02, 0x1C, 0x02, 0x7F, 420 | 0x7F, 0x04, 0x08, 0x10, 0x7F, 421 | 0x3E, 0x41, 0x41, 0x41, 0x3E, 422 | 0x7F, 0x09, 0x09, 0x09, 0x06, 423 | 0x3E, 0x41, 0x51, 0x21, 0x5E, 424 | 0x7F, 0x09, 0x19, 0x29, 0x46, 425 | 0x26, 0x49, 0x49, 0x49, 0x32, 426 | 0x03, 0x01, 0x7F, 0x01, 0x03, 427 | 0x3F, 0x40, 0x40, 0x40, 0x3F, 428 | 0x1F, 0x20, 0x40, 0x20, 0x1F, 429 | 0x3F, 0x40, 0x38, 0x40, 0x3F, 430 | 0x63, 0x14, 0x08, 0x14, 0x63, 431 | 0x03, 0x04, 0x78, 0x04, 0x03, 432 | 0x61, 0x59, 0x49, 0x4D, 0x43, 433 | 0x00, 0x7F, 0x41, 0x41, 0x41, 434 | 0x02, 0x04, 0x08, 0x10, 0x20, 435 | 0x00, 0x41, 0x41, 0x41, 0x7F, 436 | 0x04, 0x02, 0x01, 0x02, 0x04, 437 | 0x40, 0x40, 0x40, 0x40, 0x40, 438 | 0x00, 0x03, 0x07, 0x08, 0x00, 439 | 0x20, 0x54, 0x54, 0x78, 0x40, 440 | 0x7F, 0x28, 0x44, 0x44, 0x38, 441 | 0x38, 0x44, 0x44, 0x44, 0x28, 442 | 0x38, 0x44, 0x44, 0x28, 0x7F, 443 | 0x38, 0x54, 0x54, 0x54, 0x18, 444 | 0x00, 0x08, 0x7E, 0x09, 0x02, 445 | 0x18, 0xA4, 0xA4, 0x9C, 0x78, 446 | 0x7F, 0x08, 0x04, 0x04, 0x78, 447 | 0x00, 0x44, 0x7D, 0x40, 0x00, 448 | 0x20, 0x40, 0x40, 0x3D, 0x00, 449 | 0x7F, 0x10, 0x28, 0x44, 0x00, 450 | 0x00, 0x41, 0x7F, 0x40, 0x00, 451 | 0x7C, 0x04, 0x78, 0x04, 0x78, 452 | 0x7C, 0x08, 0x04, 0x04, 0x78, 453 | 0x38, 0x44, 0x44, 0x44, 0x38, 454 | 0xFC, 0x18, 0x24, 0x24, 0x18, 455 | 0x18, 0x24, 0x24, 0x18, 0xFC, 456 | 0x7C, 0x08, 0x04, 0x04, 0x08, 457 | 0x48, 0x54, 0x54, 0x54, 0x24, 458 | 0x04, 0x04, 0x3F, 0x44, 0x24, 459 | 0x3C, 0x40, 0x40, 0x20, 0x7C, 460 | 0x1C, 0x20, 0x40, 0x20, 0x1C, 461 | 0x3C, 0x40, 0x30, 0x40, 0x3C, 462 | 0x44, 0x28, 0x10, 0x28, 0x44, 463 | 0x4C, 0x90, 0x90, 0x90, 0x7C, 464 | 0x44, 0x64, 0x54, 0x4C, 0x44, 465 | 0x00, 0x08, 0x36, 0x41, 0x00, 466 | 0x00, 0x00, 0x77, 0x00, 0x00, 467 | 0x00, 0x41, 0x36, 0x08, 0x00, 468 | 0x02, 0x01, 0x02, 0x04, 0x02, 469 | 0x3C, 0x26, 0x23, 0x26, 0x3C, 470 | 0x1E, 0xA1, 0xA1, 0x61, 0x12, 471 | 0x3A, 0x40, 0x40, 0x20, 0x7A, 472 | 0x38, 0x54, 0x54, 0x55, 0x59, 473 | 0x21, 0x55, 0x55, 0x79, 0x41, 474 | 0x21, 0x54, 0x54, 0x78, 0x41, 475 | 0x21, 0x55, 0x54, 0x78, 0x40, 476 | 0x20, 0x54, 0x55, 0x79, 0x40, 477 | 0x0C, 0x1E, 0x52, 0x72, 0x12, 478 | 0x39, 0x55, 0x55, 0x55, 0x59, 479 | 0x39, 0x54, 0x54, 0x54, 0x59, 480 | 0x39, 0x55, 0x54, 0x54, 0x58, 481 | 0x00, 0x00, 0x45, 0x7C, 0x41, 482 | 0x00, 0x02, 0x45, 0x7D, 0x42, 483 | 0x00, 0x01, 0x45, 0x7C, 0x40, 484 | 0xF0, 0x29, 0x24, 0x29, 0xF0, 485 | 0xF0, 0x28, 0x25, 0x28, 0xF0, 486 | 0x7C, 0x54, 0x55, 0x45, 0x00, 487 | 0x20, 0x54, 0x54, 0x7C, 0x54, 488 | 0x7C, 0x0A, 0x09, 0x7F, 0x49, 489 | 0x32, 0x49, 0x49, 0x49, 0x32, 490 | 0x32, 0x48, 0x48, 0x48, 0x32, 491 | 0x32, 0x4A, 0x48, 0x48, 0x30, 492 | 0x3A, 0x41, 0x41, 0x21, 0x7A, 493 | 0x3A, 0x42, 0x40, 0x20, 0x78, 494 | 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 495 | 0x39, 0x44, 0x44, 0x44, 0x39, 496 | 0x3D, 0x40, 0x40, 0x40, 0x3D, 497 | 0x3C, 0x24, 0xFF, 0x24, 0x24, 498 | 0x48, 0x7E, 0x49, 0x43, 0x66, 499 | 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 500 | 0xFF, 0x09, 0x29, 0xF6, 0x20, 501 | 0xC0, 0x88, 0x7E, 0x09, 0x03, 502 | 0x20, 0x54, 0x54, 0x79, 0x41, 503 | 0x00, 0x00, 0x44, 0x7D, 0x41, 504 | 0x30, 0x48, 0x48, 0x4A, 0x32, 505 | 0x38, 0x40, 0x40, 0x22, 0x7A, 506 | 0x00, 0x7A, 0x0A, 0x0A, 0x72, 507 | 0x7D, 0x0D, 0x19, 0x31, 0x7D, 508 | 0x26, 0x29, 0x29, 0x2F, 0x28, 509 | 0x26, 0x29, 0x29, 0x29, 0x26, 510 | 0x30, 0x48, 0x4D, 0x40, 0x20, 511 | 0x38, 0x08, 0x08, 0x08, 0x08, 512 | 0x08, 0x08, 0x08, 0x08, 0x38, 513 | 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 514 | 0x2F, 0x10, 0x28, 0x34, 0xFA, 515 | 0x00, 0x00, 0x7B, 0x00, 0x00, 516 | 0x08, 0x14, 0x2A, 0x14, 0x22, 517 | 0x22, 0x14, 0x2A, 0x14, 0x08, 518 | 0xAA, 0x00, 0x55, 0x00, 0xAA, 519 | 0xAA, 0x55, 0xAA, 0x55, 0xAA, 520 | 0x00, 0x00, 0x00, 0xFF, 0x00, 521 | 0x10, 0x10, 0x10, 0xFF, 0x00, 522 | 0x14, 0x14, 0x14, 0xFF, 0x00, 523 | 0x10, 0x10, 0xFF, 0x00, 0xFF, 524 | 0x10, 0x10, 0xF0, 0x10, 0xF0, 525 | 0x14, 0x14, 0x14, 0xFC, 0x00, 526 | 0x14, 0x14, 0xF7, 0x00, 0xFF, 527 | 0x00, 0x00, 0xFF, 0x00, 0xFF, 528 | 0x14, 0x14, 0xF4, 0x04, 0xFC, 529 | 0x14, 0x14, 0x17, 0x10, 0x1F, 530 | 0x10, 0x10, 0x1F, 0x10, 0x1F, 531 | 0x14, 0x14, 0x14, 0x1F, 0x00, 532 | 0x10, 0x10, 0x10, 0xF0, 0x00, 533 | 0x00, 0x00, 0x00, 0x1F, 0x10, 534 | 0x10, 0x10, 0x10, 0x1F, 0x10, 535 | 0x10, 0x10, 0x10, 0xF0, 0x10, 536 | 0x00, 0x00, 0x00, 0xFF, 0x10, 537 | 0x10, 0x10, 0x10, 0x10, 0x10, 538 | 0x10, 0x10, 0x10, 0xFF, 0x10, 539 | 0x00, 0x00, 0x00, 0xFF, 0x14, 540 | 0x00, 0x00, 0xFF, 0x00, 0xFF, 541 | 0x00, 0x00, 0x1F, 0x10, 0x17, 542 | 0x00, 0x00, 0xFC, 0x04, 0xF4, 543 | 0x14, 0x14, 0x17, 0x10, 0x17, 544 | 0x14, 0x14, 0xF4, 0x04, 0xF4, 545 | 0x00, 0x00, 0xFF, 0x00, 0xF7, 546 | 0x14, 0x14, 0x14, 0x14, 0x14, 547 | 0x14, 0x14, 0xF7, 0x00, 0xF7, 548 | 0x14, 0x14, 0x14, 0x17, 0x14, 549 | 0x10, 0x10, 0x1F, 0x10, 0x1F, 550 | 0x14, 0x14, 0x14, 0xF4, 0x14, 551 | 0x10, 0x10, 0xF0, 0x10, 0xF0, 552 | 0x00, 0x00, 0x1F, 0x10, 0x1F, 553 | 0x00, 0x00, 0x00, 0x1F, 0x14, 554 | 0x00, 0x00, 0x00, 0xFC, 0x14, 555 | 0x00, 0x00, 0xF0, 0x10, 0xF0, 556 | 0x10, 0x10, 0xFF, 0x10, 0xFF, 557 | 0x14, 0x14, 0x14, 0xFF, 0x14, 558 | 0x10, 0x10, 0x10, 0x1F, 0x00, 559 | 0x00, 0x00, 0x00, 0xF0, 0x10, 560 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 561 | 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 562 | 0xFF, 0xFF, 0xFF, 0x00, 0x00, 563 | 0x00, 0x00, 0x00, 0xFF, 0xFF, 564 | 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 565 | 0x38, 0x44, 0x44, 0x38, 0x44, 566 | 0x7C, 0x2A, 0x2A, 0x3E, 0x14, 567 | 0x7E, 0x02, 0x02, 0x06, 0x06, 568 | 0x02, 0x7E, 0x02, 0x7E, 0x02, 569 | 0x63, 0x55, 0x49, 0x41, 0x63, 570 | 0x38, 0x44, 0x44, 0x3C, 0x04, 571 | 0x40, 0x7E, 0x20, 0x1E, 0x20, 572 | 0x06, 0x02, 0x7E, 0x02, 0x02, 573 | 0x99, 0xA5, 0xE7, 0xA5, 0x99, 574 | 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 575 | 0x4C, 0x72, 0x01, 0x72, 0x4C, 576 | 0x30, 0x4A, 0x4D, 0x4D, 0x30, 577 | 0x30, 0x48, 0x78, 0x48, 0x30, 578 | 0xBC, 0x62, 0x5A, 0x46, 0x3D, 579 | 0x3E, 0x49, 0x49, 0x49, 0x00, 580 | 0x7E, 0x01, 0x01, 0x01, 0x7E, 581 | 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 582 | 0x44, 0x44, 0x5F, 0x44, 0x44, 583 | 0x40, 0x51, 0x4A, 0x44, 0x40, 584 | 0x40, 0x44, 0x4A, 0x51, 0x40, 585 | 0x00, 0x00, 0xFF, 0x01, 0x03, 586 | 0xE0, 0x80, 0xFF, 0x00, 0x00, 587 | 0x08, 0x08, 0x6B, 0x6B, 0x08, 588 | 0x36, 0x12, 0x36, 0x24, 0x36, 589 | 0x06, 0x0F, 0x09, 0x0F, 0x06, 590 | 0x00, 0x00, 0x18, 0x18, 0x00, 591 | 0x00, 0x00, 0x10, 0x10, 0x00, 592 | 0x30, 0x40, 0xFF, 0x01, 0x01, 593 | 0x00, 0x1F, 0x01, 0x01, 0x1E, 594 | 0x00, 0x19, 0x1D, 0x17, 0x12, 595 | 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 596 | 0x00, 0x00, 0x00, 0x00, 0x00) 597 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.SSD1306/README.md: -------------------------------------------------------------------------------- 1 | # Example module Microsoft.PowerShell.IoT.SSD1306 2 | 3 | This PowerShell module is for working with [small I2C OLED displays](https://www.ebay.com/itm/2X-0-96-I2C-IIC-Serial-128X64-LED-OLED-LCD-Display-Module-for-Arduino-White/191785893008?epid=2001476960&hash=item2ca7547c90:g:WbwAAOSwAPVZLOx~) based on [SSD1306 driver chip](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf). 4 | 5 | ## Hardware setup 6 | 7 | There are many versions of small OLED displays; in this example we'll use a 128x64 version based on I2C interface. 8 | 9 | Wiring diagram with Raspberry Pi 3 is like this: 10 | 11 | ![ssd1306_schema](https://user-images.githubusercontent.com/11860095/38397481-a8d7eea8-38f2-11e8-8986-eae21e6f384e.png) 12 | 13 | ## Software setup 14 | 15 | ### Install PowerShell Core on Raspberry Pi 16 | 17 | Installation instructions can be found [here](https://github.com/PowerShell/PowerShell/tree/master/docs/installation/linux.md#raspbian). 18 | 19 | ### Enable I2C interface on Raspberry Pi 20 | 21 | 1. `sudo raspi-config` 22 | 2. `5 Interfacing options` 23 | 3. `P5 I2C` 24 | 4. `Would you like ARM I2C interface to be enabled -> Yes` 25 | 26 | ### Start Powershell and install modules 27 | 28 | ```powershell 29 | sudo pwsh 30 | Install-Module -Name Microsoft.PowerShell.IoT 31 | git clone https://github.com/PowerShell/PowerShell-IoT.git 32 | Import-Module ./PowerShell-IoT/Examples/Microsoft.PowerShell.IoT.SSD1306 33 | ``` 34 | 35 | ### Run example 36 | ```powershell 37 | PS /home/pi> New-OledDisplay | Set-OledText -Value "Hello from PowerShell" 38 | ``` -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Scroll_pHat/Microsoft.PowerShell.IoT.SadJoey.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Import-Module Microsoft.PowerShell.IoT 5 | 6 | [int]$DeviceAddress = 0x60 7 | ######### Configuration Register and Value ######### 8 | [int]$ConfigurationRegisterAddress = 0x00 9 | [int]$ConfigurationRegisterValue = 0x1B 10 | 11 | ######### Data Registers and value ######### 12 | [int[]]$DataRegisterAddress = 0x04 ..0x08 13 | [int[]]$values = 0x12,0x08,0x08,0x08,0x12 14 | ######### Brightness Register and value ######### 15 | [int]$BrightnessRegisterAddress = 0x0D 16 | [int]$BrightnessRegisterValue = 0x08 #Lowest intensity 17 | 18 | ######### Get the device and set the Configuration Register 19 | $Device = Get-I2CDevice -Id $DeviceAddress -FriendlyName phat 20 | Set-I2CRegister -Device $Device -Register $ConfigurationRegisterAddress -Data $ConfigurationRegisterValue 21 | 22 | ######## Brightness ##### 23 | Set-I2CRegister -Device $Device -Register $BrightnessRegisterAddress -Data $BrightnessRegisterValue 24 | 25 | ######### Write the #sadJoey pattern to the Data registers ######### 26 | $i = 0 27 | foreach ($register in $DataRegisterAddress) { 28 | Set-I2CRegister -Device $Device -Register $register -Data $values[$i] 29 | $i++ 30 | } 31 | 32 | #In order to update the registers, we need to write something to the column register, accoding to the datasheet: "A write operation of any 8-bit value to the Update Column Register is required to update the Data Registers" 33 | [int]$UpdateRegisterAddress = 0x0C 34 | [int]$UpdateValue = 0xFF 35 | 36 | #After executing this instruction, a Sad Joey should appear on your pHat :) 37 | Set-I2CRegister -Device $Device -Register $UpdateRegisterAddress -Data $UpdateValue 38 | -------------------------------------------------------------------------------- /Examples/Microsoft.PowerShell.IoT.Scroll_pHat/README.md: -------------------------------------------------------------------------------- 1 | # Example on how to interact with Scroll pHat 2 | 3 | With this example, we will see how we can interact with [Scroll pHat](https://shop.pimoroni.com/products/scroll-phat). 4 | 5 | ## Information 6 | 7 | After reading the [driver's data sheet](http://www.issi.com/WW/pdf/31FL3730.pdf) we know the following: 8 | 9 | There are three "types" of registers: Configuration register, Update register and data registers. 10 | The configuration register value is set to have the expected behaviour of this pHat. 11 | 12 | There are 11 data registers, ranging from address 0x01 to 1x0B. 13 | Each register holds 1byte, but since ScrollPHat only has 5 lines, only 5bits are used. 14 | To set the LED on, you need to set the correspondent bits as 1, as example, to achieve the following: 15 | 16 | ![](https://i.imgur.com/nII0q7B.jpg) 17 | 18 | we need to set the register 1 with data 0x0D (0000 1101). 19 | 20 | ## Software setup 21 | 22 | ### Install PowerShell Core on Raspberry Pi 23 | 24 | Installation instructions can be found [here](https://github.com/PowerShell/PowerShell/tree/master/docs/installation/linux.md#raspbian). 25 | 26 | ### Enable I2C interface on Raspberry Pi 27 | 28 | 1. `sudo raspi-config` 29 | 2. `5 Interfacing options` 30 | 3. `P5 I2C` 31 | 4. `Would you like ARM I2C interface to be enabled -> Yes` 32 | 33 | Start PowerShell (**with sudo, so that you can access the I2C bus**) 34 | 35 | ```powershell 36 | sudo pwsh 37 | 38 | git clone https://github.com/PowerShell/PowerShell-IoT.git #if you haven't already 39 | ./PowerShell-IoT/Examples/Microsoft.PowerShell.IoT.Scroll_pHat/Microsoft.PowerShell.IoT.SadJoey.ps1 40 | ``` 41 | 42 | After running this code, you should see a "Sad Joey" on your Scroll pHat. 43 | 44 | Note: What's "Sad Joey"? - #SadJoey became "popular" meme/hastag [on twitter](https://twitter.com/search?q=%23sadJoey&src=typd) during the #PSConfEU 2018 45 | 46 | This is what you should see: 47 | 48 | ![](https://i.imgur.com/112YDk4.jpg) -------------------------------------------------------------------------------- /Examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Example modules that leverage PowerShell IoT in a specific way. 4 | 5 | ## Table of Contents 6 | 7 | | Example | Difficulty | Type | 8 | |---------|------------|------| 9 | | [Microsoft.PowerShell.IoT.LED](/Examples/Microsoft.PowerShell.IoT.LED) | Easy | GPIO | 10 | | [Microsoft.PowerShell.IoT.Scroll_pHat](/Examples/Microsoft.PowerShell.IoT.Scroll_pHat) | Easy | I2C | 11 | | [Microsoft.PowerShell.IoT.BME280](/Examples/Microsoft.PowerShell.IoT.BME280) | Medium | I2C | 12 | | [Microsoft.PowerShell.IoT.SSD1306](/Examples/Microsoft.PowerShell.IoT.SSD1306) | Medium | I2C | 13 | | [Microsoft.PowerShell.IoT.ADXL345](/Examples/Microsoft.PowerShell.IoT.ADXL345) | Medium | I2C | 14 | | [Microsoft.PowerShell.Plant](/Examples/Microsoft.PowerShell.IoT.Plant) | Hard | GPIO | 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Microsoft Corporation. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Move-PSIoTBuild.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | param ( 5 | [Parameter(ValueFromPipeline=$true, Mandatory=$true)] 6 | [ValidateNotNullOrEmpty()] 7 | [string[]] 8 | $Ip, 9 | 10 | [Parameter()] 11 | [string[]] 12 | $WithExample, 13 | 14 | [Parameter()] 15 | [switch] 16 | $Build 17 | ) 18 | begin { 19 | $sessions = @() 20 | } 21 | 22 | process { 23 | Write-Host "Connecting to $Ip" 24 | $sessions += New-PSSession -HostName $Ip -UserName pi 25 | } 26 | 27 | end { 28 | if ($Build) { 29 | Invoke-Build 30 | } 31 | 32 | $sessions | ForEach-Object { 33 | $session = $_ 34 | 35 | # Should compress and decompress 36 | Copy-Item "$PSScriptRoot\out\Microsoft.PowerShell.IoT" "/usr/local/share/powershell/Modules/Microsoft.PowerShell.IoT" -Recurse -Force -ToSession $session 37 | 38 | if ($WithExample) { 39 | $WithExample | ForEach-Object { 40 | $path = "$PSScriptRoot\Examples\$_" 41 | if (Test-Path $path) { 42 | # Should compress and decompress 43 | Copy-Item $path "/usr/local/share/powershell/Modules" -Recurse -Force -ToSession $session 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell-IoT 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/ipvxu77rxb5ou8gb?svg=true)](https://ci.appveyor.com/project/PowerShell/powershell-iot) 4 | [![Travis](https://img.shields.io/travis/rust-lang/rust.svg?logo=travis)](https://travis-ci.com/PowerShell/PowerShell-IoT) 5 | [![PowerShell Gallery](https://img.shields.io/powershellgallery/v/Microsoft.PowerShell.IoT.svg)](https://www.powershellgallery.com/packages/Microsoft.PowerShell.IoT/) 6 | 7 | > Note: PowerShell IoT is still in Preview 8 | 9 | A PowerShell module for interacting with hardware sensors and devices using common protocols: GPIO, I2C & SPI. 10 | 11 | ![An SSD1306 displaying "Hello World from PowerShell"](https://pbs.twimg.com/media/DV8c8Y3V4Ac7PaH.jpg:small) 12 | 13 | ## Information 14 | 15 | ### Goals 16 | 17 | The main goal of this project is to provide a friendly interface for interacting with hardware sensors and devices using PowerShell. 18 | 19 | That said, 20 | it was built as close to the metal as possible to keep the library broad enough to cover a range of sensors and devices. 21 | 22 | The hope is that this module will be the foundation for other modules that will expose specific cmdlets for interacting with specific sensors and devices. 23 | 24 | For example, a cmdlet stack to turn on a light bulb might be: 25 | 26 | ```powershell 27 | > Set-Light On # Your user types this / you make this cmdlet 28 | > Set-GpioPin -Id 4 -Value High # You use this to make that^ / we make this cmdlet 29 | > # Our code that makes that^ 30 | ``` 31 | 32 | To see some examples of modules built on top of PowerShell IoT, see the [Examples folder](/Examples). 33 | 34 | ### Supported platforms 35 | 36 | #### Supported devices 37 | 38 | * Raspberry Pi 3 39 | * Raspberry Pi 2 40 | 41 | #### Supported operating systems 42 | 43 | * Raspbian Stretch 44 | 45 | ### Documentation & Examples 46 | 47 | Please see our [docs folder here](/docs) for an API reference, pin layout and other docs. For examples, checkout our [examples folder](/Examples). 48 | 49 | ### Dependencies 50 | 51 | This project relies on [.NET Core IoT Libraries](https://github.com/dotnet/iot). 52 | It is a .NET library for interacting with Raspberry Pi's IO functionality. 53 | 54 | ## Installation 55 | 56 | ### PowerShell Gallery 57 | 58 | You can grab the latest version of PowerShell IoT by running: 59 | 60 | ```powershell 61 | Install-Module Microsoft.PowerShell.IoT 62 | ``` 63 | 64 | Please note that since this module works with Hardware, higher privileges are required (run PowerShell with `sudo`, or as `root` user) 65 | 66 | Then see the section on [running](#running). 67 | 68 | If you want to write a module that uses PowerShell IoT, include it in the `RequiredModules` field of your module manifest. 69 | 70 | ### GitHub releases 71 | 72 | You can also manually download the zipped up module from the [releases](https://github.com/PowerShell/PowerShell-IoT/releases). 73 | 74 | Then see the section on [running](#running). 75 | 76 | ### AppVeyor 77 | 78 | You can download the latest CI build from our [AppVeyor build here](https://ci.appveyor.com/project/PowerShell/powershell-iot). 79 | Go to the latest build, click on either of the images, then click on the artifacts tab. 80 | From there, you can download a zip of the latest CI build. 81 | 82 | Then see the section on [running](#running). 83 | 84 | ### From Source 85 | 86 | #### Prerequisites 87 | 88 | * [PowerShell Core 6 or greater](https://github.com/PowerShell/PowerShell/releases) 89 | * [.NET Core SDK 2.0 or greater](https://www.microsoft.com/net/download/) 90 | * [InvokeBuild](https://www.powershellgallery.com/packages/InvokeBuild/) 91 | * A supported device like a [Raspberry Pi 3](https://www.raspberrypi.org/) with [PowerShell Core 6 on it](https://github.com/powershell/powershell#get-powershell) 92 | 93 | #### Building 94 | 95 | _NOTE: You can't build on ARM devices at this time so you will need to build on another machine and copy the build to the device._ 96 | 97 | 1. Clone/download the repo 98 | 2. run `./build.ps1 -Bootstrap` to see if you're missing any tooling 99 | 3. run `./build.ps1` to build 100 | 101 | At this point, you'll notice an `out` folder has been generated in the root of your repo. 102 | The project is ready to be deployed to your device. 103 | 104 | #### Deploying 105 | 106 | We have included a helper script, `Move-PSIoTBuild.ps1`, 107 | that will move the PowerShell IoT build over to your device. 108 | This copy uses PSRP over SSH so make sure you're able to connect to your pi this way. 109 | The `Microsoft.PowerShell.IoT` module will be copied to your `$env:PSModulePath` on your device. 110 | Here's an example: 111 | 112 | ```powershell 113 | Move-PSIoTBuild.ps1 -Ip 10.123.123.123 # IP address of device 114 | ``` 115 | 116 | You can also easily copy examples in the `Examples` folder over using the `-WithExample` flag. 117 | Just give a list of examples you want to copy over and it will move those to you `$env:PSModulePath` along with `Microsoft.PowerShell.IoT`: 118 | 119 | ```powershell 120 | Move-PSIoTBuild.ps1 -Ip 10.123.123.123 -WithExample Microsoft.PowerShell.IoT.Plant,Microsoft.PowerShell.IoT.SSD1306 121 | ``` 122 | 123 | Also, with the `-Build` parameter, 124 | it will build/test/package your project before moving it. 125 | 126 | _NOTE: If you'd rather not use the script, simply copy the `out/Microsoft.PowerShell.IoT` to your device to get started._ 127 | 128 | #### Running 129 | 130 | Start PowerShell: 131 | 132 | ```powershell 133 | pwsh 134 | ``` 135 | 136 | If you have the `Microsoft.PowerShell.IoT` module in your PSModulePath: 137 | 138 | ```powershell 139 | Import-Module Microsoft.PowerShell.IoT 140 | ``` 141 | 142 | Alternatively, just import the `.psd1`: 143 | 144 | ```powershell 145 | Import-Module /path/to/Microsoft.PowerShell.IoT/Microsoft.PowerShell.IoT.psd1 146 | ``` 147 | 148 | At this point you can now mess with the module: 149 | 150 | ```powershell 151 | Get-Command -Module Microsoft.PowerShell.IoT 152 | Get-GpioPin 2 # gets the data from GPIO pin 2 153 | ``` 154 | 155 | #### Testing 156 | 157 | You can run tests, but they require a particular setup. Here is how you run them: 158 | 159 | ```powershell 160 | ./build.ps1 -Test 161 | ``` 162 | 163 | The setup required: 164 | 165 | * For I2C: An [Adafruit BME280 I2C or SPI Temperature Humidity Pressure Sensor](https://www.adafruit.com/product/2652) 166 | * For GPIO: Bend pins 26 and 22 to touch each other or connect them in some way 167 | * For SPI: An [Adafruit LIS3DH Triple-Axis Accelerometer](https://www.adafruit.com/product/2809) 168 | 169 | We currently have a build agent that will deploy PR code onto a test Raspberry Pi and run the tests found in the `test` directory. 170 | -------------------------------------------------------------------------------- /SimpleBuild.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Parameter()] 3 | $Runtime = "linux-arm" 4 | ) 5 | 6 | if ((Test-Path "$PSScriptRoot\out")) { 7 | Remove-Item -Path $PSScriptRoot\out -Recurse -Force 8 | } 9 | 10 | Push-Location "$PSScriptRoot\src\Microsoft.PowerShell.IoT" 11 | dotnet publish --runtime $Runtime 12 | Pop-Location 13 | 14 | New-Item -ItemType directory -Path $PSScriptRoot\out | Out-Null 15 | New-Item -ItemType directory -Path $PSScriptRoot\out\Microsoft.PowerShell.IoT | Out-Null 16 | 17 | $OutModulePath = "$PSScriptRoot\out\Microsoft.PowerShell.IoT" 18 | 19 | Copy-Item -Path "$PSScriptRoot\src\Microsoft.PowerShell.IoT\Microsoft.PowerShell.IoT.psd1" -Destination $OutModulePath -Force 20 | Copy-Item -Path "$PSScriptRoot\src\Microsoft.PowerShell.IoT\bin\Debug\netstandard2.0\$Runtime\publish\*" -Destination $OutModulePath -Force -Recurse 21 | 22 | "Build module location: $OutModulePath" | Write-Verbose -Verbose 23 | 24 | "Setting VSTS variable 'BuildOutDir' to '$OutModulePath'" | Write-Verbose -Verbose 25 | Write-Host "##vso[task.setvariable variable=BuildOutDir]$OutModulePath" 26 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: 2 | - Visual Studio 2015 3 | - Ubuntu 4 | 5 | build: off 6 | 7 | # Ignore testing a commit if only the README.md file changed 8 | # Or if various strings are found in the commit message: updated readme, update readme, update docs, update version, update appveyor 9 | skip_commits: 10 | files: 11 | - README.md 12 | message: /updated readme.*|update readme.*s|update docs.*|update version.*|update appveyor.*/ 13 | 14 | branches: 15 | only: 16 | - master 17 | 18 | test_script: 19 | - pwsh: Install-Module InvokeBuild -Force -Scope CurrentUser; Invoke-Build 20 | 21 | artifacts: 22 | - path: out\Microsoft.PowerShell.IoT 23 | name: Microsoft.PowerShell.IoT 24 | -------------------------------------------------------------------------------- /azure-pipelines-release.yml: -------------------------------------------------------------------------------- 1 | name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) 2 | 3 | trigger: none 4 | 5 | resources: 6 | repositories: 7 | - repository: ComplianceRepo 8 | type: github 9 | endpoint: ComplianceGHRepo 10 | name: PowerShell/compliance 11 | 12 | variables: 13 | - name: PackageName 14 | value: 'Microsoft.PowerShell.IoT' 15 | - name: PackageVersion 16 | value: '0.2.0' 17 | - name: BuildOutDir 18 | value: '' 19 | 20 | stages: 21 | - stage: Build 22 | displayName: Build module 23 | pool: 24 | name: 1ES 25 | demands: 26 | - ImageOverride -equals PSMMS2019-Secure 27 | jobs: 28 | - job: BuildPkg 29 | displayName: Build module 30 | variables: 31 | - group: ESRP 32 | steps: 33 | 34 | - pwsh: | 35 | & $(Build.SourcesDirectory)\SimpleBuild.ps1 36 | displayName: Build Microsoft.PowerShell.IoT module 37 | condition: succeededOrFailed() 38 | 39 | - pwsh: | 40 | dir "$(BuildOutDir)\*" -Recurse 41 | displayName: Show BuildOutDirectory 42 | 43 | - template: Sbom.yml@ComplianceRepo 44 | parameters: 45 | BuildDropPath: "$(BuildOutDir)" 46 | Build_Repository_Uri: 'https://github.com/PowerShell/PowerShell-IoT' 47 | PackageName: $(PackageName) 48 | PackageVersion: $(PackageVersion) 49 | 50 | - pwsh: | 51 | dir "$(BuildOutDir)\*" -Recurse 52 | displayName: Show BuildOutDirectory 53 | 54 | - pwsh: | 55 | $signSrcPath = "$(BuildOutDir)" 56 | # Set signing src path variable 57 | $vstsCommandString = "vso[task.setvariable variable=signSrcPath]${signSrcPath}" 58 | Write-Host "sending " + $vstsCommandString 59 | Write-Host "##$vstsCommandString" 60 | $signOutPath = "$(Build.SourcesDirectory)\signed\Microsoft.PowerShell.IoT" 61 | $null = New-Item -ItemType Directory -Path $signOutPath 62 | # Set signing out path variable 63 | $vstsCommandString = "vso[task.setvariable variable=signOutPath]${signOutPath}" 64 | Write-Host "sending " + $vstsCommandString 65 | Write-Host "##$vstsCommandString" 66 | # Set path variable for guardian codesign validation 67 | $vstsCommandString = "vso[task.setvariable variable=GDN_CODESIGN_TARGETDIRECTORY]${signOutPath}" 68 | Write-Host "sending " + $vstsCommandString 69 | Write-Host "##$vstsCommandString" 70 | displayName: Setup variables for signing 71 | 72 | - template: EsrpSign.yml@ComplianceRepo 73 | parameters: 74 | # the folder which contains the binaries to sign 75 | buildOutputPath: $(signSrcPath) 76 | # the location to put the signed output 77 | signOutputPath: $(signOutPath) 78 | # the certificate ID to use 79 | certificateId: "CP-230012" 80 | # the file pattern to use, comma separated 81 | pattern: '*.psd1,Microsoft.PowerShell.IoT.dll' 82 | 83 | - pwsh: | 84 | Compress-Archive -Path "$(signOutPath)\*" -DestinationPath "$(System.ArtifactsDirectory)\Microsoft.PowerShell.IoT.zip" 85 | displayName: Create Microsoft.PowerShell.IoT.zip 86 | 87 | - publish: $(System.ArtifactsDirectory)\Microsoft.PowerShell.IoT.zip 88 | artifact: SignedModule 89 | 90 | - template: assembly-module-compliance.yml@ComplianceRepo 91 | parameters: 92 | # component-governance 93 | sourceScanPath: '$(signOutPath)' 94 | # credscan 95 | suppressionsFile: '' 96 | # TermCheck 97 | optionsRulesDBPath: '' 98 | optionsFTPath: '' 99 | # tsa-upload 100 | codeBaseName: 'Microsoft_PowerShell_IoT_2_14_2022' 101 | # selections 102 | APIScan: false # set to false when not using Windows APIs. 103 | # binskim 104 | AnalyzeTarget: '$(signOutPath)\Microsoft.PowerShell.IoT.dll' 105 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | #!/usr/bin/env pwsh 5 | param( 6 | [Parameter()] 7 | [switch] 8 | $Bootstrap, 9 | 10 | [Parameter()] 11 | [switch] 12 | $Clean, 13 | 14 | [Parameter()] 15 | [switch] 16 | $Test 17 | ) 18 | 19 | $NeededTools = @{ 20 | PowerShellCore = "PowerShell Core 6.0.0 or greater" 21 | DotNetSdk = "dotnet sdk 2.0 or greater" 22 | InvokeBuild = "InvokeBuild latest" 23 | } 24 | 25 | function needsPowerShellCore () { 26 | try { 27 | $powershellverison = (pwsh -v) 28 | } catch { 29 | return $true 30 | } 31 | return $false 32 | } 33 | 34 | function needsDotNetSdk () { 35 | try { 36 | $dotnetverison = (dotnet --version) 37 | } catch { 38 | return $true 39 | } 40 | return $false 41 | } 42 | 43 | function needsInvokeBuild () { 44 | if (Get-Module -ListAvailable -Name InvokeBuild) { 45 | return $false 46 | } 47 | return $true 48 | } 49 | 50 | function getMissingTools () { 51 | $missingTools = @() 52 | 53 | if (needsPowerShellCore) { 54 | $missingTools += $NeededTools.PowerShellCore 55 | } 56 | if (needsDotNetSdk) { 57 | $missingTools += $NeededTools.DotNetSdk 58 | } 59 | if (needsInvokeBuild) { 60 | $missingTools += $NeededTools.InvokeBuild 61 | } 62 | 63 | return $missingTools 64 | } 65 | 66 | function hasMissingTools () { 67 | return ((getMissingTools).Count -gt 0) 68 | } 69 | 70 | if ($Bootstrap) { 71 | $string = "Here is what your environment is missing:`n" 72 | $missingTools = getMissingTools 73 | if (($missingTools).Count -eq 0) { 74 | $string += "* nothing!`n`n Run this script without a flag to build or a -Clean to clean." 75 | } else { 76 | $missingTools | ForEach-Object {$string += "* $_`n"} 77 | $string += "`nAll instructions for installing these tools can be found on PowerShell Editor Services' Github:`n" ` 78 | + "https://github.com/powershell/PowerShellEditorServices#development" 79 | } 80 | Write-Host "`n$string`n" 81 | } elseif(hasMissingTools) { 82 | Write-Host "You are missing needed tools. Run './build.ps1 -Bootstrap' to see what they are." 83 | } else { 84 | if($Clean) { 85 | Invoke-Build Clean 86 | } 87 | 88 | Invoke-Build Package 89 | 90 | if($Test) { 91 | Invoke-Build Test 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation on PowerShell IoT 2 | 3 | This folder contains documentation on how to get started with PowerShell IoT and general usage. For examples, go [here](/Examples)! 4 | 5 | ## Table of contents 6 | 7 | * [Raspberry Pi 3 Pin layout](/docs/rpi3_pin_layout.md) 8 | * [Remoting via SSH](/docs/remoting.md) 9 | * [API reference](/docs/api_reference.md) 10 | -------------------------------------------------------------------------------- /docs/help/Get-GpioPin.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.IoT.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.IoT 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-GpioPin 9 | 10 | ## SYNOPSIS 11 | Reads data from a GPIO pin. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-GpioPin [[-Id] ] [[-PullMode] ] [-Raw] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | Reads data from a GPIO pin. 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 - Read raw data from a GPIO pin 26 | 27 | ```powershell 28 | $raw = Get-GpioPin -Id 5 -Raw 29 | ``` 30 | 31 | ## PARAMETERS 32 | 33 | ### -Id 34 | 35 | The ID number of the pin to be read. 36 | 37 | ```yaml 38 | Type: Int32[] 39 | Parameter Sets: (All) 40 | Aliases: 41 | 42 | Required: False 43 | Position: 0 44 | Default value: None 45 | Accept pipeline input: True (ByPropertyName, ByValue) 46 | Accept wildcard characters: False 47 | ``` 48 | 49 | ### -PullMode 50 | 51 | Specifies the mode to use when reading the data. Possible values are: **Off**, **PullDown**, 52 | **PullUp**. Consult the documentation of your chipset to determine the requirements. 53 | 54 | ```yaml 55 | Type: PullMode 56 | Parameter Sets: (All) 57 | Aliases: 58 | Accepted values: Off, PullDown, PullUp 59 | 60 | Required: False 61 | Position: 1 62 | Default value: None 63 | Accept pipeline input: True (ByPropertyName) 64 | Accept wildcard characters: False 65 | ``` 66 | 67 | ### -Raw 68 | 69 | When this switch is used, the cmdlet only returns a pin state value of **High** or **Low**. 70 | 71 | ```yaml 72 | Type: SwitchParameter 73 | Parameter Sets: (All) 74 | Aliases: 75 | 76 | Required: False 77 | Position: Named 78 | Default value: None 79 | Accept pipeline input: True (ByPropertyName) 80 | Accept wildcard characters: False 81 | ``` 82 | 83 | ### CommonParameters 84 | 85 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 86 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 87 | -WarningAction, and -WarningVariable. For more information, see 88 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 89 | 90 | ## INPUTS 91 | 92 | ### System.Int32[] 93 | 94 | ### System.Nullable`1[[Microsoft.PowerShell.IoT.PullMode, Microsoft.PowerShell.IoT, Version=0.1.1.0, Culture=neutral, PublicKeyToken=null]] 95 | 96 | ### System.Management.Automation.SwitchParameter 97 | 98 | ## OUTPUTS 99 | 100 | ### System.Object 101 | 102 | ## NOTES 103 | 104 | ## RELATED LINKS 105 | 106 | [Set-GpioPin](Set-GpioPin.md) 107 | -------------------------------------------------------------------------------- /docs/help/Get-I2CDevice.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.IoT.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.IoT 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-I2CDevice 9 | 10 | ## SYNOPSIS 11 | Returns an **I2CDevice** object. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-I2CDevice [-Id] [[-FriendlyName] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | Returns an **I2CDevice** object.} 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 - Get an I2C device object 26 | 27 | ```powershell 28 | $Device = Get-I2CDevice -Id $Id -FriendlyName $FriendlyName 29 | ``` 30 | 31 | ## PARAMETERS 32 | 33 | ### -FriendlyName 34 | 35 | The friendly name of the device. 36 | 37 | ```yaml 38 | Type: String 39 | Parameter Sets: (All) 40 | Aliases: 41 | 42 | Required: False 43 | Position: 1 44 | Default value: None 45 | Accept pipeline input: True (ByPropertyName) 46 | Accept wildcard characters: False 47 | ``` 48 | 49 | ### -Id 50 | 51 | The I2c address of the device. 52 | 53 | ```yaml 54 | Type: Int32 55 | Parameter Sets: (All) 56 | Aliases: 57 | 58 | Required: True 59 | Position: 0 60 | Default value: None 61 | Accept pipeline input: True (ByPropertyName) 62 | Accept wildcard characters: False 63 | ``` 64 | 65 | ### CommonParameters 66 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 67 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 68 | -WarningAction, and -WarningVariable. For more information, see 69 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 70 | 71 | ## INPUTS 72 | 73 | ### System.Int32 74 | 75 | ### System.String 76 | 77 | ## OUTPUTS 78 | 79 | ### System.Object 80 | 81 | ## NOTES 82 | 83 | ## RELATED LINKS 84 | -------------------------------------------------------------------------------- /docs/help/Get-I2CRegister.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.IoT.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.IoT 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-I2CRegister 9 | 10 | ## SYNOPSIS 11 | Reads the value of a register of an **I2CDevice**. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-I2CRegister [-Device] [-Register] [[-ByteCount] ] [-Raw] 17 | [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | Reads the value of a register of an **I2CDevice**. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 - Read the chip identifier from the register 27 | 28 | ```powershell 29 | $ChipId = Get-I2CRegister -Device $Device -Register 0xD0 -Raw 30 | ``` 31 | 32 | ## PARAMETERS 33 | 34 | ### -ByteCount 35 | 36 | The number of bytes to be returned from the register. 37 | 38 | ```yaml 39 | Type: Byte 40 | Parameter Sets: (All) 41 | Aliases: 42 | 43 | Required: False 44 | Position: 2 45 | Default value: None 46 | Accept pipeline input: True (ByPropertyName) 47 | Accept wildcard characters: False 48 | ``` 49 | 50 | ### -Device 51 | 52 | An **I2CDevice** object to be read from. 53 | 54 | ```yaml 55 | Type: I2CDevice 56 | Parameter Sets: (All) 57 | Aliases: 58 | 59 | Required: True 60 | Position: 0 61 | Default value: None 62 | Accept pipeline input: True (ByPropertyName, ByValue) 63 | Accept wildcard characters: False 64 | ``` 65 | 66 | ### -Raw 67 | 68 | Returns only the value stored in the register rather than a **I2CDeviceRegisterData** object. 69 | 70 | ```yaml 71 | Type: SwitchParameter 72 | Parameter Sets: (All) 73 | Aliases: 74 | 75 | Required: False 76 | Position: Named 77 | Default value: None 78 | Accept pipeline input: True (ByPropertyName) 79 | Accept wildcard characters: False 80 | ``` 81 | 82 | ### -Register 83 | 84 | The address of the register to be read. 85 | 86 | ```yaml 87 | Type: UInt16 88 | Parameter Sets: (All) 89 | Aliases: 90 | 91 | Required: True 92 | Position: 1 93 | Default value: None 94 | Accept pipeline input: True (ByPropertyName) 95 | Accept wildcard characters: False 96 | ``` 97 | 98 | ### CommonParameters 99 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 100 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 101 | -WarningAction, and -WarningVariable. For more information, see 102 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 103 | 104 | ## INPUTS 105 | 106 | ### Microsoft.PowerShell.IoT.I2CDevice 107 | 108 | ### System.UInt16 109 | 110 | ### System.Byte 111 | 112 | ### System.Management.Automation.SwitchParameter 113 | 114 | ## OUTPUTS 115 | 116 | ### System.Object 117 | 118 | ## NOTES 119 | 120 | ## RELATED LINKS 121 | -------------------------------------------------------------------------------- /docs/help/Microsoft.PowerShell.IoT.md: -------------------------------------------------------------------------------- 1 | --- 2 | Module Name: Microsoft.PowerShell.IoT 3 | Module Guid: eb74e8da-9ae2-482a-a648-e96550fb8745 4 | Download Help Link: {{ Update Download Link }} 5 | Help Version: {{ Please enter version of help manually (X.X.X.X) format }} 6 | Locale: en-US 7 | --- 8 | 9 | # Microsoft.PowerShell.IoT Module 10 | 11 | ## Description 12 | 13 | A PowerShell module for interacting with hardware sensors and devices using common protocols: GPIO, 14 | I2C & SPI. 15 | 16 | > Note: PowerShell IoT is still in Preview 17 | 18 | ## Microsoft.PowerShell.IoT Cmdlets 19 | 20 | ### [Get-GpioPin](Get-GpioPin.md) 21 | Reads data from a GPIO pin. 22 | 23 | ### [Get-I2CDevice](Get-I2CDevice.md) 24 | Returns an **I2CDevice** object. 25 | 26 | ### [Get-I2CRegister](Get-I2CRegister.md) 27 | Reads the value of a register of an **I2CDevice**. 28 | 29 | ### [Send-SPIData](Send-SPIData.md) 30 | Sends data on an Serial Peripheral Interface (SPI) channel. 31 | 32 | ### [Set-GpioPin](Set-GpioPin.md) 33 | Writes a value to a GPIO pin. 34 | 35 | ### [Set-I2CRegister](Set-I2CRegister.md) 36 | Writes a value to a register of an **I2CDevice**. 37 | -------------------------------------------------------------------------------- /docs/help/Send-SPIData.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.IoT.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.IoT 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Send-SPIData 9 | 10 | ## SYNOPSIS 11 | Sends data on an Serial Peripheral Interface (SPI) channel. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Send-SPIData [-Data] [[-Channel] ] [[-Frequency] ] [-Raw] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | Sends data on an Serial Peripheral Interface (SPI) channel. 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 26 | 27 | ```powershell 28 | PS C:\> {{ Add example code here }} 29 | ``` 30 | 31 | {{ Add example description here }} 32 | 33 | ## PARAMETERS 34 | 35 | ### -Channel 36 | 37 | The channel number to be written to. 38 | 39 | ```yaml 40 | Type: UInt32 41 | Parameter Sets: (All) 42 | Aliases: 43 | 44 | Required: False 45 | Position: 1 46 | Default value: None 47 | Accept pipeline input: True (ByPropertyName) 48 | Accept wildcard characters: False 49 | ``` 50 | 51 | ### -Data 52 | 53 | The data to be written to the channel. 54 | 55 | ```yaml 56 | Type: Byte[] 57 | Parameter Sets: (All) 58 | Aliases: 59 | 60 | Required: True 61 | Position: 0 62 | Default value: None 63 | Accept pipeline input: True (ByPropertyName, ByValue) 64 | Accept wildcard characters: False 65 | ``` 66 | 67 | ### -Frequency 68 | 69 | The frequency in Hertz that is used to send data on the channel. 70 | 71 | ```yaml 72 | Type: UInt32 73 | Parameter Sets: (All) 74 | Aliases: 75 | 76 | Required: False 77 | Position: 2 78 | Default value: None 79 | Accept pipeline input: True (ByPropertyName) 80 | Accept wildcard characters: False 81 | ``` 82 | 83 | ### -Raw 84 | 85 | Return the result code of the write operation rather than a full **SPIData** object. 86 | 87 | ```yaml 88 | Type: SwitchParameter 89 | Parameter Sets: (All) 90 | Aliases: 91 | 92 | Required: False 93 | Position: Named 94 | Default value: None 95 | Accept pipeline input: True (ByPropertyName) 96 | Accept wildcard characters: False 97 | ``` 98 | 99 | ### CommonParameters 100 | 101 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 102 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 103 | -WarningAction, and -WarningVariable. For more information, see 104 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 105 | 106 | ## INPUTS 107 | 108 | ### System.Byte[] 109 | 110 | ### System.UInt32 111 | 112 | ### System.Management.Automation.SwitchParameter 113 | 114 | ## OUTPUTS 115 | 116 | ### System.Object 117 | 118 | ## NOTES 119 | 120 | ## RELATED LINKS 121 | -------------------------------------------------------------------------------- /docs/help/Set-GpioPin.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.IoT.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.IoT 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-GpioPin 9 | 10 | ## SYNOPSIS 11 | Writes a value to a GPIO pin. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Set-GpioPin [-Id] [-Value] [-PassThru] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | Writes a value to a GPIO pin. 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 - Write a value to a GPIO pin 26 | 27 | This example writes the value of a variable to GPIO pin 2. 28 | 29 | ```powershell 30 | Set-GpioPin -Id 2 -Value $value 31 | ``` 32 | 33 | ### Example 2 - Set a GPIO pin state to **Low** 34 | 35 | This example sets the state of GPIO pin 0 to **Low**. 36 | 37 | ```powershell 38 | Set-GpioPin -Id 0 -Value "Low" 39 | ``` 40 | 41 | ## PARAMETERS 42 | 43 | ### -Id 44 | 45 | The ID number of the pin to be written. 46 | 47 | ```yaml 48 | Type: Int32[] 49 | Parameter Sets: (All) 50 | Aliases: 51 | 52 | Required: True 53 | Position: 0 54 | Default value: None 55 | Accept pipeline input: True (ByPropertyName, ByValue) 56 | Accept wildcard characters: False 57 | ``` 58 | 59 | ### -PassThru 60 | 61 | Normally this cmdlet does not return any output. When **PassThru** is used, the cmdlet returns a 62 | **PinData** object showing the value that was set. 63 | 64 | ```yaml 65 | Type: SwitchParameter 66 | Parameter Sets: (All) 67 | Aliases: 68 | 69 | Required: False 70 | Position: Named 71 | Default value: None 72 | Accept pipeline input: True (ByPropertyName) 73 | Accept wildcard characters: False 74 | ``` 75 | 76 | ### -Value 77 | 78 | The value to set on the GPIO pin. 79 | 80 | ```yaml 81 | Type: SignalLevel 82 | Parameter Sets: (All) 83 | Aliases: 84 | Accepted values: Low, High 85 | 86 | Required: True 87 | Position: 1 88 | Default value: None 89 | Accept pipeline input: True (ByPropertyName) 90 | Accept wildcard characters: False 91 | ``` 92 | 93 | ### CommonParameters 94 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 95 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 96 | -WarningAction, and -WarningVariable. For more information, see 97 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 98 | 99 | ## INPUTS 100 | 101 | ### System.Int32[] 102 | 103 | ### Microsoft.PowerShell.IoT.SignalLevel 104 | 105 | ### System.Management.Automation.SwitchParameter 106 | 107 | ## OUTPUTS 108 | 109 | ### System.Object 110 | 111 | ## NOTES 112 | 113 | ## RELATED LINKS 114 | -------------------------------------------------------------------------------- /docs/help/Set-I2CRegister.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.IoT.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.IoT 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-I2CRegister 9 | 10 | ## SYNOPSIS 11 | Writes a value to a register of an **I2CDevice**. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Set-I2CRegister [-Device] [-Register] [-Data] [-PassThru] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | Writes a value to a register of an **I2CDevice**. 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 26 | 27 | ```powershell 28 | Set-I2CRegister -Device $Device -Register 0x00 -Data 0xF1 29 | ``` 30 | 31 | ## PARAMETERS 32 | 33 | ### -Data 34 | 35 | The data to be written to the register. 36 | 37 | ```yaml 38 | Type: Byte[] 39 | Parameter Sets: (All) 40 | Aliases: 41 | 42 | Required: True 43 | Position: 2 44 | Default value: None 45 | Accept pipeline input: True (ByPropertyName) 46 | Accept wildcard characters: False 47 | ``` 48 | 49 | ### -Device 50 | 51 | An **I2CDevice** object to be written to. 52 | 53 | ```yaml 54 | Type: I2CDevice 55 | Parameter Sets: (All) 56 | Aliases: 57 | 58 | Required: True 59 | Position: 0 60 | Default value: None 61 | Accept pipeline input: True (ByPropertyName, ByValue) 62 | Accept wildcard characters: False 63 | ``` 64 | 65 | ### -PassThru 66 | 67 | Normally this cmdlet does not return any output. When **PassThru** is used, the cmdlet returns a 68 | **I2CDeviceRegisterData** object showing the value that was set. 69 | 70 | ```yaml 71 | Type: SwitchParameter 72 | Parameter Sets: (All) 73 | Aliases: 74 | 75 | Required: False 76 | Position: Named 77 | Default value: None 78 | Accept pipeline input: True (ByPropertyName) 79 | Accept wildcard characters: False 80 | ``` 81 | 82 | ### -Register 83 | 84 | The address of the register to be written. 85 | 86 | ```yaml 87 | Type: UInt16 88 | Parameter Sets: (All) 89 | Aliases: 90 | 91 | Required: True 92 | Position: 1 93 | Default value: None 94 | Accept pipeline input: True (ByPropertyName) 95 | Accept wildcard characters: False 96 | ``` 97 | 98 | ### CommonParameters 99 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 100 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 101 | -WarningAction, and -WarningVariable. For more information, see 102 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 103 | 104 | ## INPUTS 105 | 106 | ### Microsoft.PowerShell.IoT.I2CDevice 107 | 108 | ### System.UInt16 109 | 110 | ### System.Byte[] 111 | 112 | ### System.Management.Automation.SwitchParameter 113 | 114 | ## OUTPUTS 115 | 116 | ### System.Object 117 | 118 | ## NOTES 119 | 120 | ## RELATED LINKS 121 | -------------------------------------------------------------------------------- /docs/remoting.md: -------------------------------------------------------------------------------- 1 | # SSH Remoting Docs 2 | 3 | > NOTE: These docs were created while using a Raspberry Pi 3 with Raspbian Stretch. 4 | 5 | ## Prereqs 6 | 7 | First you need to get the IP address of the device. You can do this by running this on the device: 8 | 9 | ```bash 10 | PS > hostname -I 11 | 12 | 123.123.123.123 13 | ``` 14 | 15 | You also need to have [PowerShell Core](https://github.com/powershell/powershell) installed on your device. 16 | 17 | You'll also need some SSH client. macOS and linux have it installed by default, for Windows, check out the [Win32 port of OpenSSH](https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH). 18 | 19 | ## Using pure SSH 20 | 21 | First ssh into your pi: 22 | 23 | ```plaintext 24 | > ssh pi@123.123.123.123 25 | 26 | # At this point you are remoted into to the device and can start PowerShell 27 | 28 | > sudo pwsh 29 | PS > Get-GpioPin 1 30 | ``` 31 | 32 | ## Using PowerShell Remoting (PSRP) over SSH 33 | 34 | > NOTE: This will only work if your device doesn't require a password when you run `sudo pwsh`. The Raspberry Pi 3 with default configuration does not prompt. 35 | 36 | First you need to install [PowerShell Core](https://github.com/powershell/powershell) on your client machine. Windows PowerShell does not support PowerShell Remoting (PSRP) over SSH so that will not work. 37 | 38 | Second you need to set up PSRP over SSH. You can [follow this guide](https://github.com/PowerShell/PowerShell/blob/11631e7412197f3f803ebbef95a3ddb174a387ec/demos/SSHRemoting/README.md). 39 | 40 | When you get to the part where it says: 41 | 42 | > Add a PowerShell subsystem entry 43 | 44 | Put this: 45 | 46 | ```plaintext 47 | Subsystem powershell sudo pwsh -sshs -NoLogo -NoProfile 48 | ``` 49 | 50 | Note the use of `sudo`. 51 | 52 | If done correctly, you should be able to run: 53 | 54 | ```powershell 55 | PS > Enter-PSSession -Hostname 123.123.123.123 -UserName pi 56 | 57 | # At this point you are remoted into to the device already in PowerShell 58 | 59 | [123.123.123.123] PS > Get-GpioPin 1 60 | ``` 61 | 62 | By doing this, you should be able to automate working with your device using PowerShell 🎉 63 | -------------------------------------------------------------------------------- /docs/rpi3_pin_layout.md: -------------------------------------------------------------------------------- 1 | # Pin layout 2 | 3 | PowerShell IoT uses logical representation of the microcontroller's GPIOs. 4 | Refer to the microcontroller's datasheet to find this information. 5 | In case of Raspberry Pi use the BCM number listed in the following diagram: 6 | 7 | ## Image reference 8 | 9 | ![Raspberry Pi 3 pin layout](https://ocw.cs.pub.ro/courses/_media/iot/labs/pins-raspberrypi.png?w=700&tok=35c802) 10 | 11 | ## Text reference 12 | 13 | ```plaintext 14 | +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ 15 | | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | 16 | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ 17 | | | | 3.3v | | | 1 || 2 | | | 5v | | | 18 | | 2 | 8 | SDA.1 | ALT0 | 1 | 3 || 4 | | | 5v | | | 19 | | 3 | 9 | SCL.1 | ALT0 | 1 | 5 || 6 | | | 0v | | | 20 | | 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 0 | IN | TxD | 15 | 14 | 21 | | | | 0v | | | 9 || 10 | 1 | IN | RxD | 16 | 15 | 22 | | 17 | 0 | GPIO. 0 | IN | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 | 23 | | 27 | 2 | GPIO. 2 | OUT | 0 | 13 || 14 | | | 0v | | | 24 | | 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 | 25 | | | | 3.3v | | | 17 || 18 | 1 | IN | GPIO. 5 | 5 | 24 | 26 | | 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | | 27 | | 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 | 28 | | 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 | 29 | | | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 | 30 | | 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 | 31 | | 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | | 32 | | 6 | 22 | GPIO.22 | IN | 1 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 | 33 | | 13 | 23 | GPIO.23 | IN | 0 | 33 || 34 | | | 0v | | | 34 | | 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 | 35 | | 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 | 36 | | | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 | 37 | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ 38 | | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | 39 | +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ 40 | ``` -------------------------------------------------------------------------------- /psiot.build.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | function NeedsRestore($rootPath) { 5 | # This checks to see if the number of folders under a given 6 | # path (like "src" or "test") is greater than the number of 7 | # obj\project.assets.json files found under that path, implying 8 | # that those folders have not yet been restored. 9 | $projectAssets = (Get-ChildItem "$rootPath\*\obj\project.assets.json") 10 | return ($projectAssets -eq $null) -or ((Get-ChildItem $rootPath).Length -gt $projectAssets.Length) 11 | } 12 | 13 | task Restore -If { "Restore" -in $BuildTask -or (NeedsRestore(".\src")) } { 14 | Push-Location $PSScriptRoot\src 15 | exec { dotnet restore } 16 | Pop-Location 17 | } 18 | 19 | task Clean Restore, { 20 | Push-Location $PSScriptRoot\src 21 | exec { dotnet clean } 22 | Pop-Location 23 | } 24 | 25 | task Build Restore, { 26 | Push-Location $PSScriptRoot\src 27 | exec { dotnet build } 28 | Pop-Location 29 | } 30 | 31 | task Test { 32 | Install-Module Pester -Force -Scope CurrentUser 33 | Push-Location $PSScriptRoot\test 34 | $res = Invoke-Pester -OutputFormat NUnitXml -OutputFile TestsResults.xml -PassThru 35 | if ($env:APPVEYOR) { 36 | (New-Object System.Net.WebClient).UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestsResults.xml)); 37 | } 38 | if ($res.FailedCount -gt 0) { throw "$($res.FailedCount) tests failed."} 39 | Pop-Location 40 | } 41 | 42 | task Package Clean, Build, { 43 | if ((Test-Path "$PSScriptRoot\out")) { 44 | Remove-Item -Path $PSScriptRoot\out -Recurse -Force 45 | } 46 | 47 | Push-Location "$PSScriptRoot\src\Microsoft.PowerShell.IoT" 48 | dotnet publish 49 | Pop-Location 50 | 51 | New-Item -ItemType directory -Path $PSScriptRoot\out 52 | New-Item -ItemType directory -Path $PSScriptRoot\out\Microsoft.PowerShell.IoT 53 | 54 | Copy-Item -Path "$PSScriptRoot\src\Microsoft.PowerShell.IoT\Microsoft.PowerShell.IoT.psd1" -Destination "$PSScriptRoot\out\Microsoft.PowerShell.IoT\" -Force 55 | Copy-Item -Path "$PSScriptRoot\src\Microsoft.PowerShell.IoT\bin\Debug\netcoreapp2.0\publish\*" -Destination "$PSScriptRoot\out\Microsoft.PowerShell.IoT\" -Force -Recurse 56 | 57 | if ($env:TF_BUILD) { 58 | Import-Module "$PSScriptRoot\tools\vstsBuild.psm1" 59 | Publish-VstsBuildArtifact -ArtifactPath "$PSScriptRoot\out\Microsoft.PowerShell.IoT" -Bucket "Microsoft.PowerShell.IoT" 60 | } 61 | } 62 | 63 | # The default task is to run the entire CI build 64 | task . Package 65 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2003 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.PowerShell.IoT", "Microsoft.PowerShell.IoT\Microsoft.PowerShell.IoT.csproj", "{1AEC06E7-3F3E-4D45-A7CA-150F46C1C036}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {1AEC06E7-3F3E-4D45-A7CA-150F46C1C036}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {1AEC06E7-3F3E-4D45-A7CA-150F46C1C036}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {1AEC06E7-3F3E-4D45-A7CA-150F46C1C036}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {1AEC06E7-3F3E-4D45-A7CA-150F46C1C036}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {2F50629C-F677-4660-A42C-ABAF9EE30D65} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/Gpio/Get/GetGpioPin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Management.Automation; 4 | using System.Device.Gpio; 5 | 6 | [Cmdlet(VerbsCommon.Get, "GpioPin")] 7 | public class GetGpioPin : GpioCmdletBase 8 | { 9 | [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 0)] 10 | public int[] Id { get; set; } 11 | 12 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, Position = 1)] 13 | public PullMode? PullMode { get; set; } 14 | 15 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)] 16 | public SwitchParameter Raw { get; set; } 17 | 18 | protected override void ProcessRecord() 19 | { 20 | ArrayList pinList = new ArrayList(); 21 | 22 | if ((this.Id == null) || (this.Id.Length <= 0)) 23 | { 24 | // TODO: this is "gpio readall" functionality 25 | // do not forget to change Id param to Mandatory = false when this is implemented 26 | } 27 | else 28 | { 29 | pinList.AddRange(this.Id); 30 | } 31 | 32 | PinMode mode = PinMode.Input; 33 | if (this.PullMode.HasValue) 34 | { 35 | switch (this.PullMode.Value) 36 | { 37 | case global::PullMode.PullDown: mode = PinMode.InputPullDown; break; 38 | case global::PullMode.PullUp: mode = PinMode.InputPullUp; break; 39 | default: mode = PinMode.Input; break; 40 | }; 41 | }; 42 | 43 | foreach (int pinId in pinList) 44 | { 45 | SignalLevel slResult = SignalLevel.Low; 46 | 47 | this.EnsureOpenPin(pinId, mode); 48 | 49 | if (this.GpioController.Read(pinId) == PinValue.High) 50 | { 51 | slResult = SignalLevel.High; 52 | }; 53 | 54 | if (this.Raw) 55 | { 56 | WriteObject(slResult); 57 | } 58 | else 59 | { 60 | GpioPinData pinData = new GpioPinData(pinId, slResult); 61 | WriteObject(pinData); 62 | } 63 | } 64 | } 65 | } 66 | 67 | public enum PullMode 68 | { 69 | Off = 0, 70 | PullDown = 1, 71 | PullUp = 2 72 | } 73 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/Gpio/GpioCmdletBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Device.Gpio; 4 | 5 | public class GpioCmdletBase : Cmdlet 6 | { 7 | protected static GpioController StaticGpioController; 8 | protected GpioController GpioController 9 | { 10 | get 11 | { 12 | if (StaticGpioController == null) 13 | { 14 | StaticGpioController = new GpioController(); 15 | } 16 | return StaticGpioController; 17 | } 18 | set 19 | { 20 | StaticGpioController = value; 21 | } 22 | } 23 | 24 | protected void EnsureOpenPin(int pinId, PinMode mode) 25 | { 26 | if (this.GpioController.IsPinOpen(pinId)) 27 | { 28 | if (this.GpioController.GetPinMode(pinId) != mode) 29 | { 30 | this.GpioController.SetPinMode(pinId, mode); 31 | } 32 | } 33 | else 34 | { 35 | this.GpioController.OpenPin(pinId, mode); 36 | } 37 | } 38 | } 39 | 40 | [Cmdlet(VerbsCommon.Clear, "GpioResources")] 41 | public class ClearGpioResources : GpioCmdletBase 42 | { 43 | protected override void ProcessRecord() 44 | { 45 | if (GpioCmdletBase.StaticGpioController != null) 46 | { 47 | GpioCmdletBase.StaticGpioController.Dispose(); 48 | GpioCmdletBase.StaticGpioController = null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/Gpio/GpioPinData.cs: -------------------------------------------------------------------------------- 1 | public class GpioPinData 2 | { 3 | public int Id; 4 | public SignalLevel Value; 5 | 6 | public GpioPinData(int id, SignalLevel value) 7 | { 8 | this.Id = id; 9 | this.Value = value; 10 | } 11 | } 12 | 13 | public enum SignalLevel 14 | { 15 | Low = 0, 16 | High = 1 17 | } 18 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/Gpio/Set/SetGpioPin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Device.Gpio; 4 | 5 | [Cmdlet(VerbsCommon.Set, "GpioPin")] 6 | public class SetGpioPin : GpioCmdletBase 7 | { 8 | [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 0)] 9 | public int[] Id { get; set; } 10 | 11 | [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 1)] 12 | public SignalLevel Value { get; set; } 13 | 14 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)] 15 | public SwitchParameter PassThru { get; set; } 16 | 17 | protected override void ProcessRecord() 18 | { 19 | if (this.Id != null) 20 | { 21 | foreach (int pinId in this.Id) 22 | { 23 | this.EnsureOpenPin(pinId, PinMode.Output); 24 | 25 | if(this.Value == SignalLevel.Low) 26 | { 27 | this.GpioController.Write(pinId, PinValue.Low); 28 | } 29 | else 30 | { 31 | this.GpioController.Write(pinId, PinValue.High); 32 | } 33 | 34 | if (this.PassThru) 35 | { 36 | GpioPinData pinData = new GpioPinData(pinId, this.Value); 37 | WriteObject(pinData); 38 | } 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/I2c/Get/GetI2cDevice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Device.I2c; 4 | 5 | [Cmdlet(VerbsCommon.Get, "I2CDevice")] 6 | public class GetI2CDevice : Cmdlet 7 | { 8 | [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] 9 | public int Id { get; set; } 10 | 11 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, Position = 1)] 12 | public string FriendlyName { get; set; } 13 | 14 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, Position = 2)] 15 | public int BusId { get; set; } 16 | 17 | public GetI2CDevice() 18 | { 19 | this.FriendlyName = string.Empty; 20 | this.BusId = 1; 21 | } 22 | 23 | protected override void ProcessRecord() 24 | { 25 | var settings = new I2cConnectionSettings(this.BusId, this.Id); 26 | I2cDevice device = I2cDevice.Create(settings); 27 | WriteObject(new I2CDevice(device, this.Id, this.FriendlyName, this.BusId)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/I2c/Get/GetI2cRegister.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; // PowerShell namespace. 3 | [Cmdlet(VerbsCommon.Get, "I2CRegister")] 4 | public class GetI2CRegister : Cmdlet 5 | { 6 | [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 0)] 7 | public I2CDevice Device { get; set; } 8 | 9 | [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 1)] 10 | public ushort Register { get; set; } 11 | 12 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, Position = 2)] 13 | public byte ByteCount { get; set; } 14 | 15 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)] 16 | public SwitchParameter Raw { get; set; } 17 | 18 | public GetI2CRegister() 19 | { 20 | this.ByteCount = 1; 21 | this.Raw = false; 22 | } 23 | 24 | protected override void ProcessRecord() 25 | { 26 | if (this.ByteCount > 1) 27 | { 28 | Span readBuffer = stackalloc byte[ByteCount]; 29 | 30 | this.Device.device.WriteByte((byte)this.Register); 31 | this.Device.device.Read(readBuffer); 32 | 33 | if (this.Raw) 34 | { 35 | WriteObject(readBuffer.ToArray()); 36 | } 37 | else 38 | { 39 | I2CDeviceRegisterData result = new I2CDeviceRegisterData(this.Device, this.Register) 40 | { 41 | Data = readBuffer.ToArray() // optimize to be Span? How does PowerShell deal with it? 42 | }; 43 | WriteObject(result); 44 | } 45 | } 46 | else 47 | { 48 | this.Device.device.WriteByte((byte)this.Register); 49 | byte value = this.Device.device.ReadByte(); 50 | //byte value = this.Device.device.ReadAddressByte(this.Register); 51 | if (this.Raw) 52 | { 53 | WriteObject(value); 54 | } 55 | else 56 | { 57 | I2CDeviceRegisterData result = new I2CDeviceRegisterData(this.Device, this.Register, new byte[1] { value }); 58 | WriteObject(result); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/I2c/I2cDevice.cs: -------------------------------------------------------------------------------- 1 | public class I2CDevice 2 | { 3 | internal System.Device.I2c.I2cDevice device = null; 4 | 5 | public string FriendlyName { get; set; } 6 | public int Id { get; set; } 7 | public int BusId { get; set; } 8 | 9 | public I2CDevice(System.Device.I2c.I2cDevice device, int Id, string FriendlyName, int BusId) 10 | { 11 | this.device = device; 12 | this.Id = Id; 13 | this.FriendlyName = FriendlyName; 14 | this.BusId = BusId; 15 | } 16 | 17 | public override string ToString() 18 | { 19 | if (string.IsNullOrEmpty(this.FriendlyName)) 20 | { 21 | return this.Id.ToString(); 22 | } 23 | else 24 | { 25 | return this.FriendlyName; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/I2c/I2cRegisterData.cs: -------------------------------------------------------------------------------- 1 | public class I2CDeviceRegisterData 2 | { 3 | public I2CDevice Device { get; set; } 4 | public ushort Register { get; set; } 5 | public byte[] Data { get; set; } 6 | 7 | public I2CDeviceRegisterData(I2CDevice device, ushort register, byte[] data) 8 | { 9 | this.Device = device; 10 | this.Register = register; 11 | this.Data = data; 12 | } 13 | 14 | public I2CDeviceRegisterData(I2CDevice device, ushort register) 15 | : this(device, register, new byte[0]) 16 | { 17 | } 18 | 19 | public I2CDeviceRegisterData() 20 | : this(null, 0) 21 | { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/I2c/Set/SetI2cRegister.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; // PowerShell namespace. 3 | 4 | [Cmdlet(VerbsCommon.Set, "I2CRegister")] 5 | public class SetI2CRegister : Cmdlet 6 | { 7 | [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 0)] 8 | public I2CDevice Device { get; set; } 9 | 10 | [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 1)] 11 | public ushort Register { get; set; } 12 | 13 | [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 2)] 14 | public byte[] Data { get; set; } 15 | 16 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)] 17 | public SwitchParameter PassThru { get; set; } 18 | 19 | protected override void ProcessRecord() 20 | { 21 | Span dataOut = stackalloc byte[] { (byte)Register, Data[0] }; 22 | this.Device.device.Write(dataOut); 23 | if (this.PassThru) 24 | { 25 | I2CDeviceRegisterData result = new I2CDeviceRegisterData(this.Device, this.Register, this.Data); 26 | WriteObject(result); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/Microsoft.PowerShell.IoT.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 0.2.0.0 6 | Microsoft Corporation 7 | Microsoft Corporation 8 | Microsoft.PowerShell.IoT 9 | Microsoft.PowerShell.IoT 10 | 0.2.0.0 11 | 0.2.0.0 12 | © Microsoft Corporation. All rights reserved. 13 | https://github.com/PowerShell/PowerShell-IoT/blob/master/LICENSE.txt 14 | https://github.com/PowerShell/PowerShell-IoT 15 | https://github.com/PowerShell/PowerShell-IoT 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/Microsoft.PowerShell.IoT.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | GUID="eb74e8da-9ae2-482a-a648-e96550fb8745" 6 | Author="Microsoft Corporation" 7 | CompanyName="Microsoft Corporation" 8 | Copyright="© Microsoft Corporation. All rights reserved." 9 | Description='A PowerShell module for interacting with hardware sensors and devices using common protocols: GPIO, I2C & SPI.' 10 | ModuleVersion="0.2.0" 11 | FunctionsToExport = '*' 12 | CmdletsToExport = '*' 13 | AliasesToExport = @() 14 | NestedModules=@('Microsoft.PowerShell.IoT.dll') 15 | HelpInfoURI = 'https://github.com/PowerShell/PowerShell-IoT' 16 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 17 | PrivateData = @{ 18 | PSData = @{ 19 | # Tags applied to this module. These help with module discovery in online galleries. 20 | Tags = 'IoT','RaspberryPi','Raspbian','GPIO','I2C','SPI' 21 | 22 | # A URL to the license for this module. 23 | LicenseUri = 'https://github.com/PowerShell/PowerShell-IoT/blob/master/LICENSE.txt' 24 | 25 | # A URL to the main website for this project. 26 | ProjectUri = 'https://github.com/PowerShell/PowerShell-IoT' 27 | 28 | # A URL to an icon representing this module. 29 | # IconUri = '' 30 | 31 | # ReleaseNotes of this module 32 | ReleaseNotes = '## 0.2.0 33 | 34 | Started using System.Device.Gpio 35 | 36 | ## 0.1.1 37 | 38 | Minor bug fixes 39 | 40 | ## 0.1.0 41 | 42 | Initial preview of PowerShell IoT 43 | ' 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/SPI/SPIData.cs: -------------------------------------------------------------------------------- 1 | public class SPIData 2 | { 3 | public int BusId { get; set; } 4 | public int ChipSelectLine { get; set; } 5 | public int Frequency { get; set; } 6 | public byte[] Data { get; set; } 7 | public byte[] Response { get; set; } 8 | 9 | public SPIData(int busId, int chipSelectLine, int frequency, byte[] data, byte[] response) 10 | { 11 | this.BusId = busId; 12 | this.ChipSelectLine = chipSelectLine; 13 | this.Frequency = frequency; 14 | this.Data = data; 15 | this.Response = response; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/SPI/Send/SendSPIData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Device.Spi; 4 | 5 | [Cmdlet(VerbsCommunications.Send, "SPIData")] 6 | public class SendSPIData : Cmdlet 7 | { 8 | [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 0)] 9 | public byte[] Data { get; set; } 10 | 11 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, Position = 1)] 12 | public int BusId { get; set; } 13 | 14 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, Position = 2)] 15 | public int ChipSelectLine { get; set; } 16 | 17 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, Position = 3)] 18 | public int Frequency { get; set; } 19 | 20 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)] 21 | public SwitchParameter Raw { get; set; } 22 | 23 | public SendSPIData() 24 | { 25 | this.BusId = 0; 26 | this.ChipSelectLine = 0; 27 | this.Frequency = 500_000; // 500 KHz default speed 28 | } 29 | 30 | protected override void ProcessRecord() 31 | { 32 | var settings = new SpiConnectionSettings(this.BusId, this.ChipSelectLine); 33 | settings.ClockFrequency = this.Frequency; 34 | 35 | using(var spiDevice = SpiDevice.Create(settings)) 36 | { 37 | var response = new byte[this.Data.Length]; 38 | 39 | spiDevice.TransferFullDuplex(this.Data, response); 40 | 41 | if (this.Raw) 42 | { 43 | WriteObject(response); 44 | } 45 | else 46 | { 47 | SPIData spiData = new SPIData(this.BusId, this.ChipSelectLine, this.Frequency, this.Data, response); 48 | WriteObject(spiData); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/gen/Resources.cs: -------------------------------------------------------------------------------- 1 | 2 | //------------------------------------------------------------------------------ 3 | // 4 | // This code was generated by a dotnet run from src\ResGen folder. 5 | // To add or remove a member, edit your .resx file then rerun src\ResGen. 6 | // 7 | // Changes to this file may cause incorrect behavior and will be lost if 8 | // the code is regenerated. 9 | // 10 | //------------------------------------------------------------------------------ 11 | 12 | 13 | using System; 14 | using System.Reflection; 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 20 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 21 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 22 | 23 | internal class Resources { 24 | 25 | private static global::System.Resources.ResourceManager resourceMan; 26 | 27 | private static global::System.Globalization.CultureInfo resourceCulture; 28 | 29 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 30 | internal Resources() { 31 | } 32 | 33 | /// 34 | /// Returns the cached ResourceManager instance used by this class. 35 | /// 36 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 37 | internal static global::System.Resources.ResourceManager ResourceManager { 38 | get { 39 | if (object.ReferenceEquals(resourceMan, null)) { 40 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PowerShell.IoT.resources.Resources", typeof(Resources).Assembly); 41 | resourceMan = temp; 42 | } 43 | return resourceMan; 44 | } 45 | } 46 | 47 | /// 48 | /// Overrides the current threads CurrentUICulture property for all 49 | /// resource lookups using this strongly typed resource class. 50 | /// 51 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 52 | internal static global::System.Globalization.CultureInfo Culture { 53 | get { 54 | return resourceCulture; 55 | } 56 | set { 57 | resourceCulture = value; 58 | } 59 | } 60 | 61 | 62 | /// 63 | /// Looks up a localized string similar to 64 | /// This cmdlet requires PowerShell to be started with root privileges. 65 | /// 66 | /// 67 | internal static string ErrNeedRootPrivileges { 68 | get { 69 | return ResourceManager.GetString("ErrNeedRootPrivileges", resourceCulture); 70 | } 71 | } 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.IoT/resources/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | This cmdlet requires PowerShell to be started with root privileges. 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/ResGen/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Text; 8 | using System.Xml.Linq; 9 | 10 | namespace ConsoleApplication 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | IEnumerable dirs; 17 | string fileFilter; 18 | 19 | if (args.Length == 1) 20 | { 21 | // We are assuming resgen is run with 'dotnet run pathToResxFile.resx'. 22 | fileFilter = Path.GetFileName(args[0]); 23 | string moduleDirectory = Path.GetDirectoryName(Path.GetDirectoryName(args[0])); 24 | dirs = new List() { moduleDirectory }; 25 | } 26 | else 27 | { 28 | // We are assuming resgen is run with 'dotnet run' 29 | // so we can use relative path to get a parent directory 30 | // to process all *.resx files in all project subdirectories. 31 | fileFilter = "*.resx"; 32 | dirs = Directory.EnumerateDirectories(".."); 33 | } 34 | 35 | foreach (string folder in dirs) 36 | { 37 | string moduleName = Path.GetFileName(folder); 38 | string resourcePath = Path.Combine(folder, "resources"); 39 | 40 | if (Directory.Exists(resourcePath)) 41 | { 42 | string genFolder = Path.Combine(folder, "gen"); 43 | if (!Directory.Exists(genFolder)) 44 | { 45 | Directory.CreateDirectory(genFolder); 46 | } 47 | 48 | foreach (string resxPath in Directory.EnumerateFiles(resourcePath, fileFilter)) 49 | { 50 | string className = Path.GetFileNameWithoutExtension(resxPath); 51 | string sourceCode = GetStronglyTypeCsFileForResx(resxPath, moduleName, className); 52 | string outPath = Path.Combine(genFolder, className + ".cs"); 53 | Console.WriteLine("ResGen for " + outPath); 54 | File.WriteAllText(outPath, sourceCode); 55 | } 56 | } 57 | } 58 | } 59 | 60 | private static string GetStronglyTypeCsFileForResx(string xmlPath, string moduleName, string className) 61 | { 62 | // Example 63 | // 64 | // className = Full.Name.Of.The.ClassFoo 65 | // shortClassName = ClassFoo 66 | // namespaceName = Full.Name.Of.The 67 | 68 | string shortClassName = className; 69 | string namespaceName = null; 70 | int lastIndexOfDot = className.LastIndexOf('.'); 71 | if (lastIndexOfDot != -1) 72 | { 73 | namespaceName = className.Substring(0, lastIndexOfDot); 74 | shortClassName = className.Substring(lastIndexOfDot + 1); 75 | } 76 | 77 | var entries = new StringBuilder(); 78 | XElement root = XElement.Parse(File.ReadAllText(xmlPath)); 79 | foreach (var data in root.Elements("data")) 80 | { 81 | string value = data.Value.Replace("\n", "\n ///"); 82 | string name = data.Attribute("name").Value.Replace(' ', '_'); 83 | entries.AppendFormat(ENTRY, name, value); 84 | } 85 | 86 | string bodyCode = string.Format(BODY, shortClassName, moduleName, entries.ToString(), className); 87 | if (namespaceName != null) 88 | { 89 | bodyCode = string.Format(NAMESPACE, namespaceName, bodyCode); 90 | } 91 | 92 | string resultCode = string.Format(BANNER, bodyCode).Replace("\r\n?|\n", "\r\n"); 93 | return resultCode; 94 | } 95 | 96 | private static readonly string BANNER = @" 97 | //------------------------------------------------------------------------------ 98 | // 99 | // This code was generated by a dotnet run from src\ResGen folder. 100 | // To add or remove a member, edit your .resx file then rerun src\ResGen. 101 | // 102 | // Changes to this file may cause incorrect behavior and will be lost if 103 | // the code is regenerated. 104 | // 105 | //------------------------------------------------------------------------------ 106 | 107 | {0} 108 | "; 109 | 110 | private static readonly string NAMESPACE = @" 111 | namespace {0} {{ 112 | {1} 113 | }} 114 | "; 115 | private static readonly string BODY = @" 116 | using System; 117 | using System.Reflection; 118 | 119 | /// 120 | /// A strongly-typed resource class, for looking up localized strings, etc. 121 | /// 122 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""System.Resources.Tools.StronglyTypedResourceBuilder"", ""4.0.0.0"")] 123 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 124 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 125 | 126 | internal class {0} {{ 127 | 128 | private static global::System.Resources.ResourceManager resourceMan; 129 | 130 | private static global::System.Globalization.CultureInfo resourceCulture; 131 | 132 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(""Microsoft.Performance"", ""CA1811:AvoidUncalledPrivateCode"")] 133 | internal {0}() {{ 134 | }} 135 | 136 | /// 137 | /// Returns the cached ResourceManager instance used by this class. 138 | /// 139 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 140 | internal static global::System.Resources.ResourceManager ResourceManager {{ 141 | get {{ 142 | if (object.ReferenceEquals(resourceMan, null)) {{ 143 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(""{1}.resources.{3}"", typeof({0}).Assembly); 144 | resourceMan = temp; 145 | }} 146 | return resourceMan; 147 | }} 148 | }} 149 | 150 | /// 151 | /// Overrides the current threads CurrentUICulture property for all 152 | /// resource lookups using this strongly typed resource class. 153 | /// 154 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 155 | internal static global::System.Globalization.CultureInfo Culture {{ 156 | get {{ 157 | return resourceCulture; 158 | }} 159 | set {{ 160 | resourceCulture = value; 161 | }} 162 | }} 163 | {2} 164 | }} 165 | "; 166 | 167 | private static readonly string ENTRY = @" 168 | 169 | /// 170 | /// Looks up a localized string similar to {1} 171 | /// 172 | internal static string {0} {{ 173 | get {{ 174 | return ResourceManager.GetString(""{0}"", resourceCulture); 175 | }} 176 | }} 177 | "; 178 | 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/ResGen/README.md: -------------------------------------------------------------------------------- 1 | ResGen was copied from [PowerShell/PowerShell](https://github.com/PowerShell/PowerShell/tree/master/src/ResGen). 2 | 3 | In this case ResGen is used to process resource file `src\Microsoft.PowerShell.IoT\resources\Resources.resx` 4 | and to generate `src\Microsoft.PowerShell.IoT\gen\Resources.cs` that is used in the build. 5 | 6 | For any modification to resources: 7 | 8 | 1. make required changes in Resources.resx using a text editor 9 | 1. generate updated Resources.cs: 10 | 1. `cd src\ResGen` 11 | 1. `dotnet run` 12 | 1. compile IoT module as usual to ensure there are no build breaks 13 | 1. check in updated `Resources.resx` and `Resources.cs` 14 | 15 | [More info on ResGen](https://github.com/PowerShell/PowerShell/blob/master/docs/dev-process/resx-files.md). 16 | -------------------------------------------------------------------------------- /src/ResGen/ResGen.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generates C# typed bindings for .resx files 5 | netcoreapp2.0 6 | resgen 7 | Exe 8 | win7-x86;win7-x64;osx.10.12-x64;linux-x64 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/psiot.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "PowerShell IoT tests" { 5 | BeforeAll { 6 | # This creates the session to the test Pi. The hostname maps to a random IP 7 | $Global:SESSION = New-PSSession -HostName raspberry -UserName pi 8 | } 9 | Context "I2C tests" { 10 | BeforeAll { 11 | Invoke-Command -Session $Global:SESSION -ScriptBlock { 12 | # Import the example BME280 which wraps PowerShell IoT cmdlets 13 | Import-Module Microsoft.PowerShell.IoT.BME280 14 | } 15 | 16 | } 17 | It "Can get the the BME280 I2C device" { 18 | $device = Invoke-Command -Session $Global:SESSION -ScriptBlock { 19 | return Get-BME280Device -Id 0x76 20 | } 21 | $device | Should -Not -BeNullOrEmpty 22 | $device.Id | Should -Be 118 23 | $device.FriendlyName | Should -Be "BME280" 24 | } 25 | It "Can get the BME280 data" { 26 | $data = Invoke-Command -Session $Global:SESSION -ScriptBlock { 27 | return Get-BME280Data 28 | } 29 | $data.Temperature | Should -Not -BeNullOrEmpty 30 | $data.Pressure | Should -Not -BeNullOrEmpty 31 | $data.Humidity | Should -Not -BeNullOrEmpty 32 | } 33 | } 34 | Context "GPIO tests" { 35 | # GPIO pins can either act as an input or output pin. In other words, 36 | # you can either set a pin's value or read a pin's value. For example, 37 | # if you set pin 20 to "High" (aka 1) and attempt to read the value of 38 | # pin 20, you will not get the result of your previous set operation. 39 | 40 | # To get around this limitation, on the test Raspberry Pi we have two pins 41 | # (22 and 26) connected. By doing this, we can set the value on pin 22 and 42 | # read that value on pin 26. This next test demonstrates that. 43 | It "Can get and set a GPIO's pin value" { 44 | $highValueResult = Invoke-Command -Session $Global:SESSION -ScriptBlock { 45 | Set-GpioPin -Id 26 -Value High 46 | return Get-GpioPin -Id 22 47 | } 48 | $highValueResult.Id | Should -Be 22 49 | $highValueResult.Value | Should -Be "High" 50 | 51 | $lowValueResult = Invoke-Command -Session $Global:SESSION -ScriptBlock { 52 | Set-GpioPin -Id 26 -Value Low 53 | return Get-GpioPin -Id 22 54 | } 55 | $lowValueResult.Id | Should -Be 22 56 | $lowValueResult.Value | Should -Be "Low" 57 | } 58 | It "Can use the -Raw flag to get the raw value" { 59 | $rawValue = Invoke-Command -Session $Global:SESSION -ScriptBlock { 60 | Set-GpioPin -Id 26 -Value High 61 | return Get-GpioPin -Id 22 -Raw 62 | } 63 | $rawValue | Should -Be 1 64 | } 65 | It "Read non-connected pin with PullDown and return Low" { 66 | $result = Invoke-Command -Session $Global:SESSION -ScriptBlock { 67 | return Get-GpioPin -Id 23 -PullMode PullDown -Raw 68 | } 69 | $result | Should -Be 0 70 | } 71 | It "Read non-connected pin with PullUp and return High" { 72 | $result = Invoke-Command -Session $Global:SESSION -ScriptBlock { 73 | return Get-GpioPin -Id 23 -PullMode PullUp -Raw 74 | } 75 | $result | Should -Be 1 76 | } 77 | } 78 | Context "SPI tests" { 79 | # SPI test: LIS3DH motion sensor; datasheet: www.st.com/resource/en/datasheet/lis3dh.pdf 80 | # Read "WHO_AM_I (0Fh)" register; value should be 0x33 81 | It "Can read data from the LIS3DH motion sensor" { 82 | $result = Invoke-Command -Session $Global:SESSION -ScriptBlock { 83 | $d = @(0x8F,0x0) 84 | return Send-SPIData -Channel 0 -Data $d 85 | } 86 | $result.Channel | Should -Be 0 87 | $result.Data[0] | Should -Be 0x8F 88 | $result.Response[1] | Should -Be 0x33 89 | $result.Frequency | Should -Be 500000 90 | } 91 | It "Can use the -Raw flag to get the raw value" { 92 | $result = Invoke-Command -Session $Global:SESSION -ScriptBlock { 93 | $d = @(0x8F,0x0) 94 | return Send-SPIData -Channel 0 -Data $d -Raw 95 | } 96 | $result[1] | Should -Be 0x33 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /tools/vsts.ps1: -------------------------------------------------------------------------------- 1 | Push-Location "$PSScriptRoot/../" 2 | 3 | Install-Module InvokeBuild -Scope CurrentUser -Force 4 | 5 | # Build and package for Pi 6 | Invoke-Build 7 | 8 | # Move build to Pi 9 | ./Move-PSIoTBuild.ps1 -Ip raspberry -WithExample Microsoft.PowerShell.IoT.BME280 10 | 11 | # Run Pester tests 12 | Push-Location "test" 13 | Invoke-Build Test 14 | 15 | Pop-Location 16 | Pop-Location 17 | -------------------------------------------------------------------------------- /tools/vstsBuild.psm1: -------------------------------------------------------------------------------- 1 | # VSTS task states: Succeeded|SucceededWithIssues|Failed|Cancelled|Skipped 2 | $succeededStateName = 'Succeeded' 3 | $warningStateName = 'SucceededWithIssues' 4 | $errorStateName = 'Failed' 5 | 6 | # store the current state used by *-VstsTaskState and Write-VstsMessage 7 | $script:taskstate = $succeededStateName 8 | 9 | function Clear-VstsTaskState 10 | { 11 | $script:taskstate = $succeededStateName 12 | } 13 | 14 | function Get-TempFolder 15 | { 16 | $tempPath = [System.IO.Path]::GetTempPath() 17 | # Use the agent temp on VSTS which is cleanup between builds (the user temp is not) 18 | if($env:AGENT_TEMPDIRECTORY) 19 | { 20 | $tempPath = $env:AGENT_TEMPDIRECTORY 21 | } 22 | 23 | $tempFolder = Join-Path -Path $tempPath -ChildPath ([System.IO.Path]::GetRandomFileName()) 24 | if(!(test-path $tempFolder)) 25 | { 26 | $null = New-Item -Path $tempFolder -ItemType Directory 27 | } 28 | 29 | return $tempFolder 30 | } 31 | 32 | $script:AlternateStagingDirectory = $null 33 | function Get-StagingDirectory 34 | { 35 | # environment variable are documented here: 36 | # https://docs.microsoft.com/en-us/vsts/build-release/concepts/definitions/build/variables?tabs=batch 37 | if($env:BUILD_STAGINGDIRECTORY) 38 | { 39 | return $env:BUILD_STAGINGDIRECTORY 40 | } 41 | else { 42 | if(!$script:AlternateStagingDirectory) 43 | { 44 | Write-VstsInformation "Cannot find staging directory, logging environment" 45 | Get-ChildItem env: | ForEach-Object { Write-VstsInformation -message $_} 46 | $script:AlternateStagingDirectory = Get-TempFolder 47 | } 48 | return $script:AlternateStagingDirectory 49 | } 50 | } 51 | 52 | $script:publishedFiles = @() 53 | # Publishes build artifacts 54 | function Publish-VstsBuildArtifact 55 | { 56 | param( 57 | [parameter(Mandatory,HelpMessage="Path to publish artifacts from.")] 58 | [string]$ArtifactPath, 59 | [parameter(HelpMessage="The folder to same artifacts to.")] 60 | [string]$Bucket = 'release', 61 | [parameter(HelpMessage="If an artifact is unzipped, set a variable to the destination path with this name. Only supported with '-ExpectedCount 1'")] 62 | [string]$Variable, 63 | [parameter(HelpMessage="Expected Artifact Count. Will throw if the count does not match. Not specified or -1 will ignore this parameter.")] 64 | [int]$ExpectedCount = -1 65 | ) 66 | $ErrorActionPreference = 'Continue' 67 | Write-VstsInformation -message "Publishing artifacts: $ArtifactPath" 68 | 69 | # In VSTS, publish artifacts appropriately 70 | $files = Get-Item -Path $ArtifactPath | Select-Object -ExpandProperty FullName 71 | $destinationPath = Join-Path (Get-StagingDirectory) -ChildPath $Bucket 72 | if(-not (Test-Path $destinationPath)) 73 | { 74 | $null = New-Item -Path $destinationPath -ItemType Directory 75 | } 76 | 77 | foreach($fileName in $files) 78 | { 79 | # Only publish files once 80 | if($script:publishedFiles -inotcontains $fileName) 81 | { 82 | $leafFileName = $(Split-path -Path $fileName -Leaf) 83 | 84 | $extension = [System.IO.Path]::GetExtension($leafFileName) 85 | $nameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($leafFileName) 86 | # Only expand the symbol '.zip' package 87 | if($extension -ieq '.zip' -and $nameWithoutExtension.Contains("symbols")) 88 | { 89 | $unzipPath = (Join-Path $destinationPath -ChildPath $nameWithoutExtension) 90 | if($Variable) 91 | { 92 | Write-VstsInformation -message "Setting VSTS variable '$Variable' to '$unzipPath'" 93 | # Sets a VSTS variable for use in future build steps. 94 | Write-Host "##vso[task.setvariable variable=$Variable]$unzipPath" 95 | # Set a variable in the current process. PowerShell will not pickup the variable until the process is restarted otherwise. 96 | Set-Item env:\$Variable -Value $unzipPath 97 | } 98 | Expand-Archive -Path $fileName -DestinationPath $unzipPath 99 | } 100 | 101 | Write-Host "##vso[artifact.upload containerfolder=$Bucket;artifactname=$Bucket]$fileName" 102 | $script:publishedFiles += $fileName 103 | } 104 | } 105 | 106 | if($ExpectedCount -ne -1 -and $files.Count -ne $ExpectedCount) 107 | { 108 | throw "Build did not produce the expected number of binaries. $($files.count) were produced instead of $ExpectedCount." 109 | } 110 | } 111 | 112 | function Write-VstsError { 113 | param( 114 | [Parameter(Mandatory=$true)] 115 | [Object] 116 | $Error, 117 | [ValidateSet("error","warning")] 118 | $Type = 'error' 119 | ) 120 | 121 | $message = [string]::Empty 122 | $errorType = $Error.GetType().FullName 123 | $newLine = [System.Environment]::NewLine 124 | switch($errorType) 125 | { 126 | 'System.Management.Automation.ErrorRecord'{ 127 | $message = "{0}{2}`t{1}" -f $Error,$Error.ScriptStackTrace,$newLine 128 | } 129 | 'System.Management.Automation.ParseException'{ 130 | $message = "{0}{2}`t{1}" -f $Error,$Error.StackTrace,$newLine 131 | } 132 | 'System.Management.Automation.Runspaces.RemotingErrorRecord' 133 | { 134 | $message = "{0}{2}`t{1}{2}`tOrigin: {2}" -f $Error,$Error.ScriptStackTrace,$Error.OriginInfo,$newLine 135 | } 136 | default 137 | { 138 | # Log any unknown error types we get so we can improve logging. 139 | log "errorType: $errorType" 140 | $message = $Error.ToString() 141 | } 142 | } 143 | $message.Split($newLine) | ForEach-Object { 144 | Write-VstsMessage -type $Type -message $PSItem 145 | } 146 | } 147 | 148 | # Log messages which potentially change job status 149 | function Write-VstsMessage { 150 | param( 151 | [ValidateSet("error","warning")] 152 | $type = 'error', 153 | [String] 154 | $message 155 | ) 156 | 157 | if($script:taskstate -ne $errorStateName -and $type -eq 'error') 158 | { 159 | $script:taskstate = $errorStateName 160 | } 161 | elseif($script:taskstate -eq $succeededStateName) { 162 | $script:taskstate = $warningStateName 163 | } 164 | 165 | # See VSTS documentation at https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md 166 | # Log task message 167 | Write-Host "##vso[task.logissue type=$type]$message" 168 | } 169 | 170 | # Log informational messages 171 | function Write-VstsInformation { 172 | param( 173 | [String] 174 | $message 175 | ) 176 | 177 | Write-Host $message 178 | } 179 | 180 | function Write-VstsTaskState 181 | { 182 | # See VSTS documentation at https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md 183 | # Log task state 184 | Write-Host "##vso[task.complete result=$script:taskstate;]DONE" 185 | } 186 | 187 | Export-ModuleMember @( 188 | 'Publish-VstsBuildArtifact' 189 | 'Write-VstsError' 190 | 'Write-VstsMessage' 191 | 'Clear-VstsTaskState' 192 | 'Write-VstsTaskState' 193 | ) -------------------------------------------------------------------------------- /vsts.yml: -------------------------------------------------------------------------------- 1 | resources: 2 | - repo: self 3 | queue: 4 | name: PowerShell IoT 5 | demands: DotNetFramework 6 | steps: 7 | - task: PowerShell@1 8 | displayName: Run Build 9 | inputs: 10 | scriptType: inlineScript 11 | inlineScript: pwsh -c ./tools/vsts.ps1 12 | - task: PublishTestResults@2 13 | displayName: Publish Test Results 14 | inputs: 15 | testRunner: NUnit 16 | testResultsFiles: '**\Test*.xml' --------------------------------------------------------------------------------