title image

Ghost Busters Ghost Trap  

Youtube

Op deze pagina kun je alle informatie vinden over de bouw van mijn Ghost Busters Ghost Trap

Ghost Trap

Downloads en links

Link naar mijn tinkercad ontwerp.
Download mijn CAD bestanden.
Download mijn Arduino Sketch (voor arduino Nano).
Download mijn geluidseffecten

Onderdelen

Hout
Ik heb hout gebruikt wat ik nog had liggen.
- 12mm multiplex
- 5mm multiplex - 6mm mdf
- 4mm mdf

Aluminium
Voor het handvat gebruikte ik stukjes alluminium uit de afvalbak op het werk.
Je zou deze onderdelen ook prima uit hout kunnen maken of 3d printen.
De afmetingen zijn (bij mij). 19mmx19mm voor het vierkante deel, 20mm voor het ronde handvat.

De aluminium plaatjes voor de zijkanten en de voorkant kocht ik bij www.aluminiumopmaat.nl.
De plaatjes zijn 1.5mm dik. Ik gebruikte geanodiseerde plaat en geslepen plaat (RVS look)


Verf en lijm
Grondverf van Action
Spuitbussen zwart, geel en zilver
Ik gebruikt de volgende merken en kleuren: Spraytone Hoogglans Geel, Spraytone Zijdeglans Zwart, Hammerhite metaalverf zilver.
De zilvere verf is alleen voor het koelblokje op de zijkant. Als je dat zou printen in een grijze kleur zou dat er volgens mij ook prima uitzien.
spuitbussen
Ik gebruikte Houtlijm (patex van Action) en 5 minuten epoxylijm van bizon.

3d geprinte onderdelen
Een aantal onderdelen zijn geprint, zoals het koelblok plaatje (vector plate), de servo armpjes, de kabelgoot en een paar knopjes.
Open en kopieer het tinkercad ontwerp om die dingen te downloaden.

Arduino Nano
Dit is een oude bekende.Deze moet je altijd in huis hebben.
Klik op het plaatje voor een link naar Tinytronics
Arduino Nano


Sound board
Voor de geluidseffecten gebruik ik deze kleine MP3 speler met ingebouwde versterker.
Pas op, de opslagcapaciteit is beperkt maar groot genoeg voor dit project:
Klik op het plaatje voor een link naar Tinytronics
Sound Board


luidspreker
Klik op het plaatje voor een link naar Tinytronics
Speaker

OLED Display
Dit is een OLED display van 128x32 pixels (erg klein dus). De aansturing gaat via I2C.
Ze zijn in meerdere kleuren te krijgen. Ik heb gekozen voor GEEL
Ik kocht dit display op AliExpress.
OLED display

Accu
Ik kocht deze accu op AliExpress, het model is 104050. Het is een 3.7Volt LI-PO accu.
Li-Po Accu

Accu Management en boost converter
De accu werkt op 3.7 volt maar de rest van de schakeling werkt op 5 volt. Ik heb dus een boost converter nodig om de 3.7 volt van de batterij om te vormen tot 5 volt.
Dit printje maakt het ook mogelijk om een externe voeding aan te sluiten om de batterij op te laden.
Zoektermen op AliExpress : "3.7 to 5v charge discharge"
Booster

Servos
Ik gebruik SG90 servos. In totaal 2 stuks. Het plaatje is weer een link naar Tinytronics
Servo


Neopixel LED rings
Dit zijn de Neopixel LED rings die ik gebruikte (4 stuks):
Klik op het plaatje voor een link naar de winkel
LED Ring
Schakelaar, potmeter en lampjes
De schakelaar is een standard toggle switch - MTS-102
Toggle switch

Er zit één potmeter in de ghost trap, ik gebruikte deze.
De potmeter heeft een waarde van 10Kohm.
potmeter

Het is lastig om een passend lampje te vinden op 5 volt.
Ik kocht dit lampje op AliExpress. De omschrijving is :"7mm plastic Indicator lights"

7mm Lampje

Dit lampje kun je bestellen voor een spanning van 12 volt, 24 volt of 230 volt; Maar niet voor 5 volt. Ik heb het lampje dus gesloopt en er een LED ingebouwd. Ze verkopen dit lampje ook bij Baco army goods in IJmuiden (een bezoek waard).

Wieltjes
De wieltjes zijn POM kogellager rollers, deze worden gebruikt in sommige 3d printers.
Wheels


Oplader en spoel
Deze oplader kocht ik bij kruidvat, het is een usb-c qi lader.
Qi Oplader


Om draadloos laden mogelijk te maken heb ik deze spoel (met printje) gekocht bij AliExpress.
Qi Spoel



Het schema

Hier is mijn schema. Klik op het plaatje voor een grotere versie
Scheme


Test Programma's

In de videoserie test ik verschillende onderdelen, hier kun je die testprogramma's terug vinden.

Servo Test
In de tweede video in deze reeks liet ik zien hoe je servo's kunt aansturen
Hiervoor moet je de Servo bibliotheek hebben toegevoegd in Arduino.
Dat doe je via bibliotheek beheer of library manager in het Engels.
// Servo test programma voor de Ghost Trap
// voor het schema, zie www.bartvenneker.nl

#include <Servo.h>

Servo Rdoor;
Servo Ldoor;

void setup() {
  
}

void loop() {
  DoorsOpen();
  delay(1000);
  DoorsClose();
  delay(1000);
}

void DoorsOpen(){
  Rdoor.attach(2,544,2550); // rechter servo op arduino pin 2
  Ldoor.attach(3,544,2500); // linker servo op arduino pin 3
  Rdoor.write(180);
  Ldoor.write(30);
  delay(500);
  Rdoor.detach();  
  Ldoor.detach();  
}

void DoorsClose(){
  Rdoor.attach(2,544,2500); // rechter servo op arduino pin 2
  Ldoor.attach(3,544,2550); // linker servo op arduino pin 3
  Rdoor.write(30);
  Ldoor.write(180);
  delay(500);
  Rdoor.detach();  
  Ldoor.detach();  
}



Display test 1, de fake led array
In de derde video laat ik zien dat ik de led array simuleer met een OLED display.
Daarvoor maakte ik eerst een testprogramma, zie hier..
Voor deze sketch moet je de Adafruit SSD1306 bibliotheek en de Adafruit GFX bibliotheek hebben toegevoegd in Arduino.
Dat doe je via bibliotheek beheer of library manager in het Engels.
// Test programma voor de LED Array in de Ghost trap.
// Voor schema, zie www.BartVenneker.nl

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define yellowled A3    // de dikke gele LED zit op A3

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(128, 32, &Wire, -1); // mijn oled display is 128x32 pixels groot

void setup() {
  pinMode(yellowled,OUTPUT);
  digitalWrite(yellowled,LOW);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32  
    for(;;); // Don't proceed, loop forever
  }

  // Clear the display buffer 
  display.clearDisplay();
  display.display();
  
}

void loop() {
  // simulate the led bar array
  for (int y=0 ; y<11; y++){
    ledbar(y);  
    delay(100);
  }

  // activate the big yellow led.
  digitalWrite(yellowled,HIGH); 
  
  delay(2000);

  // reset the led bar
  ledbar(0);
  
  // deactivate the big yellow led
  digitalWrite(yellowled,LOW); 
  delay(5000);
}

void ledbar(int b){
  // Deze void tekent een aantal (aantal = b) afgeronde rechthoekjes
  // op het scherm 
  
  if (b > 11) {b=11;}  // maximaal 11
  
  
  int x=4; // de startpositie is 4 (4 pixels in de x richting)
  
  display.clearDisplay();
  for (int v = 0; v < b; v++){
    display.fillRoundRect(x+(v*12) , 0, 9, 32,3, SSD1306_INVERSE);
  }  
  display.display();
}
  



Display test 2, de batterij
In de laatste video bouw ik de oplaadfunctie en toon ik hoe ik de batterij op het display animeer. Hier is dat programma.
Ook hiervoor moet je de Adafruit SSD1306 bibliotheek en de Adafruit GFX bibliotheek hebben toegevoegd in Arduino.
Dat doe je via bibliotheek beheer of library manager in het Engels.
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(128, 32, &Wire, -1); // mijn display is 128x32 pixels

void setup() {
  // Initialize the display:
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32  
    for(;;); // Don't proceed, loop forever
  }

  display.clearDisplay();   // Clear the display buffer
  display.display();        // transfer the buffer to screen
}

void loop() {
  // draw the battery
  display.clearDisplay();
  display.drawRoundRect(20 , 0, 60, 32,5, SSD1306_WHITE);
  display.drawRoundRect(21 , 1, 58, 30,5, SSD1306_WHITE);
  display.drawRect(80 , 10, 8, 14, SSD1306_WHITE);
  display.drawRect(80 , 11, 7, 12, SSD1306_WHITE);
  
  display.drawFastVLine(80,12,10,SSD1306_BLACK);
  display.drawFastVLine(79,12,10,SSD1306_BLACK);
  display.drawFastVLine(78,12,10,SSD1306_BLACK); 
  
  display.display();

  delay(500);
  // Fill the battery
  for (int x = 22; x < 78 ; x++){
    display.drawFastVLine(x,2,28,SSD1306_WHITE);
    delay(50);
    display.display();
    }

  for (int x = 78; x < 86 ; x++){
    display.drawFastVLine(x,10,12,SSD1306_WHITE);
    delay(50);
    display.display();
    }
    
  delay(1000);

  // Display FULL inside battery
  display.setCursor(27, 10);
  display.setTextColor(SSD1306_BLACK);
  display.setTextSize(2);
  display.print("FULL");
  display.display();
  delay(3000);
  
}



Sound Test
In de ghost trap zit een geluidsbordje dat kleine geluidseffecten kan afspelen
Het bordje maakt gebruik van een seriele interface en is dus eenvoudig aan te spreken... dat wil zeggen, als de commando's niet zo onnodig ingewikkeld waren
In dit test programma geef ik je twee eenvoudige procedures: een om het volume in te stellen en een om een geluid af te spelen. Wat heb je nog meer nodig?

// Testprogramma voor het sound bordje
// voor meer info en schema, www.BartVenneker.nl

#include <SoftwareSerial.h>

SoftwareSerial Serial1(0, 12); 

void setup() {
 Serial1.begin(9600);
 volume(18);  //Volume settings 0 - 30
}

void loop() {
 play(1);  // Speel de eerste track 
 delay(5000);
 
 play(2); // Speel de tweede track
 delay(5000);
}


void play(unsigned char Track){
 unsigned char play[6] = {0xAA,0x07,0x02,0x00,Track,Track+0xB3};
 Serial1.write(play,6);
}


void volume( unsigned char vol){
  unsigned char volume[5] = {0xAA,0x13,0x01,vol,vol+0xBE};
  Serial1.write(volume,5);
}





Versie 1.5 met voetpedaal ondersteuning
26-02-2023: Nieuwe firmware met ondersteuning voor een voetpedaal.

In deze video laat ik zien hoe je een voetpedaal zou kunnen toevoegen aan de Ghosttrap.



Schema Hier is mijn nieuwe schema. Klik op het plaatje voor een grotere versie
Schema

Arduino Code
// This is the arduino software for my Ghost trap.
// version 1.5
// See www.BartVenneker.nl for the schematic and more info

#include <Servo.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_NeoPixel.h>

#define yellowled A3
#define redled 6
#define NeoPixelsPin 8
#define NUMPIXELS 32

SoftwareSerial Serial1(0, 12); // Rx=not used, Tx=12

Adafruit_NeoPixel pixels(NUMPIXELS, NeoPixelsPin, NEO_GRB + NEO_KHZ800);

Adafruit_NeoPixel pixels2(8, 13, NEO_GRB + NEO_KHZ800);

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(128, 32, &Wire, -1);

Servo Rdoor;
Servo Ldoor;

unsigned long playtime;
boolean fullBattery = false;
boolean footpedal = false;

void setup() {
  //Serial.begin(9600);
  pinMode(yellowled,OUTPUT);
  digitalWrite(yellowled,LOW);

  pinMode(redled,OUTPUT);
  digitalWrite(redled,HIGH);
   
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  pixels.clear(); // Set all pixel colors to 'off'
  pixels.show();

  pixels2.begin();
  pixels2.clear();
  pixels2.show();
  
  Serial1.begin(9600);
  volume(0);  //Volume settings 0 - 30
  DoorsClose(100);
  delay(100);

  // initialize the display.
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32  
    for(;;); // Don't proceed, loop forever
  }

  // Clear the display
  display.clearDisplay();
  display.display();
  
  // Find out if the knobs are set to charge mode
  while (inChargeMode()){
    charging();  
  }
  
  display.clearDisplay();
  display.display(); 

  Serial.begin(9600);

  if (analogRead(A1) > 1000) footpedal=true;

}

void drawBattery(boolean clearFirst){
   // Draw the battery
  if (clearFirst) { display.clearDisplay(); }
  display.drawRoundRect(20 , 0, 60, 32,5, SSD1306_WHITE);
  display.drawRoundRect(21 , 1, 58, 30,0, SSD1306_WHITE);
  display.drawRect(80 , 10, 8, 14, SSD1306_WHITE);
  display.drawRect(80 , 11, 7, 12, SSD1306_WHITE);
  if (clearFirst){
    display.drawFastVLine(80,12,10,SSD1306_BLACK);
    display.drawFastVLine(79,12,10,SSD1306_BLACK);
    display.drawFastVLine(78,12,10,SSD1306_BLACK);  
  }
  display.display();
}

void chargeAnimation(){
  // charging the battery:
  for (int x=22;x< 78 ; x++) {
    display.drawFastVLine(x,2,28,SSD1306_WHITE);
    display.display();
    delay(50);
    }
  for (int x=78; x< 86; x++){
    display.drawFastVLine(x,10,12,SSD1306_WHITE);    
    display.display();
    delay(50);
    }  
    delay(1000);
  }  

void drawFullBattery(){
  for (int x=22;x< 78 ; x++) {
    display.drawFastVLine(x,2,28,SSD1306_WHITE);        
    }
  for (int x=78; x< 86; x++){
    display.drawFastVLine(x,10,12,SSD1306_WHITE);            
    }  
  display.setCursor(27, 10);
  display.setTextColor(SSD1306_BLACK);
  display.setTextSize(2);
  display.print("FULL");
  display.display();
  delay(500);
}

boolean inChargeMode(){
  // Get the average of 10 data points.
  long b = 0;
  for (int x=0; x< 10 ; x++){
     int c = analogRead(A2);
     b +=  c;
     delay(100);
  }
  b = b / 10;
  if (b < 100) {return true;}
  return false;
}

  
void charging(){
  // pin A0 is attached to the charge led on the charge regulator
  // the green charge led is removed
  // See www.BartVenneker.nl for more info
  
  int a = analogRead(A0);


  // a < 300 = No charger attached
  // a > 300 = Charging
  // a > 600 = Full

  if (a < 300) {
    // No charger attached, just show empty battery
    drawBattery(true);
    fullBattery = false;
    return;    
    }

  // overwrite the messurement when we have seen that the battery is full.
  if (fullBattery) {a = 700;}
   
  if (a <600) {
    // Charging, show the charge animantion
    drawBattery(true);
    chargeAnimation();
    return;    
    }

  // Battery is full
  // Show the full battery
  drawBattery(false);
  drawFullBattery();
  fullBattery = true;
  return;  
}

  
void play(unsigned char Track){
  unsigned char play[6] = {0xAA,0x07,0x02,0x00,Track,Track+0xB3};
  Serial1.write(play,6);
  playtime = millis();
}

void volume( unsigned char vol){
  unsigned char volume[5] = {0xAA,0x13,0x01,vol,vol+0xBE};
  Serial1.write(volume,5);
}

void DoorsOpen(int d){
  Rdoor.attach(2,544,2550);
  Ldoor.attach(3,544,2500);
  Rdoor.write(180);
  Ldoor.write(30);
  delay(d);
  Rdoor.detach();  
  Ldoor.detach();  
}

void DoorsClose(int d){
  Rdoor.attach(2,544,2500);
  Ldoor.attach(3,544,2550);
  Rdoor.write(30);
  Ldoor.write(180);
  delay(d);
  Rdoor.detach();  
  Ldoor.detach();  
}

void loop() {

  while(analogRead(A1) > 1000) {
    delay(10);
    pixels2.setPixelColor(0, pixels.Color(255, 0, 0));
    pixels2.show();
    }

  pixels2.setPixelColor(0, pixels.Color(0, 0, 255));
  pixels2.show();
  volume(20);
  // play the catch sound 
  play(3); // duration 8.25 seconds
   
  delay(550);
  // open the doors
  DoorsOpen(1000);

  // While the sound is playing, make the led rings yellow
  // in time with the whoosh sounds :-)
  delay(200);
  fadeIn(1, 255, 255, 0, 8); 
  delay(650);  
  ringColor(1, 100, 100,0); 
  fadeIn(2, 255, 255, 0, 8);    
  delay(650);
  ringColor(2, 100, 100,0); 
  fadeIn(3, 255, 255, 0, 8);
  delay(650);
  ringColor(3, 100, 100,0); 
  fadeIn(4, 255, 255, 0 ,8);

  // close the door after 6.5 seconds
  while (millis() < playtime + 6200) {delay(1);}  
  DoorsClose(500);

  // Set all led rings to full RED
  ringColor(1, 255, 0,0);
  ringColor(2, 255, 0,0);
  ringColor(3, 255, 0,0);
  ringColor(4, 255, 0,0);
  
  // wait for the sound to stop playing
  while (millis() < playtime + 7700) {delay(1);}
  
  // simulate the led bar array
  for (int y=0 ; y<11; y++){
    ledbar(y);  
    delay(100);    
  }
  // activate the big yellow led
  digitalWrite(yellowled,HIGH);
  volume(8);
  // play the beep sound
  DoorsClose(300);
  play(2);
  

  while (millis() < playtime + 5000){
    digitalWrite(redled,LOW);
    delay(250);
    digitalWrite(redled,HIGH);
    delay(250);    
  }
  
  ledbar(0);
  pixels.clear(); // Set all pixel colors to 'off'
  pixels.show();

  // the  show is over. Now we can play with the settings knob..
  parameters();  
  
}

void parameters(){
  pixels2.setPixelColor(0, pixels.Color(0, 255, 0));
  pixels2.show();
  volume(0);
  unsigned long blinktime = millis(); 
  unsigned long changetime = millis(); 
  unsigned long blinkRedLed = millis();
  int grid= analogRead(A2);
  int lastgrid = grid;
  String parameter = "tesla grid";
  int lasty=0;
  boolean nextparam = false;
  randomSeed(grid);
  int k = random(1,5);
  if (k == 1){ parameter="tesla grid";}
  if (k == 2){ parameter="neon field";}
  if (k == 3){ parameter="iq strings";}
  if (k == 4){ parameter="delta rays";}
      
  while(true){

    if (footpedal and analogRead(A1) < 1000) {
      while (analogRead(A1) < 1000) {
        delay(10);
        };
      delay(100);
      return;
      }
    
    if (millis() > blinkRedLed + 5000){
      blinkRedLed = millis();
      digitalWrite(redled,LOW);
      delay(100);
      digitalWrite(redled,HIGH);
      }
    
    grid= analogRead(A2);
    if (abs(grid - lastgrid) > 20){
      // the knob is turning.      
      changetime = millis() + 3000;
      lastgrid = grid;
      nextparam = true;
      }    
        
    display.clearDisplay();
    displayText(parameter);
    
    int y = map(grid,0,1023,1,122);
    if (abs(lasty-y)>3) {
      // play a click sound      
      lasty = y;
      play(1); // click
      delay(100);      
      volume(10);
      }
      
    for (int x = 0; x < y ; x=x+6){
      display.fillRect(x,20,4,12,SSD1306_WHITE);      
      }
      
    if (millis() > changetime) {
      display.clearDisplay();
      y = 0;
      if (nextparam){
        k=k+1;
        if (k==5) k=1;
        if (k == 1){ parameter="tesla grid";}
        if (k == 2){ parameter="neon field";}
        if (k == 3){ parameter="iq strings";}
        if (k == 4){ parameter="delta rays";}  
        nextparam=false;
      }
    }
      
    display.display();
        
    if (y > 100){
      // blink the yellow led
      if (millis() + 200 > blinktime) {digitalWrite(yellowled,LOW);} 
      if (millis() > blinktime) {digitalWrite(yellowled,HIGH); blinktime=millis() + 400;} 
      }
    else {digitalWrite(yellowled,LOW);}
   }  
  
  
  
}
void displayText(String p){
  display.setCursor(0, 0);
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.print(p);
}
void fadeIn(int pos, int r, int g, int b, int steps) {
  // Fade in for the neopixel rings
  int s = (pos-1) * 8;
  for (int q=0; q < steps ; q++){
    int ri = (r / steps) * q;
    int gi = (g / steps) * q;
    int bi = (b / steps) * q;
    for (int i =s; i<(s+8) ; i++){
      pixels.setPixelColor(i, pixels.Color(ri, gi, bi));
    }    
    pixels.show();
    delay(50);    
  }
  // make sure to display the end color
  ringColor(pos,r,g,b);    
}


void ringColor(int pos, int r, int g, int b){
  // make all the leds in this ring the same color
  // pos = 1,2,3 or 4
  // r =red, g=green, b=blue   0~255
  int s = (pos-1) * 8;
    for (int i =s; i<(s+8) ; i++){
    pixels.setPixelColor(i, pixels.Color(r, g, b));
  }      
  pixels.show();
}

void ledbar(int b){
  if (b > 11) {b=11;}
  int x=4;  
  display.clearDisplay();
  for (int v = 0; v < b; v++){
    display.fillRoundRect(x+(v*12) , 0, 9, 32,3, SSD1306_WHITE);
  }  
  display.display();
}