동작
STM32에서 DHT22의 온습도 값을 CAN을 통해 Raspberry Pi로 전송.
구조
STM32(FreeRTOS)
Task1: DHT22의 온습도 값을 주기적으로 읽어 Task2로 값 전달.
Task2: 전달 된 온습도 값을 CAN transmit 하고 OLED display 에 출력.
Raspberry Pi
CAN ID 모니터링, 수신된 데이터를 콘솔 출력.

main 함수
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <float.h>
#include <dht22.h>
#include "4ilo/4ilo_ssd1306.h"
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
CAN_HandleTypeDef hcan1;
TIM_HandleTypeDef htim6;
SemaphoreHandle_t xSemaphore = NULL;
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityBelowNormal,
};
/* Definitions for dht22Task */
osThreadId_t dht22TaskHandle;
const osThreadAttr_t dht22Task_attributes = {
.name = "dht22Task",
.stack_size = 256 * 4,
.priority = (osPriority_t) osPriorityNormal3,
};
/* Definitions for CanReceiveTask */
osThreadId_t CanReceiveTaskHandle;
const osThreadAttr_t CanReceiveTask_attributes = {
.name = "CanReceiveTask",
.stack_size = 256 * 4,
.priority = (osPriority_t) osPriorityNormal1,
};
/* Definitions for displayTask */
osThreadId_t displayTaskHandle;
const osThreadAttr_t displayTask_attributes = {
.name = "displayTask",
.stack_size = 256 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* USER CODE BEGIN PV */
CAN_FilterTypeDef canFilter; // CAN Bus filter
QueueHandle_t dht22Queue = NULL;
int main(void)
{
xSemaphore = xSemaphoreCreateBinary();
/* USER CODE BEGIN RTOS_TIMERS */
setTimer(&htim6);
HAL_TIM_Base_Start(&htim6);
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
dht22Queue = xQueueCreate(10, sizeof(DHT_DataTypedef));
if(dht22Queue == NULL) {
printf("Failed to create queue\n\r");
while(1) {
osDelay(1);
}
}
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of dht22Task */
dht22TaskHandle = osThreadNew(StartDHT22Task, NULL, &dht22Task_attributes);
/* creation of CanReceiveTask */
CanReceiveTaskHandle = osThreadNew(StartCanReceiveTask, NULL, &CanReceiveTask_attributes);
/* creation of displayTask */
displayTaskHandle = osThreadNew(StartDisplayTask, NULL, &displayTask_attributes);
}
DHT22 온습도 값 처리 태스크
void StartDHT22Task(void *argument)
{
/* USER CODE BEGIN StartDHT22Task */
vTaskDelay(pdMS_TO_TICKS(300)); // wait for display task
DHT_DataTypedef dht22Data;
/* Infinite loop */
for(;;)
{
dht22Data.Humidity = FLT_MAX;
dht22Data.Temperature = FLT_MIN;
if(DHT_GetData(&dht22Data) == 0) {
printf("DHT_GetData() error\n\r");
} else if(xQueueSend(dht22Queue, &dht22Data, pdMS_TO_TICKS(100)) != pdPASS) {
printf("Failed to send data to queue\n\r");
}
vTaskDelay(pdMS_TO_TICKS(3000));
}
/* USER CODE END StartDHT22Task */
}
CAN Transmit & display 처리 태스크
void vMainCanTransmit(CAN_HandleTypeDef *handle, DHT_DataTypedef* dhtData)
{
CAN_TxHeaderTypeDef txHeader; // CAN Bus Receive header
uint32_t txMailbox; // CAN Bus Mail box variable
uint8_t txData[5];
uint32_t intPart, fractPart;
txHeader.IDE = CAN_ID_STD;
txHeader.RTR = CAN_RTR_DATA;
txHeader.StdId = 0x030;
txHeader.ExtId = 0x03;
txHeader.TransmitGlobalTime = DISABLE;
txHeader.DLC = 5;
intPart = (uint32_t)dhtData->Humidity;
fractPart = (uint32_t)((dhtData->Humidity - (float)intPart) * 100.0f);
txData[0] = (uint8_t)(intPart % 100);
txData[1] = (uint8_t)fractPart;
intPart = (uint32_t)dhtData->Temperature;
fractPart = (uint32_t)((dhtData->Temperature - (float)intPart) * 100.0f);
txData[2] = (uint8_t)(intPart % 100);
txData[3] = (uint8_t)fractPart;
txData[4] = ++canMsgCount;
printf("CAN send [%02d,%02d,%02d,%02d,%02d]\n\r",
txData[0], txData[1], txData[2], txData[3], txData[4]);
// check if mailboxes are free
uint32_t timeout = pdMS_TO_TICKS(100);
TickType_t start = xTaskGetTickCount();
while(HAL_CAN_GetTxMailboxesFreeLevel(handle) != 3) {
if((xTaskGetTickCount() - start) > timeout) {
// timeout
printf("CAN transmit timeout\n\r");
break;
}
taskYIELD();
}
if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, txData, &txMailbox) != HAL_OK) {
printf("CAN transmit error\n\r");
return;
}
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
void StartDisplayTask(void *argument)
{
/* USER CODE BEGIN StartDisplayTask */
DHT_DataTypedef dht22Data;
char humidity[16];
char temperature[16];
// Init lcd using one of the stm32HAL i2c typedefs
if(ssd1306_Init(&hi2c1) != 0) {
Error_Handler();
}
vTaskDelay(pdMS_TO_TICKS(100));
/* Infinite loop */
for(;;) {
if(xQueueReceive(dht22Queue, &dht22Data, pdMS_TO_TICKS(100)) == pdPASS) {
vMainCanTransmit(&hcan1, &dht22Data);
memset(humidity, 0, 16);
memset(temperature, 0, 16);
snprintf(humidity, 16, "H %.2f", dht22Data.Humidity);
snprintf(temperature, 16, "T %.2f", dht22Data.Temperature);
printf("[DT] t:%s h:%s\n\r", temperature, humidity);
ssd1306_Fill(Black);
ssd1306_UpdateScreen(&hi2c1);
ssd1306_SetCursor(0, 0);
ssd1306_WriteString(humidity, Font_7x10, White);
ssd1306_SetCursor(0, 12);
ssd1306_WriteString(temperature, Font_7x10, White);
ssd1306_UpdateScreen(&hi2c1);
}
vTaskDelay(pdMS_TO_TICKS(500));
}
/* USER CODE END StartDisplayTask */
}
CAN 데이터 수신 처리
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
if(hcan->Instance == CAN1) {
if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK) {
/* Reception Error */
return;
}
can_received = 1; // CAN Bus received flag
}
}
Raspberry pi CAN 데이터 수신 처리
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <errno.h>
#define CAN_INTERFACE "can0"
const unsigned char response_data[5] = {0x0F, 0xF0, 0x0F, 0xF0, 0x00};
#define RESPONSE_DLC sizeof(response_data)
int main() {
int s;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
int nbytes;
struct can_filter rfilter[] = {
{ .can_id = 0x30, .can_mask = CAN_SFF_MASK }
};
int num_filters = sizeof(rfilter) / sizeof(struct can_filter);
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if(s < 0) {
perror("failed to create socket");
return 1;
}
strcpy(ifr.ifr_name, CAN_INTERFACE);
if(ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl error");
close(s);
return 1;
}
if(setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)) < 0) {
perror("setsockopt error");
close(s);
return 1;
}
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind error");
close(s);
return 1;
}
printf("'%s' connected. waiting...\n", CAN_INTERFACE);
resp_frame.can_id = RESPONSE_CAN_ID;
resp_frame.can_dlc = RESPONSE_DLC;
memcpy(resp_frame.data, response_data, RESPONSE_DLC);
while(1) {
nbytes = read(s, &frame, sizeof(struct can_frame));
if(nbytes < 0) {
if(errno == ENETDOWN) {
fprintf(stderr, "network down?\n");
break;
}
perror("CAN RAW read error");
continue;
} else if(nbytes < sizeof(struct can_frame)) {
fprintf(stderr, "not completed CAN received: %d bytes\n", nbytes);
continue;
}
printf("\nReceived ");
printf("ID:0x%X, ", frame.can_id & CAN_SFF_MASK);
printf("DLC:%d\n", frame.can_dlc);
printf("data:");
for(int i = 0; i < frame.can_dlc; i++) {
printf(" %02X", frame.data[i]);
}
printf("\n");
printf("Humidity:%02d.%02d, Temperature:%02d.%02.d\n",
frame.data[0], frame.data[1], frame.data[2], frame.data[3]);
resp_frame.data[4] = frame.data[4];
nbytes = write(s, &resp_frame, sizeof(struct can_frame));
if (nbytes < 0) {
perror("CAN RAW write error");
} else {
printf("sent response.\n");
}
}
close(s);
printf("'%s' closed. bye.\n", CAN_INTERFACE);
return 0;
}
출력결과

'STM32' 카테고리의 다른 글
DHT22 온습도 센서 (0) | 2025.03.23 |
---|---|
OLED display 128x32 (0) | 2025.02.28 |
STM32 clock (0) | 2025.01.23 |
FreeRTOS - STM32F4 포팅 정보 확인 (0) | 2025.01.14 |
FreeRTOS 와 Zephyr 비교 (0) | 2025.01.13 |