Monday, November 30, 2015

Modbus Serial Communication between PLC and Unity3D Game Engine




This article about how to connection PLC to Unity3D Game Engine using Modbus Serial Communication.
in Unity3D Game Engine using C# (C Sharp) Script for Modbus Serial Communication and as a Modbus Master with serial communication setting 9600-even-8-one.
in PLC (Programmable Logic Controller) using PLC Ladder Programming for Modbus Serial Communication and as a Modbus Slave with Slave Address  1 (one).

In Case Study:
How to Fan speed control with 3D model of Unity3D using PLC input, and
how to turning ON/OFF in PLC outputs using simple 3D model of Unity3D.

PLC and Unity3D Game Engine using Modbus

Let's watch: my video demonstration on YouTube

PLC Connect to Unity3D Game Engine Using Modbus Serial Communication



Hardware List for PLC and Unity3D Game Engine

  1. PLC Modbus, I use Siemens S7-200 PLC
  2. RS232 PLC Cable, I use Simatic S7-200 RS232 PPI Multi-Master Cable
  3. USB to Serial RS232, I use BAFO USB to RS232 Converter
  4. Optional : Push Button connect to PLC Input


Hardware Connections of PLC and Unity3D Game Engine


Hardware Connections  of PLC and Unity3D Game Engine


Game Engine Software

Unity3D Free Download : https://unity3d.com/get-unity/download


Unity3D Application for PC Standalone

Unity3D modbus application for Target Platform : Windows and Architecture : x86_64
Free Download : click here


Project File for Unity3D Game Engine and PLC:

  1. Unity3D Project File : click here
  2. https://drive.google.com/file/d/0Bwwi33sDvP5DSnJ0LV9NY0RSc2M/view?usp=sharing
  3. PLC Ladder Programming for Siemens S7-200 : click here


Input/output on Siemens PLC

  1. Input I0.0 for Fan Speed is -400
  2. Input I0.1 for Fan Speed is Increments -300
  3. Input I0.2 for Fan Speed is Increments 300
  4. Input I0.3 for Fan Speed is 400
  5. Input I0.4 for Fan Speed is 0 or Fan Stop
  6. If cylinder model clicked in Unity3D then all PLC output QB0 is ON



Unity3D Scripts

Unity3D c#

serialport.cs
using UnityEngine;
using System.Collections;
using System.IO.Ports;

public class serialport : MonoBehaviour {

    private SerialPort sp;
    private const int BUFFER_SIZE = 128;
    private byte[] frame16 = new byte[BUFFER_SIZE];
    private byte[] frame03 = new byte[BUFFER_SIZE];
    private byte state=0;

    public string PortName ="COM1";
    private float Scan_Time = 0.03f; //0.03 Seconds
    public byte Connection_Status = 0;

    //FUNCTION 03
    public long F03_requests = 0;
    public long F03_successful_requests = 0;
    public long F03_failed_requests = 0;
    public long F03_exception_errors =0;

    private byte F03_Slave_Address = 1;
    private byte F03_Function = 3;
    private int F03_Starting_Address = 0;
    private int F03_NumberofRegisters = 2;
    private byte F03_Byte_Count;
    public int[] F03_values;


    //FUNCTION 16
    public long F16_requests = 0;
    public long F16_successful_requests = 0;
    public long F16_failed_requests = 0;
    public long F16_exception_errors =0;

    private byte F16_Slave_Address = 1;
    private byte F16_Function = 16;
    private int F16_Starting_Address = 0;
    private int F16_NumberofRegisters = 1;
    private byte F16_Byte_Count;
    public int[] F16_values;

    void OnGUI() {
        GUI.Label(new Rect(10, Screen.height-35, Screen.width, 35),"MODBUS SLAVE=1, SERIAL PORT=" + PortName + ", BAUD=9600, PARITY=EVEN, DATA=8BIT, STOPBITS=ONE");
    }

    void Start () {
        Application.runInBackground = true;

        F16_values = new int[F16_NumberofRegisters];
        F03_values = new int[F03_NumberofRegisters];

        bool Existing = false;
        foreach(string str in SerialPort.GetPortNames())
        {
            if(str==PortName)Existing = true;
        }

        if (Existing) {
            //Debug.Log(string.Format("Existing COM port: {0}", PortName));
        }else{
            //Debug.LogError(string.Format("Not Existing COM port: {0}", PortName));
            Connection_Status = 5;
            return;
        }

        sp = new SerialPort( PortName
                            , 9600
                            , Parity.Even
                            , 8
                            , StopBits.One);

        OpenConnection();

        state=0;

        InvokeRepeating("Modbus_Update", 1.0f, Scan_Time);

    }
    

    public void OpenConnection() {
        if (sp != null) 
        {
            if (sp.IsOpen) 
            {
                sp.Close();
                print(PortName + " Closing port, because it was already open!");
                Connection_Status = 1;
            }
            else 
            {
                sp.Open(); 
                sp.ReadTimeout = 1;  
                sp.WriteTimeout = 10;  
                print(PortName + " Port Opened!");
                sp.BaseStream.Flush();
                Connection_Status = 2;
            }
        }
        else 
        {
            if (sp.IsOpen)
            {
                print(PortName + " Port is already open");
                Connection_Status = 3;
            }
            else 
            {
                print(PortName + " Port == null");
                Connection_Status = 4;
            }
        }
    }

    void OnApplicationQuit() {
        if (sp != null) {
            if (sp.IsOpen)sp.Close ();    
        }
    }


    void end() {
        if (sp != null) {
            if (sp.IsOpen)sp.Close ();    
        }
    }


    int calculateCRC(byte[] crcframe,int bufferSize) 
    {
        int temp, temp2, flag;
        temp = 0xFFFF;
        for (int i = 0; i < bufferSize; i++)
        {
            temp = temp ^ crcframe[i];
            for (int j = 1; j <= 8; j++)
            {
                flag = temp & 0x0001;
                temp >>= 1;
                if (flag==1)
                    temp ^= 0xA001;
            }
        }

        temp2 = temp >> 8;
        temp = (temp << 8) | temp2;
        temp &= 0xFFFF;
        return temp; 
    }


    void Func16(){
        //Debug.Log("FUNCTION 16");
        F16_requests++;

        F16_NumberofRegisters = (int)(F16_values.Length);
        if (F16_NumberofRegisters < 1)F16_NumberofRegisters = 1;
        if (F16_Starting_Address < 0)F16_Starting_Address = 0;

        F16_Byte_Count = (byte)(F16_NumberofRegisters * 2);


        int frameSize = 9 + (2 * F16_NumberofRegisters);


        frame16[0] = F16_Slave_Address;
        frame16[1] = F16_Function;
        frame16[2] = (byte)(F16_Starting_Address >> 8);
        frame16[3] = (byte)F16_Starting_Address;
        frame16[4] = (byte)(F16_NumberofRegisters >> 8);
        frame16[5] = (byte)F16_NumberofRegisters;
        frame16[6] = F16_Byte_Count;
        for (int i = 0; i < F16_NumberofRegisters; i++)
        {
            frame16[7 + 2 * i] = (byte)(F16_values[i] >> 8);
            frame16[8 + 2 * i] = (byte)(F16_values[i]);
        }


        int crc16 = calculateCRC(frame16, frameSize - 2);
        byte crcHi, crcLo;
        crcHi = (byte)((crc16 >> 8)& 0xFF);
        crcLo = (byte)(crc16 & 0xFF);
        frame16[frameSize - 2] = crcHi;
        frame16[frameSize - 1] = crcLo;

        try {            
            sp.Write(frame16, 0, frameSize);
        }        
        catch (System.Exception ex) {            
            if(ex.Message.Contains("The device does not recognize the command")){
                //Debug.LogError(string.Format("Not Existing COM port: {0}", PortName));
                //Application.Quit();
                Connection_Status = 5;
                return;
            }
            //Debug.LogWarning("Func16 " + ex.Message); 
            F16_exception_errors++;
            Connection_Status = 6;
            return;
        }

        sp.BaseStream.Flush();

    }


    void RecvFunc16(){
        int Size = 0;
        byte tempB = 0;
        try {
            tempB =  (byte) sp.ReadByte();
            while (tempB == F16_Slave_Address) {
                while (Size<8) {
                    if(Size<BUFFER_SIZE)frame16[Size] = (byte) (tempB);
                    if(Size==7)break;
                    tempB = (byte) sp.ReadByte();
                    Size++;
                    Connection_Status = 100;
                }
                break;
            }
        }
        catch (System.Exception ex) {
            if(ex.Message.Contains("The device does not recognize the command")){
                //Debug.LogError(string.Format("Not Existing COM port: {0}", PortName));
                //Application.Quit();
                Connection_Status = 5;
                return;
            }
            //Debug.LogWarning("RecvFunc16 " + ex.Message);
            F16_exception_errors++;
            Connection_Status = 6;
            return;
        }

        
        if(Size==7){
            if(frame16[0]==F16_Slave_Address && frame16[1]==F16_Function){
                int crc16 = calculateCRC(frame16, Size - 1);
                int received_crc = ((frame16[Size - 1] << 8) | frame16[Size]); 
                if(crc16==received_crc){
                    F16_successful_requests++;
                    Connection_Status = 101;
                    return;
                }
            }
        }

        F16_failed_requests++;
    }


    void Func03(){
        //Debug.Log("FUNCTION 03");
        F03_requests++;

        int frameSize = 8;
        F03_NumberofRegisters = (int) (F03_values.Length);
        if (F03_NumberofRegisters < 1)F03_NumberofRegisters = 1;
        if (F03_Starting_Address < 0)F03_Starting_Address = 0;
        F03_Byte_Count = (byte)(F03_NumberofRegisters * 2);


        frame03[0] = F03_Slave_Address;
        frame03[1] = F03_Function;
        frame03[2] = (byte)(F03_Starting_Address >> 8);
        frame03[3] = (byte)F03_Starting_Address;
        frame03[4] = (byte)(F03_NumberofRegisters >> 8);
        frame03[5] = (byte)F03_NumberofRegisters;
        
        
        int crc16 = calculateCRC(frame03, frameSize - 2);
        byte crcHi, crcLo;
        crcHi = (byte)((crc16 >> 8)& 0xFF);
        crcLo = (byte)(crc16 & 0xFF);
        frame03[frameSize - 2] = crcHi;
        frame03[frameSize - 1] = crcLo;
        
        try {             
            sp.Write(frame03, 0, frameSize);
        }        
        catch (System.Exception ex)    {    
            if(ex.Message.Contains("The device does not recognize the command")){
                //Debug.LogError(string.Format("Not Existing COM port: {0}", PortName));
                //Application.Quit();
                Connection_Status = 5;
                return;
            }
            //Debug.LogWarning("Func03 " + ex); 

            F03_exception_errors++;
            Connection_Status = 6;
            return;
        }
        
        sp.BaseStream.Flush();    
        
    }


    void RecvFunc03(){
        int Size = 0;
        byte tempB = 0;
        int maxrev = 5 + F03_Byte_Count;
        try {
            tempB =  (byte) sp.ReadByte();
            while (tempB == F03_Slave_Address) {
                while (Size<maxrev) {
                    frame03[Size] = (byte) (tempB);
                    if(Size==(maxrev-1))break;
                    tempB = (byte) sp.ReadByte();
                    Size++;
                    Connection_Status = 100;
                }
                break;
            }            
        }
        catch (System.Exception ex) {
            if(ex.Message.Contains("The device does not recognize the command")){
                //Debug.LogError(string.Format("Not Existing COM port: {0}", PortName));
                //Application.Quit();
                Connection_Status = 5;
                return;
            }
            //Debug.LogWarning("RecvFunc03 " + ex.Message);
            F03_exception_errors++;
            Connection_Status = 6;
            return;
        }

        
        if(Size==(maxrev-1)){
            if(frame03[0]==F03_Slave_Address && frame03[1]==F03_Function){
                int crc16 = calculateCRC(frame03, Size - 1);
                int received_crc = ((frame03[Size - 1] << 8) | frame03[Size]); 
                if(crc16==received_crc && frame03[2]==F03_Byte_Count){
                    int index = 3;
                    for (int i = 0; i < F03_NumberofRegisters; i++)
                    {
                        F03_values[i] = (int)((frame03[index] << 8) | frame03[index + 1]); 
                        index += 2;
                        Connection_Status = 102;
                    }

                    F03_successful_requests++;
                    return;
                }
            }
        }

        F03_failed_requests++;
    }



    void Modbus_Update() 
    {
        switch (state)
        {
        case 0:
            Func16();
            state=1;
            break;

        case 1:
            RecvFunc16();
            state=2;
            break;

        case 2:
            Func03();
            state=3;
            break;
        case 3:
            RecvFunc03();
            state=0;
            break;
        }
    }


}

Unity3D c#

moveobject.cs
using UnityEngine;
using System.Collections;

public class moveobject : MonoBehaviour {
    public GameObject SerialportObject;

    public serialport script;

    public int fan_speed = 0;

    private short modbus_read;
    private short modbus_write;

    private bool conn = false;

    void OnGUI() {
        GUI.Label(new Rect((Screen.width/2)-75, 10, 150, 25),"program-plc.blogspot.com");
        switch (script.Connection_Status)
        {
        case 0:
            GUI.Label(new Rect(10, 40, Screen.width, 25),"Connection Status : Common Error");
            break;            
        case 1:
            GUI.Label(new Rect(10, 40,Screen.width, 25),"Connection Status : Closing port, because it was already open!");
            break;            
        case 2:
            GUI.Label(new Rect(10, 40,Screen.width, 25),"Connection Status : Port Opened!");
            break;
        case 3:
            GUI.Label(new Rect(10, 40,Screen.width, 25),"Connection Status : Port is already open");
            break;
        case 4:
            GUI.Label(new Rect(10, 40,Screen.width, 25),"Connection Status : Port == null");
            break;
        case 5:
            GUI.Label(new Rect(10, 40,Screen.width, 25),"Connection Status : Not Existing " + script.PortName.ToString() + " , Please ReStart");
            break;
        case 6:
            GUI.Label(new Rect(10, 40,Screen.width, 25),"Connection Status : Exception Errors");
            break;
        case 100:
            GUI.Label(new Rect(10, 40, Screen.width, 25),"Connection Status : OK");
            conn = true;
            break;
        case 101:
            GUI.Label(new Rect(10, 40, Screen.width, 25),"Connection Status : OK");
            conn = true;
            break;
        case 102:
            GUI.Label(new Rect(10, 40, Screen.width, 25),"Connection Status : OK");
            conn = true;
            break;
        }

        GUI.Label(new Rect(10, 70, 250, 25),"Modbus 40001 <= Push Button : " + modbus_write.ToString());
        GUI.Label(new Rect(10, 100, 250, 25),"Modbus 40002 => Fan Speed : " + modbus_read.ToString());
    }


    void Start () {
        script = SerialportObject.GetComponent<serialport>();
    }
    
    void Update () {
        if (Input.GetMouseButtonDown(0) && conn) 
        {
            GameObject PB;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if(Physics.Raycast(ray,out hit))
            {
                if(hit.collider.gameObject.name== "Push_Button"){
                    PB = GameObject.Find(hit.collider.gameObject.name);
                    PB.transform.localPosition = new Vector3(0, 0.4f,0);
                    if(script.F16_values.Length>0){
                        script.F16_values[0]=1;
                    }
                }
            }
        }    

        if (Input.GetMouseButtonUp(0)){
            GameObject PB;
            PB = GameObject.Find("Push_Button");
            PB.transform.localPosition = new Vector3(0, 1.2f,0);
            if(script.F16_values.Length>0){
                script.F16_values[0]=0;
            }
        }

        if (script.F03_values.Length > 1){
            modbus_write = (short) script.F03_values [0];
            modbus_read = (short) script.F03_values [1];
            fan_speed = modbus_read;
        }

        if (fan_speed !=0){
            GameObject FAN;
            FAN = GameObject.Find("Fan");
            FAN.transform.Rotate(0,fan_speed*Time.deltaTime,0);
        }

    }



}


Labels:





Newer Post Older Post Home

You may also like these ebook:

Get Free PLC eBook directly sent to your email,
and email subscription to program-plc.blogspot.com




We hate SPAM. Your information is never sold or shared with anyone.

Your Email Will Be 100% Secured !

Your email is stored safely on Google FeedBurner