📜 ⬆️ ⬇️

Another CI traffic light. This time attiny2313 and Node.js

Inspired by such a great idea as a traffic light showing the state of the build, I decided that our team needed the same. I did not want to be guided by the articles I read because they use too expensive solutions for me. Therefore, armed with the idea, I sketched a very rough work plan for the team, and in my head I kept simple principles that I want to follow.



Under the cat there is a traffic light from color music and plastic bottles, a USB traffic light control module at attiny2313 for a dollar, as well as Jenkins polling software and a USB control for Node.js.

The project was divided into 3 parts

  1. Traffic light. Remove the traffic light from the crossroads or try to buy it, we were not going to. We did not set ourselves the goal to hang a real traffic light in the office, we are quite capable of making it from scrap materials.
  2. Electronics. I immediately dropped the Rasbery Pi and Arduino as redundant and expensive solutions. I needed access to the device via USB, I planned to use the USB power supply as a power source for traffic lights, that is, to save the circuit from additional power.
  3. Software for working with USB on the side of the computer. Task: polling Jenkins for the current status of a specific build and sending the corresponding message via USB.

')

Traffic light


They decided to make a traffic light out of the old color music, an even body and good glass - this is what you need. The peaks were cut from plastic bottles and painted together with the body in black. They got rid of the internal filling, because it worked from the mains voltage, and we need to redo it from 5v to USB.

Instead of lamps, I used LEDs that had previously been dropped from the LED strip. For greater brightness, I decided to make assemblies of 4 LEDs connected in parallel and limited the current flowing through them by resistors by 100h, the attiny2313 ports normally cope with a load of about 20mA.
Picture


In order to illuminate the glass of the traffic light completely, it was necessary to make reflectors. To do this, the top part was cut from plastic bottles and covered with aluminum adhesive tape from the inside. Glasses are mounted outside, so it was necessary to cut the bottle so that the diameter of the cutoff was slightly larger than the diameter of the glass hole, so the resulting reflector could be inserted from the outside and clamped with glass, which eliminated the need to think of mounting reflectors.
Picture


I attached the LED assemblies to the bottle caps using M3 bolts and tubes with an internal diameter of 2mm, so that it was possible to adjust the distance for greater brightness.
Picture


More pictures



Electronics


I had two AVR microcontrollers available: a cheaper attiny13 with 1kB of memory and a bit more expensive than attiny2313 with 2kB of memory. In order for the device to work with the USB protocol, I was not going to complicate the circuit with additional USB modules as this can be done directly on the MK using the excellent V-USB library.

The minimum memory size required by this library is about 1.3kB - so the choice fell on attiny2313. The price of this microcontroller is 1-2 dollars. It is also easy to buy it, since it is a very popular model.

Electronics circuit

The device scheme is very simple, for clarity, I will show my own, but as a guide I advise you to explore possible options , since here 3 options are proposed for schemes, their advantages and disadvantages are described:



The main part of the scheme is the strapping necessary for the computer to correctly determine the device as USB. It is necessary to change the voltage on the contacts D + and D- USB. The fact is that the voltage on the USB power contacts and the power of my circuit are 5v, but for signals on D + and D- the voltage should be 3.3v, for this purpose there are zener diodes on them. The connection of the LEDs is so only because I soldered without prior tracing, for the same reason I do not post the board drawings. There is also a speaker on the diagram, it is necessary to play the melody from this article when the build falls.
Ready USB Module



Principle of operation:

USB module is able quite a bit. Either shine with one LED, or blink one of them. The sound of a melody on a fallen build was not originally intended, but was added to an already working device by modifying the firmware.

After connecting the USB memory library to implement the necessary functionality, there was very little, so I decided to simplify the logic on the MK side as much as possible and put all the responsibility for the correct operation of the device on the shoulders of computer software. So I came to the following form of the protocol: the device will receive messages consisting of a two-digit number, where the first digit is the port pin, and the second is a flag indicating whether the LED should be on or blinking.

The launch of the melody occurs only when the build is dropped, in general, I have a hardcode there, I did not plan any configurations for it.

Firmware

I always write the firmware code on C in Eclipse, the gcc compiler from the WinAVR set, the firmware is uploaded with the USBasp programmer bought for aliexpress for $ 4.5, I flash the fusions with the Khazama program.

It is worth starting with fusion firmware. The circuit uses 12MHz quartz and for its use fyuzy must be set to quartz with a frequency greater than 8MHz (CKSEL = 1110). In addition, you need to remember to remove the frequency division by 8 (CKDIV8 = 1), if I'm not mistaken, the divider is attiny2313 by default, I repeat, it needs to be disabled (set one). Quartz at 12MHz is not the only possible option, read about the possible options at the bottom of this page . With such a small amount of memory, I had to choose 16MHz, but I decided to stay at 12MHz.

The first step is to use the template code of the USB library. The usbdrv folder of the library should be in the project folder.

#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; } 


This code is required for the USB library. In addition to this code, you also need to configure several parameters, for this you need the usbconfig-prototype.h file in the usbdrv folder, rename usbconfig.h and correct the necessary parameters, in my case it is:

 #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 


It is worth noting here that it is possible to use any legs of the MK, but USB_CFG_DPLUS_BIT should point to a leg supporting external interrupts, since signals from the USB port will come along this leg.

The method that processes the incoming data looks like this:

 #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; } 


This method works when the device receives data via USB, here I change state variables depending on the incoming data. Then these variables will be used in the main program loop.

Reading data comes from the value of wValue, not the most correct data transfer option, but I found it entirely appropriate in my case.

In my scheme, port D is used for the green signal of the traffic light, and port B is used for the yellow and red, so I had to declare a pointer to the port being used and add a condition to use port D if the signal for a successful build came. If for all signals to use different pins of one port, then this condition can be waived.

Main () method:

In addition to initializing the USB library, it is necessary to configure the ports for output, in my case you cannot configure the entire port D, since port D will be used by the USB library. I put edinichki only on pins that I will use:

 DDRD = 0x30; // 5  4  D DDRB = 0xC; // 2  3  B 


Main loop:

The main loop starts with the USB required:

 wdt_reset(); usbPoll(); 


Next comes the logic responsible for the glow / blinking of one of the LEDs:

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


If the blink flag is not set, then the unit is simply written to the required bit.
 *blinkPort |= (1 << pin) 
. Otherwise the required bit is inverted.
 *blinkPort ^= (1 << pin) 
In this way a blinking of the LED is obtained. The delay in the cycle is only 100us, so for a more rare blinking an artificial delay was added in the form of the blinkDelay counter variable.

Then comes the unit responsible for playing the melody:

 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++; } 


From the publication “Musical door bell in the style of Star Wars on the Arduino” only arrays of frequencies were taken and the length of the notes, as I mentioned, there was very little memory left for this, the melody playback process is integrated into the common loop so as not to disrupt the USB library. I decided not to make custom delays to set the desired frequency and length of notes.

I will try to describe the process as detailed as possible. In order to give the speaker the sound of the desired tone and duration, it is necessary to switch the state of the pin with the frequency of a given note and to do this for the duration of the duration of the specified note. In my case, this process occurs in the main loop and is executed in parallel with the remaining tasks. To do this, I consider the duration of the current note and its frequency (the constants here depend on the delay at the end of the main cycle and were selected manually):

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


While the duration is not reached, I switch the state of the pin, but not to each tick, but on condition that the current tick is divided without a balance by frequency, that is, in this way I set the desired frequency to achieve the desired tonality. Here I cheated a little, in my code I admit the remainder after division, I did it because some notes will sound the same because of the low accuracy of such calculations, which spoils the melody, and the remainder tolerance makes such notes different.

Soft


After the circuit is assembled, and the microcontroller is flashed (how and what to flash, look on the Internet of your city), you can go to the computer software.

There are plenty of options here. To work with DIY USB, a virtual COM port is very popular, the same Arduino creates a virtual COM port, which is quite simple to work with, you can work with it even with a regular bat file. But I really wanted to try working with USB, so my firmware is configured as a HID device, and software should be able to work with USB.

We write to USB

After a small investigation, it turned out that one of the simplest options is the USB package for Node.js. In order to send a signal about a dropped build, just write 4 lines:

 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){}); 


Here, the number 30 is sent by the wValue parameter to the device, and in turn, it will apply voltage to the third pin of port B and start playing the melody.

We learn the status of Joba Jenkins

With the Jenkins poll, Node.js is also no problem, we include the jenkins-api package and write:

 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); }); 


In the console we will see: SUCCESS, FAILURE or null (during the build process).

Windows driver

To work with the USB module, you need to install the libUSB driver, this can be done either manually or use the zadig program taken from the USB package page from the Installation item; For details, go there, there are instructions for Linux and OSX.

Together


Short video:



Full source here .

Thanks for attention.

Source: https://habr.com/ru/post/237039/


All Articles