/* ESPNOW Transmitter by: Alexander Bobkov Date Created: June 17, 2025 Updated: June 17, 2025 SDK:abort ESP-IDF v.5.4.1 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. */ /* This program uses ESPNOW for transmitting joystick x- and y- axis values to the receiving device. */ #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/timers.h" #include "esp_adc/adc_oneshot.h" #include "nvs_flash.h" #include "esp_random.h" #include "esp_event.h" #include "esp_netif.h" #include "esp_wifi.h" #include "esp_log.h" #include "esp_err.h" #include "esp_mac.h" #include "esp_now.h" #include "esp_crc.h" #include "espnow_example.h" #include "config.h" const char *TAG = "ESP-NOW_Transmitter"; esp_now_peer_info_t devices; // Struct holding sensors values typedef struct { uint16_t crc; // CRC16 value of ESPNOW data int x_axis; // Joystick x-position int y_axis; // Joystick y-position bool nav_bttn; // Joystick push button uint8_t motor1_rpm_pcm; // PWMs for 4 DC motors uint8_t motor2_rpm_pcm; uint8_t motor3_rpm_pcm; uint8_t motor4_rpm_pcm; } __attribute__((packed)) sensors_data_t; static int x, y; // Joystick x and y positions adc_oneshot_unit_handle_t adc_xy_handle; sensors_data_t buffer; int convert_axis_to_pwm(int axis_value); void get_joystick_xy_axis(int *x_axis, int *y_axis); void sendRawData(void); void deleteDev(void); void deletePeer(void); //static sensors_data_t buffer; esp_err_t joystick_adc_init(void) { adc_oneshot_unit_init_cfg_t adc_init_config_xy = { .unit_id = ADC_UNIT_1, .ulp_mode = ADC_ULP_MODE_DISABLE, }; ESP_ERROR_CHECK(adc_oneshot_new_unit(&adc_init_config_xy, &adc_xy_handle)); adc_oneshot_chan_cfg_t config_x = { .bitwidth = SOC_ADC_DIGI_MAX_BITWIDTH, .atten = ADC_ATTEN_DB_12, }; adc_oneshot_chan_cfg_t config_y = { .bitwidth = SOC_ADC_DIGI_MAX_BITWIDTH, .atten = ADC_ATTEN_DB_12, }; ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_xy_handle, ADC_CHANNEL_0, &config_x)); ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_xy_handle, ADC_CHANNEL_1, &config_y)); return ESP_OK; } void joystick_show_raw_xy() { ESP_ERROR_CHECK(adc_oneshot_read(adc_xy_handle, ADC_CHANNEL_0, &x)); ESP_ERROR_CHECK(adc_oneshot_read(adc_xy_handle, ADC_CHANNEL_1, &y)); ESP_LOGI("(x,y)", "( %d, %d )", x, y); } void get_joystick_xy(int *x_axis, int *y_axis) { ESP_ERROR_CHECK(adc_oneshot_read(adc_xy_handle, ADC_CHANNEL_0, x_axis)); ESP_ERROR_CHECK(adc_oneshot_read(adc_xy_handle, ADC_CHANNEL_1, y_axis)); } void joystick_task(void *arg) { while (true) { joystick_show_raw_xy(); vTaskDelay (1000 / portTICK_PERIOD_MS); } } // Function to delete peer (i.e. when communication error occurs) void deletePeer (void) { uint8_t delStatus = esp_now_del_peer(receiver_mac); if (delStatus != 0) { ESP_LOGE("ESP-NOW", "Could not delete peer"); } } void statusDataSend(const uint8_t *mac_addr, esp_now_send_status_t status) { if (status == ESP_NOW_SEND_SUCCESS) { ESP_LOGI(TAG, "Data sent successfully to: %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); } else { ESP_LOGE(TAG, "Error sending data to: %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); ESP_LOGE("sendData()", "Error sending data. Error code: 0x%04X", status); ESP_LOGE("sendData()", "esp_now_send() failed: %s", esp_err_to_name(status)); ESP_LOGE("sendData()", "Ensure that receiver is powered-on and MAC is correct."); deletePeer(); } } // Function for sending the data to the receiver void sendData (void) { buffer.crc = 0; buffer.x_axis = 240; buffer.y_axis = 256; buffer.nav_bttn = 0; buffer.motor1_rpm_pcm = 0; //10; buffer.motor2_rpm_pcm = 0; buffer.motor3_rpm_pcm = 0; buffer.motor4_rpm_pcm = 0; get_joystick_xy(&x, &y); //ESP_LOGI("(x, y)", "[ %d, %d ]", x, y); buffer.x_axis = x; buffer.y_axis = y; // Display brief summary of data being sent. ESP_LOGI(TAG, "Joystick (x,y) position ( %d, %d )", buffer.x_axis, buffer.y_axis); ESP_LOGI(TAG, "pcm 1, pcm 2 [ 0x%04X, 0x%04X ]", (uint8_t)buffer.motor1_rpm_pcm, (uint8_t)buffer.motor2_rpm_pcm); ESP_LOGI(TAG, "pcm 3, pcm 4 [ 0x%04X, 0x%04X ]", (uint8_t)buffer.motor3_rpm_pcm, (uint8_t)buffer.motor4_rpm_pcm); // Call ESP-NOW function to send data (MAC address of receiver, pointer to the memory holding data & data length) uint8_t result = esp_now_send(receiver_mac, (uint8_t *)&buffer, sizeof(buffer)); // If status is NOT OK, display error message and error code (in hexadecimal). if (result != 0) { ESP_LOGE("sendData()", "Error sending data! Error code: 0x%04X", result); ESP_LOGE("sendData()", "esp_now_send() failed: %s", esp_err_to_name(result)); ESP_LOGE("sendData()", "Ensure that receiver is powered-on."); ESP_LOGE("sendData()", "Ensure that received MAC is: %02X:%02X:%02X:%02X:%02X:%02X", receiver_mac[0], receiver_mac[1], receiver_mac[2], receiver_mac[3], receiver_mac[4], receiver_mac[5]); deletePeer(); } } /* WiFi should start before using ESPNOW */ void wifi_init() { ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); ESP_ERROR_CHECK( esp_wifi_set_mode(ESPNOW_WIFI_MODE)); ESP_ERROR_CHECK( esp_wifi_start()); ESP_ERROR_CHECK( esp_wifi_set_channel(CONFIG_ESPNOW_CHANNEL, WIFI_SECOND_CHAN_NONE)); #if CONFIG_ESPNOW_ENABLE_LONG_RANGE ESP_ERROR_CHECK( esp_wifi_set_protocol(ESPNOW_WIFI_IF, WIFI_PROTOCOL_11B|WIFI_PROTOCOL_11G|WIFI_PROTOCOL_11N|WIFI_PROTOCOL_LR) ); #endif } void rc_send_data_task() { while (true) { if (esp_now_is_peer_exist(receiver_mac)) { sendData(); //sendRawData(); } vTaskDelay (1000 / portTICK_PERIOD_MS); } } void app_main(void) { joystick_adc_init(); // 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 ); wifi_init(); esp_err_t espnow_ret = esp_now_init(); if (espnow_ret != ESP_OK) { //ESP_LOGE(TAG, "Error initializing ESPNOW: %s", espnow_ret); ESP_LOGE(TAG, "esp_now_init() failed: %s", esp_err_to_name(espnow_ret)); return; } ESP_LOGI(TAG, "ESPNOW initialized successfully"); esp_now_register_send_cb(statusDataSend); // Set ESP-NOW receiver device configuration values memcpy(devices.peer_addr, receiver_mac, 6); devices.channel = 1; devices.encrypt = false; esp_now_add_peer(&devices); // Defince a task for periodically sending ESPNOW remote control data xTaskCreate(rc_send_data_task, "RC", 2048, NULL, 4, NULL); }