-
한 컴퓨터에서 서보 모터와 7Segment 제어하기Arduino/Web Socket 2021. 1. 14. 16:01
RFID에 카드를 찍을 때마다 찍은 값을 7Segment에 출력해보자.
맨 아래에 영상이 있으므로 이를 참고해보자.
아두이노 2대가 필요하다.
모든 내용을 아래에서 가지고 온다.
designatedroom87.tistory.com/372
프로젝트 WebSocketRFID를 변경하면 되는데, 너무 간단하게 끝난다.
이미 RFID의 값을 읽는 부분에서 7Segment가 연결된 아두이노로 특정한 값을 전송해서
카드를 찍은 회수를 하나씩 증가해서 출력하면 된다.
우선 아두이노 소스파일은 2개가 필요하다.
RFID를 읽는 소스파일과 7Segment에 값을 출력하는 소스 파일이 필요하다.
7Segment와 아두이노 소스파일은 아래의 내용을 거의 100% 가지고 와서 활용한다.
designatedroom87.tistory.com/267?category=900171
아두이노와 RFID의 연결은 위의 글을 참고하자.
아래는 아두이노와 7Segment의 연결 그림들이다.
아두이노에서 RFID를 읽는 소스 파일
더보기// 흰 카드 97 A8 B6 4E // 파란색 카드 C7 99 FF 32 #include <Servo.h> #include <MFRC522.h> #include <SPI.h> #define SS_PIN 10 // 칩셋핀(데이터 핀) #define RST_PIN 9 // 리셋핀 #define MOTOR_PIN 8 // 서보모터 데이터 핀 Servo servo; MFRC522 mfrc522(SS_PIN, RST_PIN); void setup() { Serial.begin(9600); SPI.begin(); // Init SPI bus servo.attach(MOTOR_PIN); // 서보모터 servo.write(20); // 서보모터 값 초기화( 20 ) mfrc522.PCD_Init(); // Init mfrc522 } void loop() { SendRFID(); // RFID 카드 읽기 ReadData(); } void ReadData() { if ( Serial.available() ) { byte servoState = Serial.read(); // 읽은 데이터가 '1'이면 서보모터를 회전시킨다. if ( servoState == '1' ) { servo.write(160); delay(1000); servo.write(20); } } } void SendRFID() { // 카드가 읽혀지지 않은 상태(찍히지 않으면)이면 리턴 if (!mfrc522.PICC_IsNewCardPresent()) return; if (!mfrc522.PICC_ReadCardSerial()) return; ShowOtherPrintHex( mfrc522.uid.uidByte, 4 ); mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } byte GetDecimalToHex(int decimal) { const int diffHex = 'A' - 10; const int diffDecimal = '0'; byte ret; if ( decimal > 9 ) ret = diffHex + decimal; else ret = diffDecimal + decimal; return ret; } void ShowOtherPrintHex(byte* buffer, byte bufferSize) { byte l_buf[8] = { 0, }; int l_bufLen = 0; const int l_HEX = 16; for (byte i = 0; i < bufferSize; i++) { int frontNum = buffer[i] / l_HEX; int rearNum = buffer[i] % l_HEX; l_buf[l_bufLen++] = GetDecimalToHex(frontNum); l_buf[l_bufLen++] = GetDecimalToHex(rearNum); } Serial.write(l_buf, 8); }
7Segment에 값을 출력하는 아두이노 소스 파일
더보기// 행의 수는 우리가 표현할 십진수의 개수이며, // 열의 수는 십진수에 따른 수 표기에 필요한 세븐세그먼트핀의 수 bool num[10][8] = { {1,1,1,1,1,1,0,0}, // 숫자0 {0,1,1,0,0,0,0,0}, // 숫자1 {1,1,0,1,1,0,1,0}, // 숫자2 {1,1,1,1,0,0,1,0}, // 숫자3 {0,1,1,0,0,1,1,0}, // 숫자4 {1,0,1,1,0,1,1,0}, // 숫자5 {1,0,1,1,1,1,1,0}, // 숫자6 {1,1,1,0,0,0,0,0}, // 숫자7 {1,1,1,1,1,1,1,0}, // 숫자8 {1,1,1,1,0,1,1,0} // 숫자9 }; // 숫자들을 출력하기 void setup() { // put your setup code here, to run once: Serial.begin(9600); // 2번핀부터 9번핀까지 모두 설정 for (int i = 2; i < 10; i++) { pinMode(i, OUTPUT); digitalWrite(i,LOW); } WriteNum(0); } void WriteNum(int _num) { for (int i = 0; i < 8; i++) { digitalWrite(i + 2, num[_num][i]); } delay(500); } static byte g_num = '0'; void loop() { // put your main code here, to run repeatedly: if ( Serial.available() ) { const byte preambleChar = '+'; // 7Segement가 달린 아두이노에서만 데이터를 // 수신 가능하도록 정의한 특별 문자 byte d = Serial.read(); // 7Segement가 연결된 아두이노 라면 if ( d == preambleChar ) { // 기존의 값을 하나 증가시킨 값으로 출력 if ( '0' <= g_num + 1 && g_num + 1 <= '9' ) { g_num += 1; WriteNum( g_num - '0' ); } // 7Segment는 한 자리 수만 표현가능하므로, 9를 넘어서면 다시 0으로 출력 else { g_num = '0'; WriteNum( g_num - '0' ); } } } }
두 아두이노에 각 각 위의 소스 파일을 업로드하자.
serial 패키지의 파일들을 보자.
변경된 사항이 있는 부분이면 별도로 설명을 하겠다.
Main.java
더보기package serial; public class Main { public static void main(String[] args) { try { (new Serial()).connect("COM7"); // RFID & Motor (new Serial()).connect("COM5"); // 7_Segment } catch(Exception e) {e.printStackTrace();} } }
Serial.java
더보기package serial; import java.io.InputStream; import java.io.OutputStream; import gnu.io.CommPort; import gnu.io.CommPortIdentifier; import gnu.io.SerialPort; public class Serial { void connect(String port) { CommPort commPort = null; SerialPort serialPort = null; try { CommPortIdentifier com = CommPortIdentifier.getPortIdentifier(port); // com포트를 확인하는 작업 if (com.isCurrentlyOwned()) System.out.println("Error : "+port +"포트를 사용중입니다."); // 포트가 열려있으면 else { commPort = com.open(this.getClass().getName(),1000); // 획득한 포트를 객체가 사용할 수 있는지 여부 확인 if (commPort instanceof SerialPort) // commPort가 SerialPort로 사용할 수 있는지 확인 { serialPort = (SerialPort)commPort; // 정상적으로 포트를 사용할 수 있을 경우 // 포트에 필요한 정보를 입력해 준다. serialPort.setSerialPortParams( 9600, // 바운드레이트 SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); // 오류제어 비트 } System.out.println("comport성공"); InputStream in = serialPort.getInputStream(); OutputStream out = serialPort.getOutputStream(); (new Thread(new SerialRead(in, out))).start(); (new Thread(new SerialWebsocket(out))).start(); } } // end try catch(Exception e) {} } }
SerialRead.java
더보기package serial; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class SerialRead implements Runnable { InputStream in; private OutputStream out; boolean dataReceiveOk = false; boolean dataReceiveComplete = false; String receivedData = ""; public SerialRead(InputStream in, OutputStream out) { this.in = in; this.out = out; } @Override public void run() { byte[] buffer = new byte[1024]; int len = -1; try { String s = ""; // buffer에 저장하고나서, 그 길이를 반환한다. while ((len = this.in.read(buffer)) > -1) { s = new String( buffer, 0, len ); if ( s.length() == 0 && this.dataReceiveOk == true ) this.dataReceiveComplete = true; if ( s.length() > 0 && this.dataReceiveComplete == false ) { receivedData += s; this.dataReceiveOk = true; } else if ( this.dataReceiveComplete ) { System.out.println("receivedData : " +this.receivedData); // 읽은 데이터가 카드정보이면, 카드 정보를 웹 서버에 전송 if ( ProcessRFID.IsMatchedRFID(this.receivedData) ) { System.out.println("카드 정보 입니다."); (new ProcessRFID(in, out)).SendReceivedDataToWebSocket(receivedData); } // 정보 초기화 this.receivedData = ""; this.dataReceiveComplete = false; this.dataReceiveOk = false; } } } catch (IOException e) {e.printStackTrace();} } }
SerialWebsocket.java
더보기package serial; import java.io.OutputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.WebSocket; import java.util.concurrent.CountDownLatch; public class SerialWebsocket implements Runnable { OutputStream out; // Constructor public SerialWebsocket(OutputStream out) {this.out = out;} @Override public void run() { try { CountDownLatch latch = new CountDownLatch(10); // 데이터를 10번만 받겠다는 의미 URI uri = URI.create("ws://localhost:9090/websocket"); WebsocketClientArduino listener = new WebsocketClientArduino(latch, out); WebSocket ws = HttpClient.newHttpClient().newWebSocketBuilder().buildAsync( uri, listener).join(); ws.sendText("java app sendmessage", false); latch.await(); }catch(Exception e) { e.printStackTrace(); } } }
ProcessRFID.java
더보기package serial; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.WebSocket; import java.util.concurrent.CountDownLatch; public class ProcessRFID { private InputStream in; private OutputStream out; private CountDownLatch latch; private WebSocket ws; // 흰색 카드 파란 카드, //public static String[] RFIDs = { "97A8B64E", "C799FF32"}; // 흰색 카드 파란 카드, 흰색 카드 파란 카드 public static String[] RFIDs = { "97A8B64E", "C799FF32", "2A8AA11A", "F7FB4734" }; public ProcessRFID(InputStream in, OutputStream out) { this.in = in; this.out = out; latch = new CountDownLatch(10); // 데이터를 10번만 받겠다는 의미 URI uri = URI.create("ws://localhost:9090/websocket"); // WebsocketClientArduino 클래스의 onText함수가 호출이 된다. WebsocketClientArduino listener = new WebsocketClientArduino( this.latch, this.out ); ws = HttpClient.newHttpClient().newWebSocketBuilder().buildAsync( uri, listener).join(); } // 파란 카드이면 true를, 흰색 카드이면 false를 리턴 public static boolean IsMatchedBlueCard(String receivedData) { if ( RFIDs[0].equals(receivedData) ) return false; else return true; } public static boolean IsMatchedRFID(String receivedData) { boolean ret = false; for ( String s : RFIDs) { if ( s.equals(receivedData)) { ret = true; break; } } return ret; } public void SendReceivedDataToWebSocket(String receivedData) { // System.out.println("카드 유형 : " +this.GetCardInfo(receivedData)); System.out.println("카드 정보를 웹에 전송"); ws.sendText(receivedData, true); } }
WebsocketClientArduino.java
WebsocketClientArduino클래스의 onText메소드에서 추가할 작업이 있다.
RFID와 일치하는 데이터이면,
7Segment의 값 또한 증가해야하므로 세븐세그먼트가 연결된 아두이노에도 데이터를 전송해줘야 한다.
더보기package serial; import java.io.IOException; import java.io.OutputStream; import java.net.http.WebSocket; import java.net.http.WebSocket.Listener; import java.util.concurrent.CompletionStage; import java.util.concurrent.CountDownLatch; // 자바에서 지원하는 기본적인 소켓을 만든 것이다. // WebSocket.Listener를 통해, 웹 소켓에 접속할 것이다. public class WebsocketClientArduino implements WebSocket.Listener { // 웹 소켓에서 전송되는 데이터 수를 정하는 변수 // 만약, 3번만 데이터를 수신하고 싶으면 3으로 설정 // 웹 소켓이 닫히게 된다. onClose함수가 자동으로 실행된다. private final CountDownLatch latch; // 몇번을 받을 것인가? private OutputStream out; // 사용할 횟수만큼 매개변수로 전달하면 된다. public WebsocketClientArduino( CountDownLatch latch, OutputStream out) { this.latch = latch; this.out = out; } @Override // 서버에 접속했을 때, 호출된다. public void onOpen(WebSocket webSocket) { System.out.println("arduino websocket open"); Listener.super.onOpen(webSocket); } @Override public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) { System.out.println("arduino websocket close"); return Listener.super.onClose(webSocket, statusCode, reason); } @Override public void onError(WebSocket webSocket, Throwable error) { System.out.println("arduino websocket error"); Listener.super.onError(webSocket, error); } @Override // 메시지(텍스트)가 도착했을 때, 호출되는 함수 public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) { System.out.println( "arduino recv message : " +data ); this.latch.countDown(); // 카드 정보이면 '1'을 전송 if ( ProcessRFID.IsMatchedRFID(data.toString()) ) { System.out.println("RFID 데이터 전송"); try { this.out.write('1'); // RFID의 아두이노로 '1'을 전송 this.out.write('+'); // 7-Segment의 아두이노로 '+'를 전송 } catch (IOException e) {e.printStackTrace();} } return Listener.super.onText(webSocket, data, last); } }
websocket 패키지를 보자.
webcontroller.java
더보기package websocket; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet("/websocketcomm") public class webcontroller extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); //페이지를 전송하기 전에 url을 분석하고 비교해서 실행 String url = request.getRequestURI(); //System.out.println("url : "+url); String contextPath = request.getContextPath(); //System.out.println("contextpath : "+contextPath); String path = url.substring(contextPath.length()); //System.out.println("path : "+path); //마지막 '/'를 기준으로 값 추출 String lastPath = url.substring(url.lastIndexOf('/')+1); System.out.println("subPath : "+ lastPath); //최종 주소 String command = lastPath; if(command.equals("led")) { RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/arduinoled.jsp"); dispatcher.forward(request, response); } else { RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/arduinoled.jsp"); dispatcher.forward(request, response); } } }
WSocket.java
더보기package websocket; import java.util.Collections; import java.util.Set; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/websocket") public class WSocket { private static final Set<Session> sessions = Collections.synchronizedSet( new java.util.HashSet<Session>() ); // onopen 이벤트가 호출 되면 실행되는 함수 @OnOpen public void handleOpen(Session session) { System.out.println("클라이언트가 접속했습니다."); System.out.println("WSocket 클래스의 handleOpen 메소드 호출 => " +"session id : " +session.getId()); sessions.add(session); System.out.println("세션 개수 : "+sessions.size()); } // onclose 이벤트가 호출 되면 실행되는 함수 @OnClose public void handleClose(Session session) { System.out.println(session.getId() + "클라이언트가 연결을 해제했습니다."); // 세션을 닫는다. sessions.remove(session); System.out.println("세션 개수 : "+sessions.size()); } // onerror 이벤트가 호출 되면 실행되는 함수 @OnError public void handleError(Throwable t){t.printStackTrace();} // onmessage 이벤트가 호출 되면 실행되는 함수 @OnMessage public void handleMessage(String message, Session session) { System.out.println("현재 handleMessage메소드에 들어온 session id : " +session.getId()); // 서버가 받는다. System.out.println("WSocket 클래스의 handleMessage 메소드 호출 => " +"클라이언트가 보내온 메시지 : " +message); try { int i = 0; for (Session s : WSocket.sessions) { System.out.println(++i +"번째 : " +s.getId()); if ( !s.getId().equals( session.getId() ) ) s.getBasicRemote().sendText(message); } }catch(Exception e) {e.printStackTrace();} try { session.close(); System.out.println("handleMessage 메소드에서 호출 => 세션 개수 : " +sessions.size()); }catch(Exception e) {} } }
서버를 실행시키고, Main.java를 실행시킨다.
파란색 혹은 흰색 카드를 찍어보자.
영상
프로젝트 폴더
'Arduino > Web Socket' 카테고리의 다른 글
한 컴퓨터로 2대의 아두이노 제어하기(서보모터 동시에 동작) (0) 2021.01.12 RFID의 값에 따라 LED의 On/Off 제어하기 (0) 2021.01.09 웹 페이지에서 아두이노로 LED On/Off 제어하기 (1) 2021.01.05