├── .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 |

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 |
--------------------------------------------------------------------------------