Arduino PlC Interface

Hi all,

I am looking for some help regarding a software interface for Arduino.

I want a simple software that will display an anaolg input from the Arduino and for a user to input a value.

I also want the software to perform a programme once a button on a form is selected.

I have a 4-20ma signal that I have converted to 1-5v and the Arduino is reading those values.

If anyone can help suggesting similar softwares that would be great.

Thank you

Serial monitor or any other terminal program?

Please provide more detail. Is that a pc programme or an Arduino programme?

Probably more towards a terminal programme

Either really but ideally a pc programme. I have looked and played with Microsoft visual studio but I have not had much success, possibly due to my lack of knowledge when it comes to C# programming.
I’m possibly looking for a scada software but if anyone know of any better software that can be used then I am open minded

Below is a simple Arduino code that well send an analog reading every two seconds and toggle the built in led when an ‘x’ is received.

const uint32_t interval = 2000;
const byte ledPin = LED_BUILTIN;

void setup()
{
  Serial.begin(57600);
  digitalWrite(ledPin, LOW);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  static uint32_t nextTime;
  if (millis() >= nextTime)
  {
    nextTime += interval;

    int x = analogRead(A0);
    Serial.print("<");
    Serial.print(x);
    Serial.print(">");
  }

  if (Serial.available() > 0)
  {
    char ch = Serial.read();
    if (ch == 'x')
    {
      digitalWrite(ledPin, !digitalRead(ledPin));
    }
  }
}

Next create a C# winform application.

  1. Add a menu bar and a status bar to the form.
  2. Add a groupbox to the form, set the text property to Serial Data and rename it from groupBox1 to gbSerialData.
  3. Add a textbox inside the groupbox; rename it from textBox1 to tbSerialIn.
  4. Add a button, rename it from button1 to btnAction; set the text property to Action.
  5. Add a backgroundworker to the form and rename it from backgroundWorker1 to bwSerial.
  6. Click the status bar, in the status bar, click the little dropdown box and add a status label; rename the status label from toolStripStatusLabel1 to slCom.
  7. Add a menu item called &Port to the menu bar and under it an item to open the port (&Open) and an item to close the port (&Close).

Motivation for the renaming: it’s clearer to what the function of an object is and it’s easier to refer to it. E.g. textBox1 does not mean much if you have plenty text boxes, tbSerialIn straight away tells you that it’s the text box for the serial data. I usually go a little futher with the renaming which would include renaming of menu items etc. But this should be enough for the demo code.

To be able to use non-blocking code in the C# application, I’ve choosen to use a backgroundworker for the receiving of serial data.

Below is the code for the mainForm

using System;
using System.ComponentModel;
using System.Windows.Forms;

using System.IO.Ports;

namespace ArduinoSerial_855804_1
{
    public partial class mainForm : Form
    {
        /// <summary>
        /// serial port object
        /// </summary>
        static SerialPort sp = new SerialPort();
        /// <summary>
        /// delegate to be able to update textbox from background worker thread
        /// </summary>
        /// <param name="text"></param>
        private delegate void SafeCallDelegate(string text);

        #region form
        public mainForm()
        {
            InitializeComponent();

            // setup the background worker process for the serial reading
            bwSerial.WorkerSupportsCancellation = true;
            bwSerial.DoWork += new DoWorkEventHandler(bwSerial_DoWork);
            bwSerial.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwSerial_RunWorkerCompleted);
        }

        /// <summary>
        /// when the user closed the form, cancel the background worker
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                if (sp.IsOpen == true)
                {
                    if (bwSerial.WorkerSupportsCancellation == true)
                    {
                        // Cancel the asynchronous operation.
                        bwSerial.CancelAsync();
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion form


        #region menu
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sp.IsOpen == false)
            {
                try
                {
                    sp.PortName = "COM8";
                    sp.BaudRate = 57600;
                    sp.Parity = Parity.None;
                    sp.DataBits = 8;
                    sp.StopBits = StopBits.One;
                    sp.DtrEnable = false;

                    sp.Open();
                    slCom.Text = sp.PortName + " open";

                    if (bwSerial.IsBusy != true)
                    {
                        // Start the asynchronous operation.
                        bwSerial.RunWorkerAsync();
                    }
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                if (sp.IsOpen == true)
                {
                    if (bwSerial.WorkerSupportsCancellation == true)
                    {
                        // Cancel the asynchronous operation.
                        bwSerial.CancelAsync();
                    }

                    //sp.Close();
                    //slCom.Text = sp.PortName + " closed";
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        #endregion menu

        #region serial receive
        /// <summary>
        /// do the serial reading in the background
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bwSerial_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            // run forever
            while (true)
            {
                // if operation canceled (close serial port selected in menu or form closed)
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                // read up to '>'
                string data = sp.ReadTo(">");
                // if the first character wasn't '<'
                if (data[0] != '<')
                {
                    // ignore
                    continue;
                }
                // get the value
                string value = data.Substring(1);
                // update the textbox that displays the analog vaue
                UpdateTextbox(value);
            }
        }

        /// <summary>
        /// run when the background process is completed; this will only happen
        /// when we indicate that we want to close the serial port from the menu
        /// or when we close the form
        /// or on error
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bwSerial_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == true)
            {
                slCom.Text = "Canceled!";
                sp.Close();
                slCom.Text = sp.PortName + " closed";
            }
            else if (e.Error != null)
            {
                slCom.Text = "Error: " + e.Error.Message;
                sp.Close();
                slCom.Text = sp.PortName + " closed";
            }
            else
            {
                //slCom.Text = "Done!";
            }
        }
        #endregion serial receive

        #region buttons
        /// <summary>
        /// initiate action on Arduino
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAction_Click(object sender, EventArgs e)
        {
            if (sp.IsOpen == true)
            {
                sp.Write("x");
            }
        }
        #endregion buttons


        #region helpers
        /// <summary>
        /// update the textbox with the text received from the Arduino
        /// </summary>
        /// <param name="text"></param>
        private void UpdateTextbox(string text)
        {
            if (tbSerialIn.InvokeRequired)
            {
                var d = new SafeCallDelegate(UpdateTextbox);
                tbSerialIn.Invoke(d, new object[] { text });
            }
            else
            {
                tbSerialIn.Text = text;
            }
        }
        #endregion helpers

    }
}

The code uses the Port → Open menu item to open the port and start the background process. It uses the menu item Port → Close to stop the background process and next close the port. The serial port settings are hard-coded, I’m reasonably sure that I have seen examples on the web to make it configurable; note that my port is set COM8.

One problem with background code is that it can not directly ‘communicate’ with the foreground (the UI); hence the use of a so-called delegate.

The code has some hardening (catching exceptions) but probably incomplete.

That’s it, if you have questions, let me know. Full project, written in VS2017, attached.
ArduinoSerial_855804_1.zip (393.8 KB)

Hi, Thank you for your help and ia will give this a go.

I thought i would compile a drawing for you for you to understand what i am trying to achieve

The aim is for the Arduino to stop the motor when a certain toque limit is reached, for example when torque has reached 50 ft lbs.

I want the user to be able to change the torque ranges on the pc to our desired torque.

When we run out external PlC with is own programme, the PLC is sending out a digital output to either tell the motor to go forward or reverse.

I could intercept this signal in the Arduino and let the Arduino send the digital output signal to the motors inverter. When torque has reached, the Arduino will cut off the signal but send another signal to out external PLC for the programme to continue.

I need the Arduino to read the anaolg input from the torque transducer which is a 4-20ma signal which I can convert to 1-5v. From here I need to scale the torque transducer so the 5v highest limit is 368.78 or 250.77 and 1v lowest limit is -367.78 or -250.77, this is depending which torque transducer we use.

I hope this make sense