commit 9e657e2558cbb0c6975799754920a79b87cc8e83 Author: chi Date: Tue Jan 7 16:07:58 2025 +0900 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f0598c --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +obj +bin +*.user +*.v12 +*.suo +.git +.vs +Debug +__vm +*.pdb +desktop.ini +packages +~*.xlsx diff --git a/Arduino_CallButton/Arduino_CallButton_Line.A.ino b/Arduino_CallButton/Arduino_CallButton_Line.A.ino new file mode 100644 index 0000000..1dd1d67 --- /dev/null +++ b/Arduino_CallButton/Arduino_CallButton_Line.A.ino @@ -0,0 +1,597 @@ +#include +#include "Arduino.h" +#define DEBUG + +bool cstate[3]; // 현재 상태(이전 상태와 비교하여 상태변화를 확인한다) +bool pstate[3]; // 이전 상태 +bool bchange[3]; //특정핀의 상태가 변경되었는가? +unsigned long clicktime[3]; //각 버튼 down 상태의 밀리초를 저장한다(롱 클릭 시간 확인용) +unsigned long ontime = 0; +unsigned long ledtime = 0; + +//bool bCharge = false; //클라이언트 7번 +bool bHWError = false; //클라이언트 8번 +bool bSelected = false; //현재 선택된 클라이언트 (LED가 깜박인다) +bool bEnbCall =false; //콜 가능여부 +bool runtimeledon=false; +String version = "2411291722"; +String recvbuffer = ""; +uint8_t clientno = 0; +uint8_t offmode = 0; +uint8_t clickcnt = 0; + +int pinIn[3] = { 2, 3 , 4 }; +int pinOut[3] = { 5, 6 , 7 }; + + +void setup() { + +delay(2000); + + +#ifdef DEBUG + Serial.begin(19200); +#endif +Serial1.begin(9600); + + //; Serial.println("Setup Start"); + for (int i = 0; i < 3; i++) + { + pinMode(pinIn[i], INPUT_PULLUP); + pinMode(pinOut[i], OUTPUT); + digitalWrite(pinOut[i], HIGH); //처음켜지면 ON상태로 한다. + cstate[i] = false; + pstate[i] = false; + clicktime[i] = 0; + } + + //digitalWrite(13,HIGH); + +/// Serial.println("Serial1 Wait"); +#ifdef DEBUG + clicktime[0] = millis(); + while (!Serial) { + unsigned long runtime = millis() - clicktime[0]; + if (runtime >= 1000) { + //bSerial=false; + // Serial.println("Serial1 timeout"); + break; + } + // wait for serial port to connect. Needed for native USB port only + } +#endif + + //digitalWrite(13,LOW); + clientno = EEPROM.read(0); //내부롬의 0번주소에서 클라이언트 번호를 확인한다. +//#ifdef DEBUG + Serial.print("AGV Caller v");// 1904020000"); + Serial.println(version); + Serial.print("ready #"); + Serial.print(clientno); + Serial.println(); +//#endif + //초기화 완료되면 oFF 한다. + digitalWrite(pinOut[0], LOW); + digitalWrite(pinOut[1], LOW); + digitalWrite(pinOut[2], LOW); + offmode = 0; + + digitalWrite(13,LOW); +} + + +void loop() { + + + #ifdef DEBUG + unsigned long runtimeled = millis() -ledtime; + if(runtimeled > 200) + { + if(runtimeledon==true) + { + digitalWrite(13,LOW); + runtimeledon=false; + }else{ + digitalWrite(13,HIGH); + runtimeledon=true; + } + runtimeled= millis(); + } + #endif + + //버튼입력상태를 확인한다. + for (int i = 0; i < 3; i++) + { + cstate[i] = !digitalRead(pinIn[i]); + if (cstate[i] != pstate[i]) + { + /*Serial.print("#"); + Serial.print(pinIn[i]); + Serial.print(" changed"); + Serial.println(cstate[i]);*/ + + bchange[i] = true; + + if (cstate[i]) //클릭시 + { + clicktime[i] = millis(); + //Serial.print("SET TIME #"); + //Serial.print(i); + //Serial.println(); + } + } + } + + //버튼 상태변화를 감지 + if (cstate[0] && cstate[1]) //두버튼이 동시에 눌려있다면 디버그모드 진입여부를 확인한다. + { + unsigned long time1 = millis() - clicktime[0]; + unsigned long time2 = millis() - clicktime[1]; + if (time1 > 3000 && time2 > 3000) + { + if (offmode == 0) + { + //OFF 상태일때에는 시리얼 버퍼처리를 하지 않는다. + offmode = 1; + #ifdef DEBUG + Serial.println("ENTER MODE SELECT"); + #endif + } + } + //동시에 눌려있지만 아직 지정 시간전이므로 처리하지 않는다. + } + else if (cstate[0] || cstate[1] || cstate[2]) //버튼이 하나만 눌려있을때 + { + if (offmode == 2) //client set mode + { + if (cstate[0] && bchange[0]) + { + clickcnt += 1; + #ifdef DEBUG + Serial.print("Click Count = "); + Serial.println(clickcnt); + #endif + } + else if (cstate[1] && bchange[1]) + { + //두번째 버튼을 눌렀다면 완료를 의미한다. + clientno = clickcnt; + SaveNo(); //현재 값을 저장 해준다. + + //선택된 번호를 깜박임으로 알려준다. + delay(100); + digitalWrite(pinOut[0], LOW); + digitalWrite(pinOut[1], LOW); + delay(1000); + for (uint8_t i = 1; i <= clientno; i++) + { + digitalWrite(pinOut[0], HIGH); + delay(500); + digitalWrite(pinOut[0], LOW); + delay(500); + } + #ifdef DEBUG + Serial.print("New Client Complete No="); + Serial.println(clientno); + #endif + offmode = 0; //다시 처음상태로 이동 + } + + } + else if (offmode == 3) //off mode + { + if (bchange[0] || bchange[1]) + { + Serial1.begin(9600); //re open port + #ifdef DEBUG + Serial.println("OFF MODE DISABLE -> NORMAL MODE"); + #endif + offmode = 0; //일반상태로 전환 + } + } + else if (offmode == 1) //mod select + { + if (bchange[0] && cstate[0]) //0번버튼이 ON 되었다면 번호편집모드로 전환 + { + #ifdef DEBUG + Serial.println("NO. SET MODE"); + #endif + clickcnt = 0; //클릭횟수를 초기화 + offmode = 2; + } + else if (bchange[1] && cstate[1]) //1번버튼이 ON 되었다면 OFF 모드로 전환 + { + Serial1.end(); + #ifdef DEBUG + Serial.println("OFF MODE : Disable Serial for xbee"); + #endif + offmode = 3; + } + } + else if (offmode == 0) { //일반 모드일때 하나만 눌렸다면 해당 버튼 명령을 전송한다. + + //uint8_t clientascii = (uint8_t)(clientno + 30); + + if (bchange[0] && cstate[0]) + { + + #ifndef DEBUG + digitalWrite(13,HIGH); + delay(200); + digitalWrite(13,LOW); + #endif + + Serial1.clearWriteError(); + Serial1.write(0x02); + Serial1.print(clientno); + Serial1.print('1'); //call + Serial1.write(0x03); + Serial1.flush(); + + #ifdef DEBUG + Serial.print("SEND CALL #"); + Serial.println(clientno); + #endif + } + else if (bchange[1] && cstate[1]) + { + + #ifndef DEBUG + digitalWrite(13,HIGH); + delay(200); + digitalWrite(13,LOW); + #endif + + Serial1.clearWriteError(); + Serial1.write(0x02); + Serial1.print(clientno); + Serial1.print('0'); //call + Serial1.write(0x03); + Serial1.flush(); +#ifdef DEBUG + Serial.print("SEND CANCEL#"); + Serial.println(clientno); + #endif + } + else if (bchange[2] && cstate[2]) //QA진행 버튼이 눌렸을때 + { + + #ifndef DEBUG + digitalWrite(13,HIGH); + delay(200); + digitalWrite(13,LOW); + #endif + + Serial1.clearWriteError(); + Serial1.write(0x02); + Serial1.print(clientno); + Serial1.print('2'); //go qa + Serial1.write(0x03); + Serial1.flush(); +#ifdef DEBUG + Serial.print("SEND QA#"); + Serial.println(clientno); + #endif + } + } + } + + //Serial.print("mode="); + //Serial.println(offmode); + //LED상태 조정 + switch (offmode) + { + case 0: //일반 상태값(버튼을 누르면 해당 버튼의 LED를 조정한다) + digitalWrite(pinOut[0], cstate[0]); + digitalWrite(pinOut[1], cstate[1]); + digitalWrite(pinOut[2], cstate[2]); + //Serial.print("led 3 stat="); + //Serial.println(cstate[2]); + break; + case 1: //모드 선택 상태( 이때 OK를 누르면 번호셋, CANCEL을 누르면 OFF 상태로) + if (digitalRead(pinOut[0]) == true) + { + digitalWrite(pinOut[1], HIGH); + digitalWrite(pinOut[0], LOW); + } + else { + digitalWrite(pinOut[0], HIGH); + digitalWrite(pinOut[1], LOW); + } + delay(100); + break; + case 2: //클라이언트 번호 설정 모드 - 적색만 깜박이고 녹색의 클릭횟수로 번호를 설정한다. + if (digitalRead(pinOut[1]) == true) + digitalWrite(pinOut[1], LOW); + else + digitalWrite(pinOut[1], HIGH); + break; + delay(100); + case 3: //OFF 상태일때에는 LED를 서로 ON/OFF 한다. + if (digitalRead(pinOut[0]) == true) + { + digitalWrite(pinOut[1], LOW); + digitalWrite(pinOut[0], LOW); + } + else { + digitalWrite(pinOut[0], HIGH); + digitalWrite(pinOut[1], HIGH); + } + delay(100); + } + + + //상태변환 여부를 초기화 + if (bchange[0]) pstate[0] = cstate[0]; + if (bchange[1]) pstate[1] = cstate[1]; + if (bchange[2]) pstate[2] = cstate[2]; + bchange[0] = false; + bchange[1] = false; + bchange[2] = false; + + //일반모드일때 처리 내역 + if (offmode == 0) + { + unsigned long runtime = millis() - ontime; + + //CALL가능하면 3번 LED를 ON 한다 230119 + if(bEnbCall) + { + digitalWrite(pinOut[2],HIGH); + } else{ + if(cstate[2]) + digitalWrite(pinOut[2],HIGH); + else + digitalWrite(pinOut[2],LOW); + } + + //현재 개체가 AGV선택개체라면 LED를 깜박거려서 표시를 해준다.(녹색버튼기준) + if (bHWError) + { + //하드웨어 오류 발생시에는 적색을 깜박거린다.(녹색:OFF) + digitalWrite(pinOut[0], LOW); //녹색은 끈다. + + //적색을 깜박인다.(1초주기로 ON/OFF 한다) + bool state = digitalRead(pinOut[1]); + if (runtime >= 250) //190319 + { + if (state) digitalWrite(pinOut[1], LOW); + else digitalWrite(pinOut[1], HIGH); + ontime = millis(); + } + } + else if (bSelected) + { + digitalWrite(pinOut[1], HIGH); //적색은 일단 켜둔다. + + //선택된 개체일때에는 적색은 켜고 녹색은 끔박인다. + bool state = digitalRead(pinOut[0]); + + //500ms 단위로 깜박인다. + if (runtime >= 500) //190319 + { + if (state) digitalWrite(pinOut[0], LOW); + else digitalWrite(pinOut[0], HIGH); + ontime = millis(); + } + } + else { + //선택개체가 아니라면 LED를 모두 OFF해준다. + for (int i = 0; i < 2; i++) + { + if (digitalRead(pinIn[0]) != digitalRead(pinOut[0])) + cstate[i] = !cstate[i]; //이전상태값을 반전시켜줌으로 써 다음 루프때 LED가 꺼지게 한다. + } + } + } + + //외부디버그 명령을 처리한다. + #ifdef DEBUG + CheckRemoteDebugCommand(); + #endif + CheckReceiveXbee(); + delay(100); +} + +void CheckRemoteDebugCommand() +{ + if (Serial.available() > 0) + { + int recv = Serial.read(); + if (recv == 0x0A || recv == 0x0D) + { + RunCommand(); + recvbuffer = ""; + } + else { + recvbuffer += (char)recv; + } + } +} + +String buffer = ""; +int kitNo = 0; +int incomingByte = 0; +void CheckReceiveXbee() +{ + //수신된 자료가 잇는지 체크한다. +#ifndef DEBUG + digitalWrite(13,HIGH); +#endif + + while (Serial1.available() > 0) { + incomingByte = Serial1.read(); + +#ifdef DEBUG + Serial.print("[RX]"); + Serial.println(incomingByte, HEX); + +#endif + + //stx ,etx 는 hex 처리하고 번호와 값을 ascii처리함 + if (incomingByte == 0x02) + { + buffer = ""; + } + else if (incomingByte == 0x03) { + + //ETX값이나 전체 길이가 4가아니라면 추가해야함 + byte clientno = (byte)(buffer.substring(0, 1).toInt()); + char clientval = buffer[1];// .substring(1, 2).toInt()); + char enablecall = '0'; //콜가능여부 추가 230119 + + //데이터가 3바이트라면 콜 가능상태로 전송된 경우이다 230119 + if(buffer.length() > 2) + { + enablecall = buffer[2]; + } + + NewMsgEvent(clientno, clientval,enablecall); + break; + } + else{ + buffer += (char)incomingByte; //문자를 누적시킨다. + } + } + + #ifndef DEBUG + digitalWrite(13,LOW); + #endif +} + +/* 190319 */ +void NewMsgEvent(uint8_t no, char value,char canCall) +{ + if (no == 1) return; //충전기관련 코드는 처리 하지 않는다. 190417 + else if (no == 8) //AGV가 HW에러 발생시 이 값을 전송한다. + { + if (bHWError == false) bHWError = true; + #ifdef DEBUG + if (value == '1')//system error + { + Serial.println("FLAG ON : H/W ERROR"); + } + else { + Serial.println("FLAG ON : AGV MANUAL MODE"); + } + #endif + return; + } + + //그외 나머지 상태에는 오류사항을 해제 해준다. + if (bHWError == true) + { + bHWError = false; + #ifdef DEBUG + Serial.println("FLAG OFF : H/W ERROR OFF"); + #endif + } + + bEnbCall = (canCall == '1'); + + //누군가가 지정되어있다. + if (value == '1') + { + if (no != clientno) //해당 번호가 내 번호와 일치하지 않는다면 내가 지정된 경우가 아니다. + { + //서버로부터 어떠한 개체가 켜졌는데. 그게 내가 아니라면 나는 OFF 한다. + if (bSelected) bSelected = false; + #ifdef DEBUG + Serial.print("Selected Off by Another ON No="); + Serial.println(no); + #endif + } + else { + bSelected = true; //내 번호가 켜졌으므로 선택됨으로 한다. + #ifdef DEBUG + Serial.print("Selected On No="); + Serial.println(no); + #endif + } + } + else if (value == '0') //누군가가 OFF 되어있다. + { + if (no == clientno) + { + //내가 꺼졋음을 보내왔다. + bSelected = false; + #ifdef DEBUG + Serial.println("Selected Off"); + #endif + } + } +} + +void RunCommand() +{ + + Serial.print("Remote Control : "); + Serial.println(recvbuffer); + + if (recvbuffer == "on") + { + Serial.print("on clientno = "); + Serial.println(clientno); + NewMsgEvent(clientno, '1','1'); + } + else if (recvbuffer == "callon") + { + Serial.print("on clientno = "); + Serial.println(clientno); + NewMsgEvent(clientno, '0','1'); + } + else if (recvbuffer == "calloff") + { + Serial.print("on clientno = "); + Serial.println(clientno); + NewMsgEvent(clientno, '0','0'); + } + else if (recvbuffer == "off") { + Serial.print("off clientno = "); + Serial.println(clientno); + NewMsgEvent(clientno, '0','0'); + } + else if (recvbuffer == "hw") { + + bHWError = !bHWError; + Serial.print("bHWError="); + Serial.println(bHWError); + } + else if (recvbuffer == "set") { + Serial1.print("+++"); + Serial1.write(0x0D); + Serial1.flush(); + } + else if (recvbuffer == "h") + { + Serial.println("AGV Caller"); + Serial.print("v"); + Serial.println(version); + Serial.println("=============="); + Serial.println("h : help"); + Serial.println("on : selected on"); + Serial.println("off : selected off"); + Serial.println("#1~6 : set client no"); + Serial.println("? : get client no"); + Serial.println("=============="); + } + else if (recvbuffer == "?") + { + Serial.print("Get Client No : #"); + Serial.println(clientno); + } + else if (recvbuffer.startsWith("#")) + { + String strNo = recvbuffer.substring(1); + clientno = (byte)(strNo.toInt()); + SaveNo(); // EEPROM.write(0, clientno); + } +} +void SaveNo() +{ + EEPROM.write(0, clientno); + #ifdef DEBUG + Serial.print("Save Client No : #"); + Serial.println(clientno); + #endif +} diff --git a/Arduino_CallButton/ReadMe.txt b/Arduino_CallButton/ReadMe.txt new file mode 100644 index 0000000..e21a18c --- /dev/null +++ b/Arduino_CallButton/ReadMe.txt @@ -0,0 +1 @@ +221118 c-line 소스 복제 (B line 은 3버튼 기본으로 설정한다) \ No newline at end of file diff --git a/Arduino_PLC/Arduino_PLC.ino b/Arduino_PLC/Arduino_PLC.ino new file mode 100644 index 0000000..380e348 --- /dev/null +++ b/Arduino_PLC/Arduino_PLC.ino @@ -0,0 +1,105 @@ +/* + Name: B_FVI_AGV.ino + Created: 2022-11-16 오전 13:32:00 + Author: KIMCHK + */ + +#include "motor.h" +#include "IO.h" +#include "HmiClass.h" +#include "UtilClass.h" +#include "VarClass.h" +#include "VarClass.h" +#include + +int debugportvalue = 0; +String version = "22.11.16.1716"; +String debugmessage = ""; +void(*resetFunc)(void) = 0; +unsigned long timesyncvalue = 0; +unsigned long startTime = 0; +void setup() +{ + Serial.begin(57600); //디버그 및 업로드 포트 + Serial1.begin(57600); //통신 포트 + + /*while (!Serial) + true; + + while (!Serial1) + true;*/ + + pinMode(13,OUTPUT); + + var.eeprom_load(); //플래그 우선 복원 + + io.Setup(); + hmi.Setup(); //HMI 초기화 + var.Setup(); + mot.Setup(); + + hmi.SendMessage(F("##:SETUP:OK"),false); + hmi.SendMessage(String("##:VERSION:") + String(version), false); + + //TCCR1B = TCCR1B & B11111000 | B00000001; // set timer 1 divisor to 1 for PWM frequency of 31372.55 Hz + TCCR1B = TCCR1B & B11111000 | B00000010; // set timer 1 divisor to 8 for PWM frequency of 3921.16 Hz + TCNT1 = 0x0000; + //TCCR1B = TCCR1B & B11111000 | B00000011; // set timer 1 divisor to 64 for PWM frequency of 490.20 Hz + + PrintDebugCommand(); +} + +void loop() +{ + io.Update(); //DIO 상태 학인; + var.Update(); //가변변수 업데이트 + mot.Update(); // 모터제어 + hmi.Update(); //HMI Events + + //초기화 + if (var.runReset) + { + Serial.println("** RESET **"); + var.eeprom_incResetCount(); + var.runReset = false; + resetFunc(); + delay(2000); + } + + //경과시간 + unsigned long runtime = millis() - startTime; + if (runtime > 60000) var.runtime = 60000; + else var.runtime = (uint16_t)runtime; + startTime = millis(); + + if(startTime % 500 == 0) + { + if(digitalRead(13)==HIGH) + digitalWrite(13,LOW); + else + digitalWrite(13,HIGH); + } + +} +void UpdateTime() +{ + //시간정보를 조회한다. 190319 + timesyncvalue = millis(); +} +void PrintDebugCommand() +{ + String data = ""; + data += String("====================================\n"); + data += String("## AGV Z-Motor Controller\n"); + data += String("## Version "+ version + "\n" ); + data += String("## Created by ATK4-EET-1P\n"); + data += String("====================================\n"); + data += String(">> ZUP (z-axis move up)\n"); + data += String(">> ZUP (z-axis move up)\n"); + data += String(">> ZUP (z-axis move up)\n"); + data += String(">> ZUP (z-axis move up)\n"); + data += String(">> ZUP (z-axis move up)\n"); + data += String(">> ZUP (z-axis move up)\n"); + data += String("====================================\n"); + Serial.println(data); +} diff --git a/Arduino_PLC/FontList.txt b/Arduino_PLC/FontList.txt new file mode 100644 index 0000000..3a6017f --- /dev/null +++ b/Arduino_PLC/FontList.txt @@ -0,0 +1,120 @@ +//16 +ġ˻ظϰ±ػ뿬ἭӰӵȣðĿƷѹӵ͸кϷʱȭ(#%).-:/><_¸ͽýۼڵI/Oʱȭ01234456789SNOVRDCNSTLIMITHOMECHRGALIGNPAUSEMAGFMAGBXBEERFIDABCDEFTIMEȨڵ̵ITEMONTAKEABCDEFGHIJKLMNOPQRSTUVWXYZvӰ谪Ѱ谨ms= +//80 +QCA0123456789Hڵ +//40 +ưپȣQCAzZ%ø-=_MHPACKINGQCQARFIDXBEEġã߿ϷǾϴʿմϴۻڼʿεڷ¼յŲ()[#]յ¿CW0123456789.vLOADINGĿ÷ּġãġ˻غ˼̵̵Ȩ + +if(va0.val==0) +{ + msg.txt=" " + tbco.val=63846 +}else if(va3.val==0) +{ + msg.txt="RFID " + tbco.val=63846 +}else if(va4.val==0) +{ + msg.txt="XBEE " + tbco.val=63846 +}else if(va6.val==0) +{ + msg.txt="ڷ¼() " + tbco.val=63846 +}else if(va7.val==0) +{ + msg.txt="ڷ¼() " + tbco.val=63846 +}else if(va8.val>0) +{ + msg.txt=" " + tbco.val=63846 +}else if(va1.val>0) +{ + msg.txt=" " + tbco.val=63846 +}else if(va5.val>0) +{ + msg.txt="(Z) ε" + tbco.val=63846 +}else if(va2.val==0) +{ + tbco.val=64520 + if(varmc.val==0) + { + msg.txt="() " + }else if(varmc.val==1) + { + msg.txt="() " + }else if(varmc.val==2) + { + msg.txt="() " + }else if(varmc.val==3) + { + msg.txt="() " + }else if(varmc.val==4) + { + msg.txt="() " + }else if(varmc.val==5) + { + msg.txt="() ̵(Ȩ)" + }else if(varmc.val==6) + { + msg.txt="() ̵()" + }else if(varmc.val==16) + { + msg.txt="() " + }else + { + msg.txt="() ˼ " + } +}else +{ + //autorun mode : parse message + tbco.val=1048 + if(varmc.val==0) + { + msg.txt="[#1] Ŀ ÷ ּ" //mcodevalue + }else if(varmc.val==1) + { + msg.txt="[#2] ̵()" //mcodevalue + }else if(varmc.val==2) + { + msg.txt="[#3] ʿմϴ" //mcodevalue + }else if(varmc.val==3) + { + msg.txt="[#4] ̵(Ȩ)" //mcodevalue + }else if(varmc.val==4) + { + msg.txt="[#5] ̵ ()" //mcodevalue + }else if(varmc.val==5) + { + msg.txt="[#6] ̵ ()" //mcodevalue + }else if(varmc.val==6) + { + msg.txt="[#7] " //mcodevalue + }else if(varmc.val==7) + { + msg.txt="[#8] Ŀ ּ" //mcodevalue + }else if(varmc.val==8) + { + msg.txt="[#9] ġ ã " //mcodevalue + }else if(varmc.val==9) + { + msg.txt="[#10] ġ ã " //mcodevalue + }else if(varmc.val==10) + { + msg.txt="[#11] غ" //mcodevalue + }else if(varmc.val==11) + { + msg.txt="[#12] " //mcodevalue + }else + { + msg.txt="[#99] ˼ " + } +} +if(tbmsg.txt!=msg.txt) +{ + tbmsg.txt=msg.txt + tbmsg.bco=tbco.val +} diff --git a/Arduino_PLC/HmiClass.cpp b/Arduino_PLC/HmiClass.cpp new file mode 100644 index 0000000..dc0a2c6 --- /dev/null +++ b/Arduino_PLC/HmiClass.cpp @@ -0,0 +1,569 @@ +#include "IO.h" +#include "VarClass.h" +#include "HmiClass.h" +#include "UtilClass.h" +#include "motor.h" +#include "arduino.h" + +unsigned long errtime = 0; +unsigned long eruntime = 0; + +void HmiClass::Setup() +{ + hmi.SendMessage("##:HMI:SETUP", false); + updatetime = millis(); + ClearTempBuffer0(); + ClearTempBuffer1(); +} + +void HmiClass::Update() +{ + //데이터는 일정 주기로 전송한다. + unsigned long runtime = millis() - updatetime; + uint8_t loopterm = var._eep_iosendinterval; + + loopterm = 500; + + //설정 중에는 통신속도를 초당 10번으로 고정한다 + if (var.getFlag(FLAG_SETUP) == true) loopterm = 100; + if (runtime > loopterm) + { + //IO상태전송 + SendIOStatus(); + + //설정모드에서는 계속 전송 한다 + if (var.getFlag(FLAG_SETUP) == true) + { + //EEP정보 전송 + SendSetupInfo(); + } + updatetime = millis(); + } + + //입력값 감지 190319 + CheckReceiveS0(); + CheckReceiveS1(); +} + +void HmiClass::SendIOStatus() +{ + //전송데이터처리 + byte payload[29]; //IO4바이트(uint32), A0~A3 A는 각 2바이트(uint16) + byte payidx = 0; + + payload[payidx++] = '@'; //@ + payload[payidx++] = '@'; //@ + payload[payidx++] = 10; //데이터 길이 + payload[payidx++] = 'I'; //49=I(IO) + payload[payidx++] = (byte)(var.IOData >> 0); //INPUT(16) + payload[payidx++] = (byte)(var.IOData >> 8); //OUTPUT(16) + payload[payidx++] = (byte)(var.IOData >> 16); + payload[payidx++] = (byte)(var.IOData >> 24); + + uint32_t flagValue = var.getFlagValue(); + payload[payidx++] = (byte)(flagValue >> 0); //FLAG (0~31) + payload[payidx++] = (byte)(flagValue >> 8); + payload[payidx++] = (byte)(flagValue >> 16); + payload[payidx++] = (byte)(flagValue >> 24); + + //쓰레드진행시간 + payload[payidx++] = (byte)var.runtime; + + //checksum + byte checksum = 0; + for (int i = 3; i < payidx; i++) + checksum = checksum ^ payload[i]; + + payload[payidx++] = checksum; + payload[payidx++] = 0x0D; + payload[payidx++] = 0x0A; + + //Serial.print("Send Payload len="); + //Serial.println(payidx); + + hmiSerial.write(payload, payidx); + hmiSerial.flush(); + + //20190325 - 1번포트로 미러링 + //dbgSerial.write(payload, sizeof(payload)); + //dbgSerial.flush(); +} + +void HmiClass::SendSetupInfo() +{ + //전송데이터처리 + byte payload[19]; //IO4바이트(uint32), A0~A3 A는 각 2바이트(uint16) + byte payidx = 0; + + payload[payidx++] = '@'; //@ + payload[payidx++] = '@'; //@ + payload[payidx++] = 5; //데이터 길이 + payload[payidx++] = 'S'; //49=I(IO),T=TEXT,S=SETUP + payload[payidx++] = var._eep_iosendinterval; + payload[payidx++] = var._eep_resetcount; + payload[payidx++] = var._eep_pindir_iH; + payload[payidx++] = var._eep_pindir_iL; + + //checksum + byte checksum = 0; + for (int i = 3; i < payidx; i++) + checksum = checksum ^ payload[i]; + + payload[payidx++] = checksum; + payload[payidx++] = 0x0D; + payload[payidx++] = 0x0A; + + //Serial.print("Send Payload len="); + //Serial.println(sizeof(payload)); + + hmiSerial.write(payload, sizeof(payload)); + hmiSerial.flush(); + + //20190325 - 1번포트로 미러링 + //dbgSerial.write(payload, sizeof(payload)); + //dbgSerial.flush(); +} + +void HmiClass::CheckReceiveS0() +{ + //수신데이터가 있는경우에만 처리함 + //bool newdata = hmiSerial.available() > 0; + //if (newdata) sprint(F("HMI Received Data [")); + while (hmiSerial.available() > 0) { + incomingByte0 = (char)(hmiSerial.read()); + + if (STX1S0 == false) + { + if (incomingByte0 != '@') + { + STX2S0 = false; + ETX1S0 = false; + SendMessage(F("ERROR:STX1"), true); + continue; + } + else + { + STX1S0 = true; + ClearTempBuffer0(); + Tempbuffer1[bufferIndex0++] = incomingByte0; //Tempbuffer1 += incomingByte; + } + } + else if (STX2S0 == false) + { + if (bufferIndex0 != 1 || bufferIndex0 < 1 || Tempbuffer1[0] != '@' || incomingByte0 != '@') + { + STX1S0 = false; + ETX1S0 = false; + SendMessage(F("ERROR:STX2"), true); + continue; + } + else + { + STX2S0 = true; + Tempbuffer1[bufferIndex0++] = incomingByte0; //Tempbuffer1 += incomingByte; + } + } + else + { + Tempbuffer1[bufferIndex0++] = incomingByte0; //Tempbuffer1 += incomingByte; + + //여기서부터는무조건 누적한다. + if (bufferIndex0 == 3) + { + if (Tempbuffer1[0] != 0x40 || Tempbuffer1[1] != '@') + { + STX1S0 = false; + STX2S0 = false; + ETX1S0 = false; + + bufferIndex0 = 0; // = ""; + } + else LEN1 = incomingByte0; //데이터 길이가온다 + } + else if (bufferIndex0 == LEN1 + 2 + 1 + 1) //체크섬이 왔다 + { + CHK1 = incomingByte0; + } + else if (bufferIndex0 == LEN1 + 2 + 1 + 1 + 1) //ETX1 + { + if (incomingByte0 != 0x0D) + { + //ETX가 와야하는데 다른데이터가 왔다 + STX1S0 = false; + STX2S0 = false; + ETX1S0 = false; + bufferIndex0 = 0;// + SendMessage(F("ERROR:STX3"), true); + } + } + else if (bufferIndex0 == LEN1 + 2 + 1 + 1 + 1 + 1) + { + //전체길이를 만족햇다. + if (incomingByte0 != 0x0A) + { + //ETX가 와야하는데 다른데이터가 왔다 + STX1S0 = false; + STX2S0 = false; + ETX1S0 = false; + //Console.WriteLine("에러 모두 파기"); + bufferIndex0 = 0;// == "";//.Clear(); + SendMessage(F("ERROR:STX4"), true); + } + else + { + STX1S0 = false; + STX2S0 = false; + ETX1S0 = false; + + //임시버퍼의 데이터를 수신데이터 변수에 넣는다 + //memcpy(buffer.c_str(), Tempbuffer1.c_str(), sizeof(Tempbuffer1)); + //Tempbuffer1.toCharArray() + //buffer = Tempbuffer1; + Parser(bufferIndex0); + bufferIndex0 = 0; + //Tempbuffer1[0] = 0; //첫비트에 nullstring 을 넣는다 + //var.runCommand( parser(buffer1, addMsg1, 0); + } + } + } + + ////stx ,etx 는 hex 처리하고 번호와 값을 ascii처리함 + //if (incomingByte == 0x02) buffer = ""; + //else if (incomingByte == 0x03) + //{ + // //ETX값이나 전체 길이가 4가아니라면 추가해야함 + // uint8_t butNo = (uint8_t)(buffer.substring(0, 3).toInt()); + // uint8_t butValue = (uint8_t)(buffer.substring(3, 4).toInt()); + // //remote 명령과 공유하기 위해서 util로 이동 + // var.runCommand((eCommand)butNo, butValue,0); //NewMsgEvent(butNo, butValue); + // break; + //} + //else{ + // buffer += (char)incomingByte; //문자를 누적시킨다. + // if (buffer.length() > 10) { + // sprintln(F("HMI buffer Over error")); + // buffer = ""; + // } + //} + } + //if (newdata) sprintln("]"); +} + +void HmiClass::CheckReceiveS1() +{ + //수신데이터가 있는경우에만 처리함 + //bool newdata = dbgSerial.available() > 0; + //if (newdata) sprint(F("HMI Received Data [")); + while (dbgSerial.available() > 0) { + incomingByte1 = (char)(dbgSerial.read()); + + if(incomingByte1 == 0x0A) //if newline + { + String cmd = ""; + for(int i = 0 ; i < bufferIndex1;i++) + { + cmd += String((char)Tempbuffer1[i]); + } + + if(cmd.equals("UP")||cmd.equals("up")) + { + Serial.println("User command : z-up"); + mot.SetZRun(ZRUN_UP); + } + else if(cmd.equals("DN")||cmd.equals("dn")) + { + Serial.println("User command : z-down"); + mot.SetZRun(ZRUN_DN); + } + else if(cmd.equals("STOP")||cmd.equals("stop")) + { + Serial.println("User command : z-stop"); + mot.SetZRun(ZRUN_STOP); + } + else{ + Serial.print("Unknown Command : "); + Serial.println(cmd); + } + + bufferIndex1 = 0; + } + else if(bufferIndex1 > 99) + { + Serial.println(F("recv1 length error(>99")); + bufferIndex1 = 0; + } + else { + Tempbuffer1[bufferIndex1++] = incomingByte1; //Tempbuffer1 += incomingByte; + } + } + //if (newdata) sprintln("]"); +} +/* +void HmiClass::CheckReceiveS1_Backup_221117() +{ + //수신데이터가 있는경우에만 처리함 + bool newdata = dbgSerial.available() > 0; + //if (newdata) sprint(F("HMI Received Data [")); + while (dbgSerial.available() > 0) { + incomingByte1 = (char)(dbgSerial.read()); + + if (STX1S1 == false) + { + if (incomingByte1 != '@') + { + STX2S1 = false; + ETX1S1 = false; + SendMessage(F("ERROR:STX1"), true); + continue; + } + else + { + STX1S1 = true; + ClearTempBuffer1(); + Tempbuffer1[bufferIndex1++] = incomingByte1; //Tempbuffer1 += incomingByte; + } + } + else if (STX2S1 == false) + { + if (bufferIndex1 != 1 || bufferIndex1 < 1 || Tempbuffer1[0] != '@' || incomingByte1 != '@') + { + STX1S1 = false; + ETX1S1 = false; + SendMessage(F("ERROR:STX2"), true); + continue; + } + else + { + STX2S1 = true; + Tempbuffer1[bufferIndex1++] = incomingByte1; //Tempbuffer1 += incomingByte; + } + } + else + { + Tempbuffer1[bufferIndex1++] = incomingByte1; //Tempbuffer1 += incomingByte; + + //여기서부터는무조건 누적한다. + if (bufferIndex1 == 3) + { + if (Tempbuffer1[0] != 0x40 || Tempbuffer1[1] != '@') + { + STX1S1 = false; + STX2S1 = false; + ETX1S1 = false; + + bufferIndex1 = 0; // = ""; + } + else LEN1 = incomingByte1; //데이터 길이가온다 + } + else if (bufferIndex1 == LEN1 + 2 + 1 + 1) //체크섬이 왔다 + { + CHK1 = incomingByte1; + } + else if (bufferIndex1 == LEN1 + 2 + 1 + 1 + 1) //ETX1 + { + if (incomingByte1 != 0x0D) + { + //ETX가 와야하는데 다른데이터가 왔다 + STX1S1 = false; + STX2S1 = false; + ETX1S1 = false; + bufferIndex1 = 0;// + SendMessage(F("ERROR:STX3"), true); + } + } + else if (bufferIndex1 == LEN1 + 2 + 1 + 1 + 1 + 1) + { + //전체길이를 만족햇다. + if (incomingByte1 != 0x0A) + { + //ETX가 와야하는데 다른데이터가 왔다 + STX1S1 = false; + STX2S1 = false; + ETX1S1 = false; + //Console.WriteLine("에러 모두 파기"); + bufferIndex1 = 0;// == "";//.Clear(); + SendMessage(F("ERROR:STX4"), true); + } + else + { + STX1S1 = false; + STX2S1 = false; + ETX1S1 = false; + + //임시버퍼의 데이터를 수신데이터 변수에 넣는다 + //memcpy(buffer.c_str(), Tempbuffer1.c_str(), sizeof(Tempbuffer1)); + //Tempbuffer1.toCharArray() + //buffer = Tempbuffer1; + Parser(bufferIndex1); + bufferIndex1 = 0; + //Tempbuffer1[0] = 0; //첫비트에 nullstring 을 넣는다 + //var.runCommand( parser(buffer1, addMsg1, 0); + } + } + } + + ////stx ,etx 는 hex 처리하고 번호와 값을 ascii처리함 + //if (incomingByte == 0x02) buffer = ""; + //else if (incomingByte == 0x03) + //{ + // //ETX값이나 전체 길이가 4가아니라면 추가해야함 + // uint8_t butNo = (uint8_t)(buffer.substring(0, 3).toInt()); + // uint8_t butValue = (uint8_t)(buffer.substring(3, 4).toInt()); + // //remote 명령과 공유하기 위해서 util로 이동 + // var.runCommand((eCommand)butNo, butValue,0); //NewMsgEvent(butNo, butValue); + // break; + //} + //else{ + // buffer += (char)incomingByte; //문자를 누적시킨다. + // if (buffer.length() > 10) { + // sprintln(F("HMI buffer Over error")); + // buffer = ""; + // } + //} + } + //if (newdata) sprintln("]"); +} +*/ + +void HmiClass::Parser(byte bufferIndex) +{ + Serial.println("Remote Command Parse"); + + //데이터를 분석해야 함 + if (Tempbuffer1[0] == '@' && Tempbuffer1[1] == '@' && + Tempbuffer1[bufferIndex - 2] == 0x0D && Tempbuffer1[bufferIndex - 1] == 0x0A) + { + byte len = Tempbuffer1[2]; + byte chk = Tempbuffer1[len + 3]; + if (bufferIndex != len + 6) + { + String msg = ("===>Frame length error len="); + msg += String(bufferIndex); + msg += (",receive="); + msg += String(len + 6); + SendMessage(msg, true); + } + else { + //체크섬확인 + byte cs = 0; + for (int i = 3; i < (3 + len); i++) + cs = cs ^ Tempbuffer1[i]; + if (chk != cs) + { + String msg = ("===>checksum error calc="); + msg.concat( String(cs)); + msg.concat("receive="); + msg.concat(String(chk)); + SendMessage(msg, true); + } + else { + //체크섬일치 + byte command = Tempbuffer1[3]; + byte param1 = Tempbuffer1[4]; + byte param2 = Tempbuffer1[5]; + var.runCommand((eCommand)command, param1, param2); + } + } + } + else { + //프레임이 이상하다. + //sprintln("===>Frame error stx, etx"); + SendMessage("==>Frame error no stx,etx", true); + } +} +void HmiClass::ClearTempBuffer0() +{ + LEN0 = 0; + bufferIndex0 = 0; + memcpy(Tempbuffer0, 0, sizeof(Tempbuffer0)); +} +void HmiClass::ClearTempBuffer1() +{ + LEN1 = 0; + bufferIndex1 = 0; + memcpy(Tempbuffer1, 0, sizeof(Tempbuffer1)); +} + +void HmiClass::SendMessage(String message, bool isError) +{ + if(message.equals(hmimessage)) + { + //동일메세지가 왓다면 1초이내로 다시 전송하지 못하게 한다. + if(hmimessagerepeat == 0 || hmimessagerepeat > millis()) + hmimessagerepeat = millis(); + + hmimessagetime = millis()-hmimessagerepeat; + if(hmimessagetime < 999) return; + + } else{ + hmimessagerepeat = millis(); + hmimessage = message; + } + // test = len 4 , totals 4+4=8 + // @ @ 6 T 0 t e s t chk \r \n + byte totalLength = message.length() + 8; + byte payload[100]; //IO4바이트(uint32), A0~A3 A는 각 2바이트(uint16) + byte payidx = 0; + + payload[payidx++] = '@'; //@ + payload[payidx++] = '@'; //@ + payload[payidx++] = message.length() + 2; //데이터 길이 + payload[payidx++] = 'T'; + payload[payidx++] = isError; //오류여부 + for (int i = 0; i < message.length(); i++) + { + payload[payidx++] = message[i]; + } + + //checksum + byte checksum = 0; + for (int i = 3; i < (3 + message.length() + 2); i++) + checksum = checksum ^ payload[i]; + + payload[payidx++] = checksum; + payload[payidx++] = 0x0D; + payload[payidx++] = 0x0A; + + hmiSerial.write(payload, totalLength); + hmiSerial.flush(); + + //20190325 - 1번포트로 미러링 + dbgSerial.println(message); + //dbgSerial.write(message, message.length); + //dbgSerial.flush(); + +} +bool HmiClass::SendValue(char msg, uint32_t value) +{ + byte totalLength = 11; + byte payload[100]; //IO4바이트(uint32), A0~A3 A는 각 2바이트(uint16) + byte payidx = 0; + + payload[payidx++] = '@'; //@ + payload[payidx++] = '@'; //@ + payload[payidx++] = 6; //데이터 길이 + payload[payidx++] = 'V'; + payload[payidx++] = msg; //값종류 + payload[payidx++] = (byte)(value >> 0); + payload[payidx++] = (byte)(value >> 8); + payload[payidx++] = (byte)(value >> 16); + payload[payidx++] = (byte)(value >> 24); + + //checksum + byte checksum = 0; + for (int i = 3; i < (3 + 4 + 2); i++) + checksum = checksum ^ payload[i]; + + payload[payidx++] = checksum; + payload[payidx++] = 0x0D; + payload[payidx++] = 0x0C; + + hmiSerial.write(payload, totalLength); + hmiSerial.flush(); + + //20190325 - 1번포트로 미러링 + dbgSerial.write(payload, totalLength); + dbgSerial.flush(); +} + +HmiClass hmi; diff --git a/Arduino_PLC/HmiClass.h b/Arduino_PLC/HmiClass.h new file mode 100644 index 0000000..01b76ec --- /dev/null +++ b/Arduino_PLC/HmiClass.h @@ -0,0 +1,59 @@ +#ifndef _HMICLASS_H_ +#define _HMICLASS_H_ +#include "arduino.h" + +#define hmiSerial Serial1 +#define dbgSerial Serial + +class HmiClass +{ +public: + + void Setup(); + void Update(); + void SendIOStatus(); + void SendSetupInfo(); + void SendMessage(String message, bool isError); + bool SendValue(char msg, uint32_t value); + bool showDebug = false; + +private: + + void ClearTempBuffer0(); + void ClearTempBuffer1(); + byte Tempbuffer0[100]; + byte Tempbuffer1[100]; + byte bufferIndex0 = 0; + byte bufferIndex1 = 0; + + unsigned long updatetime = 100; //데이터 전송 간격 측정을 위한 변수 + + void CheckReceiveS0(); //Serial0 + void CheckReceiveS1(); //Serial1 + + char incomingByte0; //수신버퍼에서 읽은 데이터를 임시 저장 + char incomingByte1; //수신버퍼에서 읽은 데이터를 임시 저장 + + //String buffer = ""; //수신버퍼 임시 저장 공간 + //String Tempbuffer1 = ""; + bool STX1S0 = false; + bool STX2S0 = false; + bool ETX1S0 = false; + + bool STX1S1 = false; + bool STX2S1 = false; + bool ETX1S1 = false; + + byte LEN1; + byte CHK1; + + byte LEN0; + byte CHK0; + + unsigned long hmimessagerepeat=0; + unsigned long hmimessagetime =0; + String hmimessage=""; + void Parser(byte bufferIndex); +}; +extern HmiClass hmi; +#endif diff --git a/Arduino_PLC/IO.cpp b/Arduino_PLC/IO.cpp new file mode 100644 index 0000000..2e75223 --- /dev/null +++ b/Arduino_PLC/IO.cpp @@ -0,0 +1,386 @@ +#include "IO.h" +#include "VarClass.h" +#include "motor.h" +#include "HmiClass.h" +#include "UtilClass.h" + +void IoClass::Setup() +{ + hmi.SendMessage("##:IO:SETUP", false); + + byte DIPins[] = { 30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45 }; + byte DOPins[] = { 22,23,24,25,26,27,28,29 }; + + //입력포트 설정 + for (int i = 0; i < sizeof(DIPins); i++) + pinMode(DIPins[i], INPUT); + + //출력포트설정 + for (int i = 0; i < sizeof(DOPins); i++) + pinMode(DOPins[i], OUTPUT); + + //MAF 초기화 + for (int i = 0; i < MAF_SIZE; i++) + { + bufferF[i] = 0; + bufferB[i] = 0; + } + +} +void IoClass::Update() +{ + + //Read DigitalInput + + bool diValue[16]; int idx = 0; + diValue[idx++] = digitalRead(PINI_EMG); //B접이다 + diValue[idx++] = digitalRead(PINI_BTN_1); + diValue[idx++] = digitalRead(PINI_BTN_2); + diValue[idx++] = digitalRead(PINI_BTN_3); + diValue[idx++] = digitalRead(PINI_BTN_4); + diValue[idx++] = digitalRead(PINI_OVERLOADL1); + diValue[idx++] = digitalRead(PINI_OVERLOADL2); + diValue[idx++] = digitalRead(PINI_OVERLOADR1); + diValue[idx++] = digitalRead(PINI_OVERLOADR2); + diValue[idx++] = digitalRead(PINI_EMPTY_38); + diValue[idx++] = digitalRead(PINI_BTN_ZUP); + diValue[idx++] = digitalRead(PINI_BTN_ZDN); + diValue[idx++] = digitalRead(PINI_LIMIT_LU); + diValue[idx++] = digitalRead(PINI_LIMIT_LD); + diValue[idx++] = digitalRead(PINI_LIMIT_RU); + diValue[idx++] = digitalRead(PINI_LIMIT_RD); + diValue[idx++] = digitalRead(PINI_STOP); + + + //비상정지 반전 + diValue[IDXI_EMG] = !diValue[IDXI_EMG]; + + //오버로드센서 반전 + diValue[IDXI_OVERLOADL] = !diValue[IDXI_OVERLOADL]; + diValue[IDXI_OVERLOADR] = !diValue[IDXI_OVERLOADR]; + diValue[IDXI_EMPTY7] = !diValue[IDXI_EMPTY7]; + diValue[IDXI_EMPTY8] = !diValue[IDXI_EMPTY8]; + + //오버로드센서를 합친다 5+6=>5, 7+8=>6 + diValue[IDXI_OVERLOADL] = diValue[IDXI_OVERLOADL] || diValue[IDXI_OVERLOADR]; + diValue[IDXI_OVERLOADR] = diValue[IDXI_EMPTY7] || diValue[IDXI_EMPTY8]; + diValue[IDXI_EMPTY7]= false; + diValue[IDXI_EMPTY8]= false; + + //리밋센서도 반전 + diValue[IDXI_LIMIT_LU] = !diValue[IDXI_LIMIT_LU]; + diValue[IDXI_LIMIT_LD] = !diValue[IDXI_LIMIT_LD]; + diValue[IDXI_LIMIT_RU] = !diValue[IDXI_LIMIT_RU]; + diValue[IDXI_LIMIT_RD] = !diValue[IDXI_LIMIT_RD]; + + //리버스 체크 200325 + //입력값은 Reverse 하지 않는다 + /* + var._eep_pindir_iL = 0; + var._eep_pindir_iH = 0; + + for (int i = 0; i < 16; i++) + { + if (i < 8) + { + if (bitRead(var._eep_pindir_iL, i) == true) + diValue[i] = !diValue[i]; + } + else { + if (bitRead(var._eep_pindir_iH, i - 8) == true) + diValue[i] = !diValue[i]; + } + }*/ + + + for (int i = 0; i < idx; i++) + { + //이 값의 변화를 체크한다. + bool oldValue = bitRead(var.IOData, i); + bool isNewValue = false; + if (oldValue != diValue[i]) isNewValue = true; + bitWrite(var.IOData, i, diValue[i]);//0번부터 기록한다 + + //값이 변화했따 + if (isNewValue) IOValueChanged(false, i, diValue[i]); + } + + //read output + bool doValue[8]; idx = 0; + doValue[idx++] = digitalRead(PINO_GUIDEMOTOR_LDIR); + doValue[idx++] = digitalRead(PINO_GUIDEMOTOR_LRUN); + doValue[idx++] = digitalRead(PINO_GUIDEMOTOR_RDIR); + doValue[idx++] = digitalRead(PINO_GUIDEMOTOR_RRUN); + doValue[idx++] = digitalRead(PINO_EMPTY_26); + doValue[idx++] = digitalRead(PINO_EMPTY_27); + doValue[idx++] = digitalRead(PINO_EMPTY_28); + doValue[idx++] = digitalRead(PINO_EMPTY_29); + + for (int i = 0; i < idx; i++) + { + bool oldValue = bitRead(var.IOData, i + 16); + bool isNewValue = false; + if (oldValue != doValue[i]) isNewValue = true; + bitWrite(var.IOData, i + 16, doValue[i]);//0번부터 기록한다 + + //값이 변화했따 + if (isNewValue) IOValueChanged(true, i, !oldValue); + } + + //Read Analog (마그넷 센서 값만 확인한다) + idx = 0; + + //원본값은 그대로 저장한다 + var.ANData[idx++] = analogRead(PINAI_0); + var.ANData[idx++] = analogRead(PINAI_1); + var.ANData[idx++] = analogRead(PINAI_2); + var.ANData[idx++] = analogRead(PINAI_3); + + idx = 0; + var.AOData[idx++] = 0; + var.AOData[idx++] = 0; + var.AOData[idx++] = 0; + var.AOData[idx++] = 0; + + //파워체크 + if (IsPowerLoss() == true) var.setFlag(FLAG_POWERLOSS, true, "IOUpdate"); + else var.setFlag(FLAG_POWERLOSS, false, "IOUpdate"); +} + +//메인전원이 OFF되어있는지 확인합니다.(리밋센서의OFF여부로 체크함) +bool IoClass::IsPowerLoss() +{ + //얘들은 B접점이라서. 항상 ON 이 되어있는 애들이다. + //전원이 OFF되면 모든 센서가 LOW 상태가 된다 + //하지만 신호를 반전(!) 시켜두었으므로 모두 켜진다면 OFF 된 경우이다) + bool b1 = bitRead(var.IOData, IDXI_LIMIT_LD); + bool b2 = bitRead(var.IOData, IDXI_LIMIT_LU); + bool b3 = bitRead(var.IOData, IDXI_LIMIT_RU); + bool b4 = bitRead(var.IOData, IDXI_LIMIT_RD); + if (b1 == true && b2 == true && b3 == true && b4 == true) return true; + return false; +} + +//IO상태값이 변화되었을때 동작한다 +void IoClass::IOValueChanged(bool isout, uint8_t index, bool newValue) +{ + if (isout == false) //입력포트 + { + + if (index == IDXI_BTN_1) + { + if (newValue == true) + { + hmi.SendMessage("button 1 pressed", false); + } + } + if (index == IDXI_BTN_2) + { + if (newValue == true) + { + hmi.SendMessage("button 2 pressed", false); + } + } + if (index == IDXI_BTN_3) + { + if (newValue == true) + { + hmi.SendMessage("button 3 pressed", false); + } + } + if (index == IDXI_BTN_4) + { + if (newValue == true) + { + hmi.SendMessage("button 4 pressed", false); + } + } + + //양쪽면의 z축 UP/DN 버튼 할당 + if (index == IDXI_BTN_ZUP) + { + if (newValue == true) + { + if (mot.GetZDirL() == ZDIR_CW && mot.IsMoveZ() == true) + { + hmi.SendMessage("z-stop by h/w button", false); + mot.SetZRun(ZRUN_STOP); + } + if (mot.GetZDirL() == ZDIR_CCW && mot.IsMoveZ() == true) + { + hmi.SendMessage("z-stop by h/w button", false); + mot.SetZRun(ZRUN_STOP); + } + else { + mot.SetZRun(ZRUN_UP); + hmi.SendMessage("Z-Up by H/W Button", false); + } + + } + } + else if (index == IDXI_BTN_ZDN) + { + if (newValue == true) + { + if (mot.GetZDirL() == ZDIR_CCW && mot.IsMoveZ() == true) + { + hmi.SendMessage("z-stop by h/w button", false); + mot.SetZRun(ZRUN_STOP); + } + if (mot.GetZDirL() == ZDIR_CW && mot.IsMoveZ() == true) + { + hmi.SendMessage("z-stop by h/w button", false); + mot.SetZRun(ZRUN_STOP); + } + else { + mot.SetZRun(ZRUN_DN); + hmi.SendMessage("Z-Dn by H/W Button", false); + } + } + } + else if (index == IDXI_EMG) //비상정지 혹은 범퍼센서가 감지된경우 + { + if (newValue == true) + { + hmi.SendMessage("EMG Stop Detected", false); + mot.SetZRun(ZRUN_STOP); + } + } + + //일반적으로 Rising 상태일때에만 동작시킨다. + if (newValue == true) + { + //하단센서2개가 들어왔다면 FLAG를 설정한다 + if (index == IDXI_LIMIT_LD || index == IDXI_LIMIT_RD) + { + if (index == IDXI_LIMIT_LD ) + { + if (var.getFlag(FLAG_LIMITLOWL) == false) + { + var.setFlag(FLAG_LIMITLOWL, true, "IO:Limit Sensor(LD) Changed"); + hmi.SendMessage(F("SET:FLAG:LOW_LIMITL:1"), false); + } + } + else if (index == IDXI_LIMIT_RD ) + { + if (var.getFlag(FLAG_LIMITLOWR) == false) + { + var.setFlag(FLAG_LIMITLOWR, true, "IO:Limit Sensor(RD) Changed"); + hmi.SendMessage(F("SET:FLAG:LOW_LIMITR:1"), false); + } + } + else { + bool b1 = bitRead(var.IOData, IDXI_LIMIT_LD); + bool b2 = bitRead(var.IOData, IDXI_LIMIT_RD); + if (b1 == false && var.getFlag(FLAG_LIMITLOWL) == true) + { + var.setFlag(FLAG_LIMITLOWL, false, "IO:Limit Sensor(LD) Changed"); + hmi.SendMessage(F("SET:FLAG:LOW_LIMITL:0"), false); + } + if (b2 == false && var.getFlag(FLAG_LIMITLOWR) == true) + { + var.setFlag(FLAG_LIMITLOWR, false, "IO:Limit Sensor(RD) Changed"); + hmi.SendMessage(F("SET:FLAG:LOW_LIMITR:0"), false); + } + } + } + + if (index == IDXI_LIMIT_LU || index == IDXI_LIMIT_RU) + { + + + if (index == IDXI_LIMIT_LU || var.getFlag(FLAG_LIMITHIGHL) == false) + { + var.setFlag(FLAG_LIMITHIGHL, true, "IO:Limit Sensor(LU) Changed"); + hmi.SendMessage(F("SET:FLAG:HIGH_LIMITL:1"), false); + } + else if (index == IDXI_LIMIT_RU || var.getFlag(FLAG_LIMITHIGHR) == false) + { + var.setFlag(FLAG_LIMITHIGHR, true, "IO:Limit Sensor(RU) Changed"); + hmi.SendMessage(F("SET:FLAG:HIGH_LIMITR:1"), false); + } + else { + bool b1 = bitRead(var.IOData, IDXI_LIMIT_LU); + bool b2 = bitRead(var.IOData, IDXI_LIMIT_RU); + if (b1 == false && var.getFlag(FLAG_LIMITHIGHL) == true) + { + var.setFlag(FLAG_LIMITHIGHL, false, "IO:Limit Sensor(LU) Changed"); + hmi.SendMessage(F("SET:FLAG:HIGH_LIMITL:0"), false); + } + if (b2 == false && var.getFlag(FLAG_LIMITHIGHR) == true) + { + var.setFlag(FLAG_LIMITHIGHR, false, "IO:Limit Sensor(RU) Changed"); + hmi.SendMessage(F("SET:FLAG:HIGH_LIMITR:0"), false); + } + } + + } + + //오버로드 플래그 활성화 + if (index == IDXI_OVERLOADL) + { + mot.SetZRun(ZRUN_STOP); + hmi.SendMessage(F("All Mot Stop by OverloadL"), false); + } + else if (index == IDXI_OVERLOADR) + { + mot.SetZRun(ZRUN_STOP); + hmi.SendMessage(F("All Mot Stop by OverloadR"), false); + } + + } + else //해당값이 OFF 되었다 + { + + //대상이 리밋센서라면 플래그를 설정 해준다 + if (index == IDXI_LIMIT_LD) + { + + hmi.SendMessage("Limit Sensor(LD) Change " + String(index) + "=" + String(newValue), false); + if (var.getFlag(FLAG_LIMITLOWL) == true) + { + var.setFlag(FLAG_LIMITLOWL, false, "IO:Limit Sensor Changed(LD)"); + hmi.SendMessage(F("SET:FLAG:LOW_LIMIT:0"), false); + } + } + else if (index == IDXI_LIMIT_RD) + { + + hmi.SendMessage("Limit Sensor(RD) Change " + String(index) + "=" + String(newValue), false); + if (var.getFlag(FLAG_LIMITLOWR) == true) + { + var.setFlag(FLAG_LIMITLOWR, false, "IO:Limit Sensor Changed(RD)"); + hmi.SendMessage(F("SET:FLAG:LOW_LIMIT:0"), false); + } + } + else if (index == IDXI_LIMIT_LU ) + { + hmi.SendMessage("Limit Sensor(LU) Change " + String(index) + "=" + String(newValue), false); + if (var.getFlag(FLAG_LIMITHIGHL) == true) + { + var.setFlag(FLAG_LIMITHIGHL, false, "IO:Limit Sensor Changed(LU)"); + hmi.SendMessage(F("SET:FLAG:HIGH_LIMIT:0"), false); + } + } + else if (index == IDXI_LIMIT_RU) + { + hmi.SendMessage("Limit Sensor(RU) Change " + String(index) + "=" + String(newValue), false); + if (var.getFlag(FLAG_LIMITHIGHR) == true) + { + var.setFlag(FLAG_LIMITHIGHR, false, "IO:Limit Sensor Changed(RU)"); + hmi.SendMessage(F("SET:FLAG:HIGH_LIMIT:0"), false); + } + } + } + } + else //출력포트 + { + //일반적으로 Rising 상태일때에만 동작시킨다. + if (newValue == true) + { + + } + } +} + +IoClass io; diff --git a/Arduino_PLC/IO.h b/Arduino_PLC/IO.h new file mode 100644 index 0000000..a3a3755 --- /dev/null +++ b/Arduino_PLC/IO.h @@ -0,0 +1,136 @@ +#ifndef _IO_H_ +#define _IO_H_ +#include "Arduino.h" + +class IoClass +{ +public: + +//input pin +#define PINI_EMG 30 +#define PINI_BTN_1 31 +#define PINI_BTN_2 32 +#define PINI_BTN_3 33 +#define PINI_BTN_4 34 + +#define PINI_OVERLOADL1 35 +#define PINI_OVERLOADL2 36 + +#define PINI_OVERLOADR1 37 +#define PINI_OVERLOADR2 38 + +#define PINI_EMPTY_38 39 + +#define PINI_BTN_ZUP 40 +#define PINI_BTN_ZDN 41 +#define PINI_LIMIT_LU 42 +#define PINI_LIMIT_LD 43 +#define PINI_LIMIT_RU 44 +#define PINI_LIMIT_RD 45 +#define PINI_STOP 46 + +//send data index +#define IDXI_EMG 0 +#define IDXI_BTN_1 1 +#define IDXI_BTN_2 2 +#define IDXI_BTN_3 3 +#define IDXI_BTN_4 4 +#define IDXI_OVERLOADL 5 +#define IDXI_OVERLOADR 6 +#define IDXI_EMPTY7 7 +#define IDXI_EMPTY8 8 +#define IDXI_BTN_ZUP 9 +#define IDXI_BTN_ZDN 10 +#define IDXI_LIMIT_LU 11 +#define IDXI_LIMIT_LD 12 +#define IDXI_LIMIT_RU 13 +#define IDXI_LIMIT_RD 14 +#define IDXI_STOP 15 + +//output +#define PINO_GUIDEMOTOR_LDIR 22 //Guide Motor Direction +#define PINO_GUIDEMOTOR_LRUN 23 //Guide Motor Start +#define PINO_GUIDEMOTOR_RDIR 24 // +#define PINO_GUIDEMOTOR_RRUN 25 //Guide Motor Run(STA�� ����ȭ�� [IO.cpp:Update()] ) +#define PINO_EMPTY_26 26 +#define PINO_EMPTY_27 27 +#define PINO_EMPTY_28 28 +#define PINO_EMPTY_29 29 + + +#define IDXO_GUIDEMOTOR_LDIR 0 //Guide Motor Direction +#define IDXO_GUIDEMOTOR_LRUN 1 //Guide Motor Start +#define IDXO_GUIDEMOTOR_RDIR 2 // +#define IDXO_GUIDEMOTOR_RRUN 3 //Guide Motor Run(STA�� ����ȭ�� [IO.cpp:Update()] ) +#define IDXO_EMPTY_04 4 +#define IDXO_EMPTY_05 5 +#define IDXO_EMPTY_06 6 +#define IDXO_EMPTY_07 7 +#define IDXO_EMPTY_08 8 +#define IDXO_CHARGE_ON 9 +#define IDXO_MOT_POWER_L 10 +#define IDXO_MOT_POWER_R 11 +#define IDXO_EMPTY_42 12 +#define IDXO_EMPTY_43 13 +#define IDXO_EMPTY_44 14 +#define IDXO_EMPTY_45 15 + +//�Ƴ��α��Է�(�ɹ�ȣ) +#define PINAI_0 A0 //���׳ݼ���(FRONT) ��ġ �� +#define PINAI_1 A1 //���׳ݼ���(REAR) ��ġ �� +#define PINAI_2 A2 //�������� +#define PINAI_3 A3 //���͸� ���� �б� + +//�Ƴ��α� �Է�(�������ε���) +#define IDXAI_0 0 +#define IDXAI_1 1 +#define IDXAI_2 2 +#define IDXAI_3 3 + +//�Ƴ��α����(PWM) +#define PINAO_4 A4 //Z�� ���� �ӵ� +#define PINAO_5 A5 //���ʸ��� �ӵ� +#define PINAO_6 A6 //���ʸ��� �ӵ� +#define PINAO_7 A7 //�����ʸ��� �ӵ� + +//�Ƴ��α����(������ �ε���) +#define IDXAO_0 0 //Z�� ���� �ӵ� +#define IDXAO_1 1 //(��+��) ���� +#define IDXAO_2 2 //���ʸ��� �ӵ� +#define IDXAO_3 3 //�����ʸ��� �ӵ� + +#define MAF_SIZE 30 + + void Setup(); + void Update(); + bool IsPowerLoss(); +private: + + int bufferF[MAF_SIZE]; + int indexF = 0; + int countF = 0; + int iF; + int sumF = 0; + int tempF; + float avgF; + + int bufferB[MAF_SIZE]; + int indexB = 0; + int countB = 0; + int iB; + int sumB = 0; + int tempB; + float avgB; + + long tm_gateoutOn = 0; + long tm_gateoutOf = 0; + long tm_markOn = 0; + long tm_markOff = 0; + + unsigned long runtime = 0; + void IOValueChanged(bool isout, uint8_t index, bool newValue); + +}; + +extern IoClass io; +#endif diff --git a/Arduino_PLC/ReadMe.txt b/Arduino_PLC/ReadMe.txt new file mode 100644 index 0000000..e183e16 --- /dev/null +++ b/Arduino_PLC/ReadMe.txt @@ -0,0 +1 @@ +221116 B라인용 소스 정리 작업 진행 \ No newline at end of file diff --git a/Arduino_PLC/StructInfo.txt b/Arduino_PLC/StructInfo.txt new file mode 100644 index 0000000..e69de29 diff --git a/Arduino_PLC/UtilClass.cpp b/Arduino_PLC/UtilClass.cpp new file mode 100644 index 0000000..954c8c7 --- /dev/null +++ b/Arduino_PLC/UtilClass.cpp @@ -0,0 +1,21 @@ +#include "UtilClass.h" +#include "VarClass.h" +#include "arduino.h" + +uint32_t UtilClass::b2i(bool Value) +{ + if (Value == true) return 1; + else return 0; +} + +uint8_t UtilClass::spdConvPercToValue(uint8_t perc) +{ + uint8_t retval = map(perc, 0, 100, 0, 255); // perc / 100 * 255; + return retval; //,0,255,0,100)); +} + +void UtilClass::copy(byte* src, byte* dst, byte len) { + memcpy(dst, src, sizeof(src[0])*len); +} + +UtilClass util; \ No newline at end of file diff --git a/Arduino_PLC/UtilClass.h b/Arduino_PLC/UtilClass.h new file mode 100644 index 0000000..e49b89d --- /dev/null +++ b/Arduino_PLC/UtilClass.h @@ -0,0 +1,17 @@ +#ifndef _UTILCLASS_H_ +#define _UTILCLASS_H_ +#include "arduino.h" + +class UtilClass +{ + +public: + + void copy(byte* src, byte* dst, byte len); + uint32_t b2i(bool Value); + uint8_t spdConvPercToValue(uint8_t perc); + + //void debug(char *str, ...); +}; +extern UtilClass util; +#endif diff --git a/Arduino_PLC/VarClass.cpp b/Arduino_PLC/VarClass.cpp new file mode 100644 index 0000000..c5463f1 --- /dev/null +++ b/Arduino_PLC/VarClass.cpp @@ -0,0 +1,271 @@ +#include "VarClass.h" +#include "motor.h" +#include "IO.h" +#include "HmiClass.h" + + +void VarClass::Setup() +{ + hmi.SendMessage("##:VAR:SETUP", false); + + for(int i = 0 ; i < 4;i++) + { + ANData[i] = 0; + AOData[i] = 0; + } +} +void VarClass::Update() +{ + /* + if (var.getFlag(FLAG_CHARGEONM) == true) + { + //���� �����̶�� �ڵ� �����¸� �����Ѵ� + if (var.getFlag(FLAG_AUTORUN) == true) + var.setFlag(FLAG_AUTORUN, false, "Manual Charge Mode"); + } + */ +} + + +/* �ܺθ���� �����մϴ�. HMI�� ���� ����̳� RS232�� REMOTE ��ɿ� ���� */ +void VarClass::runCommand(eCommand cmd, uint8_t p1, uint8_t p2) +{ + /*String msg = ("RunCommand "); + msg += String(cmd); + msg += (":"); + msg += String(p1); + msg += (":"); + msg += String(p2); + hmi.SendMessage(msg, false);*/ + + //display data + var.pingtime = millis(); + bool newValue = false; + + if (cmd == LOAD) + { + var.eeprom_load(); + } + else if (cmd == SAVE) + { + var.eeprom_save(); + } + else if (cmd == RESET) + { + var.runReset = true; + } + else if (cmd == SET_PINMODE) + { + pinMode(p1, p2); //�ش����� IO��带 �����Ѵ� + } + else if (cmd == SET_DOUTPUT) + { + String msg = ("Remote:Output:"); + msg += String(p1); + msg += ','; + msg += String(p2); + hmi.SendMessage(msg, false); + digitalWrite(p1, p2); + } + else if (cmd == SET_AOUTPUT) + { + if (p1 == 11) var.AOData[0] = p2; + else if (p1 == 12) var.AOData[1] = p2; + + analogWrite(p1, p2); + + String msg = ("Set PWM Out:"); + msg += String(p1); + msg += ','; + msg += String(p2); + hmi.SendMessage(msg, false); + } + else if (cmd == SET_FLAG) + { + String msg = ("@FLAG|"); + msg += String(p1); + msg += ','; + msg += String(p2); + hmi.SendMessage(msg, false); + + if (p2 == 1) var.setFlag(p1, true, "Remote"); + else var.setFlag(p1, false, "Remote"); + + if (p2 == 1 && p1 == FLAG_SETUP) + { + hmi.SendMessage("Entering Setup Mode", false); + } + } + else if (cmd == SET_EEPROM) + { + if (p1 == EEP_IOINTERVAL) { + var._eep_iosendinterval = p2; + hmi.SendMessage("(EEP) I/O Interval = " + String(p2), false); + } + else if (p1 == EEP_RESETCOUNT) { + hmi.SendMessage("(EEP) EEP_RESETCOUNT = " + String(p2), false); + } + else hmi.SendMessage("Set_ErrpRom p1 error", false); + //var.eeprom_save(); + } + else if (cmd == GET_SETTING) + { + //���� �������� �����Ѵ� + //�̰DZ����ؾ��� + } + + else if (cmd == GUIDE_MOT) + { + if (p1 == 'L') //left + { + if (p2 == 'P') + { + mot.SetZDir(ZDIR_CW); + mot.SetZRunL(true); + } + else if (p2 == 'N') + { + mot.SetZDir(ZDIR_CCW); + mot.SetZRunL(true); + } + else if (p2 == 'S') + { + mot.SetZRunL(false); + } + } + else if (p1 == 'R') //right + { + if (p2 == 'P') + { + mot.SetZDir(ZDIR_CW); + mot.SetZRunR(true); + } + else if (p2 == 'N') + { + mot.SetZDir(ZDIR_CCW); + mot.SetZRunR(true); + } + else if (p2 == 'S') + { + mot.SetZRunR(false); + } + } + else if (p1 == 'A') //all + { + if (p2 == 'P') + { + mot.SetZRun(ZRUN_UP); + } + else if (p2 == 'N') + { + mot.SetZRun(ZRUN_DN); + } + else if (p2 == 'S') + { + mot.SetZRun(ZRUN_STOP); + } + } + } + else if (cmd == PINGCHK) + { + //�ƹ��͵� ó������ �ʴ´�. + } + else { + hmi.SendMessage(F("Unknown Command"), true); + } +} + +void VarClass::eeprom_save() +{ + //param + EEPROM.write(EEP_IOINTERVAL, var._eep_iosendinterval); + EEPROM.write(EEP_RESETCOUNT, var._eep_resetcount); + EEPROM.write(EEP_DIREVH, var._eep_pindir_iH); + EEPROM.write(EEP_DIREVL, var._eep_pindir_iL); + + //�ɼǰ� ���� + bool opt1 = var.getFlag(FLAG_ENABLE_AD4INVERT); + bool opt2 = var.getFlag(FLAG_ENABLE_LOG_SPEED); + //bool opt3 = var.getFlag(FLAG_ENABLE_BALANCE); + //bool opt4 = var.getFlag(FLAG_ENABLE_LIDARSTOP); + //bool opt5 = var.getFlag(FLAG_ENABLE_GATEOUT); + + bitWrite(var._eep_option, FLAG_ENABLE_AD4INVERT - 24, opt1); + bitWrite(var._eep_option, FLAG_ENABLE_LOG_SPEED - 24, opt2); + //bitWrite(var._eep_option, FLAG_ENABLE_BALANCE - 24, opt3); + //bitWrite(var._eep_option, FLAG_ENABLE_LIDARSTOP - 24, opt4); + //bitWrite(var._eep_option, FLAG_ENABLE_GATEOUT - 24, opt5); + + //hmi.SendMessage("Option Store Value=" + String(var._eep_option), false); + EEPROM.write(EEP_OPTION, var._eep_option); //���� 200402 + hmi.SendMessage("@SET|SAVE:" + String(var._eep_option), false); +} + +void VarClass::eeprom_load() +{ + //hmi.SendMessage("##:EEPROM:LOAD:BEGIN", false); + var._eep_iosendinterval = EEPROM.read(EEP_IOINTERVAL); + var._eep_resetcount = EEPROM.read(EEP_RESETCOUNT); + var._eep_pindir_iH = EEPROM.read(EEP_DIREVH); + var._eep_pindir_iL = EEPROM.read(EEP_DIREVL); + var._eep_option = EEPROM.read(EEP_OPTION); //�ɼǰ��� �����س��� ����Ѵ� + + //�ɼǰ� ���� + bool opt1 = bitRead(var._eep_option, FLAG_ENABLE_AD4INVERT - 24); + bool opt2 = bitRead(var._eep_option, FLAG_ENABLE_LOG_SPEED - 24); + + + if (var._eep_iosendinterval == 0 || var._eep_iosendinterval == 0xFF) + var._eep_iosendinterval = 50; + + + + hmi.SendMessage("@SET|LOAD:" + String(var._eep_option), false); +} + +void VarClass::eeprom_incResetCount() +{ + EEPROM.write(EEP_RESETCOUNT, _eep_resetcount); + hmi.SendMessage(F("Increase Reset Count"), false); +} + +/* + system flag + */ +void VarClass::setFlag(uint8_t pos, bool value, String Readson) +{ + //������Ȯ�� + bool oldValue = bitRead(var.flag, pos); + + //##################################### + // 값이 변경된 경우에 처리 + //##################################### + if (oldValue != value) + { + bitWrite(var.flag, pos, value); + + if (pos == FLAG_AUTORUN) + { + if (value == true) + { + + } + } + else if (pos == FLAG_STOPZ) + { + hmi.SendMessage("Z-Stop Condition => " + String(value) + ": " + Readson, false); + } + + } +} + +bool VarClass::getFlag(uint8_t pos) +{ + return bitRead(var.flag, pos); +} +uint32_t VarClass::getFlagValue() +{ + return var.flag; +} + +VarClass var; diff --git a/Arduino_PLC/VarClass.h b/Arduino_PLC/VarClass.h new file mode 100644 index 0000000..73d270e --- /dev/null +++ b/Arduino_PLC/VarClass.h @@ -0,0 +1,119 @@ +#ifndef _VAR_H_ +#define _VAR_H_ + +#define DEBUG +#define sprint(...) Serial1.print(__VA_ARGS__) +#define sprintln(...) Serial1.println(__VA_ARGS__) +#ifdef DEBUG +#define dprint(...) Serial1.print(__VA_ARGS__) +#define dprintln(...) Serial1.println(__VA_ARGS__) +#else +#define dprint(...) +#define dprint(...) +#endif + + +#include "arduino.h" +#include +#include "UtilClass.h" + +//모든 사용가능한 커맨드를 넣는다(이 파일은 서브PLC파일) +enum eCommand +{ + LOAD = 0, //EEPROM 불러오기 + SAVE, //EEPROM 저장 + RESET, //초기화 + PINGCHK, + SET_PINMODE, //PINMODE 설정 + SET_DOUTPUT, //디지털출력설정(포트번호,값[1,0]) + SET_AOUTPUT, //아날로그출력설정(포트GET_SETTING = 50, //셋팅값 요청 + SET_FLAG, + SET_EEPROM, + SET_MANUALSPEED, + GET_SETTING = 50, + GUIDE_MOT= 90, //가이드커버(양쪽) 0=멈춤,1=UP,2=DN 아스키코드표 90=Z + SET_EEP_DIREV, +}; + +//asci 0=48,1=49,2=50 + +enum eEEPAddress +{ + EEP_IOINTERVAL = 0, + EEP_RESETCOUNT, + EEP_DIREVH, + EEP_DIREVL, + EEP_OPTION, + EEP_UPTIME, +}; + +//플래그 총 32개 모두 다씀 +enum eFlag +{ + FLAG_STOPZ= 0, + FLAG_SETUP, + FLAG_WAIT, + FLAG_AUTORUN, + FLAG_MANUALRUN, + FLAG_LIMITHIGHL, + FLAG_LIMITHIGHR, + FLAG_LIMITLOWL, + FLAG_LIMITLOWR, + FLAG_POWERLOSS, + FLAG_DIR, + FLAG_LEFT_RUN, + FLAG_RIGHT_RUN, + FLAG_RUN_CMD, + FLAG_GO_CHAGER = 26, + FLAG_ENABLE_AD4INVERT=27, + FLAG_ENABLE_LOG_SPEED = 28, +}; + +class VarClass +{ + +public: + + unsigned long serialprinttime = 0; + uint16_t runtime = 0; + long pingtime = 0; + + //SUB + + uint32_t IOData = 0; //IO데이터가 있으며 DI : Low 16bit, DO : High 16bit + uint8_t ANData[4]; //A0 ~ A3 + uint8_t AOData[4]; //A4 ~ A6 + + void Setup(); + void Update(); + void runCommand(eCommand cmd, uint8_t p1, uint8_t p2); + + uint8_t _eep_resetcount = 0; //장치초기화 횟수 + uint8_t _eep_iosendinterval = 0; + + uint8_t _eep_option = 0; //옵션값(이 값은 FLAG와 연결됨) + + uint8_t _eep_pindir_iH = 0; //Input High + uint8_t _eep_pindir_iL = 0; //Input Low + + uint8_t manual_speed = 0; //속도고정값 + + void eeprom_save(); //EEPROM 쓰기 + void eeprom_load(); //EEPROM 읽기 + void eeprom_incResetCount(); //초기화횟수증가 및 저장 + + void setFlag(uint8_t pos, bool value, String Readson); //플래그쓰기 + bool getFlag(uint8_t pos); //플래그읽기 + uint32_t getFlagValue(); //플래그값확인 + + bool runReset = false; //이값을 설정하면 Project.Ino 에서 초기화를 수행함 + +private: + + uint32_t flag = 0; //시스템플래그 + uint32_t flag_ = 0; + //void FlagValueChanged(uint8_t index, bool newValue); +}; + +extern VarClass var; +#endif diff --git a/Arduino_PLC/motor.cpp b/Arduino_PLC/motor.cpp new file mode 100644 index 0000000..c76ac65 --- /dev/null +++ b/Arduino_PLC/motor.cpp @@ -0,0 +1,256 @@ +#include "motor.h" +#include "IO.h" +#include "VarClass.h" +#include "UtilClass.h" +#include "HmiClass.h" + +void motor::Setup() +{ + hmi.SendMessage("##:MOT:SETUP", false); + + //Z축 멈춤 + SetZRun(ZRUN_STOP); +} +void motor::Update() +{ + //########################## + // 사용자추가코드 + //########################## + + if (var.pingtime == 0 || var.pingtime > millis()) var.pingtime = millis(); + long runtime = millis() - var.pingtime; + + //중지조건 체크 + //(충전중, 비상정지, 오버로드, 설정화면) + bool bStop1 = var.getFlag(FLAG_SETUP); + bool bStop2L = bitRead(var.IOData, IDXI_OVERLOADL); + bool bStop2R = bitRead(var.IOData, IDXI_OVERLOADR); + bool bStop3 = bitRead(var.IOData, IDXI_EMG); + bool bStop4 = bitRead(var.IOData, IDXI_STOP); + + //중지조건이 활성화되었다면 처리 + String stpReason = ""; + + //Z축 중지는 오버로드만 확읺ㄴ다. + if (bStop3== true) + { + if (var.getFlag(FLAG_STOPZ) == false) + { + dbgSerial.println("Set Flag On: flag_stopz by EMG"); + var.setFlag(FLAG_STOPZ, true, "MotUpdate"); + } + } + else if (bStop2L == true) + { + if (var.getFlag(FLAG_STOPZ) == false) + { + dbgSerial.println("Set Flag On: flag_stopz by OverloadL"); + var.setFlag(FLAG_STOPZ, true, "MotUpdate_OVLL"); + } + } + else if (bStop2R == true) + { + if (var.getFlag(FLAG_STOPZ) == false) + { + dbgSerial.println("Set Flag On: flag_stopz by OverloadR"); + var.setFlag(FLAG_STOPZ, true, "MotUpdate_OVLR"); + } + } + else if (var.getFlag(FLAG_STOPZ) == true) + { + dbgSerial.println("Set Flag Off"); + var.setFlag(FLAG_STOPZ, false, "MotUpdate"); + } + + //가이드 모터 동작시 Limit 센서 체크 + ZLimit_AutoStop(); + + if (var.getFlag(FLAG_STOPZ) == true) + { + if (IsMoveZ() == true) + { + Serial.println("Z-Axis Auto Stop by 'STOP-Z' Condition"); + mot.SetZRun(ZRUN_STOP); + } + } + + //Serial.println("motor update"); +} + + +//Z축이 동작죽이라면 리밋센서값을 보고 자동으로 멈춥니다 +void motor::ZLimit_AutoStop() +{ + if (mot.IsMoveZ() == true) + { + //정방향이동시(올라간다) + if (mot.GetZDirL() == ZDIR_CW) + { + if (bitRead(var.IOData, IDXI_OVERLOADL) == true ) //오버로드 활성화시 멈춤 + { + mot.SetZRun(ZRUN_STOP); + if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Z-Axis Auto Stop by OverloadL", false); + } + else if (bitRead(var.IOData, IDXI_OVERLOADR) == true ) //오버로드 활성화시 멈춤 + { + mot.SetZRun(ZRUN_STOP); + if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Z-Axis Auto Stop by OverloadR", false); + } + else { + //상단센서가 하나라도 동작하면 멈춘다 + if (mot.IsMoveZL() == true && bitRead(var.IOData, IDXI_LIMIT_LU) == true) + { + mot.SetZRunL(false); + if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Z-Axis (Left) Auto Stop by Upper Limit", false); + } + if (mot.IsMoveZR() == true && bitRead(var.IOData, IDXI_LIMIT_RU) == true) + { + mot.SetZRunR(false); + if (var.getFlag(FLAG_SETUP) == false)hmi.SendMessage("Z-Axis (Right) Auto Stop by Upper Limit", false); + } + } + } + else //역방향이동(내려간다) + { + //하단센서가 하나라도 동작하면 멈춘다 + if (mot.IsMoveZL() == true && bitRead(var.IOData, IDXI_LIMIT_LD) == true) + { + mot.SetZRunL(false); + if (var.getFlag(FLAG_SETUP) == false)hmi.SendMessage("Z-Axis (Left) Auto Stop by Lower Limit", false); + } + if (mot.IsMoveZR() == true && bitRead(var.IOData, IDXI_LIMIT_RD) == true) + { + mot.SetZRunR(false); + if (var.getFlag(FLAG_SETUP) == false)hmi.SendMessage("Z-Axis (Right) Auto Stop by Lower Limit", false); + } + } + } +} + + + +//가이드 모터 진행방향을 반환(CW:Up, CCW:Down) +ZDirection motor::GetZDirL() +{ + bool value = bitRead(var.IOData, IDXO_GUIDEMOTOR_LDIR + 16); + if (value == false) return ZDIR_CW; + else return ZDIR_CCW; +} +ZDirection motor::GetZDirR() +{ + bool value = bitRead(var.IOData, IDXO_GUIDEMOTOR_RDIR + 16); + if (value == false) return ZDIR_CW; + else return ZDIR_CCW; +} + +//가이드 모터(좌+우) 동작 여부 +bool motor::IsMoveZ() +{ + if (IsMoveZL() == true) return true; + else if (IsMoveZR() == true) return true; + else return false; +} + +//가이드 모터(좌) 동작 여부 +bool motor::IsMoveZL() +{ + bool v1 = bitRead(var.IOData, IDXO_GUIDEMOTOR_LRUN + 16); + //bool v2 = bitRead(var.IOData, IDXO_GUIDEMOTOR_LEFT + 16); + return v1 ; +} + +//가이드 모터(우) 동작 여부 +bool motor::IsMoveZR() +{ + bool v1 = bitRead(var.IOData, IDXO_GUIDEMOTOR_RRUN + 16); + //bool v2 = bitRead(var.IOData, IDXO_GUIDEMOTOR_RIGHT + 16); + return v1 ; +} + +//가이드모터 진행방향 결정(True:정방향, False:역방향) +void motor::SetZDir(ZDirection direction) +{ + if (direction == ZDIR_CW) { + digitalWrite(PINO_GUIDEMOTOR_LDIR, LOW); + digitalWrite(PINO_GUIDEMOTOR_RDIR, LOW); + } + else { + digitalWrite(PINO_GUIDEMOTOR_LDIR, HIGH); + digitalWrite(PINO_GUIDEMOTOR_RDIR, HIGH); + } +} + +void motor::SetZRun(ZRunMode runMode) { + + bool bEmg = var.getFlag(FLAG_STOPZ); //Z축이 움직이면 안되는 조건이므로 처리하지 않는다. + + if (runMode == ZRUN_UP) + { + if (bEmg) { + hmi.SendMessage("[Ignore] Z-Run by Stop-Z Flag", true); + return; + } + SetZDir(ZDIR_CW); + SetZRunL(true); + SetZRunR(true); + if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Guide Up", false); + } + else if (runMode == ZRUN_DN) + { + if (bEmg) { + hmi.SendMessage("[Ignore] Run by Stop-Z Flag", true); + return; + } + SetZDir(ZDIR_CCW); + SetZRunL(true); + SetZRunR(true); + if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Guide Dn", false); + } + else if (runMode == ZRUN_STOP) + { + SetZRunL(false); + SetZRunR(false); + if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Guid Stop", false); + //digitalWrite(PINO_GUIDEMOTOR_RUN, LOW); + } +} + +void motor::SetZRunL(bool run) { + if (run == true) + { + //if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Z-Axis Left Run", false); + digitalWrite(PINO_GUIDEMOTOR_LRUN, HIGH); + //digitalWrite(PINO_GUIDEMOTOR_RUN, HIGH); + } + else { + digitalWrite(PINO_GUIDEMOTOR_LRUN, LOW); + //L,R둘다 꺼져있다면 STA도 끈다 + //if (bitRead(var.IOData, IDXO_GUIDEMOTOR_RIGHT + 16) == false) + //{ + // if (var.getFlag(FLAG_SETUP) == false)hmi.SendMessage("Z-Axis Sta Off by Right", false); + // digitalWrite(PINO_GUIDEMOTOR_RUN, LOW); + //} + } + +} + +void motor::SetZRunR(bool run) { + if (run == true) + { + //if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Z-Axis Right Run", false); + digitalWrite(PINO_GUIDEMOTOR_RRUN, HIGH); + //digitalWrite(PINO_GUIDEMOTOR_RUN, HIGH); + } + else { + digitalWrite(PINO_GUIDEMOTOR_RRUN, LOW); + //L,R둘다 꺼져있다면 STA도 끈다 + //if (bitRead(var.IOData, IDXO_GUIDEMOTOR_LEFT + 16) == false) + //{ + // if (var.getFlag(FLAG_SETUP) == false) hmi.SendMessage("Z-Axis Sta Off by Right", false); + // digitalWrite(PINO_GUIDEMOTOR_RUN, LOW); + //} + } +} + + +motor mot; diff --git a/Arduino_PLC/motor.h b/Arduino_PLC/motor.h new file mode 100644 index 0000000..8a2ce92 --- /dev/null +++ b/Arduino_PLC/motor.h @@ -0,0 +1,52 @@ +#ifndef _MOTOR_H_ +#define _MOTOR_H_ +#include "arduino.h" +#include "VarClass.h" + +enum ZRunMode +{ + ZRUN_STOP = 0, + ZRUN_UP, + ZRUN_DN +}; +enum ZDirection +{ + ZDIR_CW = 0, + ZDIR_CCW +}; + +class motor { + +public: + + void Setup(); + void Update(); + + void SetSpeedZ(uint8_t value); + + ZDirection GetZDirL(); //Z축 진행방향을 표시합니다. True 일경우, 정방향(위로), False일경우 역방향(아래로) + ZDirection GetZDirR(); //Z축 진행방향을 표시합니다. True 일경우, 정방향(위로), False일경우 역방향(아래로) + void SetZDir(ZDirection direction); //1=정방향, 0=역방향 + + void SetZRun(ZRunMode run); + void SetZRunL(bool run); + void SetZRunR(bool run); + + + bool IsMoveZ(); //현재 이동중인가? + +private: + + void SetPowerL(bool on); + void SetPowerR(bool on); + + bool IsMoveZL(); + bool IsMoveZR(); + + void ZLimit_AutoStop(); //Z축 리밋센서에 의한 동작 정지 + +}; + +extern motor mot; + +#endif diff --git a/xbee_setting.docx b/xbee_setting.docx new file mode 100644 index 0000000..1e232df Binary files /dev/null and b/xbee_setting.docx differ