ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 한 컴퓨터에서 서보 모터와 7Segment 제어하기
    Arduino/Web Socket 2021. 1. 14. 16:01

    RFID에 카드를 찍을 때마다 찍은 값을 7Segment에 출력해보자.

    맨 아래에 영상이 있으므로 이를 참고해보자.

    아두이노 2대가 필요하다.

    모든 내용을 아래에서 가지고 온다.

    designatedroom87.tistory.com/372

     

    한 컴퓨터로 2대의 아두이노 제어하기

    아래의 프로젝트를 그대로 가지고 와서 진행할 것이다. designatedroom87.tistory.com/369?category=917476 RFID의 값에 따라 LED의 On/Off 제어하기 구현할 내용을 좀 더 자세히 기술하면 아두이노로부터 값(RFID..

    designatedroom87.tistory.com

     

    프로젝트 WebSocketRFID를 변경하면 되는데, 너무 간단하게 끝난다.

    이미 RFID의 값을 읽는 부분에서 7Segment가 연결된 아두이노로 특정한 값을 전송해서

    카드를 찍은 회수를 하나씩 증가해서 출력하면 된다.

     

    우선 아두이노 소스파일은 2개가 필요하다.

    RFID를 읽는 소스파일과 7Segment에 값을 출력하는 소스 파일이 필요하다.

    7Segment와 아두이노 소스파일은 아래의 내용을 거의 100% 가지고 와서 활용한다.

    designatedroom87.tistory.com/267?category=900171

     

    7Segment에 숫자 표시하기

    우선 회로도를 보자. 우리가 사용할 방식은 VCC가 아닌 GND방식이다. 아두이노와 7세그먼트와의 연결은 다음과 같이 한다.a는 2번핀, b는 3번핀, c는 4번핀, d는 5번핀, e는 6번핀, f는 7번핀, g는 8번핀,

    designatedroom87.tistory.com

     

    아두이노와 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를 실행시킨다.

    파란색 혹은 흰색 카드를 찍어보자.

     

    영상

     

    프로젝트 폴더

    WebSocketRFID.war
    0.18MB

    댓글

Designed by Tistory.