Project tutorial
Portal 2 Turret - Master Turret Control of Portal 2 Turrets

Portal 2 Turret - Master Turret Control of Portal 2 Turrets © CC BY-NC-SA

The Master Turret Control for controlling multiple Portal turrets per my other build.

  • 2,230 views
  • 0 comments
  • 17 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)

About this project

This project is an extension or remix of my original Portal Turret (Portal-2-Turret-Gun). It can also be used as a cheap controller to control anything that uses the nRF24L01 radio chip. The LCD screen is particularly useful when customizing.

After I had completed the Portal Turret, I saw a short video clip of the Cara Mia opera scene at the end of Portal 2. I thought, “hey, I’ve built a few turrets now. I can do that!” Well, I had the turrets, but to make it work, they would now need to communicate with each other, or with some other device. After giving it some thought, and thinking that manual control would also be cool, I ended up deciding that I would build a Master Turret Controller, or an MTC.

Ok, so how to go about this? Well, I didn’t feel like modifying the turrets too much, so that was a constraint. I had some nRF24L01 radio chips which I hadn't yet used, and thought that would keep in line with my goal of using inexpensive components and relying on mechanical design and code to make this work. I would have to build a controller and started mulling over what the controller would contain and what exactly I could do to the turrets. Cara Mia was already on the agenda, but what else?

Manual control: So once the wings are open, I would like to control the pitch and the pivot. 2 axes = a joystick, so joystick control and some push buttons. Push button 1 would fire the turret, and maybe push button 2 would make it say one of its sayings. Sounds good! Simple enough…

Chat: I recently saw the “Who’s on First” - Abbott and Costello routine again, and a light went off in my head! I would also make a sketch comedy routine using all of the sayings, using multiple turrets!! Ok, this one wasn’t fully figured out, but I’m sure I would get it sorted once my build was underway.

I also wanted this MTC to be wireless, so I opted for a simple 9V battery powered option and designed the controller to be powered by the mini-USB plug through the Nano at the back. Useful for updating too.

Step 1: Incorporating the Radio Into the Turrets

I first got the radio working with a few bare Nanos, to make sure I could send information between multiple devices. Once that was done, it was a matter of incorporating the radio into the existing turrets. Hmm, adding the physical PCB into the turret was pretty easy. I just modified the chip holder to include the nRF24L01, the Nano and the mp3 chip. Ok. One physical part modified, with some wiring.

The modified chip holder is already part of the turret printed parts now. I just deleted the non-radio option from the printed parts in that build. It won’t make a difference if someone wants to build the non-radio variant. Just don’t include the nRF24L01 radio chip.

Step 2: (Re)wiring of the Turrets

Uh oh...

This wasn’t looking good. I needed to add 5 connections to the Nano, and I was already low on available pins. After looking into this for a bit, I realized which connections took priority, and recognized that to make this work, I would have to remap most of the existing Nano connections.

To those who have already built the previous model and want to make a “Radio Turret” ... Sorry about that…

Now, I’ve done this change a few times and it turns out that the process isn’t too bad. It does involve going back in, but I was able to undo the existing connections to the Nano and then reconnect to the appropriate pins without too much grief. I added the 7 wires for the radio chip to the nRF24L01 (5 comm wires, 3v and GND), then connected the other ends to the Nano.

More wires in there now though, so it makes that much more important to keep things tidy when routing the wires.

IMPORTANT: When assembling, make sure the wires don’t press on the Nano reset button on the board!! That happened to me on one and threw me for a needless loop!

So the turret build now has 2 wiring schematics: the old legacy “non-radio” option and the revised “radio Turret” option. If building a “non-radio” turret today, I would still use the radio schematic and code. Just delete or comment out the radio portions if that’s the chosen route, or not. The turret should still work on its own without the radio.

Step 3: MTC Electrical Components

Having the turrets sorted out, it was time to make an MTC.

The MTC uses the following components, all sourced through Amazon or Baggood or Ali Express, etc. I’m showing the Amazon links (mostly Canadian site since that’s what I use) for reference, since these items are commonly available and reasonably priced (and I didn’t have to wait 2+ weeks to get my hands on what I needed before I could start the mechanical design!)

  • Arduino Nano
  • 0.96” LCD, SSD1306 (I used the Blue/Yellow version) Amazon link
  • 5mm LEDs of your choice for indicators. (Don’t use bright LEDs!!)
  • Generic 9V battery connector with pigtails
  • 9V battery (use a good one, not one of the dollar store varieties which can’t always supply enough power for these projects!)
  • I used silicone sheathed wire. I like using it for these projects.

I originally used some bright LEDs, but found they were too good. They were blinding me! I ended up using some old, weak LEDs, and that made more sense for this application.

Step 4: 3D Printed Parts

I designed the MTC using Fusion 360, similar to the way I did the turret design.

The printed assembly only requires 3 parts: Top panel (version 1 or 2) Bottom housing LCD strap

The nRF chip, the Nano, the 9V battery, the toggle switch and the LEDs are installed in the top plate without fasteners. The LEDs just press in the plate and are held in place by tabs. They should just snap in, but don’t overdo it. The top plate is designed to snap-hold the Nano, and the nRF chip should go in gently. Careful with the nRF small tab; it is small and is bent back to open up and released to capture the chip. Even though its travel is limited, be gentle here.

The joystick and the LCD need 2mm screws (5mm long) to hold them to the top plate. The joystick PCB has large holes, so I felt I needed small washers to make sure the screws didn’t pull through.

I’ve found that the PCB’s the LCD’s come on vary slightly dimensionally from manufacturer to manufacturer, so I opted to use a simple strap to hold it in place instead of cleats or hooks.

Note that the LCD can be physically installed into the top plate either way, but the display only shows up fully through the opening in one orientation! For this reason, I’ve included a 2nd top plate option when using the split yellow/blue screen. One version has the yellow at the top, and the other will show the yellow at the bottom as shown in my photos.

For the single colour version LCD, it doesn’t matter which one is used as you can flip the display using software.

Since all the wiring is done on the top plate, the bottom is effectively just a cover held to the top plate with longer 2mm screws (qty:4).

Instead of a “battery door” option, I just incorporated the battery into the top plate. This means removing the 4 screws that hold the bottom to the top to change a battery, but since it can also be powered by USB cable, it's not the end of the world. The top plate is made with a 9V battery holder system that should be sturdy enough for repeated use, isn’t overly complicated but prevents the battery from moving around.

I printed the top plate in 2 colours as seen in the photos. I use a Prusa i3 Mk2 without the multi colour option, but use their colour print tool (https://www.prusaprinters.org/color-print/) to change colour partway through the process. Check the layer that the text stops and becomes solid, and make that the transition layer. Voila! Coloured text! I'm not sure if this works with other printers, but worst comes to worst, one could always apply some paint in the text reliefs for the same effect.

I printed the parts at 0.35mm layer height since there is no need for finer resolution on these flat parts. I also prefer the way it looks at this resolution. Oh, and it prints pretty quickly too!

Step 5: Electrical Assembly

The electrical components are all installed on the underside of the Top plate, and all wiring is done together. Push buttons and toggle have to be installed first, and the LCD, Nano, Joystick, nRF radio can all be pre-wired before being installed in the Top plate. I recommend this method of pre-wiring the individual components, then making the final connections to the Nano at the end. I also recommend uploading the sketch to a bare Nano first, before completing the wiring.

There is nothing more satisfying than switching the device on and watching it come to life as expected when finished!

The only electrical part prep required was to remove the header pins from the joystick to make it fit under the top plate. The LCD screen can be purchased with or without pins installed, and will work either way. The Nano should be used without header pins.

Step 6: Code

Well, this was an experience in coding! I had the turret working independently, but incorporating the radio was more difficult than I originally thought! I also had some fun getting the “Chat” section to work as desired (more on that later).

The MTC code, once fully figured out, started taxing memory limits of the Nano! I had to make adjustments and economize the code to be more memory efficient. Another good learning experience.

I’ve included the corresponding “Turret w radio option” code in this build, as they work together. Building the radio option turret per the Turret build page will make it ready for use with the MTC, or for your other nRF24L01 method of control.

Also, getting the sounds to work with this code took some time, and since the sounds were taken from other publicly accessible websites, I incorporated all sounds as I’ve used them in a couple of ZIP files; one for the regular turret, and one for Defective turret. The good thing for you, dear reader, is that you can use the sounds on your SD cards with the codes as written and you’ll be good to go!

Step 7: MTC Menu - Navigation

The MTC code starts up with a custom Splash screen, then checks on the status of the turrets. If no turrets are present, it will just sit there until turrets connect!

If at least one turret is connected, the main menu will display and the “Ready” LED will illuminate, unless the turrets are interrupted or are busy “dealing with someone”. If they are busy, a “Turrets are Busy screen” will be displayed, and the “Busy” LED will illuminate.

All actively connected turrets must be in “Ready Mode” before the MTC can control the turrets.

Use the joystick (up and down) to cycle through the menu choices:

  • Aria
  • Chat
  • Manual

Select the desired option using the ‘X’ button, or by pressing down on the joystick.

Aria Mode - selecting this option will display the “Aria Mode” screen and make the turrets perform the Aria scene at the end of the Portal 2 game. Once complete, the turrets will close up and wait for a command or someone to wake them up.

Chat Mode - Selecting this option will display the “Chat Mode” screen and start the Chat sequence. Once complete, the turrets will close up and wait for a command or someone to wake them up.

Manual Mode - Selecting this option will display the “Manual Mode” screen, illuminate the “Manual” LED and allow manual operation of the turrets. Joystick control of pitch and pivot. Pressing the ‘X’ button will activate the firing sequence. Pressing the ‘T’ button will make them “talk”, where the turrets speak a random saying from their library.

Pressing the ‘<’ or back button will cancel these three modes, close up the turrets and return to the main menu.

If you would like to see the connection status of your swarm of turrets (currently limited to 3), press the ‘T’ button when in the main menu. You will be taken to the “turret status” screen, where you can see the connection status of each turret.

When in the “turret status” screen, you will see the status of each turret.

  • Ready - ready for control
  • Busy - turret is busy “tending” to someone
  • Not available - MTC cannot connect with this turret

Press the ‘<’ button to return to the main menu.

Step 8: Customizing the Code

The screen shot shown here shows what I was staring at for far too long... 4 sketches at a time! Who said debugging isn't fun!

The code provides the control and options shown above, but what about customizing the code?

Of course! But there is a fair amount going on here, so here are some guidelines or tips.

Tip1 - Modifying “Chat” sequence.

This modification takes place in the turret code.

I tried to come up with a way of making the code work for me wherever I could. Making the chat sequence more editable (Is that a word?) so that I could focus on the story took more work up front, but makes it easier to customize afterwards.

Changing the Chat sequence using the coding structure provided can be done in a pretty straightforward way, provided you follow the method used in the code. Using the sound files provided, from the Portal 2 game, you would only need to change the (chatSayings[]) table.

Select the saying for either the regular turret or defective turret. The saying is the mp3 file that is identified by “00XX -” followed by text describing the saying. It is the number that is the important part. Turret # 1 would use this XX value in the table. Turret # 2 would prefix the XX value with a ‘1’, and turret # 3 would prefix the XX value with a ‘2’. So, for example, if you were selecting the saying, “0040 - sorry” expression, and wanted turret # 3 to say it, you would insert “240” into the table in the correct sequence. If turret # 1 were to say it, you would insert “40” into the table.

Do the same for the next expression, and so on. It could be a few expressions between the turrets, or quite a few. (I’m not aware of the limitations of this method, other than memory).

You would not need to change the values of the other tables as they are the timings which correspond to the saying in the table. The only other line to change is around line 520.

if (i >= 43) { // end of sequence

The value of I here would have to be set to the number of sayings in the chatSayings[] table.

To make completely customized sayings, (where the real fun begins!), you would need to set up the expressions in numbered files, and know the length of time needed to play the file. Save the file into the “mp3” folder in the root directory of the SD card using the four digit method (“0001”, “0002”, etc.). Then log the time in milliseconds that it takes for that file to play. Insert these values in the appropriate tables.

So for an expression saved as, “0037 - [your expression]”, which takes 5400 milliseconds to play, you would put ‘37’ in the chatSayings[] table at the appropriate location (and add the prefix depending on which turret is saying it), and 5400 in the NormaTimings[] table at the same location (such as 5th item in each table).

Now when the value of ‘i’ is incremented, the code will play 0037 for 5400 milliseconds.

Note that I've added a "timeadder" variable which adds a little extra time to each saying when playing. This gives a little distance between sayings so they don't appear to overlap.

The beauty of this method is that the completed tables are the exact same in each turret! No need to customize these tables for each turret. You only need to sort out one table, and the code figures out what each turret says based on the same table.

This means that you can focus on script writing instead of coding!

If someone has a better way of doing this, I'd love to hear it!!

Step 9: Summary

So this took more time than I thought, but I’m rather tickled by the result. Some of the Chat routines still make me laugh!

I would love to hear other methods of doing the things I did within my code. I’m sure there are sections that could be rewritten and use less memory, allowing for more features or options.

I would also love to see other ideas incorporated into the MTC and control of the turrets!

I hope I have provided a platform for other designers and coders to use / steal / learn from. I could see this being used to help people learn to code. Remove a section from the MTC and/or turret, like "Manual mode" for example, and get students to develop their own way to incorporate manual control!

I’ve learned a lot from this community and the broader web in general. I’m still amazed how many people spend gobs of time figuring things out and sharing them with the world. I figure I should take what I’ve learned, apply it, then share it with you as well!

Good luck and have fun building your own army of turrets!

Code

MTC codeC/C++
My code for the MTC
/* Master Turret Control - Actual
 *  Chris Nowak - MAR 2019
 * This is the handheld MTC module.  
 *  https://www.instructables.com/member/ChrisN219/
 *  https://create.arduino.cc/projecthub/Novachris/talking-portal-2-turret-gun-637bf3
 * 
 *  This code includes all the features needed to control up to three turrets,
 *  perform the Cara Mia Opera, Chat Time and Manual mode.
 *  The turrets are featured on another build.  The turret
 *  will operate autonomously using this code without the MTC.
 *  
 *  The code is built to work with three turrets, but each will operate independently.
 *  Lots of debugging code left in.  Use or clean up as desired.  It's mostly debugged,
 *  but isn't perfect.  Given the price you paid, I figure that's ok! ;)
*/

#include <RF24.h>
#include <RF24Network.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display(4);
#define LOGO32_GLCD_HEIGHT 32 
#define LOGO32_GLCD_WIDTH  32 
static const unsigned char PROGMEM logo32_glcd_bmp[] =
{ B00000000, B00000001, B10000000, B00000000,
  B00000000, B00000110, B01100000, B00000000,
  B00000000, B00011000, B00011000, B00000000,
  B00000000, B00010000, B00001000, B00000000,
  B00000000, B00110000, B00001100, B00000000,
  B00001100, B00001000, B00010000, B00011000,
  B00110000, B00011000, B00011000, B00001100,
  B01110000, B00010001, B10001000, B00001110,
  B01100000, B00110011, B11001100, B00000110,
  B01100000, B00100011, B11000100, B00000110,
  B11000000, B00110001, B10001100, B00000011,
  B11000000, B00100000, B00000100, B00000011,
  B11000000, B00110000, B00001100, B00000011,
  B11000000, B00010000, B00001000, B00000011,
  B01100000, B00010000, B00001000, B00000110,
  B00110000, B00001100, B00110000, B00001100,

  B00011000, B00000011, B11000000, B00011000,
  B00000110, B00001101, B10110000, B01100000,
  B00000000, B00110001, B10001100, B00000000,
  B00000000, B11000001, B10000011, B00000000,
  B00000001, B10000001, B10000001, B10000000,
  B00000110, B00000001, B10000000, B01100000,
  B00011000, B00000001, B10000000, B00011000,
  B00110000, B00000001, B10000000, B00001100,
  B01100000, B00000001, B10000000, B00000110,
  B01100000, B00000001, B10000000, B00000110,
  B11000000, B00000001, B10000000, B00000011,
  B11000000, B00000001, B10000000, B00000011,
  B11000000, B00000001, B10000000, B00000011,
  B11000000, B00000001, B10000000, B00000011,
  B11000000, B00000001, B10000000, B00000011,
  B11000000, B00000000, B00000100, B00000011 };

RF24 radio(10, 9);                // nRF24L01 (CE,CSN)
RF24Network network(radio);
const uint64_t this_node = 00;    // 
const uint64_t RED = 01;       // turret #1 - Red Turret
const uint64_t WHT = 02;       // turret #2 - White Turret
const uint64_t BLU = 03;       // turret #3 - Blue Turret 

byte pb1 = A2; // Select (GRN) "X"
byte pb2 = A1; // Select 2 (WHT) "T"
byte pb3 = A0; // Break (Red) "<"
byte joyX=A6;
byte joyY=A7;
byte joyButton = A3; // joystick press

byte ledManual = 5; // LED for signalling Manual Mode (BLUE)
byte ledNotReady = 6; // LED for signalling that turrets busy (RED)
byte ledOkToGo = 7; // LED signalling ready for action (GREEN)

byte power = 8; // Through a pinout to show Nano powered and working
int goState; // Aria =1, Chatty = 2, Manual = 3
int pb1State;
int pb2State;
int pb3State;
byte joyButtonState;
byte busyWhite = 0;
byte busyRed = 0;
byte busyBlue = 0;
byte notReady;
byte DataRead;
int x=0;
int y=0;
int z=0;
byte MainBusy = 1;
byte MainAria = 0;
byte MainChat = 0;
byte MainManual = 0;
byte MenuSelection = 0;
int Rx = 0;
int Wx = 0;
int Bx = 0;
byte RedHere = 0;
byte WhiteHere = 0;
byte BlueHere = 0;
byte selectTurret;

/////////////////////////////////////////////////////////////////// 
//============================ SETUP ============================//
///////////////////////////////////////////////////////////////////
void setup() {
  Serial.begin(9600);//
  SPI.begin();
  radio.begin();
  radio.setPALevel(RF24_PA_LOW);
  radio.setDataRate(RF24_2MBPS);
  radio.enableAckPayload();
  network.begin(70, this_node);  //(channel, node address)
  pinMode(pb1, INPUT_PULLUP);
  pinMode(pb2, INPUT_PULLUP);
  pinMode(pb3, INPUT_PULLUP);
  pinMode(joyButton, INPUT_PULLUP);
  pinMode(ledManual, OUTPUT);
  pinMode(ledNotReady, OUTPUT);
  pinMode(ledOkToGo, OUTPUT);
  pinMode(power, OUTPUT);
  radio.printDetails();
  digitalWrite (power, HIGH);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 
  display.clearDisplay(); 
  display.display();
  display.setRotation(2); // yellow band on bottom. 0 is for yellow on top
  Splash();
  delay (5000); // Display spash screen for 5 seconds.
  display.clearDisplay();
  MenuSelection = 1;
  for (int i=0; i <= 20; i++){ // quick net check to see which turrets around
    ReadNet();
    TurretCheck();
  }
}
/////////////////////////////////////////////////////////////////// 
//========================= MAIN LOOP ===========================//
///////////////////////////////////////////////////////////////////
void loop() { 
  //=====  No turrets connected =====//
  while ((RedHere == 0) && (WhiteHere == 0) && (BlueHere == 0)){
    ReadNet();
    display.setTextColor(WHITE);
    display.setTextSize(0);
    display.setCursor(20,0); display.print("No Turrets!");
    display.setCursor(0,8); display.print("Check to make sure ");
    display.setCursor(0,16); display.print("Turrets powered up");
    display.setCursor(0,24); display.print("and ready to go...");
    display.display();
    display.clearDisplay ();
  }

  //=====  Turret Busy somewhere =====//
  while ((busyWhite == 1) || (busyRed == 1) || (busyBlue == 1)) {
    BusyScreen();
    ReadNet();
    digitalWrite (ledOkToGo, LOW);
    digitalWrite (ledNotReady, HIGH);
  }
  //=====  Read inputs  =====//
  x=analogRead(joyX);
  y=analogRead(joyY);
  pb1State = digitalRead(pb1); // "X"
  pb2State = digitalRead(pb2); // "T"
  pb3State = digitalRead(pb3); // "<"
  joyButtonState = digitalRead(joyButton); // "."
  
  //=====  Main Menu loop - Wait for input or Turret to get busy =====//
  if ((busyWhite == 0) && (busyRed == 0) && (busyBlue == 0)) {
    digitalWrite (ledOkToGo, HIGH);
    digitalWrite (ledNotReady, LOW);
    ReadNet();
    WriteNet();
    goState = 0;
    MainBusy = 0;
  }
  //=====  Use Joystick for menu selection  =====//
  if (MainBusy == 0) {
    if (y < 300) {--MenuSelection;}
    if (y > 700) {MenuSelection++;}
    if (MenuSelection > 3) {MenuSelection = 1;}
    if (MenuSelection < 1) {MenuSelection = 3;}
    MainMenu();
    }
  //=====  Check turret status  =====//
  if (pb2State == 0) {
    do {
      TurretCheck(); 
      pb3State = digitalRead(pb3);
      WriteNet();
      ReadNet();
      Serial.println();   
    }
    while (pb3State != 0); // press '<' button to exit status screen
  }
  //=====  Go to ARIA Mode  =====//
  if (((pb1State == 0)||(joyButtonState == 0)) && (MenuSelection == 1)){
    MainBusy = 1;
    goState = 1;
    digitalWrite (ledOkToGo, LOW);
    digitalWrite (ledNotReady, HIGH);
    AriaMode();
  }  
  //=====  Go to CHAT Mode  =====//
  if (((pb1State == 0)||(joyButtonState == 0)) && (MenuSelection == 2)) {
    MainBusy = 1; 
    goState = 2;
    digitalWrite (ledOkToGo, LOW);
    digitalWrite (ledNotReady, LOW);
    chatMode(); 
  }
  //=====  Go to MANUAL CONTROL Mode  =====//
  if (((pb1State == 0)||(joyButtonState == 0)) && (MenuSelection == 3)) {
    MainBusy = 1; 
    goState = 3;
    digitalWrite (ledOkToGo, LOW);
    digitalWrite (ledNotReady, LOW);
    digitalWrite (ledManual, HIGH);
    manualControl();
    } 
  else {
    digitalWrite(ledManual, LOW);
  }
  //====== Debugging Monitor ========//
  Serial.println();
  Serial.print(F("-MTC-  "));
  Serial.print(F("goState: "));Serial.print (goState); Serial.print ("   ");
/*  Serial.print(F("White :"));Serial.print (busyWhite); Serial.print ("   ");
  Serial.print(F("Red :"));Serial.print (busyRed); Serial.print ("   ");
  Serial.print(F("Blue :"));Serial.print (busyBlue); Serial.print ("   ");
  Serial.print(F("RedHr :"));Serial.print (RedHere); Serial.print ("   ");
  Serial.print(F("WhtHr :"));Serial.print (WhiteHere); Serial.print ("   ");
  Serial.print(F("BluHr :"));Serial.print (BlueHere); Serial.print ("   ");
  Serial.print(F("x :"));Serial.print (x); Serial.print ("\t");
  Serial.print(F("y :"));Serial.print (y); Serial.print ("\t");
*/
  Serial.print(F("pb1 :"));Serial.print (pb1State); Serial.print ("   ");
  Serial.print(F("pb2 :"));Serial.print (pb2State); Serial.print ("   ");
  Serial.print(F("pb3 :"));Serial.print (pb3State); Serial.print ("   ");
  Serial.print(F("jy4 :"));Serial.print (joyButtonState); Serial.print ("   ");
}
/////////////////////////////////////////////////////////////////// 
//======================= DISPLAY SCREENS =======================//
///////////////////////////////////////////////////////////////////

//------------- Opening Splash Screen ---------------//
void Splash (){
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.drawBitmap(0, 0,  logo32_glcd_bmp, 32, 32, 1);
  display.drawBitmap(46, 0,  logo32_glcd_bmp, 32, 32, 1);
  display.drawBitmap(92, 0,  logo32_glcd_bmp, 32, 32, 1);
  display.fillRect(0,24,124,8, BLACK);
  display.setCursor(4,24); display.print("== Turret Control ==");
  display.display();
}
//------------- Main Menu ---------------//
void MainMenu () { // display menu
  display.setTextSize(0); // display text on screen
  display.setTextColor(WHITE);
  if (MenuSelection == 1) {
    display.setTextSize(0);
    display.setCursor(0,24); display.print("----- SELECTION -----");
    display.setCursor(24,0); display.print(" == ARIA ==");
    display.setCursor(24,8); display.print("    Chat");
    display.setCursor(24,16); display.print("   Manual");}
  if (MenuSelection == 2) {
    display.setCursor(0,24); display.print("----- SELECTION -----");
    display.setCursor(24,0); display.print("    Aria");
    display.setCursor(24,8); display.print(" == CHAT ==");
    display.setCursor(24,16); display.print("   Manual");}
  if (MenuSelection == 3) {
    display.setCursor(0,24); display.print("----- SELECTION -----");
    display.setCursor(24,0); display.print("    Aria");
    display.setCursor(24,8); display.print("    Chat");
    display.setCursor(24,16); display.print("== MANUAL ==");}
  display.display();
  display.clearDisplay ();
}
//------------- Busy Screen ---------------//
void BusyScreen () { // display menu
  display.setTextSize(0); // display text on screen
  display.setTextColor(WHITE);
  display.setTextSize(0);
  display.setCursor(20,24); display.print("BUSY RIGHT NOW");
  display.setCursor(0,0); display.print("Sorry, I'm busy ");
  display.setCursor(0,8); display.print("right now, dealing");
  display.setCursor(0,16); display.print("with you people...");
  display.display();
  display.clearDisplay ();
}

//-------- Turret Check Screen ---------------//
void TurretCheck() { 
    display.setTextColor(WHITE);
    display.setTextSize(0);
    display.setCursor(26,24); display.print("Turret Status");
    
    display.setCursor(0,0); display.print("Red: ");
    if (busyRed == 1) {display.setCursor(38,0); display.print ("Busy");}
    else if (RedHere == 1) {display.setCursor(38,0); display.print ("Ready");}
    else {display.setCursor(38,0); display.print ("not available");} 
     
    display.setCursor(0,8); display.print("White: ");
    if (busyWhite == 1) {display.setCursor(38,8); display.print ("Busy");}
    else if (WhiteHere == 1) {display.setCursor(38,8); display.print ("Ready");}
    else {display.setCursor(38,8); display.print ("not available");} 

    display.setCursor(0,16); display.print("Blue: ");
    if (busyBlue == 1) {display.setCursor(38,16); display.print ("Busy");}
    else if (BlueHere == 1) {display.setCursor(38,16); display.print ("Ready");}
    else {display.setCursor(38,16); display.print ("not available");} 
    
    display.display();
    display.clearDisplay ();
}
/////////////////////////////////////////////////////////////////// 
//======================= ARIA MODE =============================//
///////////////////////////////////////////////////////////////////
void AriaMode () { 
  for (int i=0; i <= 10; i++){ // Make sure the turrets get the message.
    WriteNet();
   }
  MenuSelection = 1;
  int i=0;
  goState = 0;
  do {
    i++;
    display.setTextSize(2); // display text on screen
    display.setTextColor(WHITE);
    display.setCursor(8,8); display.print("ARIA MODE");
    display.fillRect(0,24,120,4, WHITE); // 
    display.fillRect(0+(i),2,40,4, WHITE);;
    display.display();
    display.clearDisplay ();
    pb3State = digitalRead(pb3);
    ReadNet();
    WriteNet();
    if (i > 80) (i = 0);
    if ((busyWhite == 0) && (busyRed == 0) && (busyBlue == 0)) {
      return;
    }
  }
  while (pb3State != 0); 
  for (int i=0; i <= 10; i++){
    WriteNet();
  }
}
/////////////////////////////////////////////////////////////////// 
//======================= CHAT MODE =============================//
///////////////////////////////////////////////////////////////////
void chatMode() {
   for (int i=0; i <= 10; i++){ // Make sure the turrets get the message.
    WriteNet();
  }
  Serial.println();
  digitalWrite (ledOkToGo, LOW);
  digitalWrite (ledNotReady, HIGH);
  MenuSelection = 2;
  int i=0;
  goState = 0;
  do {
    WriteNet();
    ReadNet();
    Serial.println();
    i++;
    display.setTextSize(2); // display text on screen
    display.setTextColor(WHITE);
    display.setCursor(8,8); display.print("CHAT MODE");
    display.fillRect(0,24,120,4, WHITE); // 
    display.fillRect(0+i,2,40,4, WHITE);
    display.display();
    display.clearDisplay ();
    pb3State = digitalRead(pb3);
    if (i > 80) (i = 0);
    if ((busyWhite == 0) && (busyRed == 0) && (busyBlue == 0)) {
      return;
    }
  }
  while (pb3State != 0);
  for (int i=0; i <= 10; i++){
    WriteNet();
  }
}
/////////////////////////////////////////////////////////////////// 
//======================= MANUAL CONTROL ========================//
///////////////////////////////////////////////////////////////////
void manualControl() {
  WriteNet();
  Serial.println();
  selectTurret = 2;
  MenuSelection = 3;
  display.setTextSize(2); // display text on screen
  display.setTextColor(WHITE);
  display.setCursor(0,8); display.print("MANUAL CTL");
  display.fillRect(0,24,128,4, WHITE); // 
  display.fillRect(0,2,128,4, WHITE);;
  display.display();
  do {
    x=analogRead(joyX);
    y=analogRead(joyY);
    pb1State = digitalRead(pb1); // Fire (GRN)
    pb2State = digitalRead(pb2); // Say random comment (WHT)
    pb3State = digitalRead(pb3); // Break (RED)
    WriteNet();
    Serial.println();
  }
  while (pb3State != 0);
  goState = 0;
  display.clearDisplay ();
  ReadNet();
  for (int i=0; i <= 10; i++){
    WriteNet();
  }
  Serial.println();
}

/////////////////////////////////////////////////////////////////// 
//========================= RECEIVING ===========================//
///////////////////////////////////////////////////////////////////
void ReadNet(){
  network.update();
  for (int i=0; i <= 10; i++){
    if ( network.available() )  {
      RF24NetworkHeader header;
      network.peek(header);
      network.read(header, &DataRead, sizeof(DataRead));
      if (header.from_node == 01){  // get data from RED
        busyRed = DataRead; 
        RedHere = 1;} // RED is recognized
      if (header.from_node == 02){
        busyWhite = DataRead;
        WhiteHere = 1;}      
      if (header.from_node == 03){
        busyBlue = DataRead;
        BlueHere = 1;}   
    } 
  }
}
/////////////////////////////////////////////////////////////////// 
//========================= SENDING ===========================//
///////////////////////////////////////////////////////////////////
void WriteNet(){
  network.update();
  int payload [] = {x, y, pb1State, pb2State, pb3State, goState}; 

  RF24NetworkHeader header14(RED);
  bool ok14 = network.write(header14, &payload, sizeof(payload));
  if (ok14) {
    Rx = 0;
    Serial.print("RED ok14   ");}
  else {
    Rx++;
    Serial.print("--------   ");}
     
  RF24NetworkHeader header16(WHT);
  bool ok16 = network.write(header16, &payload, sizeof(payload));
  if (ok16) {
    Wx = 0;
    Serial.print("WHT ok16   ");}
  else {
    Wx++;
    Serial.print("--------   ");}

  RF24NetworkHeader header18(BLU);
  bool ok18 = network.write(header18, &payload, sizeof(payload));
  if (ok18) {
    Bx = 0;
    Serial.print("BLU ok18   ");}
  else {
    Bx++;
    Serial.print("--------   ");}
    
  if (Rx >= 10){RedHere = 0;}  // to check if communication lost and record it.
  else {RedHere = 1;}
  if (Wx >= 10){WhiteHere = 0;}
  else WhiteHere = 1;
  if (Bx >= 10){BlueHere = 0;}
  else {BlueHere = 1;}
  
   Serial.print(F("Rx :"));Serial.print (Rx); Serial.print ("   ");
   Serial.print(F("RedHere :"));Serial.print (RedHere); Serial.print ("   ");
   Serial.print(F("Wx :"));Serial.print (Wx); Serial.print ("   ");
   Serial.print(F("WhiteHere :"));Serial.print (WhiteHere); Serial.print ("   ");
   Serial.print(F("Bx :"));Serial.print (Bx); Serial.print ("   ");
   Serial.print(F("BlueHere :"));Serial.print (BlueHere); Serial.print ("   ");
}

Custom parts and enclosures

Controller+-+Bottom+v1.stl
Controller%2B-%2BBottom%2Bv1.stl
Controller+-+Top+Plate+Yellow+on+top.stl
Controller%2B-%2BTop%2BPlate%2BYellow%2Bon%2Btop.stl
Controller+-+Screen+strap.stl
Controller%2B-%2BScreen%2Bstrap.stl
Controller+-+Top+Plate+Yellow+on+bottom.stl
Controller%2B-%2BTop%2BPlate%2BYellow%2Bon%2Bbottom.stl
Controller parts
The top plate ( 2 versions), bottom housing and little strap for LCD screen.

Schematics

Wiring Schematic - MTC
The wiring schematic I made to build the MTC
Wiring schem   portal turret master turret controller 34db13xb39

Comments

Similar projects you might like

Talking Portal 2 Turret Gun

Project tutorial by Novachris

  • 2,689 views
  • 4 comments
  • 22 respects

Buddy, the 3D-Printed Arduino Social Robot

Project showcase by Slant Concepts

  • 7,467 views
  • 6 comments
  • 64 respects

I Let Everyone On The Internet Control My X-Mas Decoration

Project showcase by Tobi_Lxtr

  • 6,387 views
  • 7 comments
  • 43 respects

I Let Everyone on the Internet Control My X-Mas Decoration 2

Project showcase by Tobi_Lxtr and wotanzero

  • 1,964 views
  • 2 comments
  • 11 respects

KjellFa Robot: A Replica Of The Trallfa Robot

Project showcase by Team Kjell Jaeger

  • 1,076 views
  • 0 comments
  • 2 respects

Otto DIY build your own robot in one hour!

Project tutorial by Camilo Parra Palacio

  • 131,363 views
  • 136 comments
  • 362 respects
Add projectSign up / Login