Showing posts with label ADAM. Show all posts
Showing posts with label ADAM. Show all posts

Tuesday, June 3, 2014

Data Upload from Arduino - Arduino Web Logger

The Arduino WebSocket we used limits the size of packet we can send.  We are unable to receive entire 512 bytes Block, even if we divided it into 3, from the WebSocket.

Thus we are going to change the protocol so we are sending each Data item one-by-one.

Get Last Data Block Number

If we send "GET0", we receive N = <Data Block Number>.


<Data Block Number> 

is unsigned 32 bit integer ranging from "N = 1" to "N = 4294967295"
Of course it doesn't get that big.
In the source code from Data Log Code posting, SD_BASE_BLOCK (1024) is equal to 0.


Get Data Block

If we send "GETn", we receive
  • N = <Data Block Number>
  • T = <time stamp>
  • D = <data>
<time stamp> uses sec part from POSIX time_t.
Because we don't use the millisecond part, we need to *1000.
"T = 4294967295". Of course it will be hard to see this number in your lifetime.

<data> has +dd.ddd format.
  • "D=+12.345"
  • "D=": when there is no data
  • "D=-99.999": termination. Data Block has finished.

Original Protocol

Digital Out

  • Turning On: "DOn=ON"
  • Turning Off: "DOn=OFF"
n is equal to Channel number which is between 0 and 7, and there is no reply.

Digital In

When you send "DI" it replies with "bbbbbbbb".  b is equal to each input channel and it is either '0' or '1'.

Analog In

When you send "AIn" it replies with "+dd.ddd". n is equal to channel number which is between 0 and 7, and "+dd.ddd" ranges from -10.000 to +10.000.

Below I am only excerpting codes related to uploading.  Refer to the link at the bottom for entire code.
// -------------------------------- Data Uploading Function
void upload_lastBlockNo(void);
void upload_DataBlock(uint32_t reqBlock);
byte *setTerminator(byte *pD);

/*
 ----------------------------------------------------------
 D A T A  U P L O A D I N G  Function
 ----------------------------------------------------------
*/

/*
 0         1
 012345678901 last data block number 12 bytes send
 N=4294967295

 0         1
 012345678901 time stamp 12 bytes send
 T=4294967295

 012345678 Data 9 bytes send
 D=+99.999
 D=        empty
 D=-99.999 terminator
*/
void xsfBlockNo(uint32_t block) {
 ltoa(block - SD_BASE_BLOCK, printLn + 2, 10);
 printLn[0] = 'N';
 printLn[1] = '=';
 WSsend(printLn, strlen(printLn));
}

void upload_loastBlockNo(void) {
 xsfBlockNo(currBlock - 1);
}

void upload_DataBlock(uint32_t reqBlock) {
 byte *pS;
 int cnt;
 float data, *pF;

 // Store the currently logging Block into SD.
 // We might lose packets that ADAM-4017 is receiving.
 sdCard.writeBlock(currBlock, sdBuffer);

 printLn[1] = '=';

 if(sdCard.readBlock(reqBlock, sdBuffer)) {
  pS = sdBuffer;

  if(*((uint32_t*)pS) == USED_MARK) {
   pS += 4;

   // block number
   xsfBlockNo(reqBlock);

   // time stamp (milliseconds)
   ltoa(*((uint32_t*)pS), printLn + 2, 10);
   printLn[0] = 'T';
   WSsend(printLn, strlen(printLn));
   pS += 4;

   // data
   printLn[0] = 'D';
   pF = (float*)pS;
   cnt = 120;

   while(cnt--) {
    data = *pF++ / 10.0;

    if(data != -99.999)
     dtostrf(data, 7, 3, printLn + 2);
    else
     printLn[2] = 0x00;

    WSsend(printLn, strlen(printLn));
   }
  }

  printLn[0] = 'D';
  setTerminator((byte*)(printLn + 2));
  WSsend(printLn, strlen(printLn));
 }

 // Reload the block that we have stored.
 sdCard.readBlock(currBlock, sdBuffer);
}

//---------------------------------------------------------
byte *setTerminator(byte *pD) {
 *pD++ = '-';
 *pD++ = '9';
 *pD++ = '9';
 *pD++ = '.';
 *pD++ = '9';
 *pD++ = '9';
 *pD++ = '9';
 *pD++ = 0x00;
 return pD;
}

/*
  ---------------------------------------------------------
  W E B S O C K E T   h a n d l e r
  ---------------------------------------------------------
*/

void onData(WebSocket &socket, char* rxLn, byte rxSz) {
 uint32_t block;

 if((rxLn[0] == 'G') && (rxLn[1] == 'E') && (rxLn[2] == 'T')) {
  *(rxLn + rxSz) = 0x00;
  block = atol(rxLn + 3);
  
  if(block)
   upload_DataBlock(block + SD_BASE_BLOCK);
  else
   upload_lastBlockNo();
 }
}

HTML/JS code for Upload Testing



<!DOCTYPE html>
<html>
<head>
    <title>WebLogger 5.0</title>

    <script type="text/javascript" src="jquery-2.0.3.min.js"></script>
    <script>
        var ws;
        var rcvCnt;

        $(document).ready(function() {
            WebSocketConnect();
        });

        function WebSocketConnect() {
            var ar = new Array();
            var arSz, i;

            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) {
                    ar = evt.data.split('=');

                    // receive Last Data Block Number  
                    if (ar[0] == 'N') {
                        $("#lastBlockNo").empty();
                        $("#lastBlockNo").append('Last Block Number:' + ar[1]);
                    }

                    // receive Time Stamp
                    if (ar[0] == 'T') {
                        $("#timestamp").empty();
                        $("#timestamp").append('TimeStamp(milliseconds):' + ar[1]);
                    }

                    // receive Data Block
                    if (ar[0] == 'D') {
                        $("#RcvLn").append('<p>' + ar[1] + '<p>');

                        if (ar[1] == '-99.999') {
                            $("#recordCnt").empty();
                            $("#recordCnt").append('Record Count:' + rcvCnt);
                        }

                        rcvCnt++;
                    }
                }
            } catch (exception) { status('Exception' + exception); }
        }

        function upload_lastBlockNo() {
            ws.send("GET0");
        }

        function upload_DataBlock() {
            var recNo = document.getElementById("getRecordNo").value;
            ws.send("GET" + recNo);
            rcvCnt = 0;
            $("#timestamp").empty();
            $("#recordCnt").empty();
            $("#RcvLn").empty();
        }

        function status(str) {
            $("#status").empty();
            $("#status").append(str);
        }

    </script>

</head>
<body>
    <p id="status"></p>
    <button type="button" onclick="upload_lastBlockNo()">Get Last Data Block Number</button>
    
    <br />
    
    <input type="text" id="getRecordNo" value="1">
    <button type="button" onclick="upload_DataBlock()">Get Data Block</button>
    
    <p id="lastBlockNo"></p>
    <p id="timestamp"></p>
    <p id="recordCnt"></p>
    <p id="RcvLn"></p>
</body>
</html>

Full codes can be found in following links





Friday, May 30, 2014

Testing: Temporary Test Board(volume) + ADAM-4017 + RS485 + Arduino UNO + Ethernet Shield

Temporary Test Board + ADAM 4017 + RS485 + Arduino UNO + Ethernet Shield

I tested connecting volume to CH0 of ADAM-4017.
I used same source voltage.
Because input range is set to ±10V, if voltage is over +10V Arduino code shows it as +10V.
I didn't cut the board because I was too lazy it is easier to turn the knob with the board like that.

Schematic for volume attaching

I am attaching schematic for volume attaching just in case.
CH0 - CH5 on ADAM-4017 is differential, and CH6 - CH7 is single ended.
Thus, Vin0- has to be set to GND.





Thursday, May 29, 2014

HTML / JavaScript - 4017 Arduino Gateway

Screen Capture of WSAdam4_0.html

I want to emphasize how easy it is to draw that circular gauge!

First of all choose the chart that you like the most.
http://chartjs.devexpress.com/Demos/VizGallery/#index

If you have picked Circular Gauge, let's have a look at 12 samples.
http://chartjs.devexpress.com/Demos/VizGallery/#chart/circulargaugesbasicelementspaletteforranges

Now we have to download the library.
http://chartjs.devexpress.com/Download

Last updated download file is DevExpressChartJS-13.2.9.zip

Next, read the tutorial very thoroughly. After reading this you can do everything!
http://chartjs.devexpress.com/Documentation/Tutorial/Configure_Gauges?version=13_2

If you want to know more, read the API reference.
http://chartjs.devexpress.com/Documentation/ApiReference/dxCircularGauge?version=13_2

I have divided up what we have to do roughly. It could easily be done by just simply following the tutorial.

  1. Load the needed js library.
  2. Create the gauge.
  3. Using HTML, create space for gauge.

    If all the above is done,
  4. Send data that gauge is going to represent.

<!DOCTYPE html>
<html>
    <head>
        <title>ADAM-4017 4.0</title>
        <!--****************************
        1. Load the needed js library
           The following 3 js library is included 
           in DevExpressChartJS-13.2.9\Lib\js
           I have attached it in this posting.
        ****************************-->
        <script type="text/javascript" src="jquery-2.0.3.min.js"></script>
        <script type="text/javascript" src="globalize.min.js"></script>
        <script type="text/javascript" src="dx.chartjs.js"></script>
        <script>
            var ws;
            var poll = 0;
            var intervalTimer;
            
//          ****************************
//          2. Create the gauge
//          ****************************
            $(function() {
                $('#gaugeContainer').dxCircularGauge({
                    scale: {
                        startValue: 0,
                        endValue: 10,
                        majorTick: { tickInterval: 1 },
                        minorTick: { visible: true, tickInterval: 0.1 }
                    },
                    rangeContainer: {
                        ranges: [{ startValue: 0, endValue: 9, color: 'royalblue' },
                                 { startValue: 9, endValue: 10, color: 'red'}]
                    },
                    valueIndicator: { color: 'red' },
                    title: {
                        text: 'ADAM-4017',
                        font: { size: 28 },
                        position: 'bottom-center'
                    }
                });
            });

            $(document).ready(function() {
                WebSocketConnect();
            });

            function WebSocketConnect() {
                try {
                    ws = new WebSocket('ws://192.168.219.16:80/');
                    ws.onopen = function() {
                        status('Connected...');
                        ws.send("AI0");
                    }
                    ws.onclose = function() { status('Closed...'); }
                    ws.onerror = function(evt) { status('Error ' + evt.data); }
                    ws.onmessage = function(evt) {
//                  ****************************
//                  4. Send data that gauge is going to represent.
//                  ****************************
                        $('#gaugeContainer').dxCircularGauge('instance').value(evt.data);
                    }
                }
                catch (exception) {status('Exception ' + exception);}
            }

            function DI_poll() {
                ws.send("AI0");
                intervalTimer = setTimeout(function() { DI_poll() }, 500);
            }

            function DI_pollEnable() {
                if (!poll) {
                    poll++;
                    DI_poll();
                }
            }

            function DI_pollDisable() {
                clearTimeout(intervalTimer);
                poll = 0;
            }

            function status(str) { $('#status').append(str); }
            
        </script>
    </head>
    <body>
    <p id="status">Connection Status: </p>
    <button type="button" onclick="DI_pollEnable()">Input Refresh ON</button>
    <button type="button" onclick="DI_pollDisable()">OFF</button>
    
    <!--****************************
    3. Using HTML create space for gauge.
    ****************************-->
    <div id="gaugeContainer" style="height:380px"></div>
    </body>
</html>



Full codes can be found in following links






Wednesday, May 28, 2014

Arduino Code - 4017 Arduino Gateway

Add Analog In to the WebSocket protocol.

Digital Out

Turning on: "DOn=ON"
Turning off: "DOn=OFF"
n is channel number (0-7)
There is no reply.

Digital In

When you send "DI", it replies in "bbbbbbbb" format.
b is equal to each input channel, and it is either '0' or '1'.

Analog In

When you send "AIn", it replies in "+dd.ddd" format. 
n is channel number between 0 to 7. 
"+dd.ddd" is between -10.000 - +10.000.


The original is in posting: http://seosmartia.blogspot.kr/2014/05/arduino-code2-webarduinoadam.html

#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);
void ADMA4017_config(void);

char rx485Ln[8];
int rx485ix;
void AdamReceiver(void);

void setup() {
 Serial.begin(9600);

 Soft485.begin(9600);
 pinMode(DE485, OUTPUT);
 ADMA4017_config(); // Only need to be configured once.

 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 ADMA4017_config(void) {
 digitalWrite(DE485, HIGH);
 // refer to ADAM - 4017 Arduino Gateway posting
 Soft485.write("02080600");
 Soft485.write(0x0D);
 digitalWrite(DE485, LOW);
}

void onData(WebSocket &socket, char* rxLn, byte rxSz) {
 // Analog In
 if((rxLn[0] == 'A') && (rxLn[1] == 'I')) {
  digitalWrite(DE485, HIGH);
  // Analog In cmd for 4017 is #AAN(cr)
  Soft485.write("#00");
  Soft485.write(rxLn[2]);
  Soft485.write(0x0D);
  digitalWrite(DE485, LOW);
 }
}

void AdamReceiver(void) {
 char rxChr;

 if(Soft485.available()) {
  rxChr = Soft485.read();
  if(rxChr == '>')
   rx485ix = 0;
  else if(rx485ix >= 0) {
   if(rxChr == 0x0d) {
    // 4017's replay is >+dd.ddd(cr)
    if(rx485ix == 7) {
     // The reply conatins decimal pts like
     // "+09.456"; however, JS handles it very
     // well.
     wsServer.send(rx485Ln, 7);
    }
    rx485ix = -1;
   }
   else {
    rx485Ln[rx485ix++] = rxChr;
    if(rx485ix > 7) 
     rx485ix = -1;
   }
  }
 }
}

Full codes can be found in following links







Tuesday, May 27, 2014

ADAM - 4017 Arduino Gateway

ADAM-4017

The manual "ADAM-4000_manual_V20.pdf" can be downloaded from the following site.
http://support.advantech.com.tw/support/DownloadSRDetail_New.aspx?SR_ID=1%2BGE%2B715&Doc_Source=Download

Use INIT* as GND and set RS-485 Address as "00". Unlike ADAM-4050, settings need to be modified.

Configuration Command

%AANNTTCCFF(cr) ( refer to manual page 150 )
  • AA: "00" current RS-485 address.
  • NN: "02" new address for RS-485. You can set it to anything. I used "02" because I used "01" for ADAM-4050
  • TT: "08" input range. ( refer to manual page 152 ). ±10V, for now I used maximum.
  • CC: "06" 9600 bps ( refer to manual page 152 ).
  • FF: "00" ( refer to manual page 152 ).
    • <b7> 0: 60 Hz
    • <b6> 0: Checksum Disable
    • <b5 b4 b3 b2> 0000
    • <b1 b0> 10: ( refer to manual page 387 ).
      Receives the 12 bit AD read value in binary format.
      It becomes 3 byte HEX-ASCII.
      However, ADAM-4050 does not support this, thus ?01(cr) will be returned.
    • <b1 b0> 00: If you set it to 00, you receive voltage value within the input range.
      If the input range is equal to ±10V, it returns values between -10.000 - +10.000, which is very nice.

Analog Input

#AAN(cr) ( refer to manual page 161 )
  • AA: "00" RS-485 address
  • N: "0" channel 0 - 7. We are using 0.
Response
>HHH(cr)
  • HHH: It returns 3 byte HEX-ASCII. "000" - "FFF"
    ADAM-4050 does not support this.
OR

>+dd.ddd(cr)
  • +dd.ddd: The following 7 bytes value gets returned. -10.000 - +10.000. 




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.