Skip to content

Commit

Permalink
One manually added UDP connection can now be saved to NVM
Browse files Browse the repository at this point in the history
Updated links to Wiki and build version to be ready for v2.0RC3
  • Loading branch information
seeul8er committed Aug 31, 2024
1 parent 5bfc8f2 commit 03e32ed
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 46 deletions.
30 changes: 4 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,22 @@ These boards are very low in price, have everything you need and are also very s
<img src="https://github.com/DroneBridge/ESP32/assets/24637325/e3b2975d-7de4-41af-b052-e4fa024d905e" alt="Official Boadrd DroneBridge for ESP32" width="350">
* **Official board for easy use as ground station coming soon!**

[For further info please check the wiki!](https://dronebridge.gitbook.io/docs/dronebridge-for-esp32/untitled#hardware)
[For further info please check the wiki!](https://dronebridge.gitbook.io/docs/dronebridge-for-esp32/hardware-and-wiring)

## Installation/Flashing using precompiled binaries

[It is recommended that you use the official online flashing tool!](https://dronebridge.github.io/ESP32/install.html)

In any other case there are multiple ways how to flash the firmware.
**[For further info please check the wiki!](https://dronebridge.gitbook.io/docs/dronebridge-for-esp32/untitled#installation-flashing)**
**[For further info please check the wiki!](https://dronebridge.gitbook.io/docs/dronebridge-for-esp32/installation)**

## Wiring

1. Connect the UART of the ESP32 to a 3.3V UART of your flight controller. It is not recommended to use the ESP32s pins that are marked with TX & RX since they often are connected to the internal serial ouput. Go for any other pin instead!
2. Set the flight controller port to the desired protocol.

**Check out the manufacturer datasheet! Only some modules can take more than 3.3V. Follow the recommendations by the ESP32 board manufacturer for powering the device**
**[For further info please check the wiki!](https://dronebridge.gitbook.io/docs/dronebridge-for-esp32/untitled#hardware)**
**[For further info please check the wiki!](https://dronebridge.gitbook.io/docs/dronebridge-for-esp32/hardware-and-wiring)**

## Configuration
1. Connect to the WiFi `DroneBridge ESP32` with password `dronebridge`
Expand All @@ -84,7 +84,7 @@ In any other case there are multiple ways how to flash the firmware.

![DroneBridge for ESP32 web interface](wiki/dbesp32_webinterface.png)

**[For further info please check the wiki!](https://github.com/DroneBridge/ESP32/wiki/Configuration)**
**[For further info please check the wiki!](https://dronebridge.gitbook.io/docs/dronebridge-for-esp32/configuration)**

## Use with QGroundControl, Mission Planner or any other GCS

Expand All @@ -108,28 +108,6 @@ For questions or general chatting regarding DroneBridge for ESP32 please visit t
</a>
</div>

## Developers

### Compile
You will need the Espressif SDK: esp-idf + toolchain. Check out their website for more info and on how to set it up.
The code is written in pure C using the esp-idf (no Arduino libs).
Compile using esp-idf v5.1 or esp-idf v5.2
- ESP32 `idf.py set-target esp32 build`
- ESP32S2 `idf.py set-target esp32s2 build`
- ESP32S3 `idf.py set-target esp32s3 build`
- ESP32C3 `idf.py set-target esp32c3 build`
- ESP32C3 `idf.py set-target esp32c6 build`

Or compile all at once with release configuration running the release scripts for bash or powershell `create_release_zip.sh` or `create_release_zip.ps1`

**This project supports the v5.3 of ESP-IDF**
Compile and flash by running: `idf.py build`, `idf.py flash`

The web interface is built using the command `idf.py frontend`. This is done automatically when compiling the entire project using `idf.py build`.
The frontend is built to `build/frontend`.
Alternatively, the frontend can be built using `npm install && npm i -D shx && npm run build` within `/frontend/`, then manually copy the content of `/frontend/build` to `/build/frontend`


[contributors-shield]: https://img.shields.io/github/contributors/DroneBridge/ESP32.svg?style=for-the-badge
[contributors-url]: https://github.com/DroneBridge/ESP32/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/DroneBridge/ESP32.svg?style=for-the-badge
Expand Down
11 changes: 7 additions & 4 deletions frontend/dronebridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ async function send_json(api_path, json_data = undefined) {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
"charset": 'UTF-8'
"charset": 'utf-8'
},
body: json_data
});
Expand Down Expand Up @@ -262,14 +262,17 @@ function get_settings() {
}

function add_new_udp_client() {
var ip = prompt("Please enter the IP address of the UDP receiver", "192.168.2.X");
var port = parseInt(prompt("Please enter the port number of the UDP receiver", "14550"));
let ip = prompt("Please enter the IP address of the UDP receiver", "192.168.2.X");
let port = prompt("Please enter the port number of the UDP receiver", "14550");
port = parseInt(port);
let save_to_nvm = confirm("Save this UDP client to the permanent storage so it will be auto added after reboot/reset?\nYou can only save one UDP client to the memory. The old ones will be overwritten.\nSelect no if you only want to add this client for this session.");
const ippattern = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;

if (ip != null && port != null && ippattern.test(ip)) {
let myjson = {
ip: ip,
port: port
port: port,
save: save_to_nvm
};
send_json("api/settings/clients/udp", JSON.stringify(myjson)).then(send_response => {
console.log(send_response);
Expand Down
13 changes: 10 additions & 3 deletions main/db_esp32_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,12 @@ void udp_client_list_destroy(udp_conn_list_t *n_udp_conn_list) {
*
* @param n_udp_conn_list Structure containing all UDP connection information
* @param new_db_udp_client New client to add to the UDP list. PORT, MAC & IP must be set. If MAC is not set then the
* device cannot be removed later on.
* device cannot be automatically removed later on. To remove it, the user must clear the entire list.
* @param save_to_nvm Set to 1 (true) in case you want the UDP client to survive the reboot. Set to 0 (false) if client is temporary for this session.
* It will then be saved to NVM and added to the udp_conn_list_t on startup. Only one client can be saved to NVM.
* @return 1 if added - 0 if not
*/
bool add_to_known_udp_clients(udp_conn_list_t *n_udp_conn_list, struct db_udp_client_t new_db_udp_client) {
bool add_to_known_udp_clients(udp_conn_list_t *n_udp_conn_list, struct db_udp_client_t new_db_udp_client, bool save_to_nvm) {
if (n_udp_conn_list == NULL) { // Check if the list is NULL
return false; // Do nothing
}
Expand All @@ -311,6 +313,11 @@ bool add_to_known_udp_clients(udp_conn_list_t *n_udp_conn_list, struct db_udp_cl
}
n_udp_conn_list->db_udp_clients[n_udp_conn_list->size] = new_db_udp_client; // Copy the element data to the end of the array
n_udp_conn_list->size++; // Increment the size of the list
if (save_to_nvm) {
save_udp_client_to_nvm(&new_db_udp_client, false);
} else {
// do not save to NVM
}
return true;
}

Expand Down Expand Up @@ -624,7 +631,7 @@ _Noreturn void control_module_udp_tcp() {
// Devices/Ports added this way cannot be removed in sta-mode since UDP is connectionless, and we cannot
// determine if the client is still existing. This will blow up the list connected devices.
// In AP-Mode the devices can be removed based on the IP/MAC address
add_to_known_udp_clients(udp_conn_list, new_db_udp_client);
add_to_known_udp_clients(udp_conn_list, new_db_udp_client, false);
} else {
// received nothing, keep on going
}
Expand Down
2 changes: 1 addition & 1 deletion main/db_esp32_control.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ typedef struct {
void control_module();
udp_conn_list_t *udp_client_list_create();
void udp_client_list_destroy(udp_conn_list_t *n_udp_conn_list);
bool add_to_known_udp_clients(udp_conn_list_t *n_udp_conn_list, struct db_udp_client_t new_db_udp_client);
bool add_to_known_udp_clients(udp_conn_list_t *n_udp_conn_list, struct db_udp_client_t new_db_udp_client, bool save_to_nvm);
void send_to_all_clients(int tcp_clients[], udp_conn_list_t *n_udp_conn_list, uint8_t data[], uint data_length);
bool remove_from_known_udp_clients(udp_conn_list_t *n_udp_conn_list, struct db_udp_client_t new_db_udp_client);

Expand Down
2 changes: 1 addition & 1 deletion main/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include "db_esp32_control.h"

#define MAX_LTM_FRAMES_IN_BUFFER 5
#define DB_BUILD_VERSION 11
#define DB_BUILD_VERSION 12
#define DB_MAJOR_VERSION 2
#define DB_MINOR_VERSION 0

Expand Down
13 changes: 12 additions & 1 deletion main/http_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,20 @@ static esp_err_t settings_clients_udp_post(httpd_req_t *req) {

int new_udp_port = 0;
char new_ip[IP4ADDR_STRLEN_MAX];
uint8_t save_to_nvm = false;
cJSON *json = cJSON_GetObjectItem(root, "ip");
if (json) strncpy(new_ip, json->valuestring, sizeof(new_ip));
new_ip[IP4ADDR_STRLEN_MAX-1] = '\0'; // to remove warning and to be sure
json = cJSON_GetObjectItem(root, "port");
if (json) new_udp_port = json->valueint;
json = cJSON_GetObjectItem(root, "save");
if (json && cJSON_IsBool(json)) {
if(cJSON_IsTrue(json)) {
save_to_nvm = true;
} else {
save_to_nvm = false;
}
} else {}

// populate the UDP connections list with a new connection
struct sockaddr_in new_sockaddr;
Expand All @@ -288,7 +297,7 @@ static esp_err_t settings_clients_udp_post(httpd_req_t *req) {
.mac = {0, 0, 0, 0, 0, 0} // dummy MAC
};
// udp_conn_list is initialized as the very first thing during startup - we expect it to be there
bool success = add_to_known_udp_clients(udp_conn_list, new_udp_client);
bool success = add_to_known_udp_clients(udp_conn_list, new_udp_client, save_to_nvm);

// Clean up
cJSON_Delete(root);
Expand Down Expand Up @@ -321,6 +330,8 @@ static esp_err_t settings_clients_clear_udp_get(httpd_req_t *req) {
}
udp_conn_list->size = 0;
ESP_LOGI(REST_TAG, "Removed all UDP clients from list!");
// Clear saved client as well. Pass any client since it will be ignored as long as clear_client is set to true.
save_udp_client_to_nvm(&udp_conn_list->db_udp_clients[0], true);
return ESP_OK;
}

Expand Down
71 changes: 62 additions & 9 deletions main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include <string.h>
#include <driver/gpio.h>
#include <lwip/apps/netbiosns.h>
#include <esp_now.h>

#include "freertos/event_groups.h"
#include "esp_mac.h"
Expand Down Expand Up @@ -174,7 +173,7 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t e
db_udp_client.udp_client.sin_len = 16;
db_udp_client.udp_client.sin_addr.s_addr = event->ip.addr;
memcpy(db_udp_client.mac, event->mac, sizeof(db_udp_client.mac));
add_to_known_udp_clients(udp_conn_list, db_udp_client);
add_to_known_udp_clients(udp_conn_list, db_udp_client, false);
}
// Wifi client mode events
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
Expand Down Expand Up @@ -432,7 +431,8 @@ void init_wifi_espnow() {
}

/**
* Write settings to non-volatile storage
* Write settings to non-volatile memory so they can be loaded on next startup. The UDP clients are saved using a
* separate function since the "save" operation is triggered by a separate button on the UI.
*/
void db_write_settings_to_nvs() {
ESP_LOGI(TAG,
Expand Down Expand Up @@ -467,6 +467,38 @@ void db_write_settings_to_nvs() {
nvs_close(my_handle);
}

/**
* Saves a udp client to the NVM so it can be automatically added on the next boot. No need for the user to manually add it again.
* Only one UDP client can be saved to the NVM.
* @param new_db_udp_client The client to add to NVM. Must have IP and port set.
* @param clear_client Set to true to remove the current client from NVM. In that case the new_db_udp_client param will be ignored.
*/
void save_udp_client_to_nvm(struct db_udp_client_t *new_db_udp_client, bool clear_client) {
char ip[INET_ADDRSTRLEN];
uint16_t port;
if (!clear_client) {
// convert addr to string
char client_str[INET_ADDRSTRLEN + 6];
inet_ntop(AF_INET, &(new_db_udp_client->udp_client.sin_addr), ip, INET_ADDRSTRLEN);
port = ntohs(new_db_udp_client->udp_client.sin_port);
snprintf(client_str, sizeof(client_str), "%s:%d", ip, port);
ESP_LOGI(TAG, "Saving UDP client %s to NVS %s", client_str, NVS_NAMESPACE);
} else {
// clear client from NVM by setting string to empty "" and port to 0
ip[0] = '\0';
port = 0;
ESP_LOGI(TAG, "Clearing UDP client from NVM");
}

nvs_handle my_handle;
ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &my_handle));
ESP_ERROR_CHECK(nvs_set_str(my_handle, "udp_client_ip", ip));
ESP_ERROR_CHECK(nvs_set_u16(my_handle, "udp_client_port", port));

ESP_ERROR_CHECK(nvs_commit(my_handle));
nvs_close(my_handle);
}

/**
* Helper function to read a string from the NVS based on a key. Handles errors accordingly and print result to console
*
Expand All @@ -490,7 +522,7 @@ void db_read_str_nvs(nvs_handle my_handle, char *key, char *dst) {
}

/**
* Read stored settings from internal storage
* Read stored settings from internal storage including the saved UDP client.
*/
void db_read_settings_nvs() {
nvs_handle my_handle;
Expand All @@ -502,7 +534,6 @@ void db_read_settings_nvs() {
db_write_settings_to_nvs();
} else {
ESP_LOGI(TAG, "Reading settings from NVS");

db_read_str_nvs(my_handle, "ssid", (char *) DB_WIFI_SSID);
db_read_str_nvs(my_handle, "wifi_pass", (char *) DB_WIFI_PWD);
db_read_str_nvs(my_handle, "ap_ip", DEFAULT_AP_IP);
Expand All @@ -520,16 +551,38 @@ void db_read_settings_nvs() {
ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_get_u8(my_handle, "proto", &DB_SERIAL_PROTOCOL));
ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_get_u16(my_handle, "trans_pack_size", &DB_TRANS_BUF_SIZE));
ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_get_u8(my_handle, "ltm_per_packet", &DB_LTM_FRAME_NUM_BUFFER));

// get saved UDP client
char udp_client_ip_str[INET_ADDRSTRLEN + 6];
udp_client_ip_str[0] = '\0';
db_read_str_nvs(my_handle, "udp_client_ip", udp_client_ip_str);
uint16_t udp_client_port = 0;
ESP_ERROR_CHECK_WITHOUT_ABORT(nvs_get_u16(my_handle, "udp_client_port", &udp_client_port));

// close NVM
nvs_close(my_handle);
ESP_LOGI(TAG,
"\tWifi Mode: %i\n\twifi_chan %i\n\tbaud %liu\n\tgpio_tx %i\n\tgpio_rx %i\n\tgpio_cts %i\n\t"
"gpio_rts %i\n\trts_thresh %i\n\tproto %i\n\ttrans_pack_size %i\n\tltm_per_packet %i",
DB_WIFI_MODE, DB_WIFI_CHANNEL, DB_UART_BAUD_RATE, DB_UART_PIN_TX, DB_UART_PIN_RX,
DB_UART_PIN_CTS, DB_UART_PIN_RTS, DB_UART_RTS_THRESH, DB_SERIAL_PROTOCOL, DB_TRANS_BUF_SIZE,
DB_LTM_FRAME_NUM_BUFFER);


if (strlen(udp_client_ip_str) > 0 && udp_client_port != 0) {
// there was a saved UDP client in the NVM - add it to the udp clients list
ESP_LOGI(TAG, "Adding %s:%i to known UDP clients.", udp_client_ip_str, udp_client_port);
struct sockaddr_in new_sockaddr;
memset(&new_sockaddr, 0, sizeof(new_sockaddr));
new_sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, udp_client_ip_str, &new_sockaddr.sin_addr);
new_sockaddr.sin_port = htons(udp_client_port);
struct db_udp_client_t new_udp_client = {
.udp_client = new_sockaddr,
.mac = {0, 0, 0, 0, 0, 0} // dummy MAC
};
bool save_to_nvm = false; // no need to save it to NVM again
add_to_known_udp_clients(udp_conn_list, new_udp_client, save_to_nvm);
} else {
// no saved UDP client - do nothing
}
}
}

Expand Down Expand Up @@ -632,7 +685,7 @@ void db_jtag_serial_info_print() {
* AP-Mode: ESP32 creates an WiFi access point of its own where the ground control stations can connect
*/
void app_main() {
udp_conn_list = udp_client_list_create(); // http server functions expect the list to exist
udp_conn_list = udp_client_list_create(); // http server functions and db_read_settings_nvs expect the list to exist
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
Expand Down
1 change: 1 addition & 0 deletions main/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ enum E_DB_SERIAL_PROTOCOL {

void db_jtag_serial_info_print();
void db_write_settings_to_nvs();
void save_udp_client_to_nvm(struct db_udp_client_t *new_db_udp_client, bool clear_client);

#endif //DB_ESP32_MAIN_H
3 changes: 2 additions & 1 deletion test/json_post_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def add_custom_udp():
url = "http://dronebridge.local/api/settings/clients/udp"
data = {
"ip": "192.168.10.32",
"port": 456
"port": 456,
"save": True
}
# Send the POST request
response = requests.post(url, json=data)
Expand Down
Binary file modified wiki/dbesp32_webinterface.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 03e32ed

Please sign in to comment.