Porting guide

High level of ESP-AT library is platform independent, written in ANSI C99, however there is an important part where middleware needs to communicate with target ESP device and it must work under different optional operating systems selected by final customer.

Porting consists of:

  • Implementation of low-level part, for actual communication between host device and ESP device

  • Implementation of system functions, link between target operating system and middleware functions

  • Assignment of memory for allocation manager

Implement low-level driver

To successfully prepare all parts of low-level driver, application must take care of:

  • Implementing lwesp_ll_init() and lwesp_ll_deinit() callback functions

  • Implement and assign send data and optional hardware reset function callbacks

  • Assign memory for allocation manager when using default allocator or use custom allocator

  • Process received data from ESP device and send it to input module for further processing

Tip

Port examples are available for STM32 and WIN32 architectures. Both actual working and up-to-date implementations are available within the library.

Note

Check Input module for more information about direct & indirect input processing.

Implement system functions

System functions are bridge between operating system calls and ESP middleware. ESP library relies on stable operating system features and its implementation and does not require any special features which do not normally come with operating systems.

Operating system must support:

  • Thread management functions

  • Mutex management functions

  • Binary semaphores only, no need for counting semaphores

  • Message queue management functions

Warning

If any of the features are not available within targeted operating system, customer needs to resolve it with care. As an example, message queue is not available in WIN32 OS API therefore custom message queue has been implemented using binary semaphores

Application needs to implement all system call functions, starting with lwesp_sys_. It must also prepare header file for standard types in order to support OS types within ESP middleware.

An example code is provided latter section of this page for WIN32 and STM32.

Steps to follow

  • Copy lwesp/src/system/lwesp_sys_template.c to the same folder and rename it to application port, eg. lwesp_sys_win32.c

  • Open newly created file and implement all system functions

  • Copy folder lwesp/src/include/system/port/template/* to the same folder and rename folder name to application port, eg. cmsis_os

  • Open lwesp_sys_port.h file from newly created folder and implement all typedefs and macros for specific target

  • Add source file to compiler sources and add path to header file to include paths in compiler options

Note

Check System functions for function prototypes.

Example: Low-level driver for WIN32

Example code for low-level porting on WIN32 platform. It uses native Windows features to open COM port and read/write from/to it.

Notes:

  • It uses separate thread for received data processing. It uses lwesp_input_process() or lwesp_input() functions, based on application configuration of LWESP_CFG_INPUT_USE_PROCESS parameter.

    • When LWESP_CFG_INPUT_USE_PROCESS is disabled, dedicated receive buffer is created by ESP-AT library and lwesp_input() function just writes data to it and does not process received characters immediately. This is handled by Processing thread at later stage instead.

    • When LWESP_CFG_INPUT_USE_PROCESS is enabled, lwesp_input_process() is used, which directly processes input data and sends potential callback/event functions to application layer.

  • Memory manager has been assigned to 1 region of LWESP_MEM_SIZE size

  • It sets send and reset callback functions for ESP-AT library

Actual implementation of low-level driver for WIN32
  1/**
  2 * \file            lwesp_ll_win32.c
  3 * \brief           Low-level communication with ESP device for WIN32
  4 */
  5
  6/*
  7 * Copyright (c) 2023 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwESP - Lightweight ESP-AT parser library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v1.1.2-dev
 33 */
 34#include "lwesp/lwesp.h"
 35#include "lwesp/lwesp_input.h"
 36#include "lwesp/lwesp_mem.h"
 37#include "system/lwesp_ll.h"
 38
 39#if !__DOXYGEN__
 40
 41volatile uint8_t lwesp_ll_win32_driver_ignore_data;
 42static uint8_t initialized = 0;
 43static HANDLE thread_handle;
 44static volatile HANDLE com_port;    /*!< COM port handle */
 45static uint8_t data_buffer[0x1000]; /*!< Received data array */
 46
 47static void uart_thread(void* param);
 48
 49/**
 50 * \brief           Send data to ESP device, function called from ESP stack when we have data to send
 51 */
 52static size_t
 53send_data(const void* data, size_t len) {
 54    DWORD written;
 55    if (com_port != NULL) {
 56#if !LWESP_CFG_AT_ECHO
 57        const uint8_t* d = data;
 58        HANDLE hConsole;
 59
 60        hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
 61        SetConsoleTextAttribute(hConsole, FOREGROUND_RED);
 62        for (DWORD i = 0; i < len; ++i) {
 63            printf("%c", d[i]);
 64        }
 65        SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
 66#endif /* !LWESP_CFG_AT_ECHO */
 67
 68        WriteFile(com_port, data, len, &written, NULL);
 69        FlushFileBuffers(com_port);
 70        return written;
 71    }
 72    return 0;
 73}
 74
 75/**
 76 * \brief           Configure UART (USB to UART)
 77 * \return          `1` if initialized, `0` otherwise
 78 */
 79static uint8_t
 80configure_uart(uint32_t baudrate) {
 81    size_t i;
 82    DCB dcb = {.DCBlength = sizeof(dcb)};
 83
 84    /*
 85     * List of COM ports to probe for ESP devices
 86     * This may be different on your computer
 87     */
 88    static const char* com_port_names[] = {"\\\\.\\COM17", "\\\\.\\COM4", "\\\\.\\COM8", "\\\\.\\COM9", "\\\\.\\COM10"};
 89
 90    /* Try to open one of listed COM ports */
 91    if (!initialized) {
 92        printf("Initializing COM port first time\r\n");
 93        for (i = 0; i < LWESP_ARRAYSIZE(com_port_names); ++i) {
 94            printf("Trying to open COM port %s\r\n", com_port_names[i]);
 95            com_port = CreateFileA(com_port_names[i], GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
 96            if (GetCommState(com_port, &dcb)) {
 97                printf("Successfully received info for COM port %s. Using this one..\r\n", com_port_names[i]);
 98                break;
 99            } else {
100                printf("Could not get info for COM port %s\r\n", com_port_names[i]);
101            }
102        }
103        if (i == LWESP_ARRAYSIZE(com_port_names)) {
104            printf("Could not get info for any COM port. Entering while loop\r\n");
105            while (1) {
106                Sleep(1000);
107            }
108        }
109    }
110
111    /* Configure COM port parameters */
112    if (GetCommState(com_port, &dcb)) {
113        COMMTIMEOUTS timeouts;
114
115        /* Set port config */
116        dcb.BaudRate = baudrate;
117        dcb.ByteSize = 8;
118        dcb.Parity = NOPARITY;
119        dcb.StopBits = ONESTOPBIT;
120        if (SetCommState(com_port, &dcb)) {
121            /* Set timeouts config */
122            if (GetCommTimeouts(com_port, &timeouts)) {
123                /* Set timeout to return immediately from ReadFile function */
124                timeouts.ReadIntervalTimeout = MAXDWORD;
125                timeouts.ReadTotalTimeoutConstant = 0;
126                timeouts.ReadTotalTimeoutMultiplier = 0;
127                if (SetCommTimeouts(com_port, &timeouts)) {
128                    GetCommTimeouts(com_port, &timeouts);
129                } else {
130                    printf("[LWESP LL] Could not set port timeout config\r\n");
131                }
132            } else {
133                printf("[LWESP LL] Could not get port timeout config\r\n");
134            }
135        } else {
136            printf("[LWESP LL] Could not set port config\r\n");
137        }
138    } else {
139        printf("[LWESP LL] Could not get port info\r\n");
140    }
141
142    /* On first function call, create a thread to read data from COM port */
143    if (!initialized) {
144        lwesp_sys_thread_create(&thread_handle, "lwesp_ll_thread", uart_thread, NULL, 0, 0);
145    }
146    return 1;
147}
148
149/**
150 * \brief           UART thread
151 */
152static void
153uart_thread(void* param) {
154    DWORD bytes_read;
155    lwesp_sys_sem_t sem;
156    FILE* file = NULL;
157
158    LWESP_UNUSED(param);
159
160    lwesp_sys_sem_create(&sem, 0); /* Create semaphore for delay functions */
161    while (com_port == NULL) {
162        lwesp_sys_sem_wait(&sem, 1); /* Add some delay with yield */
163    }
164
165    fopen_s(&file, "log_file.txt", "w+"); /* Open debug file in write mode */
166    while (1) {
167        while (com_port == NULL) {
168            lwesp_sys_sem_wait(&sem, 1);
169        }
170
171        /*
172         * Try to read data from COM port
173         * and send it to upper layer for processing
174         */
175        do {
176            ReadFile(com_port, data_buffer, sizeof(data_buffer), &bytes_read, NULL);
177            if (bytes_read > 0) {
178                HANDLE hConsole;
179                hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
180                SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);
181                for (DWORD i = 0; i < bytes_read; ++i) {
182                    printf("%c", data_buffer[i]);
183                }
184                SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
185
186                if (lwesp_ll_win32_driver_ignore_data) {
187                    printf("IGNORING..\r\n");
188                    continue;
189                }
190
191                /* Send received data to input processing module */
192#if LWESP_CFG_INPUT_USE_PROCESS
193                lwesp_input_process(data_buffer, (size_t)bytes_read);
194#else  /* LWESP_CFG_INPUT_USE_PROCESS */
195                lwesp_input(data_buffer, (size_t)bytes_read);
196#endif /* !LWESP_CFG_INPUT_USE_PROCESS */
197
198                /* Write received data to output debug file */
199                if (file != NULL) {
200                    fwrite(data_buffer, 1, bytes_read, file);
201                    fflush(file);
202                }
203            }
204        } while (bytes_read == (DWORD)sizeof(data_buffer));
205
206        /* Implement delay to allow other tasks processing */
207        lwesp_sys_sem_wait(&sem, 1);
208    }
209}
210
211/**
212 * \brief           Reset device GPIO management
213 */
214static uint8_t
215reset_device(uint8_t state) {
216    LWESP_UNUSED(state);
217    return 0; /* Hardware reset was not successful */
218}
219
220/**
221 * \brief           Callback function called from initialization process
222 */
223lwespr_t
224lwesp_ll_init(lwesp_ll_t* ll) {
225#if !LWESP_CFG_MEM_CUSTOM
226    /* Step 1: Configure memory for dynamic allocations */
227    static uint8_t memory[0x10000]; /* Create memory for dynamic allocations with specific size */
228
229    /*
230     * Create memory region(s) of memory.
231     * If device has internal/external memory available,
232     * multiple memories may be used
233     */
234    lwesp_mem_region_t mem_regions[] = {{memory, sizeof(memory)}};
235    if (!initialized) {
236        lwesp_mem_assignmemory(mem_regions,
237                               LWESP_ARRAYSIZE(mem_regions)); /* Assign memory for allocations to ESP library */
238    }
239#endif /* !LWESP_CFG_MEM_CUSTOM */
240
241    /* Step 2: Set AT port send function to use when we have data to transmit */
242    if (!initialized) {
243        ll->send_fn = send_data; /* Set callback function to send data */
244        ll->reset_fn = reset_device;
245    }
246
247    /* Step 3: Configure AT port to be able to send/receive data to/from ESP device */
248    if (!configure_uart(ll->uart.baudrate)) { /* Initialize UART for communication */
249        return lwespERR;
250    }
251    initialized = 1;
252    return lwespOK;
253}
254
255/**
256 * \brief           Callback function to de-init low-level communication part
257 */
258lwespr_t
259lwesp_ll_deinit(lwesp_ll_t* ll) {
260    LWESP_UNUSED(ll);
261    if (thread_handle != NULL) {
262        lwesp_sys_thread_terminate(&thread_handle);
263        thread_handle = NULL;
264    }
265    initialized = 0; /* Clear initialized flag */
266    return lwespOK;
267}
268
269#endif /* !__DOXYGEN__ */

Example: Low-level driver for STM32

Example code for low-level porting on STM32 platform. It uses CMSIS-OS based application layer functions for implementing threads & other OS dependent features.

Notes:

  • It uses separate thread for received data processing. It uses lwesp_input_process() function to directly process received data without using intermediate receive buffer

  • Memory manager has been assigned to 1 region of LWESP_MEM_SIZE size

  • It sets send and reset callback functions for ESP-AT library

Actual implementation of low-level driver for STM32
  1/**
  2 * \file            lwesp_ll_stm32.c
  3 * \brief           Generic STM32 driver, included in various STM32 driver variants
  4 */
  5
  6/*
  7 * Copyright (c) 2023 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwESP - Lightweight ESP-AT parser library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v1.1.2-dev
 33 */
 34
 35/*
 36 * How it works
 37 *
 38 * On first call to \ref lwesp_ll_init, new thread is created and processed in usart_ll_thread function.
 39 * USART is configured in RX DMA mode and any incoming bytes are processed inside thread function.
 40 * DMA and USART implement interrupt handlers to notify main thread about new data ready to send to upper layer.
 41 *
 42 * More about UART + RX DMA: https://github.com/MaJerle/stm32-usart-dma-rx-tx
 43 *
 44 * \ref LWESP_CFG_INPUT_USE_PROCESS must be enabled in `lwesp_config.h` to use this driver.
 45 */
 46#include "lwesp/lwesp.h"
 47#include "lwesp/lwesp_input.h"
 48#include "lwesp/lwesp_mem.h"
 49#include "system/lwesp_ll.h"
 50
 51#if !__DOXYGEN__
 52
 53#if !LWESP_CFG_INPUT_USE_PROCESS
 54#error "LWESP_CFG_INPUT_USE_PROCESS must be enabled in `lwesp_config.h` to use this driver."
 55#endif /* LWESP_CFG_INPUT_USE_PROCESS */
 56
 57#if !defined(LWESP_USART_DMA_RX_BUFF_SIZE)
 58#define LWESP_USART_DMA_RX_BUFF_SIZE 0x1000
 59#endif /* !defined(LWESP_USART_DMA_RX_BUFF_SIZE) */
 60
 61#if !defined(LWESP_MEM_SIZE)
 62#define LWESP_MEM_SIZE 0x1000
 63#endif /* !defined(LWESP_MEM_SIZE) */
 64
 65#if !defined(LWESP_USART_RDR_NAME)
 66#define LWESP_USART_RDR_NAME RDR
 67#endif /* !defined(LWESP_USART_RDR_NAME) */
 68
 69/* USART memory */
 70static uint8_t usart_mem[LWESP_USART_DMA_RX_BUFF_SIZE];
 71static uint8_t is_running, initialized;
 72static size_t old_pos;
 73
 74/* USART thread */
 75static void usart_ll_thread(void* arg);
 76static osThreadId_t usart_ll_thread_id;
 77
 78/* Message queue */
 79static osMessageQueueId_t usart_ll_mbox_id;
 80
 81/**
 82 * \brief           USART data processing
 83 */
 84static void
 85usart_ll_thread(void* arg) {
 86    size_t pos;
 87
 88    LWESP_UNUSED(arg);
 89
 90    while (1) {
 91        void* d;
 92        /* Wait for the event message from DMA or USART */
 93        osMessageQueueGet(usart_ll_mbox_id, &d, NULL, osWaitForever);
 94
 95        /* Read data */
 96#if defined(LWESP_USART_DMA_RX_STREAM)
 97        pos = sizeof(usart_mem) - LL_DMA_GetDataLength(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
 98#else
 99        pos = sizeof(usart_mem) - LL_DMA_GetDataLength(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
100#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
101        if (pos != old_pos && is_running) {
102            if (pos > old_pos) {
103                lwesp_input_process(&usart_mem[old_pos], pos - old_pos);
104            } else {
105                lwesp_input_process(&usart_mem[old_pos], sizeof(usart_mem) - old_pos);
106                if (pos > 0) {
107                    lwesp_input_process(&usart_mem[0], pos);
108                }
109            }
110            old_pos = pos;
111        }
112    }
113}
114
115/**
116 * \brief           Configure UART using DMA for receive in double buffer mode and IDLE line detection
117 */
118static void
119prv_configure_uart(uint32_t baudrate) {
120    static LL_USART_InitTypeDef usart_init;
121    static LL_DMA_InitTypeDef dma_init;
122    LL_GPIO_InitTypeDef gpio_init;
123
124    if (!initialized) {
125        /* Enable peripheral clocks */
126        LWESP_USART_CLK;
127        LWESP_USART_DMA_CLK;
128        LWESP_USART_TX_PORT_CLK;
129        LWESP_USART_RX_PORT_CLK;
130
131#if defined(LWESP_RESET_PIN)
132        LWESP_RESET_PORT_CLK;
133#endif /* defined(LWESP_RESET_PIN) */
134
135#if defined(LWESP_GPIO0_PIN)
136        LWESP_GPIO0_PORT_CLK;
137#endif /* defined(LWESP_GPIO0_PIN) */
138
139#if defined(LWESP_GPIO2_PIN)
140        LWESP_GPIO2_PORT_CLK;
141#endif /* defined(LWESP_GPIO2_PIN) */
142
143#if defined(LWESP_CH_PD_PIN)
144        LWESP_CH_PD_PORT_CLK;
145#endif /* defined(LWESP_CH_PD_PIN) */
146
147        /* Global pin configuration */
148        LL_GPIO_StructInit(&gpio_init);
149        gpio_init.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
150        gpio_init.Pull = LL_GPIO_PULL_UP;
151        gpio_init.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
152        gpio_init.Mode = LL_GPIO_MODE_OUTPUT;
153
154#if defined(LWESP_RESET_PIN)
155        /* Configure RESET pin */
156        gpio_init.Pin = LWESP_RESET_PIN;
157        LL_GPIO_Init(LWESP_RESET_PORT, &gpio_init);
158#endif /* defined(LWESP_RESET_PIN) */
159
160#if defined(LWESP_GPIO0_PIN)
161        /* Configure GPIO0 pin */
162        gpio_init.Pin = LWESP_GPIO0_PIN;
163        LL_GPIO_Init(LWESP_GPIO0_PORT, &gpio_init);
164        LL_GPIO_SetOutputPin(LWESP_GPIO0_PORT, LWESP_GPIO0_PIN);
165#endif /* defined(LWESP_GPIO0_PIN) */
166
167#if defined(LWESP_GPIO2_PIN)
168        /* Configure GPIO2 pin */
169        gpio_init.Pin = LWESP_GPIO2_PIN;
170        LL_GPIO_Init(LWESP_GPIO2_PORT, &gpio_init);
171        LL_GPIO_SetOutputPin(LWESP_GPIO2_PORT, LWESP_GPIO2_PIN);
172#endif /* defined(LWESP_GPIO2_PIN) */
173
174#if defined(LWESP_CH_PD_PIN)
175        /* Configure CH_PD pin */
176        gpio_init.Pin = LWESP_CH_PD_PIN;
177        LL_GPIO_Init(LWESP_CH_PD_PORT, &gpio_init);
178        LL_GPIO_SetOutputPin(LWESP_CH_PD_PORT, LWESP_CH_PD_PIN);
179#endif /* defined(LWESP_CH_PD_PIN) */
180
181        /* Configure USART pins */
182        gpio_init.Mode = LL_GPIO_MODE_ALTERNATE;
183
184        /* TX PIN */
185        gpio_init.Alternate = LWESP_USART_TX_PIN_AF;
186        gpio_init.Pin = LWESP_USART_TX_PIN;
187        LL_GPIO_Init(LWESP_USART_TX_PORT, &gpio_init);
188
189        /* RX PIN */
190        gpio_init.Alternate = LWESP_USART_RX_PIN_AF;
191        gpio_init.Pin = LWESP_USART_RX_PIN;
192        LL_GPIO_Init(LWESP_USART_RX_PORT, &gpio_init);
193
194        /* Configure UART */
195        LL_USART_DeInit(LWESP_USART);
196        LL_USART_StructInit(&usart_init);
197        usart_init.BaudRate = baudrate;
198        usart_init.DataWidth = LL_USART_DATAWIDTH_8B;
199        usart_init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
200        usart_init.OverSampling = LL_USART_OVERSAMPLING_16;
201        usart_init.Parity = LL_USART_PARITY_NONE;
202        usart_init.StopBits = LL_USART_STOPBITS_1;
203        usart_init.TransferDirection = LL_USART_DIRECTION_TX_RX;
204        LL_USART_Init(LWESP_USART, &usart_init);
205
206        /* Enable USART interrupts and DMA request */
207        LL_USART_EnableIT_IDLE(LWESP_USART);
208        LL_USART_EnableIT_PE(LWESP_USART);
209        LL_USART_EnableIT_ERROR(LWESP_USART);
210        LL_USART_EnableDMAReq_RX(LWESP_USART);
211
212        /* Enable USART interrupts */
213        NVIC_SetPriority(LWESP_USART_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
214        NVIC_EnableIRQ(LWESP_USART_IRQ);
215
216        /* Configure DMA */
217        is_running = 0;
218#if defined(LWESP_USART_DMA_RX_STREAM)
219        LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
220        dma_init.Channel = LWESP_USART_DMA_RX_CH;
221#else
222        LL_DMA_DeInit(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
223        dma_init.PeriphRequest = LWESP_USART_DMA_RX_REQ_NUM;
224#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
225        dma_init.PeriphOrM2MSrcAddress = (uint32_t)&LWESP_USART->LWESP_USART_RDR_NAME;
226        dma_init.MemoryOrM2MDstAddress = (uint32_t)usart_mem;
227        dma_init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
228        dma_init.Mode = LL_DMA_MODE_CIRCULAR;
229        dma_init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
230        dma_init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
231        dma_init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
232        dma_init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
233        dma_init.NbData = sizeof(usart_mem);
234        dma_init.Priority = LL_DMA_PRIORITY_MEDIUM;
235#if defined(LWESP_USART_DMA_RX_STREAM)
236        LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM, &dma_init);
237#else
238        LL_DMA_Init(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH, &dma_init);
239#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
240
241        /* Enable DMA interrupts */
242#if defined(LWESP_USART_DMA_RX_STREAM)
243        LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
244        LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
245        LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
246        LL_DMA_EnableIT_FE(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
247        LL_DMA_EnableIT_DME(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
248#else
249        LL_DMA_EnableIT_HT(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
250        LL_DMA_EnableIT_TC(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
251        LL_DMA_EnableIT_TE(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
252#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
253
254        /* Enable DMA interrupts */
255        NVIC_SetPriority(LWESP_USART_DMA_RX_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
256        NVIC_EnableIRQ(LWESP_USART_DMA_RX_IRQ);
257
258        old_pos = 0;
259        is_running = 1;
260
261        /* Start DMA and USART */
262#if defined(LWESP_USART_DMA_RX_STREAM)
263        LL_DMA_EnableStream(LWESP_USART_DMA, LWESP_USART_DMA_RX_STREAM);
264#else
265        LL_DMA_EnableChannel(LWESP_USART_DMA, LWESP_USART_DMA_RX_CH);
266#endif /* defined(LWESP_USART_DMA_RX_STREAM) */
267        LL_USART_Enable(LWESP_USART);
268    } else {
269        osDelay(10);
270        LL_USART_Disable(LWESP_USART);
271        usart_init.BaudRate = baudrate;
272        LL_USART_Init(LWESP_USART, &usart_init);
273        LL_USART_Enable(LWESP_USART);
274    }
275
276    /* Create mbox and start thread */
277    if (usart_ll_mbox_id == NULL) {
278        usart_ll_mbox_id = osMessageQueueNew(10, sizeof(void*), NULL);
279    }
280    if (usart_ll_thread_id == NULL) {
281        const osThreadAttr_t attr = {.stack_size = 1024};
282        usart_ll_thread_id = osThreadNew(usart_ll_thread, usart_ll_mbox_id, &attr);
283    }
284}
285
286#if defined(LWESP_RESET_PIN)
287/**
288 * \brief           Hardware reset callback
289 */
290static uint8_t
291prv_reset_device(uint8_t state) {
292    if (state) { /* Activate reset line */
293        LL_GPIO_ResetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
294    } else {
295        LL_GPIO_SetOutputPin(LWESP_RESET_PORT, LWESP_RESET_PIN);
296    }
297    return 1;
298}
299#endif /* defined(LWESP_RESET_PIN) */
300
301/**
302 * \brief           Send data to ESP device
303 * \param[in]       data: Pointer to data to send
304 * \param[in]       len: Number of bytes to send
305 * \return          Number of bytes sent
306 */
307static size_t
308prv_send_data(const void* data, size_t len) {
309    const uint8_t* d = data;
310
311    for (size_t i = 0; i < len; ++i, ++d) {
312        LL_USART_TransmitData8(LWESP_USART, *d);
313        while (!LL_USART_IsActiveFlag_TXE(LWESP_USART)) {}
314    }
315    return len;
316}
317
318/**
319 * \brief           Callback function called from initialization process
320 */
321lwespr_t
322lwesp_ll_init(lwesp_ll_t* ll) {
323#if !LWESP_CFG_MEM_CUSTOM
324    static uint8_t memory[LWESP_MEM_SIZE];
325    const lwesp_mem_region_t mem_regions[] = {{memory, sizeof(memory)}, {NULL, 0}};
326
327    if (!initialized) {
328        lwesp_mem_assignmemory(mem_regions, LWESP_ARRAYSIZE(mem_regions)); /* Assign memory for allocations */
329    }
330#endif /* !LWESP_CFG_MEM_CUSTOM */
331
332    if (!initialized) {
333        ll->send_fn = prv_send_data; /* Set callback function to send data */
334#if defined(LWESP_RESET_PIN)
335        ll->reset_fn = prv_reset_device; /* Set callback for hardware reset */
336#endif                                   /* defined(LWESP_RESET_PIN) */
337    }
338
339    prv_configure_uart(ll->uart.baudrate); /* Initialize UART for communication */
340    initialized = 1;
341    return lwespOK;
342}
343
344/**
345 * \brief           Callback function to de-init low-level communication part
346 */
347lwespr_t
348lwesp_ll_deinit(lwesp_ll_t* ll) {
349    if (usart_ll_mbox_id != NULL) {
350        osMessageQueueId_t tmp = usart_ll_mbox_id;
351        usart_ll_mbox_id = NULL;
352        osMessageQueueDelete(tmp);
353    }
354    if (usart_ll_thread_id != NULL) {
355        osThreadId_t tmp = usart_ll_thread_id;
356        usart_ll_thread_id = NULL;
357        osThreadTerminate(tmp);
358    }
359    initialized = 0;
360    LWESP_UNUSED(ll);
361    return lwespOK;
362}
363
364/**
365 * \brief           UART global interrupt handler
366 */
367void
368LWESP_USART_IRQHANDLER(void) {
369    LL_USART_ClearFlag_IDLE(LWESP_USART);
370    LL_USART_ClearFlag_PE(LWESP_USART);
371    LL_USART_ClearFlag_FE(LWESP_USART);
372    LL_USART_ClearFlag_ORE(LWESP_USART);
373    LL_USART_ClearFlag_NE(LWESP_USART);
374
375    if (usart_ll_mbox_id != NULL) {
376        void* d = (void*)1;
377        osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
378    }
379}
380
381/**
382 * \brief           UART DMA stream/channel handler
383 */
384void
385LWESP_USART_DMA_RX_IRQHANDLER(void) {
386    LWESP_USART_DMA_RX_CLEAR_TC;
387    LWESP_USART_DMA_RX_CLEAR_HT;
388
389    if (usart_ll_mbox_id != NULL) {
390        void* d = (void*)1;
391        osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
392    }
393}
394
395#endif /* !__DOXYGEN__ */

Example: System functions for WIN32

Actual header implementation of system functions for WIN32
 1/**
 2 * \file            lwesp_sys_port.h
 3 * \brief           WIN32 based system file implementation
 4 */
 5
 6/*
 7 * Copyright (c) 2023 Tilen MAJERLE
 8 *
 9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
32 * Version:         v1.1.2-dev
33 */
34#ifndef LWESP_SYSTEM_PORT_HDR_H
35#define LWESP_SYSTEM_PORT_HDR_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "lwesp/lwesp_opt.h"
40#include "windows.h"
41
42#ifdef __cplusplus
43extern "C" {
44#endif /* __cplusplus */
45
46#if LWESP_CFG_OS && !__DOXYGEN__
47
48typedef HANDLE lwesp_sys_mutex_t;
49typedef HANDLE lwesp_sys_sem_t;
50typedef HANDLE lwesp_sys_mbox_t;
51typedef HANDLE lwesp_sys_thread_t;
52typedef int lwesp_sys_thread_prio_t;
53
54#define LWESP_SYS_MBOX_NULL   ((HANDLE)0)
55#define LWESP_SYS_SEM_NULL    ((HANDLE)0)
56#define LWESP_SYS_MUTEX_NULL  ((HANDLE)0)
57#define LWESP_SYS_TIMEOUT     (INFINITE)
58#define LWESP_SYS_THREAD_PRIO (0)
59#define LWESP_SYS_THREAD_SS   (1024)
60
61#endif /* LWESP_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWESP_SYSTEM_PORT_HDR_H */
Actual implementation of system functions for WIN32
  1/**
  2 * \file            lwesp_sys_win32.c
  3 * \brief           System dependant functions for WIN32
  4 */
  5
  6/*
  7 * Copyright (c) 2023 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwESP - Lightweight ESP-AT parser library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v1.1.2-dev
 33 */
 34#include <stdlib.h>
 35#include <string.h>
 36#include "lwesp/lwesp_private.h"
 37#include "system/lwesp_sys.h"
 38#include "windows.h"
 39
 40#if !__DOXYGEN__
 41
 42/**
 43 * \brief           Custom message queue implementation for WIN32
 44 */
 45typedef struct {
 46    lwesp_sys_sem_t sem_not_empty; /*!< Semaphore indicates not empty */
 47    lwesp_sys_sem_t sem_not_full;  /*!< Semaphore indicates not full */
 48    lwesp_sys_sem_t sem;           /*!< Semaphore to lock access */
 49    size_t in, out, size;
 50    void* entries[1];
 51} win32_mbox_t;
 52
 53static LARGE_INTEGER freq, sys_start_time;
 54static lwesp_sys_mutex_t sys_mutex; /* Mutex ID for main protection */
 55
 56/**
 57 * \brief           Check if message box is full
 58 * \param[in]       m: Message box handle
 59 * \return          1 if full, 0 otherwise
 60 */
 61static uint8_t
 62mbox_is_full(win32_mbox_t* m) {
 63    size_t size = 0;
 64    if (m->in > m->out) {
 65        size = (m->in - m->out);
 66    } else if (m->out > m->in) {
 67        size = m->size - m->out + m->in;
 68    }
 69    return size == m->size - 1;
 70}
 71
 72/**
 73 * \brief           Check if message box is empty
 74 * \param[in]       m: Message box handle
 75 * \return          1 if empty, 0 otherwise
 76 */
 77static uint8_t
 78mbox_is_empty(win32_mbox_t* m) {
 79    return m->in == m->out;
 80}
 81
 82/**
 83 * \brief           Get current kernel time in units of milliseconds
 84 */
 85static uint32_t
 86osKernelSysTick(void) {
 87    LONGLONG ret;
 88    LARGE_INTEGER now;
 89
 90    QueryPerformanceFrequency(&freq); /* Get frequency */
 91    QueryPerformanceCounter(&now);    /* Get current time */
 92    ret = now.QuadPart - sys_start_time.QuadPart;
 93    return (uint32_t)(((ret)*1000) / freq.QuadPart);
 94}
 95
 96uint8_t
 97lwesp_sys_init(void) {
 98    QueryPerformanceFrequency(&freq);
 99    QueryPerformanceCounter(&sys_start_time);
100
101    lwesp_sys_mutex_create(&sys_mutex);
102    return 1;
103}
104
105uint32_t
106lwesp_sys_now(void) {
107    return osKernelSysTick();
108}
109
110#if LWESP_CFG_OS
111uint8_t
112lwesp_sys_protect(void) {
113    lwesp_sys_mutex_lock(&sys_mutex);
114    return 1;
115}
116
117uint8_t
118lwesp_sys_unprotect(void) {
119    lwesp_sys_mutex_unlock(&sys_mutex);
120    return 1;
121}
122
123uint8_t
124lwesp_sys_mutex_create(lwesp_sys_mutex_t* p) {
125    *p = CreateMutex(NULL, FALSE, NULL);
126    return *p != NULL;
127}
128
129uint8_t
130lwesp_sys_mutex_delete(lwesp_sys_mutex_t* p) {
131    return CloseHandle(*p);
132}
133
134uint8_t
135lwesp_sys_mutex_lock(lwesp_sys_mutex_t* p) {
136    DWORD ret;
137    ret = WaitForSingleObject(*p, INFINITE);
138    if (ret != WAIT_OBJECT_0) {
139        return 0;
140    }
141    return 1;
142}
143
144uint8_t
145lwesp_sys_mutex_unlock(lwesp_sys_mutex_t* p) {
146    return ReleaseMutex(*p);
147}
148
149uint8_t
150lwesp_sys_mutex_isvalid(lwesp_sys_mutex_t* p) {
151    return p != NULL && *p != NULL;
152}
153
154uint8_t
155lwesp_sys_mutex_invalid(lwesp_sys_mutex_t* p) {
156    *p = LWESP_SYS_MUTEX_NULL;
157    return 1;
158}
159
160uint8_t
161lwesp_sys_sem_create(lwesp_sys_sem_t* p, uint8_t cnt) {
162    HANDLE h;
163    h = CreateSemaphore(NULL, !!cnt, 1, NULL);
164    *p = h;
165    return *p != NULL;
166}
167
168uint8_t
169lwesp_sys_sem_delete(lwesp_sys_sem_t* p) {
170    return CloseHandle(*p);
171}
172
173uint32_t
174lwesp_sys_sem_wait(lwesp_sys_sem_t* p, uint32_t timeout) {
175    DWORD ret;
176
177    if (timeout == 0) {
178        ret = WaitForSingleObject(*p, INFINITE);
179        return 1;
180    } else {
181        ret = WaitForSingleObject(*p, timeout);
182        if (ret == WAIT_OBJECT_0) {
183            return 1;
184        } else {
185            return LWESP_SYS_TIMEOUT;
186        }
187    }
188}
189
190uint8_t
191lwesp_sys_sem_release(lwesp_sys_sem_t* p) {
192    return ReleaseSemaphore(*p, 1, NULL);
193}
194
195uint8_t
196lwesp_sys_sem_isvalid(lwesp_sys_sem_t* p) {
197    return p != NULL && *p != NULL;
198}
199
200uint8_t
201lwesp_sys_sem_invalid(lwesp_sys_sem_t* p) {
202    *p = LWESP_SYS_SEM_NULL;
203    return 1;
204}
205
206uint8_t
207lwesp_sys_mbox_create(lwesp_sys_mbox_t* b, size_t size) {
208    win32_mbox_t* mbox;
209
210    *b = 0;
211
212    mbox = malloc(sizeof(*mbox) + size * sizeof(void*));
213    if (mbox != NULL) {
214        memset(mbox, 0x00, sizeof(*mbox));
215        mbox->size = size + 1; /* Set it to 1 more as cyclic buffer has only one less than size */
216        lwesp_sys_sem_create(&mbox->sem, 1);
217        lwesp_sys_sem_create(&mbox->sem_not_empty, 0);
218        lwesp_sys_sem_create(&mbox->sem_not_full, 0);
219        *b = mbox;
220    }
221    return *b != NULL;
222}
223
224uint8_t
225lwesp_sys_mbox_delete(lwesp_sys_mbox_t* b) {
226    win32_mbox_t* mbox = *b;
227    lwesp_sys_sem_delete(&mbox->sem);
228    lwesp_sys_sem_delete(&mbox->sem_not_full);
229    lwesp_sys_sem_delete(&mbox->sem_not_empty);
230    free(mbox);
231    return 1;
232}
233
234uint32_t
235lwesp_sys_mbox_put(lwesp_sys_mbox_t* b, void* m) {
236    win32_mbox_t* mbox = *b;
237    uint32_t time = osKernelSysTick(); /* Get start time */
238
239    lwesp_sys_sem_wait(&mbox->sem, 0); /* Wait for access */
240
241    /*
242     * Since function is blocking until ready to write something to queue,
243     * wait and release the semaphores to allow other threads
244     * to process the queue before we can write new value.
245     */
246    while (mbox_is_full(mbox)) {
247        lwesp_sys_sem_release(&mbox->sem);          /* Release semaphore */
248        lwesp_sys_sem_wait(&mbox->sem_not_full, 0); /* Wait for semaphore indicating not full */
249        lwesp_sys_sem_wait(&mbox->sem, 0);          /* Wait availability again */
250    }
251    mbox->entries[mbox->in] = m;
252    if (++mbox->in >= mbox->size) {
253        mbox->in = 0;
254    }
255    lwesp_sys_sem_release(&mbox->sem_not_empty); /* Signal non-empty state */
256    lwesp_sys_sem_release(&mbox->sem);           /* Release access for other threads */
257    return osKernelSysTick() - time;
258}
259
260uint32_t
261lwesp_sys_mbox_get(lwesp_sys_mbox_t* b, void** m, uint32_t timeout) {
262    win32_mbox_t* mbox = *b;
263    uint32_t time;
264
265    time = osKernelSysTick();
266
267    /* Get exclusive access to message queue */
268    if (lwesp_sys_sem_wait(&mbox->sem, timeout) == LWESP_SYS_TIMEOUT) {
269        return LWESP_SYS_TIMEOUT;
270    }
271    while (mbox_is_empty(mbox)) {
272        lwesp_sys_sem_release(&mbox->sem);
273        if (lwesp_sys_sem_wait(&mbox->sem_not_empty, timeout) == LWESP_SYS_TIMEOUT) {
274            return LWESP_SYS_TIMEOUT;
275        }
276        lwesp_sys_sem_wait(&mbox->sem, timeout);
277    }
278    *m = mbox->entries[mbox->out];
279    if (++mbox->out >= mbox->size) {
280        mbox->out = 0;
281    }
282    lwesp_sys_sem_release(&mbox->sem_not_full);
283    lwesp_sys_sem_release(&mbox->sem);
284
285    return osKernelSysTick() - time;
286}
287
288uint8_t
289lwesp_sys_mbox_putnow(lwesp_sys_mbox_t* b, void* m) {
290    win32_mbox_t* mbox = *b;
291
292    lwesp_sys_sem_wait(&mbox->sem, 0);
293    if (mbox_is_full(mbox)) {
294        lwesp_sys_sem_release(&mbox->sem);
295        return 0;
296    }
297    mbox->entries[mbox->in] = m;
298    if (mbox->in == mbox->out) {
299        lwesp_sys_sem_release(&mbox->sem_not_empty);
300    }
301    if (++mbox->in >= mbox->size) {
302        mbox->in = 0;
303    }
304    lwesp_sys_sem_release(&mbox->sem);
305    return 1;
306}
307
308uint8_t
309lwesp_sys_mbox_getnow(lwesp_sys_mbox_t* b, void** m) {
310    win32_mbox_t* mbox = *b;
311
312    lwesp_sys_sem_wait(&mbox->sem, 0); /* Wait exclusive access */
313    if (mbox->in == mbox->out) {
314        lwesp_sys_sem_release(&mbox->sem); /* Release access */
315        return 0;
316    }
317
318    *m = mbox->entries[mbox->out];
319    if (++mbox->out >= mbox->size) {
320        mbox->out = 0;
321    }
322    lwesp_sys_sem_release(&mbox->sem_not_full); /* Queue not full anymore */
323    lwesp_sys_sem_release(&mbox->sem);          /* Release semaphore */
324    return 1;
325}
326
327uint8_t
328lwesp_sys_mbox_isvalid(lwesp_sys_mbox_t* b) {
329    return b != NULL && *b != NULL;
330}
331
332uint8_t
333lwesp_sys_mbox_invalid(lwesp_sys_mbox_t* b) {
334    *b = LWESP_SYS_MBOX_NULL;
335    return 1;
336}
337
338uint8_t
339lwesp_sys_thread_create(lwesp_sys_thread_t* t, const char* name, lwesp_sys_thread_fn thread_func, void* const arg,
340                        size_t stack_size, lwesp_sys_thread_prio_t prio) {
341    HANDLE h;
342    DWORD id;
343
344    LWESP_UNUSED(name);
345    LWESP_UNUSED(stack_size);
346    LWESP_UNUSED(prio);
347    h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_func, arg, 0, &id);
348    if (t != NULL) {
349        *t = h;
350    }
351    return h != NULL;
352}
353
354uint8_t
355lwesp_sys_thread_terminate(lwesp_sys_thread_t* t) {
356    if (t == NULL) { /* Shall we terminate ourself? */
357        ExitThread(0);
358    } else {
359        /* We have known thread, find handle by looking at ID */
360        TerminateThread(*t, 0);
361    }
362    return 1;
363}
364
365uint8_t
366lwesp_sys_thread_yield(void) {
367    /* Not implemented */
368    return 1;
369}
370
371#endif /* LWESP_CFG_OS */
372#endif /* !__DOXYGEN__ */

Example: System functions for CMSIS-OS

Actual header implementation of system functions for CMSIS-OS based operating systems
 1/**
 2 * \file            lwesp_sys_port.h
 3 * \brief           CMSIS-OS based system file
 4 */
 5
 6/*
 7 * Copyright (c) 2023 Tilen MAJERLE
 8 *
 9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
32 * Version:         v1.1.2-dev
33 */
34#ifndef LWESP_SYSTEM_PORT_HDR_H
35#define LWESP_SYSTEM_PORT_HDR_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "cmsis_os.h"
40#include "lwesp/lwesp_opt.h"
41
42#ifdef __cplusplus
43extern "C" {
44#endif /* __cplusplus */
45
46#if LWESP_CFG_OS && !__DOXYGEN__
47
48typedef osMutexId_t lwesp_sys_mutex_t;
49typedef osSemaphoreId_t lwesp_sys_sem_t;
50typedef osMessageQueueId_t lwesp_sys_mbox_t;
51typedef osThreadId_t lwesp_sys_thread_t;
52typedef osPriority_t lwesp_sys_thread_prio_t;
53
54#define LWESP_SYS_MUTEX_NULL  ((lwesp_sys_mutex_t)0)
55#define LWESP_SYS_SEM_NULL    ((lwesp_sys_sem_t)0)
56#define LWESP_SYS_MBOX_NULL   ((lwesp_sys_mbox_t)0)
57#define LWESP_SYS_TIMEOUT     ((uint32_t)osWaitForever)
58#define LWESP_SYS_THREAD_PRIO (osPriorityNormal)
59#define LWESP_SYS_THREAD_SS   (512)
60
61#endif /* LWESP_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWESP_SYSTEM_PORT_HDR_H */
Actual implementation of system functions for CMSIS-OS based operating systems
  1/**
  2 * \file            lwesp_sys_cmsis_os.c
  3 * \brief           System dependent functions for CMSIS based operating system
  4 */
  5
  6/*
  7 * Copyright (c) 2023 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwESP - Lightweight ESP-AT parser library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v1.1.2-dev
 33 */
 34#include "cmsis_os.h"
 35#include "system/lwesp_sys.h"
 36
 37#if !__DOXYGEN__
 38
 39static osMutexId_t sys_mutex;
 40
 41uint8_t
 42lwesp_sys_init(void) {
 43    lwesp_sys_mutex_create(&sys_mutex);
 44    return 1;
 45}
 46
 47uint32_t
 48lwesp_sys_now(void) {
 49    return osKernelGetTickCount();
 50}
 51
 52uint8_t
 53lwesp_sys_protect(void) {
 54    lwesp_sys_mutex_lock(&sys_mutex);
 55    return 1;
 56}
 57
 58uint8_t
 59lwesp_sys_unprotect(void) {
 60    lwesp_sys_mutex_unlock(&sys_mutex);
 61    return 1;
 62}
 63
 64uint8_t
 65lwesp_sys_mutex_create(lwesp_sys_mutex_t* p) {
 66    const osMutexAttr_t attr = {
 67        .attr_bits = osMutexRecursive,
 68        .name = "lwesp_mutex",
 69    };
 70    return (*p = osMutexNew(&attr)) != NULL;
 71}
 72
 73uint8_t
 74lwesp_sys_mutex_delete(lwesp_sys_mutex_t* p) {
 75    return osMutexDelete(*p) == osOK;
 76}
 77
 78uint8_t
 79lwesp_sys_mutex_lock(lwesp_sys_mutex_t* p) {
 80    return osMutexAcquire(*p, osWaitForever) == osOK;
 81}
 82
 83uint8_t
 84lwesp_sys_mutex_unlock(lwesp_sys_mutex_t* p) {
 85    return osMutexRelease(*p) == osOK;
 86}
 87
 88uint8_t
 89lwesp_sys_mutex_isvalid(lwesp_sys_mutex_t* p) {
 90    return p != NULL && *p != NULL;
 91}
 92
 93uint8_t
 94lwesp_sys_mutex_invalid(lwesp_sys_mutex_t* p) {
 95    *p = LWESP_SYS_MUTEX_NULL;
 96    return 1;
 97}
 98
 99uint8_t
100lwesp_sys_sem_create(lwesp_sys_sem_t* p, uint8_t cnt) {
101    const osSemaphoreAttr_t attr = {
102        .name = "lwesp_sem",
103    };
104    return (*p = osSemaphoreNew(1, cnt > 0 ? 1 : 0, &attr)) != NULL;
105}
106
107uint8_t
108lwesp_sys_sem_delete(lwesp_sys_sem_t* p) {
109    return osSemaphoreDelete(*p) == osOK;
110}
111
112uint32_t
113lwesp_sys_sem_wait(lwesp_sys_sem_t* p, uint32_t timeout) {
114    uint32_t tick = osKernelSysTick();
115    return (osSemaphoreAcquire(*p, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick)
116                                                                                    : LWESP_SYS_TIMEOUT;
117}
118
119uint8_t
120lwesp_sys_sem_release(lwesp_sys_sem_t* p) {
121    return osSemaphoreRelease(*p) == osOK;
122}
123
124uint8_t
125lwesp_sys_sem_isvalid(lwesp_sys_sem_t* p) {
126    return p != NULL && *p != NULL;
127}
128
129uint8_t
130lwesp_sys_sem_invalid(lwesp_sys_sem_t* p) {
131    *p = LWESP_SYS_SEM_NULL;
132    return 1;
133}
134
135uint8_t
136lwesp_sys_mbox_create(lwesp_sys_mbox_t* b, size_t size) {
137    const osMessageQueueAttr_t attr = {
138        .name = "lwesp_mbox",
139    };
140    return (*b = osMessageQueueNew(size, sizeof(void*), &attr)) != NULL;
141}
142
143uint8_t
144lwesp_sys_mbox_delete(lwesp_sys_mbox_t* b) {
145    if (osMessageQueueGetCount(*b) > 0) {
146        return 0;
147    }
148    return osMessageQueueDelete(*b) == osOK;
149}
150
151uint32_t
152lwesp_sys_mbox_put(lwesp_sys_mbox_t* b, void* m) {
153    uint32_t tick = osKernelSysTick();
154    return osMessageQueuePut(*b, &m, 0, osWaitForever) == osOK ? (osKernelSysTick() - tick) : LWESP_SYS_TIMEOUT;
155}
156
157uint32_t
158lwesp_sys_mbox_get(lwesp_sys_mbox_t* b, void** m, uint32_t timeout) {
159    uint32_t tick = osKernelSysTick();
160    return (osMessageQueueGet(*b, m, NULL, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick)
161                                                                                            : LWESP_SYS_TIMEOUT;
162}
163
164uint8_t
165lwesp_sys_mbox_putnow(lwesp_sys_mbox_t* b, void* m) {
166    return osMessageQueuePut(*b, &m, 0, 0) == osOK;
167}
168
169uint8_t
170lwesp_sys_mbox_getnow(lwesp_sys_mbox_t* b, void** m) {
171    return osMessageQueueGet(*b, m, NULL, 0) == osOK;
172}
173
174uint8_t
175lwesp_sys_mbox_isvalid(lwesp_sys_mbox_t* b) {
176    return b != NULL && *b != NULL;
177}
178
179uint8_t
180lwesp_sys_mbox_invalid(lwesp_sys_mbox_t* b) {
181    *b = LWESP_SYS_MBOX_NULL;
182    return 1;
183}
184
185uint8_t
186lwesp_sys_thread_create(lwesp_sys_thread_t* t, const char* name, lwesp_sys_thread_fn thread_func, void* const arg,
187                        size_t stack_size, lwesp_sys_thread_prio_t prio) {
188    lwesp_sys_thread_t id;
189    const osThreadAttr_t thread_attr = {.name = (char*)name,
190                                        .priority = (osPriority)prio,
191                                        .stack_size = stack_size > 0 ? stack_size : LWESP_SYS_THREAD_SS};
192
193    id = osThreadNew(thread_func, arg, &thread_attr);
194    if (t != NULL) {
195        *t = id;
196    }
197    return id != NULL;
198}
199
200uint8_t
201lwesp_sys_thread_terminate(lwesp_sys_thread_t* t) {
202    if (t != NULL) {
203        osThreadTerminate(*t);
204    } else {
205        osThreadExit();
206    }
207    return 1;
208}
209
210uint8_t
211lwesp_sys_thread_yield(void) {
212    osThreadYield();
213    return 1;
214}
215
216#endif /* !__DOXYGEN__ */