Showing posts with label ADAM4050. Show all posts
Showing posts with label ADAM4050. Show all posts

Thursday, November 20, 2014

Sequence Control Using Arduino

I am considering Arduino as one of the method to making controller programming easier.

Nowadays it seems that everyone is using Arduino, you can even see elementary school kids programming using Arduino.

Accessibility of Arduino is incredible.
Leaving performance out of discussion, I have never seen anything like Arduino, which makes programming sooooo easy, in my lifetime. 

Thus, I started to think "what if I make controller using Arduino...?"

MPU and libraries used in Arduino are good enough to use for controller.
This is very obvious because ATmega328 that are being used for Arduino are already widely used for controller.

However, MPU is too exposed in Input/Output circuit. CANNOT BE USED
Also, it seems that designer has not thought of any power system. CANNOT BE USED

If you think about it in another way, you can use Arduino as controller if you make modification to Input/Output circuit and power system.

Modifying Arduino as whole will be too tiresome so I am suggesting a following experimental changes.

Input/Output Circuit -> Remote IO Module for FA
Power System -> Power Supply for FA

This is the first example.

I have tried Feedback Control and Sequence Control using Arduino.

The controlled system is Drilling Machine.

Drilling Machine

Drilling Machine Structure



Machine is composed of 3 parts.


PRESS

Drilling Machine Press - holds samples that are going to be drilled firmly

DC 24V Motor with reduction gear and worm gear drives metal structure to move up and down with powerful strength.

Top part has a limit switch that decides the stop location. However bottom part does not have limit switch. Location of bottom limit changes depending on the thickness of the sample, thus it is impossible to put limit switch.

Instead of limit switch, we measure the load on the press motor to decide the bottom limit. We need to constantly measure the current in the press motor and stop the motor when the current skyrockets. This step requires lots of site tuning.


ELEVATOR

Drilling Machine Elevator - holds drill motor

This also has a DC 24V Motor with reduction gear and rack and pinion gear drives drill motor to move metal structure up and down.

It has limit switch at both top and bottom.


DRILL

Drilling Machine Drill

On platform, there is big motor with AC 220V. On the axis there is drill bits.
The motor load needs to be monitored all the time, because if the sample does not get drilled easily because it is too firm the drill bits needs to move up and down multiple times and also because if this does not work the drilling needs to be stopped.
To measure the motor current, the motor power line wraps CT twice.



Arduino Interface


For direct connection with the machine I am going to use ADAM-4050 and ADAM-4017, which are both made for industrial purposes. ADAM-4050 has 8 channel Digital Out and 7 channel Digital In and ADAM-4017 has 8 channel Analog In. Both use RS-485 connections.

Below is channel list.

ADAM-4050

DO0: Elevation Motor ON
DO1:                         Direction(+)
DO2:                         Direction(-)
DO3: Press Motor       ON
DO4:                         Direction(+)
DO5:                         Direction(-)
DO6: Drill Motor        ON
DO7: Fan Motor         ON

DI0: Limit Switch Position UP
DI1:                                 MID
DI2:                                 BOTTOM
DI3: Manual Switch           START
DI4:                                 STOP
DI5:
DI6:

Elevator Motor and Press Motor both moves up and down, which means it is controlled both ways thus it needs 3 ch per motor. Drill Motor is one way, since it only needs to start the motor. Fan Motor is 24V DC. It also only needs to be turned on. It sends air to the drill bits cooling it down. Fan Motor is kept ON for 30 seconds even after the drilling.

We use three limit switch: PRESS' midpoint, top, and bottom.

There are two switches for manual control. One for starting and another one for stop.


ADAM-4017

VIN0: Elevation Motor Current
VIN1: Press Motor Current
VIN2: Drill Motor Current
VIN3:
VIN4:
VIN5:
VIN6:
VIN7: 

It reads current flowing in each motor.
Elevation Motor and Press Motor puts shunt resistance on the motor drive and measures the current. For Drill Motor we use CT sensor to read the converted current value. Readings from CT sensor are AC, thus it shows value that goes through full-wave rectifier and integrated using 60Hz LPF. In this case we only need to know the relative value of the load thus we do not measure the RMS value.


Below are connection diagram for ADAM-4050/ADAM-4017 and the machine.
(Detailed circuit diagram for the machine is company's asset thus it is not released publicly; however, if you really need the diagram please contact privately.)

ADAM-4050/ADAM-4017 and the Machine Connection Diagram

OR

ADAM-4050/ADAM-4017 and the Machine Connection Diagram

OR

ADAM-4050/ADAM-4017 and the Machine Connection

ADAM-4050/ADAM-4017 and the Machine Connection


Sequence Control

STANDBY

At standby PRESS and ELEVATOR are all resting at the top. All motors are switched OFF and waits for the START switch to be ON.


PRESS DOWN

START switch is ON. Until one of the stop condition is satisfied run the Press Motor downward.

Stop Conditions:
  1. If the Press Motor Shunt current value has passed 120% of moving average line.
    Moving average line is the calculated average value before the press reaches the sample. When the press starts to push down on the sample the motor current rapidly increases and when it exceeds 120% it should stop.
  2. If the Press Motor Shunt voltage has passed 3V.
    This is FAIL-SAFE. For some reason if there are too much pressure pushing down on the sample with large moving average line the Press should stop.
  3. If it has been over 17 seconds after the press motor has started working.
    This is also FAIL-SAFE. The press should not be working more than 17 seconds.


ELEVATOR DOWN

Now we start to lower the elevator that holds the drill. Turn ON the Drill Motor and Fan Motor. Until one of the stop condition is satisfied run the Elevator Motor downward.

There is only one stop condition:
  1. If the elevator has reached the bottom point.
    Bottom limit switch decides this. The drill bits have successfully penetrated the sample.

For the following cases, we higher the drill for a little bit and lower it again. When it does not drill well it is better to move up and drill again rather than continuously trying to drill. 
  1. If the Drill Motor's current rises more than 7% of the moving average line.
    Raise the drill (raise elevator) until the current falls below the 7% line and then lower the drill (lower elevator) again.
  2. If the Drill Motor has been on for more than 17 seconds.
    Move the drill up until the top line (raise the elevator) and then lower (lower the elevator). This is the same as restarting. Thus set the maximum of this action to 6. If it exceeds 6, we can safely conclude that the sample is impenetrable.

ELEVATOR UP

Drill work has finished whether it succeeded or not. Let's quickly put the elevator back to where it was.

Run the Elevator motor in UP mode until it reaches the top point. Keep the Drill Motor ON. If the Drill Motor has been turned OFF it might get stuck in the sample thus preventing it from being raised.

When it reaches the top point, turn both Elevator and Drill Motor OFF.


PRESS UP

Run the Press Motor in UP mode until it reaches the MID point.
When it reaches MID point, turn the Press Motor OFF. Leave the Fan Motor ON for extra 30 seconds.


In any stages, if the STOP switch is ON, go back to STANDBY mode.



SOURCE CODE

Source code can be viewed in the following site.
https://github.com/michelleseo/Arduino_Web/blob/master/SequenceControl/MicroFactory.ino

Explanation of code will be difficult since there are too much. If necessary I will answer any questions.



The following is the working video.


Monday, May 26, 2014

Input Polling from Arduino - Web/Arduino/Adam

Let's poll continuously from ADAM-4050 from Arduino using RS-485.
If there is an input change from ADAM, we send it to the browser.

We are going to add or modify the following from WSAdam.ino.

omit...
char rx485Ln[8];
char preAdamPollRcv[2] = { 0, 0 };
int rx485ix;
char bitMap[8];
byte wsConnect;
long lastPollTime;

void XsfAdamPollData(void);
void AdamPoll(void);
void AdamReceiver(void);
void hexToBit(char hex, int p);

omit...
void setup() {
 Serial.begin(9600);
 Soft485.begin(9600);
 pinMode(DE485, OUTPUT);
 wsConnect = 0;
 lastPollTime = 0;
 Ethernet.begin(mac, ip);

omit...
void loop() {
 wsServer.listen();
 if((millis() - lastPollTime) >= 500)
  AdamPoll(); // Poll every 0.5 seconds
 AdamReceiver();
}

omit...
void onConnect(WebSocket &socket) {
 Serial.println("OnConnect called");
 wsConnect++;
}

void onDisconnect(WebSocket &socket) {
 Serial.println("onDisconnect called");
 wsConnect = 0;
}

omit...
 //else if(rxLn[1] == 'I') {
  //digitalWrite(DE485, HIGH);
  //Soft485.write("$006");
  //Soft485.write(0x0D);
  //digitalWrite(DE485, LOW);
 //}
 else if(rxLn[1] == 'I')
  XsfAdamPollData();
}

void XsfAdamPollData(void) {
 hexToBit(preAdamPollRcv[0], 0);
 hexToBit(preAdamPollRcv[1], 4);
 wsServer.send(bitMap, 8);
}

void AdamPoll(void) {
 digitalWrite(DE485, HIGH);
 Soft485.write("$006");
 Soft485.write(0x0D);
 digitalWrite(DE485, LOW);

 lastPollTime = millis();
}

omit...
 if(rx485ix == 6) { // !ooii00<cr>
  //hexToBit(rx485Ln[2], 0);
  //hexToBit(rx485Ln[3], 4);
  //wsServer.send(bitMap, 8);

  // send only if previous input and current input is different.
  if((preAdamPollRcv[0] != rx485Ln[2]) || (preAdamPollRcv[1] != rx485Ln[3])) {
   preAdamPollRcv[0] = rx485Ln[2];
   preAdamPollRcv[1] = rx485Ln[3];
   if(wsConnect)
    XsfAdamPollData();
  }
 }

We are going to modify HTML/JS a little bit.
Use the following source when modifying: https://github.com/michelleseo/Arduino_Web/blob/master/WSAdam/WSAdam1_0.html

omit...
//        ws.onopen = function() { status('Connected...'); }
        ws.onopen = function() {
            status('Connected...');
            DI(); // Read Input once when connected.
        }

omit...
    <!--We do not need Update Button anymore.-->
    <!--<button type="button" onclick="DI()">IN Refresh</button>-->

Full codes can be found in following links







Sunday, May 25, 2014

Input Polling from a Browser - Web/Arduino/Adam

When Input Polling from a Browser...


The previous code updated input condition when [IN Refresh] button was clicked. Now we are going to change it so that it updates continuously.

We are going to fix the following parts:

omit...
<script>
    var ws;
    var poll = 0;
    var intervalTimer;
    var InLamp = new Array();
    for (var i = 0; i < 8; i++)
        InLamp[i] = '#DI' + (7 - i) + '_c';

omit...
//    ws.onopen = function() { status('Connected...'); }
    ws.onopen = function() {
        status('Connected...');
        // When WebSocket connects, polling starts.
        DI_pollEnable();
    }

omit...
//    function DI() { ws.send("DI"); }
    
    function DI_poll() {
        ws.send("DI");
        // Polls every 500 ms.
        intervalTimer = setTimeout(function() { DI_poll() }, 500);
    }
    
    function DI_pollEnable() { // Start polling
        if (!poll) {
            poll++;
            DI_poll();
        }
    }
    
    function DI_pollDisable() { // End polling
        clearTimeout(intervalTimer);
        poll = 0;
    }

omit...
    <!--<button type="button" onclick="DI()">IN Refresh</button>-->
    <button type="button" onclick="DI_pollEnable()">Input Refresh ON</button>
    <button type="button" onclick="DI_pollDisable()">OFF</button>
</body>

Full codes can be found in following links





Saturday, May 24, 2014

HTML / JavaScript - Web/Arduino/Adam

Screen Capture of WSAdam1_0.html

Let's simplify the HTML code.

Create
ws = new WebSocket('ws://192.168.219.16:80/');

Method
ws.send("DI"); // sends message
ws.close(); // Not used this time

Event
ws.onopen = function() { status('Connected...'); }
ws.onclose = function() { status('Closed...'); }
ws.onerror = function(evt) { status('Error ' + evt.data); }
ws.onmessage = function(evt) { // function that receives message

Reference: http://www.tutorialspoint.com/html5/html5_websocket.htm


<!DOCTYPE html>
<html>
    <head>
        <title>ADAM-4050 1.0</title>
        <!--It seemed to be a waste to download it everytime, 
        so I included the file.-->
        <script type="text/javascript" src="jquery.js"></script>
        <script>
            var ws;
            var InLamp = new Array();

            for (var i = 1; i < 8; i++)
                InLamp[i] = '#DI' + (7 - i) + '_c';

            // This is the code that browser calls when done
            // loading HTML/JS code.
            $(document).ready(function() {
                var i = 0;
                var html = 'ON <br />';

                // This is the code for making ON buttons in HTML.
                // The same code is repeated.
                for (i = 0; i < 8; i++)
                    html += '<button type="button" onclick="DO_On(' + i + ')">DO ' + i + '</button>';
                $('#OutONbutton').append(html);

                // This is the code for making OFF buttons in
                // HTML.
                html = 'OFF<br/>';
                for (i = 0; i < 8; i++)
                    html += '<button type="button" onclick="DO_Off(' + i + ')">DO ' + i + '</button>';
                $('#OutOFFbutton').append(html);

                // This is the code for drawing Input circles in
                // HTML.
                html = '<table><tr align="center">';
                for (i = 0; i < 7; i++)
                    html += '<td><svg height="50" width="50"><circle id="DI' + i + '_c" cx="25" cy="25" r="20" stroke="black" fill="white"/></svg></td>';
                html += '</tr><tr align="center"><td>DI 0</td> <td>DI 1</td> <td>DI 2</td> <td>DI 3</td> <td>DI 4</td> <td>DI 5</td> <td>DI 6</td></tr></table>';
                $('#InputLamp').append(html);

                // Now connect the WebSocket.
                WebSocketConnect();
            });

            function WebSocketConnect() {
                try {
                    ws = new WebSocket('ws://192.168.219.16:80/');

                    ws.onopen = function() {
                        status('Connected...');
                    }
                    ws.onclose = function() {
                        status('Closed...'); 
                    }
                    ws.onerror = function(evt) {
                        status('Error ' + evt.data); 
                    }

                    ws.onmessage = function(evt) {
                        for (var i = 1; i < 8; i++) {
                            if (evt.data[i] == '0')
                                $(InLamp[i]).css("fill", "white");
                            else
                                $(InLamp[i]).css("fill", "red");
                        }
                    };
                }
                catch (exception) {
                    status('Exception ' + exception);
                }
            }

            function DO_On(chn) { ws.send('DO' + chn + '_ON'); }
            function DO_Off(chn) { ws.send('DO' + chn + '_OFF'); }
            function DI() { ws.send("DI"); }
            function status(str) { $("#status").append(str); }
            
        </script>
    </head>
    <body>
        <p id="status">Connection Status: </p>
        <p id="OutONbutton"></p>
        <p id="OutOFFbutton"></p>
        <p id="InputLamp"></p>
        <button type="button" onclick="DI()">IN Refresh</button>
    </body>
</html>


Full codes can be found in following links









Friday, May 23, 2014

Arduino Code(2) - Web/Arduino/Adam

Let's make a very simple protocol to use for WebSocket.

For Digital Out:
     Turning on: "DOn=ON"
     Turning off: "DOn=OFF"
     n: channel number 0-7

For Digital In:
     If you send "DI" it returns "bbbbbbbb".
     'b' is each input channel. It either becomes '0' or '1'.


#include <SPI.h>
#include <Ethernet.h>
#include <WebSocket.h>
#include <SoftwareSerial.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0x25, 0xC4 };
byte ip[] = { 192, 168, 219, 16 };
WebSocket wsServer;

int DE485 = 2;
SoftwareSerial Soft485(3, 4);

char rx485Ln[8];
int rx485ix;
char bitMap[8];

void AdamReceiver(void);
void hexToBit(char hex, int p);

void setup() {
 Serial.begin(9600); // Arduino Sketch Serial Monitor

 Soft485.begin(9600);
 pinMode(DE485, OUTPUT);

 Ethernet.begin(mac, ip);
 wsServer.registerConnectCallback(&onConnect);
 wsServer.registerDataCallback(&onData);
 wsServer.registerDisconnectCallback(&onDisconnect);
 wsServer.begin();
 delay(100);

 rx485ix = -1;
}

void loop() {
 wsServer.listen();
 AdamReceiver();
}

void onConnect(WebSocket &socket) {
 Serial.println("onConnect called");
}

void onDisconnect(WebSocket &socket) {
 Serial.println("onDisconnect called");
}

void onData(WebSocket &socket, char* rxLn, byte rxSz) {
 // Digital Out
 // It is either "DOn=ON" or "DOn=OFF". n = "0" - "7"
 // It would really be nice if we can parse "DO", "=ON",
 // "=OFF"; however, we are not focusing on this.
 // In "DO" we are only checking for "O" and for "=ON" & 
 // "=OFF", we will only check for "N".
 if(rxLn[1] == 'O') {
  // RS-485 will be in TX mode when DE is high
  digitalWrite(DE485, HIGH);
  // We send to ADAM-4050 "#001n01"<cr> if ON or
  // "#001n00"<cr> if OFF.
  Soft485.write("#001");
  Soft485.write(rxLn[2]); // n = "0" - "7"
  Soft485.write('0');
  if(rxLn[5] == 'N')
   Soft485.write('1');
  else
   Soft485.write('0');
  Soft485.write(0x0D);
  digitalWrite(DE485, LOW); // When finished, DE must be low
 }

 // Digital In
 // We are only looking for "I" in "DI"
 else if(rxLn[1] == 'I') {
  digitalWrite(DE485, HIGH);
  Soft485.write("$006"); // We send "$AA6"<cr> to ADAM-4050
  Soft485.write(0x0D);
  digitalWrite(DE485, LOW);
 }
}

// ADAM-4050 sends "!ooii00"<cr> as reply.
// The message starts with "!" and ends in <cr>, and there are 6
// bytes in between. The input data are the "ii". For more 
// information look at ADAM - Web/Arduino/Adam posting.
void AdamReceiver(void) {
 char rxChr;

 if(Soft485.available()) {
  rxChar = Soft485.read();
  if(rxChr == '!')
   rx485ix = 0; // message starts with "!"
  else if(rx485ix >= 0) {
   if(rxChr == 0x0d) { // message ends with <cr>
    //!ooii00<cr> Between "!" and <cr> there needs to be
    // 6 bytes
    if(rx485ix == 6) {
     hexToBit(rx485Ln[2], 0);
     hexToBit(rx485Ln[3], 4);
     wsServer.send(bitMap, 8);
    }
    rx485ix = -1; // it needs to be sync to "!" again
   }
   else {
    rx485Ln[rx485ix++] = rxChr;
    if(rx485ix > 6)
     // it cannot be greater than 6 bytes.
     // Re-sync to "!".
     rx485ix = -1;
   }
  }
 }
}

// We send "bbbbbbbb" to WebSocket. b is equal to each input channel, and it becomes either '0' or '1'.
void hexToBit(char hex, int p) {
 int i;

 if(hex >= 'A')
  hex = hex - 0x41 + 10;
 else
  hex -= '0';
 hex ^= 0x0f;

 for(i = 0; i < 4; i++) {
  if(hex & 0x08)
   bitMap[p + i] = '1';
  else
   bitMap[p + i] = '0';
  hex <<= 1;
 }
}



The code can be downloaded from:
https://github.com/michelleseo/Arduino_Web/blob/master/WSAdam/WSAdam.ino





Tuesday, May 20, 2014

UNO, Ethernet Shield - Web/Arduino/Adam

UNO

I used UNO, the most basic Arduino model.

The one I purchased is from the following Korean site, and it is not the original version.
http://www.nulsom.com/
You could purchase it from other sites ( a lot of them sell it ).
http://eleparts.co.kr/EPX7R9BJ
http://devicemart.co.kr/goods/view.php?seq=38296

This machine gives off a lot of heat ( enough heat to make me worry about the PCB ).

Arduino UNO from http://www.nulsom.com/





Ethernet Shield


This is an original, I purchased it from plughouse (Korean site).
http://www.plughouse.co.kr/shop/goods/Goods_view.php?G_code=102705472330

Ethernet Shield





UNO + Ethernet Shield + RS-485 Shield

Let's stack all 3.

Arduino UNO + Ethernet Shield + RS-485 Shield





Photo with ADAM!

Arduino UNO + Ethernet Shield + RS-485 Shield, Adam-4050, LED & Switches






Monday, May 19, 2014

ADAM - Web/Arduino/Adam

ADAM-4050

This is the most basic Digital Input / Output ADAM series module. ( also the cheapest* )


Download the manual "ADAM-4000_manual_V20.pdf" from the above link. 
( This manual is for the entire 4000 series modules. ADAM-4050 starts on page 74 )


ADAM has 8 Outputs and 7 Inputs.
I have attached 8 LED and 7 DIP Switches ( BEST for testing ). 

ADAM 4050 module w/ 8 LED & 7 DIP Switch
































Circuit?

Very simple. ( It's in manual pg.75 )
I set INIT* pin to GND because I didn't want to confuse 485 Address. This sets Address = 0, Speed = 9600 bps, and Checksum = disabled. ( manual pg.19 )

Circuit w/ Switch and LED











RS-485 Protocol

For ADAM-4050 command set refer to manual pg.130.
The ones that we actually use are:
  • Digital Data In (pg.241) that begins with '$' and
  • Digital Data Out (pg.243) that begins with '#'


The Confusing Question...

Is this protocol only using visible ASCII ( 0x20 - 0x7f )? or is Binary also being used?
Except for the line termination Cr ( 0x0d ), only Visible ASCII are used.

Digital Data Out

#AABB( data )( cr )
  except for ( cr ), #AABB( data ) is string
  • AA is address for 485
    AA = "00" because we have set INIT* as GND
  • BB is channel address
    If "00", it writes to all 8 channels at the same time.
    ( data ) ranges from "00" to "FF"
    lsb is DO 0, and msb is DO 7.
    ( eg. If you want to turn ON DO 7 and DO 5 and rest OFF, the ( data ) will be "A0" )

    If "10" - "17", it writes to each channel.
    "10" will turn ON DO 0, ... , "17" will turn ON DO 7
    In this case ( data ) is either "00" or "01".
It says as response we receive ">( cr )"; however we are not going to care about this for now.

Digital Data In

$AA6( cr )
  • AA is address for 485
  • 6 is Digital Data in Command
Response: !( dataOutput )( dataInput )00( cr )
  • ( dataOutput ) Loop back from output.
    2 character string. If we get "A0", it means that DO 7 and DO 5 is ON and rest are OFF.
  • ( dataInput )
    2 character string. This value is what we want.
  • "00"
    We don't know what this is for, so let's not worry about this.