[STM32#24] RS485 to TTL모듈을 이용해서 modbus rtu로 4채널릴레이(relay)와 온습도센서(XY-MD02) 제어해보기 계속!(녹칸다 내맘대로 STM32)
프로그래밍/STM32 2025. 12. 31. 17:01
https://youtube.com/live/AaawBcSBXX0
[STM32#24] RS485 to TTL모듈을 이용해서 modbus rtu로 4채널릴레이(relay)와 온습도센서(XY-MD02) 제어해보기 계속!(녹칸다 내맘대로 STM32)
심심한녹칸다의 내맘대로 STM32시리즈이다!
STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g3b3e89c7231_3_141#slide=id.g3b3e89c7231_3_141
4채널릴레이는 어느정도 했고 이번에는 온습도센서를 처리해보도록 하겠습니다!
1.녹칸다가 23편에서 만든 예제2번을 그대로 적용한다음, modbus 4채널릴레이의 매뉴얼에 보면 릴레이의 on/off상태를 읽어오는 명령이 있는데, 이 명령을 이용해서 슬레이브쪽 릴레이의 작동상태를 마스터에서 알수있도록 하시오!(usart2출력)

/* USER CODE BEGIN 0 */
uint8_t nockanda_req[][8] = {
{0x01,0x05,0x00,0x00,0xFF,0x00,0x8C,0x3A},
{0x01,0x05,0x00,0x00,0x00,0x00,0xCD,0xCA},
{0x01,0x05,0x00,0x01,0xFF,0x00,0xDD,0xFA},
{0x01,0x05,0x00,0x01,0x00,0x00,0x9C,0x0A},
{0x01,0x05,0x00,0x02,0xFF,0x00,0x2D,0xFA},
{0x01,0x05,0x00,0x02,0x00,0x00,0x6C,0x0A},
{0x01,0x05,0x00,0x03,0xFF,0x00,0x7C,0x3A},
{0x01,0x05,0x00,0x03,0x00,0x00,0x3D,0xCA}
};
uint8_t res[8];
uint8_t tx_buff[100];
//btn_num은 0부터 7까지 눌려진 버튼의 번호이다!
void control_relay(int btn_num){
HAL_UART_Transmit(&huart1, nockanda_req[btn_num], sizeof(nockanda_req[btn_num]), 100);
if(HAL_UART_Receive(&huart1, res, 8, 100) == HAL_OK){
}
HAL_Delay(200);
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
uint32_t nockanda_t = 0;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//100밀리초 간격으로 4채널릴레이에게 릴레이 상태를 물어본다!
if(HAL_GetTick() - nockanda_t >= 100){
nockanda_t = HAL_GetTick();
uint8_t req[] = {0x01,0x01,0x00,0x00,0x00,0x08,0x3D,0xCC};
HAL_UART_Transmit(&huart1, req, sizeof(req), 100);
//릴레이로부터 응답을 수신함!
//HAL_Delay(100);
if(HAL_UART_Receive(&huart1, res, 6, 100) == HAL_OK){
//res[3]에 coil의 상태가 들어있다!
uint8_t coils[4];
for(int i = 0;i<4;i++){
coils[i] = (res[3] >> i) & 0b00000001;
sprintf(tx_buff,"COIL%d = %s,",i,coils[i] ? "ON" : "OFF");
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
}
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == 0) control_relay(0);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) == 0) control_relay(2);
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == 0) control_relay(4);
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == 0) control_relay(6);
if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == 0) control_relay(1);
if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_7) == 0) control_relay(3);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0) control_relay(5);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0) control_relay(7);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
2.예제1번에서 녹칸다가 버튼을 무지하게 빠르게 누르게되면 read coils쪽 코드가 HAL_Delay에 의해서 멈추게 된다! 이 부분을 freeRTOS를 이용해서 간단하게 극복해보시오!
task1:녹칸다가 8개의 버튼중에 어떤 버튼을 눌렀는지를 대기열에 넣음!
task2:100밀리초 간격으로 read coils명령을 대기열에 넣음!
task3:대기열에 있는 내용을 수행해서 반영함

/* USER CODE BEGIN 0 */
uint8_t nockanda_req[][8] = {
{0x01,0x05,0x00,0x00,0xFF,0x00,0x8C,0x3A},
{0x01,0x05,0x00,0x00,0x00,0x00,0xCD,0xCA},
{0x01,0x05,0x00,0x01,0xFF,0x00,0xDD,0xFA},
{0x01,0x05,0x00,0x01,0x00,0x00,0x9C,0x0A},
{0x01,0x05,0x00,0x02,0xFF,0x00,0x2D,0xFA},
{0x01,0x05,0x00,0x02,0x00,0x00,0x6C,0x0A},
{0x01,0x05,0x00,0x03,0xFF,0x00,0x7C,0x3A},
{0x01,0x05,0x00,0x03,0x00,0x00,0x3D,0xCA}
};
uint8_t res[8];
uint8_t tx_buff[100];
//btn_num은 0부터 7까지 눌려진 버튼의 번호이다!
void control_relay(int btn_num){
HAL_UART_Transmit(&huart1, nockanda_req[btn_num], sizeof(nockanda_req[btn_num]), 100);
if(HAL_UART_Receive(&huart1, res, 8, 100) == HAL_OK){
}
}
typedef struct {
uint8_t fc; //fc가 0x05면 write single coil, 0x01이면 read coils
uint8_t btn_num;
} Msg_t;
//메시지 큐(전역)
osMessageQueueId_t dataQueue;
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
//메시지큐(메인)
dataQueue = osMessageQueueNew(
8, // queue length
sizeof(Msg_t), // item size
NULL);
/* USER CODE END 2 */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
//8개의 버튼중에 어떤 버튼이 눌려졌는지를 대기열에 넣는다!
Msg_t msg;
msg.fc = 0x05;
/* Infinite loop */
for(;;)
{
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == 0){
//큐에다가 버튼 0번이 눌려졌다고 넣어준다
msg.btn_num = 0;
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(200);
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) == 0){
msg.btn_num = 2;
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(200);
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == 0){
msg.btn_num = 4;
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(200);
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == 0){
msg.btn_num = 6;
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(200);
}
if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == 0) {
msg.btn_num = 1;
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(200);
}
if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_7) == 0){
msg.btn_num = 3;
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(200);
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0){
msg.btn_num = 5;
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(200);
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0){
msg.btn_num = 7;
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(200);
}
osDelay(1); //이녀석 필수!
}
/* USER CODE END 5 */
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
//100밀리초 간격으로 read coils에 대한 명령을 대기열에 넣는다!
Msg_t msg;
msg.fc = 0x01;
/* Infinite loop */
for(;;)
{
osMessageQueuePut(dataQueue, &msg, 0, 0);
osDelay(100);
}
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
//대기열에 뭔가 기다리는게 있으면 순서대로 뺴서 처리한다!
Msg_t msg;
/* Infinite loop */
for(;;)
{
osMessageQueueGet(dataQueue, &msg, NULL, osWaitForever);
if(msg.fc == 0x01){
uint8_t req[] = {0x01,0x01,0x00,0x00,0x00,0x08,0x3D,0xCC};
HAL_UART_Transmit(&huart1, req, sizeof(req), 100);
//릴레이로부터 응답을 수신함!
//HAL_Delay(100);
if(HAL_UART_Receive(&huart1, res, 6, 100) == HAL_OK){
//res[3]에 coil의 상태가 들어있다!
uint8_t coils[4];
for(int i = 0;i<4;i++){
coils[i] = (res[3] >> i) & 0b00000001;
sprintf(tx_buff,"COIL%d = %s,",i,coils[i] ? "ON" : "OFF");
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
}
}else if(msg.fc == 0x05){
//task1으로부터 온 메시지
control_relay(msg.btn_num);
}
}
/* USER CODE END StartTask03 */
}
3.modbus rtu 온습도센서를 연결해서 온도를 측정해서 putty화면에 출력하시오!

/* USER CODE BEGIN 2 */
uint8_t res[8];
uint8_t tx_buff[100];
/* USER CODE END 2 */
while (1)
{
uint8_t req[] = {0x01,0x04,0x00,0x01,0x00,0x01,0x60,0x0A};
HAL_UART_Transmit(&huart1, req, sizeof(req), 100);
if(HAL_UART_Receive(&huart1, res, 7, 100) == HAL_OK){
//USART2로 출력!
HAL_UART_Transmit(&huart2, "res = ", 6, 100);
for(int i = 0;i<7;i++){
sprintf(tx_buff,"%02X, ",res[i]);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
//온도계산 res[3] res[4]
int16_t temp = (res[3] << 8) | res[4];
sprintf(tx_buff,"temp = %.1f\n",temp/10.0);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
4.modbus rtu 온습도센서를 연결해서 습도를 측정해서 putty화면에 출력하시오!

/* USER CODE BEGIN 2 */
uint8_t res[8];
uint8_t tx_buff[100];
/* USER CODE END 2 */
while (1)
{
uint8_t req[] = {0x01,0x04,0x00,0x02,0x00,0x01,0x90,0x0A};
HAL_UART_Transmit(&huart1, req, sizeof(req), 100);
if(HAL_UART_Receive(&huart1, res, 7, 100) == HAL_OK){
//USART2로 출력!
HAL_UART_Transmit(&huart2, "res = ", 6, 100);
for(int i = 0;i<7;i++){
sprintf(tx_buff,"%02X, ",res[i]);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
//습도계산 res[3] res[4]
uint16_t humi = (res[3] << 8) | res[4];
sprintf(tx_buff,"humi = %.1f\n",humi/10.0);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
5.온도와 습도를 동시에 측정해서 putty화면에 출력하시오!

/* USER CODE BEGIN 2 */
uint8_t res[8];
uint8_t tx_buff[100];
/* USER CODE END 2 */
while (1)
{
uint8_t req[] = {0x01,0x04,0x00,0x01,0x00,0x02,0x20,0x0B};
HAL_UART_Transmit(&huart1, req, sizeof(req), 100);
if(HAL_UART_Receive(&huart1, res, 9, 100) == HAL_OK){
//USART2로 출력!
HAL_UART_Transmit(&huart2, "res = ", 6, 100);
for(int i = 0;i<9;i++){
sprintf(tx_buff,"%02X, ",res[i]);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
//온도 res[3] + res[4]
//습도 res[5] + res[6]
int16_t temp = (res[3] << 8) | res[4];
uint16_t humi = (res[5] << 8) | res[6];
sprintf(tx_buff,"temp = %.1f, humi = %.1f\n",temp/10.0,humi/10.0);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
6.측정한 온도와 습도를 1602LCD에 출력하시오!

//BEGIN0에 i2c 1602LCD관련코드 링크타고가서 추가하기!
/* USER CODE BEGIN 2 */
uint8_t res[9];
uint8_t tx_buff[100];
uint8_t line1[20];
uint8_t line2[20];
LCD_INIT(); //1회호출(메인루프 시작전)
/* USER CODE END 2 */
while (1)
{
uint8_t req[] = {0x01,0x04,0x00,0x01,0x00,0x02,0x20,0x0B};
HAL_UART_Transmit(&huart1, req, sizeof(req), 100);
if(HAL_UART_Receive(&huart1, res, 9, 100) == HAL_OK){
//USART2로 출력!
HAL_UART_Transmit(&huart2, "res = ", 6, 100);
for(int i = 0;i<9;i++){
sprintf(tx_buff,"%02X, ",res[i]);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
//온도 res[3] + res[4]
//습도 res[5] + res[6]
int16_t temp = (res[3] << 8) | res[4];
uint16_t humi = (res[5] << 8) | res[6];
sprintf(tx_buff,"temp = %.1f, humi = %.1f\n",temp/10.0,humi/10.0);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
//1602LCD에 출력하기
LCD_CLEAR();
sprintf(line1,"TEMP = %.1f'C",temp/10.0);
sprintf(line2,"HUMI = %.1f%c",humi/10.0,'%');
LCD_XY(0, 0) ; LCD_PUTS(line1);
LCD_XY(0, 1) ; LCD_PUTS(line2);
}
HAL_Delay(1000);
}

