Черговий CI світлофор. На цей раз attiny2313 і Node.js

Натхнений такою прекрасною ідеєю, як світлофор, що відображає стан білду, я вирішив, що нашій команді потрібен такий же. Я не хотів керуватися прочитані мною статтями, оскільки вони використовують занадто дорогі для мене рішення. Тому, озброївшись ідеєю, я накидав дуже приблизний план роботи для команди, а у себе в голові тримав прості принципи, яким хочу слідувати.



Під катом світлофор з світломузики і пластикових пляшок, USB модуль керування світлофором на attiny2313 за долар, а так само софт для опитування Jenkins і управління USB модулем на Node.js.

Проект був розділений на 3 частини
  1. Світлофор. Знімати світлофор з перехрестя або намагатися купити його ми не збиралися. Ми не ставили перед собою мету повісити в офісі справжній світлофор, нам цілком під силу зробити його з підручних матеріалів.
  2. Електроніка. Я відразу відкинув Rasbery Pi і Arduino як надлишкові і дорогі рішення. Мені потрібен був доступ до пристрою USB, я планироал використовувати напругу живлення USB в якості джерела живлення ламп світлофора, тобто позбавити схему додаткового живлення.
  3. Софт для роботи з USB на стороні комп'ютера. Завдання: опитування Jenkins поточного статусу певного білду та надсилання відповідного повідомлення по USB.


Світлофор

Світлофор вирішили робити з старою світломузики, рівна корпус і хороші скла — це те, що потрібно. Козирки були вирізані з пластикових пляшок і пофарбовані разом з корпусом в чорний колір. Від внутрішньої начинки позбулися, адже він працював від мережевої напруги, а нам потрібно переробити на 5в від USB.

Замість ламп я використовував світлодіоди, які попередньо выпаял з світлодіодним стрічки. Для більшої яскравості вирішив зробити складання, 4 світлодіода, з'єднаних паралельно і обмежив протікає через них струм резисторами за 100ом, порти attiny2313 нормально справляються з навантаженням близько 20мА.
Зображення

Для того, щоб скла світлофора висвітлювалися повністю, потрібно було зробити відбивачі, для цього від пластикових пляшок була відрізана верхня частина покрита зсередини алюмінієвим скотчем. Скла у нас кріпляться зовні, з цього різати пляшку потрібно було так, щоб діаметр окружності зрізу був трохи більше діаметра отвору для скла, так що вийшов відбивач можна було вставити зовні і затиснути склом що позбавило від необхідності придумувати кріплення відбивачів.
Зображення

Світлодіодні збірки я прикріпив до кришок від пляшок за допомогою болтів М3 і трубок з внутрішнім діаметром 2мм, зробив це для того, щоб була можливість відрегулювати відстань для більшої яскравості.
Зображення

зображення


Електроніка

В наявності у мене були два мікроконтролера AVR: більш дешевий attiny13 з 1kB пам'яті і трохи дорожче attiny2313 c 2kB пам'яті. Для того, щоб пристрій міг працювати з USB протоколом, я не збирався ускладнювати схему додатковими USB модулями так як це можна зробити прямо на MK за допомогою прекрасної бібліотеки V-USB.

Мінімальний розмір пам'яті, необхідний цій бібліотеці, дорівнює близько 1.3kB — тому вибір припав на attiny2313. Ціна цього мікроконтролера 1-2 долари, купити його теж не складе ніяких труднощів, так як це дуже популярна модель.

Схема електроніки
Схема пристрою дуже проста, для наочності покажу свою, але в якості керівництва раджу вивчити можливі варіанти, так як тут запропоновано 3 варіанти схем, наведено їх переваги та недоліки:



Основна частина схеми — це обв'язка, необхідна для коректного визначення комп'ютером пристрою USB. Вона необхідна для зміни напруги на контактах D+ і D — USB. Справа в тому, що напруга на контактах живлення USB і харчування моєї схеми дорівнює 5в, але для сигналів на D+ і D — напруга повинна бути 3.3в, для цього на них стоять стабілітрони. Підключення світлодіодів у мене розташоване так тільки тому, що я паяв без попереднього трасування, з цієї ж причини я не викладаю креслення плати. Так само на схемі є динамік, він необхідний для відтворення мелодії з цієї статті, коли падає білд.
Готовий USB модуль


Принцип роботи:
USB модуль вміє зовсім трохи. Або світити одним світлодіодом, або моргати одним з них. Звучання мелодії на що впав білд не було задумано спочатку, а було додано до вже працюючий пристрій модифікацією прошивки.

Після підключення USB бібліотеки пам'яті для реалізації необхідного функціоналу, залишалося дуже мало, тому я вирішив максимально спростити логіку на стороні МК і покласти всю відповідальність за правильну роботу пристрою на плечі комп'ютерного софту. Так я прийшов до наступної форми протоколу: пристрій буде приймати повідомлення, що складаються з двозначного числа, де перша цифра — це pin порти, а друга — це прапор, який вказує, має світлодіод горіти або моргати.

Запуск мелодії відбувається тільки при падінні білду, загалом у мене там хардкод, я не планував конфігурацій для нього.

Прошивка
Код прошивки я завжди пишу на C в Eclipse, компілятор gcc з набору WinAVR, прошивку заливаю програматором USBasp, купленому на алиекспресс за 4.5$, прошиваю фьюзы програмою Khazama.

Почати варто з прошивки фьюзов. У схемі використовується 12MHz кварц і для його використання фьюзы повинні бути встановлені на кварц з частотою більше 8MHz (CKSEL=1110). Крім того, потрібно не забути прибрати ділення частоти на 8 (CKDIV8=1), якщо я не помиляюся, в attiny2313 дільник встановлений по дефолту, повторюся, його потрібно відключити (встановити одиницю). Кварц на 12MHz — не єдиний можливий варіант, про можливі варіанти почитайте внизу сторінки. З таким малим кількістю пам'яті мені коштувало вибрати 16MHz, але я вирішив зупинитися на 12MHz.

Насамперед необхідно скористатися шаблонним кодом USB бібліотеки. Папка usbdrv бібліотеки повинна бути в папці проекту.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <util/delay.h>

#include "usbdrv/usbconfig.h"
#include "usbdrv/usbdrv.h"

PROGMEM const char usbHidReportDescriptor[22] = {
0x06, 0x00, 0xff,
0x09, 0x01,
0xa1, 0x01,
0x15, 0x00,
0x26, 0xff, 0x00,
0x75, 0x08,
0x95, sizeof(uchar),
0x09, 0x00,
0xb2, 0x02, 0x01,
0xc0
};

int main(void) {
wdt_enable(WDTO_1S);
usbInit();
usbDeviceDisconnect();

for(uchar i = 0; i<250; i++) { // wait 500 ms
wdt_reset();
_delay_ms(2);
}

usbDeviceConnect();

sei();

while(1) {
wdt_reset();
usbPoll();
_delay_us(100);
}

return 0;
}


Цей код необхідний для роботи USB бібліотеки. Крім цього коду необхідно налаштувати декілька параметрів, для цього потрібно файл usbconfig-prototype.h в папці usbdrv перейменувати в usbconfig.h і підправити необхідні параметри, в моєму випадку — це:

#define USB_CFG_IOPORTNAME D
#define USB_CFG_DMINUS_BIT 3
#define USB_CFG_DPLUS_BIT 2
#define USB_CFG_INTERFACE_CLASS 3
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 22


Тут варто відзначити, що є можливість використовувати будь-які ноги МК, але USB_CFG_DPLUS_BIT повинен вказувати на ногу, підтримує зовнішні переривання, так як по цій нозі будуть приходити сигнали з USB порту.

Метод, обробний прийшли дані, виглядає так:

#define PORT_D_THRESHOLD 40 // з якого числа починати використання порту D
#define SERIALIZE_DEVIDER 10 // 51 / 10 = пін 5 і моргання 1
#define PLAY_ON_STATUS 30 // 30 - це палаючий червоний, почати відтворення мелодії

volatile uint8_t *blinkPort = &PORTB;
uint8_t pin;
uint8_t isBlink;
uint8_t play;

USB_PUBLIC uchar usbFunctionSetup(uchar data[8]) {
usbRequest_t *rq = (void *)data; // cast data to correct type
uint8_t status = rq->wValue.bytes[0]; //40/41 - 20/21 - 30/31 (можливі значення)
// пишу 0 у всі використовувані мною піни очищаючи попередні стани
// щоб уникнути одночасного світіння декількох світлодіодів
PORTD &=~ ((1 << PD5) | (1 << PD4));
PORTB &=~ ((1 << PD2) | (1 << PD3));

// якщо в повідомленні піни 4 або 5, то будемо працювати з портом D
// інакше з портом B
if(status < PORT_D_THRESHOLD) {
blinkPort = &PORTB;
} else {
blinkPort = &PORTD;
}

// десериализация 
isBlink = status % SERIALIZE_DEVIDER;
pin = status / SERIALIZE_DEVIDER;
play = status % PLAY_ON_STATUS == 0;

return 0;
}


Цей метод відпрацьовує при отриманні пристроєм даних по USB, тут я міняю змінні стану в залежності від прийшли даних. Далі ці змінні будуть використовуватися у головному циклі програми.

Читання даних відбувається значення wValue, не самий правильний варіант передачі даних, але я вирішив його цілком доречним в моєму випадку.

У моїй схемі для зеленого сигналу світлофора використовується порт D, а для жовтого і червоного порт B, тому мені довелося оголосити покажчик на використовуваний порт і додати умову для використання порту D, якщо прийшов сигнал про успішне билде. Якщо для всіх сигналів використовувати різні піни одного порту, то від цієї умови можна відмовитися.

Метод main():
Крім ініціалізації USB бібліотеки необхідно налаштувати порти на output, в моєму випадку не можна налаштовувати весь порт D, тому що порт D буде використовуватися USB бібліотекою. Я виставив одинички тільки на пинах, які буду використовувати:

DDRD = 0x30; //піни 5 і 4 порти D
DDRB = 0xC; //піни 2 і 3 порти B


Main loop:
Головний цикл починається з необхідних для роботи USB:

wdt_reset();
usbPoll();


Далі йде логіка, що відповідає за світіння/моргання одного з світлодіодів:

blinkDelay++;
if(blinkDelay > BLINK_DELAY) {
blinkDelay = -BLINK_DELAY;
}

if(isBlink == 0) {
*blinkPort |= (1 << pin);
} else {
if(blinkDelay == 0) {
*blinkPort ^= (1 << pin);
}
}


Якщо прапор моргання не встановлений, то просто пишеться одиниця в необхідний біт
*blinkPort |= (1 << pin)
. Інакше необхідний біт інвертується
*blinkPort ^= (1 << pin)
, таким чином виходить моргання світлодіодом. Затримка в циклі всього 100us, тому для більш рідкісного моргання додано штучна затримка у вигляді змінної-лічильника blinkDelay.

Далі йде блок відповідає за відтворення мелодії:

if(play == 1) {
if(soundDelay < duration) {
if(soundDelay % frequency <= FREQUECY_EDGE) {
PORTD ^= (1 << PD5);
}
} else {
soundDelay = 0;
if(note == NOTES_COUNT) {
note = 0;
play = 0;
}

duration = pgm_read_word_near(durations + note) * DURATION_MULTIPLIER;
frequency = FREQUENCY_DEV / pgm_read_word_near(frequences + note);

note++;
}

soundDelay++;
}


З публікації «Музичний дверний дзвінок в стилі Star Wars на Arduino» були взяті тільки масиви частот і довжина нот, як я вже згадував, пам'яті залишалося дуже мало по цьому процес відтворення мелодії інтегрований у загальний цикл, щоб не порушити роботу USB бібліотеки. Я вирішив не робити кастомних затримок для визначення потрібної частоти і довжини ноти.

Спробую описати процес як можна детальніше. Для того, щоб подати на динамік звук потрібної тональності і тривалості, необхідно перемикати стан піна з частотою заданої ноти і робити це протягом часу тривалості заданої ноти. У мене цей процес відбувається в основному циклі і виконується паралельно іншим завданням. Для цього я вважаю тривалість поточної ноти та її частоту (константи тут залежать від затримки в кінці головного циклу і були підібрані вручну):

duration = pgm_read_word_near(durations + note) * DURATION_MULTIPLIER;
frequency = FREQUENCY_DEV / pgm_read_word_near(frequences + note);


Поки тривалість не досягнута, перемикаю стан піна, але не на кожен тик, а за умови, що поточний твк ділитися без остачі на frequency, тобто, таким чином я ставлю потрібну частоту для досягнення потрібної тональності. Тут я трохи схитрував, у мене в коді допускаю залишок після ділення, зробив це тому, що деякі ноти будуть звучати однаково з-за низької точності розрахунків, що сильно псує мелодію, а допуск за залишком робить такі ноти різними.

Софт

Після того, як схема зібрана, а мікроконтролер прошитий (як і чим прошивати шукайте в інтернетах вашого міста), можна перейти до комп'ютерного софту.

Варіантів тут предостатньо. Для роботи з DIY USB великою популярністю користується віртуальний COM порт, та ж Arduino створює віртуальний COM порт, працювати з яким досить просто, з ним можна працювати навіть звичайним bat-файлом. Але мені дуже хотілося спробувати роботу саме з USB, тому моя прошивка налаштована як HID пристрій, і софт повинен вміти працювати саме з USB.

Пишемо в USB
Після невеликого розслідування виявилося, що один з найпростіших варіантів — це пакет USB Node.js. Для того, щоб надіслати сигнал про падіння билде, достатньо написати 4 рядки:

var usb = require('usb');
var device = usb.findByIds(5824, 1500); // VID і PID нашого пристрою (ці стоять за замовчуванням в V-USB конфіги)
device.open();
device.controlTransfer(usb.LIBUSB_REQUEST_TYPE_RESERVED, usb.LIBUSB_TRANSFER_TYPE_CONTROL, 30, 0, new Buffer(0), function(error, data){});


Тут число 30 надсилається параметром wValue на пристрій, а той у свою чергу подасть напругу на третій pin порти B і почне програвання мелодії.

Дізнаємося статус джобы у Jenkins
З опитуванням Jenkins у Node.js теж ніяких проблем, підключаємо пакет jenkins-api і пишемо:

var jenkinsapi = require('jenkins-api');
var JOB_NAME = 'JOB';
var JENKINS_URL = 'URL';
var jenkins = jenkinsapi.init(JENKINS_URL);

jenkins.last_build_info(JOB_NAME, function(err, data){
console.log('Статус білду', data.result);
});


В консолі побачимо: SUCCESS, FAILURE або null (у процесі складання).

Драйвер для Windows
Для роботи з USB модулем необхідно встановити драйвер libUSB, це можна зробити вручну або скористатися програмою zadig, взятої зі сторінки пакета USB з пункту Installation; за подробицями — туди, там же інструкції для Linux і OSX.

Всі разом

Коротке відео:



Повні вихідні коди тут.

Дякую за увагу.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.