[STM32#23] RS485 to TTL모듈을 이용해서 modbus rtu로 4채널릴레이(relay)와 온습도센서(XY-MD02) 제어하는 방법!(녹칸다 내맘대로 STM32)
프로그래밍/STM32 2025. 12. 29. 23:21반응형

https://youtube.com/live/oMZoDk0ourA
[STM32#23] RS485 to TTL모듈을 이용해서 modbus rtu로 4채널릴레이(relay)와 온습도센서(XY-MD02) 제어하는 방법!(녹칸다 내맘대로 STM32)
심심한녹칸다의 내맘대로 STM32시리즈이다!
STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g3ab12818c94_4_1#slide=id.g3ab12818c94_4_1
산업용 통신인 modbus rtu를 해보도록 하겠습니다!
(연결)

1.STM32가 RS485릴레이의 채널0을 1초간격으로 on/off하도록 하시오!

/* USER CODE BEGIN 2 */
uint8_t on_req[] = {0x01,0x05,0x00,0x00,0xFF,0x00,0x8C,0x3A};
uint8_t off_req[] = {0x01,0x05,0x00,0x00,0x00,0x00,0xCD,0xCA};
uint8_t res[8];
uint8_t tx_buff[100];
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//채널0 ON명령어 전송!(01 05 00 00 FF 00 8C 3A)
HAL_UART_Transmit(&huart1, on_req, sizeof(on_req), 100);
HAL_UART_Transmit(&huart2, "on_req sent!\n", 13, 100);
//릴레이로부터 응답을 수신함!
//HAL_Delay(100);
if(HAL_UART_Receive(&huart1, res, 8, 100) == HAL_OK){
//USART2로 출력!
HAL_UART_Transmit(&huart2, "res = ", 6, 100);
for(int i = 0;i<8;i++){
sprintf(tx_buff,"%02X, ",res[i]);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
}
HAL_Delay(1000);
//채널0 OFF명령어 전송!(01 05 00 00 00 00 CD CA)
HAL_UART_Transmit(&huart1, off_req, sizeof(off_req), 100);
HAL_UART_Transmit(&huart2, "off_req sent!\n", 14, 100);
//HAL_Delay(100);
//릴레이로부터 응답을 수신함!
if(HAL_UART_Receive(&huart1, res, 8, 100) == HAL_OK){
//USART2로 출력!
HAL_UART_Transmit(&huart2, "res = ", 6, 100);
for(int i = 0;i<8;i++){
sprintf(tx_buff,"%02X, ",res[i]);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
}
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
2.녹칸다의 STM32쉴드에 붙어있는 버튼 8개를 이용해서 4채널 릴레이를 모두 on/off할수있는 기능을 구현하시오!

/* 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){
//채널0 ON명령어 전송!(01 05 00 00 FF 00 8C 3A)
HAL_UART_Transmit(&huart1, nockanda_req[btn_num], sizeof(nockanda_req[btn_num]), 100);
HAL_UART_Transmit(&huart2, "on_req sent!\n", 13, 100);
//릴레이로부터 응답을 수신함!
//HAL_Delay(100);
if(HAL_UART_Receive(&huart1, res, 8, 100) == HAL_OK){
//USART2로 출력!
HAL_UART_Transmit(&huart2, "res = ", 6, 100);
for(int i = 0;i<8;i++){
sprintf(tx_buff,"%02X, ",res[i]);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
}
HAL_Delay(200);
}
/* USER CODE END 0 */
while (1)
{
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 */
}
3.예제2에서 릴레이를 제어하는 8개의 커맨드가 있는데, 각 커맨드의 CRC16을 계산하는 함수를 이용해서 CRC16만 계산해서 출력하시오!

/* USER CODE BEGIN 0 */
uint8_t nockanda_req[][8] = {
{0x01,0x05,0x00,0x00,0xFF,0x00,0x8C,0x3A}, //0
{0x01,0x05,0x00,0x00,0x00,0x00,0xCD,0xCA}, //1
{0x01,0x05,0x00,0x01,0xFF,0x00,0xDD,0xFA}, //2
{0x01,0x05,0x00,0x01,0x00,0x00,0x9C,0x0A}, //3
{0x01,0x05,0x00,0x02,0xFF,0x00,0x2D,0xFA}, //4
{0x01,0x05,0x00,0x02,0x00,0x00,0x6C,0x0A}, //5
{0x01,0x05,0x00,0x03,0xFF,0x00,0x7C,0x3A}, //6
{0x01,0x05,0x00,0x03,0x00,0x00,0x3D,0xCA} //7
};
uint8_t res[8];
uint8_t tx_buff[100];
//함수의 프로토타입 선언
void control_relay(int btn_num);
uint16_t cal_crc(uint8_t *data, uint8_t len);
//btn_num은 0부터 7까지 눌려진 버튼의 번호이다!
void control_relay(int btn_num){
uint16_t crc16 = cal_crc(nockanda_req[btn_num],6);
sprintf(tx_buff,"btn_num= %d, crc16= %04x\n",btn_num,crc16);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
HAL_Delay(200);
}
uint16_t cal_crc(uint8_t *data, uint8_t len)
{
uint16_t crc = 0xFFFF;
for(uint8_t i = 0; i < len; i++)
{
crc ^= data[i]; // LSB부터 XOR
for(uint8_t j = 0; j < 8; j++)
{
if(crc & 0x0001)
{
crc >>= 1;
crc ^= 0xA001; // Modbus polynomial
}
else
{
crc >>= 1;
}
}
}
return crc;
}
while (1)
{
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 */
}
4.modbus rtu명령어에서 CRC16자리를 비워두고 직접 계산해서 대입한다음 명령을 전송하시오!

/* USER CODE BEGIN 0 */
uint8_t nockanda_req[][8] = {
{0x01,0x05,0x00,0x00,0xFF,0x00,0x00,0x00}, //0
{0x01,0x05,0x00,0x00,0x00,0x00,0x00,0x00}, //1
{0x01,0x05,0x00,0x01,0xFF,0x00,0x00,0x00}, //2
{0x01,0x05,0x00,0x01,0x00,0x00,0x00,0x00}, //3
{0x01,0x05,0x00,0x02,0xFF,0x00,0x00,0x00}, //4
{0x01,0x05,0x00,0x02,0x00,0x00,0x00,0x00}, //5
{0x01,0x05,0x00,0x03,0xFF,0x00,0x00,0x00}, //6
{0x01,0x05,0x00,0x03,0x00,0x00,0x00,0x00} //7
};
uint8_t res[8];
uint8_t tx_buff[100];
//함수의 프로토타입 선언
void control_relay(int btn_num);
uint16_t cal_crc(uint8_t *data, uint8_t len);
//btn_num은 0부터 7까지 눌려진 버튼의 번호이다!
void control_relay(int btn_num){
uint16_t crc16 = cal_crc(nockanda_req[btn_num],6);
nockanda_req[btn_num][6] = crc16 & 0x00FF; //CRC_L
nockanda_req[btn_num][7] = crc16 >> 8; //CRC_H
HAL_UART_Transmit(&huart1, nockanda_req[btn_num], sizeof(nockanda_req[btn_num]), 100);
HAL_UART_Transmit(&huart2, "on_req sent!\n", 13, 100);
//릴레이로부터 응답을 수신함!
//HAL_Delay(100);
if(HAL_UART_Receive(&huart1, res, 8, 100) == HAL_OK){
//USART2로 출력!
HAL_UART_Transmit(&huart2, "res = ", 6, 100);
for(int i = 0;i<8;i++){
sprintf(tx_buff,"%02X, ",res[i]);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
HAL_UART_Transmit(&huart2, "\n", 1, 100);
}
HAL_Delay(200);
}
uint16_t cal_crc(uint8_t *data, uint8_t len)
{
uint16_t crc = 0xFFFF;
for(uint8_t i = 0; i < len; i++)
{
crc ^= data[i]; // LSB부터 XOR
for(uint8_t j = 0; j < 8; j++)
{
if(crc & 0x0001)
{
crc >>= 1;
crc ^= 0xA001; // Modbus polynomial
}
else
{
crc >>= 1;
}
}
}
return crc;
}
while (1)
{
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 */
}반응형


