Skip navigation
All People > Roger Palmen > Roger Palmen's Blog

If you follow the OSIsoft installation guides, SSL certificates for PI Vision, PI Web API etc. require installation or changing through the OSIsoft installer for the Program. However, i find the certificate validation checks to be too restrictive in nearly all cases, so i find myself resorting to manual changes to the certificates. Especially since certificates will have a validity of 1 year max nowadays.

So i thought let's create a detailed blogpost to explain how to change SSL certificates for HTTPS traffic. In most of my cases i only need this for PI Vision, PI Web API and PI Integrator for BA, but this should be fairly generic.


The problem

For many reasons a valid SSL certificate might not be passing the validity checks done by the OSIsoft installer, leaving no option to install or change the certificate using the documented procedure.


Manual Installation

Manual installation of the certificate is an easy way to install or change SSL certificates. The SSL Certificate is bound to a specific TCP Port. The instructions below assume all applications (e.g. PI Vision and PI Web API) use the same port TCP.443 for encrypted traffic.


Step 1: Check current SSL binding

Open a command-line or powershell session and run

netsh http show sslcert

This should show the certificates with a binding to


Step 2: Install the new certificate

First you need the certificate (typically a .pfx or .p12 file) including the private key, and the password.

Typically you can right-click -> Install PFX to install the certificate into the Windows Certificate Store. Key things: 

  1. Store location should be Local Machine
  2. Mark key as exportable

You can have the store selected automatically.

You should now be able to locate the certificate in the Certificate Manager:

  • Open mmc.exe
  • Add/Remove snap-in "Certificates"" choose "Computer Account" for local computer
  • Locate the certificate in Personal:

Double-click to view the details and locate the thumbprint:


Step 3: Remove binding to old certificate

On your command line (see step 1), remove the old certificate:

netsh http delete sslcer ipport=


Step 4: Add binding to new certificate

On your command line (see step 1), add the new certificate:

netsh http add sslcer ipport= certhash=<hash> appid={<id>}

Where <hash> is the thumbprint from step 2 and <id> is the application ID you see in step 1. The application ID does not have a real function, except for referencing the application that is tied to this. As an alternative you can use any valid application ID, like 00000000-0000-0000-0000-000000000000. (8-4-4-4-12 zeros)



Step 5: Remove old certificate

Not really nessecary, but it does not hurt to use Certificate Manager to remove the old certificate. 


You should now be able to see the correct certificate issued to your webbrowser. Check using the lock icon on the URL.



Orginally stemmed from this post: PI Web API SSL Certificate: certificate failed basic validation policy 

In a different context this basically follows the same procedure: 

On a PI Vision display you can control if the toolbar and/or timebar is shown with the URL parameters like HideTimebar. E.g.: https://webserver/PIVision/#/Displays/123?HideTimebar

However, these settings are inherited when another display is opened. So if you have a navigation link to another display where you don't have this set, the timebar is still hidden even if you did not explicitly hid the timebar.


It turns out that these switches allow a true/false boolean to override the inheritance, so if you need a timebar on a display, but navigate there from a display where the timebar is turned off, you need HideTimebar=false


Tested in PI Vision 2017R2. Original (but thus not entirely complete) documentation here:

When parts of the PI system are down for maintenance, it might be a good idea to redirect PI Vision users to a webpage to indicate the system is not available due to maintenance.


Digging around, it turns out this is a stock feature of IIS. How to use:

  1. Create a placeholder HTML page with the filename "app_offline.htm". You could generate one here:
  2. Place the app_offline.htm file in the PI Vision root folder. This is typically located at C:\Program Files\PIPC\PIVision


And presto! No more PI Vision.


When done. just rename the app_offline.htm to e.g. app_offline_DISABLED or something similar, and PI Vision should return to normal operation.

Hi all,


We have spent some time to get aquinted with PI Vision 4 and the new extensibility model. We are all no programmers, so we did spend some time on the learning curve here. But next to getting a head start on PI Vision 4, we also managed to create a symbol to display EventFrame information. The symbol simply shows EventFrames, grouped by category, along a timeline. If you align this with a Trend symbol, it gives you the EventFrame context of the Trend values:




And a short video showing the overlay: PI Vision 4 EventFrames symbol - YouTube


We make use of the amCharts library (the Javascript one, not the Angular one) to take the strain of proper rendering.

Of course, strapped for time as we had lots of project work, team members needing to spend time elsewhere, etc., so there are some items not done yet:

Open ends:

  • there is no service delivering the time of the display, and we did not change the Web API calls to make use of the timestamps provided by the OnChange handler. So timerange is fixed
  • ran out of time to do a search for Elements and Categories, so currently WebID is a configuration item, and no filtering on EF categories is done
  • a bug in  the amCharts datetime formatting of the dates in the balloon, so no formatting there

Unrealized ideas: plenty! Key item we wanted to include is writeback of Acknowledgements, Reasons, etc. Overlay of a trend, etc.


GitHub here: GitHub - RogerPalmen/PIVision4-symbols: 2018 Hackathon submission


Team: Gerald van Berkel and Roger Palmen

... and no better way to drink that coffee than in my personalized PI Geek espresso cup!



Happy holidays everyone!

Roger Palmen

IoT on the cheap

Posted by Roger Palmen Jan 17, 2016

Playing around with some IoT stuff i wanted to understand a few things: how cheap can you build something? what does a industrial / production ready IoT look like? what works for my personal "needs"? I think these are questions best learned by doing and experiencing first hand what works and what not. So i started with one of the easiest: how cheap can you build something? Of course for any example, a PI server is the target platform for the data

Of course this is nothing new. Butch Payne already wrote a nice few blog posts on this: Butch Payne's Blog But maybe this slightly different angle gives some ideas. It is that cheap that you can leave one at every customer you go to... If you link it to your cellphone Wifi, it can send data whenever you are around.

Warning: i''m not a programmer, so forgive me for my clumsy programming...



Let's first define IoT. In this case it's not the platform where you analyze all the data (we have PI for that...), but all that is needed to get data from the field to your PI server. PI Interfaces are too heavy or expensive to run for just a single measurement, and are difficult, if not impossible to operate across the internet. IoT also implies that the devices must connect through the internet to my PI eventually. For the sake of testing this in my own house, or anywhere i go, i wanted to have a Wifi solution. Using our corporate Wifi, my home Wifi, or my cellphone Wifi Hotspot to work where ever i did not have access to the resident WiFi network.


So let's look at the components needed:


  1. Data Hub: where the data is held in Transit. Must live in the cloud of course.
  2. Hardware: The as-cheap-as-possible piece linking reality to the internet.
  3. PI Reader: getting the data from the data Hub and pushing into PI


Data Hub:

As HTTPS / SSL was out of the question (ready why in the hardware section), any industrial-grade hub like Azure EventHubs was out. So i settled for ThingSpeak (Internet Of Things - ThingSpeak ). Create a channel within your account, and for free you can send and receive data through that. When you channel is private, you need read and write API keys to access the channel. So if your device or channel is compromised due to the use of HTTP or because the device is insecure, retire the channel. If the channel is public, anyone can read or write. Lookup mine here: IoT_Test - ThingSpeak




Cheap and Wifi. And of course a programmable microcontroller. So you quickly end up with the ESP8266 module:



They cost about 2 euro each. Yes, it's more expensive to read this again than to buy one. Run on 3v so on two AA batteries for some time.


As these mini boards require some periphals to program, do power conversions 5v -3v etc. i made it easy for myself and spent a whopping 7 euro on an extensive development board: NodeMCU



The microcontroller supports the Lua scripting interpreter, so load that onto the board and get going doing some programming: nodemcu (zeroday) · GitHub

Being to lazy to do wiring, i did not connect any sensors, so i just used a random value.


The major drawback of the hardware is the limited processing capacity. In other words: it does not support HTTPS. The price point does make this a short-term throwaway option anyway, so by the time someone compromised the solution just retire the unit and deploy a new one.

That limited the options for Data Hubs too, mainly ruling out Azure Event Hubs. So now for the key lines of code in the Lua scripting language:


pin_LED = 0
upload_msdelay = 30000 --upload every 30000ms
port = 80
URL = ""
HTTPcall = "/update"
APIkey = "api_key=XXXXXXXXX"
Value1 = "field1="
function led_off()
    gpio.write(pin_LED, gpio.HIGH)
    ledstate = 0

function led_on()
    gpio.write(pin_LED, gpio.LOW)
    ledstate = 1

function led_toggle()
    if ledstate == 0 then
        ledstate = 1
        ledstate = 0

function send_randvalue()
    --Get random value between 0 and 100
    randvalue = math.random(0,100)
    print("Send Value: "..randvalue)
    HTTPstring = HTTPcall.."?"..APIkey.."&"..Value1..randvalue
    print("HTTP: "..HTTPstring)
    --init the connection
    conn = nil
    conn = net.createConnection(net.TCP,0)

    conn:on("receive", function(conn, payloadout)
        if (string.find(payloadout, "Status: 200 OK") ~= nil) then
            print("Posted OK");

    conn:on("connection", function(conn, payloadout)
        conn:send("GET "..HTTPstring
        .. " HTTP/1.1\r\n"
        .. "Host:\r\n"
        .. "Connection: close\r\n"
        .. "Accept: */*\r\n"
        .. "User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n"
        .. "\r\n")

    conn:on("disconnection", function(conn, payloadout)
    --do the connection

function periodic()
--Start loop timer
tmr.alarm(1, upload_msdelay, 1, periodic) --repeating alarm


The microcontroller is single-threaded, so that's why we need timers to run stuff. There is also an init and startup routine that do the connection to the Wifi, but that's all pretty standard stuff for the NodeMCU so i did not include that.



PI Reader:

Nothing special here, just some lines of C# / NET code in a console app to read the Thingspeak API through HTTPS.


Reading the public stream data for field1, last value only, using XML (JSON is another option):


Reading thinkspeak data:

        public static IoT_Data ReadThingSpeak(string strURL)
            HttpWebRequest ThingspeakReq;
            HttpWebResponse ThingspeakResp;
            StreamReader reader;
            XmlDocument xmlDoc;

            IoT_Data results;

            results = new IoT_Data();
            //Create request to fetch latest value
            ThingspeakReq = (HttpWebRequest)WebRequest.Create(strURL);
            ThingspeakResp = (HttpWebResponse)ThingspeakReq.GetResponse();
            //Check response
            if (ThingspeakResp.StatusCode != HttpStatusCode.OK)
                throw new Exception(string.Format("ERROR! In calling ThingSpeak: {0}: {1}", ThingspeakResp.StatusCode, ThingspeakResp.StatusDescription));
                //read the datastream
                reader = new StreamReader(ThingspeakResp.GetResponseStream());
                xmlDoc = new XmlDocument();
                //loop the feeds list
                XmlNodeList feeds = xmlDoc.SelectNodes("/channel/feeds/feed");
                foreach (XmlNode feed in feeds)
                    //Get values from node and writeout
                    results.timestamp = feed["created-at"].InnerText;
                    results.value = feed["field1"].InnerText;
            return results;

(yes, still need to fix the loop....its crappy only retaining the last. Not an issue as the URL only retrieves a single measurement though)


Sending to PI:

        public static void Write2PI(IoT_Data IoT_eventdata, string strAFattribute)
            AFAttribute myAFAttribute;
            AFValue myAFVal;
            AFTime myAFTime;
            //First create a PIValue object
            myAFTime = new AFTime(Convert.ToDateTime(IoT_eventdata.timestamp), CultureInfo.InvariantCulture);
            myAFVal = new AFValue(Convert.ToInt16(IoT_eventdata.value),myAFTime);
            //Find Attribute
            myAFAttribute = AFAttribute.FindAttribute(strAFattribute,null);
            if ( myAFAttribute == null)
                throw new Exception(string.Format("ERROR! Cannot find AFattribute {0}.",strAFattribute));
            } else {
                //Write value




It's possible to build something very cheap, running on batteries, that pushes data to PI. Not very secure but that was not the point. However this is not a sustainable solution if you need to manage deploying tens or more of these units, or if you want data that can be trusted more than just checking your outside pool temperature during winter.

Another thought: why can't i just send a call to a PI service? A PI Connect REST endpoint feeding into my AF? Or an online PI database, no warrantees, but just there to play with? Plenty of players investigating this area but little sight of OSIsoft.