Project#TwinCAT3 x Cisco Meraki API

At today’s factories, the challenge is to visualize various data because of IOT4.0. Moreover, there are demands for not only visualization but also safety, expandability, easy use, and remote operation.

This time, I applied the activity called Meraki D-1 Grump in Japan, and I borrowed a Meraki switch from Cisco to try . So, in this article, I’ll take the Cisco Meraki MS120-8FP switch as an example. On Cisco Meraki devices, the settings are managed on the Cloud, and APIs can also provide and change the device status from the 3rd Third Party. In addition, it has a webhook function that allows you to be notified in real time when an event occurs.

https://www.cisco.com/c/m/ja_jp/meraki/d1grandprix.html?mkt_tok=MDEwLUtOWi01MDEAAAGDxfVEJiGnLkeiu0cJ7hQVgNQS9kegg8T6MQ2TnwmSpo6fryX03Xo0V0CcA8o_x-ya9AAjV07PXu4ShKhDYX_nU9tsAUz1HU4SBtp5IKeUJXBjB5R0k88#~detail


Configuration

Here is the configuration in this tutorial,the Cisco Meraki MS120-8FP switch is connected to my Home router for cloud connection.

Beckhoff TwinCAT3(SoftwarePLC) is connected to the internet with wireless – and using TF6760 to send the HTTP/REST request to Meraki API to get the information/status of this Cisco Meraki MS120-8FP switch.

A MQTT Broker is configured in the Raspberry Pi and receives the message from the Meraki Webhook system and changes to MQTT format.. TF6701 is used to subscribe to the following Topic and stored as a webhook info history.

Finally,TF1800 is used to display all this information.

Meraki Side

Please reference these tutorials to set up your Meraki device.

Meraki#001_Basic Setup
Meraki#002_Add Network
Meraki#003_Add your License

Please reference this tutorial to enable your API.

Meraki#004_Play with API

Now the basic setup of Cisco Meraki Devices is Finished and API Key is ready.


Raspberry PI Side

Please Reference this Tutorial to setup the MQTT Broker in your Raspberry pi.

MQTT#Broker Setup

Please reference this link to get more information about how to Subscribe/Public the Topic.

MQTT#using Python Library as a Subscriber and Publisher

Please reference this link to set up a Ngrok server and Publish it to Ngrok services.

ngrok#Test your server in Public area

Please reference this link to get more information about how to integrate the Ngrok services to your local server and receive the webhook. 

Meraki#005_Webhook x Raspberrypi

Change Webhook message to MQTT Message

Here is the Sample to start up a Http local server in Python, and receive the message from Cisco Meraki Webhook, then publish it to the MQTT Broker.

import http.server
import socketserver
import json
import paho.mqtt.client as mqtt
import time

def on_connect(client,userdata,flag,rc):
        print(‘connected with result code’+str(rc))

def on_disconnect(client,userdata,rc):
        if rc!=0:
                print(‘something wrong..’)

def on_publish(client,userdata,mid):
        print(‘publish:{0}’.format(mid))

URL=’127.0.0.1′
PORT=1883
BASE=’/Switch1′
FIX_TOPIC_WEBHOOK=’Wehook’
FIX_TOPIC_ALERT=’Alert’
FIX_TOPIC_DATE=’Date’

class MyHandler(http.server.BaseHTTPRequestHandler):
        def do_POST(self):
                self.send_response(200)
                self.end_headers()
                print(self.path)
                content_length = int(self.headers[‘Content-Length’])
                #post_data = self.rfile.read(content_length)
                #post_data_string=post_data.decode()
                #post_data_json=json.loads(post_data_string)
                data_in_json=json.loads(self.rfile.read(content_length).decode())

                #d[‘sentAt’]
            #d[‘deviceName’]
            #d[‘deviceSerial’]
            #d[‘alertType’]
            #d[‘alertTypeId’]
            #d[‘alertLevel’]
            #d[‘occurredAt’]

                client=mqtt.Client()
                client.on_connect=on_connect
                client.on_disconnect=on_disconnect
                client.on_publish=on_publish
                client.connect(URL,PORT,60)
                #client.publish(‘/Switch1/hello’,’hallo’)
                NetworkName=data_in_json[‘networkName’]
                DeviceName=data_in_json[‘deviceName’]

                client.publish(‘/’+NetworkName+’/’+DeviceName+’/’+FIX_TOPIC_WEBHOOK+’/’+FIX_TOPIC_ALERT+’/ID/’,data_in_json[‘alertTypeId’])
                client.publish(‘/’+NetworkName+’/’+DeviceName+’/’+FIX_TOPIC_WEBHOOK+’/’+FIX_TOPIC_ALERT+’/Type/’,data_in_json[‘alertType’])
                client.publish(‘/’+NetworkName+’/’+DeviceName+’/’+FIX_TOPIC_WEBHOOK+’/’+FIX_TOPIC_ALERT+’/Level/’,data_in_json[‘alertLevel’])
                client.publish(‘/’+NetworkName+’/’+DeviceName+’/’+FIX_TOPIC_WEBHOOK+’/’+FIX_TOPIC_ALERT+’/Occurred/’,data_in_json[‘occurredAt’])
                client.disconnect()



        def do_GET(self):
                self.send_response(200,”GET is received.”)
                print(“GET is received”)
                self.end_headers()

with socketserver.TCPServer((“”, 8001), MyHandler) as httpd:
        httpd.serve_forever()

Now a MQTT broker is started up in your raspberry pi and available to receive any message from Cisco Meraki Webhook message>then use mqtt to pulich to message again.

TwinCAT Side

There are no Special things that need to be implemented in Beckhoff Side. TF6701,TF676- and TF1800 are used in this project.

Library

TF6701

TF6701 is used to Connect with a MQTT Broker.Of course this function can be connected to AWS/Google/MS Azure but not just Local Broker.

Beckhoff#Using TwinCAT TF6701 to Subscribe Topics
Beckhoff#Using TwinCAT TF6701 to Publish message inAWS IOT Core

TF6760

TF6760 is used to configure and send the HTTP Request and take care of the JSON Object.

Beckhoff#Using TwinCAT TF6701 to Publish message inAWS IOT Core

TF1800

TF1800 is used for display.


Points when using Meraki API

FIXHeader

A X-Cisco-Meraki-API-Key Header must be added into your Request while sending to the Meraki server.FB_IotHttpHeaderFieldMap is used to insert the Fix header in your http request.

{
    “X-Cisco-Meraki-API-Key”: <Meraki_API_Key>
}

A 401 Unauthorized is returned while there is no A X-Cisco-Meraki-API-Key Header/Wrong Header in your Request.

{ “errors”: [ “Invalid API key” ] }

https://developer.cisco.com/meraki/api-v1/#!authorization

Response 302

Reference to the API Documents, the base URL of the API is shown as below.

https://api.meraki.com/api/v1

Although TF6760 of TwinCAT3 can send the request to the Meraki server without any problem, the 302 status code is returned.Reference to the TF6760 Manual, redirect is not supported – So we need to enter the 100% correct URL as the parameter.

Here is the API URL of the Cisco Meraki API.

n424.meraki.com


Array of Json Object

Json Object is returned for the Meraki API – we would use GetArrayValueByIdx or GetArraySize method to get the element inside the json object.


Function Block

I will only explain the function block that is used in this tutorial.

Please reference this link to get more details about Beckhoff TwinCAT3 TF6760;

Beckhoff#Using TwinCAT3 TF6760 HTTPS/REST to send the Get Request

FB_IotHttpHeaderFieldMap

We can use this function block to add the fix header into your http request and add a header by calling the AddField() method.

Finally we only need to pass this function block directly to the HTTP Function Block.

VAR_OUTPUT

VariablesData TypeDescription
bError BOOL True=Error
hrErrorCode HRESULT ADS Return Code

Method – AddField

Add a header.

VAR_INPUT
VariablesData TypeDescription
sField STRING The header that you would like to add in the HTTP Request
sValue STRING The header value
bAppendValue BOOL False=appendTrue=clear the current header and add again
Return Value
VariablesData TypeDescription
AddField BOOL True=Method is executed

Implementation – Meraki API Side

GET Organizations

Get the list of your organizations.

https://developer.cisco.com/meraki/api-v1/#!get-organizations

Request

GET /organizations

curl https://api.meraki.com/api/v1/organizations \
  -L -H ‘X-Cisco-Meraki-API-Key: {MERAKI-API-KEY}’

Response

JSON Array is returned.


Flow

Check if the Json Object is valid or not > Check if the JSON Data Process Function Block is valid or not> Get the Json Object from Json Array > Check if the Member exists in the object or not> convert it to TwinCAT variable.

DUT_Meraki_API_organizations

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_organizations :
STRUCT
Id :STRING;
Url :STRING;
Name :STRING;

Licensing :STRING;
API :Bool;
Cloud :STRING;
END_STRUCT
END_TYPE


FC_Meraki_Organizations_JsonData_Encoder

A Function to convert the json Object to String and return as a Cisco meraki Structure variable.

VAR
FUNCTION FC_Meraki_Organizations_JsonData_Encoder : DUT_Meraki_API_organizations
VAR_INPUT
jsonDoc    :SJsonValue;
FBJson     :REFERENCE TO FB_JsonDomParser;

END_VAR
VAR
udi32JsonObjectLength: UDINT;
jsonDocBase:SJsonValue;
jsonval: SJsonValue;
bValid:BOOL;
END_VAR

PROGRAM
// Reset all the Return to Zero
FC_Meraki_Organizations_JsonData_Encoder.Id:=”;
FC_Meraki_Organizations_JsonData_Encoder.Cloud:=”;
FC_Meraki_Organizations_JsonData_Encoder.API:=FALSE;
FC_Meraki_Organizations_JsonData_Encoder.Licensing:=”;
FC_Meraki_Organizations_JsonData_Encoder.Name:=”;
FC_Meraki_Organizations_JsonData_Encoder.Url:=”;

//Check the Reference is Valid or not
bValid:=__ISVALIDREF(FBJson);

IF jsonDoc <> 0 and bValid THEN

//Get the Basic Object, Json arary is returned from Meraki Api, and we only use index0 in here
udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc);
jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=udi32JsonObjectLength-1);

//Get id
IF fbJson.HasMember(v:=jsonDocBase,member:=’id’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’id’);
FC_Meraki_Organizations_JsonData_Encoder.Id:=fbJson.GetString(v:=jsonval);
END_IF

//Get Url
IF fbJson.HasMember(v:=jsonDocBase,member:=’url’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’url’);
FC_Meraki_Organizations_JsonData_Encoder.Url:=fbJson.GetString(v:=jsonval);
END_IF

//Get Name
IF fbJson.HasMember(v:=jsonDocBase,member:=’name’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’name’);
FC_Meraki_Organizations_JsonData_Encoder.name:=fbJson.GetString(v:=jsonval);
END_IF

//Get Licensing
IF fbJson.HasMember(v:=jsonDocBase,member:=’licensing’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’licensing’);
jsonval:=fbJson.FindMember(v:=jsonval,member:=’model’);
FC_Meraki_Organizations_JsonData_Encoder.Licensing:=fbJson.GetString(v:=jsonval);
END_IF

//Get Api
IF fbJson.HasMember(v:=jsonDocBase,member:=’api’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’api’);
jsonval:=fbJson.FindMember(v:=jsonval,member:=’enabled’);
FC_Meraki_Organizations_JsonData_Encoder.API:=fbJson.GetBool(v:=jsonval);
END_IF

//Get Cloud
IF fbJson.HasMember(v:=jsonDocBase,member:=’cloud’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’cloud’);
jsonval:=fbJson.FindMember(v:=jsonval,member:=’region’);
jsonval:=fbJson.FindMember(v:=jsonval,member:=’name’);
FC_Meraki_Organizations_JsonData_Encoder.Cloud:=fbJson.GetString(v:=jsonval);
END_IF
END_IF

GET Networks

現在Network情報が返答されます。

https://developer.cisco.com/meraki/api-v1/#!get-network

Request

/networks/{networkId}

curl -L –request GET \
–url https://api.meraki.com/api/v1/networks/{networkId} \
–header ‘Content-Type: application/json’ \
–header ‘Accept: application/json’ \
–header ‘X-Cisco-Meraki-API-Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’

Response

Network ObjectがReturnされます。

Flow

Check if the Json Object is valid or not > Check if the JSON Data Process Function Block is valid or not> Get the Json Object from Json Array > Check if the Member exists in the object or not> convert it to TwinCAT variable.

DUT_Meraki_API_Networks

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_Networks :
STRUCT

Id :STRING;
OrganizationId :STRING;
NetworkType :STRING;
Name :STRING;
TimeZone :STRING;


END_STRUCT
END_TYPE

FC_Meraki_Networks_JsonData_Encoder 

A Function to convert the json Object to String and return as a Cisco meraki Structure variable.

VAR
FUNCTION FC_Meraki_Networks_JsonData_Encoder : DUT_Meraki_API_Networks
VAR_INPUT
jsonDoc :SJsonValue;
FBJson :REFERENCE TO FB_JsonDomParser;
END_VAR
VAR
udi32JsonObjectLength: UDINT;
jsonDocBase:SJsonValue;
jsonval: SJsonValue;
bValid:BOOL;
END_VAR
PROGRAM
// Reset all the Return to Zero
FC_Meraki_Networks_JsonData_Encoder.Id:=”;
FC_Meraki_Networks_JsonData_Encoder.Name:=”;
FC_Meraki_Networks_JsonData_Encoder.NetworkType:=”;
FC_Meraki_Networks_JsonData_Encoder.OrganizationId:=”;
FC_Meraki_Networks_JsonData_Encoder.TimeZone:=”;

//Check the Reference is Valid or not
bValid:=__ISVALIDREF(FBJson);


IF jsonDoc <> 0 AND bValid THEN

//Get the Basic Object, Json arary is returned from Meraki Api, and we only use index0 in here
udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc);
jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=udi32JsonObjectLength-1);

//Get id
IF fbJson.HasMember(v:=jsonDocBase,member:=’id’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’id’);
FC_Meraki_Networks_JsonData_Encoder.Id:=fbJson.GetString(v:=jsonval);
END_IF

IF fbJson.HasMember(v:=jsonDocBase,member:=’organizationId’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’organizationId’);
FC_Meraki_Networks_JsonData_Encoder.OrganizationId:=fbJson.GetString(v:=jsonval);
END_IF

IF fbJson.HasMember(v:=jsonDocBase,member:=’name’) THEN
FC_Meraki_Networks_JsonData_Encoder.Name:=fbJson.GetString(v:=jsonval);
END_IF

IF fbJson.HasMember(v:=jsonDocBase,member:=’timeZone’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’timeZone’);
FC_Meraki_Networks_JsonData_Encoder.TimeZone:=fbJson.GetString(v:=jsonval);
END_IF
END_IF

Get Device

A API Request to get the information of the device.

https://developer.cisco.com/meraki/api-v1/#!get-device

Request

GET /devices/{serial}

curl -L –request GET \
–url https://api.meraki.com/api/v1/devices/{serial} \
–header ‘Content-Type: application/json’ \
–header ‘Accept: application/json’ \
–header ‘X-Cisco-Meraki-API-Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’

Response

The device information is returned if the serial number is matched.

Flow

Check if the Json Object is valid or not > Check if the JSON Data Process Function Block is valid or not> Get the Json Object from Json Array > Check if the Member exists in the object or not> convert it to TwinCAT variable.

DUT_Meraki_API_Devices

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_Devices :
STRUCT
Name :STRING;
Lat :LREAL;
Lng :LREAL;
Serial :STRING;
Mac :STRING;
Address :STRING;
Notes :STRING;
Lanip :STRING;
Firmware :STRING;
END_STRUCT
END_TYPE

FC_Meraki_Devices_SetRequestURL

Because the URL is being complex, A separate function is used to generate the Request.

VAR
FUNCTION FC_Meraki_Devices_SetRequestURL : String
VAR_INPUT
id:STRING;
END_VAR
VAR
sId:STRING;
END_VAR
PROGRAM
sId:=”;
sId:=CONCAT(‘/api/v1/networks/’,id);
sId:=CONCAT(sId,’/devices’);

FC_Meraki_Devices_SetRequestURL:=sId;

FC_Meraki_Networks_JsonData_Encoder

A Function to convert the json Object to String and return as a Cisco meraki Structure variable.

VAR
FUNCTION FC_Meraki_Networks_JsonData_Encoder : DUT_Meraki_API_Networks
VAR_INPUT
jsonDoc :SJsonValue;
FBJson :REFERENCE TO FB_JsonDomParser;
END_VAR
VAR
udi32JsonObjectLength: UDINT;
jsonDocBase:SJsonValue;
jsonval: SJsonValue;
bValid:BOOL;
END_VAR
PROGRAM
// Reset all the Return to Zero
FC_Meraki_Networks_JsonData_Encoder.Id:=”;
FC_Meraki_Networks_JsonData_Encoder.Name:=”;
FC_Meraki_Networks_JsonData_Encoder.NetworkType:=”;
FC_Meraki_Networks_JsonData_Encoder.OrganizationId:=”;
FC_Meraki_Networks_JsonData_Encoder.TimeZone:=”;

//Check the Reference is Valid or not
bValid:=__ISVALIDREF(FBJson);


IF jsonDoc <> 0 AND bValid THEN

//Get the Basic Object, Json arary is returned from Meraki Api, and we only use index0 in here
udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc);
jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=udi32JsonObjectLength-1);

//Get id
IF fbJson.HasMember(v:=jsonDocBase,member:=’id’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’id’);
FC_Meraki_Networks_JsonData_Encoder.Id:=fbJson.GetString(v:=jsonval);
END_IF

IF fbJson.HasMember(v:=jsonDocBase,member:=’organizationId’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’organizationId’);
FC_Meraki_Networks_JsonData_Encoder.OrganizationId:=fbJson.GetString(v:=jsonval);
END_IF

IF fbJson.HasMember(v:=jsonDocBase,member:=’name’) THEN
FC_Meraki_Networks_JsonData_Encoder.Name:=fbJson.GetString(v:=jsonval);
END_IF

IF fbJson.HasMember(v:=jsonDocBase,member:=’timeZone’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’timeZone’);
FC_Meraki_Networks_JsonData_Encoder.TimeZone:=fbJson.GetString(v:=jsonval);
END_IF
END_IF


Get Device Switch Ports

Get All the port information from the device.

Request

GET /devices/{serial}/switch/ports/statuses

curl -L –request GET \
–url https://api.meraki.com/api/v1/devices/{serial}/switch/ports/statuses \
–header ‘Content-Type: application/json’ \
–header ‘Accept: application/json’ \
–header ‘X-Cisco-Meraki-API-Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’

Response

A JSON Array that includes all the port information is returned.

Flow

Check if the Json Object is valid or not > Check if the JSON Data Process Function Block is valid or not> Get the Json Object from Json Array > Check if the Member exists in the object or not> convert it to TwinCAT variable.

DUT_Meraki_API_Ports

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_Ports :
STRUCT
PortID :STRING;
Name :STRING;
Enabled :BOOL;
PoeEnabled :BOOL;
PortType :STRING;
Vlan :LINT;
VoiceVlan :LINT;
IsolationEnabled     :BOOL;
LinkNegotiation :STRING;
Udid :STRING;
END_STRUCT
END_TYPE

FC_Meraki_Ports_SetRequestUrl 

Because the URL is being complex, A separate function is used to generate the Request.

VAR
FUNCTION FC_Meraki_Ports_SetRequestUrl : String
VAR_INPUT
id:STRING;
END_VAR
VAR
sId:STRING;
END_VAR
PROGRAM
sId:=”;
sId:=CONCAT(‘/api/v1/devices/’,id);
sId:=CONCAT(sId,’/switch/ports’);

FC_Meraki_Ports_SetRequestUrl:=sId;

FC_Meraki_Ports_JsonData_Enconder 

A Function to convert the json Object to String and return as a Cisco meraki Structure variable.

VAR
FUNCTION FC_Meraki_Ports_JsonData_Enconder : ARRAY[1..10] OF DUT_Meraki_API_Ports
VAR_INPUT
jsonDoc:SJsonValue;
FBJson :REFERENCE TO FB_JsonDomParser;
END_VAR
VAR
udi32JsonObjectLength: UDINT;
jsonDocBase:SJsonValue;
jsonval: SJsonValue;
bValid:BOOL;
i:INT;
END_VAR
PROGRAM
// Reset all the Return to Zero

FOR i := 1 TO 10 DO
FC_Meraki_Ports_JsonData_Enconder[i].Enabled:=FALSE;
FC_Meraki_Ports_JsonData_Enconder[i].IsolationEnabled:=FALSE;
FC_Meraki_Ports_JsonData_Enconder[i].LinkNegotiation:=”;
FC_Meraki_Ports_JsonData_Enconder[i].Name:=”;
FC_Meraki_Ports_JsonData_Enconder[i].PoeEnabled:=FALSE;
FC_Meraki_Ports_JsonData_Enconder[i].PortID:=”;
FC_Meraki_Ports_JsonData_Enconder[i].PortType:=”;
FC_Meraki_Ports_JsonData_Enconder[i].Udid:=”;
FC_Meraki_Ports_JsonData_Enconder[i].Vlan:=0;
FC_Meraki_Ports_JsonData_Enconder[i].VoiceVlan:=0;
END_FOR

//Check the Reference is Valid or not
bValid:=__ISVALIDREF(FBJson);


IF jsonDoc <> 0 AND bValid THEN
//Get the Basic Object, Json arary is returned from Meraki Api
udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc);

FOR i:=1 TO UDINT_TO_INT(udi32JsonObjectLength) DO
jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=i-1);
//Get id
IF fbJson.HasMember(v:=jsonDocBase,member:=’name’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’name’);
FC_Meraki_Ports_JsonData_Enconder[i].Name:=fbJson.GetString(v:=jsonval);
END_IF
IF fbJson.HasMember(v:=jsonDocBase,member:=’portId’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’portId’);
FC_Meraki_Ports_JsonData_Enconder[i].PortID:=fbJson.GetString(v:=jsonval);
END_IF
IF fbJson.HasMember(v:=jsonDocBase,member:=’enabled’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’enabled’);
FC_Meraki_Ports_JsonData_Enconder[i].Enabled:=fbJson.GetBool(v:=jsonval);
END_IF
IF fbJson.HasMember(v:=jsonDocBase,member:=’poeEnabled’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’poeEnabled’);
FC_Meraki_Ports_JsonData_Enconder[i].PoeEnabled:=fbJson.GetBool(v:=jsonval);
END_IF
IF fbJson.HasMember(v:=jsonDocBase,member:=’type’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’type’);
FC_Meraki_Ports_JsonData_Enconder[i].PortType:=fbJson.GetString(v:=jsonval);
END_IF
IF fbJson.HasMember(v:=jsonDocBase,member:=’vlan’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’vlan’);
FC_Meraki_Ports_JsonData_Enconder[i].Vlan:=fbJson.GetInt64(v:=jsonval);
END_IF
IF fbJson.HasMember(v:=jsonDocBase,member:=’voiceVlan’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’voiceVlan’);
FC_Meraki_Ports_JsonData_Enconder[i].VoiceVlan:=fbJson.GetInt64(v:=jsonval);
END_IF
IF fbJson.HasMember(v:=jsonDocBase,member:=’isolationEnabled’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’isolationEnabled’);
FC_Meraki_Ports_JsonData_Enconder[i].IsolationEnabled:=fbJson.GetBool(v:=jsonval);
END_IF
IF fbJson.HasMember(v:=jsonDocBase,member:=’linkNegotiationCapabilities’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’linkNegotiationCapabilities’);
FC_Meraki_Ports_JsonData_Enconder[i].LinkNegotiation:=fbJson.GetString(v:=jsonval);
END_IF

IF fbJson.HasMember(v:=jsonDocBase,member:=’udld’) THEN
jsonval:=fbJson.FindMember(v:=jsonDocBase,’udld’);
FC_Meraki_Ports_JsonData_Enconder[i].Udid:=fbJson.GetString(v:=jsonval);
END_IF
END_FOR



END_IF

Get Device Switch Ports Statuses

Get the Monitor status for All Ports in a device,

https://developer.cisco.com/meraki/api-v1/#!get-device-switch-ports-statuses

Request

GET /devices/{serial}/switch/ports/statuses

curl -L –request GET \
–url https://api.meraki.com/api/v1/devices/{serial}/switch/ports/statuses \
–header ‘Content-Type: application/json’ \
–header ‘Accept: application/json’ \
–header ‘X-Cisco-Meraki-API-Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’

Response

Json ArrayがReturnします。

A JSON Array is returned.

Flow

DUT_Meraki_API_MonitorPortStatus_configOverrides

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_MonitorPortStatus_configOverrides :
STRUCT
configType :STRING;
allowedVlans :STRING;
vlan :LINT;

END_STRUCT
END_TYPE

DUT_Meraki_API_MonitorPortStatus_lldp

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_MonitorPortStatus_lldp :
STRUCT
systemName :STRING;
systemDescription :STRING;
portId :STRING;
portDescription :STRING;
chassisId :STRING;
managementVlan :LINT;
portVlan :LINT;
managementAddress :LINT;
systemCapabilities :STRING;
END_STRUCT
END_TYPE

DUT_Meraki_API_MonitorPortStatus_secureConnect 

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_MonitorPortStatus_secureConnect :
STRUCT
enabled :BOOL;
active :BOOL;
authenticationStatus :STRING;
configOverrides :DUT_Meraki_API_MonitorPortStatus_configOverrides;
END_STRUCT
END_TYPE

DUT_Meraki_API_MonitorPortStatus_usageInKb

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_MonitorPortStatus_usageInKb :
STRUCT
Total :LINT;
Sent :LINT;
Recv :LINT;
END_STRUCT
END_TYPE

DUT_Meraki_API_MonitorPortStatus_usageInKbs 

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_MonitorPortStatus_usageInKbs :
STRUCT
Total :LREAL;
Sent :LREAL;
Recv :LREAL;
END_STRUCT
END_TYPE

DUT_Meraki_API_MonitorPortStatus 

A DUT that is referenced by the Cisco Meraki API object.

TYPE DUT_Meraki_API_MonitorPortStatus :
STRUCT
portId :STRING;
enabled :BOOL;
status :STRING;
isUplink :BOOL;
errors :STRING;
warnings :STRING;
Speed :STRING;
duplex :STRING;
usageInKb :DUT_Meraki_API_MonitorPortStatus_usageInKb;
cdp :DUT_Meraki_API_MonitorPortStatus_cdp;
lldp :DUT_Meraki_API_MonitorPortStatus_lldp;
clientCount :LINT;
powerUsageInWh :LREAL;
trafficInKbps :DUT_Meraki_API_MonitorPortStatus_usageInKbs;
secureConnect :DUT_Meraki_API_MonitorPortStatus_secureConnect;


END_STRUCT
END_TYPE

FC_Meraki_MonitorStatus_SetRequestURL

Because the URL is being complex, A separate function is used to generate the Request.

VAR
FUNCTION FC_Meraki_MonitorStatus_SetRequestURL : String
VAR_INPUT
id:STRING;
END_VAR
VAR
sId:STRING;
END_VAR
PROGRAM
// https://developer.cisco.com/meraki/api-v1/#!get-device-switch-ports-statuses
// /api/v1/devices/{serial}/switch/ports/statuses
sId:=”;
sId:=CONCAT(‘/api/v1/devices/’,id);
sId:=CONCAT(sId,’/switch/ports/statuses’);

FC_Meraki_MonitorStatus_SetRequestURL:=sId;

FC_Meraki_MonitorStatus_JsonData_Enconder 

A Function to convert the json Object to String and return as a Cisco meraki Structure variable.

VAR
FUNCTION FC_Meraki_MonitorStatus_JsonData_Enconder : DUT_Meraki_API_MonitorPortStatus
VAR_INPUT
jsonDoc :SJsonValue;
FBJson :REFERENCE TO FB_JsonDomParser;
END_VAR
VAR
udi32JsonObjectLength: UDINT;

jsonval: SJsonValue;
jsonvalBuffer:SJsonValue;
bValid:BOOL;
END_VAR
PROGRAM
// Reset all the Return to Zero
FC_Meraki_MonitorStatus_JsonData_Enconder.cdp.address:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.clientCount:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.duplex:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.enabled:=FALSE;
FC_Meraki_MonitorStatus_JsonData_Enconder.errors:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.isUplink:=FALSE;
FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.chassisId:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.managementAddress:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.managementVlan:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.portId:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.portVlan:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.systemCapabilities:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.systemDescription:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.lldp.systemName:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.portId:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.powerUsageInWh:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.active:=FALSE;
FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.authenticationStatus:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.configOverrides.allowedVlans:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.configOverrides.configType:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.secureConnect.configOverrides.vlan:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.Speed:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.status:=”;
FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Recv:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Sent:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Total:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Recv:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Sent:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Total:=0;
FC_Meraki_MonitorStatus_JsonData_Enconder.warnings:=”;

//Check the Reference is Valid or not
bValid:=__ISVALIDREF(FBJson);

IF jsonDoc <> 0 AND bValid THEN
//usageInKb
IF fbJson.HasMember(v:=jsonDoc,member:=’usageInKb’) THEN
jsonval:=fbJson.FindMember(v:=jsonDoc,’usageInKb’);
//total
IF fbJson.HasMember(v:=jsonval,member:=’total’) THEN
jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’total’);
FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Total:=fbJson.GetInt64(v:=jsonvalBuffer);
END_IF
//sent
IF fbJson.HasMember(v:=jsonval,member:=’sent’) THEN
jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’sent’);
FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.Sent:=fbJson.GetInt64(v:=jsonvalBuffer);
END_IF
//recv
IF fbJson.HasMember(v:=jsonval,member:=’recv’) THEN
jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’recv’);
FC_Meraki_MonitorStatus_JsonData_Enconder.usageInKb.recv:=fbJson.GetInt64(v:=jsonvalBuffer);
END_IF
END_IF
//trafficInKbps
IF fbJson.HasMember(v:=jsonDoc,member:=’trafficInKbps’) THEN
jsonval:=fbJson.FindMember(v:=jsonDoc,’trafficInKbps’);
//total
IF fbJson.HasMember(v:=jsonval,member:=’total’) THEN
jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’total’);
FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Total:=fbJson.GetDouble(v:=jsonvalBuffer);
END_IF
//sent
IF fbJson.HasMember(v:=jsonval,member:=’sent’) THEN
jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’sent’);
FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.Sent:=fbJson.GetDouble(v:=jsonvalBuffer);
END_IF
//recv
IF fbJson.HasMember(v:=jsonval,member:=’recv’) THEN
jsonvalBuffer:=fbJson.FindMember(v:=jsonval,’recv’);
FC_Meraki_MonitorStatus_JsonData_Enconder.trafficInKbps.recv:=fbJson.GetDouble(v:=jsonvalBuffer);
END_IF
END_IF

//portId
IF fbJson.HasMember(v:=jsonDoc,member:=’portId’) THEN
jsonval:=fbJson.FindMember(v:=jsonDoc,’portId’);
FC_Meraki_MonitorStatus_JsonData_Enconder.portId:=fbJson.GetString(v:=jsonval);
END_IF
//enabled
IF fbJson.HasMember(v:=jsonDoc,member:=’enabled’) THEN
jsonval:=fbJson.FindMember(v:=jsonDoc,’enabled’);
FC_Meraki_MonitorStatus_JsonData_Enconder.enabled:=fbJson.GetBool(v:=jsonval);
END_IF
//status
IF fbJson.HasMember(v:=jsonDoc,member:=’status’) THEN
jsonval:=fbJson.FindMember(v:=jsonDoc,’status’);
FC_Meraki_MonitorStatus_JsonData_Enconder.status:=fbJson.GetString(v:=jsonval);
END_IF
//isUplink
IF fbJson.HasMember(v:=jsonDoc,member:=’isUplink’) THEN
jsonval:=fbJson.FindMember(v:=jsonDoc,’isUplink’);
FC_Meraki_MonitorStatus_JsonData_Enconder.isUplink:=fbJson.GetBool(v:=jsonval);
END_IF
//errors
IF fbJson.HasMember(v:=jsonDoc,member:=’errors’) THEN
jsonval:=fbJson.FindMember(v:=jsonDoc,’errors’);
FC_Meraki_MonitorStatus_JsonData_Enconder.errors:=fbJson.GetString(v:=jsonval);
END_IF
//warnings
IF fbJson.HasMember(v:=jsonDoc,member:=’warnings’) THEN
jsonval:=fbJson.FindMember(v:=jsonDoc,’warnings’);
FC_Meraki_MonitorStatus_JsonData_Enconder.warnings:=fbJson.GetString(v:=jsonval);
END_IF
END_IF

pMerakiAPI

Flow

VAR

PROGRAM pMerakiAPI
VAR

fbClient        :FB_IotHttpClient;
fbRequest       :FB_IotHttpRequest;
fbheader :FB_IotHttpHeaderFieldMap;

//
bSend:BOOL;
Step:INT;
sConect:STRING(9999);

StringBuffer:STRING(512);

//json
jsonDoc:SJsonValue;

fbJson              : FB_JsonDomParser;
fbJson2             : FB_JsonDomParser;
bgetfromJson:BOOL;
sId:STRING;


//Meraki
Meraki :DUT_Meraki;


si2: UDINT;
udi32JsonObjectLength: UDINT;
jsonDocBase:SJsonValue;
i:INT;
udi32JsonSubObjectLength: UDINT;
jsonvalBuffer:SJsonValue;
END_VAR
VAR CONSTANT
cStep_Init :INT:=0;
cStep_Meraki_Get_Organizations :INT:=10;
cStep_Meraki_Process_Oranizations_Data :INT:=15;
cStep_Meraki_GetNetworks :INT:=20;
cStep_Meraki_Process_Networks_Data :INT:=21;
cStep_Meraki_GetNetworkDevices :INT:=30;
cStep_Meraki_Process_NetworkDevices_Data     :INT:=31;

cStep_Meraki_Get_Ports :INT:=40;
cStep_Meraki_Process_Ports_Data :INT:=41;
cStep_Meraki_Get_MonitorStatus :INT:=50;
cStep_Meraki_Process_MonitorStatus_Data :INT:=51;
END_VAR

PROGRAM

CASE Step OF
cStep_Init:
(*
Configurate the Fix Header of Cisco API
The Meraki Dashboard API requires a header parameter of X-Cisco-Meraki-API-Key to provide authorization for each request.
Basic Foramt
curl https://api.meraki.com/api/v1/organizations \
  -H ‘X-Cisco-Meraki-API-Key: {MERAKI-API-KEY}’
*)
fbClient.sHostName:=’n424.meraki.com’;
fbClient.nHostPort:=443;
fbClient.stTLS.bNoServerCertCheck:=TRUE;
fbheader.AddField(
sField:=’X-Cisco-Meraki-API-Key’
,sValue:=’YourAPIKEY’
,bAppendValue:=FALSE
);
bSend:=TRUE;
Step:=cStep_Meraki_Get_Organizations;

cStep_Meraki_Get_Organizations:
(*
List the organizations that the user has privileges on
/organizations
https://developer.cisco.com/meraki/api-v1/#!get-organizations
*)
IF bSend THEN
fbRequest.sContentType:=’application/json’;
IF fbRequest.SendRequest(
sUri:=’/api/v1/organizations’
,fbClient:=fbClient
,eRequestType:=ETcIotHttpRequestType.HTTP_GET
,pContent:=0
,nContentSize:=0
,fbHeader:=fbheader
) THEN
Step:=cStep_Meraki_Process_Oranizations_Data;
END_IF;
bSend:=FALSE;
END_IF
cStep_Meraki_Process_Oranizations_Data:
IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN
fbRequest.GetContent(
pContent:=ADR(sConect)
,nContentSize:=SIZEOF(sConect)
,TRUE
);
Meraki.Organizations:=
FC_Meraki_Organizations_JsonData_Encoder(
jsonDoc:=fbRequest.GetJsonDomContent(fbJson)
,FBJson:=fbJson2
);
Step:=cStep_Meraki_GetNetworks;
END_IF
cStep_Meraki_GetNetworks:
sId:=”;
sId:=CONCAT(‘/api/v1/organizations/’,Meraki.Organizations.Id);
sId:=CONCAT(sId,’/networks’);
IF fbRequest.SendRequest(
sUri:=sId
,fbClient:=fbClient
,eRequestType:=ETcIotHttpRequestType.HTTP_GET
,pContent:=0
,nContentSize:=0
,fbHeader:=fbheader
) THEN
Step:=cStep_Meraki_Process_Networks_Data;
END_IF;
cStep_Meraki_Process_Networks_Data:
IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN
fbRequest.GetContent(
pContent:=ADR(sConect)
,nContentSize:=SIZEOF(sConect)
,TRUE
);
Meraki.Networks:=
FC_Meraki_Networks_JsonData_Encoder(jsonDoc:=fbRequest.GetJsonDomContent(fbJson),FBJson:=fbJson2);
Step:=cStep_Meraki_GetNetworkDevices;
END_IF
cStep_Meraki_GetNetworkDevices:
sId:=”;
sId:=CONCAT(‘/api/v1/networks/’,Meraki.Networks.Id);
sId:=CONCAT(sId,’/devices’);
sId:=FC_Meraki_Devices_SetRequestURL(id:=Meraki.Networks.Id);
IF fbRequest.SendRequest(
sUri:=sId
,fbClient:=fbClient
,eRequestType:=ETcIotHttpRequestType.HTTP_GET
,pContent:=0
,nContentSize:=0
,fbHeader:=fbheader
) THEN
Step:=cStep_Meraki_Process_NetworkDevices_Data;
END_IF;
cStep_Meraki_Process_NetworkDevices_Data:
IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN
fbRequest.GetContent(
pContent:=ADR(sConect)
,nContentSize:=SIZEOF(sConect)
,TRUE
);
Meraki.Devices:=FC_Meraki_Devices_JsonData_Enconder(
jsonDoc:=fbRequest.GetJsonDomContent(fbJson)
,FBJson:=fbJson2
);
Step:=cStep_Meraki_Get_Ports;
END_IF
cStep_Meraki_Get_Ports:
// /api/v1/devices/{serial}/switch/ports

sId:=FC_Meraki_Ports_SetRequestUrl(id:=Meraki.Devices.Serial);
IF fbRequest.SendRequest(
sUri:=sId
,fbClient:=fbClient
,eRequestType:=ETcIotHttpRequestType.HTTP_GET
,pContent:=0
,nContentSize:=0
,fbHeader:=fbheader
) THEN
Step:=cStep_Meraki_Process_Ports_Data;
END_IF;
cStep_Meraki_Process_Ports_Data:
IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN
fbRequest.GetContent(
pContent:=ADR(sConect)
,nContentSize:=SIZEOF(sConect)
,TRUE
);
Meraki.Ports:=FC_Meraki_Ports_JsonData_Enconder(
jsonDoc:=fbRequest.GetJsonDomContent(fbJson)
,FBJson:=fbJson2
);
Step:=cStep_Meraki_Get_MonitorStatus;
END_IF
cStep_Meraki_Get_MonitorStatus:
// https://developer.cisco.com/meraki/api-v1/#!get-device-switch-ports-statuses
// /api/v1/devices/{serial}/switch/ports/statuses

sId:=FC_Meraki_MonitorStatus_SetRequestURL(id:=Meraki.Devices.Serial);
IF fbRequest.SendRequest(
sUri:=sId
,fbClient:=fbClient
,eRequestType:=ETcIotHttpRequestType.HTTP_GET
,pContent:=0
,nContentSize:=0
,fbHeader:=fbheader
) THEN
Step:=cStep_Meraki_Process_MonitorStatus_Data;
END_IF;
cStep_Meraki_Process_MonitorStatus_Data:
IF NOT fbRequest.bBusy AND NOT fbRequest.bError THEN
fbRequest.GetContent(
pContent:=ADR(sConect)
,nContentSize:=SIZEOF(sConect)
,TRUE
);
jsonDoc:=fbRequest.GetJsonDomContent(fbJson);
//
IF jsonDoc <>0 THEN
udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc);
jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=udi32JsonObjectLength-1);

//Get the Basic Object, Json arary is returned from Meraki Api, and we only use index0 in here
udi32JsonObjectLength:=fbJson.GetArraySize(v:=jsonDoc);

FOR i:=1 TO UDINT_TO_INT(udi32JsonObjectLength) DO
jsonDocBase:=fbJson.GetArrayValueByIdx(v:=jsonDoc,idx:=i-1);
Meraki.MonitorStatus[i]:=FC_Meraki_MonitorStatus_JsonData_Enconder(jsonDocBase,fbJson2);

END_FOR
Step:=cStep_Meraki_Get_Ports;
END_IF
END_IF
END_CASE




fbClient.Execute();

Implementation – Meraki Webhook

Flow


pGetWebhockFromPi

VAR

PROGRAM pGetWebhockFromPi
VAR
MQTTClient:FB_IotMqttClient;
payload:STRING(255);

fbMessageQueue : FB_IotMqttMessageQueue;
fbMessage: FB_IotMqttMessage;
sTopic :STRING;

bScbscribed:BOOL;
sTopicReceived:STRING(255);

WebhookBuffer :DUT_Meraki_Webhook;
Record :ARRAY[0..99]OF DUT_Meraki_Webhook;
bShift :BOOL;
i :INT;
currentIndex :INT;
END_VAR

PROGRAM

//MQTT Client Setup
//Parameters
MQTTClient.sHostName:=’192.168.3.10′;
MQTTClient.nKeepAlive:=60;
MQTTClient.ipMessageQueue:=fbMessageQueue;
//Execute() must always Run
MQTTClient.Execute(bConnect:=TRUE);
//Subscribe Topic
sTopic:=’/MyPlant1/#’;
IF MQTTClient.bConnected THEN
IF NOT bScbscribed THEN
MQTTClient.Subscribe(
sTopic:=sTopic
,eQoS:=0
);
END_IF
END_IF

//Check Message From Broker
IF fbMessageQueue.nQueuedMessages > 0 THEN
IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN
//Get the Topic
fbMessage.GetTopic(pTopic:=ADR(sTopicReceived), nTopicSize:=SIZEOF(sTopicReceived));
//Compare
IF fbMessage.CompareTopic(sTopic:=’/MyPlant1/MYSWTICH1/Wehook/Alert/ID/’) THEN
fbMessage.GetPayload(pPayload:=ADR(WebhookBuffer.ID), nPayloadSize:=SIZEOF(WebhookBuffer.ID), bSetNullTermination:=FALSE);
ELSIF
fbMessage.CompareTopic(sTopic:=’/MyPlant1/MYSWTICH1/Wehook/Alert/Type/’) THEN
fbMessage.GetPayload(pPayload:=ADR(WebhookBuffer.MessageType), nPayloadSize:=SIZEOF(WebhookBuffer.MessageType), bSetNullTermination:=FALSE);
ELSIF
fbMessage.CompareTopic(sTopic:=’/MyPlant1/MYSWTICH1/Wehook/Alert/Level/’) THEN
fbMessage.GetPayload(pPayload:=ADR(WebhookBuffer.Level), nPayloadSize:=SIZEOF(WebhookBuffer.Level), bSetNullTermination:=FALSE);
ELSIF
fbMessage.CompareTopic(sTopic:=’/MyPlant1/MYSWTICH1/Wehook/Alert/Occurred/’) THEN
fbMessage.GetPayload(pPayload:=ADR(WebhookBuffer.Occurred), nPayloadSize:=SIZEOF(WebhookBuffer.Occurred), bSetNullTermination:=FALSE);
END_IF
END_IF
END_IF

IF WebhookBuffer.ID <> ” 
AND WebhookBuffer.Level <> ”
AND WebhookBuffer.MessageType <>”
AND WebhookBuffer.Occurred <> ”
THEN

Record[0].ID:=WebhookBuffer.ID;
Record[0].Level:=WebhookBuffer.Level;
Record[0].MessageType:=WebhookBuffer.MessageType;
Record[0].Occurred:=WebhookBuffer.Occurred;

MEMCPY(
destAddr:=ADR(Record[1])
,srcAddr:=ADR(Record[0])
,n:=SIZEOF(Record[0])*99
);
WebhookBuffer.ID:=”;
WebhookBuffer.Level:=”;
WebhookBuffer.MessageType:=”;
WebhookBuffer.Occurred:=”;

Record[0].ID:=”;
Record[0].Level:=”;
Record[0].MessageType:=”;
Record[0].Occurred:=”;
END_IF

DUT_Meraki 

TYPE DUT_Meraki :
STRUCT
Organizations     :DUT_Meraki_API_organizations;
Networks     :DUT_Meraki_API_Networks;
Devices     :DUT_Meraki_API_Devices;
Ports   :ARRAY[1..10]OF DUT_Meraki_API_Ports;
MonitorStatus     :ARRAY[1..10]OF DUT_Meraki_API_MonitorPortStatus;
END_STRUCT
END_TYPE

Implement – Main

PROGRAM

pMerakiAPI();
pGetWebhockFromPi();

Visitilization

Result

https://youtu.be/lhmunTFkV2I

Sample Code

https://github.com/soup01Threes/TwinCAT3/blob/main/TwinCAT%20HTTPReqWithCiscoMerakiAPI.zip

Summary

I believe that more and more devices will be supported in the future because of this IOT4.0.

So the knowledge of network/Web would be very important.

Configuring a Cisco Meraki is very easy because of theirUser-friendly Dashboard, Because in my image, you can only set up the Cisco device by using Terminal and Commands…

Meraki is a really very Interesting Device.

The Next point is I am very surprised by the power of TwinCAT TF6760. This TwinCAT TF6760 can extract a standard Web API payload with a complex JSON array object(Cisco Meraki API in this case).

You may ask – Why can I replace the twincat with Raspberry and python only?

My answer is – Exactly you can, but in the Factory Automation Field, we need to confirm these devices that using in the production line are;

  • Same
  • maintenanable
  • Easy to modify
  • Non-Stop
  • Easy to replace

So, the Window-base system TwinCAT3 Runtime is my choice.

And also, the classic network device in FA area,

  • You may Need a specific software
  • You can only access the device locally
  • You can only access less information from the device
  • Not all devices can access this device to get the information
  • Cloud is not support

In the future, we may see this type of cloud-base network device in your next site.

Finally, You may have seen this picture before and this is my mindset before I start this project.

  • Can I play with this Cisco Meraki Switch?
  • Can I make the connection between TwinCAT and this switch?
  • Can I understand the API document?
  • Can TF6760 access decode the JSON Object?

But After I started the project, these types of anxiety are becoming too enjoyable!The more that I search, the more interesting things I have never seen.

Sure My demo was not as good as the other participants, but it was a great learning experience. I want to use it again.

Footer_Basic

Please Support some devices for my blog

Amazon Gift List

Find ME

Twitter:@3threes2
Email:soup01threes*gmail.com (* to @)
YoutubeChannel:https://www.youtube.com/channel/UCQ3CHGAIXZAbeOC_9mjQiWQ

シェアする

  • このエントリーをはてなブックマークに追加

フォローする