Tuesday, March 10, 2015

Home Water System Pt 2 - Get and Set SNMP

With PowerShell, there's a couple of great options to purchase SNMP libraries to perform SNMP get and set. I had some success with OlePrn.OleSNMP class as UsPeoples, over at tomorrow.uspeoples.org, wrote about, but my success was short lived. It only supports SNMPv1 and attempting to do any of the Methods from three of my Windows 7 computers returns:

Exception calling "Get" with "1" argument(s): "WinSNMP API Error: For internal/undefined errors"
At line:1 char:10
+ $SNMP.Get <<<< (".")
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ComMethodTargetInvocation

Alternatively, I've been using a command-line (CLI) tool with PowerShell and capturing the output. Since there are a few old CLI tools for SNMP that are free, like snmpget.exe and snmpset.exe by snmpsoft (dot) com. I've been using this for my SNMP get and set routines to control my home brewed SCADA system for nearly a year now with no issue.

So, where do we get started?

You can capture any output from a CLI tool fairly easily. Just run the program inside of a PowerShell variable value, as such:

$output = mycli.exe

That seems simple enough. Next, you need to ensure that you capture all of your CLI tool's output - or at least I prefer to - including any errors which sometimes sneak by. about_Redirection describes that 2>&1 at the end of your CLI tool will capture your success output along with any error output. You can also include all output with *>&1 - to each their own, but I prefer just errors and success output.

$output = mycli.exe 2>&1

Wrap it all tight in a few functions and you just saved some dollars... on SNMP get and set funcitons.

PowerShell get-snmp function
  Function to capture output from snmpget exe CLI tool.  
  Based on the parameters sent, the CLI tool will be triggered and the output will be returned based on the output.  
  Author   : Justin Bennett   
  Date     : 2015-03-10  
  Contact  : http://www.allthingstechie.net
  Revision : v1.01
  Changes  : v1.01 - Changed Write-Error to Throw  
  IP Address or Hostname  
  SNMP Version  
 .PARAMETER community  
  SNMP Community String to read the OID  
  SNMP OID to read  
 .PARAMETER returntype  
  Specify the preferred return type - gauge32, integer, string (Default), timeticks, or todate   
  C:\PS> get-snmp -ip "" -ver "1" -community "public" -oid ""  
  C:\PS> #Get SNMP Contact Info  
  C:\PS> $oid = "", "", ""  
  C:\PS> $oid | % { get-snmp -ip "" -ver "1" -community "public" -oid $_ }  
 function get-snmp {  
      param (  
           [parameter(Mandatory = $true)] [string]$ip,  
           [parameter(Mandatory = $true)] [string]$ver,  
           [parameter(Mandatory = $true)] [string]$community,  
           [parameter(Mandatory = $true)] [string]$oid,  
           [parameter(Mandatory = $false)] [ValidateSet("timeticks","integer","gauge32","string","todate")] [string]$returntype = "string"  
      #location of the snmpget program  
      $snmpdir = ".\"  
      $output = (. $snmpdir\snmpget.exe -v:$ver -c:"$community" -r:$ip -o:$oid 2>&1) | select -skip 3  
      $outputsplit = $output -split "="  
      if ($outputsplit[0].tolower() -ne "oid") {  
           #one more try  
           $output = (. $snmpdir\snmpget.exe -v:$ver -c:"$community" -r:$ip -o:$oid 2>&1) | select -skip 3  
           $outputsplit = $output -split "="  
      switch ($outputsplit[0].tolower()) {  
       "oid" {  
           if($returntype -eq $null) { $returntype = $outputsplit[3] }  
           switch ($returntype.tolower()) {  
                 "timeticks" {  
                     $time = ($outputsplit[5]) -replace "\.", ":" -split ":"  
                     return New-TimeSpan -hour $time[0] -min $time[1]  
                 "integer" {  
                     return [int]$outputsplit[5]  
                 "gauge32" {  
                     return [int]$outputsplit[5]  
                 "string" {  
                     return [string]$outputsplit[5]  
                 "todate" {  
                     return [datetime](get-date (($outputsplit[5]) -replace "`"",""))  
                     return $outputsplit[5]  
       default {  
             throw $output  
Formatted for web with http://codeformatter.blogspot.com/ 

Example Output

Cheers. Even more pieces to the puzzle coming soon.

Monday, March 9, 2015

Home Water System Pt 1 - Give yourself to the Dark Side... of automation.

In the last few years, I've been leveraging PowerShell more and more. So much more, that I'm using it at home to control my fresh water well.

Before I jump head first into the land of crazy, I'm going preface, this will be split up over a few posts. My goal by sharing this isn't to see a future full of PowerShell'd home water systems, but rather just to spark interest in others to break technical norms and explore what you can build. I can see many other uses for simple controlled SNMP devices all over.

First, I'll give you a quick overview of how this is working logically as a system, then I'll show each key piece to the PowerShell puzzle in subsequent posts.

To understand why I've done this and to see the benefit, I have to explain the traditional configuration of a fresh water well partnered with a ground level storage tank.

WARNING: I'm a hack at best when it comes to water - so please don't take what I've done or what I say about water systems as advise. Consult a local professional.

As described by Clean Water Store, my home water well "system uses a large storage tank to store the water before it is pumped again to the house", as depicted below. The portion I choose PowerShell to manage is controlling the start/stop and reading the amp draw for the submersible deep well pump and reading the storage tank's water level with a pressure sensor.


With those two components, I have two .ps1 scripts that run. The first .ps1 is used to fill my storage tank every other day in set 15 min increments for the course of one evening. This is done by 1) reading the water level in the tank to limit the max amount of water 2) the amperage being drawn off the pump when it is running as to not damage the pump should it draw too much amps or begin to suck air. At the end of each fill, a follow-up report is sent with the details of each run. The second .ps1 reports on the tank's water level readings sent every 12 hours. The booster pump still depends on a pressure switch to run and is not yet monitored.

Two main benefits in doing this is: 1) I know how much water is in my water storage tank, and 2) I know as soon as my deep well pump begins to have a problem, so I have time to react before all of my stored water is gone. Additionally, I have the ability to control the timing of how long and when the pump runs beyond the limits of mechanical control timers.

As you can see, PowerShell can be leveraged as your own SCADA system limited by your own imagination.

My system consists of a low power consumption PC running Windows 7 (Dell OptiPlex FX170), an APC Rack PDU AP7940 to control the well pump, and an Arduino UNO R3 with a 3 wire 15 psi water pressure sensor and a Ethernet Shield. The script makes all the calls via SNMP to read the devices and control the power to the well pump.


Building the "water shed" for my pumps
Assembling Pumps and Pipes
Deep pump controller wired to tank's float shut-off and bypass switch 
Pipes and pumps connected


APC Rack PDU Interface
Run Well Pump .ps1 Report Image 
Deep pump wired to C19 plug and bypass switch

Arduino with Ethernet shield used to read water pressure

Pressure sensor
Thoroughly wrapped by black 10 mil pipe wrap

More explanation of this mayhem to be continued...