Raspberry BASIC > ScriptBasic

Pico

<< < (3/3)

John Spikowski:
This is an example of the ESP32-PICO acting as a TCP Echo Server over WIFI. I'm connecting to it with ScriptBasic using its built in socket support.

TCP Server

--- Code: C ---/* BSD Socket API Example    This example code is in the Public Domain (or CC0 licensed, at your option.)    Unless required by applicable law or agreed to in writing, this   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR   CONDITIONS OF ANY KIND, either express or implied.*/#include <string.h>#include <sys/param.h>#include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "esp_system.h"#include "esp_wifi.h"#include "esp_event.h"#include "esp_log.h"#include "nvs_flash.h"#include "esp_netif.h"#include "protocol_examples_common.h" #include "lwip/err.h"#include "lwip/sockets.h"#include "lwip/sys.h"#include <lwip/netdb.h>  #define PORT CONFIG_EXAMPLE_PORT static const char *TAG = "example"; static void do_retransmit(const int sock){    int len;    char rx_buffer[128];     do {        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);        if (len < 0) {            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);        } else if (len == 0) {            ESP_LOGW(TAG, "Connection closed");        } else {            rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string            ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);             // send() can return less bytes than supplied length.            // Walk-around for robust implementation.            int to_write = len;            while (to_write > 0) {                int written = send(sock, rx_buffer + (len - to_write), to_write, 0);                if (written < 0) {                    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);                }                to_write -= written;            }        }    } while (len > 0);} static void tcp_server_task(void *pvParameters){    char addr_str[128];    int addr_family = (int)pvParameters;    int ip_protocol = 0;    struct sockaddr_in6 dest_addr;     if (addr_family == AF_INET) {        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);        dest_addr_ip4->sin_family = AF_INET;        dest_addr_ip4->sin_port = htons(PORT);        ip_protocol = IPPROTO_IP;    } else if (addr_family == AF_INET6) {        bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));        dest_addr.sin6_family = AF_INET6;        dest_addr.sin6_port = htons(PORT);        ip_protocol = IPPROTO_IPV6;    }     int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);    if (listen_sock < 0) {        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);        vTaskDelete(NULL);        return;    }#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)    // Note that by default IPV6 binds to both protocols, it is must be disabled    // if both protocols used at the same time (used in CI)    int opt = 1;    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));#endif     ESP_LOGI(TAG, "Socket created");     int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));    if (err != 0) {        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);        goto CLEAN_UP;    }    ESP_LOGI(TAG, "Socket bound, port %d", PORT);     err = listen(listen_sock, 1);    if (err != 0) {        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);        goto CLEAN_UP;    }     while (1) {         ESP_LOGI(TAG, "Socket listening");         struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6        socklen_t addr_len = sizeof(source_addr);        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);        if (sock < 0) {            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);            break;        }         // Convert ip address to string        if (source_addr.ss_family == PF_INET) {            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);        } else if (source_addr.ss_family == PF_INET6) {            inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);        }        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);         do_retransmit(sock);         shutdown(sock, 0);        close(sock);    } CLEAN_UP:    close(listen_sock);    vTaskDelete(NULL);} void app_main(void){    ESP_ERROR_CHECK(nvs_flash_init());    ESP_ERROR_CHECK(esp_netif_init());    ESP_ERROR_CHECK(esp_event_loop_create_default());     /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.     * Read "Establishing Wi-Fi or Ethernet Connection" section in     * examples/protocols/README.md for more information about this function.     */    ESP_ERROR_CHECK(example_connect()); #ifdef CONFIG_EXAMPLE_IPV4    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);#endif#ifdef CONFIG_EXAMPLE_IPV6    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);#endif} 
TCP Client

--- Code: Script BASIC ---' ScriptBasic TCP Echo Client OPEN "192.168.0.246:3333" FOR SOCKET AS #1PRINT #1, "Hello ESP32-PICO\n"LINE INPUT #1, responsePRINT responseCLOSE(1) 

pi@RPi3B:~/esp/tcp_server $ scriba tcp_client.sb
Hello ESP32-PICO
pi@RPi3B:~/esp/tcp_server $

John Spikowski:
FreeRTOS Real Time Stats Example

FreeRTOS provides the function vTaskGetRunTimeStats() to obtain CPU usage statistics of tasks. However, these statistics are with respect to the entire runtime of FreeRTOS (i.e. run time stats). Furthermore, statistics of vTaskGetRunTimeStats() are only valid whilst the timer for run time statistics has not overflowed.

This example demonstrates how to get CPU usage statistics of tasks with respect to a specified duration (i.e. real time stats) rather than over the entire runtime of FreeRTOS. The print_real_time_stats() function of this example demonstrates how this can be achieved.

Example Breakdown

Spin tasks

During the examples initialization process, multiple spin tasks are created. These tasks will simply spin a certain number of CPU cycles to consume CPU time, then block for a predetermined period.

Understanding the stats

From the log output, it can be seen that the spin tasks consume nearly an equal amount of time over the specified stats collection period of print_real_time_stats(). The real time stats also display the CPU time consumption of other tasks created by default in ESP-IDF (e.g. IDLE and ipc tasks).


--- Code: C ---/* FreeRTOS Real Time Stats Example    This example code is in the Public Domain (or CC0 licensed, at your option.)    Unless required by applicable law or agreed to in writing, this   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR   CONDITIONS OF ANY KIND, either express or implied.*/ #include <stdio.h>#include <stdlib.h>#include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "freertos/semphr.h"#include "esp_err.h" #define NUM_OF_SPIN_TASKS   6#define SPIN_ITER           500000  //Actual CPU cycles used will depend on compiler optimization#define SPIN_TASK_PRIO      2#define STATS_TASK_PRIO     3#define STATS_TICKS         pdMS_TO_TICKS(1000)#define ARRAY_SIZE_OFFSET   5   //Increase this if print_real_time_stats returns ESP_ERR_INVALID_SIZE static char task_names[NUM_OF_SPIN_TASKS][configMAX_TASK_NAME_LEN];static SemaphoreHandle_t sync_spin_task;static SemaphoreHandle_t sync_stats_task; /** * @brief   Function to print the CPU usage of tasks over a given duration. * * This function will measure and print the CPU usage of tasks over a specified * number of ticks (i.e. real time stats). This is implemented by simply calling * uxTaskGetSystemState() twice separated by a delay, then calculating the * differences of task run times before and after the delay. * * @note    If any tasks are added or removed during the delay, the stats of *          those tasks will not be printed. * @note    This function should be called from a high priority task to minimize *          inaccuracies with delays. * @note    When running in dual core mode, each core will correspond to 50% of *          the run time. * * @param   xTicksToWait    Period of stats measurement * * @return *  - ESP_OK                Success *  - ESP_ERR_NO_MEM        Insufficient memory to allocated internal arrays *  - ESP_ERR_INVALID_SIZE  Insufficient array size for uxTaskGetSystemState. Trying increasing ARRAY_SIZE_OFFSET *  - ESP_ERR_INVALID_STATE Delay duration too short */static esp_err_t print_real_time_stats(TickType_t xTicksToWait){    TaskStatus_t *start_array = NULL, *end_array = NULL;    UBaseType_t start_array_size, end_array_size;    uint32_t start_run_time, end_run_time;    esp_err_t ret;     //Allocate array to store current task states    start_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET;    start_array = malloc(sizeof(TaskStatus_t) * start_array_size);    if (start_array == NULL) {        ret = ESP_ERR_NO_MEM;        goto exit;    }    //Get current task states    start_array_size = uxTaskGetSystemState(start_array, start_array_size, &start_run_time);    if (start_array_size == 0) {        ret = ESP_ERR_INVALID_SIZE;        goto exit;    }     vTaskDelay(xTicksToWait);     //Allocate array to store tasks states post delay    end_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET;    end_array = malloc(sizeof(TaskStatus_t) * end_array_size);    if (end_array == NULL) {        ret = ESP_ERR_NO_MEM;        goto exit;    }    //Get post delay task states    end_array_size = uxTaskGetSystemState(end_array, end_array_size, &end_run_time);    if (end_array_size == 0) {        ret = ESP_ERR_INVALID_SIZE;        goto exit;    }     //Calculate total_elapsed_time in units of run time stats clock period.    uint32_t total_elapsed_time = (end_run_time - start_run_time);    if (total_elapsed_time == 0) {        ret = ESP_ERR_INVALID_STATE;        goto exit;    }     printf("| Task | Run Time | Percentage\n");    //Match each task in start_array to those in the end_array    for (int i = 0; i < start_array_size; i++) {        int k = -1;        for (int j = 0; j < end_array_size; j++) {            if (start_array[i].xHandle == end_array[j].xHandle) {                k = j;                //Mark that task have been matched by overwriting their handles                start_array[i].xHandle = NULL;                end_array[j].xHandle = NULL;                break;            }        }        //Check if matching task found        if (k >= 0) {            uint32_t task_elapsed_time = end_array[k].ulRunTimeCounter - start_array[i].ulRunTimeCounter;            uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time * portNUM_PROCESSORS);            printf("| %s | %d | %d%%\n", start_array[i].pcTaskName, task_elapsed_time, percentage_time);        }    }     //Print unmatched tasks    for (int i = 0; i < start_array_size; i++) {        if (start_array[i].xHandle != NULL) {            printf("| %s | Deleted\n", start_array[i].pcTaskName);        }    }    for (int i = 0; i < end_array_size; i++) {        if (end_array[i].xHandle != NULL) {            printf("| %s | Created\n", end_array[i].pcTaskName);        }    }    ret = ESP_OK; exit:    //Common return path    free(start_array);    free(end_array);    return ret;} static void spin_task(void *arg){    xSemaphoreTake(sync_spin_task, portMAX_DELAY);    while (1) {        //Consume CPU cycles        for (int i = 0; i < SPIN_ITER; i++) {            __asm__ __volatile__("NOP");        }        vTaskDelay(pdMS_TO_TICKS(100));    }} static void stats_task(void *arg){    xSemaphoreTake(sync_stats_task, portMAX_DELAY);     //Start all the spin tasks    for (int i = 0; i < NUM_OF_SPIN_TASKS; i++) {        xSemaphoreGive(sync_spin_task);    }     //Print real time stats periodically    while (1) {        printf("\n\nGetting real time stats over %d ticks\n", STATS_TICKS);        if (print_real_time_stats(STATS_TICKS) == ESP_OK) {            printf("Real time stats obtained\n");        } else {            printf("Error getting real time stats\n");        }        vTaskDelay(pdMS_TO_TICKS(1000));    }} void app_main(void){    //Allow other core to finish initialization    vTaskDelay(pdMS_TO_TICKS(100));     //Create semaphores to synchronize    sync_spin_task = xSemaphoreCreateCounting(NUM_OF_SPIN_TASKS, 0);    sync_stats_task = xSemaphoreCreateBinary();     //Create spin tasks    for (int i = 0; i < NUM_OF_SPIN_TASKS; i++) {        snprintf(task_names[i], configMAX_TASK_NAME_LEN, "spin%d", i);        xTaskCreatePinnedToCore(spin_task, task_names[i], 1024, NULL, SPIN_TASK_PRIO, NULL, tskNO_AFFINITY);    }     //Create and start stats task    xTaskCreatePinnedToCore(stats_task, "stats", 4096, NULL, STATS_TASK_PRIO, NULL, tskNO_AFFINITY);    xSemaphoreGive(sync_stats_task);} 

Getting real time stats over 100 ticks
| Task | Run Time | Percentage
| stats | 1468 | 0%
| spin3 | 209100 | 10%
| spin4 | 225361 | 11%
| spin5 | 225360 | 11%
| spin1 | 217281 | 10%
| spin2 | 225387 | 11%
| spin0 | 225371 | 11%
| IDLE | 269997 | 13%
| IDLE | 394701 | 19%
| esp_timer | 0 | 0%
| ipc1 | 0 | 0%
| ipc0 | 0 | 0%
Real time stats obtained


Getting real time stats over 100 ticks
| Task | Run Time | Percentage
| stats | 988 | 0%
| spin3 | 225354 | 11%
| spin1 | 225357 | 11%
| IDLE | 443980 | 22%
| IDLE | 202904 | 10%
| spin2 | 225354 | 11%
| spin5 | 225355 | 11%
| spin0 | 225354 | 11%
| spin4 | 225354 | 11%
| ipc0 | 0 | 0%
| esp_timer | 0 | 0%
| ipc1 | 0 | 0%
Real time stats obtained


Getting real time stats over 100 ticks
| Task | Run Time | Percentage
| stats | 965 | 0%
| spin1 | 225354 | 11%
| IDLE | 202929 | 10%
| IDLE | 443980 | 22%
| spin2 | 225354 | 11%
| spin5 | 225355 | 11%
| spin0 | 225354 | 11%
| spin3 | 225355 | 11%
| spin4 | 225354 | 11%
| ipc1 | 0 | 0%
| ipc0 | 0 | 0%
| esp_timer | 0 | 0%
Real time stats obtained


Getting real time stats over 100 ticks
| Task | Run Time | Percentage
| stats | 965 | 0%
| IDLE | 443980 | 22%
| IDLE | 202929 | 10%
| spin2 | 225354 | 11%
| spin5 | 225355 | 11%
| spin0 | 225354 | 11%
| spin3 | 225355 | 11%
| spin1 | 225354 | 11%
| spin4 | 225354 | 11%
| esp_timer | 0 | 0%
| ipc1 | 0 | 0%
| ipc0 | 0 | 0%
Real time stats obtained

Navigation

[0] Message Index

[*] Previous page

Go to full version