Skip to main content

Установка Zigbee2mqtt на NAPI-C (P)

· One min read
dmn
maintainer

Устанавливаем на NAPI-C (P) Zigbee2mqtt

  1. Устанавливаем Аrmbian

  2. Ставим пакеты

apt-get install -y curl curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - apt-get install -y nodejs git make g++ gcc libsystemd-dev tmux apt install -y mosquitto mosquitto-clients corepack enable
  1. Создадим каталог
mkdir /opt/zigbee2mqtt
  1. На случай если ставили не от рута нужно дать права на каталог
sudo chown -R ${USER}: /opt/zigbee2mqtt
  1. Скачиваем гит в каталог
git clone --depth 1 https://github.com/Koenkk/zigbee2mqtt.git /opt/zigbee2mqtt
  1. Переходим в каталог:
cd /opt/zigbee2mqtt
  1. Запустим tmux и продолжим сборку в нем:
tmux
  1. Собираем приложение

Запустим сборку в один поток

pnpm install --frozen-lockfile --child-concurrency=1 6.1
  1. После сборки выполняем запуск
cd /opt/zigbee2mqtt
pnpm start
  1. Делаем сервис

Создаем файл

nano /etc/systemd/system/zigbee2mqtt.service

С таким содержимым

[Unit]
Description=zigbee2mqtt
After=network.target

[Service]
Environment=NODE_ENV=production
Type=simple
ExecStart=/usr/bin/pnpm start
WorkingDirectory=/opt/zigbee2mqtt
StandardOutput=inherit
StandardError=inherit
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
  1. Выполняем инициализацию и запуск сервиса
systemctl daemon-reload
systemctl enable zigbee2mqtt.service
systemctl start zigbee2mqtt.service
systemctl status zigbee2mqtt.service
journalctl -u zigbee2mqtt.service -f

#napi #zigbee $zigbee2mqtt

Чтение и программирование EEPROM преобразователя FT232

· 2 min read
dmn
maintainer

Читаем внутреннюю прошивку преобразователя rtu-usb ft232 под Linux (armbian na Napi-C)

Ставим софт

sudo apt install libftdi1-2 libftdi1-dev ftdi-eeprom

Читаем

touch c.txt #создаем пустой файл

#vendor/product смотрим в lsusb
echo 'vendor_id=0x0403
product_id=0x6001' > c.txt
ftdi_eeprom --read-eeprom c.txt  --verbose

Получаем результат

FTDI eeprom generator v0.17
(c) Intra2net AG and the libftdi developers <opensource@intra2net.com>
Unable to find FTDI device with description: i:0403:6001
Error code: -3 (device not found)
root@rockpi-s:~# ftdi_eeprom --read-eeprom c.txt --verbose

FTDI eeprom generator v0.17
(c) Intra2net AG and the libftdi developers <opensource@intra2net.com>
Unable to find FTDI devices under given vendor/product id: 0x0/0x0
Error code: -3 (device not found)
Retrying with default FTDI pid=0x6001.
FTDI read eeprom: 0
EEPROM size: 128
VID: 0x0403
PID: 0x6001
Release: 0x0000
Bus Powered: 90 mA USB Remote Wake Up
Manufacturer: FTDI
Product: FT232R USB UART
Serial: A10M5ZU3
Checksum : 17cb
Internal EEPROM
Oscillator: Internal
Enable Remote Wake Up
PNP: 1
Channel A has Mode UART VCP
C0 Function: TXLED
C1 Function: RXLED
C2 Function: TXDEN
C3 Function: PWREN
C4 Function: SLEEP
Warning: Not writing eeprom, you must supply a valid filename
FTDI close: 0

Этой же программой можно писать параметры в еепром ft232, создав конфигурационный файлик.

🙋‍♀️Зачем это надо ?

  • Проверить жив ли ft232
  • Проверить назначение линий CBUS (C0-C4), которые могут быть переопределены.

👍 Полезные факты

  • lsusb -v — должно быть состояние bConfigurationValue = 1

  • PWREN # после того как модуль инициализирован драйвером должен быть в НУЛЕ (0)

  • SLEEP #у FTDI — это сигнал, который показывает, что чип перешёл в режим сна (USB suspend).

Когда USB-шина в нормальном рабочем состоянии → SLEEP# = 1 (логическая единица).

Когда хост перевёл устройство в режим suspend (спящий режим по USB, обычно при бездействии или энергосбережении) → SLEEP# = 0.

😂 Несколько подсказок от чатГПТ (я не проверял)

  1. Сохранить сырой еепром
sudo ftdi_eeprom --read-eeprom текущий.conf --device ft232 --small-eeprom --eeprom-filename дамп.bin
  1. Файл, если нужно прошить свои кастом параметры (ftdi.conf)
vendor_id=0x0403
product_id=0x6001
manufacturer="MyCompany"
product="My FT232R"
serial="FT123456"

# CBUS configuration:
cbus0=1 # Можно оставить как есть или назначить PWREN
cbus1=1 # PWREN, к примеру
cbus2=0 # TXDEN
cbus3=2 # RXLED (имитация RXDEN через LED#)
cbus4=4 # TXRXLED

Как писать

sudo ftdi_eeprom --flash-eeprom ftdi.conf

#sys #ft232 #linux

Конфигурация uEnv.txt для Сборщик компакт 0.3

· One min read
dmn
maintainer

uEnv.txt для Сборщик компакт 0.3 (с блоком питания 1,2А)

verbosity=7
fdtfile=rk3308-napi-c.dtb
console=ttyS0,115200n8
overlays=rk3308-uart1 rk3308-uart3-m0 rk3308-i2c1-ds1338 rk3308-i2c3-m0 rk3308-usb20-host
kernelimg=Image
extraargs=

🔥Важно: RS485 у этой модели находится на /dev/ttyS3

#fcc #fcc03 #fcc3308

Конфигурация UART на модуле CM4 в Токосборщике

· One min read
dmn
maintainer

В Токосборщике на модуле CM4 UART-ы расположились следующим образом:

✔️UART3 - внешний датчик Modbus ✔️UART9 - модуль расширений (Zigbee) ✔️UART7 - встроенный датчик тока

Для корректной работе Debian, необходимо подключить оверлеи с uart7,9 в файле /boot/orangepiEnv.txt

root@orangepicm4:~# cat /boot/orangepiEnv.txt 
verbosity=1
bootlogo=false
extraargs=cma=128M
overlay_prefix=rk356x
overlays=uart7-m2 uart9-m2
rootdev=UUID=a0f8ca89-7eb7-4a1e-947a-2341637b4782
rootfstype=ext4
console=serial

#fcucm4 #orangecm4 #fcu

Python-сниффер для анализа Modbus RTU трафика

· 3 min read
dmn
maintainer

Написал "на коленке" полезный сниффер modbus

modbus_sniffer_raw_pretty.py

#!/usr/bin/env python3
"""
Raw Modbus RTU Sniffer — listens to a serial port and prints decoded Modbus RTU frames.
Now includes decoding of address/count/value for popular function codes.
"""

import serial
import argparse
import time
import logging
import struct

logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.INFO)

def calculate_crc(data: bytes):
crc = 0xFFFF
for pos in data:
crc ^= pos
for _ in range(8):
lsb = crc & 0x0001
crc >>= 1
if lsb:
crc ^= 0xA001
return crc.to_bytes(2, 'little')

def validate_crc(frame: bytes):
if len(frame) < 4:
return False
data, received_crc = frame[:-2], frame[-2:]
return calculate_crc(data) == received_crc

def decode_payload(fc, payload):
if fc in [1, 2, 3, 4]: # Read coils, discrete inputs, HR, IR
if len(payload) >= 4:
address, count = struct.unpack(">HH", payload[:4])
return f"Read | Addr={address} | Count={count}"
elif fc in [5, 6]: # Write single coil/register
if len(payload) >= 4:
address, value = struct.unpack(">HH", payload[:4])
return f"Write Single | Addr={address} | Value={value}"
elif fc in [15, 16]: # Write multiple coils/registers
if len(payload) >= 5:
address, count, byte_count = struct.unpack(">HHB", payload[:5])
return f"Write Multiple | Addr={address} | Count={count} | Bytes={byte_count}"
return f"Payload: {payload.hex()}"

def print_frame_info(frame: bytes):
if not validate_crc(frame):
log.warning(f"❌ Invalid CRC: {frame.hex()}")
return
unit_id = frame[0]
function_code = frame[1]
payload = frame[2:-2]
desc = decode_payload(function_code, payload)
log.info(f"📥 Unit={unit_id} | FC={function_code}{desc}")

def read_frames(ser):
buffer = bytearray()
last_byte_time = time.time()
inter_char_timeout = 0.01
frame_timeout = 0.1

while True:
if ser.in_waiting:
byte = ser.read(1)
now = time.time()
if now - last_byte_time > frame_timeout and buffer:
print_frame_info(bytes(buffer))
buffer.clear()
buffer.append(byte[0])
last_byte_time = now
elif buffer and time.time() - last_byte_time > frame_timeout:
print_frame_info(bytes(buffer))
buffer.clear()
else:
time.sleep(0.005)

def main():
parser = argparse.ArgumentParser(description="Raw Modbus RTU Sniffer with decoding")
parser.add_argument('--port', required=True, help='Serial port (e.g. /dev/ttyUSB0)')
parser.add_argument('--baudrate', type=int, default=9600)
parser.add_argument('--parity', choices=['N', 'E', 'O'], default='N')
parser.add_argument('--stopbits', type=int, choices=[1, 2], default=1)
args = parser.parse_args()

try:
ser = serial.Serial(
port=args.port,
baudrate=args.baudrate,
parity={'N': serial.PARITY_NONE, 'E': serial.PARITY_EVEN, 'O': serial.PARITY_ODD}[args.parity],
stopbits=args.stopbits,
bytesize=8,
timeout=0
)
log.info(f"🔍 Listening on {args.port} at {args.baudrate} bps...")
read_frames(ser)
except Exception as e:
log.error(f"Failed to open serial port: {e}")

if __name__ == "__main__":
main()

использует библиотеку pyserial

pip install pyserial

Пример:

(venv) orangepi@cm4-right:~$ python3 modbus_sniffer_raw_pretty.py  --baudrate 9600 --port /dev/ttyS9
INFO:root:🔍 Listening on /dev/ttyS9 at 9600 bps...
INFO:root:📥 Unit=1 | FC=3 — Read | Addr=0 | Count=1
INFO:root:📥 Unit=1 | FC=3 — Read | Addr=10 | Count=2

Скрипт читает и распознает пакеты modbus, запросы modbus транслирует на экран.

#modbus #modbussniffer

Адресация GPIO в gpiod на примере GPIO2_B4

· One min read
dmn
maintainer

Как адресуются gpio в gpiod на примере GPIO2_B4. Есть банки 0-4 - это первая цифра после GPIO ("2"). В каждом банке 32 ячейки.

А0-А7 - 0 -7
B0-B7 - 8-15
C0-C7 - 16-24
D0-D7 - 25-31

Поэтому GPIO2_B4 это банк 2, ячейка 12.

Опрашивается так:

gpioget --numeric -a -c gpiochip2 12

А устанавливается так:

gpioset -t 0 -c gpiochip2 12=1

Прочитать все банки:

gpiodetect

Прочитать банк:

gpioinfo -c gpiochip2

#gpio #napigpio

Подключение UART4 в NapiLinux

· One min read
dmn
maintainer

Для подключения uart4 в NapiLinux необходимо:

✔️Создать папку /boot/overlay-user ✔️Положить в нее файл rk3308-uart4.dtbo ✔️Добавить строчку в файл /boot/uEnv.txt

user_overlays=rk3308-uart4 

✔️Перезагрузить систему ✔️Должно появиться устройство /dev/ttyS4

#napi #napioverlay #dts

Тестирование MQTT датчиков через Telegraf

· One min read
dmn
maintainer

Для тестирования датчиков mqtt не подходит вывод нашего теста через Веб, так как данные приходят не сразу. Поэтому можем тестировать вручную.

Для того, чтобы было видно какие данные увидел телеграф, перенаправим вывод на экран.

Делаем файл telegtraf.conf.out в любом месте, но не в /etc/telegraf/telegraf.d (чтобы телеграф его по умолчанию не подхватывал)

[[outputs.file]]
files = ["stdout"]
data_format = "influx"

И тестируем командой

telegraf --config /etc/telegraf/telegraf.d/cl-203.conf --config /root/telegraf.conf.out --debug

Смотрим на экране данные

CL213E,host=napi-fearlessporcupine,sensor=CL213E,topic=sensors/CL213E/CO CO=0 1751463487564614267
CL213E,host=napi-fearlessporcupine,sensor=CL213E,topic=sensors/CL213E/CO2 CO2=410 1751463487605135529
CL213E,host=napi-fearlessporcupine,sensor=CL213E,topic=sensors/CL213E/PM25 PM25=248 1751463487605167843
CL213E,host=napi-fearlessporcupine,sensor=CL213E,topic=sensors/CL213E/RH RH=47.6 1751463487605195050
CL213E,host=napi-fearlessporcupine,sensor=CL213E,topic=sensors/CL213E/TC TC=24 1751463487605267960
CL213E,host=napi-fearlessporcupine,sensor=CL213E,topic=sensors/CL213E/DC DC=12.2 1751463487605276033

#telegraf #mqtt #napilinux

Настройка аутентификации Mosquitto по паролю

· One min read
dmn
maintainer

Для запуска Mosquitto с проверкой пользователя и пароля нужно выполнить несколько простых шагов.

Создание файла паролей

Создаем файл паролей с помощью утилиты mosquitto_passwd:

mosquitto_passwd -c /etc/mosquitto/passwd user

После выполнения команды система запросит пароль для пользователя user.

Смена владельца файла

Меняем владельца файла на пользователя mosquitto:

chown mosquitto /etc/mosquitto/passwd

Конфигурация Mosquitto

Простейший конфиг для включения аутентификации:

allow_anonymous false
password_file /etc/mosquitto/passwd

После применения этих настроек Mosquitto будет требовать авторизацию для всех подключений.

Добро пожаловать в техблог!

· One min read
dmn
maintainer

Это новый техблог для технических заметок и решений! 🎯

Раздел основан на материалах из Telegram канала t.me/napilab и находится в процессе наполнения.

Здесь будут публиковаться:

  • Быстрые техзаметки из Telegram канала
  • Решения проблем и troubleshooting
  • Полезные скрипты и автоматизация
  • Советы по работе с Linux и embedded устройствами

Отличие от основного блога новостей:

  • Основной блог - официальные новости, анонсы продуктов, выставки
  • Техблог - технические заметки, решения, quick tips

Пример использования:

  1. Скопируйте заметку из Telegram
  2. Создайте файл в папке techblog/
  3. Добавьте соответствующие теги
  4. Опубликуйте!