Here's our circuit diagram.  You can easily switch in a 3V Trinket in place of one or both Gemmas.

Do not shine the laser into anyone's eyes! Especially for a prolonged period.

If you have any safety concern about the laser, then substitute in a super bright red 5mm LED and a 50 Ohm resistor.  I installed both and put connectors on them to switch back and forth.  The LED still looks pretty good.

First up, the servo control.  You may want to refer to the Laser Dog Goggles guide as it is based off that project but with a few tweaks:

  • The laser has been moved to the other Gemma device.  This because we wanted one switch to activate the motor and the other to activate lights.
  • I wanted a slower sweep speed. To do this I changed the moveAmount and servoPos variables from int to float, which opens up decimal increments. So I can use a moveAmount of 0.5
  • There is some code that makes the servo jump +/- 15 degrees at random. Why? Becuase we thought it looked creepier.

Here's the sketch:

/*******************************************************************
  SoftServo sketch for Adafruit Trinket. Increments values to change position on the servo 
  (0 = zero degrees, full = 180 degrees)
 
  Required library is the Adafruit_SoftServo library
  available at https://github.com/adafruit/Adafruit_SoftServo
  The standard Arduino IDE servo library will not work with 8 bit
  AVR microcontrollers like Trinket and Gemma due to differences
  in available timer hardware and programming. We simply refresh
  by piggy-backing on the timer0 millis() counter
 
  Required hardware includes an Adafruit Trinket microcontroller
  a servo motor, and a potentiometer (nominally 1Kohm to 100Kohm
 
  As written, this is specifically for the Trinket although it should
  be Gemma or other boards (Arduino Uno, etc.) with proper pin mappings
 
  Trinket:        USB+   Gnd   Pin #0  
  Connection:     Servo+  -    Servo1
 
 *******************************************************************/

#include <Adafruit_SoftServo.h>  // SoftwareServo (works on non PWM pins)

#define SERVO1PIN 0   // Servo control line (orange) on Trinket Pin #0

int skipCount = 100;  //These are variables for the random servo skip
int skip = 0;

float moveAmount = 0.5;  // change this value to change speed of servo
float servoPos = 0;  // variable for servo position

Adafruit_SoftServo myServo1;  //create servo object
   
void setup() {
  // Set up the interrupt that will refresh the servo for us automagically
  OCR0A = 0xAF;            // any number is OK
  TIMSK |= _BV(OCIE0A);    // Turn on the compare interrupt (below!)

  myServo1.attach(SERVO1PIN);   // Attach the servo to pin 0 on Trinket
  myServo1.write(90);           // Tell servo to go to position per quirk
  delay(15);                    // Wait 15ms for the servo to reach the position
}

void loop()  {

  skip = skip + 1;                      //counter for when the servo will a random jump
  if (skip == skipCount) {              
    servoPos = servoPos + random (-15,15);
    if (servoPos < 1) servoPos=1;       //this stops the servo from jumping outside its range
    if (servoPos > 119) servoPos=119;   //the servo goes crazy if this happens
    skip=0;
    skipCount=random (100,300);         //set the next interval for the servo to skip
  }

  myServo1.write(servoPos);             // tell servo to go to position
  servoPos = servoPos + moveAmount;     // increment servo position (value between 0 and 180) 
  if (servoPos == 0 || servoPos == 120){
    moveAmount = -moveAmount;           //reverse incrementer at bounds
  }
  delay(15);                            // waits 15ms for the servo to reach the position
}

// We'll take advantage of the built in millis() timer that goes off
// to keep track of time, and refresh the servo every 20 milliseconds
// The SIGNAL(TIMER0_COMPA_vect) function is the interrupt that will be
// Called by the microcontroller every 2 milliseconds
volatile uint8_t counter = 0;
SIGNAL(TIMER0_COMPA_vect) {
  // this gets called every 2 milliseconds
  counter += 2;
  // every 20 milliseconds, refresh the servos!
  if (counter >= 20) {
    counter = 0;
    myServo1.refresh();
  }
}

Now the Neopixels.  There's a are three things happening in the sketch:

  • D0 is driving the laser/LED which is usually on. But at random intervals will flicker.
  • At a set interval (in this case 3 seconds) one of the three pixels is selected at random and set to a random colour within an array.  This uses the millis() timer.
  • The pixel ring blinks a randomly selected pixel a random duration, picking a random colout from an array.

Yes that's a lot of use of the random() function.

You'll need the NeoPixel library of course.

Here's the sketch to upload to the second Gemma or Trinket:

#include <Adafruit_NeoPixel.h>

#define PIN 1

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(15, PIN, NEO_GRB);

uint32_t colorArray[3] = {0x10002C, 0x001000, 0x000010}; //the colours to use on the pixel ring
uint32_t colorArray2[4] = {0x500000, 0x005000, 0x000050, 0x505000}; //the colours to use for the neopixels

int howLongToWait = 3000;                // Wait this many millis()
int lastTimeItHappened = 0;              // The clock time in millis()
int howLongItsBeen;                      // A calculated value

int led = 0;
int flkr = 0;

void setup() {
  pixels.begin();
  pinMode(led, OUTPUT);
  digitalWrite(led, HIGH);
}

void loop() {
  uint8_t i;
  uint16_t d;

//pixel ring is on pixels 4-15
  i = random(3,15); //select a random pixel on the ring
  d = random(5, 250); //light it for a random duration
  pixels.setPixelColor(i, colorArray[random(3)]);
  pixels.show();
  delay(d);
  pixels.setPixelColor(i, 0);
  
//add some random blinks to the Laser Diode
  flkr = flkr + 1;
  if ( flkr >= 80) {  //arbitrary number raise/lower to adjust the frquency of blinks
    digitalWrite(led, LOW);
    delay (50);
    digitalWrite(led, HIGH);
    delay (25);
    digitalWrite(led, LOW);
    delay (50);
    digitalWrite(led, HIGH);
    flkr = 0;
  }

//millis timer code to cycle random colours to pixels 1-3  
  howLongItsBeen = millis() - lastTimeItHappened;
  if ( howLongItsBeen >= howLongToWait ) {
    pixels.setPixelColor(random(0, 3), colorArray2[random(4)]);
    pixels.show();
    // do it (again)
  lastTimeItHappened = millis();
  }
}

Test everything out, modify code if you want to alter the timing or behaviour. If everything checks out you're ready to put it all together.

This guide was first published on Nov 25, 2014. It was last updated on Nov 25, 2014.

This page (Prototyping and Coding) was last updated on Oct 17, 2021.

Text editor powered by tinymce.