venerdì 14 novembre 2025

Lettura OTG di dispositivo HID con Esp32S3

 Un programma per leggere i messaggi HID da un volante Logitech con Esp32S3 Otg Host

 

## IDF Component Manager Manifest File
dependencies:
idf: '>=4.4'
usb_host_hid: ^1.0.1
m5stack/m5unified: '*'


 

 

/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "esp_err.h"
#include "esp_log.h"
#include "usb/usb_host.h"
#include "errno.h"
#include "driver/gpio.h"

#include "usb/hid_host.h"
#include "usb/hid_usage_keyboard.h"
#include "usb/hid_usage_mouse.h"
#include "m5_wrapper.h"

/* GPIO Pin number for quit from example logic */
#define APP_QUIT_PIN GPIO_NUM_0

/* Logitech device VID:PID */
#define LOGITECH_VID 0x046D
#define LOGITECH_PID 0xC262

static const char *TAG = "logitech_hid";

QueueHandle_t app_event_queue = NULL;
static bool is_logitech_device = false;
static usb_host_client_handle_t usb_client_hdl = NULL;

/**
* @brief APP event group
*
* Application logic can be different. There is a one among other ways to distinguish the
* event by application event group.
* In this example we have two event groups:
* APP_EVENT - General event, which is APP_QUIT_PIN press event (Generally, it is IO0).
* APP_EVENT_HID_HOST - HID Host Driver event, such as device connection/disconnection or input report.
*/
typedef enum {
APP_EVENT = 0,
APP_EVENT_HID_HOST
} app_event_group_t;

/**
* @brief APP event queue
*
* This event is used for delivering the HID Host event from callback to a task.
*/
typedef struct {
app_event_group_t event_group;
/* HID Host - Device related info */
struct {
hid_host_device_handle_t handle;
hid_host_driver_event_t event;
void *arg;
} hid_host_device;
} app_event_queue_t;

/**
* @brief HID Protocol string names
*/
static const char *hid_proto_name_str[] = {
"NONE",
"KEYBOARD",
"MOUSE"
};

/**
* @brief USB HID Host Generic Interface report callback handler
*
* Prints raw data and checks for 10-byte messages from Logitech device
*
* @param[in] data Pointer to input report data buffer
* @param[in] length Length of input report data buffer
*/
static void hid_host_generic_report_callback(const uint8_t *const data, const int length)
{
// Print raw data
printf("Data [%d bytes]: ", length);
for (int i = 0; i < length; i++) {
printf("%02X ", data[i]);
}
// Check if it's the Logitech device and message is 10 bytes
if (is_logitech_device && length == 10) {
for (int i = 0; i < length; i++) {
int sterzo = data[5]*256 +data[4];
int acceleratore = data[6];
int freno = data[7];
m5_clear();
m5_set_text_size(2);
m5_set_cursor(5,5);
m5_print_int(sterzo);
m5_set_cursor(5,15);
m5_print_int(acceleratore);
m5_set_cursor(5,25);
m5_print_int(freno);
}
printf("logitech");
}
printf("\r\n");
fflush(stdout);
}

/**
* @brief USB HID Host interface callback
*
* @param[in] hid_device_handle HID Device handle
* @param[in] event HID Host interface event
* @param[in] arg Pointer to arguments, does not used
*/
void hid_host_interface_callback(hid_host_device_handle_t hid_device_handle,
const hid_host_interface_event_t event,
void *arg)
{
uint8_t data[64] = { 0 };
size_t data_length = 0;
hid_host_dev_params_t dev_params;
ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params));

switch (event) {
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
ESP_ERROR_CHECK(hid_host_device_get_raw_input_report_data(hid_device_handle,
data,
64,
&data_length));

hid_host_generic_report_callback(data, data_length);

break;
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HID Device, protocol '%s' DISCONNECTED",
hid_proto_name_str[dev_params.proto]);
ESP_ERROR_CHECK(hid_host_device_close(hid_device_handle));
is_logitech_device = false;
break;
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
ESP_LOGI(TAG, "HID Device, protocol '%s' TRANSFER_ERROR",
hid_proto_name_str[dev_params.proto]);
break;
default:
ESP_LOGE(TAG, "HID Device, protocol '%s' Unhandled event",
hid_proto_name_str[dev_params.proto]);
break;
}
}

/**
* @brief USB Host client event callback
* Used to get VID/PID during enumeration
*/
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
{
usb_host_client_handle_t client_hdl = (usb_host_client_handle_t)arg;
if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV) {
usb_device_handle_t dev_hdl;
ESP_ERROR_CHECK(usb_host_device_open(client_hdl, event_msg->new_dev.address, &dev_hdl));
const usb_device_desc_t *dev_desc;
ESP_ERROR_CHECK(usb_host_get_device_descriptor(dev_hdl, &dev_desc));
uint16_t vid = dev_desc->idVendor;
uint16_t pid = dev_desc->idProduct;
ESP_LOGI(TAG, "USB Device detected - VID:PID %04X:%04X", vid, pid);
if (vid == LOGITECH_VID && pid == LOGITECH_PID) {
is_logitech_device = true;
ESP_LOGI(TAG, "Logitech device 046D:C262 detected!");
}
usb_host_device_close(client_hdl, dev_hdl);
}
}

/**
* @brief USB HID Host Device event
*
* @param[in] hid_device_handle HID Device handle
* @param[in] event HID Host Device event
* @param[in] arg Pointer to arguments, does not used
*/
void hid_host_device_event(hid_host_device_handle_t hid_device_handle,
const hid_host_driver_event_t event,
void *arg)
{
hid_host_dev_params_t dev_params;
ESP_ERROR_CHECK(hid_host_device_get_params(hid_device_handle, &dev_params));

switch (event) {
case HID_HOST_DRIVER_EVENT_CONNECTED:
ESP_LOGI(TAG, "HID Device CONNECTED - Protocol '%s'",
hid_proto_name_str[dev_params.proto]);

const hid_host_device_config_t dev_config = {
.callback = hid_host_interface_callback,
.callback_arg = NULL
};

ESP_ERROR_CHECK(hid_host_device_open(hid_device_handle, &dev_config));
ESP_ERROR_CHECK(hid_host_device_start(hid_device_handle));
break;
default:
break;
}
}

/**
* @brief USB Host client task
*/
static void usb_host_client_task(void *arg)
{
usb_host_client_handle_t client_hdl;
usb_host_client_config_t client_config = {
.is_synchronous = false,
.max_num_event_msg = 5,
.async = {
.client_event_callback = client_event_cb,
.callback_arg = NULL // Will be set after registration
}
};
ESP_ERROR_CHECK(usb_host_client_register(&client_config, &client_hdl));
// Update callback arg with client handle
client_config.async.callback_arg = (void *)client_hdl;
while (1) {
usb_host_client_handle_events(client_hdl, portMAX_DELAY);
}
}

/**
* @brief Start USB Host install and handle common USB host library events while app pin not low
*
* @param[in] arg Not used
*/
static void usb_lib_task(void *arg)
{
const usb_host_config_t host_config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1,
};

ESP_ERROR_CHECK(usb_host_install(&host_config));
xTaskNotifyGive(arg);

while (true) {
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
// In this example, there is only one client registered
// So, once we deregister the client, this call must succeed with ESP_OK
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
ESP_ERROR_CHECK(usb_host_device_free_all());
break;
}
}

ESP_LOGI(TAG, "USB shutdown");
// Clean up USB Host
vTaskDelay(10); // Short delay to allow clients clean-up
ESP_ERROR_CHECK(usb_host_uninstall());
vTaskDelete(NULL);
}

/**
* @brief BOOT button pressed callback
*
* Signal application to exit the HID Host task
*
* @param[in] arg Unused
*/
static void gpio_isr_cb(void *arg)
{
BaseType_t xTaskWoken = pdFALSE;
const app_event_queue_t evt_queue = {
.event_group = APP_EVENT,
};

if (app_event_queue) {
xQueueSendFromISR(app_event_queue, &evt_queue, &xTaskWoken);
}

if (xTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}

/**
* @brief HID Host Device callback
*
* Puts new HID Device event to the queue
*
* @param[in] hid_device_handle HID Device handle
* @param[in] event HID Device event
* @param[in] arg Not used
*/
void hid_host_device_callback(hid_host_device_handle_t hid_device_handle,
const hid_host_driver_event_t event,
void *arg)
{
const app_event_queue_t evt_queue = {
.event_group = APP_EVENT_HID_HOST,
// HID Host Device related info
.hid_host_device.handle = hid_device_handle,
.hid_host_device.event = event,
.hid_host_device.arg = arg
};

if (app_event_queue) {
xQueueSend(app_event_queue, &evt_queue, 0);
}
}

void app_main(void)
{
BaseType_t task_created;
app_event_queue_t evt_queue;
ESP_LOGI(TAG, "HID Host for Logitech Device 046D:C262");
m5_init();

// Init BOOT button: Pressing the button simulates app request to exit
// It will disconnect the USB device and uninstall the HID driver and USB Host Lib
const gpio_config_t input_pin = {
.pin_bit_mask = BIT64(APP_QUIT_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE,
};
ESP_ERROR_CHECK(gpio_config(&input_pin));
ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1));
ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_isr_cb, NULL));

/*
* Create usb_lib_task to:
* - initialize USB Host library
* - Handle USB Host events while APP pin in in HIGH state
*/
task_created = xTaskCreatePinnedToCore(usb_lib_task,
"usb_events",
4096,
xTaskGetCurrentTaskHandle(),
2, NULL, 0);
assert(task_created == pdTRUE);

// Wait for notification from usb_lib_task to proceed
ulTaskNotifyTake(false, 1000);

// Create USB host client task to detect VID/PID
task_created = xTaskCreate(usb_host_client_task,
"usb_client",
4096,
NULL,
3, NULL);
assert(task_created == pdTRUE);

/*
* HID host driver configuration
* - create background task for handling low level event inside the HID driver
* - provide the device callback to get new HID Device connection event
*/
const hid_host_driver_config_t hid_host_driver_config = {
.create_background_task = true,
.task_priority = 5,
.stack_size = 4096,
.core_id = 0,
.callback = hid_host_device_callback,
.callback_arg = NULL
};

ESP_ERROR_CHECK(hid_host_install(&hid_host_driver_config));

// Create queue
app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t));

ESP_LOGI(TAG, "Waiting for HID Device to be connected");

while (1) {
// Wait queue
if (xQueueReceive(app_event_queue, &evt_queue, portMAX_DELAY)) {
if (APP_EVENT == evt_queue.event_group) {
// User pressed button
usb_host_lib_info_t lib_info;
ESP_ERROR_CHECK(usb_host_lib_info(&lib_info));
if (lib_info.num_devices == 0) {
// End while cycle
break;
} else {
ESP_LOGW(TAG, "To shutdown example, remove all USB devices and press button again.");
// Keep polling
}
}

if (APP_EVENT_HID_HOST == evt_queue.event_group) {
hid_host_device_event(evt_queue.hid_host_device.handle,
evt_queue.hid_host_device.event,
evt_queue.hid_host_device.arg);
}
}
}

ESP_LOGI(TAG, "HID Driver uninstall");
ESP_ERROR_CHECK(hid_host_uninstall());
gpio_isr_handler_remove(APP_QUIT_PIN);
xQueueReset(app_event_queue);
vQueueDelete(app_event_queue);
}

 

 

 

 

SmartConfig Esp32

SmartConfig e' un metodo per configurare il WiFi di una Esp32 utilizzando una app su smartphone scaricabile dagli store o personalizzabile tramite sorgenti  (EspTouch, Github

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_smartconfig.html 

Esistono due versioni. In V1 vengono configurati inviando le credenziali di rete a tutti i dispositivi in ascolto 

V1
In V2 si puo' usare il Custom Data per inviare un codice riconosciuto da un solo dispositivo
 
V2

Dopo la configurazione i dati vengono salvati nella memoria NVS cosi' che al prossimo riavvio l'Esp32 riesca a connettersi in automatico al WiFi. Per cancellare la NVS si usa

idf.py erase-flash
 

 // Change from:
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );

// To:
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH_V2) );

menu "SmartConfig Example Configuration"

config LED_GPIO
int "LED GPIO Pin"
default 2
help
GPIO pin for LED status indicator.
Default is GPIO 2 for most ESP32 boards.
ESP32-C3: Try GPIO 8
ESP32-S2/S3: Try GPIO 18 or 48

endmenu


 

 

/*
* ESP32 SmartConfig Example - ESP-IDF
*
* File: main/smartconfig_main.c
*/

#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"
#include "driver/gpio.h"

/* LED GPIO */
#define LED_GPIO CONFIG_LED_GPIO
#define LED_GPIO_DEFAULT 2

/* FreeRTOS event group to signal when we are connected/disconnected */
static EventGroupHandle_t s_wifi_event_group;

/* Event bits */
#define CONNECTED_BIT BIT0
#define ESPTOUCH_DONE_BIT BIT1

static const char *TAG = "smartconfig";

static void smartconfig_task(void * parm);

/* Event handler for WiFi and SmartConfig events */
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "WiFi station started");
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Disconnected from AP");
esp_wifi_connect();
xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
ESP_LOGI(TAG, "Scan done");
} else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
ESP_LOGI(TAG, "Found channel");
} else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
ESP_LOGI(TAG, "Got SSID and password");

smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
wifi_config_t wifi_config;
uint8_t ssid[33] = { 0 };
uint8_t password[65] = { 0 };
uint8_t rvd_data[33] = { 0 };

bzero(&wifi_config, sizeof(wifi_config_t));
memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
wifi_config.sta.bssid_set = evt->bssid_set;
if (wifi_config.sta.bssid_set == true) {
memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
}

memcpy(ssid, evt->ssid, sizeof(evt->ssid));
memcpy(password, evt->password, sizeof(evt->password));
ESP_LOGI(TAG, "SSID:%s", ssid);
ESP_LOGI(TAG, "PASSWORD:%s", password);
if (evt->type == SC_TYPE_ESPTOUCH_V2) {
ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
ESP_LOGI(TAG, "RVD_DATA:");
for (int i=0; i<33; i++) {
printf("%02x ", rvd_data[i]);
}
printf("\n");
}

ESP_ERROR_CHECK( esp_wifi_disconnect() );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
esp_wifi_connect();
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
}
}

/* Initialize WiFi in station mode */
static void initialise_wifi(void)
{
ESP_ERROR_CHECK(esp_netif_init());
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );

ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );

ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_start() );
}

/* SmartConfig task */
static void smartconfig_task(void * parm)
{
EventBits_t uxBits;
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
ESP_LOGI(TAG, "SmartConfig started");
ESP_LOGI(TAG, "Use ESPTouch app to configure WiFi");
while (1) {
uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
if(uxBits & CONNECTED_BIT) {
ESP_LOGI(TAG, "WiFi Connected to AP");
}
if(uxBits & ESPTOUCH_DONE_BIT) {
ESP_LOGI(TAG, "SmartConfig over");
esp_smartconfig_stop();
vTaskDelete(NULL);
}
}
}

/* LED blink task */
static void led_task(void *pvParameters)
{
gpio_reset_pin(LED_GPIO);
gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);
while(1) {
EventBits_t bits = xEventGroupGetBits(s_wifi_event_group);
if (bits & CONNECTED_BIT) {
/* Slow blink when connected */
gpio_set_level(LED_GPIO, 1);
vTaskDelay(100 / portTICK_PERIOD_MS);
gpio_set_level(LED_GPIO, 0);
vTaskDelay(900 / portTICK_PERIOD_MS);
} else {
/* Fast blink during configuration */
gpio_set_level(LED_GPIO, 1);
vTaskDelay(200 / portTICK_PERIOD_MS);
gpio_set_level(LED_GPIO, 0);
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
}

void app_main(void)
{
ESP_LOGI(TAG, "=== ESP32 SmartConfig Example ===");
/* Initialize NVS */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);

/* Initialize WiFi */
initialise_wifi();
/* Try to connect with saved credentials */
ESP_LOGI(TAG, "Attempting connection with saved credentials...");
esp_wifi_connect();
/* Wait 10 seconds for connection */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
CONNECTED_BIT,
pdFALSE,
pdFALSE,
10000 / portTICK_PERIOD_MS);
if (bits & CONNECTED_BIT) {
ESP_LOGI(TAG, "Connected with saved credentials!");
} else {
ESP_LOGI(TAG, "No saved credentials or connection failed");
ESP_LOGI(TAG, "Starting SmartConfig...");
xTaskCreate(smartconfig_task, "smartconfig_task", 4096, NULL, 3, NULL);
}
/* Start LED task */
xTaskCreate(led_task, "led_task", 2048, NULL, 2, NULL);
}


 

 

 

 

 

giovedì 13 novembre 2025

test ESPNow con IDF

Ho provato Espnow, un protocollo per inviare brevi messaggi tra Esp32 e simili senza l'appoggio di un accesspoint 

 ho usato Idf 5.3.1 ed il programma di esempio

idf.py create-project-from-example "espressif/esp-now:get-started"

idf.py menuconfig

Example configuration 

CONFIG_ESPNOW_CHANNEL → e.g. 1

 CONFIG_ESPNOW_SEND_LEN → e.g. 200

 CONFIG_ESPNOW_SEND_COUNT → e.g. 100

 CONFIG_ESPNOW_SEND_DELAY → e.g. 1000 (ms)

se non si vuole un progetto di esempio si aggiunge il componente
idf.py add-dependency "espressif/esp-now" 

 l'esempio si puo' usare in assenza di wifi e si carica lo stesso firmware di esempio su entrambi i dispositivi. A seconda di send_param->magic=esp_random() viene scelto quale tra i due dispositivi e' il sender e quale e' il receiver 

se si vuole forzare il ruolo si 

#define FORCE_SENDER 1 // 1 = always sender, 0 = always receiver 

sender.c (a seconda delle impostazione di peer mac la comunicazione puo' essere broadcast, specificando come Mac Address FF:FF:FF:FF:FF:FF od indirizzata specificatamente ad un dispositivo)

==================================== 

#include "esp_wifi.h"
#include "esp_now.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "string.h"

static const char *TAG = "ESPNOW_SENDER";

// 👉 Replace this with your receiver’s MAC address (check via `esp_read_mac`)
uint8_t peer_mac[] = {0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC};

static void send_cb(const uint8_t *mac_addr, esp_now_send_status_t status) {
    ESP_LOGI(TAG, "Send to %02X:%02X:%02X:%02X:%02X:%02X  status: %s",
             mac_addr[0], mac_addr[1], mac_addr[2],
             mac_addr[3], mac_addr[4], mac_addr[5],
             status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL");
}

void app_main(void) {
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(esp_wifi_init(&(wifi_init_config_t)WIFI_INIT_CONFIG_DEFAULT()));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_ERROR_CHECK(esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE));

    ESP_ERROR_CHECK(esp_now_init());
    ESP_ERROR_CHECK(esp_now_register_send_cb(send_cb));

    esp_now_peer_info_t peer = {0};
    memcpy(peer.peer_addr, peer_mac, 6);
    peer.channel = 1;
    peer.encrypt = false;
    ESP_ERROR_CHECK(esp_now_add_peer(&peer));

    const char *msg = "Hello ESPNOW";
    while (1) {
        esp_err_t res = esp_now_send(peer_mac, (uint8_t *)msg, strlen(msg));
        if (res == ESP_OK) ESP_LOGI(TAG, "Message sent: %s", msg);
        else ESP_LOGE(TAG, "Send error: %s", esp_err_to_name(res));
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

================================ 

receiver.c

============================= 

#include "esp_wifi.h"
#include "esp_now.h"
#include "esp_log.h"
#include "nvs_flash.h"

static const char *TAG = "ESPNOW_RECEIVER";

static void recv_cb(const esp_now_recv_info_t *info, const uint8_t *data, int len) {
    ESP_LOGI(TAG, "From %02X:%02X:%02X:%02X:%02X:%02X | Len: %d | Data: %.*s",
             info->src_addr[0], info->src_addr[1], info->src_addr[2],
             info->src_addr[3], info->src_addr[4], info->src_addr[5],
             len, len, data);
}

void app_main(void) {
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(esp_wifi_init(&(wifi_init_config_t)WIFI_INIT_CONFIG_DEFAULT()));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_ERROR_CHECK(esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE));

    ESP_ERROR_CHECK(esp_now_init());
    ESP_ERROR_CHECK(esp_now_register_recv_cb(recv_cb));

    ESP_LOGI(TAG, "Receiver ready");
}

================================= 

se si vuole mandare variabili di tipo differente nel pacchetto queste devono essere incluse in una struttura. La lunghezza dei pacchetti e' 250 bytes

================================= 

#include "esp_wifi.h"
#include "esp_now.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include <string.h>

static const char *TAG = "ESPNOW_SENDER";

uint8_t peer_mac[] = {0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC};

// Define your custom data structure
typedef struct {
    uint32_t id;
    float temperature;
    float humidity;
    int status;
    char label[16];
} sensor_packet_t;

static void send_cb(const uint8_t *mac_addr, esp_now_send_status_t status) {
    ESP_LOGI(TAG, "Send to %02X:%02X:%02X:%02X:%02X:%02X  status: %s",
             mac_addr[0], mac_addr[1], mac_addr[2],
             mac_addr[3], mac_addr[4], mac_addr[5],
             status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL");
}

void app_main(void) {
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(esp_wifi_init(&(wifi_init_config_t)WIFI_INIT_CONFIG_DEFAULT()));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_ERROR_CHECK(esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE));

    ESP_ERROR_CHECK(esp_now_init());
    ESP_ERROR_CHECK(esp_now_register_send_cb(send_cb));

    esp_now_peer_info_t peer = {0};
    memcpy(peer.peer_addr, peer_mac, 6);
    peer.channel = 1;
    peer.encrypt = false;
    ESP_ERROR_CHECK(esp_now_add_peer(&peer));

    sensor_packet_t pkt = {0};
    pkt.id = 1;

    while (1) {
        pkt.temperature = 22.5 + (esp_random() % 100) / 10.0;
        pkt.humidity = 60.0 + (esp_random() % 100) / 10.0;
        pkt.status = esp_random() % 2;
        strcpy(pkt.label, "Node_A");

        esp_err_t res = esp_now_send(peer_mac, (uint8_t *)&pkt, sizeof(pkt));
        if (res == ESP_OK)
            ESP_LOGI(TAG, "Sent packet: id=%lu T=%.1f H=%.1f status=%d",
                     pkt.id, pkt.temperature, pkt.humidity, pkt.status);
        else
            ESP_LOGE(TAG, "Send error: %s", esp_err_to_name(res));

        pkt.id++;
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

==================== 

#include "esp_wifi.h"
#include "esp_now.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include <string.h>

static const char *TAG = "ESPNOW_RECEIVER";

typedef struct {
    uint32_t id;
    float temperature;
    float humidity;
    int status;
    char label[16];
} sensor_packet_t;

static void recv_cb(const esp_now_recv_info_t *info, const uint8_t *data, int len) {
    if (len != sizeof(sensor_packet_t)) {
        ESP_LOGW(TAG, "Invalid packet size: %d", len);
        return;
    }

    sensor_packet_t pkt;
    memcpy(&pkt, data, sizeof(pkt));

    ESP_LOGI(TAG, "From %02X:%02X:%02X:%02X:%02X:%02X | id=%lu | T=%.1f | H=%.1f | status=%d | label=%s",
             info->src_addr[0], info->src_addr[1], info->src_addr[2],
             info->src_addr[3], info->src_addr[4], info->src_addr[5],
             pkt.id, pkt.temperature, pkt.humidity, pkt.status, pkt.label);
}

void app_main(void) {
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(esp_wifi_init(&(wifi_init_config_t)WIFI_INIT_CONFIG_DEFAULT()));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_ERROR_CHECK(esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE));

    ESP_ERROR_CHECK(esp_now_init());
    ESP_ERROR_CHECK(esp_now_register_recv_cb(recv_cb));

    ESP_LOGI(TAG, "Receiver ready");
}
 

 

 

mercoledì 12 novembre 2025

REPL in emulatore QEMU Esp32

Nell'SDK IDF ' contenuto un emulatore ESP32 QEmu

Non si possono simulare sensori e protocolli di trasmissione ma per cose semplici come REPL sulla seriale funziona piu' che bene

Non sono riuscito a fare partire l'emulatore con la finestra grafica 

 

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define MAX_LINE_LEN 128

void repl_task(void *arg) {
char line[MAX_LINE_LEN];
int pos = 0;
int c;
int echo = 1; // set to 0 to disable echo

printf("REPL started. Type commands:\n> ");
fflush(stdout);

while (true) {
c = fgetc(stdin); // blocking read from UART

if (c == EOF) {
vTaskDelay(10 / portTICK_PERIOD_MS);
continue;
}

// Handle carriage return or newline
if (c == '\r' || c == '\n') {
if (echo) printf("\r\n");
line[pos] = '\0';

if (pos > 0) {
if (strcmp(line, "hello") == 0)
printf("Hi there!\n");
else if (strcmp(line, "echo off") == 0)
echo = 0, printf("Echo disabled.\n");
else if (strcmp(line, "echo on") == 0)
echo = 1, printf("Echo enabled.\n");
else if (strcmp(line, "exit") == 0)
printf("Goodbye!\n");
else
printf("Unknown command: %s\n", line);
}

printf("> ");
fflush(stdout);
pos = 0; // reset buffer
}
// Handle backspace or delete
else if ((c == 8 || c == 127) && pos > 0) {
pos--;
if (echo) {
printf("\b \b"); // erase last char on terminal
fflush(stdout);
}
}
// Normal printable characters
else if (pos < MAX_LINE_LEN - 1 && c >= 32 && c <= 126) {
line[pos++] = (char)c;
if (echo) {
fputc(c, stdout);
fflush(stdout);
}
}
}
}

void app_main() {
xTaskCreate(repl_task, "repl_task", 4096, NULL, 5, NULL);
}


 

Stepper NEMA17 e driver A4988

Mi sono comprato una scheda per pilotare un NEMA 17 dalla Cina ed e' arrivata senza istruzioni. Per fortuna dopo un po' di ricerca ho trovato lo schema di connessione che peraltro abbastanza semplice

I motori stepper si dividono in closed loop e open loop: nel primo caso nel motore c'e' un encoder ottico che verifica il corretto posizionamento del motore anche in presenza di carichi che limitino il motore non perdendo passi 

Il modulo A4988 non arriva montato ...deve essere inserito facendo coincidere il pin VIN (ultimo in basso fila di sinistra del connettore rosso centrale) 

 

 

https://docs.google.com/document/d/e/2PACX-1vTyiIMFXQ1cY5TMhOh_0EjdznmC3Nw6CplW7Xx8F61ogOaUpHVyuaXsqrWhIiuE8SjpbON_VzB-rk3R/pub

Il Nema17 ha 200 steps per 360 gradi (quindi ogni step vale 1.8 gradi).  Agendo sui microswitch si puo' impostare 1/2,1/4,1/8,1/16 dello step di base ...si ha quindi una rotazione minima di circa 0.11 gradi (su questa scheda non sembra che si possa impostare tale valore da software, su altre schede MS1,MS2 ed MS3 possono essere collegati ai pin digitali)

 


 Per fare una rotazione di 90 gradi usando lo step base si devono inviare 50 impulsi

 

Nello sketch che segue viene eseguita anche una accelerazione e decelerazione in partenza ed arrivo (valore di ramp), usando il delayMin viene gestita la velocita' (in pratica l'intervallo tra gli impulsi) 

/*
 * Stepper motor control with A4988 driver
 * Includes acceleration/deceleration ramp
 * Speed controlled by potentiometer on A0
 */

#define enablePin  6  // Enable (LOW = ON)
#define dirPin     4  // Direction
#define stepPin    5  // Step
#define potPin     A0 // Potentiometer input

const int stepsPerRevolution = 200; // 1.8° per step motor

// Motion control variables
int delayMin;       // Minimum delay between steps (fastest speed)
int delayMax = 2000; // Maximum delay (slowest speed)
int rampSteps = 50;  // Number of steps to accelerate/decelerate

void setup() {
  pinMode(enablePin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(potPin, INPUT);

  digitalWrite(enablePin, LOW); // Enable driver
  digitalWrite(dirPin, HIGH);   // Initial direction
}

void loop() {
  // Read potentiometer to set minimum delay (speed)
  int potValue = analogRead(potPin);
  delayMin = map(potValue, 0, 1023, 2000, 200); // slower -> faster

  // Rotate forward
  digitalWrite(dirPin, HIGH);
  moveStepper(stepsPerRevolution, delayMin);

  delay(500); // pause

  // Rotate backward
  digitalWrite(dirPin, LOW);
  moveStepper(stepsPerRevolution, delayMin);

  delay(500);
}

//--------------------------------------------
// Function to move stepper with accel/decel
//--------------------------------------------
void moveStepper(int steps, int delayMin) {
  for (int i = 0; i < steps; i++) {
    int currentDelay;

    // Acceleration phase
    if (i < rampSteps)
      currentDelay = map(i, 0, rampSteps, delayMax, delayMin);
    // Deceleration phase
    else if (i > steps - rampSteps)
      currentDelay = map(i, steps - rampSteps, steps, delayMin, delayMax);
    // Constant speed
    else
      currentDelay = delayMin;

    // Step pulse
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(currentDelay);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(currentDelay);
  }
}

 

lunedì 10 novembre 2025

USB OTG in ESP32-S3

Attenzione: dopo aver flashato un programma che usa OTG si perde la seriale USB, il dispositivo deve essere quindi resettato prima di poter essere riprogrammato

 

Da notare che in StampS3 il tasto e' nascosto sotto l'adesivo

------------------------------------------------------------------  

Ho provato ad usare la porta USB in modalita' OTG in ESP32-S3

Con il modulo Esp32-S3-Wroom esistono due porte USB di cui una di programmazione (a sinistra) ed una OTG (a destra)

 


Provando gli esempi in examples/peripherals/usb/device/hid tutto funziona, la scheda  viene riconosciuta come un device. Il problema e'  che usando gli esempi examples/peripherals/usb/host/hid il dispositivo viene flashato in modo correto ma non funziona

Dopo averle provate tutto ho trovato che la seconda porta USB non porta corrente ma solo dati ...in pratica in modalita' host il dispositivo nemmeno di accende/ Ho provato una alimentazione esterna sul pin 5V e GND ma niente 

Usando pero' dispositivi che hanno una batteria interna (tipo Cardputer basato su Esp32S3) il programma funziona...non riesco a trovare lo schema elettrica del Wroom a questo punto credo che non ci sia proprio connessione elettrica di alimentazione sull'USB Otg ma solo D+ e D- 


Anche la Esp32S3 Feather alimentata via LiPo non alimenta in modo efficace la USb (i 3.7 V della Lipo non passano attraverso un booster)

 per fornire 5V alla porta USB (e per alimentare nello stesso tempo la scheda) si stacca la LiPo si alimenta dall'esterno   sul pin USB +5V e sul pin GND


 

 In altri schede si deve collegare due piazzole come nella scheda sottostante per avere alimentazione su OTG



 

Analisi MNF su spettri di riflettanza di plastica

Devo cerca di lavorare su spettri di riflettanza di plastica e la prima domanda e': quale sono le bande significative? Sono partito dal ...