#include <LiquidCrystal_I2C.h>
// Address가 0x27인 20x4 I2C LCD 인스턴스(변수) 생성
LiquidCrystal_I2C lcd(0x27, 20, 4);
#define TRIG_PIN 7
#define ECHO_PIN 6
int getUltrasonicSensor(int trigPin, int echoPin);
int dist(int distance);
bool counter(int d);
void setup() {
lcd.init();
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
lcd.backlight(); // LCD 백라이트 ON
lcd.setCursor(0, 0); // 커서값을 (0,0) 위치로 이동
lcd.print("TIME"); // TIME 을 출력
lcd.setCursor(9, 0); // 커서값을 (9,0) 위치로 이동
lcd.print("Push up"); // Push up 을 출력
lcd.setCursor(9, 1); // 커서값을 (9,1) 위치로 이동
lcd.print('0'); // 0 을 출력(Push 시작 전 개수)
for (int i = 3; i >= 0; i--) // 시작 카운트다운 4초(3,2,1,0 Start)
{
lcd.setCursor(2, 1);
lcd.print(i);
delay(1000);
}
}
void loop()
{
int time; // 표기할 시간
int delay_count = 0; // delay 횟수 (delay 0.1초로 가정하고, 대략 1초당 time-- 할 용도)
int push_up_count = 0; // push_up 횟수
for (time = 60; time > 0; delay_count++) // 60초부터 0초까지 for 루프 반복 (반복 주기는 0.1초)
{
if (delay_count == 8) // delay_count 가 8 될 때마다 실행. (자체적 딜레이 0.2~0.25초로 추정되는 것 고려)
{
time--; // 시간 1초 감소
delay_count = 0; // delay_count 는 0으로 초기화
}
if (time >= 0 && time <= 9) // 시간값이 0 ~ 9 사이일때마다 실행(앞부분에 기존 숫자가 남는 것을 방지하고, 일의 자리 위치를 고정적으로 사용하기위해 별도로 구현)
{
lcd.setCursor(1, 1); // 커서값을 시간의 십의 자리 위치로 이동
lcd.print(' '); // 시간의 십의 자리 위치를 공백으로 처리(시간을 역순으로 처리하기 때문에, 9초일 때만 공백처리를 해도 무관)
lcd.setCursor(2, 1); // 커서값을 시간의 일의 자리 위치로 이동
lcd.print(time); // 시간의 일의 자리 위치에 시간을 출력
}
else { // 시간값이 일의 자리가 아닐 때, 실행
lcd.setCursor(1, 1); // 커서값을 시간의 십의 자리 위치로 이동
lcd.print(time); // 시간을 출력
}
if (counter(dist(getUltrasonicSensor(TRIG_PIN, ECHO_PIN)))) // counter가 true를 리턴했을 때 실행(푸쉬업을 한게 인정이 되었을 때)
{
push_up_count++; // push_up_count 를 추가
lcd.setCursor(9, 1); // 커서값을 push_up 개수 위치로 이동
lcd.print(push_up_count); // push_up 개수를 출력
}
delay(100); // delay 0.1초(새로고침 빈도)
}
while (1); // 60초 경과 후 정지. loop() 함수 재실행 방지차원
}
int getUltrasonicSensor(int trigPin, int echoPin) { //초음파 센서를 이용해 거리를 측정하여, mm 단위의 정수형으로 리턴
unsigned long duration;
// 10 마이크로초의 펄스 생성
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Echo 펄스의 길이를 측정
duration = pulseIn(echoPin, HIGH, 30000);
return int(duration * 0.17); // 정수형으로 변환하여 리턴
}
int dist(int distance) // 거리값을 받아, 시리얼 모니터에 출력
{
if (distance == 0) // 거리가 0 일 때 실행
{
Serial.println("Timeout Error!"); // 시리얼 모니터에 Timeout Error! 출력
}
else { // 거리가 0 이 아닐 때 실행
Serial.print(distance); // 시리얼 모니터에 거리 출력
Serial.println("mm"); // 시리얼 모니터에 mm 출력
}
return distance; // 거리값 반환
}
bool counter(int d) // 거리값을 받아 push_up 알고리즘을 구현하여 push_up 여부를 리턴해주는 함수
{
static int upper = 0; // 올라왔을 때를 카운트하는 변수
static int lower = 0; // 내려갔을 때를 카운트하는 변수
if (upper && lower) { // upper 와 lower 가 모두 1인 경우
upper = 0; // upper 를 0 으로 초기화
lower = 0; // lower 를 0 으로 초기화
}
if (d <= 150) lower++; // 거리가 150mm 보다 작거나 같으면, lower 카운트
if (lower) // lower 카운트가 되어있을 때 실행
{
if (d >= 450) // 거리가 450mm 보다 크거나 같을 때 실행
{
upper++; // upper 카운트
return true; // push_up 1회를 실행했으므로 True 리턴
}
}
else return false; // push_up 을 실행한 상황이 아니므로 False 리턴
}