From f96e01483e9341ef7e10d93a3aa9dac10fd9f1be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EB=AF=BC=EC=84=9C?= <chlalstj1@ajou.ac.kr> Date: Sat, 7 Dec 2024 15:50:56 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=B0=B0=ED=84=B0=EB=A6=AC=20=EB=9E=9C?= =?UTF-8?q?=EB=8D=A4=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EC=8B=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- charger.cpp | 423 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 250 insertions(+), 173 deletions(-) diff --git a/charger.cpp b/charger.cpp index 1bf8858..35fc97c 100644 --- a/charger.cpp +++ b/charger.cpp @@ -1,253 +1,330 @@ -#include <stdio.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <pthread.h> -#include <stdlib.h> -#include <stdint.h> -#include <linux/i2c-dev.h> -#include <arpa/inet.h> -#include <sys/ioctl.h> -#include "MFRC522.h" - -#define I2C_ADDR 0x23 -#define LCD_CHR 1 -#define LCD_CMD 0 -#define LINE1 0x80 -#define LINE2 0xC0 -#define LCD_BACKLIGHT 0x08 -#define ENABLE 0b00000100 -#define BUFFER_SIZE 1024 - -#define SERVER_IP "127.0.0.1" -#define SERVER_PORT 12345 - -int lcd_fd; -int client_socket; -int battery_percentage = 50; -int charging = 0; -int fire_detected = 0; -int car_parked = 0; - -void lcd_init(int fd); -void lcd_byte(int fd, int bits, int mode); -void lcd_toggle_enable(int fd, int bits); -void lcd_loc(int fd, int line); -void lcd_clear(int fd); -void typeln(int fd, const char *s); -void *listen_to_server(void *arg); -void *update_battery_status(void *arg); -void *rfid_tag_listener(void *arg); +#include <stdio.h> // 표준 입출력 라이브러리 +#include <string.h> // 문자열 처리 라이브러리 +#include <fcntl.h> // 파일 제어 관련 라이브러리 +#include <unistd.h> // 유닉스 표준 시스템 호출 관련 라이브러리 +#include <pthread.h> // POSIX 스레드 관련 라이브러리 +#include <stdlib.h> // 일반 유틸리티 함수 라이브러리 +#include <stdint.h> // 정수 타입 정의 라이브러리 +#include <linux/i2c-dev.h> // I2C 장치 드라이버 관련 헤더 +#include <arpa/inet.h> // 인터넷 주소 변환 함수 관련 헤더 +#include <sys/ioctl.h> // I/O 제어 함수 관련 헤더 +#include "MFRC522.h" // RFID 모듈 관련 헤더 파일 (사용자 정의) + +#define I2C_ADDR 0x23 // I2C LCD 디바이스 주소 +#define LCD_CHR 1 // LCD 데이터 모드 +#define LCD_CMD 0 // LCD 명령 모드 +#define LINE1 0x80 // LCD 첫 번째 줄 +#define LINE2 0xC0 // LCD 두 번째 줄 +#define LCD_BACKLIGHT 0x08 // LCD 백라이트 ON +#define ENABLE 0b00000100 // LCD enable 비트 설정 +#define BUFFER_SIZE 1024 // 서버로부터 수신할 데이터의 버퍼 크기 + +#define SERVER_IP "127.0.0.1" // 서버 IP 주소 (localhost) +#define SERVER_PORT 12345 // 서버 포트 번호 + +// 전역 변수 선언 +int lcd_fd; // LCD I2C 파일 디스크립터 +int client_socket; // 클라이언트 소켓 +int battery_percentage; // 배터리 충전 상태 (0 ~ 99까지 랜덤한 값으로 가정) +int charging = 0; // 충전 여부 (0: 충전 중 아님, 1: 충전 중) +int fire_detected = 0; // 화재 감지 여부 (0: 감지 안 됨, 1: 감지됨) +int car_parked = 0; // 차량 주차 여부 (0: 미주차, 1: 주차) + +// 함수 프로토타입 선언 +void lcd_init(int fd); // LCD 초기화 함수 +void lcd_byte(int fd, int bits, int mode); // LCD 데이터 전송 함수 +void lcd_toggle_enable(int fd, int bits); // LCD enable 토글 함수 +void lcd_loc(int fd, int line); // LCD 줄 선택 함수 +void lcd_clear(int fd); // LCD 화면 초기화 함수 +void typeln(int fd, const char *s); // LCD 문자열 출력 함수 +void *listen_to_server(void *arg); // 서버 메시지 수신 스레드 함수 +void *update_battery_status(void *arg); // 배터리 상태 업데이트 스레드 함수 +void *rfid_tag_listener(void *arg); // RFID 태그 감지 스레드 함수 + +// 서버 연결을 초기화하는 함수 +int connect_to_server() { + if (client_socket > 0) { // 이미 소켓이 열려 있다면 닫기 + close(client_socket); + } -int main() { - pthread_t server_thread, battery_thread, rfid_thread; + client_socket = socket(AF_INET, SOCK_STREAM, 0); // 새 소켓 생성 + if (client_socket < 0) { + perror("Socket creation failed"); // 소켓 생성 실패 처리 + return -1; + } + + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; // IPv4 설정 + server_address.sin_port = htons(SERVER_PORT); // 포트 설정 + inet_pton(AF_INET, SERVER_IP, &server_address.sin_addr); // IP 주소 변환 + + if (connect(client_socket, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) { // 서버 연결 시도 + perror("Connection to server failed"); + return -1; + } + + printf("Connected to the server successfully.\n"); + return 0; +} - // Initialize LCD - const char *i2c_device = "/dev/i2c-1"; - lcd_fd = open(i2c_device, O_RDWR); +// LCD 초기화를 수행하는 함수 +void lcd_reset() { + const char *i2c_device = "/dev/i2c-1"; // I2C 장치 파일 경로 + lcd_fd = open(i2c_device, O_RDWR); // I2C 장치 열기 if (lcd_fd < 0) { - perror("Failed to open I2C device"); - return 1; + perror("Failed to open I2C device"); // 실패 시 오류 출력 + return; } - if (ioctl(lcd_fd, I2C_SLAVE, I2C_ADDR) < 0) { + if (ioctl(lcd_fd, I2C_SLAVE, I2C_ADDR) < 0) { // 슬레이브 장치 설정 perror("Failed to acquire bus access or talk to slave"); close(lcd_fd); - return 1; + return; } - lcd_init(lcd_fd); + lcd_init(lcd_fd); // LCD 초기화 +} - client_socket = socket(AF_INET, SOCK_STREAM, 0); - if (client_socket < 0) { - perror("Socket creation failed"); - return 1; - } +int main() { + pthread_t server_thread, battery_thread, rfid_thread; // 스레드 식별자 + srand(time(NULL)); // 랜덤 시드 설정 + battery_percentage = rand() % 100; // 배터리 초기값 설정 - struct sockaddr_in server_address; - server_address.sin_family = AF_INET; - server_address.sin_port = htons(SERVER_PORT); - inet_pton(AF_INET, SERVER_IP, &server_address.sin_addr); + lcd_reset(); // LCD 초기화 - if (connect(client_socket, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) { - perror("Connection to server failed"); - return 1; - } + while (1) { + if (connect_to_server() == 0) { // 서버 연결 성공 시 + printf("Connected to server. Starting threads...\n"); - // Create threads - pthread_create(&server_thread, NULL, listen_to_server, NULL); - pthread_create(&battery_thread, NULL, update_battery_status, NULL); - pthread_create(&rfid_thread, NULL, rfid_tag_listener, NULL); + pthread_create(&server_thread, NULL, listen_to_server, NULL); // 서버 수신 스레드 생성 + pthread_create(&battery_thread, NULL, update_battery_status, NULL); // 배터리 업데이트 스레드 생성 + pthread_create(&rfid_thread, NULL, rfid_tag_listener, NULL); // RFID 감지 스레드 생성 - pthread_join(server_thread, NULL); - pthread_join(battery_thread, NULL); - pthread_join(rfid_thread, NULL); + pthread_join(server_thread, NULL); // 서버 스레드 종료 대기 + + pthread_cancel(battery_thread); // 배터리 스레드 종료 + pthread_cancel(rfid_thread); // RFID 스레드 종료 + pthread_join(battery_thread, NULL); // 배터리 스레드 자원 해제 + pthread_join(rfid_thread, NULL); // RFID 스레드 자원 해제 + } else { + sleep(5); // 재연결 대기 + } + } - close(client_socket); - close(lcd_fd); + close(client_socket); // 클라이언트 소켓 닫기 + close(lcd_fd); // LCD 파일 디스크립터 닫기 return 0; } +// LCD 초기화 함수 void lcd_init(int fd) { - lcd_byte(fd, 0x33, LCD_CMD); - lcd_byte(fd, 0x32, LCD_CMD); - lcd_byte(fd, 0x06, LCD_CMD); - lcd_byte(fd, 0x0C, LCD_CMD); - lcd_byte(fd, 0x28, LCD_CMD); - lcd_byte(fd, 0x01, LCD_CMD); - usleep(5000); + lcd_byte(fd, 0x33, LCD_CMD); // 초기화 명령 1 + lcd_byte(fd, 0x32, LCD_CMD); // 초기화 명령 2 + lcd_byte(fd, 0x06, LCD_CMD); // 화면 이동 설정 + lcd_byte(fd, 0x0C, LCD_CMD); // 디스플레이 켜기 + lcd_byte(fd, 0x28, LCD_CMD); // 2라인 모드 설정 + lcd_byte(fd, 0x01, LCD_CMD); // 화면 지우기 + usleep(5000); // 초기화 대기 } +// LCD 데이터 전송 함수 void lcd_byte(int fd, int bits, int mode) { - int bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT; - int bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT; + int bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT; // 상위 4비트 설정 + int bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT; // 하위 4비트 설정 - write(fd, &bits_high, 1); - lcd_toggle_enable(fd, bits_high); + write(fd, &bits_high, 1); // 상위 4비트 전송 + lcd_toggle_enable(fd, bits_high); // Enable 신호 토글 - write(fd, &bits_low, 1); - lcd_toggle_enable(fd, bits_low); + write(fd, &bits_low, 1); // 하위 4비트 전송 + lcd_toggle_enable(fd, bits_low); // Enable 신호 토글 } +// LCD Enable 토글 함수 void lcd_toggle_enable(int fd, int bits) { - usleep(500); - int bits_enable = bits | ENABLE; - int bits_disable = bits & ~ENABLE; + usleep(500); // 대기 + int bits_enable = bits | ENABLE; // Enable 설정 + int bits_disable = bits & ~ENABLE; // Enable 해제 - write(fd, &bits_enable, 1); - write(fd, &bits_disable, 1); - usleep(500); + write(fd, &bits_enable, 1); // Enable 활성화 + write(fd, &bits_disable, 1); // Enable 비활성화 + usleep(500); // 대기 } +// LCD 특정 줄로 커서 이동 void lcd_loc(int fd, int line) { - lcd_byte(fd, line, LCD_CMD); + lcd_byte(fd, line, LCD_CMD); // 줄 위치 설정 } +// LCD 화면 초기화 void lcd_clear(int fd) { - lcd_byte(fd, 0x01, LCD_CMD); - lcd_byte(fd, 0x02, LCD_CMD); + lcd_byte(fd, 0x01, LCD_CMD); // 화면 지우기 명령 + usleep(5000); // 대기 + lcd_byte(fd, 0x02, LCD_CMD); // 커서 초기화 + usleep(5000); // 대기 } +// LCD 문자열 출력 함수 void typeln(int fd, const char *s) { while (*s) { - lcd_byte(fd, *(s++), LCD_CHR); + lcd_byte(fd, *(s++), LCD_CHR); // 문자 출력 } } +// 문자열 양끝 공백 제거 함수 +char *trim(char *str) { + char *end; + while (isspace((unsigned char)*str)) str++; // 시작 공백 제거 + if (*str == 0) return str; // 문자열이 비어 있으면 반환 + + end = str + strlen(str) - 1; // 끝 공백 제거 + while (end > str && isspace((unsigned char)*end)) end--; + + *(end + 1) = '\0'; // 문자열 종료 설정 + return str; +} + +// 서버 메시지 수신 및 처리 스레드 void *listen_to_server(void *arg) { char buffer[BUFFER_SIZE]; while (1) { - int bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0); - if (bytes_received <= 0) { - perror("Server connection lost"); - break; + int bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0); // 서버로부터 데이터를 수신 + if (bytes_received <= 0) { // 수신 실패 또는 연결 종료 시 + perror("Server connection lost. Attempting to reconnect..."); // 오류 메시지 출력 + lcd_clear(lcd_fd); // LCD 화면 초기화 + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 커서 이동 + typeln(lcd_fd, "Connection lost."); // 연결 끊김 메시지 출력 + lcd_loc(lcd_fd, LINE2); // 두 번째 줄로 커서 이동 + typeln(lcd_fd, "Reconnecting..."); // 재연결 시도 메시지 출력 + break; // 재연결을 위해 루프 종료 } - buffer[bytes_received] = '\0'; - printf("%s\n", buffer); - - fire_detected = strstr(buffer, "-FIRE") != NULL; - if (fire_detected) { - charging = 0; - lcd_clear(lcd_fd); - lcd_loc(lcd_fd, LINE1); - typeln(lcd_fd, "FIRE ALERT!"); - lcd_loc(lcd_fd, LINE2); - typeln(lcd_fd, "EVACUATE AREA!"); - } else if (strcmp(buffer, "0-PARK") == 0) { - car_parked = 1; - } else if (strcmp(buffer, "0-EXIT") == 0) { - car_parked = 1; - car_parked = 0; - battery_percentage = 50; - charging = 0; - lcd_clear(lcd_fd); - } else if (strcmp(buffer, "CLEAR") == 0) { - car_parked = 1; - fire_detected = 0; - lcd_clear(lcd_fd); + buffer[bytes_received] = '\0'; // 수신 데이터 끝에 null 문자 추가 + printf("Received message: %s\n", buffer); // 수신된 메시지 출력 + + if (fire_detected) { // 화재 감지 상태에서 + if (strcmp(trim(buffer), "CLEAR") == 0) { // CLEAR 메시지 확인 + lcd_reset(); // LCD 초기화 + lcd_clear(lcd_fd); // LCD 화면 지우기 + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 + typeln(lcd_fd, "Fire cleared!"); // 화재 해제 메시지 출력 + lcd_loc(lcd_fd, LINE2); // 두 번째 줄로 이동 + typeln(lcd_fd, "All systems OK"); // 시스템 정상 메시지 출력 + sleep(5); // 5초 대기 + fire_detected = 0; // 화재 경고 해제 + car_parked = 0; // 차량 주차 상태 초기화 + } else { + printf("Fire alarm still active. Ignored message: %s\n", buffer); // 무시된 메시지 출력 + continue; // 루프 계속 진행 + } + } else if (strstr(buffer, "-FIRE") != NULL) { // 화재 경고 메시지 수신 시 + printf("FIRE ALERT RECEIVED\n"); // 경고 로그 출력 + fire_detected = 1; // 화재 상태 활성화 + charging = 0; // 충전 중지 + lcd_reset(); // LCD 초기화 + lcd_clear(lcd_fd); // LCD 화면 지우기 + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 + typeln(lcd_fd, "FIRE ALERT!"); // 화재 경고 메시지 출력 + lcd_loc(lcd_fd, LINE2); // 두 번째 줄로 이동 + typeln(lcd_fd, "EVACUATE AREA!"); // 대피 메시지 출력 + } else if (strcmp(trim(buffer), "0-PARK") == 0) { // 차량 주차 메시지 수신 시 + printf("CAR PARKED\n"); // 주차 상태 로그 출력 + car_parked = 1; // 차량 주차 상태 활성화 + } else if (strcmp(trim(buffer), "0-EXIT") == 0) { // 차량 출차 메시지 수신 시 + printf("CAR EXITED\n"); // 출차 상태 로그 출력 + lcd_reset(); // LCD 초기화 + usleep(5000); + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 + typeln(lcd_fd, "Car exited."); // 출차 메시지 출력 + sleep(2); // 2초 대기 + car_parked = 0; // 차량 주차 상태 초기화 + battery_percentage = rand() % 100; // 배터리 상태 초기화 + charging = 0; // 충전 중지 } } - return NULL; + return NULL; // 스레드 종료 } +// 배터리 상태 업데이트 스레드 void *update_battery_status(void *arg) { while (1) { - if (charging && !fire_detected && car_parked) { - if (battery_percentage < 100) { - battery_percentage++; - lcd_clear(lcd_fd); - lcd_loc(lcd_fd, LINE1); + if (charging && !fire_detected && car_parked) { // 충전 조건 확인 + if (battery_percentage < 100) { // 배터리 충전 중 + battery_percentage++; // 배터리 퍼센트 증가 + lcd_reset(); // LCD 초기화 + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 char msg[16]; - snprintf(msg, sizeof(msg), "Charging: %d%%", battery_percentage); - typeln(lcd_fd, msg); + snprintf(msg, sizeof(msg), "Charging: %d%%", battery_percentage); // 충전 상태 메시지 생성 + typeln(lcd_fd, msg); // 충전 상태 메시지 출력 char server_msg[16]; - snprintf(server_msg, sizeof(server_msg), "0-%d", battery_percentage); - send(client_socket, server_msg, strlen(server_msg), 0); + snprintf(server_msg, sizeof(server_msg), "0-%d", battery_percentage); // 서버 전송 메시지 생성 + send(client_socket, server_msg, strlen(server_msg), 0); // 서버로 전송 } else { - lcd_loc(lcd_fd, LINE1); - typeln(lcd_fd, "CHARGE COMPLETE"); - sleep(1); + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 + typeln(lcd_fd, "CHARGE COMPLETE"); // 충전 완료 메시지 출력 + sleep(1); // 1초 대기 } - sleep(1); + sleep(1); // 다음 업데이트까지 대기 } else { - usleep(500000); + usleep(500000); // 충전 조건 미충족 시 대기 } } - return NULL; + return NULL; // 스레드 종료 } +// RFID 태그 감지 및 처리 스레드 void *rfid_tag_listener(void *arg) { - MFRC522 rfid(RPI_V2_GPIO_P1_24, RPI_V2_GPIO_P1_15); - rfid.PCD_Init(); + MFRC522 rfid(RPI_V2_GPIO_P1_24, RPI_V2_GPIO_P1_15); // RFID 모듈 초기화 + rfid.PCD_Init(); // RFID 모듈 초기화 호출 - uint8_t card_id[4]; + uint8_t card_id[4]; // RFID 카드 ID 저장 공간 while (1) { - if (!fire_detected) { - if (!car_parked) { // 주차 대기 상황 - lcd_clear(lcd_fd); - lcd_loc(lcd_fd, LINE1); - typeln(lcd_fd, "NO CAR PARKED"); - sleep(2); - } else if (charging) { // 충전중 (충전 중지 감지) - if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { - memcpy(card_id, rfid.uid.uidByte, 4); - - charging = 0; - lcd_clear(lcd_fd); - lcd_loc(lcd_fd, LINE1); - typeln(lcd_fd, "Charging Stopped"); - sleep(2); + if (!fire_detected) { // 화재 상태가 아닐 경우 + if (!car_parked) { // 차량이 주차되지 않은 경우 + lcd_reset(); // LCD 초기화 + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 + typeln(lcd_fd, "NO CAR PARKED"); // 주차 필요 메시지 출력 + sleep(2); // 2초 대기 + } else if (charging) { // 차량이 충전 중인 경우 + if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { // 새 카드 감지 및 읽기 성공 + memcpy(card_id, rfid.uid.uidByte, 4); // 카드 ID 복사 + charging = 0; // 충전 중지 + lcd_reset(); // LCD 초기화 + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 + typeln(lcd_fd, "Charging Stopped"); // 충전 중지 메시지 출력 + sleep(2); // 2초 대기 } - } else if (!charging) { // 충전 대기중 (충전 시작 감지) - lcd_clear(lcd_fd); - lcd_loc(lcd_fd, LINE1); - typeln(lcd_fd, "Scan your card"); - - if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { - memcpy(card_id, rfid.uid.uidByte, 4); - - charging = 1; - lcd_clear(lcd_fd); - lcd_loc(lcd_fd, LINE1); - typeln(lcd_fd, "Card Accepted"); + } else if (!charging) { // 차량이 충전 대기 중인 경우 + lcd_reset(); // LCD 초기화 + + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 + typeln(lcd_fd, "Scan your card"); // 카드 스캔 요청 메시지 출력 + lcd_loc(lcd_fd, LINE2); // 두 번째 줄로 이동 + typeln(lcd_fd, "for 1 second"); // 카드 스캔 요청 메시지 출력 + sleep(1); // 1초 대기 + if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { // 새 카드 감지 및 읽기 성공 + memcpy(card_id, rfid.uid.uidByte, 4); // 카드 ID 복사 + + charging = 1; // 충전 시작 + lcd_reset(); // LCD 초기화 + lcd_loc(lcd_fd, LINE1); // 첫 번째 줄로 이동 + typeln(lcd_fd, "Card Accepted"); // 카드 인식 메시지 출력 char uid_message[64]; - snprintf(uid_message, sizeof(uid_message), "0-d%02X%02X%02X%02X", card_id[0], card_id[1], card_id[2], card_id[3]); - send(client_socket, uid_message, strlen(uid_message), 0); - sleep(2); + snprintf(uid_message, sizeof(uid_message), "0-d%02X%02X%02X%02X", card_id[0], card_id[1], card_id[2], card_id[3]); // 카드 ID 메시지 생성 + send(client_socket, uid_message, strlen(uid_message), 0); // 서버로 전송 + sleep(2); // 2초 대기 } } - usleep(100000); + usleep(100000); // 루프 대기 시간 } } - return NULL; + return NULL; // 스레드 종료 } -- GitLab