Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Desktop support (Linux / Windows / Mac) (ESF-122) #102

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "submodules/stm32-cmake"]
path = submodules/stm32-cmake
url = https://github.com/ObKo/stm32-cmake.git
[submodule "submodules/serial"]
path = submodules/serial
url = https://github.com/wjwwood/serial
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just my 2c, I would recommend fetching this project at build time using ExternalProject or FetchContent CMake modules, instead of adding a submodule into esp-serial-flasher.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! This would be a good time to move over the stm32-cmake submodule to FetchContent as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a find_package script which can do the fetching if needed. In my application the serial library is already present. As a note, I didn't use add_subdirectory on this since it wants catkin as a dependancy, which I don't need here.

17 changes: 16 additions & 1 deletion examples/common/example_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#if !defined(WIN32)
#include <sys/param.h>
#endif
#include "esp_loader_io.h"
#include "esp_loader.h"
#include "example_common.h"

#ifndef MAX
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
#endif

#ifndef MIN
#define MIN(a, b) ((a) < (b)) ? (a) : (b)
#endif

#ifndef SINGLE_TARGET_SUPPORT


Expand Down Expand Up @@ -336,7 +346,8 @@ esp_loader_error_t load_ram_binary(const uint8_t *bin)
printf("Start loading\n");
esp_loader_error_t err;
const esp_loader_bin_header_t *header = (const esp_loader_bin_header_t *)bin;
esp_loader_bin_segment_t segments[header->segments];
//esp_loader_bin_segment_t segments[header->segments];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this being moved to the heap?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it didn't compile on MSVC

Copy link
Contributor

@DNedic DNedic Mar 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, after C99 VLAs are a GNU extension as well. We shouldn't be using VLAs in the first place IMO but I am unsure about using malloc(), as we might want to support platforms in the future that don't have it available. Also, using malloc() in example code for embedded platforms where malloc() is often prohibited is not ideal, especially as some of these example functions happen to be used in production a lot of the time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A simple static array with a max segment count should suffice.

esp_loader_bin_segment_t* segments = malloc(header->segments * sizeof(esp_loader_bin_segment_t));

// Parse segments
uint32_t seg;
Expand All @@ -357,6 +368,7 @@ esp_loader_error_t load_ram_binary(const uint8_t *bin)
err = esp_loader_mem_start(segments[seg].addr, segments[seg].size, ESP_RAM_BLOCK);
if (err != ESP_LOADER_SUCCESS) {
printf("Loading ram start with error %d.\n", err);
free(segments);
return err;
}

Expand All @@ -367,6 +379,7 @@ esp_loader_error_t load_ram_binary(const uint8_t *bin)
err = esp_loader_mem_write(data_pos, data_size);
if (err != ESP_LOADER_SUCCESS) {
printf("\nPacket could not be written! Error %d.\n", err);
free(segments);
return err;
}
data_pos += data_size;
Expand All @@ -377,9 +390,11 @@ esp_loader_error_t load_ram_binary(const uint8_t *bin)
err = esp_loader_mem_finish(header->entrypoint);
if (err != ESP_LOADER_SUCCESS) {
printf("\nLoad ram finish with Error %d.\n", err);
free(segments);
return err;
}
printf("\nFinished loading\n");

free(segments);
return ESP_LOADER_SUCCESS;
}
80 changes: 80 additions & 0 deletions examples/desktop_esp32_example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
set(ESPSERIAL_PATH ${CMAKE_CURRENT_LIST_DIR}/../../)

# LIB serial

SET(serial_path ${ESPSERIAL_PATH}submodules/serial)
SET(serial_SRCS ${serial_path}/src/serial.cc)

if(APPLE)
# If OSX
list(APPEND serial_SRCS ${serial_path}/src/impl/unix.cc)
list(APPEND serial_SRCS ${serial_path}/src/impl/list_ports/list_ports_osx.cc)
elseif(UNIX)
# If unix
list(APPEND serial_SRCS ${serial_path}/src/impl/unix.cc)
list(APPEND serial_SRCS ${serial_path}/src/impl/list_ports/list_ports_linux.cc)
else()
# If windows
list(APPEND serial_SRCS ${serial_path}/src/impl/win.cc)
list(APPEND serial_SRCS ${serial_path}/src/impl/list_ports/list_ports_win.cc)
endif()

add_library(serial
${serial_SRCS}
)

target_include_directories(serial PUBLIC
${serial_path}/include
)

# LIB esp_serial

include(${ESPSERIAL_PATH}examples/common/bin2array.cmake)
create_resources(${ESPSERIAL_PATH}examples/binaries/Hello-world ${CMAKE_BINARY_DIR}/binaries_1.c)
set_property(SOURCE ${CMAKE_BINARY_DIR}/binaries_1.c PROPERTY GENERATED 1)
create_resources(${ESPSERIAL_PATH}examples/binaries/RAM_APP ${CMAKE_BINARY_DIR}/binaries_2.c)
set_property(SOURCE ${CMAKE_BINARY_DIR}/binaries_2.c PROPERTY GENERATED 1)


add_library(esp_serial
${ESPSERIAL_PATH}port/serial_port.cpp
${ESPSERIAL_PATH}src/esp_loader.c
${ESPSERIAL_PATH}src/esp_targets.c
${ESPSERIAL_PATH}src/md5_hash.c
${ESPSERIAL_PATH}src/slip.c
${ESPSERIAL_PATH}src/protocol_uart.c
${ESPSERIAL_PATH}src/protocol_common.c
${CMAKE_BINARY_DIR}/binaries_1.c
${CMAKE_BINARY_DIR}/binaries_2.c
${ESPSERIAL_PATH}examples/common/example_common.c
)

target_include_directories(esp_serial PUBLIC
${ESPSERIAL_PATH}include
${ESPSERIAL_PATH}port
${ESPSERIAL_PATH}examples/common
)

target_include_directories(esp_serial PRIVATE
${ESPSERIAL_PATH}private_include
)

target_compile_definitions(esp_serial PUBLIC
-DSERIAL_FLASHER_INTERFACE_USB
-DMD5_ENABLED
-DSERIAL_FLASHER_WRITE_BLOCK_RETRIES=4
)

target_link_libraries(esp_serial PUBLIC
serial
)

# EXECUTABLE desktop_esp32_example

add_executable(desktop_esp32_example
main/main.cpp
)

target_link_libraries(desktop_esp32_example PRIVATE
esp_serial
)
63 changes: 63 additions & 0 deletions examples/desktop_esp32_example/main/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* Flash multiple partitions 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 <string.h>

#include "serial_port.h"
#include "esp_loader.h"
#include "esp_loader_io.h"
extern "C" {
#include "example_common.h"

}

#include "serial/serial.h"

#define HIGHER_BAUDRATE 230400

int main(int argv, char **argc)
{
if(argv < 2) {
printf("Usage: %s <port>\n", argc[0]);
return 1;
}

example_binaries_t bin;

loader_serial_config_t config;
config.portName = argc[1];
// config.portName = "COM4";
config.baudrate = 115200;
config.timeout = 1000;

if (loader_port_serial_init((const loader_serial_config_t*)&config) != ESP_LOADER_SUCCESS) {
printf("Serial initialization failed.\n");
return -1;
}

printf("Connecting...\n");
if (connect_to_target(HIGHER_BAUDRATE) == ESP_LOADER_SUCCESS) {

get_example_binaries(esp_loader_get_target(), &bin);

printf("Loading bootloader...\n");
flash_binary(bin.boot.data, bin.boot.size, bin.boot.addr);
printf("Loading partition table...\n");
flash_binary(bin.part.data, bin.part.size, bin.part.addr);
printf("Loading app...\n");
flash_binary(bin.app.data, bin.app.size, bin.app.addr);
printf("Done!\n");
} else {
printf("Connect failed\n");
return -1;
}

return 0;
}
181 changes: 181 additions & 0 deletions port/serial_port.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <thread>
#include <chrono>
#include <memory>
#include "serial/serial.h"

#include "serial_port.h"

using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;
using namespace std::chrono_literals;

loader_serial_config_t serial_config;
std::unique_ptr<serial::Serial> serial_port;
DNedic marked this conversation as resolved.
Show resolved Hide resolved
std::chrono::steady_clock::time_point serial_timer;

#ifdef SERIAL_FLASHER_DEBUG_TRACE
static void transfer_debug_print(const uint8_t *data, uint16_t size, bool write)
{
static bool write_prev = false;

if (write_prev != write) {
write_prev = write;
printf("\n--- %s ---\n", write ? "WRITE" : "READ");
}

for (uint32_t i = 0; i < size; i++) {
printf("%02x ", data[i]);
}
}
#endif

void setTimeout(uint32_t timeout)
{
if(timeout == serial_config.timeout)
return;

if(!serial_port || !serial_port->isOpen())
return;

serial_port->setTimeout(serial::Timeout::simpleTimeout(timeout));
serial_config.timeout = timeout;
}

esp_loader_error_t loader_port_write(const uint8_t *data, uint16_t size, uint32_t timeout)
{
try {
if(!serial_port || !serial_port->isOpen())
return ESP_LOADER_ERROR_FAIL;

setTimeout(timeout);
size_t result = serial_port->write(data, size);
if (result != size) {
return ESP_LOADER_ERROR_FAIL;
}
} catch (std::exception &e){
loader_port_debug_print(e.what());
return ESP_LOADER_ERROR_FAIL;
}

#ifdef SERIAL_FLASHER_DEBUG_TRACE
transfer_debug_print(data, size, true);
#endif

return ESP_LOADER_SUCCESS;
}


esp_loader_error_t loader_port_read(uint8_t *data, uint16_t size, uint32_t timeout)
{
try {
if(!serial_port || !serial_port->isOpen())
return ESP_LOADER_ERROR_FAIL;

setTimeout(timeout);
size_t result = serial_port->read(data, size);
if (result != size) {
return ESP_LOADER_ERROR_FAIL;
}
} catch (std::exception &e){
loader_port_debug_print(e.what());
return ESP_LOADER_ERROR_FAIL;
}

#ifdef SERIAL_FLASHER_DEBUG_TRACE
transfer_debug_print(data, size, true);
#endif

return ESP_LOADER_SUCCESS;
}

esp_loader_error_t loader_port_serial_init(const loader_serial_config_t *config)
{
serial_config = *config;

try {
serial_port = std::make_unique<serial::Serial>(serial_config.portName, config->baudrate, serial::Timeout::simpleTimeout(config->timeout));
if ( serial_port->isOpen() == false ) {
return ESP_LOADER_ERROR_FAIL;
}
} catch (std::exception &e){
loader_port_debug_print(e.what());
return ESP_LOADER_ERROR_FAIL;
}

return ESP_LOADER_SUCCESS;
}

// Set GPIO0 LOW, then
// assert reset pin for 100 milliseconds.
void loader_port_enter_bootloader(void)
{
// todo
loader_port_reset_target();
}


void loader_port_reset_target(void)
{
// todo
}


void loader_port_delay_ms(uint32_t ms)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}


void loader_port_start_timer(uint32_t ms)
{
serial_timer = high_resolution_clock::now() + (ms * 1ms);
}


uint32_t loader_port_remaining_time(void)
{
auto time_now = high_resolution_clock::now();
int32_t remaining = (duration_cast<milliseconds>(serial_timer - time_now)).count();
return (remaining > 0) ? (uint32_t)remaining : 0;
}


void loader_port_debug_print(const char *str)
{
printf("DEBUG: %s", str);
}

esp_loader_error_t loader_port_change_transmission_rate(uint32_t baudrate)
{
if(!serial_port || !serial_port->isOpen())
return ESP_LOADER_ERROR_FAIL;

try {
serial_port->setBaudrate(baudrate);
} catch (std::exception &e){
loader_port_debug_print(e.what());
return ESP_LOADER_ERROR_FAIL;
}

return ESP_LOADER_SUCCESS;
}
Loading