아두이노 프로젝트 3. 시각장애인을 위한 스마트 지팡이: 두 판 사이의 차이

아두위키 : Arduwiki
편집 요약 없음
편집 요약 없음
27번째 줄: 27번째 줄:
== '''회로 구성''' ==
== '''회로 구성''' ==


 
[[파일:스마트 지팡이 회로1.png|936x936픽셀]]


== '''기능 구현''' ==
== '''기능 구현''' ==
<syntaxhighlight lang="c++" line="1">
<syntaxhighlight lang="c++" line="1">
#include <DHT.h>
/* 스마트 지팡이 프로젝트
// 사용 핀번호
사용 부품
const int dht_pin = 2;
- Arduino nano
const int ENB = 7;  //(IN3 = +, IN4 = -)
- 조도센서(cds)
- 저항(1k옴)
- 초음파 센서(HC-SR04) 2개
- 진동모터
- NeoPixel 바
- 버튼 3개
- 블루투스 모듈(HC-06)
 
기능
- 어두울 시 NeoPixel 점등
- 버튼으로 보호자 문자 발송 기능 : 블루투스 모듈로 지팡이 사용자의 스마트폰에 연결
  - 버튼 1 : 출발 문자 발송
  - 버튼 2 : 도착 문자 발송
  - 버튼 3 : 위험 및 현재 위치 문자 발송
- 초음파센서로 물체 감지
  - 초음파 센서 사양(2cm ~ 400cm 거리의 물체 감지)으로 10cm ~ 350cm 의 구간만 구별 가능하게 하였으며 350 초과시 350, 10 미만 시 10으로 측정되도록 지정
  - 알람(진동)단계별 세기 기능
  - 단위 [cm], 단계가 높아질수록 진동의 세기가 강해짐
    1단계 100 ~ 80
    2단계 80 ~ 60
    3단계 60 ~ 40
    4단계 40 ~ 20
    5단계 0 ~ 20
*/
 


#include <Adafruit_NeoPixel.h>


DHT dht(dht_pin, DHT11);
#include <SoftwareSerial.h>
unsigned long past = 0;
unsigned long past2 = 0;
const unsigned long interval = 500;  // 설정 시간때마다 스마트폰으로 현재 습도 및 온도 전송[ms]
int waterState = 0;
int hum = 0;
int temp = 0;


// 사용자 조정 필요 변수
#define trigPin_1 3  //1번초음파(상단) trig핀번호
int hum_criticalPoint = 25;          // [*]습도 센싱 경계값(본 값보다 측정된 값이 낮을 시 워터펌프 작동)
#define echoPin_1 4  //1번초음파(상단) echo핀번호
const unsigned long waterdly = 500; // [*]워터펌프 on 시간(앱에서 눌렀을 때)
#define trigPin_2 6  //2번초음파(하단) trig핀번호
#define echoPin_2 7  //2번초음파(하단) echo핀번호
#define vibratePin 5 //진동센서 핀번호(PWM)
#define cds A0        //조도센서 핀번호
#define neoPixel 2    //네오픽셀 핀번호
#define hc06Rx 10    //블루투스 rx 핀번호
#define hc06Tx 9      //블루투스 tx 핀번호
#define btn1 8        //버튼 핀번호(출발)
#define btn2 11      //버튼 핀번호(도착)
#define btn3 12      //버튼 핀번호(긴급)
#define NumPixel 23    //네오픽셀 갯수
#define bright 255    //네오픽셀 밝기


void waterOnOff(bool onOff, int val = 0) {
SoftwareSerial BTSerial(hc06Tx, hc06Rx);
  if (onOff == 0) { //off
 
    digitalWrite(ENB, LOW);
Adafruit_NeoPixel neo(NumPixel, neoPixel, NEO_GRB + NEO_KHZ800);
    Serial3.write(-10);  //거치 스마트폰 전송 구분자
 
    Serial3.write(0);   //모터상태 전송
int Btn1, Btn2, Btn3;
    waterState = 0;
 
   } else if (onOff == 1) { //on
double distance_now_top = 0;  //현재 장애물과의 거리 변수
     digitalWrite(ENB, HIGH);
double distance_now_bot = 0; //현재 장애물과의 거리 변수
     Serial3.write(-10);  //거치 스마트폰 전송 구분자
int vibratePower = 255;      //진동 세기 변수(0~255)
    Serial3.write(10);  //모터상태 전송
int vibrateTime = 150;       //진동 유지 시간[ms]
    waterState = 1;
double alert_distance = 100;  //알람작동 거리[cm]
 
int btnFlg1 = 0;
int btnFlg2 = 0;
int btnFlg3 = 0;
int btn1Chk() {
   if (digitalRead(btn1) == 0) {
     btnFlg1 = 1;
     return 0;
   }
   }
   if (val != 0) {
   if (btnFlg1 == 1) {
     waterState = val;
     btnFlg1 = 0;
    return 1;
   }
   }
  return 0;
}
}


int dhtfunc(char choice) {
int btn2Chk() {
   int hh = (int)dht.readHumidity();    // 슴도
   if (digitalRead(btn2) == 0) {
  int tt = (int)dht.readTemperature();  // 섭씨 온도
     btnFlg2 = 1;
  if (isnan(hh) || isnan(tt)) {
     return 0;
     Serial.println("Failed to read from DHT sensor!");
     waterOnOff(0);
    return -1;
   }
   }
   switch (choice) {
   if (btnFlg2 == 1) {
     case 'h':
     btnFlg2 = 0;
      return hh;
     return 1;
      break;
     case 't':
      return tt;
      break;
    default:
      break;
   }
   }
  return 0;
}
int btn3Chk() {
  if (digitalRead(btn3) == 0) {
    btnFlg3 = 1;
    return 0;
  }
  if (btnFlg3 == 1) {
    btnFlg3 = 0;
    return 1;
  }
  return 0;
}
void BTsignal() {
  if (btn1Chk() == 1) BTSerial.println('1');
  else if (btn2Chk() == 1) BTSerial.println('2');
  else if (btn3Chk() == 1) BTSerial.println('3');
}
}


void setup() {
void setup() {
   dht.begin();
   BTSerial.begin(9600);
   pinMode(ENB, OUTPUT);
  pinMode(btn1, INPUT_PULLUP);
   digitalWrite(ENB, LOW);
  pinMode(btn2, INPUT_PULLUP);
   Serial.begin(9600);
  pinMode(btn3, INPUT_PULLUP);
   Serial.println("strat");
  pinMode(cds, INPUT);
   Serial1.begin(9600); // 사용자 스마트폰
  pinMode(vibratePin, OUTPUT);
   Serial3.begin(9600); // 거치된 스마트폰
  digitalWrite(vibratePin, LOW);
  pinMode(trigPin_1, OUTPUT);
  pinMode(echoPin_1, INPUT);
   pinMode(trigPin_2, OUTPUT);
  pinMode(echoPin_2, INPUT);
  digitalWrite(trigPin_1, LOW);
   digitalWrite(trigPin_2, LOW);
   neo.begin();
   neo.setBrightness(bright);
   neo.clear();
   neo.show();
}
}


unsigned long ppast = 0;
void loop() {
void loop() {
   hum = dhtfunc('h');
   BTsignal();                                        //블루투스 모듈
   temp = dhtfunc('t');
  cdsNeo(NumPixel);                                  //조도값 낮을 시 neopixel ON
  distance_now_top = distance(trigPin_1, echoPin_1); //상단 초음파 센서 장애물 거리
   distance_now_bot = distance(trigPin_2, echoPin_2); //하단 초음파 센서 장애물 거리


   if (millis() - past2 > waterdly && waterState == 3) {
   distance_interval(distance_now_top, distance_now_bot);  //상단 초음파 센서
    waterOnOff(0);
  distance_interval(distance_now_bot, distance_now_top); //하단 초음파 센서
  }
  delay(vibrateTime);
  if (Serial1.available()) { // 사용자 스마트폰 수신 파트
}
    int dump = Serial1.read();
    waterOnOff(1, 3);
    past2 = millis();
  }


  // 습도 기준 워터펌프 작동 부분
void distance_interval(double dis_cm, int dis_cm_otherside) {
   if (hum == -1 || temp == -1) {
   if (dis_cm < dis_cm_otherside) {
     // 사용자 스마트폰 전송
     if (dis_cm < alert_distance / 5) {  //0~20cm
    Serial1.write(-1);   // 전송 시작 구분자
      analogWrite(vibratePin, vibratePower);
     Serial1.write(-99); // 습도 전송
     } else if (dis_cm < alert_distance / 5 * 2) { //20~40cm
    Serial1.write(-99); // 섭씨 온도 전송
      analogWrite(vibratePin, vibratePower / 5 * 4);
 
     } else if (dis_cm < alert_distance / 5 * 3) {  //40~60cm
     // 거치 스마트폰 전송
      analogWrite(vibratePin, vibratePower / 5 * 3);
    Serial3.write(-1);     // 전송 시작 구분자
     } else if (dis_cm < alert_distance / 5 * 4) //60~80cm
     Serial3.write(-99);    // 습도 전송
      analogWrite(vibratePin, vibratePower / 5 * 2);
    Serial3.write(-99);     // 섭씨 온도 전송
     } else if (dis_cm < alert_distance) {  //80~100cm
     if (waterState == 0) {  // 모터 상태 전송
       analogWrite(vibratePin, vibratePower / 5);
       Serial3.write(-10);
      Serial3.write(0);
     } else {
     } else {
       Serial3.write(-10);
       analogWrite(vibratePin, 0);
      Serial3.write(10);
     }
     }
     past = millis();
  }
}
 
void cdsNeo(int i) {
  int val = analogRead(cds);
  if (val > 950) {
     for (int j = 0; j < i; j++)
      neo.setPixelColor(j, bright, bright, bright);
    neo.show();
   } else {
   } else {
     if (hum < hum_criticalPoint && waterState == 0) waterOnOff(1);
     for (int j = 0; j < i; j++)
     if (hum > hum_criticalPoint && waterState == 1) waterOnOff(0);
      neo.setPixelColor(j, 0, 0, 0);
     neo.show();
   }
   }
}




  // 스마트폰 통신 부분
double distance(int trig, int echo) {
  if ((millis() - past) >= interval) { // 스마트폰으로 현재 습도 및 온도 전송(interval 마다 반복)
  digitalWrite(trig, HIGH);
    // 사용자 스마트폰 전송
  delayMicroseconds(10);
    Serial1.write(-1);   // 전송 시작 구분자
   digitalWrite(trig, LOW);
    Serial1.write(hum);  // 습도 전송
  double pulseTime = pulseIn(echo, HIGH);
    Serial1.write(temp); // 섭씨 온도 전송
  double distance_cm = pulseTime * 17.33 / 1000;
 
  if (distance_cm > 350) {
    // 거치 스마트폰 전송
    return 350;
    Serial3.write(-1);     // 전송 시작 구분자
  } else if (distance_cm < 10) {
    Serial3.write(hum);    // 습도 전송
    return 10;
    Serial3.write(temp);   // 섭씨 온도 전송
    if (waterState == 0) { // 모터 상태 전송
      Serial3.write(-10);
      Serial3.write(0);
    } else {
      Serial3.write(-10);
      Serial3.write(10);
    }
    past = millis();
   }
   }
   delay(5);
   return distance_cm;
}
}
</syntaxhighlight>
</syntaxhighlight>


=== 자동 급수 기능 ===
=== 장애물 감지 시 진동 기능 ===
DHT11 온습도 센서를 활용해 온습도 정보를 받고, 이 값을 기준으로 워터 펌프의 작동 여부를 판단합니다.
[[초음파 센서(HC-SR04)]]를 상단과 하단에 각각 하나씩 부착하여 장애물 감지 거리 구간에 따라 부착된 [[소형 진동 모터 1027|소형 진동 모터]]의 세기를 조절합니다.
 
워터 펌프 작동 여부가 어플리케이션과 거치용 디스플레이에 표시되어야 하기 때문에 해당하는 신호를 보내줍니다.


펌프가 가동되면 물이 미리 연결된 관을 타고 식물로 이동합니다.
[[소형 진동 모터 1027|소형 진동 모터]]의 경우 지팡이의 손잡이에 부착하여 진동 세기가 체감되도록 합니다.


=== 거치용 디스플레이 ===
=== 거치용 디스플레이 ===

2024년 5월 24일 (금) 16:32 판


개요

시각장애인을 위한 스마트 지팡이

시각장애인이 사용하는 지팡이에 편의 및 안전 기능을 추가하여 보다 유용한 지팡이를 제작합니다.

고려사항

  • 초음파 센서를 활용해 장애물이 가까워질수록 더 강하게 진동합니다.
  • 주변이 어두워질 경우 지팡이에 부착된 네오픽셀이 자동으로 점등합니다.
  • 블루투스로 안드로이드 앱과 지팡이를 연결한 후, 지팡이의 버튼을 누르면 앱에 설정된 문자로 "목적지로 출발", "목적지에 도착", "현재 위치"를 문자로 전송합니다.

사용 하드웨어


회로 구성

기능 구현

/* 스마트 지팡이 프로젝트
사용 부품
- Arduino nano
- 조도센서(cds)
- 저항(1k옴)
- 초음파 센서(HC-SR04) 2개
- 진동모터
- NeoPixel 바
- 버튼 3개
- 블루투스 모듈(HC-06)

기능
- 어두울 시 NeoPixel 점등
- 버튼으로 보호자 문자 발송 기능 : 블루투스 모듈로 지팡이 사용자의 스마트폰에 연결
  - 버튼 1 : 출발 문자 발송
  - 버튼 2 : 도착 문자 발송
  - 버튼 3 : 위험 및 현재 위치 문자 발송
- 초음파센서로 물체 감지
  - 초음파 센서 사양(2cm ~ 400cm 거리의 물체 감지)으로 10cm ~ 350cm 의 구간만 구별 가능하게 하였으며 350 초과시 350, 10 미만 시 10으로 측정되도록 지정
  - 알람(진동)단계별 세기 기능
  - 단위 [cm], 단계가 높아질수록 진동의 세기가 강해짐
    1단계 100 ~ 80
    2단계 80 ~ 60
    3단계 60 ~ 40
    4단계 40 ~ 20
    5단계 0 ~ 20
*/


#include <Adafruit_NeoPixel.h>

#include <SoftwareSerial.h>

#define trigPin_1 3   //1번초음파(상단) trig핀번호
#define echoPin_1 4   //1번초음파(상단) echo핀번호
#define trigPin_2 6   //2번초음파(하단) trig핀번호
#define echoPin_2 7   //2번초음파(하단) echo핀번호
#define vibratePin 5  //진동센서 핀번호(PWM)
#define cds A0        //조도센서 핀번호
#define neoPixel 2    //네오픽셀 핀번호
#define hc06Rx 10     //블루투스 rx 핀번호
#define hc06Tx 9      //블루투스 tx 핀번호
#define btn1 8        //버튼 핀번호(출발)
#define btn2 11       //버튼 핀번호(도착)
#define btn3 12       //버튼 핀번호(긴급)
#define NumPixel 23    //네오픽셀 갯수
#define bright 255    //네오픽셀 밝기

SoftwareSerial BTSerial(hc06Tx, hc06Rx);

Adafruit_NeoPixel neo(NumPixel, neoPixel, NEO_GRB + NEO_KHZ800);

int Btn1, Btn2, Btn3;

double distance_now_top = 0;  //현재 장애물과의 거리 변수
double distance_now_bot = 0;  //현재 장애물과의 거리 변수
int vibratePower = 255;       //진동 세기 변수(0~255)
int vibrateTime = 150;        //진동 유지 시간[ms]
double alert_distance = 100;  //알람작동 거리[cm]

int btnFlg1 = 0;
int btnFlg2 = 0;
int btnFlg3 = 0;
int btn1Chk() {
  if (digitalRead(btn1) == 0) {
    btnFlg1 = 1;
    return 0;
  }
  if (btnFlg1 == 1) {
    btnFlg1 = 0;
    return 1;
  }
  return 0;
}

int btn2Chk() {
  if (digitalRead(btn2) == 0) {
    btnFlg2 = 1;
    return 0;
  }
  if (btnFlg2 == 1) {
    btnFlg2 = 0;
    return 1;
  }
  return 0;
}

int btn3Chk() {
  if (digitalRead(btn3) == 0) {
    btnFlg3 = 1;
    return 0;
  }
  if (btnFlg3 == 1) {
    btnFlg3 = 0;
    return 1;
  }
  return 0;
}


void BTsignal() {
  if (btn1Chk() == 1) BTSerial.println('1');
  else if (btn2Chk() == 1) BTSerial.println('2');
  else if (btn3Chk() == 1) BTSerial.println('3');
}

void setup() {
  BTSerial.begin(9600);
  pinMode(btn1, INPUT_PULLUP);
  pinMode(btn2, INPUT_PULLUP);
  pinMode(btn3, INPUT_PULLUP);
  pinMode(cds, INPUT);
  pinMode(vibratePin, OUTPUT);
  digitalWrite(vibratePin, LOW);
  pinMode(trigPin_1, OUTPUT);
  pinMode(echoPin_1, INPUT);
  pinMode(trigPin_2, OUTPUT);
  pinMode(echoPin_2, INPUT);
  digitalWrite(trigPin_1, LOW);
  digitalWrite(trigPin_2, LOW);
  neo.begin();
  neo.setBrightness(bright);
  neo.clear();
  neo.show();
}

void loop() {
  BTsignal();                                         //블루투스 모듈
  cdsNeo(NumPixel);                                   //조도값 낮을 시 neopixel ON
  distance_now_top = distance(trigPin_1, echoPin_1);  //상단 초음파 센서 장애물 거리
  distance_now_bot = distance(trigPin_2, echoPin_2);  //하단 초음파 센서 장애물 거리

  distance_interval(distance_now_top, distance_now_bot);  //상단 초음파 센서
  distance_interval(distance_now_bot, distance_now_top);  //하단 초음파 센서
  delay(vibrateTime);
}

void distance_interval(double dis_cm, int dis_cm_otherside) {
  if (dis_cm < dis_cm_otherside) {
    if (dis_cm < alert_distance / 5) {  //0~20cm
      analogWrite(vibratePin, vibratePower);
    } else if (dis_cm < alert_distance / 5 * 2) {  //20~40cm
      analogWrite(vibratePin, vibratePower / 5 * 4);
    } else if (dis_cm < alert_distance / 5 * 3) {  //40~60cm
      analogWrite(vibratePin, vibratePower / 5 * 3);
    } else if (dis_cm < alert_distance / 5 * 4) {  //60~80cm
      analogWrite(vibratePin, vibratePower / 5 * 2);
    } else if (dis_cm < alert_distance) {  //80~100cm
      analogWrite(vibratePin, vibratePower / 5);
    } else {
      analogWrite(vibratePin, 0);
    }
  }
}

void cdsNeo(int i) {
  int val = analogRead(cds);
  if (val > 950) {
    for (int j = 0; j < i; j++)
      neo.setPixelColor(j, bright, bright, bright);
    neo.show();
  } else {
    for (int j = 0; j < i; j++)
      neo.setPixelColor(j, 0, 0, 0);
    neo.show();
  }
}


double distance(int trig, int echo) {
  digitalWrite(trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(trig, LOW);
  double pulseTime = pulseIn(echo, HIGH);
  double distance_cm = pulseTime * 17.33 / 1000;
  if (distance_cm > 350) {
    return 350;
  } else if (distance_cm < 10) {
    return 10;
  }
  return distance_cm;
}

장애물 감지 시 진동 기능

초음파 센서(HC-SR04)를 상단과 하단에 각각 하나씩 부착하여 장애물 감지 거리 구간에 따라 부착된 소형 진동 모터의 세기를 조절합니다.

소형 진동 모터의 경우 지팡이의 손잡이에 부착하여 진동 세기가 체감되도록 합니다.

거치용 디스플레이

거치용 디스플레이는 안드로이드 기기를 활용하며 아두이노와는 HC-06 모듈을 활용해 블루투스로 연결됩니다.

아두이노 코드에는 거치용 디스플레이로 데이터를 전송하는 코드가 포함됩니다.


블루투스로 전달받은 온도, 습도, 모터 작동 여부를 표시하는 어플리케이션을 앱 인벤터를 활용하여 간단하게 제작했습니다.

사용자용 기기

사용자용 기기 역시 안드로이드 기기를 활용하며 아두이노와는 HC-06 모듈을 활용해 블루투스로 연결됩니다.

아두이노 코드에 사용자용 기기로 데이터를 전송하는 코드가 포함됩니다.


사용자 기기용 어플리케이션을 앱 인벤터를 활용하여 제작했습니다.

어플리케이션에는 온습도 표시, 모터 가동 버튼, 음성인식(급수, 온습도 확인) 기능이 포함됩니다.

결과

사진

Comment

완성된 시스템을 실제 화분에 설치한 모습입니다.

화분 토양에 설치된 온습도 센서 데이터는 거치용 디스플레이와 사용자 기기로 전송됩니다.

수동 모터 작동(버튼, 음성인식) 혹은 습도 기준 미달 시 워터 펌프가 가동되어 통 안의 물이 흐르게 됩니다.

추신

공집사의 아두이노 프로젝트 결과물은 판매되는 제품이 아니며, 프로젝트 수준에서 간단하게 진행되었습니다.

문의 및 의뢰는 크몽 공집사로 연락주시면 감사하겠습니다.