2021年6月6日 星期日

DS18B20-Arduino-VFD 溫度控制

空調溫度控制

Arduino 讀取溫度感測器 DS18B20數值,根據溫度-頻率控制法則改變變頻器頻率

系統架構

PLC系統















Arduino 系統








線路

變頻器RS485配線(使用RJ11電話線)




RJ11端子定義



DS18B20配線参考 DS18B20溫度寫入PLC

RS485配線参考Arduino與PLC RS485通訊

控制要求

溫度 0 ~ 19 ,變頻器頻率為 15 Hz

溫度 20 ~ 24,變頻器頻率為 20 Hz

溫度 25 ~ 29 ,變頻器頻率為 25 Hz

溫度 30 ~ 34 ,變頻器頻率為 30 Hz

溫度 35 ~ 39 ,變頻器頻率為 35 Hz

溫度 40 ~ 99 ,變頻器頻率為 40 Hz

VFD-M 變頻器參數設定 

參數

設定值

說明

00-20

1

頻率由 RS-485 通信界面操作(RJ-11

00-21

0

由鍵盤操作

09-00

1

VFD-M 變頻器的通訊位址 01

09-01

9.6

通訊傳送速度 Baud rate  9600

09-04

14

RTU 模式,資料格式<8E1>

通訊位址



程式 

// DS18B20-Arduino-VFD  

// Include the libraries we need

#include <OneWire.h>

#include <DallasTemperature.h>

#define MB_IDEL 0

#define MB_SEND 1

#define MB_RECV 2

byte mb_state;

int result = -1;

void rtuRequest(byte id, byte fc, word address, word len, HardwareSerial& port ); 

byte rtuResopnse(byte id, HardwareSerial& port);

word CRC(byte* buf, byte len);

byte mb_frame[50]; //request / response messages

word mb_data[20]={32,20};  //request / response datas

#define POLLING_TIME 4000 // ms

unsigned long last_polling = 0;

bool polling = false;

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

void setup() {  

  Serial.begin(115200);   

  Serial1.begin(9600,SERIAL_8E1);  // Modbus RTU 9600,8,E,1 

  sensors.begin();  

  delay(100);  

}

void loop() {

 if(millis() - last_polling > 4000) {

  polling = true; 

  last_polling = millis();                

}

 switch(mb_state) {

  case MB_IDEL: if(polling)  {

                sensors.requestTemperatures();

                float tempC = sensors.getTempCByIndex(0);

                // Check if reading was successful

                if(tempC != DEVICE_DISCONNECTED_C) {

                  Serial.print("Temperature for the device 1 (index 0) is: ");

                  Serial.println(tempC);

                  int temp = (int)tempC;                  

                  switch(temp){

                    case  0 ... 19: mb_data[0] = 1500; break; //15Hz

                    case 20 ... 24: mb_data[0] = 2000; break; //20Hz

                    case 25 ... 29: mb_data[0] = 2500; break; //25Hz

                    case 30 ... 34: mb_data[0] = 3000; break; //30Hz

                    case 35 ... 39: mb_data[0] = 3500; break; //35Hz

                    case 40 ... 99: mb_data[0] = 4000; break; //40Hz 

                    default:        mb_data[0] = 1000; break; //10Hz

                  }

                  Serial.println(mb_data[0]);

                  mb_state = MB_SEND;

                } 

                else{

                  Serial.println("Error: Could not read temperature data");

                  mb_state = MB_IDEL; // do nothing

                 }

                }

                 polling = false;

                break;

  case MB_SEND: rtuRequest(1,16,0x2001,1,Serial1); 

                delay(50); //wait 50ms                               

                mb_state = MB_RECV;

                break;

  case MB_RECV: result = rtuResponse(1,Serial1);

                if(result != -1) {  //receive message finished

                  if(result == 0) { //response message correct                   

                   //do somthing                                    

                  }

                  else if(result > 0) {  //error occure

                    Serial.print("error code -->");  // print error code

                    Serial.println(result); 

                  }

                  mb_state = MB_IDEL;  //reset

                  result = -1;         //reset

                }

                break;

                }  

}

//slave response parse

byte rtuResponse(byte id, HardwareSerial& port) { 

  if(port.available()) {      

      byte len = 0;

      while (port.available())        

        mb_frame[len++] = port.read(); 

      if(len > 7){ // message ok      

        if(mb_frame[0] == id){   //step 1: check id          

          if(word(mb_frame[len-1],mb_frame[len-2]) == CRC(mb_frame,len-2)) { //step 2: check crc           

            switch(mb_frame[1]) { //step 3: get datas or comfirm                 

              case 3: for(int i=0 , j=0 ; i<mb_frame[2]; i+=2) 

                        mb_data[j++] = word(mb_frame[3+i],mb_frame[4+i]);

                      return 0;  

                      break;

              case 16: return 0;  // write success  

                      break;            

              deafult:break;    

            }//end switch    //step 3: parse mb_frame message    

          } //crc

          else {

               ; //Serial.println(" crc error "); 

          } 

        } //if(mb_frame[0] == id)            

        else {

              ; //Serial.println(" id  error ");

        }

      } 

      else {

        return mb_frame[3]; 

      }

  }

 }

void rtuRequest(byte id, byte fc, word address, word reg_len, HardwareSerial& port) {

  word crc=0;

  byte len=0; 

  mb_frame[0] = id; //unit id

  mb_frame[1] = fc; //function code  

  mb_frame[2] = highByte(address);  //start address high byte

  mb_frame[3] = lowByte(address);  //start address low byte

  mb_frame[4] = highByte(reg_len); //register length high byte

  mb_frame[5] = lowByte(reg_len);  //register length low byte

  switch(fc) {

    case 3: crc = CRC(mb_frame,6);

            // Serial.print("CRC :");

            // Serial.println(crc,HEX);

            mb_frame[6] = lowByte(crc); //crc low byte

            mb_frame[7] = highByte(crc);  //crc high byte

            len = 8;

            break;

    case 16:len = reg_len*2;    //data byte count = register length*2

            mb_frame[6] = len;   //data byte count = register length*2

            for(int i = 0 ; i < reg_len ; i++) {

              mb_frame[7+i*2] = highByte(mb_data[i]);  //data[0+i*2] high byte 

              mb_frame[7+i*2+1] = lowByte(mb_data[i]);  //data[1+i*2] low byte               

            }   

            crc = CRC(mb_frame,len+7);

            // Serial.print("CRC :");

            // Serial.println(crc,HEX);

            mb_frame[len+7] = lowByte(crc); //crc low byte

            mb_frame[len+8] = highByte(crc);  //crc high byte             

            len = len+9;

            break;            

    deafult:break;

  }   

    port.write(mb_frame,len); 

   // return len;  

}


word CRC(byte* buf, byte len) {

  word crc = 0xFFFF;  

  for (byte i = 0; i < len; i++) { 

    crc =  crc ^ (word) buf[i];  

    for (byte j = 0; j < 8; j++) 

      crc = (crc & 0x0001)?(crc >> 1) ^ 0xA001:(crc >> 1);        

  }

  return crc;

}

結果





















2021年6月5日 星期六

Arduino與變頻器通訊

目的:測試Arduino 與VFD RS485通訊

系統架構


配線(使用RJ11電話線)



RJ11腳位定義


變頻器通訊位址

程式 

/*

  Arduino RS232(RS485) and Delta VFD-S(變頻器

 */

 

byte inByte[50] ; // 用來儲存收進來的 data byte

int data[10];     // 儲存 data

//read HZ, NO:10

// command : "0A03210200012E8D"

byte cmd[]={0x0A,0x03,0x21,0x02,0x00,0x01,0x2E,0x8D};

int i=0;

boolean once = false;

void setup()                    // run once, when the sketch starts

{

  Serial.begin(9600);

  Serial1.begin(9600,SERIAL_8E1);     // Modbus RTU 9600,8,E,1

}

void loop()                      

  if(!once){ 

   Serial.println("Arduino --> VFD");

   for(i=0;i<sizeof(cmd);i++){   //for PC Monitor

    Serial.print(cmd[i],HEX);

    Serial.print(" ");

  }

    Serial.println(" ");

    Serial.println(" ");  

    Serial1.write(cmd,sizeof(cmd));  

    delay(100);   

  

 // response

 i=0;

 while (Serial1.available()) {

    inByte[i++] = Serial1.read();

  }

 int len = i; 

 //Serial.println(len);

 Serial.println("VFD --> Arduino"); 

 for(i=0;i<len;i++){   //for PC Monitor

    Serial.print(inByte[i],HEX);

    Serial.print(" ");

 }

 Serial.println(" ");

 

//get slave no., func code, and data length 

     

 Serial.print("slave no.: ");

 Serial.println(inByte[0]);

 Serial.print("func code: ");

 Serial.println(inByte[1]);

 Serial.print("data length(byte): ");

 Serial.println(inByte[2]);

// get data

 

 for(int i=0 ; i< inByte[2] ; i+=2){

     data[i/2] = (int) ((inByte[3+i] << 8) + inByte[3+i+1]);

 }

 for(int i=0 ; i< inByte[2] ; i+=2){

    Serial.println(data[i/2]);

 }

once = true;

  }//end once

}

結果






2021年6月4日 星期五

PLC ST教學4 - ST語法介紹

結構化語言(Structured TextST)是一種類似 C 語言編程方式,程式碼最大的特色是使用人類孰悉的文字例如: if then else while for ..等文字敘述組合而成,與CPU有關的IL指令比較,結構化語言接近人類語法所以被稱為高階語言,IL指令式(LD)被稱為低階指令,IL指令專長在處理01的信號;ST語言則擅長在處理數值資料。當今的工業自動化已朝向智慧化發展,使用AD/DA、伺服馬達、視覺影像、機器手臂已經非常普遍,PLC程式也必須大量的處數值運算,可以預見未來結構化語言會逐漸成為PLC編碼的主流。

ST 程式要素

敘述句(Statement)是 ST 語言中最基本的執行單位,它代表的是一個階梯圖區段(NETWORK),敘述句由表達式(Expression)加上分號 ”;” 而成,而表達式由運算元(Operand)與運算子(Operator)組合而成。

運算元是資料,運算子是處理資料的方法。

EX:

D0 := D1+10;

敘述句: D0 := D1+10;

表達式: D1+10

運算元: D1,10

運算子: + 

布林資料做ANDOR NOT位元邏輯運算。

EX:

M0 := X0.0 AND X0.1; 

數值資料做 加,減,乘,除 算術運算及 < > <= >= 比較運算。

EX:

M0 := (D0 >= 100);

假如 D0的數值大於等於100M0 ON;否則 M0 OFF 

在(   * )符號之間的文字為註解不會進行編譯,程式中多寫注解以便後續維護。 

ST 語言中使用常數時,其格式如下。

10 進制:23456

16 進制:16#5BA0

8 進制:8#55640

2 進制:2#101101110100000

字串:”XYZ 

BOOLTRUE FALSE 

ST 語法運算子表。



 











結構化語言包括3種結構:

l   順序結構:依照程式編寫順序,依序一行一行執行,用上表運算子組成的敘述屬順序結構。

指定結構 :=

語法

範例

<Var> := <Expressions>

a := b;

 

範例說明

b值指定(傳送)給a裝置或變數符號。

 

l   分歧結構(判斷結構):依判斷示結果(True False) 21執行,有2種語法 IF CASE

分歧結構 IF

語法

範例

IF <Boolean expression 1> THEN

statements;

ELSIF <Boolean expression 2>      THEN

statements;

.

.

ELSIF <Boolean expression n> THEN

statements;

ELSE

statements;

END_IF;

IF d<e THEN

    f:=1;

ELSIF d=e THEN

    f:=2;

ELSE

    f:=3;

END_IF;

範例說明

假如 d<e 為真(True)時,然後執行f:=1後離開;否則假如 d=e為真時,

然後執行f:=1後離開;否則執行f:=3後結束。

 

分歧結構 CASE

語法

範例

CASE <Var1> OF
<Value 1>:
   statements 1;
<Value 2>:
    statements 2;
    ...
<Value n>:
    statements n;
ELSE
    statements
END_CASE;

CASE f OF

   1:  g := 10;

   2:  g := 20;

ELSE

    g := 0;

END_CASE;

範例說明

判斷 的值是否為1,若是,則執行 g := 10; 後離開此結構;若否,繼續判斷 的值是否為 2,若是,則執行 g := 20; 後離開此結構;若否,則執行關鍵字「ELSE」之後的 g := 0; 後離開。

 

l   重複結構(迴圈結構):重複執行程式待條件成立後即離開迴圈,繼續執行下一行程式,有3種語法 REPEATWHILEFOR

迴圈結構 REPEAT

語法

範例

REPEAT
   statements;
UNTIL <Boolean expression>
END_REPEAT;

REPEAT

    i := m + n;

UNTIL i < 100

END_REPEAT;

範例說明

先執 i := m + n; 後再判斷 i < 100 的值是否為真(True),若其值為真,則程式會直接離開此結構;若為否(False),則結構內部的程序便會重複執行 i := m + n;,直 i < 100 值為真(True)之後才會離開。

 

迴圈結構 WHILE

語法

範例

WHILE <Boolean expression> DO
    statements;
END_WHILE;

WHILE m > 1 DO

    a := a + m;

END_WHILE;

範例說明

先判斷假如 m > 1 的值是否為真(True),若為真,則程式便會重複執行 a := a + m;,直到 m > 1值為否(False)之後才會離開。

 

迴圈結構 FOR

語法

範例

FOR <Var > := <start value> TO <end value> {BY <increment> } DO

statements;

END_FOR;

 

FOR h:=1 TO 10 BY 2 DO

    a := a + h;

END_FOR;

範例說明

先將變數 設為初始值 1,假如變數 h 值小於終點值10,執行 a := a + h;  之後再將變數h的內容值加上增量值2,接著再判斷變數 h 值是否小於終點值10,若是,則繼續執行 a := a + h; 及 的內容值加上增量值2;直到 值大於終點值10,則程式便會離開 FOR 結構。 

2021年6月3日 星期四

PLC ST教學3-第一個ST程式

  •  新增POU 
程式/新增

POU名稱:自訂

語言:ST

  • 編寫程式
 輸入 Y0.0 := X0.0;

圖示

  • 編譯
  • 下載

  • 執行

  • 線上監控

操作模擬器輸出入面板,看輸出結果。





2021年6月2日 星期三

PLC ST教學2-建立專案與硬體規劃

  • 建立新專案 
專案名稱:自訂

控制器種類:AH

機種:AHCPU510EN


  • 啟動PLC模擬器通訊通道
選擇AH5x0 模擬器

按下啟動


  • 工具/通訊設定
通訊通道名稱選擇:你所建立AH5x0模擬器的名稱



  • 硬體規劃
第1槽位:16點輸入 AH16AM10N

第2槽位:16點輸出  AH16AN01R

第3槽位:類比模組 AH06XA



  • 按下 IO Module 會出現規劃的模組






















2021年6月1日 星期二

PLC ST教學1-軟體安裝與模擬器設定

  • 安裝以下2個軟體

PLC編輯軟體, 通訊功能需搭配安裝COMMGR

ISPSoft V3.12 

通訊管理軟體

COMMGR V1.13 

DOWNLOAD

 

  • 新增PLC模擬器通訊設定

開啟COMMGR




按下新增



驅動程序名稱:自訂

通訊類別設定:AH5x0 Simulator

按下確認


  • 啟動PLC模擬器
選擇模擬器按下啟動








出現這個畫面表示成功設定並啟動PLC模擬器