> For the complete documentation index, see [llms.txt](https://dh-organization.gitbook.io/droneshub-rover-kontakt/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dh-organization.gitbook.io/droneshub-rover-kontakt/opisanie-elektronnykh-komponentov/sensory/ultrazvukovoi-dalnomer.md).

# Ультразвуковой дальномер

<figure><img src="/files/wtqA6gs4OeTzeL9Oe8Rg" alt=""><figcaption></figcaption></figure>

Датчик JSN-SR04T представляет собой водонепроницаемый ультразвуковой дальномер, широко используемый в различных проектах, связанных с измерением расстояний в условиях повышенной влажности или прямого контакта с водой. Это делает его идеальным для таких решений, как уровнемеры для воды, робототехника и автомобильные системы помощи при парковке.

## Основные характеристики JSN-SR04T:

* **Диапазон измерения**: от 20 см до 600 см, что обеспечивает достаточную гибкость для большинства приложений.
* **Водонепроницаемость**: Модуль способен функционировать во влажной среде, что расширяет возможности его применения.
* **Точность измерения**: Высокая точность измерений благодаря ультразвуковой технологии, которая позволяет избежать ошибок, связанных с изменением температуры и других условий окружающей среды.
* **Рабочее напряжение**: 5 В, что делает его совместимым с большинством микроконтроллеров, включая Arduino и Raspberry Pi.
* **Простота интеграции**: Использует стандартные цифровые сигналы для связи, что упрощает подключение к большинству систем управления.

## Принцип работы:

JSN-SR04T работает по принципу эхолокации — излучает ультразвуковой сигнал и измеряет время, за которое эхо сигнала возвращается от препятствия. Это время пропорционально расстоянию до объекта, позволяя точно определять его положение.

## Применение:

Датчик JSN-SR04T часто используется в таких проектах, как:

* **Автоматизация и контроль уровня жидкости**: Измерение уровня воды в баках, резервуарах и других емкостях.
* **Робототехника**: Навигация и избегание препятствий для подводных или влажных сред.
* **Автомобильные приложения**: Ультразвуковая парковочная система, которая помогает водителю оценивать расстояние до ближайших объектов при парковке.

## Настройка дальномеров в ровере

&#x20;Дальномер нужно подключить в порты gpio uart в соответствии со схемой в разделе "Одноплатный компьютер". Для настройки дальномеров потребуется программа putty или mobaxterm

1. Откройте программу для настройки образа и зайдите на ip адрес вашего Orange pi 5
2. Введите команду sudo `vim /boot/orangepiEnv.txt`

<figure><img src="/files/ag6AgFUtV2heHfNZ7gDV" alt=""><figcaption></figcaption></figure>

Строка overlays в вашем файле должна выглядеть так же как и на фотографии. После изменений сохраните и выйдите из файла (Ctrl C + Ctrl X)

3. Просмотрите доступные вам порты командой `ls /dev/ttyS*`

<figure><img src="/files/mwvMVqXZVwq9lUeCVY0B" alt=""><figcaption></figcaption></figure>

Если список показывается подобным образом, значит порты настроены правильно.

## Код для проверки дальномеров

```python
from serial import Serial
import time
import threading

ports_in = ["COM18"]
ports = []


IP= "192.168.10.116"
PORT=7851

ifing=False


def connect():
    global ports_in
    for port in ports_in:
        temp_port = Serial(
            port,
            baudrate=9600,
            bytesize=8,
            parity='N',
            stopbits=1,
            timeout=1
        )
        try:
            print('[JSN-SR04T] Connected')
            ports.append({
                'dataChannel': temp_port,
                'message': [],
                'savedData': [],
                'distance': 0
            })
            if len(ports) == len(ports_in):
                start_collect_data()
        except Exception as err:
            print('[JSN-SR04T] Connection ERROR', err)
def set_ports():
    connect()

def question(prompt):
    return input(prompt)

set_ports()


def average(arr):
    return sum(arr) / len(arr)

def get_jsn():
    return [port['distance'] for port in ports]


def collect_data(port):
    try:
        while True:
            data = port['dataChannel'].read(4)  # Чтение данных
            # Если используете bytes, то:
            distance = int.from_bytes(data[1:3], byteorder='big')  # Преобразование в целое число
            # print(f'Distance: {distance/10} cm')  # Вывод расстояния на экране
            port['distance'] =distance/10
            print(f'Distance: {port["distance"]:.2f} cm')  # Вывод расстояния на экране

    except Exception as e:
        print(e)

def start_collect_data():
    for port in ports:
        thread=threading.Thread(target=collect_data,daemon=True,args=(port,))  # Вызов функции в новом потоке
        thread.start()

    global ifing
    while True:
        time.sleep(0.1)
        mass=get_jsn()
        ifing = any(num < 50 for num in mass)
        # print(ifing)

start_collect_data()

```

Код состоит из следующих основных частей:

* **Импорт библиотек:**

  ```python
  from serial import Serial
  import time
  import threadin
  ```

* **Настройка портов:**

  ```python
  ports_in = ["COM18"] # Для Windows
  ports = []
  ```

  * `ports_in`: Список последовательных портов, к которым подключены датчики. **Важно:** Для Linux используются пути вида `"/dev/tty..."`, для Windows - `"COM..."`. Пример показывает один COM-порт для Windows (COM18).&#x20;
  * `ports`: Пустой список, который будет содержать объекты `Serial` и дополнительную информацию для каждого порт

* Эти строки закомментированы и, вероятно, предназначались для отправки данных по сети (через сокеты). В текущей версии кода сетевая передача данных не реализована.

* **Глобальная переменная `ifing`:**

  ```python
  ifing=False
  ```

  * Эта глобальная переменная используется для индикации, есть ли датчики, показывающие расстояние меньше порогового значения (50 см).

* **Функция `connect()`:**

  ```python
  def connect():
      global ports_in
      for port in ports_in:
          temp_port = Serial(
              port,
              baudrate=9600,
              bytesize=8,
              parity='N',
              stopbits=1,
              timeout=1
          )
          try:
              print('[JSN-SR04T] Connected')
              ports.append({
                  'dataChannel': temp_port,
                  'message': [],
                  'savedData': [],
                  'distance': 0
              })
              if len(ports) == len(ports_in):
                  start_collect_data()
          except Exception as err:
              print('[JSN-SR04T] Connection ERROR', err)
  ```

  * Эта функция устанавливает соединение с каждым последовательным портом, указанным в `ports_in`.
  * Для каждого порта создается объект `Serial` с заданными параметрами:
    * `port`: Имя порта (например, “COM18” или “/dev/ttyUSB0”).
    * `baudrate=9600`: Скорость передачи данных (бод).&#x20;
    * `bytesize=8`: Размер байта данных (8 бит).
    * `parity='N'`: Отсутствие контроля четности.
    * `stopbits=1`: Один стоп-бит.
    * `timeout=1`: Максимальное время ожидания ответа (1 секунда).
  * В случае успешного подключения создается словарь (dictionary) для хранения информации о порте и добавляется в список `ports`. Словарь содержит:
    * `'dataChannel'`: Объект `Serial` для связи с портом.
    * `'message'`: Список для хранения входящих сообщений (в данном коде не используется активно).
    * `'savedData'`: Список для хранения сохраненных данных (в данном коде не используется).
    * `'distance'`: Значение расстояния, полученное с датчика.
  * После подключения ко всем портам, вызывает функцию `start_collect_data()` для запуска сбора данных.
  * В случае ошибки подключения, выводит сообщение об ошибке.

* **Функция `set_ports()`:**

  ```python
  def set_ports():
      connect()
  ```

  * Эта функция просто вызывает функцию `connect()`, т.е. выполняет подключение к портам. В коде присутствует для возможного расширения функциональности в будущем (например, для повторных попыток подключения).

* **Функция `question(prompt)` (не используется):**

  ```python
  def question(prompt):
      return input(prompt)
  ```

  * Эта функция принимает запрос и возвращает ввод пользователя. В данном коде не используется.

* **Функция `average(arr)` (не используется):**

  ```python
  def average(arr):
      return sum(arr) / len(arr)
  ```

  * Эта функция принимает массив чисел и возвращает среднее арифметическое. В данном коде не используется.

* **Функция `get_jsn()`:**

  ```python
  def get_jsn():
      return [port['distance'] for port in ports]
  ```

  * Эта функция возвращает список расстояний, полученных с каждого датчика. Она использует списковое включение (list comprehension) для извлечения значения `'distance'` из каждого словаря в списке `ports`.

* **Функция `collect_data(port)`:**

  ```python
  def collect_data(port):
      try:
          while True:
              data = port['dataChannel'].read(4)  # Чтение данных
              # Если используете bytes, то:
              distance = int.from_bytes(data[1:3], byteorder='big')  # Преобразование в целое число
              # print(f'Distance: {distance/10} cm')  # Вывод расстояния на экране
              port['distance'] =distance/10
              print(f'Distance: {port["distance"]:.2f} cm')  # Вывод расстояния на экране

      except Exception as e:
          print(e)
  ```

  * Эта функция отвечает за чтение данных с конкретного последовательного порта в цикле.
  * `data = port['dataChannel'].read(4)`: Читает 4 байта данных из последовательного порта. Предполагается, что датчик посылает 4 байта данных, представляющих расстояние.
  * `distance = int.from_bytes(data[1:3], byteorder='big')`: Преобразует байты, полученные из последовательного порта, в целое число, представляющее расстояние. В данном случае, используются байты `data[1]` и `data[2]`, которые интерпретируются как большое (big-endian) целое число.
  * `port['distance'] =distance/10`: Преобразует значение расстояния из миллиметров в сантиметры (делит на 10) и сохраняет его в словаре `port`.
  * `print(f'Distance: {port["distance"]:.2f} cm')`: Выводит расстояние в сантиметрах на экран с двумя знаками после запятой.
  * В случае ошибки, выводит сообщение об ошибке.

* **Функция `start_collect_data()`:**

  ```python
  def start_collect_data():
      for port in ports:
          thread=threading.Thread(target=collect_data,daemon=True,args=(port,))  # Вызов функции в новом потоке
          thread.start()

      global ifing
      while True:
          time.sleep(0.1)
          mass=get_jsn()
          ifing = any(num < 50 for num in mass)
          # print(ifing)
  ```

  * Эта функция создает и запускает отдельные потоки для функции `collect_data()` для каждого порта в списке `ports`. Это позволяет считывать данные со всех датчиков одновременно.
  * `thread=threading.Thread(target=collect_data,daemon=True,args=(port,))`: Создает объект `Thread`.
    * `target=collect_data`: Указывает функцию, которую нужно запустить в потоке.
    * `daemon=True`: Устанавливает поток как даймон (daemon thread). Это означает, что поток автоматически завершится, когда завершится основная программа.
    * `args=(port,)`: Передает словарь `port` в качестве аргумента в функцию `collect_data()`. Обратите внимание на запятую после `port`. Это необходимо, чтобы передать аргумент в виде кортежа (tuple).
  * `thread.start()`: Запускает поток.
  * Затем, в бесконечном цикле, эта функция получает данные с каждого датчика, проверяет, есть ли расстояние менее 50 см, и устанавливает глобальную переменную `ifing`.

* **Вызов `set_ports()` и `start_collect_data()`:**

  ```python
  set_ports()
  start_collect_data()
  ```

  * Эти строки запускают процесс подключения к портам и сбора данных. (на самом деле `start_collect_data` уже вызывается внутри connect так что строка `start_collect_data()` избыточна и никогда не достигнется).

Для работы с кодом требуется установить библиотеку pyserial: `pip install pyserial`&#x20;

В порт для проверки требуется подключить один из портов со списка выше (ttyS\*)

Файл с кодом требуется закинуть на Orange pi и запустить командой `python3 your_filename.py`&#x20;

Если все сделано по инструкции, в консоли побегут данные с дальномера.
