Электрокарнизу Akko ac-72e по rs485

Понимаю что вопрос вышел за рамки WB, но мне и другим пользователям было бы полезно разобраться как управлять электрокарнизами по RS485, тем более что у всех аналогичное решение (akko, dooya, sunflower и т.д.) подозреваю что везде стоит двигатель dooya в перечисленных брендах.
Напомню скрипт:

#!/usr/bin/perl

use Device::SerialPort;
my $port = Device::SerialPort->new("/dev/ttyRS485-2");

$port->baudrate(9600); 
$port->databits(8);
$port->parity("none");
$port->stopbits(1);

$output = "";
if ($ARGV[0]==100) # команда Установить положение эл.карниза в 100% (0x64), тут широкофещательный адрес 0000
    {
        $output = pack("C*",0x55,0x00,0x00,0x03,0x04,0x64,0x35,0xFF);
    }
elsif ($ARGV[0]>=90) # .. в 90% (0x5A) далее адрес FEFE
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x5A,0x66,0xD9);
    }
elsif ($ARGV[0]>=80) # .. в 80%
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x50,0xE6,0xDE);
    }
elsif ($ARGV[0]>=70) # .. в 70%
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x46,0x67,0x10);
    }
elsif ($ARGV[0]>=60) # .. в 60%
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x3C,0xE6,0xF3);
    }
elsif ($ARGV[0]>=50)
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x32,0x67,0x37);
    }
elsif ($ARGV[0]>=40)
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x28,0xE6,0xFC);
    }
elsif ($ARGV[0]>=30)
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x1E,0x66,0xEA);
    }
elsif ($ARGV[0]>=20)
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x14,0xE6,0xED);
    }
elsif ($ARGV[0]>=10) # .. в 10%
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x0A,0x66,0xE5);
    }
elsif (@ARGV) # .. в 0%
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x03,0x04,0x00,0xE6,0xE2);
    }
else # считать текущее положение эл.карниза
    {
        $output = pack("C*",0x55,0xFE,0xFE,0x01,0x02,0x01,0x85,0x42);
    }

$port->write($output);
sleep(1);
$in = $port->read("100");
$per = substr($in,5,1);
printf ("%s\n",ord($per)); # вывести на экран ответ от эл.карниза

$port->close;

Скрипт запустил, но вот беда - он мне на все чтобы я ему не посылал возвращает положение двигателя и оно равно 0, т.е. проходит все условия скрипта perl и не срабатывает.
Как буд-то скрипт не видит аргументов которые ему даю.

root@wirenboard-A3PW5UD7:~# /usr/bin/akko.perl 20
0
root@wirenboard-A3PW5UD7:~# /usr/bin/akko.perl 100
0
root@wirenboard-A3PW5UD7:~# /usr/bin/akko.perl 35
0
root@wirenboard-A3PW5UD7:~# /usr/bin/akko.perl -35
0

К сожалению, ничего не могу подсказать вам в программировании на perl, но, думаю, вот этот вызов:

возвращает в $in ссылку на массив, а не текст из буфера.

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

if ($ARGV[0]==100) # команда Установить положение эл.карниза в 100% (0x64), тут широкофещательный адрес 0000
    {
        $output = pack("C*",0x55,0x00,0x00,0x03,0x04,0x64,0x35,0xFF);
	printf ("%s\n",$output);
    }

вижу результат:
root@wirenboard-A3PW5UD7:~# /usr/bin/akko.perl 100
Ud5▒0

Хорошо думаю, буду разбираться дальше что там в порт лезет и почему не срабывает привод.
Но вопрос у меня следующий:
почему при перемещение регулятора, не вызывается и не срабатывает скрипт perl, ведь я не вижу сообщений в консоле
%D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5

скрипт напоминаю:

defineRule("set_position", {
    whenChanged: "akko/position",
    then: function (newValue, devName, cellName){
        runShellCommand("./usr/bin/akko.perl "+newValue);
    }
});

Точка перед слэшем указывает на вложенную директорию. Убрать.

Сделал, но результата не увидел. Пробывал в явном виде отправить команду из скрипта, например
runShellCommand("/usr/bin/akko.perl 90");
но вывод не осуществляется в консоль, в то время когда с консоли отправляешь /usr/bin/akko.perl 90 в консоль валятся данные.

Есть кто готов разобраться до конца? Сам я с линкусом столкнулся только в WB, ощущения как у слепого на оргии.

1 лайк

Скрипт и консоль это два разных терминала. Откуда отправил, туда и пришёл ответ.
Из консоли отправляете, привод отрабатывает команду?

Привод работает когда ему подаем команду через com порт (USB), куда воткнут преобразователь RS485 - USB. Таким путем мы определили команды которые надо слать в последовательный порт и команды сосуществуют тем что описал автор в начале темы.
Но по какой-то непонятной причине посылая эти команды через perl привод не реагирует, ни какой реакции.

@costa, вы могли бы поделиться спецификацией на мотор? Мне приехали с алиэкспресса моторы Dooya dt82tv. Китайцы смогли дать документацию по rs485 только на китайском. Но содержимое очень похоже на ваши скриншоты, похоже, действительно, какой-то стандартный протокол используется.

Аналогичная просьба дать документацию, лежат эти моторы и не приложу у как их подключать.

Т.к. в скрипте не действуют настройки окружения консоли (пути и прочее), думаю, стоит попробовать следующую конструкцию:
runShellCommand("/usr/bin/perl /usr/bin/akko.perl "+newValue);

А чтобы получать результат вывода в переменную - можно попробовать так:

     runShellCommand("/usr/bin/perl /usr/bin/akko.perl "+newValue, {
        captureOutput: true,
        exitCallback: function (exitCode, capturedOutput) {
            log(capturedOutput);
        }
    });

Благодарю за участие, скрипт скорректировал. В результате в консоли ничего не появилось, в логе (var/log/messages) вывод также пуст.
Обратил внимание что когда в devices меняя положение карниза в виртуальном устройстве, на время (секунда не более) пропадает связь с WUI, после сама восстанавливается.

Думаю команде WB стоит взять один такой карниз да разобраться с ним основательно, написав понятную инструкцию что,как и почему. Потребители будут вам очень признательны.

Готов и далее ковыряться, только ума не приложу что и как делать

Ну как обычно ))0 Карниз есть в наличии могу выслать для опытов ))

Задачу решил, отпишусь позже подробно

1 лайк

Постараюсь подробно изложить все, глядишь через полгода и не вспомню.
Итак требовалось управлять 6 электрокарнизами sunflower KT82TV (в реальности это теже dooya, akko и т.д.). Управление через iridium, где у пользователя есть три кнопки для каждого привода - влево, вправо, стоп.

  1. Выше есть инструкция на английском, она рабочая. Мне мой китаец выслал только на китайском, но внешне видно что команды и логика одинаковые.

  2. Проверяем вообще что привода отвечают на команды, для этого я пользовался преобразователем интерфейса USB - 485, которых на ali также полно. Установил ПО Tik modscan https://www.tik.perm.ru/produkciya/programmnoe_obespechenie/tik_modscan/ и послал приводу команды. Команды привод принял, отработал.

  3. Как формируются команды (для лентяев кто не читает мануал). Например 5500000301E93C, где “55” всегда стоит в начале, 00 00 - адрес в данном случае широковещательный, 03 команда, 01 отрыть, E93С - CRC. Как определить CRC? Заходим в https://crccalc.com/ и забиваем команду 5500000301 переводим в CRC-16 HEX. Обратите внимание что байты надо будет поменять местами. т.е. результат у вас будет 3СE9, но когда будете собирать команду и подавать в привод ЗС и E9 надо поменять местами.

  4. Проверили что все работает локально. Заготавливаем себе таблицу для всех приводов
    %D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5

  5. Меняем адреса у приводов, для этого бежим по таблице и посылаем для каждого привода команду на изменение адреса: 55 00 00 02 00 02 01 01 9D 58 это команда для установки адреса 0101. Чтобы привод понял что вы хотите ему поменять адрес потребуется зажать кнопку на нем (4-5 сек), он моргнет 2 раза после чего посылаем команду указанную выше.

  6. В WB создаем виртуальное устройство и отправляем команду в perl скрипт

defineVirtualDevice("shades",{
    title: "shades",
    cells:{
        command: { //определили виртуальное устройство диапазон, у которого каждое значение является определенной командой в perl скрипте
            type: "range",
            value: 0,
            min: 0,
            max: 100,
        },      
    }
});

/*значение command
0 - открыть все шторы
1 - закрыть все шторы
2 - стоп все шторы
3 - открыть штора 1
4 - закрыть штора 1
5 - стоп штора 1
6 - открыть штора 2
7 - закрыть штора 2
8 - стоп штора 2
9 - открыть штора 3
10 - закрыть штора 3
11 - стоп штора 3
12 - открыть штора 4
13 - закрыть штора 4
14 - стоп штора 4
15 - открыть штора 5
16 - закрыть штора 5
17 - стоп штора 5
18 - открыть штора 6
19 - закрыть штора 6
20 - стоп штора 6
*/

defineRule("sunflower", { //название правила
  whenChanged: "shades/command",
  then: function (newValue, devName, cellName) { //выполняй следующие действия
     runShellCommand("/usr/bin/akko.perl "+ newValue); // вызываем perl скрипт и передаем код команды в атрибут
}
});
  1. Пишем perl скрипт
#!/usr/bin/perl

use Device::SerialPort;
my $port = Device::SerialPort->new("/dev/ttyRS485-2");

$port->baudrate(9600); 
$port->databits(8);
$port->parity("none");
$port->stopbits(1);

$output = "";
# Широковещательные команды для всех приводов
if ($ARGV[0] == 0) # команда открыть 5500000301E93C
    {
        $output = pack("C*",0x55,0x00,0x00,0x03,0x01,0xE9,0x3C);
    }
elsif ($ARGV[0] == 1) # команда закрыть 5500000302A93D
    {
        $output = pack("C*",0x55,0x00,0x00,0x03,0x02,0xA9,0x3D);
    }
# Адресные команды для привода с адресом 0101
elsif ($ARGV[0] == 3) # команда открыть 5501010301B900
    {
        $output = pack("C*",0x55,0x01,0x01,0x03,0x01,0xB9,0x00);
    }
elsif ($ARGV[0] == 4) # команда закрыть 5501010302F901
    {
        $output = pack("C*",0x55,0x01,0x01,0x03,0x02,0xF9,0x01);
    }
elsif ($ARGV[0] == 5) # команда стоп 550101030338C1
    {
        $output = pack("C*",0x55,0x01,0x01,0x03,0x03,0x38,0xC1);
    }
# Адресные команды для привода с адресом 0102
elsif ($ARGV[0] == 6) # команда открыть 55010203014900
    {
        $output = pack("C*",0x55,0x01,0x02,0x03,0x01,0x49,0x00);
    }
elsif ($ARGV[0] == 7) # команда закрыть 55010203020901
    {
        $output = pack("C*",0x55,0x01,0x02,0x03,0x02,0x09,0x01);
    }
elsif ($ARGV[0] == 8) # команда стоп 5501020303C8C1
    {
        $output = pack("C*",0x55,0x01,0x02,0x03,0x03,0xC8,0xC1);
    }
# Адресные команды для привода с адресом 0103
elsif ($ARGV[0] == 9) # команда открыть 550103030118C0
    {
        $output = pack("C*",0x55,0x01,0x03,0x03,0x01,0x18,0xC0);
    }
elsif ($ARGV[0] == 10) # команда закрыть 550103030258C1
    {
        $output = pack("C*",0x55,0x01,0x03,0x03,0x02,0x58,0xC1);
    }
elsif ($ARGV[0] == 11) # команда стоп 55010303039901
    {
        $output = pack("C*",0x55,0x01,0x03,0x03,0x03,0x99,0x01);
    }
# Адресные команды для привода с адресом 0104
elsif ($ARGV[0] == 12) # команда открыть 5501040301A901
    {
        $output = pack("C*",0x55,0x01,0x04,0x03,0x01,0xA9,0x01);
    }
elsif ($ARGV[0] == 13) # команда закрыть 5501040302E900
    {
        $output = pack("C*",0x55,0x01,0x04,0x03,0x02,0xE9,0x00);
    }
elsif ($ARGV[0] == 14) # команда стоп 550104030328C0
    {
        $output = pack("C*",0x55,0x01,0x04,0x03,0x03,0x28,0xC0);
    }
# Адресные команды для привода с адресом 0201
elsif ($ARGV[0] == 15) # команда открыть 5502010301B944
    {
        $output = pack("C*",0x55,0x02,0x01,0x03,0x01,0xB9,0x44);
    }
elsif ($ARGV[0] == 16) # команда закрыть 5502010302F945
    {
        $output = pack("C*",0x55,0x02,0x01,0x03,0x02,0xF9,0x45);
    }
elsif ($ARGV[0] == 17) # команда стоп 55020103033885
    {
        $output = pack("C*",0x55,0x02,0x01,0x03,0x03,0x38,0x85);
    }
# Адресные команды для привода с адресом 0202
elsif ($ARGV[0] == 18) # команда открыть 55020203014944
    {
        $output = pack("C*",0x55,0x02,0x02,0x03,0x01,0x49,0x44);
    }
elsif ($ARGV[0] == 19) # команда закрыть 55020203020945
    {
        $output = pack("C*",0x55,0x02,0x02,0x03,0x02,0x09,0x45);
    }
elsif ($ARGV[0] == 20) # команда стоп 5502020303C885
    {
        $output = pack("C*",0x55,0x02,0x02,0x03,0x03,0xC8,0x85);
    }
else # Широковещательныя команда стоп для всех приводов 550000030368FD
    {
        $output = pack("C*",0x55,0x00,0x00,0x03,0x03,0x68,0xFD);
	printf ("%s\n","stop");
    }	
$port->write($output);
sleep(1);
$in = $port->read("100");
$per = substr($in,5,1);
#printf ("%s\n",ord($per)); # вывести на экран ответ от привода

$port->close;
  1. Т.е. логика такова, что в иридиум когда пользователь нажимает определенную кнопку, я в shades/command получаю код команды, который далее передаю в perl. Это позволило уменьшить кол-во тегов (т.е. не создавать для каждой шторы свое устройство), кто работал с иридиум поймет.

  2. Скорей всего у вас ничего не заработает, т.к. WB коробочный и нужно с ним произвести определенный обряд.

  3. В консоли выполняем команду apt-get install build-essential

  4. В консоли выполняем команду perl -MCPAN -e 'install Device::SerialPort

  5. perl скрипт который я в своем случае разместил в /usr/bin/ надо дать права доступа, для этого выполняем команду chmod +x /usr/bin/akko.perl

  6. Если у вас на шине сидят только привода, то должно все заработать.

Что у меня не получилось решить: если в шине по мимо приводов находятся устройства WB, то при подаче команды на привод, привод у меня команду исполняет и перестает отвечать. Пока думаю докупить модуль расширения с еще одной шиной 485 и туда повесить китайские привода, если кто знает как это лечится или посоветует как провести диагностику буду благодарен.
Пост писал по памяти, возможно что-то упустил - пробуйте. Всем кто принимал участие огромное человеческое спасибо, без вас бы точно не смог!

Есть у кого-то мысли по шине, почему отваливается китаец если есть другие устройства 485?

5 лайков

А клавишу на стену выводили? выводили через непосредственное управление моторами? или через сухие контакты?

Все эти пляски вокруг привода для управления по RS485, если бы сухие контакты то проблем вообще нет, но на объекте закончились дискретные выхода, да и было как-то интересно разок решить вопрос, чтобы впоследствии не мучиться.

Тем не менее вопрос до конца не решен в части работы приводов и других устройств на одной шине.

Может я не так выразился, понятно что этот привод можно прикрутить к шине 485 с плясками и бубном и что остались вопросы. Но у привода есть два способа ручного управления через физическую кнопу. Первый это через 220 поочередно замыкая контакты, тут лучше кнопка без фиксации, пока держишь клавишу штора двигается. Второй способ на этом приводе есть сухие контакты, через которые можно управлять физически, но алгоритм работы такой, что один раз нажал и двигатель работает пока не нажмешь второй раз. Просто используя данные физические механизмы можно получать данные положения штор? Понятно что можно использовать сухие контакты и на WB. Просто у меня пока еще не чего не зашито и можно докинуть провода, хотелось бы понять принципиальное решение по управлению, или доложить а потом по месту разбираться как управлять и получать положения.

Схема управления по сухим контактам (логика с кнопкой фиксации или без) может быть изменена, правда сам не пробывал за ненадобностью, но тем не менее информация ниже может быть полезна:

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

Спасибо @costa и @somebody за выложенную документацию!
Сегодня смог подключиться к своим моторам и управлять ими по rs485 (также спасибо команде WirenBoard за удобную утилиту serial_tool). Правда пока только с компьютера пробовал.

Присоединюсь к просьбе к команде WirenBoard добавить официальную поддержку данных моторов. Это не какое-то редкое и дорогое устройство, а вполне массовый продукт. Сейчас электрокарнизы постепенно становятся популярнее и китайцы предлагают их по вполне доступным ценам. Было бы здорово иметь поддержку для них в прошивке, а не делать каждому свой “велосипед”.
Например вот карниз+мотор меньше 8тр на распродаже: https://ru.aliexpress.com/item/SILENT-motorized-curtain-track-smart-home-used-motorized-curtain-DOOYA-motor-DT82TV-FREE-SHIPPING/32634582958.html

Вряд ли скоро дойдут руки этим заняться. Чтобы написать поддержку, нужно нам купить этот мотор, разобраться как он работает и протестировать. Пока такой запрос не придёт от какого-нибудь крупного коммерческого заказчика, делать не будем.

С другой стороны, исходники wb-mqtt-serial открыты, можно дописать туда поддержку, зная протокол и немного C++.