I really do enjoy PowerShell; it makes interfacing with a whole host of components within a machine--including the built-in geolocation API (PowerShell Script – Get-ComputerGeolocation | Anything about IT).  Additionally, PowerShell makes it very easy to read data that is sent from an Arduino's serial port--such as the serial portemulator that is accessible through the USB port of an Arduino Micro (Arduino - ArduinoBoardMicro).  And on top of that, using the Phidgets drivers (Operating System Support - Phidgets Support), PowerShell can directly communicate with Phidgets I/O boards (such as this USB I/O converter board: Phidgets Inc. - 1011_0 - PhidgetInterfaceKit 2/2/2), allowing easy interfacing between a computer and a whole host of analog and digital sensors and controllers, such as vibration sensors (Phidgets Inc. - 1104_0 - Vibration Sensor) and servo controllers (Phidgets Inc. - 1061_1 - PhidgetAdvancedServo 8-Motor).

 

I've assembled a small collection of such sensors, and below that, I'll discuss how I specifically read data from those.

 

image.jpg

On the left, you can see the collection of sensors, connected to the computer via USB; the laptop is showing a PI Coresight screen that shows real-time data from those sensors.

 

For reference, this diagram shows the layout of those sensors:

Mobile Site Poster - landscape.png

 

I've written up a PowerShell script that allows me to read data (through direct USB connections) from an Arduino Micro (connected to a solar cell and a combination light/humidity sensor: Overview | DHTxx Sensors | Adafruit Learning System), a Phidgets Servo Controller, and a Phidgets InterfaceKit (reading vibration levels, via Phidgets Inc. - 1104_0 - Vibration Sensor, and sound levels, via Phidgets Inc. - 1133_0 - Sound Sensor).  The script reads data from all of those data sources (in addition to determining the current location of the connected computer), then it writes that data to text files, which can be easily parsed by the PI UFL interface, but the script could easily be modified to push that data via the PI Web API directly to a PI System.

 

Hopefully this script can be useful for folks who are interested in exploring how they can collect data from such IoT peripherals as Arduinos and Phidgets.  I hope this helps with your own research!

 

# Preparation: close ports and connections if they exist
if ($MyPort -ne $null) { $MyPort.Close() }
if ($MyAdvancedServoObject -ne $null) { $MyAdvancedServoObject.Close() }
if ($MyIOKitObject -ne $null) { $MyIOKitObject.Close() }


#------------------------------------------------------------------------------------------------
# Read Arduino
#------------------------------------------------------------------------------------------------
write-host "Read: Arduino -----------------------------------------------------------------"


# Specify where the output data should be written
$MyOutputFilePath_Arduino = "C:\ArduinoData\ArduinoSerialOutput.txt"
write-host "Output will be written to" $MyOutputFilePath_Arduino


# Define the time format
$MyDateFormat_Arduino = "dd-MMM-yy HH-mm-ss"


# Get all of the ports
write-host "Ports:"
$MySerialPorts = Get-WMIObject Win32_SerialPort | Select-Object DeviceID,Description


# Search through the port list to find the Arduino
$DesiredDescription = "*Arduino*"
$MyPortNumber = ""
for ($i=0; $i -lt $MySerialPorts.Count; $i++) {


    # List out each port
    write-host "Port found:" $MySerialPorts.item($i)


    # If the port matches, store its port number
    if ($MySerialPorts.item($i).Description -like $DesiredDescription) {
        $MyPortNumber = $MySerialPorts.item($i).DeviceID
        write-host "Serial port found:" $MyPortNumber
    }
}


# Define a flag for monitoring this data source
$MonitorCOMPort = $false


# If a port was found, open the port and start listening; otherwise, don't
if ($MyPortNumber -eq "") {
    write-host "No port found!"
    $MonitorCOMPort = $false
}
else {


    # Create the serial port object and enable DTR (this is critical for the Arduino)
    $MyPort = new-Object System.IO.Ports.SerialPort $MyPortNumber,9600,None,8,one
    $MyPort.DtrEnable = "true"


    # Open the serial port
    write-host "Opening COM port..."
    $MyPort.Open()    
    
    # Test to see if the data source is ready
    if ($MyPort.IsOpen) {


        write-host "Port open!  Now listening for new values..."


        # Turn on monitoring!
        $MonitorCOMPort = $true
    }
    else {
        # Turn off monitoring
        write-host "Port open failed; try unplugging and replugging in the USB cable, or closing and relauncing the script."
        $MonitorCOMPort = $false
    }
}


#------------------------------------------------------------------------------------------------
# Read Phidget server controller
#------------------------------------------------------------------------------------------------
write-host "Read: Servo Controller -----------------------------------------------------------------"


# Specify the path to the Phidget DLL
$MyPhidgetDLL = "C:\Program Files\Phidgets\Phidget21.NET.dll"


# Add the Phidget type usign that path
Add-Type -Path $MyPhidgetDLL


# Specify where the output data should be written
$MyOutputFilePath_ServoController = "C:\PhidgetData\phidgetServoOutput"
write-host "Output will be written to" $MyOutputFilePath_ServoController


# Define the time format
$MyDateFormat_Servo = "dd-MMM-yy HH-mm-ss-ff"


# Specify which port on the servo controller the servo is on
$MyServoNumber = 0


# Create a servo controller object
$MyAdvancedServoObject = New-Object -TypeName Phidgets.AdvancedServo


# Define a flag for monitoring this data source
$MonitorServoController = $false


# Try to open the connection to the servo controller
$MyAdvancedServoObject.open()


# Wait for the Phidget to be attached
$AttachWait_Seconds = 20
write-host "Waiting" ($AttachWait_Seconds) "seconds for Phidget to be attached..."
$MyAdvancedServoObject.waitForAttachment($AttachWait_Seconds * 1000)


# Test to see if the data source is ready
if ($MyAdvancedServoObject.Attached -eq $true)
{
    # Turn on monitoring!
    $MonitorServoController = $true
    write-host "Phidget detected!"


    # Set the servo velocity!
    $MyAdvancedServoObject.servos[$MyServoNumber].VelocityLimit = ($MyAdvancedServoObject.servos[$MyServoNumber].VelocityMax) * 0.4


    # Engage the servo
    $MyAdvancedServoObject.servos[$MyServoNumber].Engaged = $true


    # Start the servo moving!
    $MyAdvancedServoObject.servos[$MyServoNumber].Position = $MyAdvancedServoObject.servos[$MyServoNumber].PositionMax
}
else
{
    # Turn off monitoring
    $MonitorServoController = $false
    write-host "No Phidget detected"
}


#------------------------------------------------------------------------------------------------
# Read Phidget vibration and sound sensor
#------------------------------------------------------------------------------------------------
write-host "Read: Phidget vibration and sound sensor -----------------------------------------------------------------"


# Specify where the output data should be written
$MyOutputFilePath_SoundandVibration = "C:\PhidgetData\phidgetOutput"
write-host "Output will be written to" $MyOutputFilePath_SoundandVibration


# Define the time format
$MyDateFormat_SoundAndVibration = "MM-dd-yyyy-HH-mm-ss-ff"


# Create the Phidget object
$MyIOKitObject = New-Object -TypeName Phidgets.InterfaceKit


# Define a flag for monitoring this data source
$MonitorLightAndSound = $false


# Try to open the connection 
$MyIOKitObject.open()


# Wait for the Phidget to be attached
$AttachWait_Seconds = 20
write-host "Waiting" ($AttachWait_Seconds) "seconds for Phidget to be attached..."
$MyIOKitObject.waitForAttachment($AttachWait_Seconds * 1000)


# Test to see if the data source is ready
if ($MyIOKitObject.Attached -eq $true)
{
    # Turn on monitoring!
    $MonitorLightAndSound = $true
    write-host "Phidget detected!"
}
else
{
    # Turn off monitoring
    $MonitorLightAndSound = $false
    write-host "No Phidget detected"
}


#------------------------------------------------------------------------------------------------
# Read Geolocation data 
#------------------------------------------------------------------------------------------------
write-host "Read: Geolocation -----------------------------------------------------------------"


# Specify where the output data should be written
$MyOutputFilePath_Geolocation = "C:\Geolocation\locationddata"
write-host "Output will be written to" $MyOutputFilePath_Geolocation


# Define the time format
$MyDateFormat_Geolocation = "MM-dd-yyyy-HH-mm-ss-ff"


# Define a flag for monitoring this data source
$MonitorGeolocation = $true


#------------------------------------------------------------------------------------------------
# Begin state machine
#------------------------------------------------------------------------------------------------


#Define the wait duration in between loops


$MyWaitDuration_Milliseconds = 500


while($true) {
    
    # ---------------------------------------------------------------------------


    # Check the Arduino data source
    if ($MonitorCOMPort -eq $true)
    {
        # Read data, and outout it to a file
        $MyNewLine = $MyPort.readline()
        Out-File -FilePath ($MyOutputFilePath_Arduino + "_" + (Get-Date).ToString($MyDateFormat_Arduino) + ".txt") -InputObject $MyNewLine -Encoding ascii 
    }


    # ---------------------------------------------------------------------------


    # Check the servo data source
    if ($MonitorServoController -eq $true)
    {
        # Define a buffer range
        $MyServoPositionBuffer = 3


        # If the servo has reached its max position, reverse its direction; tell it to go to the other range 
        if ($MyAdvancedServoObject.servos[$MyServoNumber].Position -ge $MyAdvancedServoObject.servos[$MyServoNumber].PositionMax - $MyServoPositionBuffer)
        {
            # Go to the min position
            $MyAdvancedServoObject.servos[$MyServoNumber].Position = $MyAdvancedServoObject.servos[$MyServoNumber].PositionMin
        }
        if ($MyAdvancedServoObject.servos[$MyServoNumber].Position -le $MyAdvancedServoObject.servos[$MyServoNumber].PositionMin + $MyServoPositionBuffer)
        {
            # Go to the max position
            $MyAdvancedServoObject.servos[$MyServoNumber].Position = $MyAdvancedServoObject.servos[$MyServoNumber].PositionMax
        }


        # Read data, and outout it to a file
        $MyNewLine = "" + 
        "Time,ControllerName,SerialNumber,ServoNumber,Acceleration,Current,Position,Velocity,Type,IsSynchronized," + 
        (Get-Date).ToString($MyDateFormat_Servo) + "," +
        $MyAdvancedServoObject.Name +"," +
        $MyAdvancedServoObject.SerialNumber +"," +
        $MyServoNumber + "," + 
        $MyAdvancedServoObject.servos[$MyServoNumber].Acceleration + "," + 
        $MyAdvancedServoObject.servos[$MyServoNumber].Current + "," + 
        $MyAdvancedServoObject.servos[$MyServoNumber].Position + "," + 
        $MyAdvancedServoObject.servos[$MyServoNumber].Velocity + "," + 
        $MyAdvancedServoObject.servos[$MyServoNumber].Type + "," +
        $MyAdvancedServoObject.servos[$MyServoNumber].IsSynchronized
        Out-File -FilePath ($MyOutputFilePath_ServoController + "_" + (Get-Date).ToString($MyDateFormat_Servo) + ".txt") -InputObject $MyNewLine -Encoding ascii 
    }


    # ---------------------------------------------------------------------------


    # Check the sound and vibration data source
    if ($MonitorLightAndSound -eq $true)
    {
        # Read data, and outout it to a file
        $MyNewLine = "sensor0," + (Get-Date).ToString($MyDateFormat_SoundAndVibration) + "," + $MyIOKitObject.sensors[0].Value +
        "`n" + 
        "sensor1," + (Get-Date).ToString($MyDateFormat_SoundAndVibration) + "," + $MyIOKitObject.sensors[1].Value +
        "`n"
        Out-File -FilePath ($MyOutputFilePath_SoundandVibration + "_" + (Get-Date).ToString($MyDateFormat_SoundAndVibration) + ".csv") -InputObject $MyNewLine -Encoding ascii 
    }


    # ---------------------------------------------------------------------------


    # Check the geolocation data source
    if ($MonitorGeolocation -eq $true)
    {
        # Initialize variables
        $MyLatitude = 0
        $MyLongitude = 0


        # Create an object that uses the Windows Location API
        $MyLocation = New-Object –ComObject LocationDisp.LatLongReportFactory

        # Get Status 
        $MyLocationStatus = $MyLocation.status


        # Define the success status
        $SuccessStatus = "4"


        # Test if geolocation succeeded
        If ($MyLocationStatus -eq $SuccessStatus)
        {


            # Get Latitude and Longitude from the LatlongReport property
            $MyLatitude = $MyLocation.LatLongReport.Latitude 
            $MyLongitude = $MyLocation.LatLongReport.Longitude
            #write-host "Geolocation succeeded: lat , lng =" $MyLatitude "," $MyLongitude


            # Read data, and outout it to a file
            $MyNewLine = "Time,Latitude,Longitude," + (Get-Date).ToString($MyDateFormat_Geolocation) + "," + $MyLatitude + "," + $MyLongitude
            Out-File -FilePath ($MyOutputFilePath_Geolocation + "_"  + ".csv") -InputObject $MyNewLine -Encoding ascii 
        }
    }


    # ---------------------------------------------------------------------------


    # Wait until the next measurement
    start-sleep -Milliseconds $MyWaitDuration_Milliseconds
}