Информация
Услуги
  • Внедрение
  • Настройка
  • Поддержка
  • Ремонт
Контакты
Новинка
Распродажа
Новости
Доставка
Оплата
Загрузки
  • Прошивки
    • WinBox
    • RouterOS
    • Мобильные приложения MikroTik
    • Архив
  • RouterOS
  • Мобильные приложения MikroTik
  • Архив
Форум
Настройка
    info@mikrotik.moscow
    +7 495 320-55-52
    Заказать звонок
    Mikrotik.moscow
    Каталог
    • Акции
      Акции
    • Маршрутизаторы
      Маршрутизаторы
    • Коммутаторы
      Коммутаторы
    • Радиомосты и уличные точки доступа
      Радиомосты и уличные точки доступа
    • Wi-Fi для дома и офиса
      Wi-Fi для дома и офиса
    • LTE/5G
      LTE/5G
    • Powerline адаптеры
      Powerline адаптеры
    • IoT устройства
      IoT устройства
    • Оборудование 60 ГГц
      Оборудование 60 ГГц
    • Материнские платы RouterBOARD
      Материнские платы RouterBOARD
    • Корпуса
      Корпуса
    • Интерфейсы
      Интерфейсы
    • SFP/QSFP трансиверы
      SFP/QSFP трансиверы
    • Аксессуары
      Аксессуары
    • Антенны
      Антенны
    • Архив
      Архив
    Войти
    0 Сравнение
    0 Избранное
    0 Корзина
    Скачать WinBox Скачать Прошивки Форум > RouterOS Форум > SwOS Форум > Железо
    Mikrotik.moscow
    Каталог
    Войти
    0 Сравнение
    0 Избранное
    0 Корзина
    Mikrotik.moscow
    Телефоны
    +7 495 320-55-52
    Заказать звонок
    0
    0
    0
    Mikrotik.moscow
    • +7 495 320-55-52
      • Назад
      • Телефоны
      • +7 495 320-55-52
      • Заказать звонок
    • info@mikrotik.moscow
    • г. Москва, ул. Бакунинская, 84
    • Пн-Пт: 09-00 до 18-00
      Сб-Вс: выходной


    • Кабинет
    • 0 Сравнение
    • 0 Избранное
    • 0 Корзина
    Главная
    Форум
    Форум
    RouterOS
    Преобразовать пример на C в скрипт KNOT

    Преобразовать пример на C в скрипт KNOT

    Форумы: RouterOS, Аппаратное обеспечение, SwOS, Обратная связь, Объявления, Сторонние инструменты
    Поиск  Пользователи  Правила  Войти
    Страницы: 1
    RSS
    Преобразовать пример на C в скрипт KNOT, RouterOS
     
    Jonty
    Guest
    #1
    0
    27.03.2024 07:23:00
    Добрый день! Нужна помощь. У меня есть Bluetooth-датчик, который я подключил и внёс в белый список на роутере MikroTik KNOT LR8, но я не понимаю, где и как писать скрипт. У меня есть информация, которую дала компания. Есть кто-нибудь, кто может помочь?

    bool parse_packet(bd_addr addr, byte[] scanData)
    {  
     // Проверка ожидаемой длины  
     if (scanData.Length != 18) return false;  
     
     // Производитель (manufacturer specific)  
     if (scanData[1] != 0xFF) return false;
     
     // Длина хвостового пакета  
     if (scanData[0] != 13) return false;
     
     // Проверка данных в адресе совпадает с MAC — часть нашего протокола для исправления на iOS  
     if (scanData[9] != addr.Address[2]) return false;
     if (scanData[10] != addr.Address[1]) return false;
     if (scanData[11] != addr.Address[0]) return false;
     
     // Проверка кода производителя  
     if (scanData[2] != 0x59 || scanData[3] != 0x00) return false;
     
     // Чтение версии датчика – выставить по необходимости  
     prod_version = scanData[4] & 0x7F;
     if (prod_version != 0x03 && prod_version != 0x05) return false;  
     
     // Проверка, что это наш “синхронизированный” датчик  
     if (addr != synced_mac) return false;  
     
     return true;  
    }

    bool is_sync_pressed(uint8_t *scanData)  
    {  
     if (scanData[6] & 0x80) {
       return true;  
     }  
     return false;  
    }

    getAcceloY(rawByte) {  
     // Возвращает значение в “G”, которое можно преобразовать в угол  
     return (char)scanData[12] / 1024;
    }

    getAcceloX(rawByte) {  
     // Возвращает значение в “G”, которое можно преобразовать в угол  
     return (char)scanData[13] / 1024;
    }

    // Возвращает измеренную высоту жидкости (или воздуха) в мм  
    // scanData — массив сырых байт с данными производителя  
    uint32_t get_tank_height(const uint8_t* scanData)  
    {  
     // Для LPG (сжиженный пропан) используем следующие коэффициенты  
     const double coef[3] = { 0.573045, -0.002822, -0.00000535 };
     // Получаем сырое значение уровня из данных. Учитываем только младшие 14 бит  
     uint16_t raw = (scanData[8] * 256) + scanData[7];
     double raw_level = raw & 0x3FFF;  
     // Добавляем диапазон 16384us до 81916us с разрешением 4us  
     if (scanData[4] & 0x80) {
       raw_level = 16384.0 + raw_level * 4.0;  
     }  
     // Получаем несмасштабированную температуру из рекламного пакета  
     double raw_t = (scanData[6] & 0x7F);
     // Старший бит — состояние кнопки, игнорируем  
     // Применяем полином 2-го порядка для компенсации времени полёта (TOF) в мм LPG  
     return (uint32_t)(raw_level * (coef[0] + coef[1] * raw_t + coef[2] * raw_t * raw_t));
    }

    float get_battery_voltage(uint8_t *scanData)  
    {  
     // Возвращает напряжение батареи — старший бит зарезервирован и должен быть 0  
     return (float)(scanData[5] & 0x7F) / 32.0f;
    }

    // Произвольное преобразование напряжения батареи в проценты для CR2032  
    float get_battery_percentage(float voltage)  
    {  
     float percent = (voltage - 2.2f) / 0.65f * 100.0f;  
     if (percent < 0.0f) {  
       return 0.0f;  
     }  
     if (percent > 100.0f) {  
       return 100.0f;  
     }  
     return percent;  
    }

    int get_temperature_reading(uint8_t *scanData)  
    {  
     uint8_t temp = scanData[6] & 0x7F;
     return ((int)temp - 40);  
    }
     
     
     
    Jonty
    Guest
    #2
    0
    18.04.2024 07:41:00
    @Ammo, вот этот код:

    # Основная функция для декодирования данных Bluetooth-объявлений  
    /iot bluetooth scanners advertisements {  
       :local adids [find]
       :foreach adid in=$adids do={  
          :local pkt [get $adid]
         :local pktdata ($pkt->"data")  
         :if ([:len $pktdata]!=36 || [:pick $pktdata 1 2]="\FF") do={:error "bad packet: got $pktdata with length $[:len $pktdata]"}
     
    # Извлечение полей из данных объявления  
       :local batteryVoltage [:pick $pktdata 10 12]
       :local temperature [:pick $pktdata 12 14]
       :local tankLevel  [:pick $pktdata 14 18]
     
         :put "payload:$pktdata"  
         :put "batteryVoltage: $batteryVoltage"  
         :put "temperature: $temperature"  
         :put "tankLevel: $tankLevel"  
    }  
    }  

    Когда я запускаю, получаю вот что:  
    [admin@MikroTik] > system script run test7
    payload:0dff5900035f42000024684e2d000302e5fe  
    batteryVoltage: 5f  
    temperature: 42  
    tankLevel: 0000  

    Можно ли преобразовать каждое значение batteryVoltage, temperature и tankLevel из шестнадцатеричного в десятичное по отдельности или нужно конвертировать весь payload целиком?
     
     
     
    Amm0
    Guest
    #3
    0
    18.04.2024 11:52:00
    В сущности, преобразование из hex-в-строке в int выглядит сегодня так: [:tonum “0x$[:pick $pktdata ( + ) ]”]. В твоём коде это выглядит следующим образом:

    # Главная функция для декодирования Bluetooth-рекламных данных  
    /iot bluetooth scanners advertisements {  
       :local adids [find]
       :foreach adid in=$adids do={  
          :local pkt [get $adid]
          :local pktdata ($pkt->"data")  
          :if ([:len $pktdata]!=36 || [:pick $pktdata 1 2]="\FF") do={:error "плохой пакет: получено $pktdata с длиной $[:len $pktdata]"}

         # Извлечение полей из рекламных данных  
         :local batteryVoltage [:tonum "0x$[:pick $pktdata 10 (10 + 2) ]"]
         :local temperature [:tonum "0x$[:pick $pktdata 12 (12 + 2) ]"]
         :local tankLevel [:tonum "0x$[:pick $pktdata 14 (10 + 4) ]"]

          :put "payload:$pktdata"  
          :put "batteryVoltage: $batteryVoltage"  
          :put "temperature: $temperature"  
          :put "tankLevel: $tankLevel"  
        }  
    }

    Теперь уровень бака имеет более сложный итоговый расчёт согласно C-коду, но, как я предполагаю, он всегда будет относительным. Как отмечалось выше, математика включает числа с плавающей точкой. Вот где всё становится сложнее. Я использую пример строки, в которой значение уровня бака равно 0. Если у тебя будет та же исходная строка с BT с данными бака и какой-нибудь результат из другого инструмента, который делал этот расчёт — это поможет протестировать вычисления «коэффициента» уровня бака в сообщении, которое было немного выше.
     
     
     
    Jonty
    Guest
    #4
    0
    18.04.2024 12:04:00
    @Amm0, я уже кое-куда продвинулся, теперь нужно просто заставить этот скрипт обновляться каждые 10 секунд, когда новые локальные pktdata обновляются.

    # Главная функция для декодирования данных Bluetooth-объявлений  
    /iot bluetooth scanners advertisements {  
       :local adids [find]
       :foreach adid in=$adids do={
          :local pkt [get $adid]
          :local pktdata ($pkt->"data")
          :if ([:len $pktdata]!=36 || [:pick $pktdata 1 2]="\FF") do={:error "плохой пакет: получил $pktdata с длиной $[:len $pktdata]"}

          # Извлечение полей из данных объявления
          :local batteryVoltage [:pick $pktdata 10 12]
          :local decimalbatteryVoltage [:tonum "0x$batteryVoltage"]
          # Выполняется расчет с десятичными знаками
          # делимое / делитель = частное.дробная часть = результат

          :local dividend ($decimalbatteryVoltage)
          :local divisor "32"
          :local decimalplaces "2"

          # Математический расчет
          :local quotient 0
          :local remainder 0
          :local result 0
          :local decimal 0

          :set quotient ($dividend / $divisor)

          :if ($quotient = 0) do={
              :set dividend [:tonum ($dividend . "0")]
          }

          :set remainder ($dividend - ($divisor * $quotient))

          :if ($remainder > 0) do={
              :local tmpremainder [:tonum ($remainder . "0")]
              :for x from=1 to=$decimalplaces do={
                  :local tmpdecimal [:tonum ($tmpremainder / $divisor)]
                  :set decimal [:tonum ($decimal . $tmpdecimal)]
                  :set tmpremainder [:tonum (($tmpremainder - ($tmpdecimal * $divisor)) . "0")]
              }
              :set result ($quotient . "." . $decimal)
          } else={
              :set result $quotient
          }
          :local 2decimalbatteryVoltage ($result)

          :local temperature [:pick $pktdata 12 14]
          :local decimaltemperature [:tonum "0x$temperature"]
          # Выполняется расчет с десятичными знаками
          # делимое / делитель = частное.дробная часть = результат

          :local dividend ($decimaltemperature)
          :local divisor 40
          :local decimalplaces 2

          # Математический расчет
          :local quotient 0
          :local remainder 0
          :local result 0
          :local decimal 0

          :set quotient ($dividend - $divisor)

          :if ($quotient = 0) do={
              :set dividend [:tonum ($dividend . "0")]
          }

          :set remainder ($dividend - ($divisor * $quotient))

          :if ($remainder > 0) do={
              :local tmpremainder [:tonum ($remainder . "0")]
              :for x from=1 to=$decimalplaces do={
                  :local tmpdecimal [:tonum ($tmpremainder / $divisor)]
                  :set decimal [:tonum ($decimal . $tmpdecimal)]
                  :set tmpremainder [:tonum (($tmpremainder - ($tmpdecimal * $divisor)) . "0")]
              }
              :set result ($quotient . "." . $decimal)
          } else={
              :set result $quotient
          }
          # Конец математического расчёта

          :local 2decimaltemperature ($result )
          :local tankLevel [:pick $pktdata 14 18]
          :local decimaltankLevel [:tonum "0x$tankLevel"]
          # Выполняется расчет с десятичными знаками
          # делимое / делитель = частное.дробная часть = результат

          :local dividend ($decimaltankLevel)
          :local divisor 10
          :local decimalplaces 2

          # Математический расчет
          :local quotient 0
          :local remainder 0
          :local result 0
          :local decimal 0

          :set quotient ($dividend * $divisor)

          :if ($quotient = 0) do={
              :set dividend [:tonum ($dividend . "0")]
          }

          :set remainder ($dividend - ($divisor * $quotient))

          :if ($remainder > 0) do={
              :local tmpremainder [:tonum ($remainder . "0")]
              :for x from=1 to=$decimalplaces do={
                  :local tmpdecimal [:tonum ($tmpremainder / $divisor)]
                  :set decimal [:tonum ($decimal . $tmpdecimal)]
                  :set tmpremainder [:tonum (($tmpremainder - ($tmpdecimal * $divisor)) . "0")]
              }
              :set result ($quotient . "." . $decimal)
          } else={
              :set result $quotient
          }
          # Конец математического расчёта

          :local 2decimaltankLevel ($result)

          :put "payload: $pktdata"
          :put "batteryVoltage: $2decimalbatteryVoltage  V"
          :put "temperature: $2decimaltemperature  °C"
          :put "tankLevel: $2decimaltankLevel  MM"
       }
    }

    [admin@MikroTik] > system script run test10
    payload: 0dff5900035f41000024684e2dfd0302e5fe  
    batteryVoltage: 2.96  V  
    temperature: 25   C  
    tankLevel: 0  MM  
    [admin@MikroTik] >
     
     
     
    Amm0
    Guest
    #5
    0
    18.04.2024 12:08:00
    Вы можете вставить этот код в скрипт /system/schedule с интервалом 10 секунд, который запускается при старте системы. Скорее всего, удобнее в winbox/webfig скопировать и вставить код, чем использовать CLI для добавления задания, чтобы не заморачиваться с экранированием.
     
     
     
    Amm0
    Guest
    #6
    0
    18.04.2024 12:10:00
    Или запустите в командной строке, добавьте ещё один :while (1) do={ … } вокруг кода с :delay 10s перед завершением цикла. Чтобы выйти — нужно нажать ctrl-c.
     
     
     
    Jonty
    Guest
    #7
    0
    18.04.2024 12:16:00
    @Amm0 спасибо, попробую так сделать.
     
     
     
    Jonty
    Guest
    #8
    0
    18.04.2024 12:21:00
    @Amm0 теперь, когда я разобрал пакет и получил какой-то читаемый результат, протестирую его на газовом баллоне, чтобы узнать, какой уровень бака получится.
     
     
     
    Amm0
    Guest
    #9
    0
    18.04.2024 12:22:00
    Небольшое замечание: в скрипте планировщика лучше использовать «/log info TEXT» вместо «:put».
     
     
     
    Jonty
    Guest
    #10
    0
    19.04.2024 06:01:00
    @Amm0 расписание прописывать прямо в код для сенсора, который я уже сделал, или использовать планировщик системы в системном меню?
     
     
     
    Amm0
    Guest
    #11
    0
    19.04.2024 11:45:00
    Ты вставляешь свой код в событие On Event планировщика. Это нужно сделать всего один раз. Планировщик (он же cron) затем будет запускать твой скрипт с заданным интервалом. С помощью winbox ты сможешь просто скопировать и вставить свой код в «On Event», установить «Start Time» как «startup» и интервал в 00:00:10. Или через CLI: /system/scheduler/add name=readBT start-time=startup interval=10s on-event={ your_code }. В любом случае, дальнейшие правки кода нужно делать именно в этом планировщике. Теоретически, ты можешь написать новый код, который добавит планировщик для выполнения какой-то конкретной задачи в будущем (без параметра interval=), а когда этот код сработает, он удалит старый планировщик и добавит новый запланированный элемент (тоже без interval=). Но такой подход сложный и излишний, если хочешь запускать что-то каждые 10 секунд — параметр interval= сам всё сделает за тебя.
     
     
     
    Jonty
    Guest
    #12
    0
    22.04.2024 05:53:00
    @Amm0 попробую, и у меня получилась нагрузка с датчиком на газовом баллоне 0dff5900035e41f5c124684e20000302e5fe. f5c1 — это две части нагрузки, отвечающие за объём газа в баллоне. При помощи приложения для датчика показывало, что в баллоне 20,2 см газа. Теперь мне нужно разобраться с математикой этого.
     
     
     
    Jonty
    Guest
    #13
    0
    10.06.2024 09:04:00
    @Amm0, возможно ли отправлять пакет данных Bluetooth через MQTT? Если да, то это делается через подписку на топик или с помощью скрипта? Я думаю о чем-то вроде этого, но пока не уверен, где именно ошибаюсь. #config  
    :local broker "intellisec"  
    :local topic "intellisec/iot/bluetooth"  

    #gather info  
    :local adids [/iot bluetooth scanners advertisements]
    :foreach adid in=$adids do={  
     :local pkt [get $adid]
     :local pktdata ($pkt->"data")  
     :if ([:len $pktdata]!=36 || [:pick $pktdata 1 2]="\FF") do={:error "плохой пакет: получили $pktdata длиной $[:len $pktdata]"}

     #compose message in JSON - bluetooth sensor  
     :local message "$pktdata"  
     :put $message  

     /iot mqtt publish broker=$broker topic="$topic/pktdata" message=$message retain=no  
     /log/info message=$message  
    }
     
     
     
    Страницы: 1
    Читают тему
    +7 495 320-55-52
    info@mikrotik.moscow
    Электрозаводская, Бауманская
    Москва, ул. Бакунинская, 84с21
    Конфиденциальность Оферта
    © 2026 «Mikrotik.Moscow»
    Главная Каталог 0 Корзина 0 Избранные Кабинет 0 Сравнение Акции Контакты Услуги Бренды Отзывы Компания Лицензии Документы Реквизиты Поиск Блог Обзоры