diff --git a/.gitignore b/.gitignore index c6b0d5f..c21916d 100644 --- a/.gitignore +++ b/.gitignore @@ -43,7 +43,12 @@ Thumbs.db .cache/ simulation.json +# Зовнішня MAVLink-бібліотека (склонований окремий git-репозиторій) +drone_simulation_mavlink/c_library_v2/ + +.idea/ homework_13/robot_ws/install/ homework_13/robot_ws/build/ -homework_13/robot_ws/log/ \ No newline at end of file +homework_13/robot_ws/log/ + diff --git a/.vscode/extensions.json b/.vscode/extensions.json index bd817ad..922946d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ // студента, щоб відкрити репозиторій у devcontainer. C++ інструменти // встановлюється всередині контейнера через .devcontainer/devcontainer.json. "recommendations": [ - "ms-vscode-remote.remote-containers" + "ms-vscode-remote.remote-containers", + "wokwi.wokwi-vscode" ] } diff --git a/homework_36_esp32/.gitignore b/homework_36_esp32/.gitignore new file mode 100644 index 0000000..51c9513 --- /dev/null +++ b/homework_36_esp32/.gitignore @@ -0,0 +1,3 @@ +build/ +sdkconfig +sdkconfig.old \ No newline at end of file diff --git a/homework_36_esp32/CMakeLists.txt b/homework_36_esp32/CMakeLists.txt new file mode 100644 index 0000000..23c408c --- /dev/null +++ b/homework_36_esp32/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(homework_36_esp32) \ No newline at end of file diff --git a/homework_36_esp32/components/device_core/CMakeLists.txt b/homework_36_esp32/components/device_core/CMakeLists.txt new file mode 100644 index 0000000..37471d0 --- /dev/null +++ b/homework_36_esp32/components/device_core/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/device_core.cpp" + INCLUDE_DIRS "include" +) \ No newline at end of file diff --git a/homework_36_esp32/components/device_core/include/device_core.hpp b/homework_36_esp32/components/device_core/include/device_core.hpp new file mode 100644 index 0000000..1e32e48 --- /dev/null +++ b/homework_36_esp32/components/device_core/include/device_core.hpp @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +namespace core { + +enum class CmdType { + None, + SetPeriod, + Unknown +}; + +struct AccelSample { float ax, ay, az; }; + +struct Command { + CmdType type= CmdType::None; + uint32_t period_ms = 0; +}; + +AccelSample accel_from_raw(int16_t rx, int16_t ry, int16_t rz); + +const char* mode_from_period(uint32_t period_ms); + +int format_status(char* out, size_t out_size, + int64_t t_ms, const AccelSample& a, uint32_t period_ms); + +Command parse_command(const char* line); + +} // namespace core \ No newline at end of file diff --git a/homework_36_esp32/components/device_core/src/device_core.cpp b/homework_36_esp32/components/device_core/src/device_core.cpp new file mode 100644 index 0000000..f999fbe --- /dev/null +++ b/homework_36_esp32/components/device_core/src/device_core.cpp @@ -0,0 +1,39 @@ +#include "device_core.hpp" +#include + +namespace core { + +AccelSample accel_from_raw(int16_t rx, int16_t ry, int16_t rz) +{ + return { rx / 16384.0f, ry / 16384.0f, rz / 16384.0f }; +} + +const char* mode_from_period(uint32_t period_ms) +{ + return (period_ms <= 250) ? "fast" : "slow"; +} + +int format_status(char* out, size_t out_size, + int64_t t_ms, const AccelSample& a, uint32_t period_ms) +{ + return snprintf(out, out_size, + "t=%lld ms ax=%.2f ay=%.2f az=%.2f mode=%s", + (long long)t_ms, a.ax, a.ay, a.az, mode_from_period(period_ms)); +} + +Command parse_command(const char* line) +{ + Command cmd; + unsigned int val = 0; + if (sscanf(line, "p %u", &val) == 1) { + cmd.type = CmdType::SetPeriod; + cmd.period_ms = val; + + return cmd; + } + cmd.type = CmdType::Unknown; + + return cmd; +} + +} // namespace core \ No newline at end of file diff --git a/homework_36_esp32/diagram.json b/homework_36_esp32/diagram.json new file mode 100644 index 0000000..85ea9b2 --- /dev/null +++ b/homework_36_esp32/diagram.json @@ -0,0 +1,24 @@ +{ + "version": 1, + "author": "cMiltech", + "editor": "wokwi", + "parts": [ + { "type": "board-esp32-s3-devkitc-1", "id": "esp", "top": 0, "left": 0, "attrs": {} }, + { "type": "wokwi-mpu6050", "id": "imu", "top": 40, "left": 320, "attrs": {} }, + { "type": "board-ssd1306", "id": "oled", "top": 220, "left": 320, "attrs": { "i2cAddress": "0x3c" } } + ], + "connections": [ + [ "esp:TX", "$serialMonitor:RX", "", [] ], + [ "esp:RX", "$serialMonitor:TX", "", [] ], + + [ "imu:SDA", "esp:8", "green", [] ], + [ "imu:SCL", "esp:9", "yellow", [] ], + [ "imu:VCC", "esp:3V3.1", "red", [] ], + [ "imu:GND", "esp:GND.1", "black", [] ], + + [ "oled:DATA", "esp:8", "green", [] ], + [ "oled:CLK", "esp:9", "yellow", [] ], + [ "oled:3V3", "esp:3V3.1", "red", [] ], + [ "oled:GND", "esp:GND.1", "black", [] ] + ] +} diff --git a/homework_36_esp32/main/CMakeLists.txt b/homework_36_esp32/main/CMakeLists.txt new file mode 100644 index 0000000..6d1dd3f --- /dev/null +++ b/homework_36_esp32/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "main.cpp" + INCLUDE_DIRS "." + REQUIRES esp_timer esp_driver_i2c esp_driver_uart device_core +) \ No newline at end of file diff --git a/homework_36_esp32/main/main.cpp b/homework_36_esp32/main/main.cpp new file mode 100644 index 0000000..6f73443 --- /dev/null +++ b/homework_36_esp32/main/main.cpp @@ -0,0 +1,155 @@ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_timer.h" +#include "driver/i2c_master.h" +#include "esp_err.h" +#include "driver/uart.h" +#include "device_core.hpp" + +static constexpr gpio_num_t PIN_SDA = GPIO_NUM_8; +static constexpr gpio_num_t PIN_SCL = GPIO_NUM_9; +static constexpr uint8_t MPU_ADDR = 0x68; +static constexpr uint8_t REG_PWR1 = 0x6B; +static constexpr uint8_t REG_ACCEL = 0x3B; +static constexpr uart_port_t UART_PORT = UART_NUM_0; + +static volatile bool g_tick = false; +static volatile uint32_t g_tick_count = 0; + +static void on_timer(void*) +{ + g_tick = true; + g_tick_count = g_tick_count + 1; +} + +static i2c_master_dev_handle_t s_mpu = nullptr; + +static esp_err_t mpu_init(void) +{ + i2c_master_bus_config_t bus_cfg = {}; + bus_cfg.i2c_port = I2C_NUM_0; + bus_cfg.sda_io_num = PIN_SDA; + bus_cfg.scl_io_num = PIN_SCL; + bus_cfg.clk_source = I2C_CLK_SRC_DEFAULT; + bus_cfg.glitch_ignore_cnt = 7; + bus_cfg.flags.enable_internal_pullup = true; + + i2c_master_bus_handle_t bus = nullptr; + ESP_ERROR_CHECK(i2c_new_master_bus(&bus_cfg, &bus)); + + i2c_device_config_t dev_cfg = {}; + dev_cfg.dev_addr_length = I2C_ADDR_BIT_LEN_7; + dev_cfg.device_address = MPU_ADDR; + dev_cfg.scl_speed_hz = 400000; + + ESP_ERROR_CHECK(i2c_master_bus_add_device(bus, &dev_cfg, &s_mpu)); + + // wakeup value = 0 + uint8_t wake[2] = { REG_PWR1, 0x00 }; + + return i2c_master_transmit(s_mpu, wake, sizeof(wake), 100); +} + +static esp_err_t mpu_read_accel(core::AccelSample& out) +{ + uint8_t reg = REG_ACCEL; + uint8_t buf[6] = {}; + esp_err_t err = i2c_master_transmit_receive(s_mpu, ®, 1, buf, sizeof(buf), 100); + if (err != ESP_OK) return err; + + int16_t rx = (int16_t)((buf[0] << 8) | buf[1]); + int16_t ry = (int16_t)((buf[2] << 8) | buf[3]); + int16_t rz = (int16_t)((buf[4] << 8) | buf[5]); + out = core::accel_from_raw(rx, ry, rz); + return ESP_OK; +} + +static void uart_init(void) +{ + uart_config_t cfg = {}; + cfg.baud_rate = 115200; + cfg.data_bits = UART_DATA_8_BITS; + cfg.parity = UART_PARITY_DISABLE; + cfg.stop_bits = UART_STOP_BITS_1; + cfg.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + cfg.source_clk = UART_SCLK_DEFAULT; + + ESP_ERROR_CHECK(uart_driver_install(UART_PORT, 256, 0, 0, nullptr, 0)); + ESP_ERROR_CHECK(uart_param_config(UART_PORT, &cfg)); +} + +static void handle_command(const char* line, esp_timer_handle_t timer, uint32_t& period_ms) +{ + core::Command cmd = core::parse_command(line); + switch (cmd.type) { + case core::CmdType::SetPeriod: + if (cmd.period_ms >= 20 && cmd.period_ms <= 10000) { + period_ms = cmd.period_ms; + esp_timer_stop(timer); + esp_timer_start_periodic(timer, (uint64_t)period_ms * 1000ULL); + printf("OK period=%ums\n", (unsigned)period_ms); + } else { + printf("ERR period out of range 20..10000\n"); + } + break; + default: + printf("ERR unknown cmd: '%s'\n", line); + break; + } +} + +extern "C" void app_main(void) +{ + ESP_ERROR_CHECK(mpu_init()); + uart_init(); + + const esp_timer_create_args_t timer_args = { + .callback = &on_timer, + .arg = nullptr, + .dispatch_method = ESP_TIMER_TASK, + .name = "sample_tick", + .skip_unhandled_events = true + }; + esp_timer_handle_t timer = nullptr; + ESP_ERROR_CHECK(esp_timer_create(&timer_args, &timer)); + + uint32_t period_ms = 200; + ESP_ERROR_CHECK(esp_timer_start_periodic(timer, period_ms * 1000ULL)); + + char line[64]; + int idx = 0; + + while (1) { + uint8_t ch; + while (uart_read_bytes(UART_PORT, &ch, 1, 0) == 1) { + if (ch == '\n' || ch == '\r') { + if (idx > 0) { + line[idx] = '\0'; + handle_command(line, timer, period_ms); + idx = 0; + } + } else if (idx < (int)sizeof(line) - 1) { + line[idx++] = (char)ch; + } else { + idx = 0; + } + } + + if (g_tick) { + g_tick = false; + int64_t t_ms = esp_timer_get_time() / 1000; + + core::AccelSample a; + if (mpu_read_accel(a) == ESP_OK) { + char buf[96]; + core::format_status(buf, sizeof(buf), t_ms, a, period_ms); + printf("%s\n", buf); + } + } + + vTaskDelay(pdMS_TO_TICKS(5)); + } +} \ No newline at end of file diff --git a/homework_36_esp32/sdkconfig.defaults b/homework_36_esp32/sdkconfig.defaults new file mode 100644 index 0000000..ef2cb5e --- /dev/null +++ b/homework_36_esp32/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESP_CONSOLE_UART_DEFAULT=y +CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 \ No newline at end of file diff --git a/homework_36_esp32/wokwi.toml b/homework_36_esp32/wokwi.toml new file mode 100644 index 0000000..84aced2 --- /dev/null +++ b/homework_36_esp32/wokwi.toml @@ -0,0 +1,4 @@ +[wokwi] +version = 1 +firmware = 'build/homework_36_esp32.bin' +elf = 'build/homework_36_esp32.elf' \ No newline at end of file