adding backend script + test program for microcontroller

This commit is contained in:
2026-07-06 18:25:06 +07:00
parent d09e1a58e5
commit 6ffc2e606b
16 changed files with 452 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,19 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:upesy_wroom]
platform = espressif32
board = upesy_wroom
framework = arduino
monitor_speed = 115200
upload_speed = 921600
upload_port = /dev/ttyUSB0
lib_deps =
knolleary/PubSubClient@^2.8

View File

@@ -0,0 +1,129 @@
// arduino_code/Test/src/main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
// WiFi credentials
const char* ssid = "Home";
const char* password = "88888888qwE";
// MQTT Server details
const char* mqtt_server = "192.168.31.225";
const char* mqtt_user = "test";
const char* mqtt_password = "1234";
WiFiClient espClient;
PubSubClient client(espClient);
// Пины для управления TMC2209
const int DIR_PIN = 18; // Направление
const int STEP_PIN = 19; // Шаг
const int EN_PIN = 21; // Включение (опционально)
// Переменные для управления
bool motor_running = false;
int step_delay = 1000; // Задержка между шагами (в микросекундах)
int steps_per_second = 1000; // Количество шагов в секунду
void setup_wifi() {
delay(10);
// Подключение к WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}
void startMotor() {
motor_running = true;
Serial.println("Motor started");
}
void stopMotor() {
motor_running = false;
Serial.println("Motor stopped");
}
void updateStepDelay() {
// Рассчитываем задержку между шагами
step_delay = 1000000 / steps_per_second;
}
void setSpeed(int speed) {
// Установка скорости в шагах в секунду
if (speed > 0) {
steps_per_second = speed;
updateStepDelay();
Serial.print("Speed set to: ");
Serial.print(speed);
Serial.println(" steps/sec");
}
}
void callback(char* topic, byte* payload, unsigned int length) {
// Обработка команд через MQTT
String message;
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
if (message == "start") {
startMotor();
} else if (message == "stop") {
stopMotor();
} else if (message.startsWith("speed:")) {
int speed = message.substring(6).toInt();
setSpeed(speed);
} else if (message.startsWith("turnoff")) {
digitalWrite(EN_PIN, HIGH);
} else if (message.startsWith("turnon")) {
digitalWrite(EN_PIN, LOW);
}
}
void reconnect() {
// Повторное подключение к MQTT
while (!client.connected()) {
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
client.subscribe("motor/control");
} else {
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
// Настройка пинов
pinMode(DIR_PIN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);
pinMode(EN_PIN, OUTPUT);
// Включаем двигатель
digitalWrite(EN_PIN, LOW);
// Начальное направление (вперед)
digitalWrite(DIR_PIN, HIGH);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
// Обновляем задержку для правильной скорости
updateStepDelay();
// Управление двигателем
if (motor_running) {
// Выполняем шаг
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(step_delay);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(step_delay);
}
}

View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html

View File

@@ -0,0 +1,6 @@
#!/bin/bash
python -m venv testing
source testing/bin/activate
pip install paho-mqtt

80
backend_control/main.py Normal file
View File

@@ -0,0 +1,80 @@
# mqtt_motor_control.py
import paho.mqtt.client as mqtt
import time
# Настройки MQTT
MQTT_BROKER = "192.168.31.225" # Адрес MQTT брокера
MQTT_PORT = 1883 # Порт MQTT
MQTT_USER = "test" # Имя пользователя
MQTT_PASSWORD = "1234" # Пароль
MQTT_TOPIC = "motor/control" # Топик для управления двигателем
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT broker")
else:
print(f"Failed to connect, return code {rc}")
def send_command(command):
"""Отправка команды в MQTT топик"""
client.publish(MQTT_TOPIC, command)
print(f"Sent command: {command}")
def main():
# Создание клиента MQTT
global client
client = mqtt.Client()
# Установка учетных данных
client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
# Установка функции обратного вызова при подключении
client.on_connect = on_connect
# Подключение к брокеру
try:
client.connect(MQTT_BROKER, MQTT_PORT, 60)
client.loop_start() # Запуск цикла обработки в отдельном потоке
print("MQTT Motor Controller")
print("Commands:")
print(" start - запустить двигатель")
print(" stop - остановить двигатель")
print(" turnon - включить управление двигателем")
print(" turnoff - выключить управление двигателем")
print(" speed <value> - установить скорость (например: speed 2000)")
print(" exit - выход")
while True:
command = input("\nEnter command: ").strip()
if command == "exit":
break
elif command == "start":
send_command("start")
elif command == "stop":
send_command("stop")
elif command == "turnon":
send_command("turnon")
elif command == "turnoff":
send_command("turnoff")
elif command.startswith("speed "):
try:
speed = int(command.split()[1])
send_command(f"speed:{speed}")
except ValueError:
print("Invalid speed value. Please enter a number.")
else:
print("Unknown command. Use 'start', 'stop', 'speed <value>', or 'exit'")
except KeyboardInterrupt:
print("\nShutting down...")
except Exception as e:
print(f"Error: {e}")
finally:
client.loop_stop()
client.disconnect()
print("Disconnected")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,2 @@
(kicad_pcb (version 20260206) (generator "pcbnew") (generator_version "10.0")
)

View File

@@ -0,0 +1 @@
{}

14
kicad/ozon/ozon.kicad_sch Normal file
View File

@@ -0,0 +1,14 @@
(kicad_sch
(version 20260306)
(generator "eeschema")
(generator_version "10.0")
(uuid 8eb30ef0-8ef0-4108-b9d0-4c3ae489af42)
(paper "A4")
(lib_symbols)
(sheet_instances
(path "/"
(page "1")
)
)
(embedded_fonts no)
)

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -0,0 +1,97 @@
Привет! Как инженер-конструктор и эксперт по электронике, я с удовольствием помогу тебе спроектировать этот макет. Задача ясна: создаем систему автоматизированной сортировки с точным управлением скоростью и маршрутизацией по высоте.
Давай разберем твои вопросы по порядку.
### 1. Выбор двигателя: Шаговый vs Коллекторный
Для твоей задачи — **максимально точного выдерживания скорости** и управления с микроконтроллера — выбор очевиден.
| Характеристика | Коллекторный двигатель (DC) | Шаговый двигатель (Stepper) |
| :--- | :--- | :--- |
| **Точность скорости** | Низкая. Скорость зависит от напряжения и нагрузки. Для точности нужен энкодер (обратная связь). | **Высокая**. Скорость задается частотой импульсов. Постоянна при заданной нагрузке. |
| **Управление** | Простое (PWM/ШИМ). | Требует специальный драйвер (например, A4988 или TMC2208). |
| **Пуск/Стоп** | Плавный разгон, сложно точно позиционировать. | **Идеальный**. Можно точно задать количество шагов и мгновенно остановиться. |
| **Крутящий момент** | Высокий на высоких оборотах. | Высокий на низких скоростях (идеально для ленты). |
| **Сложность реализации** | Очень низкая. | Средняя (нужен драйвер и настройка тока). |
**Мой вердикт:** Используй **шаговый двигатель (например, NEMA 17)**.
Если ты хочешь «выдерживать скорость максимально точно», обычный DC-мотор тебя разочарует: при увеличении веса объекта на ленте скорость будет падать. Шаговый же двигатель будет поддерживать заданный темп, пока не будет достигнут предел его крутящего момента.
---
### 2. Селекторы (Сервоприводы)
Использование сервоприводов с барьером под 45° — это классическое и эффективное решение для макета.
**Советы по реализации:**
* **Механика:** Чтобы объект не застревал и не «зажевывался», сделай барьер обтекаемым или используй гладкий пластик/алюминий.
* **Угол:** 45 градусов — оптимально, но убедись, что при срабатывании сервопривод не толкает объект слишком резко, чтобы тот не перевернулся.
* **Логика:** В промышленном подходе, как отмечено в контексте, важно отслеживать «корректность маршрутизации» [1]. Рекомендую добавить в код проверку: действительно ли объект ушел в нужную зону, или произошла ошибка.
---
### 3. Датчик определения высоты
Для измерения высоты объекта сверху, когда лента движется, тебе подойдут три варианта в зависимости от требуемой точности:
1. **Ультразвуковой датчик (HC-SR04):** Самый дешевый и популярный.
* *Минус:* Широкий луч (может «цеплять» борта конвейера) и низкая скорость обновления.
2. **ИК-датчик расстояния (Sharp GP2Y0A21YK0F):** Более точный, узкий луч.
* *Минус:* Аналоговый сигнал, чувствителен к цвету объекта (черный поглощает ИК).
3. **Лазерный ToF-датчик (VL53L0X / VL53L1X):** **Мой выбор для этого проекта.**
* *Плюсы:* Измеряет время полета фотона (Time-of-Flight), работает независимо от цвета объекта, очень высокая точность (до миллиметров) и очень узкий луч.
---
### Итоговая архитектура (предложение):
* **Контроллер:** Arduino (Uno или Mega, если планируешь расширять количество датчиков).
* **Привод ленты:** Шаговый двигатель NEMA 17 $\rightarrow$ Драйвер A4988/TMC2208 $\rightarrow$ Arduino.
* **Сортировка:** 3 $\times$ Сервоприводы SG90 или MG90S (с металлическим редуктором надежнее).
* **Датчик высоты:** VL53L0X (I2C интерфейс).
* **Питание:** Отдельный блок питания 12В для двигателя и 5В для Arduino/сервоприводов (общий минус/земля).
**Важное замечание из спецификации:**
Не забудь предусмотреть в логике категорию «Не подходит для сортировки» [1]. Если датчик зафиксирует объект, который либо слишком высокий, либо слишком низкий (выходит за допустимые габариты), система должна отправить его в отдельный поток или оставить на ленте до конца [1].
Поскольку мы проектируем макет, важно учитывать не только функциональность, но и ограничения, упомянутые в техническом контексте: доступную площадь участка [1], разнообразие форм товаров (включая круглые в сечении) и необходимость бережного обращения с хрупкими или ценными объектами [1].
# Сервоприводы с барьером — это надежный вариант, но в зависимости от того, какие объекты вы будете сортировать, рассмотрите следующие альтернативы:
### 1. Электромагнитные толкатели (Solenoids)
Вместо плавного поворота барьера, соленоид дает резкий линейный толчок.
* **Плюсы:** Очень высокая скорость срабатывания, компактность [1].
* **Минусы:** Резкий удар. Если ваши объекты хрупкие, этот вариант **не рекомендуется**, так как он противоречит требованию к «мягкости обращения» [1].
* **Когда использовать:** Для прочных, тяжелых или однотипных деталей.
### 2. Откидная платформа (Tipping Platform)
Это участок конвейера, который может наклоняться в сторону или под ним открывается люк.
* **Плюсы:** Позволяет перемещать объекты за счет гравитации. Это один из самых бережных способов перемещения, что важно для ценных или хрупких товаров [1].
* **Минусы:** Требует более сложной механической интеграции в саму ленту.
* **Когда использовать:** Если приоритетом является сохранность объекта и минимизация механических ударов.
### 3. Поворотный диск-распределитель (Rotating Disc)
В конце основной ленты устанавливается диск, который поворачивается на определенный угол, направляя объект в один из трех лотков.
* **Плюсы:** Занимает минимум места (оптимально для ограниченной площади участка [1]). Отлично работает с объектами, имеющими круг в сечении [1].
* **Минусы:** Требует точной синхронизации остановки ленты и поворота диска.
* **Когда использовать:** Если макет должен быть максимально компактным, а объекты имеют схожую форму (например, цилиндры или сферы).
### 4. Боковой конвейер (Cross-Belt / Diverter)
Это короткий перпендикулярный отрезок ленты, который включается только тогда, когда объект должен быть сброшен в зону.
* **Плюсы:** Самый профессиональный и «мягкий» метод. Идеален для хрупких товаров [1].
* **Минусы:** Высокая стоимость и сложность (нужно еще 3 маленьких мотора и дополнительные ленты).
* **Когда использовать:** Если вы хотите создать максимально приближенный к реальному промышленному производству макет.
### Сводная таблица для выбора:
| Вариант | Бережность (для хрупких [1]) | Компактность (площадь [1]) | Сложность реализации | Работа с круглыми объектами [1] |
| :--- | :--- | :--- | :--- | :--- |
| **Серво-барьер** | Средняя | Высокая | Низкая | Хорошо |
| **Соленоид** | Низкая | Очень высокая | Низкая | Средне |
| **Платформа** | Высокая | Средняя | Средняя | Хорошо |
| **Диск** | Средняя | Очень высокая | Средняя | Идеально |
| **Боковой конвейер**| Очень высокая | Низкая | Высокая | Хорошо |
**Моя рекомендация:**
Если вы хотите сохранить простоту управления через Arduino, но при этом учесть требования к бережности и компактности [1], я рекомендую остаться на **сервоприводах**, но изменить конструкцию барьера: вместо «толкателя» сделать **«поворотный желоб»** или **«шлюз»**. Это позволит объекту плавно скатываться в нужную зону, не подвергаясь резким ударам.