📜 ⬆️ ⬇️

Laser gun with two lasers focusing

image

The idea to make a laser gun, which suggests two beams at one point, came to me after the game Fallout: New Vegas. The prototype of my laser gun was a unique weapon , the Euclidean Algorithm , which strikes from the orbital station. The parallels between the two-beam laser gun and the orbital station are simple: I had two laser modules lying around, and the picture of the Euclidean Algorithm gave me the idea of ​​focusing two laser beams at one point to get more power.

Little pathos


I didn’t want to do “just a cannon” with focusing, and I decided to diversify it with additional ~~ pathetic ~~ options, like, for example, the voice of shooting, the display of a triangle, like the “Euclidean Algorithm” from the game, and that it changes depending on focusing distance of two lasers, as well as all sorts of little things like monitoring the supply voltage, etc. The result was that you can see in the photo and video:


')
Photo gun
image

image

image

Main part


The main thing in a laser gun, (probably) , lasers. As them, I used two laser modules, each up to 1.5 watts (since there was a bad battery in the gun, the burning power of the lasers was reduced). It was decided to leave one laser fixed and turn the second one using a servo. The result was this design:

image

image

Under the laser modules there was an ultrasonic distance meter with a maximum range of up to 4 meters. Of course, this is a small distance, but the gun still did not pretend to be anything more serious than an ordinary toy.

more about the work of the ultrasonic sensor
The ultrasonic distance meter works quite simply, the main thing is to start it and measure the length of the incoming pulse and then use the formula to convert the pulse length into a distance.

In my circuit, work with the sensor was organized on the AVR microcontroller using an external interrupt.

Attention! All code was written in C language in the CVAVR 3 environment.

//         CVAVR 3  mega128 #define HS PORTB.6 //       //   #define _TRUE 1 #define _ FALSE 0 unsigned int distance; //    unsigned char takt = 0; //   unsigned char isErrorHsFlag = _FALSE; //    ( ) unsigned char isFlag = _FALSE; //    (   ) //    1 ,   0xFFFF     //  ,   38 . //        16 . //   1  ,     interrupt [TIM1_OVF] void timer1_ovf_isr(void) { isErrorHsFlag = _TRUE; } //   interrupt [EXT_INT4] void ext_int4_isr(void) { takt++; switch (takt) { case 1: //   TCNT1 = 0; isErrorHsFlag = _FALSE; break; case 2: //   distance = TCNT1; takt = 0; isFlag = _TRUE; break; }; if (isErrorHsFlag == _TRUE) { //       distance = 0xFFFF; isErrorHsFlag = _FALSE; } } //    unsigned int getDistance(void) { unsigned char whileTakt = 0; //    ( ) //     . 0 HS = 0; //      takt = 0; isFlag = _FALSE; isErrorHsFlag = _FALSE; //    HS=1; delay_us(10); HS=0; whileTakt=0; //   while (isFlag == _FALSE) { whileTakt++; delay_ms(1); //     (   38 ) if (whileTakt > 38) { isFlag = _TRUE; //    }; }; return distance; }; 


So that the distance “did not make noise”, I decided to apply a median filter to the data of the ultrasonic sensor (the code below was found on the Internet). The only thing left to do was get the desired angle of rotation of the servo, which was calculated using the algorithm below under the spoiler (the code differs from the original one, since the original still has an account of customizable “additions” to the lengths of the triangle).

Median Filter Code
 //    #define NULL 0 #define STOPPER 0 /* Smaller than any datum */ #define MEDIAN_FILTER_SIZE 5 //   typedef struct pair{ struct pair *point; /* Pointers forming list linked in sorted order */ unsigned int value; /* Values to sort */ } PAIR_T; static PAIR_T small = {NULL, STOPPER}; static PAIR_T buffer[MEDIAN_FILTER_SIZE] = {0}; /* Pointer into circular buffer of data */ static PAIR_T *datpoint = buffer; /* Chain stopper */ /* Pointer to head (largest) of linked list.*/ static PAIR_T big = {&small, 0}; /* Pointer to successor of replaced data item */ struct pair *successor; /* Pointer used to scan down the sorted list */ struct pair *scan; /* Previous value of scan */ struct pair *scanold; /* Pointer to median */ struct pair *median; unsigned int i; unsigned int MedianFilter(unsigned int datum) { if (datum == STOPPER){ datum = STOPPER + 1; /* No stoppers allowed. */ } if ( (++datpoint - buffer) >= MEDIAN_FILTER_SIZE){ datpoint = buffer; /* Increment and wrap data in pointer.*/ } datpoint->value = datum; /* Copy in new datum */ successor = datpoint->point; /* Save pointer to old value's successor */ median = &big; /* Median initially to first in chain */ scanold = NULL; /* Scanold initially null. */ scan = &big; /* Points to pointer to first (largest) datum in chain */ /* Handle chain-out of first item in chain as special case */ if (scan->point == datpoint){ scan->point = successor; } scanold = scan; /* Save this pointer and */ scan = scan->point ; /* step down chain */ /* Loop through the chain, normal loop exit via break. */ for (i = 0 ; i < MEDIAN_FILTER_SIZE; ++i){ /* Handle odd-numbered item in chain */ if (scan->point == datpoint){ scan->point = successor; /* Chain out the old datum.*/ } if (scan->value < datum){ /* If datum is larger than scanned value,*/ datpoint->point = scanold->point; /* Chain it in here. */ scanold->point = datpoint; /* Mark it chained in. */ datum = STOPPER; }; /* Step median pointer down chain after doing odd-numbered element */ median = median->point; /* Step median pointer. */ if (scan == &small){ break; /* Break at end of chain */ } scanold = scan; /* Save this pointer and */ scan = scan->point; /* step down chain */ /* Handle even-numbered item in chain. */ if (scan->point == datpoint){ scan->point = successor; } if (scan->value < datum){ datpoint->point = scanold->point; scanold->point = datpoint; datum = STOPPER; } if (scan == &small){ break; } scanold = scan; scan = scan->point; } return median->value; } 


Code to calculate the angle of rotation of the servo
 #define CONST_RAD 5092.95817 //    unsigned int ac,bc,rad; //  ac bc    float bck; //    bc //  AC     ac = MedianFilter(getDistance()); if (ac > 50000) ac = 50000; //   //   ,       //  bc -     rad = atan(ac / (bc * bck)); //     pwmServo(CONST_RAD*rad); 


The sound of "pi-piu"


Of course, the real laser does not sound, but the gun will look much more effective if you add sound and some other functions, so I decided to implement a WAV player inside the MK by connecting a 4 GB flash memory card to it. The sound output was carried out through the PWM, while the PWM signal controlled the transistor, which already controlled the current through the speaker. I made the sounds myself in Fruity Loops 9 to create music.

WAV player code curve
 //         CVAVR 3  mega128 //    SD     FAT  : // #include <sdcard.h> // #include <ff.h> //   SD  static FRESULT f_err_code; // FRESULT    static FATFS FATFS_Obj; //  -   unsigned int ByteRead = 255; //      FIL fil_obj; // ,    char var[127]; //,    ,     . //       //      . //       ,  1   2. //  2     ,   - .  - " " //        16 . void openSnd(unsigned char nSnd) { switch (nSnd) { case 0: f_err_code = f_open(&fil_obj, "but_1.wav", FA_READ); //   "but_1.wav" break; case 1: f_err_code = f_open(&fil_obj, "but_2.wav", FA_READ); break; case 2: f_err_code = f_open(&fil_obj, "but_no.wav", FA_READ); break; case 3: f_err_code = f_open(&fil_obj, "but_ok.wav", FA_READ); break; case 4: f_err_code = f_open(&fil_obj, "but_sa.wav", FA_READ); break; case 5: f_err_code = f_open(&fil_obj, "but.wav", FA_READ); break; case 6: f_err_code = f_open(&fil_obj, "warning.wav", FA_READ); break; case 7: f_err_code = f_open(&fil_obj, "on.wav", FA_READ); / break; case 8: f_err_code = f_open(&fil_obj, "laz_sys.wav", FA_READ); break; case 9: f_err_code = f_open(&fil_obj, "laz_act.wav", FA_READ); break; case 10: f_err_code = f_open(&fil_obj, "laz_actk.wav", FA_READ); break; case 11: f_err_code = f_open(&fil_obj, "voice_s.wav", FA_READ); break; case 12: f_err_code = f_open(&fil_obj, "bat_full.wav", FA_READ); break; case 13: f_err_code = f_open(&fil_obj, "bat_at.wav", FA_READ); break; case 14: f_err_code = f_open(&fil_obj, "Bat_a.wav", FA_READ); break; case 15: f_err_code = f_open(&fil_obj, "zel_no.wav", FA_READ); break; case 16: f_err_code = f_open(&fil_obj, "new_1.wav", FA_READ); break; case 17: f_err_code = f_open(&fil_obj, "new_2.wav", FA_READ); break; case 18: f_err_code = f_open(&fil_obj, "new_3.wav", FA_READ); break; case 19: f_err_code = f_open(&fil_obj, "new_4.wav", FA_READ); break; case 20: f_err_code = f_open(&fil_obj, "new_5.wav", FA_READ); break; }; //    if (f_err_code & FR_OK) puts("FR_OK\r\n"); else if (f_err_code & FR_NO_FILE ) puts("FR_NO_FILE\r\n"); else if (f_err_code & FR_NO_PATH ) puts("FR_NO_PATH\r\n"); else if (f_err_code & FR_INVALID_NAME ) puts("FR_INVALID_NAME\r\n"); else if (f_err_code & FR_INVALID_DRIVE ) puts("FR_INVALID_DRIVE\r\n"); else if (f_err_code & FR_EXIST ) puts("FR_EXIST\r\n"); else if (f_err_code & FR_DENIED ) puts("FR_DENIED\r\n"); else if (f_err_code & FR_NOT_READY ) puts("FR_NOT_READY\r\n"); else if (f_err_code & FR_WRITE_PROTECTED ) puts("FR_WRITE_PROTECTED\r\n"); else if (f_err_code & FR_DISK_ERR ) puts("FR_DISK_ERR\r\n"); else if (f_err_code & FR_INT_ERR ) puts("FR_INT_ERR\r\n"); else if (f_err_code & FR_NOT_ENABLED ) puts("FR_NOT_ENABLED\r\n"); else if (f_err_code & FR_NO_FILESYSTEM ) puts("FR_NO_FILESYSTEM\r\n"); //   ,   if (f_err_code == 0) { //  1       var f_err_code = f_read(&fil_obj,var,44,&ByteRead); //   1    // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 16000,000 kHz TCCR1A = (0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<COM1C1) | (0<<COM1C0) | (0<<WGM11) | (0<<WGM10); TCCR1B = (0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10); DDRB.7 = 1; //  7   B   //     while(_TRUE) { TCNT1 = 0; //    f_err_code = f_read(&fil_obj,var,1,&ByteRead); OCR2 = var[0]; //    if (ByteRead == 0) break; //   ,  while (TCNT1 < 1000); // ,     . } } DDRB.7=0; //  .     . f_err_code = f_close(&fil_obj); //   //   1      // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 2000,000 kHz TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<COM1C1) | (0<<COM1C0) | (0<<WGM11) | (0<<WGM10); TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10); }; 


LCD Screen


Since once a friend presented me with an LCD screen LPH8731-3C, I decided to use it. In general, this was my first experience of using an LCD screen in my projects. All information on this screen and a library for working with it were found here .

Nutrition


Since the servo and microcontroller required a supply of 5 volts, I needed boosting DC-DC, as I planned to power the gun from a single Li-Pol battery. I already had DC-DC (on LM2621 chip), once for some reason I made them in the form of modules poured into epoxy resin:




As a battery charger was used scheme on linear stabilizers, found on the Internet.

Scheme


When it was already clear what to solder with, I decided to finally assemble the entire circuit as a multi-layered cake:



This “cake” was able to play WAV, display data on the LCD, interrogate the ultrasonic distance sensor, interrogate the voltage on the Li-Pol battery, interrogate the buttons, interrogate the potentiometer, control the operation of the lasers and control the servo drive.

Housing


The case was made of a metal box from the computer's power supply unit and a stainless steel sheet. The handle was made of foam and painted with black paint. In order to fasten the cover of the case, I had to put the nuts on the glue, since they could not be reached by hands inside the case.

"Gun Guts"


image

Eventually


Everything worked: the gun fired, focused to a point with a good setting of parameters. I also added a binary password when it was turned on, settings for all focusing parameters and mode settings that are saved in the microcontroller's ROM, as well as a battery discharge graph (which, by the way, showed how the battery sags when the lasers turn on). For lasers, I also made an additional two modes of operation, when one laser is working, and when two lasers work, maintaining a certain distance between them equal to the distance between the opponent's eyes at a distance from the target.

A bit of gun history.


I had one customer, whom I eventually refused to design his order. He tried to explain to me for a long time that I had lost very much, refusing his offer to work 20 thousand per month on a GPS tracker for money laundering for cars. Once he even had an explanatory conversation with me about what I want to achieve in my life, and that I “sleep with his idea.” Actually, when I posted the news about the gun on my page, he wrote under the photo that I was engaged in all garbage and in principle he was right. And later, thanks to the laser gun, the customer with the quests found me, and as a result I became a freelancer until I moved to another city and found a good job for myself. In general, thanks to the gun for advertising.

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


All Articles