четверг, 13 июля 2017 г.

Использование C-библиотек из Golang 1.8 с callback-функцией и cross-компиляция

Выяснил для себя, что писать системные программы на Golang гораздо приятнее, чем на С. Расплата - минимальный размер программы в 1 Мбайт, в отличие от десятков Кбайт на чистом C. Жалко, конечно, но C требует слишком много лишнего в современном программировании.
Поэтому перевожу сервер поддержки протокола DALI и управления освещением (под Raspberry) с C на Go.
Сложности: как я писал ранее, в сервере используется библиотека pigpio. Поэтому нужен вызов C из Golang. Но мало того, нужен callback на Go-функцию из C. Настоящий хардкор - все это хочется в cross-компиляции.

Пример на Go с вызовом C и Go-callback

В исходнике ниже три примера:
1) вызов функции setCallback C-библиотеки extclib из Go
2) передача в С указателя на Go-функцию goCallback
3) создание C-функции myCFunc прямо в Go-исходном файле с последующим вызовом ее из Go

package main
/*
// (1) подключение внешней C-библиотеки к линковке
// (1) объектный файл extclib, скомпилированный под целевую платформу,
// (1) а также extclib.h, должны быть доступны локально (см. ниже
// (1) "Настройка cross-компиляции")
#cgo LDFLAGS: -lextclib
// (1) подключение заголовочного файла внешней C-библиотеки
// (1) в нем описана функция setCallback (см. ниже)
#include <extclib.h>
#include <stdint.h>
// (2) описание callback-функции на Go в C, чтобы можно было передать в C указатель на нее
extern void goCallback(unsigned data);

#ifndef MYCINCL
#define MYCINCL 1
// (3) static inline - необходимо для предотвращения ошибки компиляции
// (3) о повторном объявлении функции myCFunc
static inline void myCFunc(uint32_t data){
// здесь C-код
}
#endif
*/
import "C"


// (1) вызов C-функции setCallback с передачей указателя на функцию Go
func installCallback(){
C.setCallback((*[0]byte)(C.goCallback))
}

// (2) ниже комментарий "//export goCallback" указывает Go, что функция будет видна из С
// (2) внимание! Пробелов между // и export быть не должно, иначе Go не сделает экспорт
//export goCallback
func goCallback(data C.uint){
}

// (3) вызов C-функции из Go
func callCFunc(data uint32){
C.myCFunc(C.uint32_t(data))
}

Настройка cross-компиляции

Для cross-компиляции необходимо локальное наличие компилятора под целевую платформу, заголовочных C-файлов, которые будут использоваться и объектных файлов для линковки (скомпилированных под целевую платформу).
Например, можно установить GNU Toolchain for Raspberry (http://gnutoolchains.com/raspberry/tutorial), который умеет забирать заголовочные и объектные файлы, установленные на Raspberry, себе для локальной cross-компиляции.

Дальше можно сделать bat-файл для компиляции или выставить переменные окружения.
Выставить путь к cross-компилятору, который находится внутри toolchain:
@set CC=arm-linux-gnueabihf-gcc

Установить архитектуру. Если этого не сделать, то получим ошибку вроде "unrecognized command line option '-m64'":
@set GOARCH=arm
@set GOARM=6
и операционную систему, иначе ошибка "unsupported GOOS/GOARCH pair windows/arm":
@set GOOS=linux

Выставить CGO_ENABLED, потому что по-умолчанию она выключена для cross-компиляции (и go build в этом случае молча игнорирует файлы, где есть import "C" - даже ошибки в них не проверяет):
@set CGO_ENABLED=1

Запустить сборку:
@call go build

В результате сборки получим список ошибок (как в Go, так и в C-коде) или готовый исполняемый файл, который можно запускать на Raspberry.

суббота, 20 мая 2017 г.

Выключатель света без фиксации из стандартного своими руками

Для продвинутого освещения дома нужны выключатели света без фиксации. То есть, которые после нажатия сами возвращаются в исходное состояние, как кнопка звонка. В остальном они ничем не отличаются от обычных, только купить их в Питере практически невозможно.
Тем более, если хочется иметь общий стиль выключателей, например, с розетками.

Я уже писал ранее, что в качестве двухклавишного выключателя без фиксации можно найти в продаже такой "для жалюзи". Однако, он стоит заметно дороже обычного. А с одноклавишными - беда.

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

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


Пружинки наденем на направляющую, которую согнем из толстой проволоки:


Она должна свободно входить в отверстия, по длине и толщине проволоки - не раскачиваться сильно по вертикальной оси, а по ширине - надежно упираться в снятую ранее черную качалку:

Надеваем две пружины от первых попавшихся авторучек. Одной не хватает жесткости, чтобы вернуть выключатель:

Вставляем направляющую с пружинами в отверстия и ставим качалку на место:


Если направляющая все же сильно болтается, следует закрепить ее на качалке тонкой проволочкой, под которую как раз подойдут имеющиеся на качалке выемки (в данном случае  не потребовалось):


Ставим на место крышку, и это все - теперь после отпускания выключателя, он сам надежно возвращается в исходное.

Двухклавишный выключатель делается заметно сложнее - в нем нет отверстий под направляющую проволоку.

четверг, 18 мая 2017 г.

Простой блок питания шины DALI

Особенность шины DALI такова, что она требует отдельного блока питания не только для работы слаботочных устройств (типа выключателей), подключенных к ней, но и для передачи сигнала вообще. Без питания шина сигналы передавать не может.

Особенность блока питания, связанная как раз со способом формирования сигнала, он должен выдавать ток, ограниченный 250 мА (с быстрой реакцией). Напряжение без нагрузки может составлять от 11.5 до 22.5 вольт. В продаже такие блоки, безусловно, есть. Для испытаний можно взять обычный блок питания и ограничить его ток достаточно мощным резистором.

Однако, чтобы без дела не сидеть (и экономия!), уже имея в освещении квартиры, как я писал ранее, блоки питания 24 вольта, можно собрать устройство самому. Для этого нужно 2 линейных стабилизатора, подходящей мощности и напряжения, и 3 резистора.


В интернете есть несколько сайтов со схемами включения и расчетом резисторов и мощности стабилизаторов на ИМС, например, LM317. Первая микросхема включена как стабилизатор напряжения и снижает напряжение с 24 до приблизительно 18 вольт (чтобы, с учетом падения на второй микросхеме, получить на выходе около 16 вольт), вторая включена как стабилизатор тока примерно на 250 мА. Получаем блок питания шины DALI с входным напряжением 20-40 вольт, выходным напряжением 16 вольт без нагрузки и ограничением тока 250 мА.

Что будет с шиной DALI если к ней не подключать питание? Она, во-первых, не будет передавать сигналы, во-вторых, устройства с питанием от шины работать не будут.
Что будет, если включить питание без ограничения тока? Сигналы передаваться не будут, могут сгореть выходные элементы передатчиков (поскольку они рассчитаны на ток до 250 мА, а при передаче замыкают шину DALI, т.е. создают короткое замыкание блоку питания).

среда, 19 апреля 2017 г.

Raspberry Pi 3, pigpio, шина DALI и программа для работы с ней

см. ранее Raspberry Pi 3, работа с GPIO в Linux и DALI

Ядро программы на C под Raspbian, которое умеет отправлять и принимать сообщения шины DALI, готово. Для подключения Raspberry к шине используется самодельный адаптер на GPIO. Для работы с GPIO - библиотека pigpio (почему именно она - см. ранее).

Ради организации полной проверки программы, в плане работы с сигналами, проверки адаптера и GPIO, учитывая описанные ранее сложности с задержками в многопотоковой среде, я реализовал процедуру поиска устройств на шине DALI по неизвестным длинным адресам - самый сложный алгоритм, который обязано выполнять любое устройство DALI-мастер.
Он подразумевает посылку множества команд в шину и получение ответов, причем от каждого ответа зависит все дальнейшее поведение алгоритма - при сбоях в приеме запросов устройствами или получении ответов весь алгоритм ломается. Вообще, программа похожа на бинарный поиск в отсортированном массиве из 16 миллионов адресов, причем последовательно обнаруживается устройство с самым меньшим значением адреса среди еще не найденных.

Поэтому с одной стороны, алгоритм наиболее интересен, с другой он работает только при стабильной работе шины, а с третьей, он необходим для подключения и конфигурирования каждого нового управляемого устройства.
За два вечера все заработало как надо (тестовый стенд на фото - два 4-х канальных диммера подключены к шине и дают 8 самостоятельных устройств DALI, каждое со своим адресом).

Raspberry в корпусе, адаптер, диммеры и сигнал DALI на экране:
посылка запроса и ответ от диммера

В процессе работы над программой пришлось преодолеть несколько трудностей. Первая - в библиотеке pigpio. Она показала себя как удобная, продуманная в плане интерфейса и вариантов использования, и работающая с малыми задержками. У меня заняло несколько дней, однако, чтобы разобраться в причине нестабильной работы всего комплекса - диммер через раз отвечал на мои однотипные запросы, а по осциллографу сигнал имел правильную форму.

Проблема оказалась в формировании сигнала в библиотеке pigpio: она требует указания ширины каждого импульса в микросекундах, выдавая при этом сигнал, по моим замерам, на 15% длиннее запрошенного. Из-за этого диммер не всего мог правильно расшифровать принятый фрейм. После учета этой коррекции программа заработала как часы. Проблема, думается, не зашита в код pigpio, а зависит от каких-то факторов, скажем, версии Raspberry (Pi 3), Raspbian (последняя на апрель 2017 года) или настроек каких-либо таймингов системы (я лично ничего не менял, кроме скорости работы SD-карты). Т.е. это ошибка, но проявляется не у всех.

Другая трудность - на нее я уже жаловался - недоступность полной документации по системе команд DALI и расшифровке ответов. Приходится комбинировать, часто неточную или неверную, информацию из нескольких источников, включая анализ доступного в интернете исходного кода программ.

понедельник, 17 апреля 2017 г.

Raspberry Pi 3, работа с GPIO в Linux и DALI

см. ранее Освещение на протоколе DALI и его компоненты

Raspberry привлекает не только как маленький и дешевый компьютер, но и наличием GPIO - цифровых интерфейсов ввода-вывода, и с первого взгляда похож на мощный микроконтроллер, вроде замены, Arduino, вкупе с Linux-сервером. Заманчиво, заманчиво...

Я приобрел Raspberry в качестве сервера заботливой ("умной") квартиры, имея ввиду подключение к нему шины управления светом DALI через простой самодельный адаптер, вероятных цифровых датчиков, вроде температуры, видеокамер, как датчиков движения и охраны, ну, и тому подобное.
И вот теперь, начиная писать под Raspbian (официальный Linux для Raspberry), я понимаю свое заблуждение про универсальность.

В общем, Raspberry с Raspbian производит приятное впечатление: работает сразу после копирования системы на SD-карту и подключения hdmi, загружается быстро. Замечая отсутствие хорошей документации, известную медленную работу с SD, пока не пошаманишь и слабый Wi-Fi, начинается понимание, что Raspberry - не зрелый продукт серьезной фирмы, а неплохая реализация неплохой идеи, но, "на коленке". Нет, я только "за" и поддерживаю кошельком, я скорее про завышенные ожидания.

Итак, возвращаясь к настоящей задаче - добавлению квартирному серверу функции управления освещением по интерфейсу DALI.

адаптер собран, к Raspberry подключен (2xGPIO и питание)

Для приема данных DALI нужно уметь считывать состояние цифрового входа не реже 1/2400 секунды или раз в 0,4 мс. Эта простейшая задача для любого копеечного микроконтроллера, однако, под Raspbian сталкивается с серьезными трудностями. Все дело в работающих параллельно моей программе процессах Linux kernel (даже при отсутствии других запущенных программ). Из-за них процесс, читающий состояние GPIO или, что правильнее, вызываемый по прерыванию на фронт сигнала, получает управление нерегулярно и с задержками. Для человека задержка процесса на 2 мс не заметна, и она допустима для настольных многозадачных систем, но для чтения сигнала скоростью более 250 бит/c - почти фатальна.

Начиная программировать на C под Raspbian я узнал, что встроенной в систему поддержки работы с GPIO как бы и нет. И нашел стороннюю библиотеку bcm2835. С ее помощью я проверил, что адаптер работает и сигналы DALI видит. Попытка написать процедуру чтения данных DALI разбилась о тот факт, что bcm2835 не поддерживает прерывания. Вообразите мое разочарование: любой микроконтроллер о шести ножках за 30 центов поддерживает прерывания и GPIO, а Raspberry Pi под официальной Raspbian с официальной поддержкой GPIO, не работает из коробки не только с прерываниями, но и с GPIO.

Новые поиски библиотеки работы с цифровыми входами навели на wiringPi, с прерываниями. И новый опыт показал затруднения программной работы с DALI из-за случайных задержек, описанных выше: при появлении на входе сигнала выставляется флаг прерывания, однако, при параллельно работающих процессах ядра, которые сами активно управляют прерываниями, моя функция периодически вызывается с задержкой, из-за которой часть данных DALI оказывается "не услышана". Если говорить о реальных тестах, то на Raspbian, где работает только программа прослушивания DALI, я получаю 5% ошибок из-за задержек до 2 мс (из которых 2% вполне реально исправить алгоритмом).

Третья по счету библиотека - pigpio - работает с GPIO через DMA (процесс работы с памятью, реализованный аппаратно, поэтому прерывания и другие сложности в ядре процессора ему не должны мешать). Библиотека обещает точность временных интервалов порядка 5 мкс. Такие времена вполне подходят для декодирования DALI. Только нужно понимать, что задержки в вызове программы никуда не денутся. Она будет получать все биты с цифрового входа и точное время появления каждого бита, но получать их с опозданием. Поэтому возможность реализации процесса, где после получения данных требуется ответить за определенный интервал времени, остается под вопросом, благо, в DALI такое поведение необходимо только исполнительным устройствам, но не мастер-контроллеру.

понедельник, 10 апреля 2017 г.

Освещение на протоколе DALI и его компоненты

см. ранее Дружелюбная квартира. Постановка задачи

Не помню уже, как я наткнулся на описание протокола DALI: управление освещением, открытый стандарт, соединение любой топологией, двумя проводами, по которым передается и управление, и маломощное питание (выключателей и т.п.). У всех принимающих устройств (регуляторов мощности освещения - диммеров) свой адрес.
И что важно, недорогое оборудование можно купить, и описание протокола и электрических схем, хотя и не всех, найти можно. У многих "открытых" стандартов есть своя особенность: документацию к ним публиковать запрещено, ее можно только купить, например, у "некоммерческой" организации IEC (издевка "демократического общества"). Поэтому внизу статьи полезные ссылки.

Источники света можно использовать разные, я прицеливаюсь на мощные светодиодные ленты и светодиодные лампы, если света от лент, расположенных на полочке под потолком и светящих в него, не хватит. Проверка в прихожей: лента 14 Вт/м, 5 метров, брошена буквой "Г" за карниз (т.е. только отраженный свет), светит достаточно ярко.



Что нужно для организации освещения на светодиодных лентах или низковольтных лампах? Лампы, блоки питания (вход 220 вольт, выход - постоянное напряжение, подходящее для ламп), диммеры (включение/выключение и управление яркостью по сигналу управления), выключатели (для ручного управления).

После небольшого изучения светодиодных лент, блоков питания, DALI-диммеров и кнопок, я выбираю следующие компоненты:
  1. недорогие светодиодные ленты "теплый белый" (warm white, WW), 14.4 Вт/м, 12 или 24 вольта;
  2. в спальне и гостиной хочется менять цвет освещения (кроме обычного, белого, скажем, цвет рассвета...) - светодиодные ленты WW+RGB (теплый белый + красный + зеленый + синий) суммарная мощность 28.8 Вт/м или 2 ленты (WW и RGB) на 24 вольта каждая
  3. блоки питания 24 вольта
  4. мощные DALI-диммеры постоянного тока (constant current) порядка 480 ватт при 24 вольтах, на 4 канала
Почему именно 24 вольта? Больше всего лент и лампочек в продаже на 12 вольт, гораздо меньше на 24 (кроме четырехцветных WW+RGB - их я видел только на 24) и еще меньше на 36. Однако, при выборе 24 вольта мы получаем: 
  • более мощные чем 12 вольт блоки питания, которые можно купить все еще без вентиляторов
  • подключение лент или ламп на 24 вольта, а также и на 12, соединяя их последовательно по 2 одинаковых штуки (ключевое слово - одинаковых). Да, одноцветные ленты на 12 вольт можно подключить к блоку питания 24 вольта, если разрезать их на 2 одинаковых отрезка и соединить последовательно (плюс первого к минусу второго, минус первого и плюс второго - к блоку питания или диммеру). Такое нельзя проделать с многоцветными лентами, потому что в них плюсовой провод общий, один на все каналы
  • диммеру постоянного тока почти все равно, каким напряжением он управляет, у него ограничение по току. Поэтому один и тот же диммер работает как с 12, так и с 24 вольтами, однако, во втором случае максимальная мощность подключенных ламп в 2 раза больше первого!
Нужно обратить внимание, что если я выбрал управление DALI, то и приобретаемые компоненты должны его поддерживать. Лентам и блокам питания протокол не интересен, а диммеры общаются с выключателями по DALI.
Почему диммеры по 4 канала? Так дешевле: 4-х канальный DALI-диммер имеет 4 адреса, поэтому работает так же, как 4 одноканальных. Одна независимо включаемая точка освещения одного цвета - это один канал (т.е. 3-х цветная RGB - это три канала), а сколько будет диммеров - один, два или три - не важно. Важно, сколько каналов. Лучше, однако, RGB-ленту подключать к идентичным каналам (т.е. к одному многоканальному диммеру или, если нескольким, то одинаковым), потому что кривая яркости канала может варьироваться по моделям.

Немного о функционале диммера. Его основное назначение - установка яркости ламп в соответствие с заданным значением (в DALI яркость задается числом от 0 до 254). У него также могут быть настройки, определяющие скорость нарастания или спада яркости при ее изменении, минимальная и максимальная яркость, яркость при включении питания. Настройки можно изменить с помощью устройства DALI мастер-контроллер, которое для обычной работы системы вообще-то не требуется. Кроме этого, у диммера может быть миниатюрный экран и кнопки для установки некоторых параметров вручную, что позволяет частично настроить его без мастер-контроллера и использовать. В диммерах бывает и дополнительный функционал, типа "Push dimming", который позволяет включать и выключать свет без интерфейса DALI. Хотя его наличие как-будто излишне, потому что можно просто подавать или не подавать питание на лампу, вообще не имея диммера.

Остались выключатели. Для DALI есть выбор выключателей, как правило, сенсорных, которые позволяют не только включать, но и управлять яркостью. Есть даже модели с выбором цвета освещения.
Однако, для экономии (и из-за нелюбви к сенсорным кнопкам), я приобретаю заметно более дешевые устройства, к которым на входе подключается кнопочный контакт, а на выходе имеем DALI:

Аккумулятор здесь для сравнения размера, питание коробочка получает от шины DALI. Итого, одна коробочка на 4 контакта. Есть несколько видов такого устройства "DALI Push Switch": управляющие адресами или группами DALI и т.п.
На первое нажатие свет включается, на втрое - выключается. На первое долгое нажатие - плавно увеличивает яркость, на второе долгое - уменьшает. Адрес диммера для управления выставляется вручную внутри коробочки (настраивается первый адрес, остальные 3 должны идти вслед за ним. В диммере же, как правило, адрес каждого канала настаивается независимо).
Единственно - нужны контакты без фиксации (которые сами возвращаются в исходное положение после отпускания кнопки). Например, без фиксации бывают кнопки для управления жалюзи:


04.2017 продолжение на подходе...

Ссылки:
1. Microchip AN1465 (pdf) - схема адаптера DALI<->GPIO и простого блока питания DALI, описание электрических сигналов DALI, тайминги, алгоритм кодирования, программы на C
2. Rayzig - самый полный список команд DALI, но с неинформативным описанием каждой команды, и описание формата фрейма
3. Rayzig.pdf - включает описание части протокола, в частности, процедуру назначение адресов устройств ("DALI address setting explained")
4. python-dali - программа на python, включает множество команд DALI и ответов (ответных фреймов) к ним
5. WAGO DALI/DSI Master Module - позволяет расшифровать часть специальных команд и ответов к ним
6. Beckhoff DALI-Commands - хорошее описание фрейма и команд, но нет специальных команд
7. NXP Motorola (DALI) Unit Using the MC68HC908KX8 - частичное описание DALI, в приложении список команд, программы на C
8. NEC DALI 78K0/Ix2 Series - схема адаптера, описание электрических сигналов DALI, программы на C
9. NXP AN10760 - pdf, описание таймингов, список команд, программы на C

пятница, 7 апреля 2017 г.

Дружелюбная квартира. Постановка задачи

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

Самое простое - свет в прихожей, который включается и выключается сам. Можно сделать на одном детекторе движения и освещения из магазина электрики. Получится не идеально, зато просто.

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

Итак, что хочется: чтобы квартира рутинные вещи делала сама, и чтобы со временем ее самостоятельность можно было расширять.
Что есть: квартира из нескольких комнат, экономный бюджет, некоторые навыки радиолюбителя, руки, большие навыки программирования, свободное время по выходным и желание творить.
Что должно получиться - первая цель: управление освещением в квартире выключателями (вручную) и автоматически. Ну, и удаленно (с телефона выключить забытый свет на кухне, когда ложишься спать).
Прицел на будущее: чтобы управлять можно было централизованно, скорее всего, через небольшой квартирный сервер. Прямо рисую в голове картинку: приходишь домой, и если никого больше нет, сама включается музыка, и квартирная душа говорит: "Я здесь, босс".
Ты ей: фильм буду смотреть. Она включает телевизор, если светло на улице - опускает жалюзи, и приглушает свет в гостиной...

04.2017 продолжение на подходе...