[68호]일회용품을 사용하지 않는 친환경 자판기
2021 ICT 융합 프로젝트 공모전 우수상
일회용품을 사용하지 않는 친환경 자판기
(Eco-friendly Vending Machine)
글 | 대한상공회의소 서울기술교육센터 한승진, 김연진, 신상우, 유종선, 진민경
1. 심사평
칩센 최근에 여러 가지 형태로 무인음료 자판기가 설치되는 것으로 알고 있습니다. 이러한 무인 자판기는 기존과 같이 컵이 내장된 형태도 있지만, 작품과 같이 개인컵을 이용한 제품도 있는 것으로 알고 있습니다. 이미 기존에 있는 제품이라 하더라도 더 보완된 제품을 가격 경쟁력이 있게 만들 수 있다면 개발의 의미는 충분히 있습니다. 또한, 불편함이 있더라도 작품의 기획의도와 같은 목적을 보여주는 것도 좋은 방법이라 생각합니다. 이왕 echo-friendly라는 가치를 걸었다면 상용화된 1회용 컵을 구분하는 제약을 두어 제품의 목적을 분명히 밝히는 것도 좋은 방향이 아닐까 하는 생각입니다.
펌테크 친환경을 고려한 실생활과 접목된 실용성과 창의성을 지닌 작품으로 생각됩니다. 전체 하드웨어 및 소프트웨어 개발 환경 구성이 작품의 성격에 맞추어 체계적으로 적절하였다. 시스템 구성중 opencv를 사용한 영상처리를 과정 및 필요기능 위주의 심플한 형태로 짜임새 있고 깔끔하게 구성한 스마트폰용 APP은 인상적이었으며 전체적으로 기획의도, 기술 구현도, 완성도 등에서 우수한 작품이라고 생각됩니다.
위드로봇 아이디어를 프로토타입을 통해 개념을 확인한 부분이 훌륭합니다.
뉴티씨 텀블러를 사용하도록 하는 시스템은 조금 불편하더라도 환경을 지킬 수 있는 솔루션 중 하나일 수 있다는 생각이 듭니다. 나의 편리성을 위한 것이 아닌 후세대를 위한 지구 환경을 생각하여, 일회용품 사용을 줄일 수 있는 이와 같은 시스템을 좀 더 연구해야 할 것입니다.
2. 개발 배경
일회용품을 많이 사용하는 요즘, 전 세계적으로 일회용품 사용을 줄이기 위해 여러 가지 정책을 펼치고 있다. EU의 경우 올해(2021년)부터 플라스틱 세를 도입하여 플라스틱 사용 절감에 앞장서고 있다. 우리나라의 경우 커피 전문점 매장 내에서는 플라스틱 컵과 빨대를 사용할 수 없다. 또한, 슈퍼마켓에선 비닐봉지 사용이 불가하며, 대형마트에서는 포장용 테이프를 제공하지 못하게 된 상태이다.
하지만 1인 가구의 증가와 더불어 코로나 바이러스로 인한 배달음식 주문량이 많아지면서 일회용품 사용량 즉, 일회용품 배출량이 나날이 증가하고 있는 추세이다. 서울디지털재단의 ‘1인 가구 증가에 따른 일회용 플라스틱 배출 실태 분석’ 보고서에 따르면 1인 가구가 배출한 일회용품 양은 일평균 30개로 조사되었으며, 이는 다인 가구에 거주하는 1인의 배출량보다 약 2.3배 많은 양이라고 한다. 또, 서울 거주 시민 1000명을 대상으로 실시한 설문에 따르면 배달 음식 주문빈도는 코로나 발생을 기준으로 약 1.4배 증가한 것으로 조사되었다고 한다.
더불어 일회용 플라스틱 배출을 줄일 수 있는 방법에 대한 설문조사(1000명 대상)에서 ‘일회용 플라스틱 사용 저감 유도를 위한 보상체계 필요 여부’에 대해서는 응답자의 91.3%가 보상(인센티브)의 필요성을 공감하고 있는 것으로 나타났다고 한다. 보상방식으로는 ‘현금 지급’이 42.4%로 가장 높았고 이어서 ‘에코-마일리지 지급’(25.6%), ‘지역 화폐 지급’(18.3%) 등이 뒤를 이었다.
위의 보고서 내용과 마찬가지로 팀원들 모두가 일회용품 사용의 심각성과 사용 절감에 대한 필요성 모두 느끼고 있었다. 따라서 우리는 일상 속에서 일회용품을 절감할 수 있고 이에 따른 보상을 지급하는 제품을 만들기 위해 아이디어를 모았다. 그 결과 ‘일회용품을 사용하지 않는 자판기’를 개발하게 되었다.
3. 개발 목표
요즘 일회용품 사용을 절감하기 위한 사회적 움직임의 일환으로 일부 프랜차이즈 카페에서 플라스틱 컵 대신 개인 용기에 음료를 받으면 일정 금액을 할인해주고 있다. 이러한 서비스의 적용 범위를 확대하여 도서관, 스터디카페 또는 대학교, 공공기관 등 특정 사람들이 지속적으로 방문하는 곳에 배치된 자판기에 적용하여 일회용품 사용 절감에 이바지하도록 한다.
해당 자판기를 사용할 때마다 핸드폰 번호 입력을 통해 일정 금액이 에코-마일리지로 적립되며 추후에 실제로 제품이 상용화되는 경우 마일리지를 사용할 수 있도록 한다.
4. 작품 개요
일상 속에서 아두이노와 라즈베리 파이 그리고 젯슨 나노를 활용하였다.
아두이노는 음료 결제, 선택, 추출 등 자판기의 전체적인 동작을 담당하고, 젯슨 나노는 영상처리를 통해 음료 추출 전 사용자의 텀블러가 제자리에 놓여있는지 판단하였다. 라즈베리 파이는 터치스크린을 통해 자판기 내부의 진행상황을 알려주고, 자판기 이용 후 휴대폰 번호를 입력하면 에코-마일리지를 적립함과 동시에 누적된 마일리지를 확인할 수 있도록 하였다.
부가적으로 자판기 관리자를 위한 안드로이드 애플리케이션을 제작하여 자판기 내부 음료의 상태(양)를 확인하고, 음료별 누적 판매량을 통해 어느 음료가 인기가 많은지 확인 할 수 있도록 하였다.
4.1. 개발 환경 및 개발 도구 설명
4.1.1. 아두이노(Arduino)
아두이노는 다양한 스위치나 센서로부터 입력 값을 받아들이고 전자 장치들로 출력을 제어하여 디지털 장치를 만들기 위한 도구로, 간단한 마이크로컨트롤러를 기반으로 한 오픈 소스 컴퓨팅 플랫폼과 소프트웨어 개발 환경이다.
본 제품에서는 Arduino Mega 2560, Uno를 사용하여 전체적인 자판기의 동작을 구현하였다.
4.1.2. 젯슨 나노(Jetson Nano)
젯슨 나노는 엔비디아(NVDA)가 개발한 싱글 보드로 애플리케이션에서 다수의 뉴럴 네트워크를 병렬로 실행하게 해주는 강력한 소형 컴퓨터이다.
본 제품에서는 이와 같은 개발 환경에서 우분투의 vi 문서 편집기를 통해 언어 PYTHON으로 코드를 구현하고 실행 파일을 생성하였다.
4.1.3. 라즈베리 파이(Raspberry Pi)
라즈베리 파이는 영국 잉글랜드의 라즈베리 파이 재단이 학교와 개발도상국에서 기초 컴퓨터 과학의 교육을 증진하기 위해 개발한 신용카드 크기의 싱글 보드 컴퓨터이다.
라즈베리 파이 OS를 실행할 때, 기본 터미널 애플리케이션은 LXTerminal이다. 해당 프롬프트에서 리눅스 명령어를 기반으로 vi 에디터 또는 문서 편집기를 이용해 실행 프로그램을 생성할 수 있다. 본 제품에서는 html과 언어 php를 사용하여 웹 파일을 생성하였다.
4.1.4. 안드로이드(Android)
안드로이드는 휴대전화의 운영체제, 미들웨어, 사용자 인터페이스, 응용프로그램 등을 묶은 소프트웨어 플랫폼이다. 리눅스(Linux)2.6 커널 위에서 동작하며 운영체제, 라이브러리, 멀티미디어 사용자 인터페이스, 애플리케이션 등을 제공한다.
안드로이드 프로그래밍 언어로는 JAVA와 Kotlin이 있다. 또한, 안드로이드 스튜디오는 안드로이드 앱을 빌드할 때 생산성을 높여주도록 다양한 기능을 제공한다. 본 제품에서는 프로그래밍 언어로 JAVA를 사용하였다.
Android Emulator의 각 인스턴스는 Android Virtual Device(AVD)를 사용하여 시뮬레이션된 기기의 Android 버전과 하드웨어 특성을 지정한다. 효과적으로 앱을 테스트하려면 앱이 실행될 각 기기를 모델링하는 AVD를 만들어야 한다. AVD를 만들고 관리하려면 AVD Manager를 사용하면 된다.
4.2. 작품 구성
4.2.1. 부품 리스트
부품명 | 디바 상품 번호 |
물높이(수위) 센서 스위치 | |
PLEOMAX W-210 PC캠 화상캠 웹캠 | |
라즈베리파이 5인치 HDMI LCD 터치스크린 모니터 | 1382229 |
아두이노 워터펌프 모터 3~5V | |
아두이노 워터펌프용 실리콘 튜브(1m) | |
아두이노 4채널 5V 릴레이 모듈 | 1327545 |
아두이노 WIFI ESP8266 | 1279338 |
아두이노 RFID-RC522 리더기 | 1279308 |
아두이노 수동 부저 모듈 | 10916342 |
아두이노 LED | |
아두이노 6×6 택트 스위치 | |
아두이노 우노 Uno R3 호환보드 | 1245596 |
아두이노 메가 2560 호환보드 | 10918650 |
NVIDIA Jetson Nano Development Kit-B01 | 12513656 |
라즈베리파이4 (Raspberry Pi 4 Model B) 2GB | 12234533 |
4.2.2. 회로도
4.2.4. 작품 구상도
5. 작품 설명
5.1. 작품 제작 및 동작
5.1.1. 소켓 및 스레드 통신
자판기에 사용되는 장치들 간의 통신을 위해 소켓을 사용하여 서버를 구축하고 각각의 장치(아두이노, 젯슨 나노, 라즈베리 파이)들이 클라이언트가 되도록 하였다. 서버를 구축하는 데 있어서 C, PYTHON, JAVA 등의 언어 중 JAVA를 사용하였다.
스레드를 사용하지 않으면 여러 클라이언트의 접속이 불가하고, 다른 동작을 실행할 수 없다. 따라서 멀티 스레드를 사용하였고 초기에는 서버 하나에 여러 개의 클라이언트가 붙도록 구현하였다.
모든 클라이언트는 접속 시에 자신의 아이디를 서버에 전송한다. 이를 토대로 서버에서는 등록된 아이디 리스트와 접속 아이디를 비교, 검증한 후 클라이언트의 접속을 허용한다. 또한, 접속된 아이디를 기준으로 클라이언트 간 메시지를 보낼 수 있도록 하였다.
5.1.2. 아두이노(Arduino)
음료 결제
자판기의 결제 기능을 구현하기 위해 RFID 리더기 모듈을 사용하여 자판기 사용자의 카드가 인식되도록 하였다. 이후 전체 동작을 위해 RFID 리더기에 카드가 인식되면 ‘음료 선택’ 동작으로 넘어가도록 하였다. Arduino Mega 2560(이후 Mega로 칭함.)에서 Arduino Uno(이후 Uno로 칭함.)로 시리얼 통신으로 일정 값을 보내주고 Uno에서 값을 판단하여 재전송 하도록 한다.
음료 선택
사용자가 상황에 맞게 음료를 선택할 수 있도록 버튼과 LED를 구현하였다. LED는 현재 해당 음료를 선택할 수 있다는 신호이며, 버튼은 음료를 추출하도록 한다. 서버에서 결제가 되었다는 값을 전달 받으면 LED를 점등함과 동시에 버튼 기능을 활성화한다. 그리고 사용자가 버튼을 누를 시 해당 버튼에 맞는 음료의 정보를 Mega에서 시리얼 통신으로 Uno에게 메시지를 전송하여 ‘음료 추출’ 동작이 진행되도록 하였다.
음료 추출
음료를 추출하기 위해서 워터 펌프 모듈을 사용하였다. 워터 펌프 모듈의 불안정한 작동을 보안하기 위해 릴레이 모듈을 추가하였다. Mega에서 사용자가 선택한 음료 정보를 시리얼 통신으로 Uno에게 넘겨주고 음료 정보와 일치하는 워터 펌프를 작동시킨다. 음료 추출이 끝나면 부저를 통해 종료 음을 출력한다.
이후 모든 동작이 완료되면 언제든 사용자의 카드를 읽을 수 있는 초기 상태(결제 대기 상태)로 되돌린다.
음료 잔량 확인
플로트 스위치 센서를 사용하여 음료 잔량을 확인하도록 하였다. 부력으로 음료 표면에 떠 있다가 바닥에 닿으면 음료가 부족하다고 판단하여 LED를 소등한다. 그리고 사용자가 음료 선택 버튼을 눌러도 동작하지 않도록 하였다.
관리자가 애플리케이션을 통해 음료 잔량 정보를 요청하면 현재 모든 음료의 수위 센서 상태를 읽어 서버를 통해 애플리케이션에 전달한다.
매출 관리
애플리케이션에서 자판기 누적 판매량 기능을 구현하기 위해 음료 펌프가 작동된 횟수를 Mega의 비휘발성 메모리인 EEPROM에 저장하도록 하였다.
관리자가 애플리케이션을 통해 누적 판매량 정보를 요청하면 EEPROM에 저장된 값을 읽어 서버를 통해 애플리케이션에 전달한다.
5.1.3. 젯슨 나노(Jetson Nano)
컵 인식
먼저 Window 환경에서 opencv와 tensorflow를 사용하여 자판기 사용자가 놓은 컵을 인식하도록 하였다. 컵이 인식되면 웹캠을 종료하고 사진을 찍도록 하였다.
코드의 정상 동작 여부를 확인하고 젯슨 나노에서 개발 환경을 구축한 후 멀티 스레드를 사용하여 Mega에서 전송한 ‘음료 결제’ 메시지를 받으면 카메라를 동작시켜 컵을 인식하도록 하였다. 컵이 인식되면 Mega로 완료 메시지를 보내 계속해서 자판기의 동작을 수행한다. 컵이 인식되지 않으면 자판기의 동작을 중단하고 환불 페이지로 이동한다.
5.1.4. 라즈베리 파이(Raspberry Pi)
진행 상황 출력
라즈베리 파이에 부착된 터치스크린을 통하여 자판기 동작 진행상황 및 이용 순서에 맞게 웹 페이지를 띄워 사용자에게 알려준다.
에코-마일리지 적립
음료 추출이 종료되면 바로 에코-마일리지 적립 페이지로 이동한다. 자신의 전화번호를 입력하면 일정 금액의 마일리지가 적립된다. 입력을 완료하면 방금 적립된 마일리지와 누적된 마일리지 모두를 확인 할 수 있고 자동으로 페이지가 종료된다.
사용자가 입력한 정보는 MariaDB를 통해 관리한다.
5.1.5. 안드로이드(Android)
하단 탭
BottomNavigationView와 Fragment를 사용하여 하단 탭을 생성하였다. 하단 탭 클릭을 통해 ‘상태’ 페이지와 ‘판매량’ 페이지로 이동한다.
로그인(서버 접속)
메인 화면 상단에 로그인을 위한 옵션 버튼을 추가하였다. 해당 버튼을 선택하면 로그인 창을 띄운다.
해당 창에서 서버 접속(로그인)을 위한 IP, PORT, ID, PASSWORD를 입력한다. 미리 설정한 값이 기본 값으로 입력되어 있어 보다 편리하게 접속할 수 있다.
음료 잔량 확인
자판기 내부 음료의 잔량을 자판기 관리자가 수시로 확인 할 수 있도록 ‘상태’ 페이지 안에 CONDITION 버튼을 추가했다. 버튼을 누르면 서버를 통해 자판기 내부의 MCU에 특정 메시지를 전송하여 음료의 잔량을 요청한다.
자판기 내부의 음료가 충분하다면 아래 사진과 같이 음료 아이콘의 테두리를 강조 해준다.
음료 누적 판매량 확인
‘판매량’ 페이지의 초기 화면은 아래와 같이 ‘?개’로 나타낸다. RENEWAL 버튼을 누르면 서버를 통해 특정 메시지를 전송하여 음료 각각의 누적 판매량을 보여준다.
RESET 버튼을 누르게 되면 서버를 통해 판매량을 초기화 해달라는 메시지를 MCU에게 전송하여 각 음료의 누적 판매량을 모두 0개로 초기화한다.
6. 전체 흐름도
7. 최종구현
8. 시연
9. 결과 및 향후 목표
9.1. 결과
자판기에서 일회용품 사용을 줄이기 위해 초기에 기획한 기능으로 영상처리를 통한 텀블러 인식, 에코-마일리지 적립 등을 모두 구현하였다. 정상적으로 모든 기능이 동작하였지만 몇 가지의 아쉬운 점이 발생하였다.
먼저, 음료의 잔량을 측정하는 플로트 스위치 센서의 동작이 불안정하였다. 무게 센서나 무접점 수위 센서를 사용했으면 보다 안정된 동작을 구현할 수 있을 것이라 생각하였다. 다음으로 영상처리 부분에서 오픈 소스를 활용하다보니 필요 이상의 학습데이터로 인해 프로세서의 부담이 커져 텀블러 인식 속도에 영향을 미쳤다. 머신러닝을 접목하여 직접 필요한 이미지만 학습시킨다면 인식 속도를 개선할 수 있을 것이라 생각한다. 마지막으로 텀블러 크기에 따라 음료 추출량을 조절할 수 있는 방법에 대해 생각해보려 한다.
9.2. 향후 목표
본 제품은 소켓 통신을 하기 위해 와이파이를 사용한다. 하지만 와이파이를 대체할 수 있는 IoT 전용망이 전국적으로 구축되고 있다. 이는 유지비용 측면에서 최소 350원에서 최대 2,000원 정도로 저렴한 요금을 제공한다. 또 넓은 커버리지를 제공하여 본 제품이 상용화된다면 IoT 전용망을 통해 비용을 절감할 수 있다.
현재 애플리케이션은 자판기 관리자용으로 한정적인 기능을 제공한다. 이를 보완하기 위해 다양한 오픈 API를 사용할 수 있다. 예를 들면 구글 MAP API를 통해 자판기의 위치 정보를 제공하여 사용자는 보다 쉽게 자판기 이용이 가능하고, 관리자는 용이하게 유지 보수를 할 수 있다.
해당 자판기는 현재 일회용품을 사용하지 않는 것이 유일한 친환경적인 요소이다. 이를 확장하여 태양열 발전을 통한 전기 사용 등 친환경적인 요소를 추가적으로 접목시켜 환경 보호에 더욱 기여할 수 있다.
10. 참고자료
· 파이낸셜 뉴스, “1인 가구 플라스틱”, https://www.fnnews.com/news/202009181711464621
· 땅오니 로봇 코딩 블로그, “TCP/IP 소켓 통신”, https://ddangeun.tistory.com/31
· 멈춤보단 천천히라도, “파이썬 소켓”, https://webnautes.tistory.com/1381
· IT, 정보보안 자료실, “파이썬 멀티쓰레드”, https://nalara12200.tistory.com/153
· 명월 일지, “소켓통신”, https://nowonbun.tistory.com/315
· 토이메이커스, “아두이노 피에조 스피커“, https://blog.naver.com/yulian/221748202220
· 뤼즈나의 IT 블로그, “아두이노 워터 펌프”, https://in-reason.tistory.com/16
· 하이! 제니스, “아두이노 RFID”, https://m.blog.naver.com/chandong83/220920789808
· 에듀이노 코딩 스쿨, “ESP8266″, https://blog.naver.com/PostView.nhn?blogId=eduino&logNo=221152914869
· 폴나의 공방, “아두이노 시리얼 통신”, https://m.blog.naver.com/darknisia/221234187170
· 몽구스 프로그래밍, “아두이노 인터럽트”, https://m.blog.naver.com/PostView.nhn?blogId=yuyyulee&logNo=220310875023&proxyReferer=https:%2F%2Fwww.google.com%2F
· YouTube, “아두이노 RFID”, https://www.youtube.com/watch?v=SQ IGilMagm0
· 테크월드 뉴스, “젯슨 나노”, http://www.epnc.co.kr/news/articleView.html?idxno=95313
· MANUAL FACTORY, “우분투 설치”, https://www.manualfactory.net/13400
· DoProgramming, “젯슨나노”, https://doprogramming.tistory.com/category/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/Jetson%20Nano
· 제너럴공국, “젯슨나노 텐서플로우”, https://generalthird.tistory.com/m/15
· 멈춤보단 천천히라도, “opencv”, https://webnautes.tistory.com/1186
· 개발하는 도치, “Seleninum”, https://heodolf.tistory.com/48
· 삶의 향기, “html 창 닫기”, https://lia47.tistory.com/995
· 안드로이드 스튜디오, Android Emulator, https://developer.android.com/studio/run/emulator?hl=ko
· Bugwhale World, 라즈베리파이 라즈비안 APM 설치하기 https://bugwhale.tistory.com/entry/raspberrypi-raspbian-apm-install
· 정재곤, 「Do it! 안드로이드 앱 프로그래밍」, 이지스퍼블리싱, 2020
11. 소스코드
MCU(아두이노) : MAGA
#include <SoftwareSerial.h>
#include “WiFiEsp.h”
#include <TimerOne.h>
#include <Wire.h>
#include <EEPROM.h>
#include <SPI.h> // RFID를 위한 SPI 라이브러리
#include <MFRC522.h>// RFID 라이브러리
#define DEBUG
#define DEBUG_WIFI
#define AP_SSID “embsystem2″
#define AP_PASS “embsystem20″
#define SERVER_NAME “192.168.1.20″
#define SERVER_PORT 5000
#define LOGID “ECO_ARD”
#define PASSWD “PASSWD”
#define CMD_SIZE 50
#define ARR_CNT 5
#define SS_PIN 53 //RFID SS(SDA:ChipSelect) PIN
#define RST_PIN 5 //RFID Reset PIN
#define led1 10
#define led2 9
#define led3 8
#define WLV_PIN1 A0
#define WLV_PIN2 A1
#define WLV_PIN3 A2
//SoftwareSerial Serial2(6, 7);
MFRC522 mfrc(SS_PIN, RST_PIN); //RFID 라이브러리
//MFRC522::MIFARE_Key key;
char eepromPw[5]; //(eeprom에 저장된)비밀번호
char sendBuf[CMD_SIZE];
bool timerIsrFlag = false;
unsigned int secCount;
int tag;
WiFiEspClient client;
int drinkslt = 0; // 음료 선택
boolean sltflag = false; // ok 수신 플래그
int CokeCount;
int FantaCount;
int SpriteCount;
int watervalue1;
int watervalue2;
int watervalue3;
int waterlv1 = 1;
int waterlv2 = 1;
int waterlv3 = 1;
int buzzflag = 0; // 부저
int Checkflag = 0; // RFID
char received;
void isr1()
{
drinkslt = 1;
// Serial2.write(“a”);
}
void isr2()
{
drinkslt = 2;
// secCount2 = 0;
// Serial2.write(“b”);
}
void isr3()
{
drinkslt = 3;
// secCount3 = 0;
// Serial2.write(“c”);
}
void setup() {
Serial.begin(9600); //DEBUG
Serial2.begin(9600);
wifi_Setup();
SPI.begin(); // SPI 시작
mfrc.PCD_Init(); // RF 모듈 시작
attachInterrupt(3, isr1, RISING); //20번
attachInterrupt(0, isr2, RISING); //2번
attachInterrupt(1, isr3, RISING); //3번
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
digitalWrite(led3, LOW);
Timer1.initialize(1000000);
Timer1.attachInterrupt(timerIsr); // timerIsr to run every 1 seconds
readEeprom();
}
void loop() {
if (client.available()) {
socketEvent();
}
if (timerIsrFlag)
{
timerIsrFlag = false;
if (!(secCount % 5))
{
if (!client.connected()) {
server_Connect();
}
}
}
//카드가 인식 안되었다면 더이상 진행하지 말고 빠져나감
if (mfrc.PICC_IsNewCardPresent())
{
if (mfrc.PICC_ReadCardSerial())
{
for (byte i = 0; i < mfrc.uid.size; i++) {
Serial.print(mfrc.uid.uidByte[i] < 0×10 ? ” 0″ : ” “);
Serial.print(mfrc.uid.uidByte[i], DEC);
}
Serial.println();
Serial2.write(‘x’);
delay(500);
mfrc.PICC_HaltA();
}
}
if (waterlv1 && drinkslt == 1)
{
if ( sltflag )
{
Serial2.write(‘a’);
delay(500);
digitalWrite(led2, LOW);
digitalWrite(led3, LOW);
sprintf(sendBuf , “[ECO_WEB]chooseok\n”);
client.write(sendBuf, strlen(sendBuf));
sltflag = false;
CokeCount++;
EEPROM.write(1, CokeCount);
drinkslt = 0;
}
}
else if (waterlv2 && drinkslt == 2)
{
if ( sltflag )
{
Serial2.write(‘b’);
delay(500);
digitalWrite(led1, LOW);
digitalWrite(led3, LOW);
sprintf(sendBuf , “[ECO_WEB]chooseok\n”);
client.write(sendBuf, strlen(sendBuf));
sltflag = false;
FantaCount++;
EEPROM.write(2, FantaCount);
drinkslt = 0;
}
}
else if (waterlv3 && drinkslt == 3)
{
if ( sltflag )
{
Serial2.write(‘c’);
delay(500);
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
sprintf(sendBuf , “[ECO_WEB]chooseok\n”);
client.write(sendBuf, strlen(sendBuf));
sltflag = false;
SpriteCount++;
EEPROM.write(3, SpriteCount);
drinkslt = 0;
}
}
if (Serial2.available())
{
received = Serial2.read();
Serial.println(received);
if (received == ‘x’)
{
sprintf(sendBuf , “[ECO_CAM]camon\n”);
client.write(sendBuf, strlen(sendBuf));
sprintf(sendBuf , “[ECO_WEB]camon\n”);
client.write(sendBuf, strlen(sendBuf));
drinkslt = 0;
}
else if (received == ‘o’)
{
digitalWrite(led3, LOW);
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
delay(100);
}
}
}
void socketEvent()
{
int i = 0;
char * pToken;
char * pArray[ARR_CNT] = {0};
char recvBuf[CMD_SIZE] = {0};
int len;
Serial.print(“socket ok “);
sendBuf[0] = ”;
len = client.readBytesUntil(‘\n’, recvBuf, CMD_SIZE);
client.flush();
#ifdef DEBUG
Serial.print(“recv : “);
Serial.println(recvBuf);
#endif
pToken = strtok(recvBuf, “[@]“);
while (pToken != NULL)
{
pArray[i] = pToken;
if (++i >= ARR_CNT)
break;
pToken = strtok(NULL, “[@]“);
}
if (!strncmp(pArray[1], ” New”, 4)) // New Connected
{
Serial.write(‘\n’);
return ;
}
else if (!strncmp(pArray[1], ” Alr”, 4)) //Already logged
{
Serial.write(‘\n’);
client.stop();
server_Connect();
return ;
}
else if (!strncmp(pArray[1], “camok”, 5)) //rfid ok
{
waterlv_check();
sltflag = true;
drinkslt = 0;
digitalWrite(led1, HIGH);
digitalWrite(led2, HIGH);
digitalWrite(led3, HIGH);
if (!waterlv1)
digitalWrite(led1, LOW);
if (!waterlv2)
digitalWrite(led2, LOW);
if (!waterlv3)
digitalWrite(led3, LOW);
if (!waterlv1 && !waterlv2 && !waterlv3)
{
Serial2.write(‘d’);
sprintf(sendBuf, “[ECO_WEB]empty\n”);
client.write(sendBuf, strlen(sendBuf));
}
return ;
}
else if (!strncmp(pArray[1], “error”, 5)) //Error
{
Serial.write(‘\n’);
Serial2.write(‘d’);
return ;
}
else if (!strncmp(pArray[1], “STATE”, 5)) //State
{
waterlv_check();
sprintf(sendBuf, “[ECO_APP]DRINK@%d@%d@%d\n”, waterlv1, waterlv2, waterlv3);
client.write(sendBuf, strlen(sendBuf));
return;
}
else if (!strncmp(pArray[1], “RENEWAL”, 7)) //Renewal
{
sprintf(sendBuf, “[ECO_APP]SELL@%d@%d@%d\n”, CokeCount, FantaCount, SpriteCount);
client.write(sendBuf, strlen(sendBuf));
return;
}
else if (!strncmp(pArray[1], “RESET”, 5)) //Count Reset
{
CokeCount = 0;
EEPROM.write(1, CokeCount);
FantaCount = 0;
EEPROM.write(2, FantaCount);
SpriteCount = 0;
EEPROM.write(3, SpriteCount);
sprintf(sendBuf, “[ECO_APP]SELL@%d@%d@%d\n”, CokeCount, FantaCount, SpriteCount);
client.write(sendBuf, strlen(sendBuf));
return;
}
else {
sprintf(sendBuf, “[%s]%s@%s\n”, pArray[0], pArray[1], pArray[2]);
}
client.write(sendBuf, strlen(sendBuf));
client.flush();
#ifdef DEBUG
Serial.print(“, send : “);
Serial.print(sendBuf);
#endif
}
void timerIsr()
{
timerIsrFlag = true;
secCount++;
}
void wifi_Setup() {
Serial3.begin(9600);
wifi_Init();
server_Connect();
}
void wifi_Init()
{
do {
WiFi.init(&Serial3);
if (WiFi.status() == WL_NO_SHIELD) {
#ifdef DEBUG_WIFI
Serial.println(“WiFi shield가 존재하지 않습니다.”);
#endif
}
else
break;
} while (1);
#ifdef DEBUG_WIFI
Serial.print(“WiFi 연결을 시도합니다.”);
//Serial.println(AP_SSID);
#endif
while (WiFi.begin(AP_SSID, AP_PASS) != WL_CONNECTED) {
#ifdef DEBUG_WIFI
Serial.print(“WiFi 연결을 시도합니다.”);
//Serial.println(AP_SSID);
#endif
}
#ifdef DEBUG_WIFI
Serial.println(“WiFi 연결에 성공하였습니다.”);
printWifiStatus();
#endif
}
int server_Connect()
{
#ifdef DEBUG_WIFI
Serial.println(“Server 연결을 시도합니다.”);
#endif
if (client.connect(SERVER_NAME, SERVER_PORT)) {
#ifdef DEBUG_WIFI
Serial.println(“Server 연결에 성공하였습니다.”);
#endif
client.print(“["LOGID":"PASSWD"]“);
}
else
{
#ifdef DEBUG_WIFI
Serial.println(“Server 연결에 실패하였습니다.”);
#endif
}
}
void printWifiStatus()
{
// print the SSID of the network you’re attached to
//Serial.print(“SSID: “);
//Serial.println(WiFi.SSID());
// print your WiFi shield’s IP address
IPAddress ip = WiFi.localIP();
//Serial.print(“IP Address: “);
Serial.println(ip);
// print the received signal strength
long rssi = WiFi.RSSI();
//Serial.print(“Signal strength (RSSI):”);
//Serial.print(rssi);
//Serial.println(” dBm”);
}
//EEPROM에 저장된 판매량을 읽어옴
void readEeprom() {
CokeCount = EEPROM.read(1);
// Serial.print(CokeCount);
FantaCount = EEPROM.read(2);
//Serial.print(FantaCount);
SpriteCount = EEPROM.read(3);
//Serial.print(SpriteCount);
}
void waterlv_check()
{
watervalue1 = analogRead(WLV_PIN1);
watervalue2 = analogRead(WLV_PIN2);
watervalue3 = analogRead(WLV_PIN3);
Serial.println(watervalue1);
if (watervalue1 >= 1000)
{
waterlv1 = 0;
}
else
{
waterlv1 = 1;
}
Serial.println(watervalue2);
if (watervalue2 >= 1000)
{
waterlv2 = 0;
}
else
{
waterlv2 = 1;
}
Serial.println(watervalue3);
if (watervalue3 >= 1000)
{
waterlv3 = 0;
}
else
{
waterlv3 = 1;
}
}
MCU(아두이노) : UNO
#include <SoftwareSerial.h>
#include <SPI.h>
#include <TimerOne.h>
#define buzz 5
SoftwareSerial rfid(6, 7);
bool timerIsrFlag = false;
unsigned int secCount1 = 11;
unsigned int secCount2 = 11;
unsigned int secCount3 = 11;
//static boolean selectmod1 = false; //인터럽트
char receved; // serial 통신 수신 값
int Relaypin1 = 8;
int Relaypin2 = 9;
int Relaypin3 = 10;
int buzzflag = 0; // 부저
int Checkflag = 0; // RFID
void setup()
{
// put your setup code here, to run once:
Serial.begin(9600); //DEBUG
rfid.begin(9600);
SPI.begin(); // SPI 시작
pinMode(Relaypin1, OUTPUT); // 릴레이를 출력으로 설정
pinMode(Relaypin2, OUTPUT);
pinMode(Relaypin3, OUTPUT);
Timer1.initialize(1000000);
Timer1.attachInterrupt(timerIsr);
digitalWrite(Relaypin1, HIGH);
digitalWrite(Relaypin2, HIGH);
digitalWrite(Relaypin3, HIGH);
Serial.println(“연진아 준비해라”);
}
void loop() {
if (rfid.available())
{
if (Checkflag == 0)
{
receved = rfid.read();
if (receved == ‘x’)
{
Serial.println(receved);
Serial.println(“x 재송신 후 다음 동작 대기”);
rfid.write(‘x’);
Checkflag = 1;
}
}
if (Checkflag == 1)
{
receved = rfid.read();
if (receved == ‘a’)
{
secCount1 = 0;
Serial.println(receved);
Serial.println(“물펌프a 동작”);
digitalWrite(Relaypin1, LOW); // 1채널 릴레이 ON
}
else if (receved == ‘b’)
{
secCount2 = 0;
Serial.println(receved);
Serial.println(“물펌프b 동작”);
digitalWrite(Relaypin2, LOW); // 2채널 릴레이 ON
}
else if (receved == ‘c’)
{
secCount3 = 0;
Serial.println(receved);
Serial.println(“물펌프c 동작”);
digitalWrite(Relaypin3, LOW); // 3채널 릴레이 ON
}
else if (receved == ‘d’)
{
Serial.println(receved);
Checkflag = 0;
}
}
}
if (buzzflag == 1)
{
tone(buzz, 262, 500); // 도
delay(400);
tone(buzz, 330, 500); // 미
delay(400);
tone(buzz, 392, 500); // 솔
delay(400);
tone(buzz, 523, 500); // 높은 도
delay(400);
rfid.write(‘o’);
delay(500);
Checkflag = 0;
Serial.println(Checkflag);
buzzflag = 0;
}
}
void timerIsr()
{
timerIsrFlag = true;
secCount1++;
secCount2++;
secCount3++;
if (secCount1 == 10 )
{
digitalWrite(Relaypin1, HIGH); // 1채널 릴레이
buzzflag = 1;
Serial.println(buzzflag);
}
else if (secCount2 == 10 )
{
digitalWrite(Relaypin2, HIGH); // 2채널 릴레이
buzzflag = 1;
}
else if (secCount3 == 10 )
{
digitalWrite(Relaypin3, HIGH); // 3채널 릴레이
buzzflag = 1;
}
}