ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 웹 페이지에서 아두이노로 LED On/Off 제어하기
    Arduino/Web Socket 2021. 1. 5. 21:32

    우선 아래의 내용을 보기에 앞서 아래의 내용에서 Web Socket에 대해 알아보고 오도록 하자.

    designatedroom87.tistory.com/347?category=909022

     

    Spring MVC2 - 7. Web Socket( Led on/off, 서보모터 제어)

    아래의 글을 이어서 진행해보자. designatedroom87.tistory.com/346 Spring MVC2 - 6 - (2). SpringMVC2에 아두이노와 시리얼 통신(버튼) 앞의 내용과 이어지는 부분이 있다. designatedroom87.tistory.com/345 Sp..

    designatedroom87.tistory.com

     

    Dynamic Web Project로 프로젝트를 하나 생성하자.

    이름은 WebSocketLED 라고 하자.

    다이나믹 웹 프로젝트를 만드는 순서와 서버의 설정 등에 대한 정보는 아래에서 익히고 오도록 하자.

    designatedroom87.tistory.com/321?category=903927

     

    아두이노의 데이터를 웹에 전송하기

    아두이노에서 데이터를 가져오는 내용은 아래의 내용에서 가지고 왔다. designatedroom87.tistory.com/316 아두이노와 자바의 시리얼 통신(아두이노에서 자바로 0과 1을 전송) 자바에서 시리얼 통신을 설

    designatedroom87.tistory.com

     

    시리얼 통신을 위해서 RXTXcomm.jar파일과 rxtxSerial.dll파일을 추가하고 설정까지 해야 한다.

    아래에서 시리얼 통신의 기본 설정에 대해서 하고 오도록 하자.

    designatedroom87.tistory.com/315?category=903927

     

    Java 시리얼 통신 기본 설정

    아두이노는 pc와 유선으로 연결되어 있다. 통신은 선을 통해서 한다. 아두이노와 pc가 서로 통신을 하려면 아두이노와 pc(자바)가 서로서로 시리얼 통신을 지원해야 한다. 이를 tx,rx

    designatedroom87.tistory.com

     

    그리고 기존의 앞에서한 내용을 그대로 들고 올 것이다.

    src폴더에 serial패키지를 하나 생성한다.

    Main.java, Serial.java, SerialRead.java, SerialWrite.java, DataProc.java 파일을 그대로 들고온다.

    모든 소스 파일은 맨 아래에 있다.

     

    Main.java에서는 포트번호를 반드시 확인해야 한다. 컴퓨터마다 다르다.

    Main.java

    더보기
    package serial;
    
    public class Main {
    	public static void main(String[] args) 
    	{
    		try {
    			
    			(new Serial()).connect("COM6");
    		}
    		catch(Exception e) 
    		{
    			e.printStackTrace();
    		}
    	}
    }

     

    SerialWrite.java

    더보기
    package serial;
    import java.io.IOException;
    import java.io.OutputStream;
    
    public class SerialWrite implements Runnable 
    {
    	OutputStream out;
    	
    	public SerialWrite(OutputStream out) {this.out = out;}
    	
    	@Override
    	public void run() 
    	{
    		int c = 0;
    		
    		try 
    		{
    			while ((c = System.in.read()) > -1)
    			{
    				out.write(c);
    			}
    		} catch (IOException e) {e.printStackTrace();}
    	}
    }

     

    DataProc.java

    더보기
    package serial;
    
    public class DataProc 
    {
    	String recvData;
    	
    	public DataProc(String recvData) 
    	{
    		this.recvData = recvData;
    		Print();
    	}
    	
    	public void Print()
    	{
    		System.out.println("DataProc : "+recvData);
    	}
    }

     

    SerialRead.java

    더보기
    package serial;
    import java.io.IOException;
    import java.io.InputStream;
    
    //	값을 읽는 클래스로, 이는 Thread로 구현해야 한다.
    public class SerialRead implements Runnable
    {
    	InputStream in;
    	
    	public SerialRead(InputStream in){this.in = in;}
    	
    	@Override
    	public void run() 
    	{
    		byte[] buffer = new byte[1024];
    		int len = -1;
    		
    		try 
    		{
    			//	buffer에 저장하고나서, 그 길이를 반환한다.
    			while ((len = this.in.read(buffer)) > -1)
    			{
    				//	System.out.println(new String(buffer,0,len));
    				//	new DataProc(new String(buffer,0,len));
    				String s = new String(buffer,0,len);
    				
    				if (len != 0) 
    					new DataProc(s);
    			}
    		} 
    		catch (IOException 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))).start();
    				new Thread(new SerialWrite(out)).start();
    				(new Thread(new SerialWebsocket(out))).start();
    			}
    		}	//	end try
    		catch(Exception e) {}
    	}
    }

     

    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", true);
    			latch.await(); 	
    	
    		}catch(Exception e) { e.printStackTrace();	}
    
    	}
    }

     

     

     

     

    그리고, src폴더에 websocket패키지를 하나 만든다.

    이 패키지에 아래의 4개의 파일들을 만들 것이다.

    webcontroller.java를 하나 만든다.

    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파일을 하나 만든다.

    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.RemoteEndpoint.Basic;
    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()
    	{
    		System.out.println("클라이언트가 접속했습니다.");
    	}
    	*/
    	//	onopen 이벤트가 호출 되면 실행되는 함수
    	@OnOpen
    	public void handleOpen(Session session)
    	{
    		System.out.println("클라이언트가 접속했습니다.");
    		System.out.println("session id : " +session.getId());
    		sessions.add(session);
    	}
    	/*
    	//	onclose 이벤트가 호출 되면 실행되는 함수
    	@OnClose
    	public void handleClose()
    	{
    		System.out.println("클라이언트가 연결을 해제했습니다.");
    	}
    	*/
    	//	onclose 이벤트가 호출 되면 실행되는 함수
    	@OnClose
    	public void handleClose(Session session)
    	{
    		System.out.println(session.getId() + "클라이언트가 연결을 해제했습니다.");
    		
    		//	세션을 닫는다.
    		sessions.remove(session);
    	}
    	
    	//	onerror 이벤트가 호출 되면 실행되는 함수
    	@OnError
    	public void handleError(Throwable t)
    	{
    		t.printStackTrace();
    	}
    	/*
    	//	onmessage 이벤트가 호출 되면 실행되는 함수
    	@OnMessage
    	public String handleMessage(String message)
    	{
    		//	서버가 받는다.
    		System.out.println("클라이언트가 보내온 메시지 : ");
    		System.out.println(message);
    		
    		return "메시지 수신 완료";
    	}
    	*/
    	
    	//	onmessage 이벤트가 호출 되면 실행되는 함수
    	@OnMessage
    	public void handleMessage(String message, Session session)
    	{
    		//	서버가 받는다.
    		System.out.println("클라이언트가 보내온 메시지 : ");
    		System.out.println(message);
    		
    		this.sendAll(session, message);
    		
    		try {
    			final Basic basic = session.getBasicRemote();
    			basic.sendText(message);
    		}catch(Exception e) {}
    	}
    	
    	public void sendAll(Session session, String message)
    	{
    		System.out.println(session.getId() + ":" +message);
    		try {
    			int i = 0;
    			//	웹 소켓에 연결되어 있는 모든 아이디를 찾는다.	
    			for (Session s : WSocket.sessions) 
    			{
    				System.out.println(++i);
    				if (!s.getId().equals(session.getId()))
    				{
    					s.getBasicRemote().sendText(message);
    				}
    			}
    		}catch(Exception e) {e.printStackTrace();}
    	}
    }

     

    WebsocketClient.java

    더보기
    package websocket;
    
    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 WebsocketClient implements WebSocket.Listener
    {
    	//	웹 소켓에서 전송되는 데이터 수를 정하는 변수
    	//	만약, 3번만 데이터를 수신하고 싶으면 3으로 설정
    	//	웹 소켓이 닫히게 된다. onClose함수가 자동으로 실행된다.
    	private final CountDownLatch latch;	//	몇번을 받을 것인가?
    	
    	//	사용할 횟수만큼 매개변수로 전달하면 된다.
    	public WebsocketClient( CountDownLatch latch) {
    		this.latch = latch;
    	}
    	
    	@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("websocketclient recv message : " +data);
    		
    		this.latch.countDown();		
    		
    		return Listener.super.onText(webSocket, data, last);
    	}
    }

     

    SocketMain.java

    더보기
    package websocket;
    
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.WebSocket;
    import java.util.concurrent.CountDownLatch;
    
    public class SocketMain {
    	public static void main(String[] args) {
    		try {
    			CountDownLatch latch = new CountDownLatch(10);	//	데이터를 10번만 받겠다는 의미
    			URI uri = URI.create("ws://localhost:9090/websocket");
    			WebsocketClient listener = new WebsocketClient(latch);
    	
    			WebSocket ws = HttpClient.newHttpClient().newWebSocketBuilder().buildAsync(
    					uri, listener).join();
    			
    			ws.sendText("java app sendmessage", true);
    			latch.await(); 	
    			
    		}catch(Exception e) { e.printStackTrace();	}
    	}
    }

     

    우선 WEB-INF폴더에 jsp파일을 하나 만들자.

    파일의 이름은 arduinoled.jsp라고 한다.

    arduinoled.jsp파일에는 LED를 제어할 ON/OFF버튼만 만들어 두었다.

    arduinoled.jsp

    더보기
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    
    <body>
    <button onclick="ws.send('1')">LED ON</button><p>
    <button onclick="ws.send('0')">LED OFF</button><p>
    
    <input type="range" id="vol" name="vol" min="0" max="180" onchange="ws.send(this.value)">
     
    <script>
    	var ws = new WebSocket("ws://localhost:9090/websocket");
    	
    	ws.onopen = function() {console.log('open')}
    	
    	ws.onmessage = function(event) { console.log(event.data);}
    	
    	ws.onopen = function(event) {console.log('close')}
    	
    	ws.onopen = function(event) {console.log('error')}
    </script>
    
    </body>
    </html>

     

    WebsocketClientArduino.java

    더보기
    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();		
    		
    		try {this.out.write(data.toString().getBytes());} 
    		catch (IOException e) {e.printStackTrace();}
    		
    		return Listener.super.onText(webSocket, data, last);
    	}
    }

     

     

    아두이노와 LED를 연결한다.

    8번핀에 연결한다. 그리고 아래의 소스 파일을 아두이노에 업로드 한다.

    아두이노 소스파일

    더보기
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
    
      pinMode(8, OUTPUT);
    
      digitalWrite(8, LOW);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      if ( Serial.available() )
      {
        byte ledState = Serial.read();
    
        if ( ledState == '1' )        digitalWrite(8, HIGH);
        else if ( ledState == '0' )   digitalWrite(8, LOW);
      }
    }

     

    서버를 먼저 실행하고, 웹 페이지에 접속해서 http://localhost:9090/websocketcomm로 접속한다.

    그리고 serial패키지의 Main.java를 실행한다.

    그리고 나서 LED의 On/Off 버튼을 클릭해보자.

     

    소스 파일

    WebSocketLED.war
    0.18MB

     

    위의 파일을 내려받아서 이클립스에서 우클릭하고 import를 하면 된다.

    그리고 나서 실행하면 된다.

    다만 실행하다가 아래와 같은 에러가 발생하면 WEB-INF/lib폴더의 RXTXcomm.jar 파일과 rxtxSerial.dll 파일을 지우고

    다시 설정을 해야 한다.

     

    댓글

Designed by Tistory.