May 16 2016

Turning on a led from powershell over wi-fi, i2c

Category: C# | Hardware | IoT | PIC | PowerShell | Raspberry | Robot — Duke @ 16:44

It is a couple of week i’ve started a new project, something i’ve always wanted to do, but i’ve never had time or resources or… the real will to complete it.

I’m not saying i will make it this time but certainly it is progressing.

Today i’ve reached a critic mass, something interesting and usefull enough to post about it, what the hispter and the millennials calls “minumum viable product”, it will be part of somthing bigger but i want to blog it to not forget it and, as i’ve found difficulties in it, maybe help someone else, who knows?

My project is very “hardware” oriented, something in the real “maker” area. I’m not an hardware guy, but i’ve the minimal skill required to play whit the modern electronics, that at my eyes looks like logo bricks for grown-up (while you treat them as black box).

So here is what i want to do:

I’ve a Raspberry Pi 2, a wi-fi dongle TP-link TL-WN725 (i also have the EDIMAX EW-7811Un but doesn’t work well with Win 10 IoT yet)  and the latest build of Windows 10 IoT (v.10.0.14328.1000, apparently, longer version are better….)

Then i’ve a breadboard, a bounch of headed cables, resistors and a led, plus a PIC16F18325 (in a PDIP14 package)

To complete this mini-lab i’ve a mini dso 203 oscilloscope and of course a PicKit3 programmer for the PIC

I want to type a command in my computer (any of the my computers) and have a led turned on or off, but not by the raspberry, on the PIC microcontroller instead.this because i’ll later need some of the advanced capabilities on the PIC, but for now for educational and demonstrative purposes, the led is well enough.

so my setup is the following:

image

So from wherever am i, i want to turn on that led with a command on my shell

that’s a lot of protocols and OS and software and languages together, from the very high level powershell to the closest possible to the metal +vcc signal,

I know, i know, it is called Internet Of Things, and it has already been invented by someone else, but who cares? I want to make my very own IoT.

so, first thing first:

Today we will cover how to connect from my pc to a raspberry who has in turn Win 10 iot on. If you are intereste on how to install Windows 10 Iot, the excellent Scott Hanselman has a blog post for this.

so i want to open powershell a type a command to connect to my raspberry, Win 10 IoT is already configured to receive powershell session (WinRM is turned on by default) all you need to know is your raspberry name and the root user’s credential

i’ve then a module called Connect-Raspberry.psm1 which loads automatically any time i open a PS session (thanks to an import-module in my $psprofile)

the code of the module is the following

$knownRaspberry = @{Jeeg=@{Username="Administrator";Password="mysecretpassword"}}

function Get-CredentialObject([string] $username, [string] $password){ 
    $encryptedPass = ConvertTo-SecureString $password -AsPlainText -Force 
    return new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $encryptedPass 
}

function Connect-Raspberry([string] $machine, [string] $username, [string] $password){    
   

    $trustedHosts = get-item  "WSMan:\localhost\Client\TrustedHosts" 
    if(!($trustedHosts.value -contains $machine)){ 
        Set-Item WSMan:\localhost\Client\TrustedHosts -Value $machine -Force 
    }

    if($machine -eq $null){ throw throw [System.IO.Exception] "Please specify Machine"} 
    if($knownRaspberry.ContainsKey($machine) -and (!$username) -and (!$password)) 
    { 
        $password = $knownRaspberry[$machine].password; 
        $username = $knownRaspberry[$machine].Username;        
    } 
    
    if($username) { 
        $username = "Administrator" 
    }    
    if(!$password){ 
        $cred = get-credential -Message "enter password" -UserName "$machine\$username" 
    } 
    else{ 
        $cred = get-CredentialObject -username "$machine\$username" -password $password 
    } 
    ############## 
    #the following line is an hack to workaround a bug, maybe in the future will not be required 
    #remove-module psreadline -force 
    ############## 
    Enter-PSSession -ComputerName $machine -Credential $cred 
}

 

if it is the first time you run it you may need to run as administrator as it will set the trustedhosts for the WSMan\client to trust the raspberry

given that i can connect to my  raspberry (it is called Jeeg as all the machine in my network have name of Gō Nagai’s robots)

connect

of course the script can take username and password but you can map in the $knownRaspberry variables all your frequently accessed raspberry

Obviously Jeeg is powered up and connecte to my Wi-Fi lan by the TP-Link Usb dongle

Now i have a shell on my Pc (Grendizer) that is actually a remote session on Jeeg and i can ask Jeeg to do anything it knows.

but now i want Jeeg to write a message on it’s I2C bus. Windows 10 IoT has the drivers, but powershell knows nothing about them.

The good thing is that Powershell understand .Net very well and that Windows 10 IoT can run .Net Universal Windows App!

so my idea was to write a simplified wrapper around the .Net API for the I2C to use as base for a Powershell Module

to do this you need Visual Studio 2015 Update 2 with the Windows 10 IoT SDK that match at best possible the version of Windows 10 IoT oon the raspberry. I’ve found that the SDK are usually a couple of versions behind the Raspberry Build but fortunately it doesn’t seems to be a problem!

so File>New>Project and select Universal Windows (dll)

image

you should have these references.

the wrapper’s code is the following:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using Windows.Devices.Enumeration; 
using Windows.Devices.I2c;


namespace Bus 
{    
    public enum Speed 
    { 
        Standard = 0, 
        Fast = 1, 
    }

    public class I2CControllerAddressException : Exception 
    { 
        public I2CControllerAddressException(int address, string deviceId) 
            : base(string.Format("The address {0} on I2C Controller {1} is currently in use.", address, deviceId)) 
        { } 
    }

    public class I2CControllerFoundException : Exception 
    { 
        public I2CControllerFoundException() 
            : base("Could not find the I2C controller") 
        { } 
    }   

    public class I2c 
    { 
        private static I2cDevice Open(int targetAddress, Speed speed) 
        {

            var advancedQuerySyntaxString = I2cDevice.GetDeviceSelector(); 
            var controllerDeviceIds = DeviceInformation.FindAllAsync(advancedQuerySyntaxString).GetAwaiter().GetResult(); 
            if (controllerDeviceIds == null || controllerDeviceIds.Count == 0) 
            { 
                throw new I2CControllerFoundException(); 
            } 
            var i2cControllerDeviceId = controllerDeviceIds[0].Id;

            var i2cSettings = new I2cConnectionSettings(targetAddress); 
            i2cSettings.BusSpeed = (I2cBusSpeed)speed;

            // Create an I2cDevice with our selected bus controller ID and I2C settings 
            var I2cSalve = I2cDevice.FromIdAsync(i2cControllerDeviceId, i2cSettings).GetAwaiter().GetResult();

            return I2cSalve; 
        }

        public static void Send(int targetAddress, Speed speed, byte[] message) 
        { 
            var device = Open(targetAddress, speed); 
            device.Write(message); 
            
            device.Dispose(); 
        }

        public static byte[] Receive(int targetAddress, Speed speed, byte[] message, int expectedReplyLenght) 
        { 
            var device = Open(targetAddress, speed); 
            var reply = new byte[expectedReplyLenght]; 
            device.WriteRead(message, reply); 
            device.Dispose(); 
            return reply; 
        }

    } 
}

 

complie it and you will have a nice i2c.dll file

that is almost enough to play with the i2c bus from the command line.

why almost? because this file reference the System.Runtime.WindowsRuntime that you have to find out where it is, it will not copyed in the same folder of your dll, it is not the same mechanism of the old projects where you can specify “copy local”

the best way i’ve find is to make an universal windows app (.exe) wich refers this assembly and compile it, in the binary folder you’ll find also the the System.Runtime.WindowsRuntime.dll you need

 

now open the file explorer and type \\Jeeg\C$

you’ll see the “Jeeg” C disk, copy both the assembly in the path

\\jeeg\c$\Program Files\WindowsPowerShell\Modules\i2c

(you need to create the I2C folder of course)

so you will have the following

image

and you are ready!

from your remoted session on Powershell just type

Import-module i2c

to load the module and be able to send and receive date on i2c using powershell

and this ishow you send a command on the i2c bus

[bus.i2c]::Send(8,[bus.speed]::Standard,[byte[]](0xAA))

this command for example sends (master write on server, in the i2c terminology) on the address 8 the byte 0xAA, using the standatd speed (100KHz)

Raspberry is also able to use the fast mode (400KHz)

I2C has also two speed,Full Speed 1MHz and High Speed 3,2MHz but Raspberry is not able (at the moment at least) to use these speed, in any case the PIC16F series doesn’t have enough processing power to handle these clocks, i think it is necessary at least 16bit PIC to work with these speed, but i’ve not investigated.

 

next time i'll talk about the PIC firmware, that's the most interesting part to me and there are many tricks that may be useful to any hobbist that may face these topics for the first time :)

Tags: