SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트). 전체 코드리뷰에서 도출된 보안 하드닝 10건 반영: - 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시) - 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php) - 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점) - CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php) - 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화 _backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
176 lines
6.4 KiB
C
176 lines
6.4 KiB
C
/* =============================================================================
|
|
* applog.c - UART 디버그 로그 (RPi setup_logger() 대응)
|
|
*
|
|
* USART3(PD8 TX / PD9 RX, APP_LOG_UART_BAUD) 로 printf 스타일
|
|
* 라인 로그를 출력한다. 라인은 다음 형식으로 찍힌다:
|
|
*
|
|
* [ 12345 ms] INFO 메시지...
|
|
*
|
|
* 직렬화:
|
|
* FreeRTOS 스케줄러 동작 중이면 뮤텍스로 라인 단위 직렬화한다.
|
|
* 부팅 초기(스케줄러 시작 전, 또는 ISR 문맥)에는 뮤텍스 없이 그대로 출력한다.
|
|
*
|
|
* USART3 HAL 핸들과 MspInit(핀/클럭) 은 이 파일이 소유한다.
|
|
* ===========================================================================*/
|
|
#include "applog.h"
|
|
#include "app_config.h"
|
|
|
|
#include "stm32f4xx_hal.h"
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "semphr.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* ── 설정 상수 ───────────────────────────────────────────────────────────── */
|
|
/* 한 줄 로그 최대 길이(접두부 포함). 스택 버퍼 크기. */
|
|
#define LOG_LINE_MAX 160
|
|
/* HAL_UART_Transmit 블로킹 타임아웃(ms). */
|
|
#define LOG_TX_TIMEOUT_MS 100u
|
|
/* 뮤텍스 획득 대기(ms). 초과 시 직렬화 포기하고 그대로 출력(로그는 best-effort). */
|
|
#define LOG_MUTEX_WAIT_MS 50u
|
|
|
|
/* ── USART3 HAL 핸들 (이 모듈 소유) ───────────────────────────────────────── */
|
|
static UART_HandleTypeDef huart_log;
|
|
|
|
/* ── 직렬화용 뮤텍스 + 초기화 플래그 ─────────────────────────────────────── */
|
|
static SemaphoreHandle_t s_logMutex = NULL;
|
|
static volatile int s_inited = 0;
|
|
|
|
/* =============================================================================
|
|
* USART3 MspInit — PD8(TX)/PD9(RX), AF7, 클럭 게이팅
|
|
* HAL_UART_Init() 내부에서 HAL_UART_MspInit() 약함수 형태로 호출된다.
|
|
* (여러 UART 를 쓰는 프로젝트라면 instance 분기. 여기선 USART3 만 처리.)
|
|
* ===========================================================================*/
|
|
void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle)
|
|
{
|
|
GPIO_InitTypeDef g = {0};
|
|
|
|
if (uartHandle->Instance != USART3) {
|
|
return; /* 다른 UART 는 해당 모듈이 처리 */
|
|
}
|
|
|
|
__HAL_RCC_GPIOD_CLK_ENABLE();
|
|
__HAL_RCC_USART3_CLK_ENABLE();
|
|
|
|
/* PD8=USART3_TX, PD9=USART3_RX, AF7.
|
|
* (PA2/PA3 는 LAN8720 RMII 의 ETH_MDIO/미사용과 충돌하므로 사용 불가 →
|
|
* 로그 UART 는 USART3 PD8/PD9 로 배치한다. HARDWARE.md 핀맵 참조.) */
|
|
g.Pin = GPIO_PIN_8 | GPIO_PIN_9;
|
|
g.Mode = GPIO_MODE_AF_PP;
|
|
g.Pull = GPIO_PULLUP;
|
|
g.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
|
g.Alternate = GPIO_AF7_USART3;
|
|
HAL_GPIO_Init(GPIOD, &g);
|
|
}
|
|
|
|
/* =============================================================================
|
|
* applog_init — USART3 초기화 + 로그 뮤텍스 생성
|
|
* ===========================================================================*/
|
|
void applog_init(void)
|
|
{
|
|
huart_log.Instance = USART3;
|
|
huart_log.Init.BaudRate = APP_LOG_UART_BAUD;
|
|
huart_log.Init.WordLength = UART_WORDLENGTH_8B;
|
|
huart_log.Init.StopBits = UART_STOPBITS_1;
|
|
huart_log.Init.Parity = UART_PARITY_NONE;
|
|
huart_log.Init.Mode = UART_MODE_TX_RX;
|
|
huart_log.Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
|
huart_log.Init.OverSampling = UART_OVERSAMPLING_16;
|
|
|
|
if (HAL_UART_Init(&huart_log) != HAL_OK) {
|
|
/* UART 를 못 켜면 로그는 무음 처리(디바이스 동작 자체는 계속). */
|
|
s_inited = 0;
|
|
return;
|
|
}
|
|
|
|
/* 뮤텍스 생성 시도. 실패해도(메모리 부족 등) 로그는 비직렬화로 동작. */
|
|
s_logMutex = xSemaphoreCreateMutex();
|
|
s_inited = 1;
|
|
}
|
|
|
|
/* ── 스케줄러가 돌고 있고 ISR 문맥이 아닐 때만 뮤텍스를 쓴다 ──────────────── */
|
|
static int log_can_use_mutex(void)
|
|
{
|
|
if (s_logMutex == NULL) {
|
|
return 0;
|
|
}
|
|
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
|
return 0; /* 스케줄러 시작 전: 뮤텍스 블로킹 불가 */
|
|
}
|
|
if (__get_IPSR() != 0u) {
|
|
return 0; /* ISR 문맥: 블로킹 뮤텍스 호출 금지 */
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* ── 원시 바이트 전송(블로킹) ────────────────────────────────────────────── */
|
|
static void log_tx(const char *buf, uint16_t len)
|
|
{
|
|
if (len == 0u) {
|
|
return;
|
|
}
|
|
(void)HAL_UART_Transmit(&huart_log, (uint8_t *)buf, len, LOG_TX_TIMEOUT_MS);
|
|
}
|
|
|
|
/* =============================================================================
|
|
* applog — printf 스타일 라인 로그
|
|
* 형식: "[%8lu ms] %-5s <msg>\r\n"
|
|
* ===========================================================================*/
|
|
void applog(const char *level, const char *fmt, ...)
|
|
{
|
|
char line[LOG_LINE_MAX];
|
|
int n;
|
|
int pos = 0;
|
|
va_list ap;
|
|
int locked = 0;
|
|
|
|
if (!s_inited) {
|
|
return; /* UART 미초기화: 무음 */
|
|
}
|
|
if (level == NULL) {
|
|
level = "?";
|
|
}
|
|
|
|
/* 업타임 스탬프(ms). 스케줄러 전/후 모두 HAL_GetTick() 으로 일관 — TIM6 기반. */
|
|
n = snprintf(line, sizeof(line), "[%8lu ms] %-5s ",
|
|
(unsigned long)HAL_GetTick(), level);
|
|
if (n < 0) {
|
|
return;
|
|
}
|
|
pos = (n < (int)sizeof(line)) ? n : (int)sizeof(line) - 1;
|
|
|
|
/* 사용자 메시지 */
|
|
va_start(ap, fmt);
|
|
n = vsnprintf(line + pos, sizeof(line) - (size_t)pos, fmt, ap);
|
|
va_end(ap);
|
|
if (n > 0) {
|
|
pos += n;
|
|
if (pos > (int)sizeof(line) - 1) {
|
|
pos = (int)sizeof(line) - 1; /* vsnprintf 가 잘라낸 경우 */
|
|
}
|
|
}
|
|
|
|
/* 줄바꿈(\r\n). 공간이 모자라면 마지막 2바이트를 덮어쓴다. */
|
|
if (pos > (int)sizeof(line) - 3) {
|
|
pos = (int)sizeof(line) - 3;
|
|
}
|
|
line[pos++] = '\r';
|
|
line[pos++] = '\n';
|
|
|
|
/* 직렬화 후 전송 */
|
|
if (log_can_use_mutex()) {
|
|
if (xSemaphoreTake(s_logMutex, pdMS_TO_TICKS(LOG_MUTEX_WAIT_MS)) == pdTRUE) {
|
|
locked = 1;
|
|
}
|
|
}
|
|
|
|
log_tx(line, (uint16_t)pos);
|
|
|
|
if (locked) {
|
|
(void)xSemaphoreGive(s_logMutex);
|
|
}
|
|
}
|