#4893498
I have to say I wish I would have found this site before I started this journey. My son has decided that he wants to be a ghostbuster for halloween and have a Ghostbusters birthday. His birthday is Nov 1 so it works out well. I found a number of plans on Thingiverse for proton packs, traps, and PKE Meters and decided I'd take a crack at one of them. I originally thought the PKE Meter would be the easiest so started with that. I now think I was wrong as it was difficult to get the electronics as small as I needed it. About a month later I've completed it and it looks like this:



Image

It's based off this model on Thingiverse I scaled it up to 110%, removed a few buttons to make it easier to use for my son, and created a new battery holder for the 4 AAA's that power it. I decided on an Arduino nano as the brains with a cheap LCD for display. Again, not going for screen accurate but something that will keep a 6-7 year old entertained. I did program in the Ghostbusters 2 LED pattern for the wings.

Image

The big trick to all of this was getting the electronics to fit in such a small place. I build my own PCB from OshPark for the LED driver board/5v rail to power everything. This worked out pretty good.

Image

Image

Image

it took me about a month from start to finish to build this. I'm really pleased with how it came out. All told it cost me about $50 for materials and parts to build it.

Now I'm starting in on a proton pack for him. Sure hope I can get it all done by halloween

Update

I did finish the proton pack in time for Halloween :) He was one awesome ghostbuster this year

Image

Video of the final proton pack:
Last edited by CountDeMonet on February 7th, 2018, 11:13 am, edited 3 times in total.
Glenn Frederick, grogking, noslliT and 1 others liked this
User avatar
By CountDeMonet
#4893541
Thanks :) I was originally going to use LED's like most other builds I have seen but when I saw the little LCD was less than $10 I thought I would give it a try. The one I bought is not in stock anymore but this one is basically the same thing

https://www.amazon.com/LANMU-Serial-128 ... B01G6SAWNY

Now that my son has used it a bunch I'm thinking of changing how the buttons work. Instead a press and hold I might make them a toggle.
User avatar
By CountDeMonet
#4893554
I might as well share the code for all of this. I don't have any full diagrams of everything but it's not that complex. I have zipped everything up including my Eagle board/schematic files if you wanted to order from oshpark or modify my boards. I have some extra 5v outputs on the rail and an extra LED spot. It also has an extra separate input with a capacitor. I was originally going to run the servo off the 5v from arduino but decided things would be more stable to run from the buck converter and the boards were already in fab when I decided to do that.

It's been a fun project. Now I'm starting in on the pack using a pair of arduino nano's and an Adafruit Audio FX Sound Board

A zip file with the 3D model STL files for printing, eagle files for the pcb, and the arduino code

The original model comes from here http://www.thingiverse.com/thing:700251 and is under the creative commons license
https://creativecommons.org/licenses/by-sa/3.0/

As far as hardware goes it uses:
The wiring is not so hard. From the battery pack 1 power/ground to the arduino vin/gnd pins and 1 power/ground to the buck converter. The converter is then adjusted for a 5v output and that goes to the power/ground of the servo. The full pin out from the arduino looks like this:
Code: Select allvin/gnd - to power 5v/gnd - to board from oshpark A4 - SDA on LCD Display A5 - SLC on LCD Display D3 - Button 1 + D4 - Button 2 + D5 - Buzzer + D6 - LED 1 (A7 on OshPark board) D7 - LED 2 (A6 on OshPark board) D8 - LED 3 (A5 on OshPark board) D9 - LED 4 (A4 on OshPark board) D10 - LED 5 (A3 on OshPark board) D11 - LED 6 (A2 on OshPark board) D12 - LED 7 (A1 on OshPark board) D13 - Servo Signal OshPark Board P1 - 5v + on LCD Display G - Gnd on LCD Display G - Gnd on Buzzer G - Shared ground on button board G - Shared ground on LED wings For the LED's I used 1 output from 1-7 on the board from OshPark to each wing. Yes I am using one resistor for 2 LED's. The current in this setup is less than 10ma so they will be fine
And here is the arduino code.
Code: Select all#include <Servo.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); Servo SERVO1; int LEDNum = 0; // current LED that is lit unsigned long previousMillis; // will store last time LED was updated const int LED1 = 12; const int LED2 = 11; const int LED3 = 10; const int LED4 = 9; const int LED5 = 8; const int LED6 = 7; const int LED7 = 6; const int BUZZER = 5; const int BUTTON1 = 3; const int BUTTON2 = 4; void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); pinMode(LED7, OUTPUT); pinMode(BUZZER, OUTPUT); pinMode(BUTTON1, INPUT); pinMode(BUTTON2, INPUT); digitalWrite(BUTTON1, HIGH); digitalWrite(BUTTON2, HIGH); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); delay(1000); // Clear the buffer. display.clearDisplay(); SERVO1.attach(13); SERVO1.write(90); } void loop() { int convertedVal = 0; if (digitalRead(BUTTON1) == false) { convertedVal = 40; }else if(digitalRead(BUTTON2) == false) { convertedVal = 90; } // do the led stuff LEDLoop(convertedVal); // do the servo stuff ServoLoop(convertedVal); // delay in between reads for stability delay(1); } int prevB = 0; void drawDisplay(int level, int bar) { display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(29, 5); display.print("Ghostbusters"); int b1 = random(prevB, bar); int b2 = random(prevB, bar); int b3 = random(prevB, bar); int b4 = random(prevB, bar); int b5 = random(prevB/2, bar/2); int b6 = random(prevB/2, bar/2); prevB = bar; display.fillRect(2, display.height() - (5 + b1), 5, b1, WHITE); display.fillRect(10, display.height() - (5 + b2), 5, b2, WHITE); display.fillRect(18, display.height() - (5 + b5), 5, b5, WHITE); display.fillRect(display.width() - 23, display.height() - (5 + b6), 5, b6, WHITE); display.fillRect(display.width() - 15, display.height() - (5 + b4), 5, b4, WHITE); display.fillRect(display.width() - 7, display.height() - (5 + b3), 5, b3, WHITE); display.drawCircle(display.width() / 2, display.height() / 2 + 7, level, WHITE); display.display(); } void clearLoop() { display.clearDisplay(); prevB = 0; } void LEDLoop(int convertedVal) { int ledSpeed = map(convertedVal, 0, 100, 500, 20); // check to see if it's time to change the state of the LED unsigned long currentMillis = millis(); if ((currentMillis - previousMillis >= ledSpeed)) { previousMillis = millis(); if ( LEDNum == 0 ) { TriggerBuzzer(); clearLoop(); digitalWrite(LED3, true); LEDNum = 1; drawDisplay(21, 5); } else if ( LEDNum == 1 ) { TriggerBuzzer(); digitalWrite(LED3, false); digitalWrite(LED1, true); LEDNum = 2; drawDisplay(18, 20); } else if ( LEDNum == 2 ) { TriggerBuzzer(); digitalWrite(LED1, false); digitalWrite(LED6, true); LEDNum = 3; drawDisplay(15, 30); } else if ( LEDNum == 3 ) { TriggerBuzzer(); digitalWrite(LED6, false); digitalWrite(LED4, true); LEDNum = 4; drawDisplay(12, 45); } else if ( LEDNum == 4 ) { TriggerBuzzer(); digitalWrite(LED4, false); digitalWrite(LED7, true); LEDNum = 5; drawDisplay(9, 50); } else if ( LEDNum == 5 ) { TriggerBuzzer(); digitalWrite(LED7, false); digitalWrite(LED5, true); LEDNum = 6; drawDisplay(6, 55); } else if ( LEDNum == 6 ) { TriggerBuzzer(); digitalWrite(LED5, false); digitalWrite(LED2, true); LEDNum = 7; drawDisplay(3, 65); }else if ( LEDNum == 7 ) { TriggerBuzzer(); clearLoop(); digitalWrite(LED2, false); digitalWrite(LED3, true); LEDNum = 1; drawDisplay(21, 5); } } } void TriggerBuzzer() { tone(BUZZER, 2000); // Send 2KHz sound signal... delay(20); // ...for 20 msec noTone(BUZZER); } void ServoLoop(int convertedVal) { int servoVal = map(convertedVal, 0, 100, 110, 40); SERVO1.write(servoVal); }
grogking liked this
#4893894
I changed the code to toggle each state instead of requiring the button be held down. I found this is easier for my son to use. I now have everything in github here:

https://github.com/CountDeMonet/Arduino_PKE_Meter

The current code:
Code: Select all#include <Servo.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); Servo SERVO1; int LEDNum = 0; // current LED that is lit unsigned long previousMillis; // will store last time LED was updated const int LED1 = 12; const int LED2 = 11; const int LED3 = 10; const int LED4 = 9; const int LED5 = 8; const int LED6 = 7; const int LED7 = 6; const int BUZZER = 5; const int BUTTON1 = 3; const int BUTTON2 = 4; // know thy button state bool medium_state = false; bool high_state = false; bool button1_down = false; bool button2_down = false; void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); pinMode(LED7, OUTPUT); pinMode(BUZZER, OUTPUT); pinMode(BUTTON1, INPUT); pinMode(BUTTON2, INPUT); digitalWrite(BUTTON1, HIGH); digitalWrite(BUTTON2, HIGH); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); delay(500); // Clear the buffer. display.clearDisplay(); SERVO1.attach(13); SERVO1.write(90); } // current display val int currentVal = 0; void loop() { int button1 = digitalRead(BUTTON1); int button2 = digitalRead(BUTTON2); if( button1 == LOW && button1_down == false ){ button1_down = true; if ( medium_state == true) { medium_state = false; high_state = false; currentVal = 0; }else { medium_state = true; high_state = false; currentVal = 40; } } else { if (button1 == HIGH && button1_down == true ){ button1_down = false; } } if( button2 == LOW && button2_down == false ){ button2_down = true; if(high_state == true) { medium_state = false; high_state = false; currentVal = 0; }else { medium_state = false; high_state = true; currentVal = 90; } }else{ if (button2 == HIGH && button2_down == true ){ button2_down = false; } } .z // do the led stuff LEDLoop(currentVal); // do the servo stuff ServoLoop(currentVal); // delay in between reads for stability delay(1); } int prevB = 0; void drawDisplay(int level, int bar) { display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(29, 5); display.print("Ghostbusters"); int b1 = random(prevB, bar); int b2 = random(prevB, bar); int b3 = random(prevB, bar); int b4 = random(prevB, bar); int b5 = random(prevB/2, bar/2); int b6 = random(prevB/2, bar/2); prevB = bar; display.fillRect(2, display.height() - (5 + b1), 5, b1, WHITE); display.fillRect(10, display.height() - (5 + b2), 5, b2, WHITE); display.fillRect(18, display.height() - (5 + b5), 5, b5, WHITE); display.fillRect(display.width() - 23, display.height() - (5 + b6), 5, b6, WHITE); display.fillRect(display.width() - 15, display.height() - (5 + b4), 5, b4, WHITE); display.fillRect(display.width() - 7, display.height() - (5 + b3), 5, b3, WHITE); display.drawCircle(display.width() / 2, display.height() / 2 + 7, level, WHITE); display.display(); } void clearLoop() { display.clearDisplay(); prevB = 0; } void LEDLoop(int convertedVal) { int ledSpeed = map(convertedVal, 0, 100, 500, 20); // check to see if it's time to change the state of the LED unsigned long currentMillis = millis(); if ((currentMillis - previousMillis >= ledSpeed)) { previousMillis = millis(); if ( LEDNum == 0 ) { TriggerBuzzer(); clearLoop(); digitalWrite(LED3, true); LEDNum = 1; drawDisplay(21, 5); } else if ( LEDNum == 1 ) { TriggerBuzzer(); digitalWrite(LED3, false); digitalWrite(LED1, true); LEDNum = 2; drawDisplay(18, 20); } else if ( LEDNum == 2 ) { TriggerBuzzer(); digitalWrite(LED1, false); digitalWrite(LED6, true); LEDNum = 3; drawDisplay(15, 30); } else if ( LEDNum == 3 ) { TriggerBuzzer(); digitalWrite(LED6, false); digitalWrite(LED4, true); LEDNum = 4; drawDisplay(12, 45); } else if ( LEDNum == 4 ) { TriggerBuzzer(); digitalWrite(LED4, false); digitalWrite(LED7, true); LEDNum = 5; drawDisplay(9, 50); } else if ( LEDNum == 5 ) { TriggerBuzzer(); digitalWrite(LED7, false); digitalWrite(LED5, true); LEDNum = 6; drawDisplay(6, 55); } else if ( LEDNum == 6 ) { TriggerBuzzer(); digitalWrite(LED5, false); digitalWrite(LED2, true); LEDNum = 7; drawDisplay(3, 65); }else if ( LEDNum == 7 ) { TriggerBuzzer(); clearLoop(); digitalWrite(LED2, false); digitalWrite(LED3, true); LEDNum = 1; drawDisplay(21, 5); } } } void TriggerBuzzer() { tone(BUZZER, 2000); // Send 2KHz sound signal... delay(20); // ...for 20 msec noTone(BUZZER); } void ServoLoop(int convertedVal) { int servoVal = map(convertedVal, 0, 100, 110, 40); SERVO1.write(servoVal); }
grogking liked this
#4899408
I used some m2.5 hex head screws but pretty much any m2.5 screw should work. I don't remember the exact length. I think the 10mm ones. I bought this a while back for my other hobbies but used some for the pke meter

Haobase 200Pcs DIN912 M2.5 Hex socket head cap screw kit set grad12.9 m2.5 x 4/5/6/8/10/12/14/16/18/20 https://www.amazon.com/dp/B01N8XJVHQ/re ... 3zbZKRBXHS

The plate slides in from behind. It's kinda hard to describe so I'll see if I have a test print laying around to show with pictures. It fits snug but should slide in and lock in.
Last edited by CountDeMonet on October 11th, 2017, 7:27 am, edited 1 time in total.
#4899449
I no longer have the test pieces I printed. I think my wife must have cleaned it up. I've done the best that I can with the 3d software to show how I do it. The face piece is bent a bit to get it in the area.

start by moving it back and forth as you move it into the display area
Image

With it still not lined up continue to move it up until it you can press it back against the mounts
Image

At this point it should be pretty well lined up and will seat well
Image

Just finish pushing it into place
Image

I did not have to glue mine in. Once in place it won't move.
doctorevil30564 liked this
#4902910
I found a few more pics from the build I thought I would add here as well. They show the installation of the leds in the wings and stuff

Image

Then I pulled it out and soldered the leads on. Here I used the wrong wire so this part would be easier with the wire I recommend

Image

Image

Then I first installed the screen with low temp hot glue

Image

Then the servo and buzzer with the low temp hot glue

Image

I don't have a picture of it but the button board was mounted and low temp hot glued in. then the arduino was placed on top of it with double sided tape. The custom board was low temp hot glued on top of the screen and the connections were soldered up

Image

I sprayed the wings with the metallic paint (not happy with the chrome I used so resprayed with a more matte finish. Honestly I should have just gone with a dull gray paint I think and not try to chrome it.

Image

The wings were then installed and the wires soldered up. A small servo wire from one of my old rc airplanes was used to connect the servo to the wings. I also installed the wire switch and metal brackets I pulled out of one of my sons toys. And all finished up and ready to be screwed together

Image
#4904273
Hey count, I am loving this one meter! I have many questions but the biggest one for me is, do you need that eagle board the one you had made by oshpark? From what I gathered the led wires run though it which I could do without the board and it looks also looks like the 5 volt goes from the nano to the board then out to the screen. So the only big thing I see is it allows for several connected grounds is that correct?
#4905137
Thanks for sharing this project! I think I'm going to give this one a try.

I have downloaded all the files and while previewing the parts, I'm getting a not manifold error on the front top and the button?

Also, looking at the inside of the front top, I noticed there are a couple features in the part at the lower end that I don't see in your pictures. Looks like a couple of screw bosses (sticking out of the wall that would butt up against the handle)? What are they for?

Thanks again!
#4905150
Yeah, there is an issue with both parts. I was able to fix the button one and replace the one on thingiverse. The front top though is not fixable. Cura has no problem slicing it even with the error and netfabb is not able to fix it. I didn't create the original part. I just modified the stl's to do what I wanted so I had to work with that. I know there are quite a few people who have printed it with various slicers so you shouldn't have a problem.

Those two things were remnants from the original part. I used my dremel to remove them
#4906102
Count -- I'm the guy who's been pestering you on Thingiverse the last couple weeks, and I just want to say thank you so much for all your help! I'm waiting for the custom PCBs, the screws, and a couple AAA battery holders (I misread it all initially and was working with AA, which I assume is why I was confused about the buck converter), then I should be able to put it all together. I'll post pics when it's done.
#4906186
I changed the code to toggle each state instead of requiring the button be held down. I found this is easier for my son to use. I now have everything in github here:

https://github.com/CountDeMonet/Arduino_PKE_Meter

The current code:
Code: Select all#include <Servo.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); Servo SERVO1; int LEDNum = 0; // current LED that is lit unsigned long previousMillis; // will store last time LED was updated const int LED1 = 12; const int LED2 = 11; const int LED3 = 10; const int LED4 = 9; const int LED5 = 8; const int LED6 = 7; const int LED7 = 6; const int BUZZER = 5; const int BUTTON1 = 3; const int BUTTON2 = 4; // know thy button state bool medium_state = false; bool high_state = false; bool button1_down = false; bool button2_down = false; void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); pinMode(LED7, OUTPUT); pinMode(BUZZER, OUTPUT); pinMode(BUTTON1, INPUT); pinMode(BUTTON2, INPUT); digitalWrite(BUTTON1, HIGH); digitalWrite(BUTTON2, HIGH); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); delay(500); // Clear the buffer. display.clearDisplay(); SERVO1.attach(13); SERVO1.write(90); } // current display val int currentVal = 0; void loop() { int button1 = digitalRead(BUTTON1); int button2 = digitalRead(BUTTON2); if( button1 == LOW && button1_down == false ){ button1_down = true; if ( medium_state == true) { medium_state = false; high_state = false; currentVal = 0; }else { medium_state = true; high_state = false; currentVal = 40; } } else { if (button1 == HIGH && button1_down == true ){ button1_down = false; } } if( button2 == LOW && button2_down == false ){ button2_down = true; if(high_state == true) { medium_state = false; high_state = false; currentVal = 0; }else { medium_state = false; high_state = true; currentVal = 90; } }else{ if (button2 == HIGH && button2_down == true ){ button2_down = false; } } .z // do the led stuff LEDLoop(currentVal); // do the servo stuff ServoLoop(currentVal); // delay in between reads for stability delay(1); } int prevB = 0; void drawDisplay(int level, int bar) { display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(29, 5); display.print("Ghostbusters"); int b1 = random(prevB, bar); int b2 = random(prevB, bar); int b3 = random(prevB, bar); int b4 = random(prevB, bar); int b5 = random(prevB/2, bar/2); int b6 = random(prevB/2, bar/2); prevB = bar; display.fillRect(2, display.height() - (5 + b1), 5, b1, WHITE); display.fillRect(10, display.height() - (5 + b2), 5, b2, WHITE); display.fillRect(18, display.height() - (5 + b5), 5, b5, WHITE); display.fillRect(display.width() - 23, display.height() - (5 + b6), 5, b6, WHITE); display.fillRect(display.width() - 15, display.height() - (5 + b4), 5, b4, WHITE); display.fillRect(display.width() - 7, display.height() - (5 + b3), 5, b3, WHITE); display.drawCircle(display.width() / 2, display.height() / 2 + 7, level, WHITE); display.display(); } void clearLoop() { display.clearDisplay(); prevB = 0; } void LEDLoop(int convertedVal) { int ledSpeed = map(convertedVal, 0, 100, 500, 20); // check to see if it's time to change the state of the LED unsigned long currentMillis = millis(); if ((currentMillis - previousMillis >= ledSpeed)) { previousMillis = millis(); if ( LEDNum == 0 ) { TriggerBuzzer(); clearLoop(); digitalWrite(LED3, true); LEDNum = 1; drawDisplay(21, 5); } else if ( LEDNum == 1 ) { TriggerBuzzer(); digitalWrite(LED3, false); digitalWrite(LED1, true); LEDNum = 2; drawDisplay(18, 20); } else if ( LEDNum == 2 ) { TriggerBuzzer(); digitalWrite(LED1, false); digitalWrite(LED6, true); LEDNum = 3; drawDisplay(15, 30); } else if ( LEDNum == 3 ) { TriggerBuzzer(); digitalWrite(LED6, false); digitalWrite(LED4, true); LEDNum = 4; drawDisplay(12, 45); } else if ( LEDNum == 4 ) { TriggerBuzzer(); digitalWrite(LED4, false); digitalWrite(LED7, true); LEDNum = 5; drawDisplay(9, 50); } else if ( LEDNum == 5 ) { TriggerBuzzer(); digitalWrite(LED7, false); digitalWrite(LED5, true); LEDNum = 6; drawDisplay(6, 55); } else if ( LEDNum == 6 ) { TriggerBuzzer(); digitalWrite(LED5, false); digitalWrite(LED2, true); LEDNum = 7; drawDisplay(3, 65); }else if ( LEDNum == 7 ) { TriggerBuzzer(); clearLoop(); digitalWrite(LED2, false); digitalWrite(LED3, true); LEDNum = 1; drawDisplay(21, 5); } } } void TriggerBuzzer() { tone(BUZZER, 2000); // Send 2KHz sound signal... delay(20); // ...for 20 msec noTone(BUZZER); } void ServoLoop(int convertedVal) { int servoVal = map(convertedVal, 0, 100, 110, 40); SERVO1.write(servoVal); }
So, I was torn between using the light pattern from the first movie vs the light pattern from the second movie that you programmed in. In conversation, I asked a friend which I should do and he suggested "Why not both? Control it with a switch?" There's really no more room for a switch in the hardware, but there are the buttons on the face and usually some room for more code. I forked your repo, Count, and refactored the code a little bit, and was able to add in a pair of loading screens which will let you choose the wing light pattern from either of the movies (depending on, I guess, which suit you're wearing) and whether or not to play the sounds. The code, if anyone is interested, is available in this Github repo: https://github.com/panatale1/Arduino_PKE_Meter. Let me know if you'd like me to submit a pull request, Count.
#4906290
Sorry to hear that. I've lost my fair share of parts due to warping in the sun. Happened to me a few times with the pack build while painting. Parts wouldn't be in the sun when I painted but check back an hour later and oops.

If you are getting them reprinted and want it to be a little more resilient see if the place can print it in ABS. It's easier to sand and a lot more resilient to heat than PLA.
#4906293
If you are getting them reprinted and want it to be a little more resilient see if the place can print it in ABS. It's easier to sand and a lot more resilient to heat than PLA.
Unfortunately, the library that I'm getting my parts printed out only does PLA filament. As for sanding and painting, I'm planning on leaving it entirely unfinished. I really like the look it has from being printed. It gives it a real cobbled together/custom design aesthetic that updates the aesthetic from the movies to modern day. My fiancee even said "it feels like something Holtzmann would do."

http://i.imgur.com/1kB7fOZ.jpg Unfortunately, I[…]

oh 1 last thing, use a fresh new 9v batter to test[…]

1st Build

Thanks to everyone here for threads and plans. the[…]

3D Printed Pack Build

Just to say I have a davinci mini as well. The XYZ[…]