├── .github └── FUNDING.yml ├── LICENSE.TXT ├── NimaLTD.I-CUBE-ATC_conf.h ├── README.md ├── atc.c └── atc.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [nimaltd] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: nimaltd 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | otechie: # Replace with a single Otechie username 10 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 11 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Software License Terms 2 | 3 | This software is dual-licensed: 4 | 5 | 1. GNU General Public License v2 (GPLv2): 6 | - You may redistribute and/or modify this software under the terms of GPLv2 as published by the Free Software Foundation. 7 | - This license does not provide any warranty of any kind, including but not limited to MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 8 | - See for details. 9 | 10 | 2. Commercial License: 11 | - The commercial license removes all GPLv2 restrictions and allows you to redistribute your closed-source products with the Library embedded. 12 | - This license includes access to software maintenance, such as updates and upgrades. Customers who purchase this license are eligible to receive updates whenever 13 | they are released. However, the software provider is not obligated to release updates on a specific schedule. 14 | - The commercial license is available in two models: 15 | - Single-Product License: Allows usage in one specific product. 16 | - Company-Wide License: Allows usage across all products of the company. 17 | - No Warranty: The software is provided "AS IS" without any warranties, express or implied, including but not limited to the implied warranties of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, or NON-INFRINGEMENT. 18 | - Liability Disclaimer: In no event shall the copyright holder or contributors be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. 19 | 20 | By using this software under the commercial license, you agree to these terms and acknowledge that no additional guarantees or obligations are provided beyond what is explicitly stated in this document. 21 | 22 | --- 23 | 24 | Copyright © Nima Askari, 2025. All rights reserved. 25 | For inquiries, contact: nima.askari@gmail.com All rights reserved 26 | -------------------------------------------------------------------------------- /NimaLTD.I-CUBE-ATC_conf.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * File Name : NimaLTD.I-CUBE-ATC_conf.h 4 | * Description : This file provides code for the configuration 5 | * of the NimaLTD.I-CUBE-ATC_conf.h instances. 6 | ****************************************************************************** 7 | * @attention 8 | * 9 | * Copyright (c) 2024 STMicroelectronics. 10 | * All rights reserved. 11 | * 12 | * This software is licensed under terms that can be found in the LICENSE file 13 | * in the root directory of this software component. 14 | * If no LICENSE file comes with this software, it is provided AS-IS. 15 | * 16 | ****************************************************************************** 17 | */ 18 | /* Define to prevent recursive inclusion -------------------------------------*/ 19 | #ifndef _NIMALTD_I_CUBE_ATC_CONF_H_ 20 | #define _NIMALTD_I_CUBE_ATC_CONF_H_ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define ATC_DEBUG_DISABLE 0 27 | #define ATC_DEBUG_ENABLE 1 28 | 29 | #define ATC_RTOS_DISABLE 0 30 | #define ATC_RTOS_CMSIS_V1 1 31 | #define ATC_RTOS_CMSIS_V2 2 32 | #define ATC_RTOS_THREADX 3 33 | 34 | /** 35 | MiddleWare name : NimaLTD.I-CUBE-ATC.4.0.0 36 | MiddleWare fileName : NimaLTD.I-CUBE-ATC_conf.h 37 | */ 38 | /*---------- ATC_DEBUG -----------*/ 39 | #define ATC_DEBUG ATC_DEBUG_ENABLE 40 | 41 | /*---------- ATC_RTOS -----------*/ 42 | #define ATC_RTOS ATC_RTOS_DISABLE 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | #endif /* _NIMALTD_I_CUBE_ATC_CONF_H_ */ 48 | 49 | /** 50 | * @} 51 | */ 52 | 53 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AT Command Library for STM32 2 | --- 3 | ## Please Do not Forget to get STAR, DONATE and support me on social networks. Thank you. :sparkling_heart: 4 | --- 5 | - **Author**: Nima Askari 6 | - **Github**: [https://www.github.com/NimaLTD](https://www.github.com/NimaLTD) 7 | - **Youtube**: [https://www.youtube.com/@nimaltd](https://www.youtube.com/@nimaltd) 8 | - **LinkedIn**: [https://www.linkedin.com/in/nimaltd](https://www.linkedin.com/in/nimaltd) 9 | - **Instagram**: [https://instagram.com/github.NimaLTD](https://instagram.com/github.NimaLTD) 10 | --- 11 | ## Overview 12 | The AT Command Library (ATC) simplifies UART communication for STM32 microcontrollers with an event-driven approach and debug capabilities. This updated version introduces AT command slave functionality, allowing the STM32 to process commands like `AT+LED=ON` and respond (e.g., `+OK`), making it ideal for IoT and embedded applications. 13 | 14 | ### Key Features 15 | - Event-driven UART handling with DMA support. 16 | - Optional RTOS compatibility (FreeRTOS, ThreadX). 17 | - Debug logging when enabled. 18 | - AT command slave mode with custom handlers. 19 | 20 | --- 21 | 22 | ## Installation 23 | 1. Download the library: [NimaLTD.I-CUBE-ATC.pdsc](https://github.com/nimaltd/STM32-PACK/raw/main/ATC/NimaLTD.I-CUBE-ATC.pdsc). 24 | 2. Import it into STM32CubeMX and enable it. 25 | 3. Configure UART: 26 | - Enable UART interrupt. 27 | - Enable TX/RX DMA in Normal Mode. 28 | 4. In the Code Generator tab, select "Generate peripheral initialization as a pair of .c/.h files per peripheral." 29 | 5. Generate the project code. 30 | 31 | --- 32 | 33 | ## Getting Started 34 | 1. Declare an `ATC_HandleTypeDef` structure in your code. 35 | 2. Add `ATC_IdleLineCallback()` to the UART idle line callback (e.g., in `HAL_UARTEx_RxEventCallback`). 36 | 3. Initialize the library with `ATC_Init()`. 37 | 4. Optionally, set up events with `ATC_SetEvents()` or AT commands with `ATC_SetCommands()`. 38 | 5. Call `ATC_Loop()` in your main loop to process incoming data. 39 | 40 | --- 41 | 42 | ## Using AT Command Handlers 43 | This library now supports AT command handling, enabling the STM32 to act as a command slave. Send commands like `AT+LED=ON` from an external device, and the STM32 will execute them and respond (e.g., `+OK` or `+LED:ON`). 44 | 45 | ### Setup Steps 46 | 1. **Define Command Handlers**: Create functions to process commands and generate responses. 47 | 2. **Create a Command Table**: Use `ATC_CmdTypeDef` to map command prefixes (e.g., `AT+LED=`) to handlers. 48 | 3. **Register Commands**: Call `ATC_SetCommands()` to link the table to your ATC handle. 49 | 4. **Run the Loop**: Use `ATC_Loop()` to handle incoming commands and events. 50 | 51 | ### Example: LED Control 52 | Control an LED and query its state via AT commands: 53 | 54 | ```c 55 | #include "atc.h" 56 | 57 | // ATC handle 58 | ATC_HandleTypeDef hAtc; 59 | 60 | // Command handler for setting LED state 61 | void Handle_LED(const char* args, char* response) 62 | { 63 | if (strcmp(args, "ON") == 0) 64 | { 65 | HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // Adjust port/pin 66 | strcpy(response, "+OK"); 67 | } 68 | else if (strcmp(args, "OFF") == 0) 69 | { 70 | HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); 71 | strcpy(response, "+OK"); 72 | } 73 | else 74 | { 75 | strcpy(response, "+ERROR"); 76 | } 77 | } 78 | 79 | // Command handler for querying LED state 80 | void Handle_GetLED(const char* args, char* response) 81 | { 82 | GPIO_PinState led_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5); 83 | strcpy(response, (led_state == GPIO_PIN_SET) ? "+LED:ON" : "+LED:OFF"); 84 | } 85 | 86 | // Command table 87 | ATC_CmdTypeDef at_commands[] = 88 | { 89 | {"AT+LED?", Handle_GetLED}, // Query LED state: "AT+LED?" -> "+LED:ON" 90 | {"AT+LED=", Handle_LED}, // Set LED state: "AT+LED=ON" -> "+OK" 91 | {NULL, NULL} // Terminator 92 | }; 93 | 94 | // UART handle (from STM32CubeMX) 95 | extern UART_HandleTypeDef huart1; 96 | 97 | int main(void) 98 | { 99 | // HAL initialization (from STM32CubeMX) 100 | HAL_Init(); 101 | SystemClock_Config(); 102 | MX_GPIO_Init(); 103 | MX_DMA_Init(); 104 | MX_USART1_UART_Init(); 105 | 106 | // Initialize ATC 107 | ATC_Init(&hAtc, &huart1, 256, "Slave1"); 108 | 109 | // Register AT commands 110 | ATC_SetCommands(&hAtc, at_commands); 111 | 112 | while (1) 113 | { 114 | ATC_Loop(&hAtc); // Process commands and events 115 | } 116 | } 117 | 118 | // Add to your UART IRQ handler (e.g., stm32f1xx_it.c) 119 | void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { 120 | if (huart->Instance == USART1) 121 | { // Adjust for your UART instance 122 | ATC_IdleLineCallback(&hAtc, Size); 123 | } 124 | } 125 | ``` 126 | 127 | ## Command Responses 128 | - `AT+LED=ON` → `+OK` (turns LED on) 129 | - `AT+LED=OFF` → `+OK` (turns LED off) 130 | - `AT+LED?` → `+LED:ON` or `+LED:OFF` (queries state) 131 | - Unknown command → `+ERROR` 132 | 133 | --- 134 | 135 | # Watch the Video: 136 | 137 |
138 | Video 139 |
140 | 141 | --- 142 | The old Version: https://github.com/nimaltd/ATC/archive/refs/tags/3.0.3.zip 143 | 144 | 145 | -------------------------------------------------------------------------------- /atc.c: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************************************************************ 3 | ************** Include Headers 4 | ************************************************************************************************************/ 5 | 6 | #include "atc.h" 7 | #include "usart.h" 8 | #include 9 | #include 10 | 11 | #if ATC_DEBUG == ATC_DEBUG_ENABLE 12 | #define dprintf(...) printf(__VA_ARGS__) 13 | #else 14 | #define dprintf(...) 15 | #endif 16 | 17 | /************************************************************************************************************ 18 | ************** Private Definitions 19 | ************************************************************************************************************/ 20 | 21 | // none 22 | 23 | /************************************************************************************************************ 24 | ************** Private Variables 25 | ************************************************************************************************************/ 26 | 27 | // none 28 | 29 | /************************************************************************************************************ 30 | ************** Private Functions 31 | ************************************************************************************************************/ 32 | 33 | void ATC_Delay(uint32_t Delay); 34 | void* ATC_Malloc(size_t Size); 35 | void ATC_Free(void** pMem); 36 | void ATC_RxFlush(ATC_HandleTypeDef* hAtc); 37 | bool ATC_TxRaw(ATC_HandleTypeDef* hAtc, const uint8_t* pData, uint16_t Len); 38 | bool ATC_TxBusy(ATC_HandleTypeDef* hAtc); 39 | bool ATC_TxWait(ATC_HandleTypeDef* hAtc, uint32_t Timeout); 40 | void ATC_CheckEvents(ATC_HandleTypeDef* hAtc); 41 | uint8_t ATC_CheckResponse(ATC_HandleTypeDef* hAtc,char** ppFound); 42 | void ATC_CheckErrors(ATC_HandleTypeDef* hAtc); 43 | 44 | /***********************************************************************************************************/ 45 | 46 | void* ATC_Malloc(size_t size) 47 | { 48 | void *ptr = NULL; 49 | #if ATC_RTOS == ATC_RTOS_DISABLE 50 | ptr = malloc(size); 51 | #elif (ATC_RTOS == ATC_RTOS_CMSIS_V1) || (ATC_RTOS == ATC_RTOS_CMSIS_V2) 52 | ptr = pvPortMalloc(size); 53 | #elif ATC_RTOS == ATC_RTOS_THREADX 54 | ?? 55 | #endif 56 | return ptr; 57 | } 58 | 59 | /***********************************************************************************************************/ 60 | 61 | void ATC_Free(void** ptr) 62 | { 63 | if (ptr != NULL && *ptr != NULL) 64 | { 65 | #if ATC_RTOS == ATC_RTOS_DISABLE 66 | free(*ptr); 67 | #elif (ATC_RTOS == ATC_RTOS_CMSIS_V1) || (ATC_RTOS == ATC_RTOS_CMSIS_V2) 68 | vPortFree(*ptr); 69 | #elif ATC_RTOS == ATC_RTOS_THREADX 70 | ?? 71 | #endif 72 | *ptr = NULL; 73 | } 74 | } 75 | 76 | /***********************************************************************************************************/ 77 | 78 | void ATC_RxFlush(ATC_HandleTypeDef* hAtc) 79 | { 80 | hAtc->RxIndex = 0; 81 | memset(hAtc->pReadBuff, 0, hAtc->Size); 82 | } 83 | 84 | /***********************************************************************************************************/ 85 | 86 | bool ATC_TxRaw(ATC_HandleTypeDef* hAtc, const uint8_t* Data, uint16_t Len) 87 | { 88 | bool answer = false; 89 | do 90 | { 91 | #if ATC_DEBUG == ATC_DEBUG_ENABLE 92 | dprintf("ATC<%s> - TX: ", hAtc->Name); 93 | for (uint16_t i = 0 ; i < Len; i++) 94 | { 95 | dprintf("%c", Data[i]); 96 | } 97 | dprintf("\r\n"); 98 | #endif 99 | hAtc->TxLen = Len; 100 | if (HAL_UART_Transmit_DMA(hAtc->hUart, Data, Len) != HAL_OK) 101 | { 102 | break; 103 | } 104 | answer = true; 105 | 106 | } while (0); 107 | 108 | return answer; 109 | } 110 | 111 | /***********************************************************************************************************/ 112 | 113 | bool ATC_TxBusy(ATC_HandleTypeDef* hAtc) 114 | { 115 | if ((HAL_UART_GetState(hAtc->hUart) == HAL_UART_STATE_BUSY_TX) || (HAL_UART_GetState(hAtc->hUart) == HAL_UART_STATE_BUSY_TX_RX)) 116 | { 117 | return true; 118 | } 119 | else 120 | { 121 | return false; 122 | } 123 | } 124 | 125 | /***********************************************************************************************************/ 126 | 127 | bool ATC_TxWait(ATC_HandleTypeDef* hAtc, uint32_t Timeout) 128 | { 129 | bool answer = false; 130 | uint32_t start_time = HAL_GetTick(); 131 | while (1) 132 | { 133 | ATC_Delay(1); 134 | if ((HAL_UART_GetState(hAtc->hUart) == HAL_UART_STATE_BUSY_RX) || (HAL_UART_GetState(hAtc->hUart) == HAL_UART_STATE_READY)) 135 | { 136 | answer = true; 137 | break; 138 | } 139 | if ((HAL_UART_GetState(hAtc->hUart) == HAL_UART_STATE_ERROR) || (HAL_UART_GetState(hAtc->hUart) == HAL_UART_STATE_TIMEOUT)) 140 | { 141 | break; 142 | } 143 | if (HAL_GetTick() - start_time >= Timeout) 144 | { 145 | HAL_UART_AbortTransmit(hAtc->hUart); 146 | break; 147 | } 148 | } 149 | 150 | return answer; 151 | } 152 | 153 | /***********************************************************************************************************/ 154 | 155 | void ATC_CheckEvents(ATC_HandleTypeDef* hAtc) 156 | { 157 | if (hAtc->RxIndex > 0) 158 | { 159 | char *rx_data = (char*) hAtc->pReadBuff; 160 | bool command_processed = false; 161 | bool event_processed = false; 162 | // 1. Check for AT commands first 163 | for (uint32_t i = 0; i < hAtc->CmdCount; i++) 164 | { 165 | const char *prefix = hAtc->psCmds[i].pCmd; 166 | if (strncmp(rx_data, prefix, strlen(prefix)) == 0) 167 | { 168 | // Extract arguments (e.g., "ON" from "AT+LED=ON") 169 | const char *args = rx_data + strlen(prefix); 170 | char response[64]; 171 | hAtc->psCmds[i].CmdCallback(args, response); 172 | // Send response (use TX buffer) 173 | snprintf((char*) hAtc->pTxBuff, hAtc->Size, "%s\r\n", response); 174 | ATC_TxRaw(hAtc, hAtc->pTxBuff, strlen((char*) hAtc->pTxBuff)); 175 | command_processed = true; 176 | break; 177 | } 178 | } 179 | // 2. If no command matched, check for events (original behavior) 180 | if (!command_processed) 181 | { 182 | for (uint32_t ev = 0; ev < hAtc->EventCount; ev++) 183 | { 184 | char *found = strstr(rx_data, hAtc->psEvents[ev].Event); 185 | if (found != NULL) 186 | { 187 | hAtc->psEvents[ev].EventCallback(found); 188 | event_processed = true; 189 | break; 190 | } 191 | } 192 | } 193 | if ((!command_processed) && (!event_processed)) 194 | { 195 | snprintf((char*) hAtc->pTxBuff, hAtc->Size, "%s\r\n", "+ERROR"); 196 | ATC_TxRaw(hAtc, hAtc->pTxBuff, strlen((char*) hAtc->pTxBuff)); 197 | } 198 | ATC_RxFlush(hAtc); // Clear buffer after processing 199 | } 200 | } 201 | 202 | /***********************************************************************************************************/ 203 | 204 | uint8_t ATC_CheckResponse(ATC_HandleTypeDef* hAtc, char** ppFound) 205 | { 206 | uint8_t index = 0; 207 | if (hAtc->RxIndex > 0) 208 | { 209 | for (uint16_t i = 0; i < hAtc->RespCount; i++) 210 | { 211 | char *found = strstr((char*)hAtc->pReadBuff, (char*)hAtc->ppResp[i]); 212 | if (found != NULL) 213 | { 214 | if (ppFound != NULL) 215 | { 216 | *ppFound = found; 217 | } 218 | index = i + 1; 219 | break; 220 | } 221 | } 222 | } 223 | return index; 224 | } 225 | 226 | /***********************************************************************************************************/ 227 | 228 | void ATC_CheckErrors(ATC_HandleTypeDef* hAtc) 229 | { 230 | if (HAL_UART_GetError(hAtc->hUart) != HAL_UART_ERROR_NONE) 231 | { 232 | __HAL_UART_CLEAR_FLAG(hAtc->hUart, 0xFFFFFFFF); 233 | HAL_UART_AbortReceive(hAtc->hUart); 234 | HAL_UARTEx_ReceiveToIdle_DMA(hAtc->hUart, hAtc->pRxBuff, hAtc->Size); 235 | __HAL_DMA_DISABLE_IT(hAtc->hUart->hdmarx, DMA_IT_HT); 236 | } 237 | if (!((HAL_UART_GetState(hAtc->hUart) == HAL_UART_STATE_BUSY_RX) || 238 | (HAL_UART_GetState(hAtc->hUart) == HAL_UART_STATE_BUSY_TX_RX))) 239 | { 240 | __HAL_UART_CLEAR_FLAG(hAtc->hUart, 0xFFFFFFFF); 241 | HAL_UART_AbortReceive(hAtc->hUart); 242 | HAL_UARTEx_ReceiveToIdle_DMA(hAtc->hUart, hAtc->pRxBuff, hAtc->Size); 243 | __HAL_DMA_DISABLE_IT(hAtc->hUart->hdmarx, DMA_IT_HT); 244 | } 245 | } 246 | 247 | /************************************************************************************************************ 248 | ************** Public Functions 249 | ************************************************************************************************************/ 250 | 251 | /** 252 | * @brief Initializes the ATC handle structure. 253 | * @param hAtc: Pointer to the ATC handle. 254 | * @param hUart: Pointer to the UART handle. 255 | * @param BufferSize: Size of the RX buffer. It needs 2X memory. 256 | * @param pName: Name identifier for the ATC. 257 | * @retval true if initialization is successful, false otherwise. 258 | */ 259 | bool ATC_Init(ATC_HandleTypeDef* hAtc, UART_HandleTypeDef* hUart, uint16_t BufferSize, const char* pName) 260 | { 261 | bool answer = false; 262 | do 263 | { 264 | if (hAtc == NULL || hUart == NULL) 265 | { 266 | break; 267 | } 268 | memset(hAtc, 0, sizeof(ATC_HandleTypeDef)); 269 | if (pName != NULL) 270 | { 271 | strncpy(hAtc->Name, pName, sizeof(hAtc->Name) - 1); 272 | } 273 | hAtc->hUart = hUart; 274 | hAtc->pRxBuff = ATC_Malloc(BufferSize); 275 | if (hAtc->pRxBuff != NULL) 276 | { 277 | memset(hAtc->pRxBuff, 0, BufferSize); 278 | } 279 | else 280 | { 281 | dprintf("ATC<%s> - ERROR MALLOC RX BUFF\r\n", hAtc->Name); 282 | break; 283 | } 284 | hAtc->pTxBuff = ATC_Malloc(BufferSize); 285 | if (hAtc->pTxBuff != NULL) 286 | { 287 | memset(hAtc->pTxBuff, 0, BufferSize); 288 | } 289 | else 290 | { 291 | dprintf("ATC<%s> - ERROR MALLOC TX BUFF\r\n", hAtc->Name); 292 | break; 293 | } 294 | hAtc->pReadBuff = ATC_Malloc(BufferSize); 295 | if (hAtc->pReadBuff != NULL) 296 | { 297 | memset(hAtc->pReadBuff, 0, BufferSize); 298 | } 299 | else 300 | { 301 | dprintf("ATC<%s> - ERROR MALLOC READ BUFF\r\n", hAtc->Name); 302 | break; 303 | } 304 | hAtc->Size = BufferSize; 305 | __HAL_UART_CLEAR_FLAG(hAtc->hUart, 0xFFFFFFFF); 306 | if (HAL_UARTEx_ReceiveToIdle_DMA(hAtc->hUart, hAtc->pRxBuff, hAtc->Size) != HAL_OK) 307 | { 308 | dprintf("ATC<%s> - ERROR ENABLE RX DMA\r\n", hAtc->Name); 309 | break; 310 | } 311 | __HAL_DMA_DISABLE_IT(hAtc->hUart->hdmarx, DMA_IT_HT); 312 | answer = true; 313 | 314 | } while (0); 315 | 316 | if (answer == false) 317 | { 318 | if (hAtc->pRxBuff != NULL) 319 | { 320 | ATC_Free((void**)&hAtc->pRxBuff); 321 | } 322 | if (hAtc->pReadBuff != NULL) 323 | { 324 | ATC_Free((void**)&hAtc->pReadBuff); 325 | } 326 | memset(hAtc, 0, sizeof(ATC_HandleTypeDef)); 327 | } 328 | else 329 | { 330 | dprintf("ATC<%s> - INIT DONE\r\n", hAtc->Name); 331 | } 332 | return answer; 333 | } 334 | 335 | /***********************************************************************************************************/ 336 | 337 | /** 338 | * @brief DeInitializes the ATC handle structure. 339 | * @param hAtc: Pointer to the ATC handle. 340 | * @retval None. 341 | */ 342 | void ATC_DeInit(ATC_HandleTypeDef* hAtc) 343 | { 344 | do 345 | { 346 | if (hAtc == NULL) 347 | { 348 | break; 349 | } 350 | if (hAtc->hUart == NULL) 351 | { 352 | break; 353 | } 354 | ATC_Free((void**)&hAtc->pRxBuff); 355 | ATC_Free((void**)&hAtc->pReadBuff); 356 | memset(hAtc, 0, sizeof(ATC_HandleTypeDef)); 357 | 358 | } while (0); 359 | } 360 | 361 | /***********************************************************************************************************/ 362 | 363 | /** 364 | * @brief Sets the ATC event handlers. 365 | * @param hAtc: Pointer to the ATC handle. 366 | * @param psEvents: Pointer to the event handler structure. 367 | * @retval true if events are set successfully, false otherwise. 368 | */ 369 | bool ATC_SetEvents(ATC_HandleTypeDef* hAtc, const ATC_EventTypeDef* psEvents) 370 | { 371 | bool answer = false; 372 | uint32_t ev = 0; 373 | do 374 | { 375 | if (hAtc == NULL) 376 | { 377 | break; 378 | } 379 | if (psEvents == NULL) 380 | { 381 | break; 382 | } 383 | while ((psEvents[ev].Event != NULL) && (psEvents[ev].EventCallback != NULL)) 384 | { 385 | ev++; 386 | } 387 | hAtc->psEvents = (ATC_EventTypeDef*)psEvents; 388 | hAtc->EventCount = ev; 389 | answer = true; 390 | 391 | } while (0); 392 | 393 | return answer; 394 | } 395 | 396 | /***********************************************************************************************************/ 397 | 398 | /** 399 | * @brief Set callback for command. 400 | * @param hAtc: Pointer to the ATC handle. 401 | * @param psCmds: Pointer to the command callback. 402 | * @retval bool true if succeed. 403 | */ 404 | bool ATC_SetCommands(ATC_HandleTypeDef *hAtc, const ATC_CmdTypeDef *psCmds) 405 | { 406 | bool answer = false; 407 | uint32_t cmd = 0; 408 | do 409 | { 410 | if (hAtc == NULL || psCmds == NULL) 411 | { 412 | break; 413 | } 414 | // Count the number of commands (terminated by {NULL, NULL}) 415 | while (psCmds[cmd].pCmd != NULL && psCmds[cmd].CmdCallback != NULL) 416 | { 417 | cmd++; 418 | } 419 | hAtc->psCmds = (ATC_CmdTypeDef*) psCmds; 420 | hAtc->CmdCount = cmd; 421 | answer = true; 422 | 423 | } while (0); 424 | 425 | return answer; 426 | } 427 | 428 | /***********************************************************************************************************/ 429 | 430 | /** 431 | * @brief Main loop for processing ATC events and errors. 432 | * @param hAtc: Pointer to the ATC handle. 433 | * @retval None. 434 | */ 435 | void ATC_Loop(ATC_HandleTypeDef* hAtc) 436 | { 437 | ATC_CheckErrors(hAtc); 438 | ATC_CheckEvents(hAtc); 439 | } 440 | 441 | /***********************************************************************************************************/ 442 | 443 | /** 444 | * @brief Sends a command and waits for a response. 445 | * @param hAtc: Pointer to the ATC handle. 446 | * @param pCommand: Pointer to the command string. 447 | * @param TxTimeout: Timeout for sending the command. 448 | * @param ppResp: Pointer to the response buffer. It Can be NULL. 449 | * @param RxTimeout: Timeout for receiving the response. 450 | * @param Items: Number of String for Searching 451 | * @param ...: Variable arguments for expected responses. 452 | * @retval Response index if found, error code otherwise. 453 | */ 454 | int ATC_SendReceive(ATC_HandleTypeDef* hAtc, const char* pCommand, uint32_t TxTimeout, char** ppResp, uint32_t RxTimeout, uint8_t Items, ...) 455 | { 456 | int answer = ATC_RESP_NOT_FOUND; 457 | if (ATC_TxBusy(hAtc) == true) 458 | { 459 | return ATC_RESP_TX_BUSY; 460 | } 461 | if (Items > ATC_RESP_MAX) 462 | { 463 | return ATC_RESP_ITEMS; 464 | } 465 | ATC_CheckErrors(hAtc); 466 | va_list args; 467 | va_start(args, Items); 468 | for (int i = 0; i < Items; i++) 469 | { 470 | char *arg = va_arg(args, char*); 471 | hAtc->ppResp[i] = (uint8_t*) ATC_Malloc(strlen(arg) + 1); 472 | if (hAtc->ppResp[i] == NULL) 473 | { 474 | for (uint8_t j = 0; j < i; j++) 475 | { 476 | ATC_Free((void**)&hAtc->ppResp[j]); 477 | } 478 | return ATC_RESP_MEM_ERROR; 479 | } 480 | strcpy((char*) hAtc->ppResp[i], arg); 481 | hAtc->ppResp[i][strlen(arg)] = 0; 482 | } 483 | va_end(args); 484 | 485 | do 486 | { 487 | ATC_RxFlush(hAtc); 488 | if (ATC_TxRaw(hAtc, (const uint8_t*)pCommand, strlen((char*)pCommand)) == false) 489 | { 490 | answer = ATC_RESP_SENDING_ERROR; 491 | break; 492 | } 493 | if (ATC_TxWait(hAtc, TxTimeout) == false) 494 | { 495 | answer = ATC_RESP_SENDING_TIMEOUT; 496 | break; 497 | } 498 | 499 | } while (0); 500 | 501 | if ((Items > 0) && (answer == ATC_RESP_NOT_FOUND)) 502 | { 503 | uint32_t start_time = HAL_GetTick(); 504 | hAtc->RespCount = Items; 505 | while (HAL_GetTick() - start_time < RxTimeout) 506 | { 507 | ATC_Delay(1); 508 | uint8_t found_index = ATC_CheckResponse(hAtc, ppResp); 509 | if (found_index > 0) 510 | { 511 | answer = found_index; 512 | break; 513 | } 514 | } 515 | } 516 | hAtc->RespCount = 0; 517 | for (uint8_t i = 0; i < Items; i++) 518 | { 519 | ATC_Free((void**)&hAtc->ppResp[i]); 520 | } 521 | return answer; 522 | } 523 | 524 | /***********************************************************************************************************/ 525 | 526 | /** 527 | * @brief Send a command. 528 | * @param hAtc: Pointer to the ATC handle. 529 | * @param pCommand: Pointer to the command string, it can use like printf format 530 | * @param TxTimeout: Timeout for sending the command. 531 | * @param ... , it can use like printf format 532 | * @retval Response true or false. 533 | */ 534 | bool ATC_Send(ATC_HandleTypeDef *hAtc, const char *pCommand, uint32_t TxTimeout, ...) 535 | { 536 | bool answer = false; 537 | do 538 | { 539 | if (ATC_TxBusy(hAtc) == true) 540 | { 541 | break; 542 | } 543 | ATC_CheckErrors(hAtc); 544 | va_list args; 545 | va_start(args, TxTimeout); 546 | int len = vsnprintf((char*)hAtc->pTxBuff, hAtc->Size, pCommand, args); 547 | va_end(args); 548 | if ((len < 0) || (len > hAtc->Size)) 549 | { 550 | break; 551 | } 552 | ATC_RxFlush(hAtc); 553 | if (ATC_TxRaw(hAtc, (const uint8_t*)hAtc->pTxBuff, strlen((char*)hAtc->pTxBuff)) == false) 554 | { 555 | break; 556 | } 557 | if (ATC_TxWait(hAtc, TxTimeout) == false) 558 | { 559 | break; 560 | } 561 | answer = true; 562 | 563 | } while (0); 564 | 565 | return answer; 566 | } 567 | 568 | /***********************************************************************************************************/ 569 | 570 | /** 571 | * @brief waiting for a response. 572 | * @param hAtc: Pointer to the ATC handle. 573 | * @param ppResp: Pointer to the response buffer. It Can be NULL. 574 | * @param RxTimeout: Timeout for sending the command. 575 | * @param Items: Number of searching strings 576 | * @param ...: Variable arguments for expected responses. 577 | * @retval Response index if found, error code otherwise. 578 | */ 579 | int ATC_Receive(ATC_HandleTypeDef *hAtc, char **ppResp, uint32_t RxTimeout, uint8_t Items, ...) 580 | { 581 | int answer = ATC_RESP_NOT_FOUND; 582 | if (Items > ATC_RESP_MAX) 583 | { 584 | return ATC_RESP_ITEMS; 585 | } 586 | ATC_CheckErrors(hAtc); 587 | va_list args; 588 | va_start(args, Items); 589 | for (int i = 0; i < Items; i++) 590 | { 591 | char *arg = va_arg(args, char*); 592 | hAtc->ppResp[i] = (uint8_t*) ATC_Malloc(strlen(arg) + 1); 593 | if (hAtc->ppResp[i] == NULL) 594 | { 595 | for (uint8_t j = 0; j < i; j++) 596 | { 597 | ATC_Free((void**)&hAtc->ppResp[j]); 598 | } 599 | return ATC_RESP_MEM_ERROR; 600 | } 601 | strcpy((char*) hAtc->ppResp[i], arg); 602 | hAtc->ppResp[i][strlen(arg)] = 0; 603 | } 604 | va_end(args); 605 | 606 | if (Items > 0) 607 | { 608 | uint32_t start_time = HAL_GetTick(); 609 | hAtc->RespCount = Items; 610 | while (HAL_GetTick() - start_time < RxTimeout) 611 | { 612 | ATC_Delay(1); 613 | uint8_t found_index = ATC_CheckResponse(hAtc, ppResp); 614 | if (found_index > 0) 615 | { 616 | answer = found_index; 617 | break; 618 | } 619 | } 620 | } 621 | hAtc->RespCount = 0; 622 | for (uint8_t i = 0; i < Items; i++) 623 | { 624 | ATC_Free((void**)&hAtc->ppResp[i]); 625 | } 626 | return answer; 627 | } 628 | 629 | /***********************************************************************************************************/ 630 | 631 | /** 632 | * @brief Callback for handling UART idle line detection. 633 | * @param hAtc: Pointer to the ATC handle. 634 | * @param Len: Length of received data. 635 | * @retval None. 636 | */ 637 | inline void ATC_IdleLineCallback(ATC_HandleTypeDef* hAtc, uint16_t Len) 638 | { 639 | if (Len > hAtc->Size - hAtc->RxIndex) 640 | { 641 | Len = hAtc->Size - hAtc->RxIndex; 642 | } 643 | memcpy(&hAtc->pReadBuff[hAtc->RxIndex], hAtc->pRxBuff, Len); 644 | #if ATC_DEBUG == ATC_DEBUG_ENABLE 645 | dprintf("ATC<%s> - RX: ", hAtc->Name); 646 | for (int i = 0; i < Len; i++) 647 | { 648 | dprintf("%c", hAtc->pRxBuff[i]); 649 | } 650 | dprintf("\r\n"); 651 | #endif 652 | hAtc->RxIndex += Len; 653 | if (HAL_UARTEx_ReceiveToIdle_DMA(hAtc->hUart, hAtc->pRxBuff, hAtc->Size) == HAL_OK) 654 | { 655 | __HAL_DMA_DISABLE_IT(hAtc->hUart->hdmarx, DMA_IT_HT); 656 | } 657 | else 658 | { 659 | __HAL_UART_CLEAR_FLAG(hAtc->hUart, 0xFFFFFFFF); 660 | HAL_UART_AbortReceive(hAtc->hUart); 661 | HAL_UARTEx_ReceiveToIdle_DMA(hAtc->hUart, hAtc->pRxBuff, hAtc->Size); 662 | __HAL_DMA_DISABLE_IT(hAtc->hUart->hdmarx, DMA_IT_HT); 663 | } 664 | } 665 | 666 | /***********************************************************************************************************/ 667 | 668 | /** 669 | * @brief Delay function. 670 | * @param Delay: delay in milisecond.. 671 | * @retval None. 672 | */ 673 | void ATC_Delay(uint32_t Delay) 674 | { 675 | #if ATC_RTOS == ATC_RTOS_DISABLE 676 | HAL_Delay(Delay); 677 | #elif (ATC_RTOS == ATC_RTOS_CMSIS_V1) || (ATC_RTOS == ATC_RTOS_CMSIS_V2) 678 | uint32_t d = (configTICK_RATE_HZ * Delay) / 1000; 679 | if (d == 0) 680 | d = 1; 681 | osDelay(d); 682 | #elif ATC_RTOS == ATC_RTOS_THREADX 683 | uint32_t d = (TX_TIMER_TICKS_PER_SECOND * Delay) / 1000; 684 | if (d == 0) 685 | d = 1; 686 | tx_thread_sleep(d); 687 | #endif 688 | } 689 | -------------------------------------------------------------------------------- /atc.h: -------------------------------------------------------------------------------- 1 | #ifndef _ATC_H_ 2 | #define _ATC_H_ 3 | 4 | /*********************************************************************************************************** 5 | 6 | Author: Nima Askari 7 | Github: https://www.github.com/NimaLTD 8 | LinkedIn: https://www.linkedin.com/in/nimaltd 9 | Youtube: https://www.youtube.com/@nimaltd 10 | Instagram: https://instagram.com/github.NimaLTD 11 | 12 | Version: 4.3.0 13 | 14 | History: 15 | 16 | 4.3.0 17 | - Added Command callback 18 | 19 | 4.2.2 20 | - Fixed SetEvent 21 | 22 | 4.2.1 23 | - Fixed Debug print 24 | - Removed Temp Callback :) 25 | 26 | 4.2.0 27 | - Fixed Returned response 28 | - Added Temp Callback 29 | 30 | 4.1.2 31 | - Fixed Definitions 32 | 33 | 4.1.1 34 | - Fixed RX Items counter 35 | 36 | 4.1.0 37 | - Added ATC_Send and ATC_Receive functions 38 | - Changed declaration 39 | 40 | 4.0.2 41 | - Fixed Debug Printing 42 | 43 | 4.0.1 44 | - Fixed Initialization 45 | - Changed ATC_Delay function to public 46 | 47 | 4.0.0 48 | - Rewrite again 49 | - Working with RX/TX DMA, Less CPU load 50 | - A separate callback for each event received (auto searching string) 51 | - Support STM32CubeMx Packet installer 52 | 53 | ***********************************************************************************************************/ 54 | 55 | #ifdef __cplusplus 56 | extern "C" 57 | { 58 | #endif 59 | 60 | /************************************************************************************************************ 61 | ************** Include Headers 62 | ************************************************************************************************************/ 63 | 64 | #include "NimaLTD.I-CUBE-ATC_conf.h" 65 | #include "usart.h" 66 | #include 67 | #include 68 | #include 69 | 70 | #define ATC_DEBUG_DISABLE 0 71 | #define ATC_DEBUG_ENABLE 1 72 | 73 | #define ATC_RTOS_DISABLE 0 74 | #define ATC_RTOS_CMSIS_V1 1 75 | #define ATC_RTOS_CMSIS_V2 2 76 | #define ATC_RTOS_THREADX 3 77 | 78 | /*---------- ATC_DEBUG -----------*/ 79 | #define ATC_DEBUG ATC_RTOS_DISABLE 80 | 81 | /*---------- ATC_RTOS -----------*/ 82 | #define ATC_RTOS ATC_RTOS_DISABLE 83 | 84 | 85 | #if ATC_RTOS == ATC_RTOS_DISABLE 86 | #elif ATC_RTOS == ATC_RTOS_CMSIS_V1 87 | #include "cmsis_os.h" 88 | #include "freertos.h" 89 | #elif ATC_RTOS == ATC_RTOS_CMSIS_V2 90 | #include "cmsis_os2.h" 91 | #include "freertos.h" 92 | #elif ATC_RTOS == ATC_RTOS_THREADX 93 | #include "app_threadx.h" 94 | #endif 95 | 96 | #define ATC_RESP_MAX_LEN 256 97 | 98 | /************************************************************************************************************ 99 | ************** Public Definitions 100 | ************************************************************************************************************/ 101 | 102 | #define ATC_RESP_MAX 5 103 | 104 | #define ATC_RESP_ITEMS -5 105 | #define ATC_RESP_TX_BUSY -4 106 | #define ATC_RESP_MEM_ERROR -3 107 | #define ATC_RESP_SENDING_TIMEOUT -2 108 | #define ATC_RESP_SENDING_ERROR -1 109 | #define ATC_RESP_NOT_FOUND 0 110 | 111 | /************************************************************************************************************ 112 | ************** Public struct/enum 113 | ************************************************************************************************************/ 114 | 115 | typedef struct 116 | { 117 | char* pCmd; 118 | void (*CmdCallback)(const char* args, char* response); 119 | 120 | } ATC_CmdTypeDef; 121 | 122 | typedef struct 123 | { 124 | char* Event; 125 | void (*EventCallback)(const char*); 126 | 127 | } ATC_EventTypeDef; 128 | 129 | typedef struct 130 | { 131 | UART_HandleTypeDef* hUart; 132 | char Name[8]; 133 | ATC_EventTypeDef* psEvents; 134 | uint32_t EventCount; 135 | ATC_CmdTypeDef* psCmds; 136 | uint32_t CmdCount; 137 | uint16_t Size; 138 | uint16_t RespCount; 139 | uint16_t RxIndex; 140 | uint16_t TxLen; 141 | uint8_t* pRxBuff; 142 | uint8_t* pTxBuff; 143 | uint8_t* pReadBuff; 144 | uint8_t* ppResp[ATC_RESP_MAX]; 145 | 146 | } ATC_HandleTypeDef; 147 | 148 | /************************************************************************************************************ 149 | ************** Public Functions 150 | ************************************************************************************************************/ 151 | 152 | bool ATC_Init(ATC_HandleTypeDef* hAtc, UART_HandleTypeDef* hUart, uint16_t BufferSize, const char* pName); 153 | void ATC_DeInit(ATC_HandleTypeDef* hAtc); 154 | bool ATC_SetEvents(ATC_HandleTypeDef* hAtc, const ATC_EventTypeDef* psEvents); 155 | bool ATC_SetCommands(ATC_HandleTypeDef* hAtc, const ATC_CmdTypeDef* psCmds); 156 | void ATC_Loop(ATC_HandleTypeDef* hAtc); 157 | int ATC_SendReceive(ATC_HandleTypeDef* hAtc, const char* pCommand, uint32_t TxTimeout, char** ppResp, uint32_t RxTimeout, uint8_t Items, ...); 158 | bool ATC_Send(ATC_HandleTypeDef* hAtc, const char* pCommand, uint32_t TxTimeout, ...); 159 | int ATC_Receive(ATC_HandleTypeDef* hAtc, char** ppResp, uint32_t RxTimeout, uint8_t Items, ...); 160 | 161 | void ATC_IdleLineCallback(ATC_HandleTypeDef* hAtc, uint16_t Len); 162 | void ATC_Delay(uint32_t Delay); 163 | 164 | #ifdef __cplusplus 165 | } 166 | #endif 167 | #endif 168 | --------------------------------------------------------------------------------