/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2026 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "string.h" #include "E22.h" #include "flash.h" #include "SoilSensor.h" #include "hyp_ringbuffer.h" #include "DS3231.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define LORA_HEADER1 0x55U #define LORA_HEADER2 0xAAU #define LORA_VERSION 2U #define LORA_TYPE_REQ 1U #define LORA_TYPE_RESP 2U #define LORA_TYPE_ACK 3U #define LORA_TYPE_TIME_SYNC 4U #define MASTER_ID 0UL #define RESP_RETRY_MAX 3U #define RESP_REPEAT_GAP_MS 1000UL #define BATTERY_ADC_VREF_MV 3300UL #define BATTERY_ADC_MAX 4095UL #define BATTERY_DIV_R_TOP_OHM 1000000UL #define BATTERY_DIV_R_BOTTOM_OHM 560000UL #define BATTERY_ADC_SAMPLE_COUNT 16U #define LORA_DMA_RX_SIZE 256U #define ACK_TIMEOUT_MS 3000UL #define APP_READ_SOIL_ONLY 0U #define SOIL_READ_INTERVAL_MS 2000UL #define SOIL_SENSOR_UART_BAUDRATE 9600U #define LORA_SEND_INTERVAL_MS 3000UL #define LORA_SEND_REPEAT_COUNT 3U #define LORA_SEND_REPEAT_GAP_MS 2000UL #define LORA_HELLO_TEST 0U #define LORA_HELLO_INTERVAL_MS 1000UL #define APP_DS3231_WAKE_MODE 1U #define POWER_STABILIZE_MS 1200UL #define LORA_ACK_RETRY_GAP_MS 1000UL #define LORA_NODE_ID_BASE 1001UL #define LORA_NODE_SLOT_COUNT 4UL #define LORA_NODE_SLOT_MS 6000UL #define RELAY_CHAIN_NODE_COUNT 2U #if ((RELAY_CHAIN_NODE_COUNT < 2U) || (RELAY_CHAIN_NODE_COUNT > 4U)) #error "RELAY_CHAIN_NODE_COUNT must be 2, 3, or 4" #endif #define APP_RELAY_TEST_MODE 1U #if (APP_RELAY_TEST_MODE != 0U) #define DS3231_WAKE_INTERVAL_MINUTES 5U #else #define DS3231_WAKE_INTERVAL_MINUTES 240U #endif #define DS3231_FALLBACK_DELAY_MS 60000UL #define DS3231_INT_RELEASE_TIMEOUT_MS 1000UL #define SLEEP_DEBUG_LED_ACTIVE_LOW 1U #define RELAY_QUEUE_SIZE 8U #define RELAY_TTL_INITIAL RELAY_CHAIN_NODE_COUNT #define RELAY_MAX_RETRIES 5U #define RELAY_ACK_REPEAT_COUNT 2U #define RELAY_ACK_REPEAT_GAP_MS 100U #define TIME_SYNC_ACK_REPEAT_COUNT 3U #define TIME_SYNC_ACK_REPEAT_GAP_MS 500U #define RELAY_RETRY_GAP_MS 500U #define RELAY_POLL_DELAY_MS 2U #define TIME_SYNC_GMT_OFFSET_SEC 25200UL #define TIME_SYNC_FINAL_NODE_ID (LORA_NODE_ID_BASE + RELAY_CHAIN_NODE_COUNT - 1UL) #define RELAY_NODE4_TX_OFFSET_MS 25000UL #define RELAY_NODE3_TX_OFFSET_MS 40000UL #define RELAY_NODE2_TX_OFFSET_MS 60000UL #define RELAY_NODE1_TX_OFFSET_MS 85000UL #define DS3231_SET_TIME_ON_BOOT 0U #define APP_RS485_ID_TOOL_MODE 0U #define APP_E22_CONFIG_READ_MODE 0U /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ ADC_HandleTypeDef hadc1; I2C_HandleTypeDef hi2c1; UART_HandleTypeDef huart1; UART_HandleTypeDef huart2; DMA_HandleTypeDef hdma_usart1_rx; DMA_HandleTypeDef hdma_usart1_tx; DMA_HandleTypeDef hdma_usart2_rx; DMA_HandleTypeDef hdma_usart2_tx; /* USER CODE BEGIN PV */ typedef struct __attribute__((packed)) { uint8_t header1; uint8_t header2; uint8_t version; uint8_t type; uint32_t src_id; uint32_t dst_id; uint32_t hop_src_id; uint32_t hop_dst_id; uint32_t seq; uint32_t timestamp; uint16_t humidity; int16_t temperature; uint16_t sht_humidity; int16_t sht_temperature; uint16_t ph; uint16_t nitrogen; uint16_t phosphorus; uint16_t potassium; uint16_t battery_mv; uint8_t status; uint8_t ttl; uint8_t reserved[5]; uint16_t crc16; } LoraPacket_t; typedef struct { LoraPacket_t packet; uint8_t used; } RelayQueueEntry_t; _Static_assert(sizeof(LoraPacket_t) == 55U, "STM32 LoRa packet size mismatch"); SoilNpkData_t soilData; LoraPacket_t rxPacket; LoraPacket_t txPacket; HYP_RINGBUFFER_Obj loraRxRing; uint8_t loraDmaRxBuf[LORA_DMA_RX_SIZE]; uint16_t loraDmaOldPos = 0U; volatile uint32_t deviceId = 0U; volatile uint8_t sensorStatus = 0U; volatile HAL_StatusTypeDef lora_tx_ret = HAL_OK; volatile HAL_StatusTypeDef lora_rx_dma_ret = HAL_OK; volatile uint32_t idle_count = 0U; volatile uint32_t lora_dma_rx_count = 0U; volatile uint32_t lora_ring_available = 0U; volatile uint32_t lora_packet_count = 0U; volatile uint32_t lora_crc_fail_count = 0U; volatile uint32_t lora_drop_count = 0U; volatile uint32_t req_count = 0U; volatile uint32_t resp_count = 0U; volatile uint8_t resp_pending = 0U; volatile uint32_t resp_retry_count = 0U; volatile uint32_t resp_fail_count = 0U; volatile uint8_t ack_pending = 0U; volatile uint32_t pending_ack_seq = 0U; volatile uint32_t ack_wait_start = 0U; volatile uint32_t ack_count = 0U; volatile uint32_t ack_timeout_count = 0U; volatile uint32_t main_loop_count = 0U; volatile uint32_t init_step = 0U; volatile uint32_t soil_read_count = 0U; volatile uint32_t soil_read_fail_count = 0U; volatile uint32_t soil_last_read_tick = 0U; volatile uint32_t lora_last_send_tick = 0U; volatile uint32_t lora_send_seq = 0U; volatile uint8_t soil_data_valid = 0U; volatile uint32_t app_start_tick = 0U; volatile uint32_t lora_packet_cycle_count = 0U; volatile uint32_t last_request_seq = 0U; volatile uint32_t duplicate_req_count = 0U; volatile uint8_t ds3231_wake_ready = 0U; volatile uint32_t ds3231_stop_count = 0U; volatile uint32_t ds3231_wake_count = 0U; volatile uint32_t ds3231_int_low_before_stop_count = 0U; RelayQueueEntry_t relayQueue[RELAY_QUEUE_SIZE]; volatile uint8_t relay_queue_count = 0U; volatile uint8_t relay_cycle_stage = 0U; volatile uint32_t relay_parent_id = MASTER_ID; volatile uint32_t relay_rx_count = 0U; volatile uint32_t relay_forward_count = 0U; volatile uint32_t relay_ack_sent_count = 0U; volatile uint32_t relay_duplicate_count = 0U; volatile uint32_t relay_queue_full_count = 0U; volatile uint32_t relay_retry_exhausted_count = 0U; volatile uint32_t relay_ttl_drop_count = 0U; volatile uint32_t relay_time_sync_count = 0U; volatile uint32_t relay_time_sync_forward_count = 0U; volatile uint32_t relay_time_sync_ack_forward_count = 0U; volatile uint32_t relay_time_sync_last_epoch = 0U; volatile uint32_t relay_time_sync_last_seq = 0U; volatile uint8_t relay_time_sync_last_status = 0U; volatile HAL_StatusTypeDef e22_config_read_status = HAL_ERROR; volatile uint8_t e22_config_raw[9] = {0U}; volatile uint16_t e22_config_address = 0U; volatile uint8_t e22_config_netid = 0U; volatile uint8_t e22_config_uart_baud_code = 0U; volatile uint8_t e22_config_uart_parity_code = 0U; volatile uint8_t e22_config_air_rate_code = 0U; volatile uint8_t e22_config_channel = 0U; volatile uint8_t e22_config_tx_power_code = 0U; volatile uint8_t e22_config_fixed_mode = 0U; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_USART1_UART_Init(void); static void MX_USART2_UART_Init(void); static void MX_ADC1_Init(void); static void MX_I2C1_Init(void); /* USER CODE BEGIN PFP */ static uint16_t Crc16Ccitt(uint8_t *data, uint16_t len); static void FinalizePacket(LoraPacket_t *packet); static uint8_t ValidatePacket(LoraPacket_t *packet); static uint16_t LoraRingPeek(uint8_t *data, uint16_t len); static void LoraRingDrop(uint16_t len); static void LoraUart_InitDma(void); static void LoraUart_ProcessDma(void); static uint8_t LoraTryReadPacket(LoraPacket_t *packet); static uint8_t SendPendingResponse(void); static uint8_t SendResponsePacket(void); static void SendResponseRepeated(void); static uint16_t ReadBatteryMv(void); static void CheckAckTimeout(void); static void HandleAck(const LoraPacket_t *ack); static void HandleRequest(const LoraPacket_t *request); static void HandlePacket(const LoraPacket_t *packet); static void UpdateSoilDataIfDue(void); static void SendSoilDataIfDue(void); static void ReadAndSendSoilData(void); static uint8_t WaitForAck(uint32_t seq, uint32_t timeoutMs); static uint32_t GetNodeTxSlotDelayMs(void); static void AppPowerOn(void); static void AppPowerOff(void); static void AppRestorePoweredSignalPins(void); static void AppReleasePoweredSignalPins(void); static void EnterStopUntilDs3231Wake(void); static void ReadAndSendSoilDataUntilAck(void); static uint32_t RelayGetParentId(void); static uint32_t RelayGetChildId(void); static uint32_t RelayGetTxOffsetMs(void); static int8_t RelayQueueFind(uint32_t originId, uint32_t seq); static int8_t RelayQueueStore(const LoraPacket_t *packet); static void RelaySendAck(const LoraPacket_t *dataPacket); static void RelayHandleDataPacket(const LoraPacket_t *packet); static uint8_t RelayEpochToDateTime(uint32_t epoch, DS3231_DateTime_t *dateTime); static void RelaySendTimeSyncAck(const LoraPacket_t *packet); static void RelayHandleTimeSyncPacket(const LoraPacket_t *packet); static void RelayHandleTimeSyncAck(const LoraPacket_t *packet); static void RelayHandleIncomingPacket(const LoraPacket_t *packet); static uint8_t RelayWaitForAck(uint32_t originId, uint32_t seq, uint32_t parentId); static void RelayListenUntil(uint32_t cycleStart, uint32_t offsetMs); static void RelayForwardQueue(uint32_t parentId); static void RunRelayCycle(void); static uint8_t AppDs3231InitWakeEveryMinute(void); static void SleepDebugLedAwake(void); static void SleepDebugLedSleep(void); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ static uint16_t Crc16Ccitt(uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFFU; for (uint16_t i = 0U; i < len; i++) { crc ^= (uint16_t)data[i] << 8; for (uint8_t j = 0U; j < 8U; j++) { if ((crc & 0x8000U) != 0U) { crc = (uint16_t)((crc << 1) ^ 0x1021U); } else { crc <<= 1; } } } return crc; } static void FinalizePacket(LoraPacket_t *packet) { packet->crc16 = 0U; packet->crc16 = Crc16Ccitt((uint8_t *)packet, sizeof(LoraPacket_t) - 2U); } static uint8_t ValidatePacket(LoraPacket_t *packet) { uint16_t crc_recv = packet->crc16; uint16_t crc_calc; if ((packet->header1 != LORA_HEADER1) || (packet->header2 != LORA_HEADER2) || (packet->version != LORA_VERSION)) { return 0U; } packet->crc16 = 0U; crc_calc = Crc16Ccitt((uint8_t *)packet, sizeof(LoraPacket_t) - 2U); packet->crc16 = crc_recv; return (crc_recv == crc_calc) ? 1U : 0U; } static uint16_t ReadBatteryMv(void) { uint32_t adcSum = 0U; uint8_t validSamples = 0U; for (uint8_t i = 0U; i <= BATTERY_ADC_SAMPLE_COUNT; i++) { if (HAL_ADC_Start(&hadc1) != HAL_OK) { continue; } if (HAL_ADC_PollForConversion(&hadc1, 20U) == HAL_OK) { uint32_t raw = HAL_ADC_GetValue(&hadc1); if (i != 0U) { adcSum += raw; validSamples++; } } (void)HAL_ADC_Stop(&hadc1); HAL_Delay(1U); } if (validSamples == 0U) { return 0U; } uint32_t adcAvg = adcSum / validSamples; uint64_t batteryMv = (uint64_t)adcAvg * BATTERY_ADC_VREF_MV * (BATTERY_DIV_R_TOP_OHM + BATTERY_DIV_R_BOTTOM_OHM); batteryMv /= ((uint64_t)BATTERY_ADC_MAX * BATTERY_DIV_R_BOTTOM_OHM); if (batteryMv > 65535ULL) { return 65535U; } return (uint16_t)batteryMv; } static uint16_t LoraRingPeek(uint8_t *data, uint16_t len) { uint16_t i; uint16_t tail = loraRxRing.tail; for (i = 0U; i < len; i++) { if (tail == loraRxRing.head) { break; } data[i] = loraRxRing.buffer[tail]; tail = (uint16_t)((tail + 1U) % HYP_RINGBUFFER_SIZE); } return i; } static void LoraRingDrop(uint16_t len) { uint8_t dummy; while (len > 0U) { if (RingBuffer_Read(&loraRxRing, &dummy, 1U) == 0U) { break; } len--; } } static void LoraUart_InitDma(void) { RingBuffer_Init(&loraRxRing); memset(loraDmaRxBuf, 0, sizeof(loraDmaRxBuf)); loraDmaOldPos = 0U; __HAL_UART_CLEAR_OREFLAG(&huart1); lora_rx_dma_ret = HAL_UART_Receive_DMA(&huart1, loraDmaRxBuf, LORA_DMA_RX_SIZE); } static void LoraUart_ProcessDma(void) { uint16_t newPos; if (huart1.hdmarx == 0) { return; } newPos = (uint16_t)(LORA_DMA_RX_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx)); while (loraDmaOldPos != newPos) { uint8_t byte = loraDmaRxBuf[loraDmaOldPos]; if (RingBuffer_Write(&loraRxRing, &byte, 1U) == 0U) { LoraRingDrop(1U); lora_drop_count++; if (RingBuffer_Write(&loraRxRing, &byte, 1U) == 0U) { loraDmaOldPos = (uint16_t)((loraDmaOldPos + 1U) % LORA_DMA_RX_SIZE); continue; } } loraDmaOldPos = (uint16_t)((loraDmaOldPos + 1U) % LORA_DMA_RX_SIZE); lora_dma_rx_count++; } lora_ring_available = RingBuffer_Available(&loraRxRing); } static uint8_t LoraTryReadPacket(LoraPacket_t *packet) { uint8_t header[2]; while (RingBuffer_Available(&loraRxRing) >= 2U) { if (LoraRingPeek(header, 2U) < 2U) { return 0U; } if (header[0] != LORA_HEADER1) { LoraRingDrop(1U); lora_drop_count++; continue; } if (header[1] != LORA_HEADER2) { LoraRingDrop(1U); lora_drop_count++; continue; } if (RingBuffer_Available(&loraRxRing) < sizeof(LoraPacket_t)) { return 0U; } RingBuffer_Read(&loraRxRing, (uint8_t *)packet, sizeof(LoraPacket_t)); if (ValidatePacket(packet) != 0U) { lora_packet_count++; return 1U; } lora_crc_fail_count++; } return 0U; } static void BuildResponsePacket(LoraPacket_t *packet, const LoraPacket_t *request) { memset(packet, 0, sizeof(*packet)); packet->header1 = LORA_HEADER1; packet->header2 = LORA_HEADER2; packet->version = LORA_VERSION; packet->type = LORA_TYPE_RESP; packet->src_id = deviceId; packet->dst_id = MASTER_ID; packet->hop_src_id = deviceId; packet->hop_dst_id = RelayGetParentId(); packet->seq = request->seq; packet->timestamp = HAL_GetTick() / 1000UL; packet->humidity = soilData.humidity; packet->temperature = soilData.temperature; packet->sht_humidity = soilData.sht_humidity; packet->sht_temperature = soilData.sht_temperature; packet->ph = soilData.ph; packet->nitrogen = soilData.nitrogen; packet->phosphorus = soilData.phosphorus; packet->potassium = soilData.potassium; packet->battery_mv = ReadBatteryMv(); packet->status = sensorStatus; packet->ttl = RELAY_TTL_INITIAL; packet->reserved[0] = 0xA5U; packet->reserved[1] = soil_npk_debug_source; packet->reserved[2] = soil_npk_debug_low_status; packet->reserved[3] = 0U; packet->reserved[4] = 0U; } static void BuildSoilPacket(LoraPacket_t *packet) { memset(packet, 0, sizeof(*packet)); packet->header1 = LORA_HEADER1; packet->header2 = LORA_HEADER2; packet->version = LORA_VERSION; packet->type = LORA_TYPE_RESP; packet->src_id = deviceId; packet->dst_id = MASTER_ID; packet->hop_src_id = deviceId; packet->hop_dst_id = RelayGetParentId(); packet->seq = ++lora_send_seq; packet->timestamp = HAL_GetTick() / 1000UL; packet->humidity = soilData.humidity; packet->temperature = soilData.temperature; packet->sht_humidity = soilData.sht_humidity; packet->sht_temperature = soilData.sht_temperature; packet->ph = soilData.ph; packet->nitrogen = soilData.nitrogen; packet->phosphorus = soilData.phosphorus; packet->potassium = soilData.potassium; packet->battery_mv = ReadBatteryMv(); packet->status = sensorStatus; packet->ttl = RELAY_TTL_INITIAL; packet->reserved[0] = 0xA5U; packet->reserved[1] = soil_npk_debug_source; packet->reserved[2] = soil_npk_debug_low_status; packet->reserved[3] = 0U; packet->reserved[4] = 0U; } static uint8_t SendPendingResponse(void) { if (resp_pending == 0U) { return 1U; } FinalizePacket(&txPacket); lora_tx_ret = E22_Send((uint8_t *)&txPacket, sizeof(txPacket)); if (lora_tx_ret == HAL_OK) { resp_pending = 0U; ack_pending = 1U; pending_ack_seq = txPacket.seq; ack_wait_start = HAL_GetTick(); resp_count++; return 1U; } resp_retry_count++; resp_fail_count++; return 0U; } static uint8_t SendResponsePacket(void) { FinalizePacket(&txPacket); lora_tx_ret = E22_Send((uint8_t *)&txPacket, sizeof(txPacket)); if (lora_tx_ret == HAL_OK) { resp_count++; return 1U; } resp_retry_count++; resp_fail_count++; return 0U; } static void SendResponseRepeated(void) { for (uint8_t retry = 0U; retry < RESP_RETRY_MAX; retry++) { (void)SendResponsePacket(); HAL_Delay(RESP_REPEAT_GAP_MS); } resp_pending = 0U; ack_pending = 0U; pending_ack_seq = txPacket.seq; ack_wait_start = HAL_GetTick(); } static void CheckAckTimeout(void) { if (ack_pending == 0U) { return; } if ((HAL_GetTick() - ack_wait_start) < ACK_TIMEOUT_MS) { return; } ack_timeout_count++; ack_pending = 0U; resp_pending = 1U; } static void HandleAck(const LoraPacket_t *ack) { if ((ack->type != LORA_TYPE_ACK) || (ack->src_id != MASTER_ID) || (ack->dst_id != deviceId) || (ack_pending == 0U) || (ack->seq != pending_ack_seq)) { return; } ack_pending = 0U; resp_pending = 0U; ack_count++; } static void HandleRequest(const LoraPacket_t *request) { if ((request->type != LORA_TYPE_REQ) || (request->dst_id != deviceId)) { return; } req_count++; resp_pending = 1U; if (request->seq == last_request_seq) { duplicate_req_count++; SendResponseRepeated(); return; } last_request_seq = request->seq; sensorStatus = SoilSensor_Read(&soilData); soil_read_count++; if (sensorStatus == 0U) { soil_data_valid = 1U; } else { soil_read_fail_count++; } BuildResponsePacket(&txPacket, request); SendResponseRepeated(); } static void HandlePacket(const LoraPacket_t *packet) { if (packet->type == LORA_TYPE_ACK) { HandleAck(packet); return; } if (packet->type == LORA_TYPE_REQ) { HandleRequest(packet); } } static void UpdateSoilDataIfDue(void) { SoilNpkData_t newSoilData; uint8_t newSensorStatus; if ((HAL_GetTick() - soil_last_read_tick) < SOIL_READ_INTERVAL_MS) { return; } soil_last_read_tick = HAL_GetTick(); newSensorStatus = SoilSensor_Read(&newSoilData); soil_read_count++; if (newSensorStatus == 0U) { soilData = newSoilData; sensorStatus = 0U; soil_data_valid = 1U; } else { sensorStatus = newSensorStatus; soil_read_fail_count++; } } static void SendSoilDataIfDue(void) { if (soil_data_valid == 0U) { return; } if ((HAL_GetTick() - lora_last_send_tick) < LORA_SEND_INTERVAL_MS) { return; } lora_last_send_tick = HAL_GetTick(); BuildSoilPacket(&txPacket); FinalizePacket(&txPacket); lora_tx_ret = E22_Send((uint8_t *)&txPacket, sizeof(txPacket)); if (lora_tx_ret == HAL_OK) { resp_count++; } else { resp_fail_count++; } } static void ReadAndSendSoilData(void) { sensorStatus = SoilSensor_Read(&soilData); soil_read_count++; if (sensorStatus == 0U) { soil_data_valid = 1U; } else { soil_read_fail_count++; return; } BuildSoilPacket(&txPacket); FinalizePacket(&txPacket); for (uint8_t repeat = 0U; repeat < LORA_SEND_REPEAT_COUNT; repeat++) { lora_tx_ret = E22_Send((uint8_t *)&txPacket, sizeof(txPacket)); if (lora_tx_ret != HAL_OK) { resp_fail_count++; } HAL_Delay(LORA_SEND_REPEAT_GAP_MS); } lora_packet_cycle_count++; resp_count++; } static uint8_t WaitForAck(uint32_t seq, uint32_t timeoutMs) { uint32_t startTick = HAL_GetTick(); while ((HAL_GetTick() - startTick) < timeoutMs) { LoraUart_ProcessDma(); if (LoraTryReadPacket(&rxPacket) == 0U) { continue; } if ((rxPacket.type == LORA_TYPE_ACK) && (rxPacket.hop_src_id == RelayGetParentId()) && (rxPacket.hop_dst_id == deviceId) && (rxPacket.seq == seq)) { ack_count++; return 1U; } } return 0U; } static uint32_t GetNodeTxSlotDelayMs(void) { uint32_t slot; if (deviceId >= LORA_NODE_ID_BASE) { slot = (deviceId - LORA_NODE_ID_BASE) % LORA_NODE_SLOT_COUNT; } else { slot = deviceId % LORA_NODE_SLOT_COUNT; } return slot * LORA_NODE_SLOT_MS; } static uint8_t AppDs3231InitWakeEveryMinute(void) { if (DS3231_Init(&hi2c1) == 0U) { return 0U; } #if (DS3231_SET_TIME_ON_BOOT != 0U) const DS3231_DateTime_t dateTime = { .year = 26U, .month = 7U, .date = 15U, .day = 4U, .hour = 14U, .minute = 53U, .second = 0U }; (void)DS3231_SetDateTime(&dateTime); #endif return DS3231_ConfigAlarm1AfterMinutes(DS3231_WAKE_INTERVAL_MINUTES); } static void SleepDebugLedAwake(void) { #if (SLEEP_DEBUG_LED_ACTIVE_LOW != 0U) HAL_GPIO_WritePin(SLEEP_DEBUG_LED_GPIO_Port, SLEEP_DEBUG_LED_Pin, GPIO_PIN_RESET); #else HAL_GPIO_WritePin(SLEEP_DEBUG_LED_GPIO_Port, SLEEP_DEBUG_LED_Pin, GPIO_PIN_SET); #endif } static void SleepDebugLedSleep(void) { #if (SLEEP_DEBUG_LED_ACTIVE_LOW != 0U) HAL_GPIO_WritePin(SLEEP_DEBUG_LED_GPIO_Port, SLEEP_DEBUG_LED_Pin, GPIO_PIN_SET); #else HAL_GPIO_WritePin(SLEEP_DEBUG_LED_GPIO_Port, SLEEP_DEBUG_LED_Pin, GPIO_PIN_RESET); #endif } static void AppRestorePoweredSignalPins(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; HAL_GPIO_WritePin(E22_M1_GPIO_Port, E22_M1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, E22_M0_Pin | RS485_RE_Pin | RS485_DE_Pin, GPIO_PIN_RESET); GPIO_InitStruct.Pin = E22_M1_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(E22_M1_GPIO_Port, &GPIO_InitStruct); GPIO_InitStruct.Pin = E22_M0_Pin | RS485_RE_Pin | RS485_DE_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = E22_AUX_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(E22_AUX_GPIO_Port, &GPIO_InitStruct); } static void AppReleasePoweredSignalPins(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; (void)HAL_UART_DMAStop(&huart1); (void)HAL_UART_DMAStop(&huart2); (void)HAL_UART_DeInit(&huart1); (void)HAL_UART_DeInit(&huart2); GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3 | E22_M1_Pin | GPIO_PIN_9 | GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = E22_M0_Pin | E22_AUX_Pin | RS485_RE_Pin | RS485_DE_Pin; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } static void AppPowerOn(void) { HAL_GPIO_WritePin(GPIOB, PWR_E22_WAKE_Pin | PWR_SOIL_WAKE_Pin, GPIO_PIN_SET); AppRestorePoweredSignalPins(); HAL_Delay(POWER_STABILIZE_MS); MX_USART1_UART_Init(); MX_USART2_UART_Init(); E22_Init(&huart1); LoraUart_InitDma(); SoilSensor_Init(); } static void AppPowerOff(void) { AppReleasePoweredSignalPins(); HAL_GPIO_WritePin(GPIOB, PWR_E22_WAKE_Pin | PWR_SOIL_WAKE_Pin, GPIO_PIN_RESET); } static void EnterStopUntilDs3231Wake(void) { uint32_t waitStart; if (DS3231_ConfigAlarm1AfterMinutes(DS3231_WAKE_INTERVAL_MINUTES) == 0U) { HAL_Delay(DS3231_FALLBACK_DELAY_MS); return; } __HAL_GPIO_EXTI_CLEAR_IT(DS3231_INT_Pin); HAL_NVIC_ClearPendingIRQ(DS3231_INT_EXTI_IRQn); waitStart = HAL_GetTick(); while (HAL_GPIO_ReadPin(DS3231_INT_GPIO_Port, DS3231_INT_Pin) == GPIO_PIN_RESET) { if ((HAL_GetTick() - waitStart) >= DS3231_INT_RELEASE_TIMEOUT_MS) { ds3231_int_low_before_stop_count++; HAL_Delay(DS3231_FALLBACK_DELAY_MS); return; } (void)DS3231_ClearAlarm1(); HAL_Delay(10U); } __HAL_GPIO_EXTI_CLEAR_IT(DS3231_INT_Pin); HAL_NVIC_ClearPendingIRQ(DS3231_INT_EXTI_IRQn); ds3231_stop_count++; SleepDebugLedSleep(); HAL_SuspendTick(); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); HAL_ResumeTick(); SystemClock_Config(); SleepDebugLedAwake(); ds3231_wake_count++; } static void ReadAndSendSoilDataUntilAck(void) { sensorStatus = SoilSensor_Read(&soilData); soil_read_count++; if (sensorStatus == 0U) { soil_data_valid = 1U; } else { soil_read_fail_count++; } BuildSoilPacket(&txPacket); FinalizePacket(&txPacket); ack_pending = 1U; pending_ack_seq = txPacket.seq; HAL_Delay(GetNodeTxSlotDelayMs()); do { lora_tx_ret = E22_Send((uint8_t *)&txPacket, sizeof(txPacket)); if (lora_tx_ret == HAL_OK) { resp_count++; } else { resp_fail_count++; } if (WaitForAck(txPacket.seq, ACK_TIMEOUT_MS) != 0U) { break; } ack_timeout_count++; HAL_Delay(LORA_ACK_RETRY_GAP_MS); } while (1); ack_pending = 0U; lora_packet_cycle_count++; } static uint32_t RelayGetParentId(void) { switch (deviceId) { case 1004UL: return 1003UL; case 1003UL: return 1002UL; case 1002UL: return 1001UL; case 1001UL: return MASTER_ID; default: return MASTER_ID; } } static uint32_t RelayGetChildId(void) { if ((deviceId >= LORA_NODE_ID_BASE) && (deviceId < TIME_SYNC_FINAL_NODE_ID)) { return deviceId + 1UL; } return MASTER_ID; } static uint32_t RelayGetTxOffsetMs(void) { switch (deviceId) { case 1004UL: return RELAY_NODE4_TX_OFFSET_MS; case 1003UL: return RELAY_NODE3_TX_OFFSET_MS; case 1002UL: return RELAY_NODE2_TX_OFFSET_MS; case 1001UL: return RELAY_NODE1_TX_OFFSET_MS; default: return RELAY_NODE4_TX_OFFSET_MS; } } static int8_t RelayQueueFind(uint32_t originId, uint32_t seq) { for (uint8_t i = 0U; i < RELAY_QUEUE_SIZE; i++) { if ((relayQueue[i].used != 0U) && (relayQueue[i].packet.src_id == originId) && (relayQueue[i].packet.seq == seq)) { return (int8_t)i; } } return -1; } static int8_t RelayQueueStore(const LoraPacket_t *packet) { int8_t existing = RelayQueueFind(packet->src_id, packet->seq); if (existing >= 0) { return existing; } for (uint8_t i = 0U; i < RELAY_QUEUE_SIZE; i++) { if (relayQueue[i].used == 0U) { relayQueue[i].packet = *packet; relayQueue[i].used = 1U; relay_queue_count++; return (int8_t)i; } } relay_queue_full_count++; return -1; } static void RelaySendAck(const LoraPacket_t *dataPacket) { LoraPacket_t ack; memset(&ack, 0, sizeof(ack)); ack.header1 = LORA_HEADER1; ack.header2 = LORA_HEADER2; ack.version = LORA_VERSION; ack.type = LORA_TYPE_ACK; ack.src_id = dataPacket->src_id; ack.dst_id = MASTER_ID; ack.hop_src_id = deviceId; ack.hop_dst_id = dataPacket->hop_src_id; ack.seq = dataPacket->seq; ack.timestamp = HAL_GetTick() / 1000UL; ack.ttl = dataPacket->ttl; for (uint8_t repeat = 0U; repeat < RELAY_ACK_REPEAT_COUNT; repeat++) { FinalizePacket(&ack); if (E22_Send((uint8_t *)&ack, sizeof(ack)) == HAL_OK) { relay_ack_sent_count++; } HAL_Delay(RELAY_ACK_REPEAT_GAP_MS); } } static void RelayHandleDataPacket(const LoraPacket_t *packet) { int8_t existing; if ((packet->type != LORA_TYPE_RESP) || (packet->hop_dst_id != deviceId) || (packet->src_id == deviceId)) { return; } if (packet->ttl == 0U) { relay_ttl_drop_count++; return; } existing = RelayQueueFind(packet->src_id, packet->seq); if (existing >= 0) { relay_duplicate_count++; RelaySendAck(packet); return; } if (RelayQueueStore(packet) < 0) { return; } relay_rx_count++; RelaySendAck(packet); } static uint8_t RelayEpochToDateTime(uint32_t epoch, DS3231_DateTime_t *dateTime) { static const uint8_t daysPerMonth[12] = {31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U}; uint32_t localEpoch; uint32_t dayIndex; uint32_t days; uint32_t secondsOfDay; uint16_t year = 1970U; uint8_t month = 1U; if ((dateTime == 0) || (epoch < 1577836800UL)) { return 0U; } localEpoch = epoch + TIME_SYNC_GMT_OFFSET_SEC; dayIndex = localEpoch / 86400UL; days = dayIndex; secondsOfDay = localEpoch % 86400UL; while (year <= 2099U) { uint8_t leap = (uint8_t)((((year % 4U) == 0U) && ((year % 100U) != 0U)) || ((year % 400U) == 0U)); uint16_t daysInYear = (uint16_t)(leap != 0U ? 366U : 365U); if (days < daysInYear) { break; } days -= daysInYear; year++; } if ((year < 2000U) || (year > 2099U)) { return 0U; } while (month <= 12U) { uint8_t daysInMonth = daysPerMonth[month - 1U]; if ((month == 2U) && (((year % 4U) == 0U) && (((year % 100U) != 0U) || ((year % 400U) == 0U)))) { daysInMonth++; } if (days < daysInMonth) { break; } days -= daysInMonth; month++; } if (month > 12U) { return 0U; } dateTime->year = (uint8_t)(year - 2000U); dateTime->month = month; dateTime->date = (uint8_t)(days + 1U); dateTime->day = (uint8_t)(((dayIndex + 4U) % 7U) + 1U); dateTime->hour = (uint8_t)(secondsOfDay / 3600UL); dateTime->minute = (uint8_t)((secondsOfDay % 3600UL) / 60UL); dateTime->second = (uint8_t)(secondsOfDay % 60UL); return 1U; } static void RelaySendTimeSyncAck(const LoraPacket_t *packet) { LoraPacket_t ack; memset(&ack, 0, sizeof(ack)); ack.header1 = LORA_HEADER1; ack.header2 = LORA_HEADER2; ack.version = LORA_VERSION; ack.type = LORA_TYPE_ACK; ack.src_id = MASTER_ID; ack.dst_id = TIME_SYNC_FINAL_NODE_ID; ack.hop_src_id = deviceId; ack.hop_dst_id = RelayGetParentId(); ack.seq = packet->seq; ack.timestamp = packet->timestamp; ack.ttl = RELAY_TTL_INITIAL; for (uint8_t repeat = 0U; repeat < TIME_SYNC_ACK_REPEAT_COUNT; repeat++) { FinalizePacket(&ack); if (E22_Send((uint8_t *)&ack, sizeof(ack)) == HAL_OK) { relay_time_sync_ack_forward_count++; } if ((repeat + 1U) < TIME_SYNC_ACK_REPEAT_COUNT) { HAL_Delay(TIME_SYNC_ACK_REPEAT_GAP_MS); } } } static void RelayHandleTimeSyncPacket(const LoraPacket_t *packet) { DS3231_DateTime_t dateTime; uint32_t childId; LoraPacket_t outgoing; if ((packet->type != LORA_TYPE_TIME_SYNC) || (packet->hop_dst_id != deviceId) || (packet->ttl == 0U)) { return; } relay_time_sync_last_epoch = packet->timestamp; relay_time_sync_last_seq = packet->seq; if (RelayEpochToDateTime(packet->timestamp, &dateTime) == 0U) { relay_time_sync_last_status = 1U; return; } if (DS3231_SetDateTime(&dateTime) == 0U) { relay_time_sync_last_status = 2U; return; } relay_time_sync_last_status = 0U; relay_time_sync_count++; childId = RelayGetChildId(); if (childId == MASTER_ID) { RelaySendTimeSyncAck(packet); return; } outgoing = *packet; outgoing.hop_src_id = deviceId; outgoing.hop_dst_id = childId; outgoing.ttl--; FinalizePacket(&outgoing); if (E22_Send((uint8_t *)&outgoing, sizeof(outgoing)) == HAL_OK) { relay_time_sync_forward_count++; } } static void RelayHandleTimeSyncAck(const LoraPacket_t *packet) { LoraPacket_t outgoing; uint32_t nextHop; if ((packet->type != LORA_TYPE_ACK) || (packet->src_id != MASTER_ID) || (packet->dst_id != TIME_SYNC_FINAL_NODE_ID) || (packet->hop_dst_id != deviceId)) { return; } nextHop = RelayGetParentId(); outgoing = *packet; outgoing.hop_src_id = deviceId; outgoing.hop_dst_id = nextHop; FinalizePacket(&outgoing); if (E22_Send((uint8_t *)&outgoing, sizeof(outgoing)) == HAL_OK) { relay_time_sync_ack_forward_count++; } } static void RelayHandleIncomingPacket(const LoraPacket_t *packet) { if (packet->type == LORA_TYPE_TIME_SYNC) { RelayHandleTimeSyncPacket(packet); return; } if ((packet->type == LORA_TYPE_ACK) && (packet->src_id == MASTER_ID) && (packet->dst_id == TIME_SYNC_FINAL_NODE_ID)) { RelayHandleTimeSyncAck(packet); return; } RelayHandleDataPacket(packet); } static uint8_t RelayWaitForAck(uint32_t originId, uint32_t seq, uint32_t parentId) { uint32_t startTick = HAL_GetTick(); while ((HAL_GetTick() - startTick) < ACK_TIMEOUT_MS) { LoraUart_ProcessDma(); if (LoraTryReadPacket(&rxPacket) == 0U) { HAL_Delay(RELAY_POLL_DELAY_MS); continue; } if ((rxPacket.type == LORA_TYPE_ACK) && (rxPacket.src_id == originId) && (rxPacket.hop_src_id == parentId) && (rxPacket.hop_dst_id == deviceId) && (rxPacket.seq == seq)) { ack_count++; return 1U; } RelayHandleIncomingPacket(&rxPacket); } return 0U; } static void RelayListenUntil(uint32_t cycleStart, uint32_t offsetMs) { uint32_t deadline = cycleStart + offsetMs; while ((int32_t)(HAL_GetTick() - deadline) < 0) { LoraUart_ProcessDma(); if (LoraTryReadPacket(&rxPacket) != 0U) { RelayHandleIncomingPacket(&rxPacket); } else { HAL_Delay(RELAY_POLL_DELAY_MS); } } } static void RelayForwardQueue(uint32_t parentId) { for (uint8_t i = 0U; i < RELAY_QUEUE_SIZE; i++) { if (relayQueue[i].used == 0U) { continue; } if (relayQueue[i].packet.ttl == 0U) { relayQueue[i].used = 0U; if (relay_queue_count > 0U) { relay_queue_count--; } relay_ttl_drop_count++; continue; } LoraPacket_t outgoing = relayQueue[i].packet; outgoing.hop_src_id = deviceId; outgoing.hop_dst_id = parentId; outgoing.ttl--; uint8_t delivered = 0U; for (uint8_t retry = 0U; retry < RELAY_MAX_RETRIES; retry++) { FinalizePacket(&outgoing); lora_tx_ret = E22_Send((uint8_t *)&outgoing, sizeof(outgoing)); if (lora_tx_ret == HAL_OK) { resp_count++; if (RelayWaitForAck(outgoing.src_id, outgoing.seq, parentId) != 0U) { delivered = 1U; break; } } else { resp_fail_count++; } ack_timeout_count++; HAL_Delay(RELAY_RETRY_GAP_MS); } if (delivered != 0U) { relayQueue[i].used = 0U; if (relay_queue_count > 0U) { relay_queue_count--; } relay_forward_count++; } else { relay_retry_exhausted_count++; } } } static void RunRelayCycle(void) { uint32_t cycleStart = HAL_GetTick(); LoraPacket_t ownPacket; relay_cycle_stage = 1U; relay_parent_id = RelayGetParentId(); sensorStatus = SoilSensor_Read(&soilData); soil_read_count++; if (sensorStatus == 0U) { soil_data_valid = 1U; } else { soil_read_fail_count++; } BuildSoilPacket(&ownPacket); (void)RelayQueueStore(&ownPacket); relay_cycle_stage = 2U; RelayListenUntil(cycleStart, RelayGetTxOffsetMs()); relay_cycle_stage = 3U; RelayForwardQueue(relay_parent_id); relay_cycle_stage = 4U; lora_packet_cycle_count++; } uint8_t enable = 0; /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init(); MX_ADC1_Init(); MX_I2C1_Init(); /* USER CODE BEGIN 2 */ app_start_tick = HAL_GetTick(); #if (APP_E22_CONFIG_READ_MODE != 0U) deviceId = FLASH_LoadDeviceID(); HAL_GPIO_WritePin(PWR_E22_WAKE_GPIO_Port, PWR_E22_WAKE_Pin, GPIO_PIN_SET); HAL_Delay(POWER_STABILIZE_MS); (void)E22_Init(&huart1); e22_config_read_status = E22_ReadConfiguration((uint8_t *)e22_config_raw); if (e22_config_read_status == HAL_OK) { e22_config_address = ((uint16_t)e22_config_raw[0] << 8) | e22_config_raw[1]; e22_config_netid = e22_config_raw[2]; e22_config_uart_baud_code = (e22_config_raw[3] >> 5) & 0x07U; e22_config_uart_parity_code = (e22_config_raw[3] >> 3) & 0x03U; e22_config_air_rate_code = e22_config_raw[3] & 0x07U; e22_config_channel = e22_config_raw[5]; e22_config_tx_power_code = e22_config_raw[4] & 0x03U; e22_config_fixed_mode = (e22_config_raw[6] >> 6) & 0x01U; } #elif (APP_RS485_ID_TOOL_MODE != 0U) deviceId = FLASH_LoadDeviceID(); AppPowerOn(); SoilSensor_RunIdTool(); #else #if (LORA_HELLO_TEST == 0U) SoilSensor_Init(); init_step = 5U; soil_last_read_tick = HAL_GetTick(); init_step = 6U; #endif #if ((APP_READ_SOIL_ONLY == 0U) || (LORA_HELLO_TEST != 0U)) deviceId = FLASH_LoadDeviceID(); E22_Init(&huart1); init_step = 7U; #if (LORA_HELLO_TEST == 0U) LoraUart_InitDma(); #endif lora_last_send_tick = HAL_GetTick(); init_step = 8U; #endif ds3231_wake_ready = AppDs3231InitWakeEveryMinute(); AppPowerOff(); #endif /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { main_loop_count++; #if (APP_E22_CONFIG_READ_MODE != 0U) HAL_Delay(1000U); #elif (APP_RS485_ID_TOOL_MODE != 0U) HAL_Delay(1000U); #elif (LORA_HELLO_TEST != 0U) static uint32_t helloTick = 0U; static const uint8_t helloMessage[] = "hello\r\n"; if ((HAL_GetTick() - helloTick) >= LORA_HELLO_INTERVAL_MS) { helloTick = HAL_GetTick(); lora_tx_ret = E22_Send((uint8_t *)helloMessage, sizeof(helloMessage) - 1U); if (lora_tx_ret == HAL_OK) { resp_count++; } else { resp_fail_count++; } } #elif (APP_READ_SOIL_ONLY != 0U) if ((HAL_GetTick() - soil_last_read_tick) >= SOIL_READ_INTERVAL_MS) { soil_last_read_tick = HAL_GetTick(); sensorStatus = SoilSensor_Read(&soilData); soil_read_count++; if (sensorStatus == 0U) { soil_data_valid = 1U; } else { soil_read_fail_count++; } } HAL_Delay(10); #else #if (APP_DS3231_WAKE_MODE != 0U) (void)DS3231_ClearAlarm1(); AppPowerOn(); RunRelayCycle(); (void)DS3231_ClearAlarm1(); AppPowerOff(); if (ds3231_wake_ready != 0U) { EnterStopUntilDs3231Wake(); } else { HAL_Delay(DS3231_FALLBACK_DELAY_MS); } #else LoraUart_ProcessDma(); if (LoraTryReadPacket(&rxPacket) != 0U) { HandlePacket(&rxPacket); } else { idle_count++; HAL_Delay(5); } #endif #endif /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /** * @brief ADC1 Initialization Function * @param None * @retval None */ static void MX_ADC1_Init(void) { /* USER CODE BEGIN ADC1_Init 0 */ /* USER CODE END ADC1_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC1_Init 1 */ /* USER CODE END ADC1_Init 1 */ /** Common config */ hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_1; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */ } /** * @brief I2C1 Initialization Function * @param None * @retval None */ static void MX_I2C1_Init(void) { /* USER CODE BEGIN I2C1_Init 0 */ /* USER CODE END I2C1_Init 0 */ /* USER CODE BEGIN I2C1_Init 1 */ /* USER CODE END I2C1_Init 1 */ hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C1_Init 2 */ /* USER CODE END I2C1_Init 2 */ } /** * @brief USART1 Initialization Function * @param None * @retval None */ static void MX_USART1_UART_Init(void) { /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 9600; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART1_Init 2 */ /* USER CODE END USART1_Init 2 */ } /** * @brief USART2 Initialization Function * @param None * @retval None */ static void MX_USART2_UART_Init(void) { /* USER CODE BEGIN USART2_Init 0 */ /* USER CODE END USART2_Init 0 */ /* USER CODE BEGIN USART2_Init 1 */ /* USER CODE END USART2_Init 1 */ huart2.Instance = USART2; huart2.Init.BaudRate = 4800; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART2_Init 2 */ /* USER CODE END USART2_Init 2 */ } /** * Enable DMA controller clock */ static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA interrupt init */ /* DMA1_Channel4_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn); /* DMA1_Channel5_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn); /* DMA1_Channel6_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn); /* DMA1_Channel7_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn); } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(SLEEP_DEBUG_LED_GPIO_Port, SLEEP_DEBUG_LED_Pin, GPIO_PIN_SET); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(E22_M1_GPIO_Port, E22_M1_Pin, GPIO_PIN_RESET); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOB, E22_M0_Pin|RS485_RE_Pin|RS485_DE_Pin|PWR_E22_WAKE_Pin |PWR_SOIL_WAKE_Pin, GPIO_PIN_RESET); /*Configure GPIO pin : SLEEP_DEBUG_LED_Pin */ GPIO_InitStruct.Pin = SLEEP_DEBUG_LED_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(SLEEP_DEBUG_LED_GPIO_Port, &GPIO_InitStruct); SleepDebugLedAwake(); /*Configure GPIO pin : DS3231_INT_Pin */ GPIO_InitStruct.Pin = DS3231_INT_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(DS3231_INT_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pin : E22_M1_Pin */ GPIO_InitStruct.Pin = E22_M1_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(E22_M1_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pins : E22_M0_Pin RS485_RE_Pin RS485_DE_Pin PWR_E22_WAKE_Pin PWR_SOIL_WAKE_Pin */ GPIO_InitStruct.Pin = E22_M0_Pin|RS485_RE_Pin|RS485_DE_Pin|PWR_E22_WAKE_Pin |PWR_SOIL_WAKE_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /*Configure GPIO pin : E22_AUX_Pin */ GPIO_InitStruct.Pin = E22_AUX_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(E22_AUX_GPIO_Port, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */