Sean Miller
Created November 30, 2019 © GPL3+

Avnet Azure Sphere Coke Drum Health Monitoring IoT AI

When Big Data meets Big Oil, Big Things happen to improve worker safety, protect the environment, and grow margins.

AdvancedFull instructions provided20 hours73
Avnet Azure Sphere Coke Drum Health Monitoring IoT AI

Things used in this project

Hardware components

Azure Sphere MT3620 Starter Kit
Avnet Azure Sphere MT3620 Starter Kit
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
TFMini LIDAR Module
×1
LM2596 DC to DC Buck Converter Power Supply
×1
Strain Gauge, Foil 120 Ohm
×1
Resistance Amplifier
×1
Piezo Buzzer
×1

Software apps and online services

Microsoft Visual Studio 2019

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

Project Case Lid

The 3D Printable lid to our Avnet Azure Sphere Project

Project Case Body

The body to the case of our Avnet Azure Sphere Project.

Schematics

Hookup Guide

How to hookup the resistance amplifier, TFMini, and OLED Display.

Code

User Web Application to View the Data Assessed in the Cloud

VB.NET
This is an example end user web page that would present the deep learning happening in the cloud to the customer.
<%@ Page Title="" Language="VB" MasterPageFile="~/cokedrumAI.master" AutoEventWireup="true" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.ADO" %>
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Collections.Generic" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
    <script runat="server">
        Dim CommonRoutines As New Common
        Dim myCokeAI As New CokeAI
        Sub Page_Load(Sender As Object, e As EventArgs)
            drum_report.Text = myCokeAI.GetCurrentHealthReport()
            drum_visual.Text = myCokeAI.GetCurrentDrumConditionImage()
        End Sub
 </script>
    <main role="main">
        <div>
            <div>
                <div>
                    <div>
                    <h1>COKE DRUM HEALTH MONITORING PAGE</h1>
                    </div>
                 </div>
            </div>
            <div>
                <div>
                    <div>
                    <a href="https://coke-drum-health-monitoring.azureiotcentral.com/analytics">View Health Trends</a>
                    </div>
                 </div>
            </div>
            <div>
                <div>
                    <div>
                        <asp:label runat="server" ID="drum_visual" ></asp:label>
                    </div>
                 </div>
            
                <div>
                    <div>
                        <asp:label runat="server" ID="drum_report" ></asp:label>
                    </div>
                 </div>
            </div>
        </div>
    </main>
</asp:Content>

appMainfest.json

JSON
Used to specify which hardware pins and modules will be used by the code
app-manifest.json
{
"SchemaVersion": 1,
"Name": "Coke Drum AI V1.0",
"ComponentId": "xxxxxx",
"EntryPoint": "/bin/app",
"CmdArgs": [ "Coke-Drum-AI" ],
"Capabilities": {
"AllowedConnections": [ "xxxxxxxxxxxxxxxxxxxx", "34.193.179.91", "34.193.12.42", "35.174.96.85", "ec2-52-4-137-27.compute-1.amazonaws.com", "ec2-34-193-12-42.compute-1.amazonaws.com", "ec2-35-174-96-85.compute-1.amazonaws.com", "ec2-52-72-221-205.compute-1.amazonaws.com", "amazonaws.com", "compute-1.amazonaws.com", "52.3.137.27", "52.72.221.205", "iotc-25850c0c-35dd-43de-ab38-4eb5da61a113.azure-devices.net", "darksky.net", "35.174.96.85", "api.darksky.net", "34.193.12.42", "34.193.179.91" ],
"Gpio": [ 0, 1, 4, 5, 8, 9, 10, 12, 13, 34, 35 ],
"Adc": [ "$AVNET_MT3620_SK_ADC_CONTROLLER0" ],
"Uart": [],
"I2cMaster": [ "ISU2" ],
"SpiMaster": [],
"WifiConfig": true,
},
"ApplicationType": "Default"
}

Upgraded InitI2C Routine for TFMini

C/C++
This code allows for the TFMini to be used to detect distance.
int initI2c(void) {   
 // Begin MT3620 I2C init   
...  
// Initialize TFMini  
 uint8_t my_reset_value =  0x06 ;  
 I2C_DeviceAddress my_TFMini = 0x10;  
    
 result = I2CMaster_Write(i2cFd, my_TFMini, &my_reset_value, 1);  
 if (result < 0) {  
  Log_Debug("WARNING: TFMini Soft Reset Fail: errno=%d (%s)\n", errno, strerror(errno));  
  has_TFMini = false;  
 }  
 else {  
  has_TFMini = true;  
  Log_Debug("TFMini Found!\n");  
 }  
   
 ...  
}  
   
float readDistance() {  
 //Routine to output the distance to the console  
 if (!has_TFMini) return -1;  
 uint16_t distance = 0; //distance  
 uint16_t strength = 0; // signal strength  
 uint8_t rangeType = 0; //range scale  
      
 I2C_DeviceAddress myLIDAR = 0x10;  
 uint8_t incoming[7]; //an array of bytes to hold the returned data from the TFMini.  
 uint8_t cmdBuffer[] = { 0x01, 0x02, 7 }; //the bytes to send the request of distance  
   
 int32_t retVal = I2CMaster_WriteThenRead(i2cFd,myLIDAR,&cmdBuffer[0],3,&incoming[0],7);  
   
 if (retVal < 0) {  
  Log_Debug("ERROR: seanWriteI2C: errno=%d (%s)\n", errno, strerror(errno));  
  return -1;  
 }  
 else {  
  Log_Debug("Success:  Read Distance!\n");  
  for (int x = 0; x < 7; x++)  
  {  
   if (x == 0)  
   {  
    //Trigger done  
    if (incoming[x] == 0x00)  
    {  
     Log_Debug("Problem:  Distance data not valid-");//for debugging  
    }  
    else if (incoming[x] == 0x01)  
    {  
     Log_Debug("Data valid:     ");  
    }  
   }  
   else if (x == 2)  
    distance = incoming[x]; //LSB of the distance value "Dist_L"  
   else if (x == 3)  
    distance |= incoming[x] << 8; //MSB of the distance value "Dist_H"  
   else if (x == 4)  
    strength = incoming[x]; //LSB of signal strength value  
   else if (x == 5)  
    strength |= incoming[x] << 8; //MSB of signal strength value  
   else if (x == 6)  
    rangeType = incoming[x]; //range scale  
  }  
  float the_return = distance / (12 * 2.54);  
  Log_Debug("Distance=%f Feet\n",the_return);  
  return the_return;  
 }  
 return -1;  //if we get this far, it failed.  
}  

Weather API Code

C/C++
This code allows the Azure Sphere to communicate with an API such as DarkSky.net
/// <summary>
///     Download a web page over HTTPS protocol using cURL.
/// </summary>
static void PerformWebPageDownload(char the_site[])
{
 CURL *curlHandle = NULL;
 CURLcode res = 0;
 MemoryBlock block = { .data = NULL,.size = 0 };
 char *certificatePath = NULL;
 bool isNetworkingReady = false;
 if ((Networking_IsNetworkingReady(&isNetworkingReady) < 0) || !isNetworkingReady) {
  Log_Debug("\nNot doing download because network is not up.\n");
  goto exitLabel;
 }
 Log_Debug("\n -===- Starting download -===-\n");
 // Init the cURL library.
 if ((res = curl_global_init(CURL_GLOBAL_ALL)) != CURLE_OK) {
  LogCurlError("curl_global_init", res);
  goto exitLabel;
 }
 if ((curlHandle = curl_easy_init()) == NULL) {
  Log_Debug("curl_easy_init() failed\n");
  goto cleanupLabel;
 }
 // Specify URL to download.
 // Important: any change in the domain name must be reflected in the AllowedConnections
 // capability in app_manifest.json.
 if ((res = curl_easy_setopt(curlHandle, CURLOPT_URL, the_site)) != CURLE_OK) {
  LogCurlError("curl_easy_setopt CURLOPT_URL", res);
  goto cleanupLabel;
 }
 // Set output level to verbose.
 if ((res = curl_easy_setopt(curlHandle, CURLOPT_VERBOSE, 1L)) != CURLE_OK) {
  LogCurlError("curl_easy_setopt CURLOPT_VERBOSE", res);
  goto cleanupLabel;
 }
 // Get the full path to the certificate file used to authenticate the HTTPS server identity.
 // The DigiCertGlobalRootCA.pem file is the certificate that is used to verify the
 // server identity.
 certificatePath = Storage_GetAbsolutePathInImagePackage("certs/DigiCertGlobalRootCA.pem");
 if (certificatePath == NULL) {
  Log_Debug("The certificate path could not be resolved: errno=%d (%s)\n", errno,
   strerror(errno));
  goto cleanupLabel;
 }
 // Set the path for the certificate file that cURL uses to validate the server certificate.
 if ((res = curl_easy_setopt(curlHandle, CURLOPT_CAINFO, certificatePath)) != CURLE_OK) {
  LogCurlError("curl_easy_setopt CURLOPT_CAINFO", res);
  goto cleanupLabel;
 }
 
 res = curl_easy_setopt(curlHandle, CURLOPT_SSL_VERIFYPEER, false); //don't verify its certificate since it causes problems
 
 // Let cURL follow any HTTP 3xx redirects.
 // Important: any redirection to different domain names requires that domain name to be added to
 // app_manifest.json.
 if ((res = curl_easy_setopt(curlHandle, CURLOPT_FOLLOWLOCATION, 1L)) != CURLE_OK) {
  LogCurlError("curl_easy_setopt CURLOPT_FOLLOWLOCATION", res);
  goto cleanupLabel;
 }
 // Set up callback for cURL to use when downloading data.
 if ((res = curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, StoreDownloadedDataCallback)) !=
  CURLE_OK) {
  LogCurlError("curl_easy_setopt CURLOPT_FOLLOWLOCATION", res);
  goto cleanupLabel;
 }
 // Set the custom parameter of the callback to the memory block.
 if ((res = curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *)&block)) != CURLE_OK) {
  LogCurlError("curl_easy_setopt CURLOPT_WRITEDATA", res);
  goto cleanupLabel;
 }
 // Specify a user agent.
 if ((res = curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0")) != CURLE_OK) {
  LogCurlError("curl_easy_setopt CURLOPT_USERAGENT", res);
  goto cleanupLabel;
 }
 // Perform the download of the web page.
 if ((res = curl_easy_perform(curlHandle)) != CURLE_OK) {
  LogCurlError("curl_easy_perform", res);
 }
 else {
  Log_Debug("\n -===- Downloaded content (%zu bytes): -===-\n", block.size);
  Log_Debug("%s\n", block.data);
 }
 
 char my_data[50000];
 memset(my_data,'\0', 50000);
 strncpy(my_data, block.data, block.size);
 Log_Debug("My Data: %s\n",my_data);
 JSON_Value *my_json = json_parse_string("{\"name\":\"\"}");
 int the_type = json_value_get_type(my_json);
 if (json_value_get_type(my_json) != JSONArray) {
  Log_Debug("JSON not working!\n");
 }
 JSON_Array *my_array = json_value_get_array(my_json);
 JSON_Object *commit = my_json;
 Log_Debug("The array: %d", json_array_get_count(my_array));
 Log_Debug("Here it is:  %s\n", json_object_get_string(my_json, "currently.summary") );
cleanupLabel:
 // Clean up allocated memory.
 free(block.data);
 free(certificatePath);
 // Clean up sample's cURL resources.
 curl_easy_cleanup(curlHandle);
 // Clean up cURL library's resources.
 curl_global_cleanup();
 Log_Debug("\n -===- End of download -===-\n");
exitLabel:
 return;
}
//end of https stuff

Sean J. Miller's Coke Drum AI Git Hub Repository

See the page for installation instructions for all the code.

Credits

Sean Miller

Sean Miller

2 projects • 4 followers
Thanks to Peter Fenn.

Comments