• Thor’s Well: Oregon’s Gate to Hell in the Pacific Ocean

    Take a three-hour drive down the west coast from Portland, and you’ll find yourself looking at a strange sight. The coast is beautiful, the waves are rolling, and then seemingly out of nowhere, the water goes — well, nowhere!

    During stormy weather and high tides, what appears to be a massive sinkhole shows up, sucking thousands of gallons of water into its dark depths. People refer to the strange sight in a few different ways. A “giant sinkhole,” a “gate to hell,” and the amusing “drainpipe of the Pacific.”

    But regardless of what it’s called, photographers flock to the site year-round to see its massive suction power.

    What is Thor’s Well?

    The publisher of Coast Explorer Magazine, Gary Hayes, figures the sinkhole is actually a sea cave that was dug out by waves over millennia. The surrounding shoreline is made of rough basalt, a volcanic mineral suitable to be carved out like this. At some point, the ceiling of the undersea cave collapsed and left openings for the ocean to sink its watery teeth into.

    While it’s completely dry at low tide, it starts its wondrous spectacle when the high tide approaches. Researchers estimate Thor’s Well is actually only around 20 feet deep, but it’s best not to test this theory yourself.

    If you do end up visiting the amazing area, please be careful! There are a few incidents every year where people get swept up by an unseen wave while taking pictures of the well.

    Thor’s Well photos and video

    A woman standing in front of Thor’s Well
    Photo by Falling-cosmosis, CC BY-SA 4.0
    Photo by John Fowler, CC BY 2.0
    Photo by Ashlyn G, CC BY 2.0

    How to get to Thor’s Well

    Thor’s well is located in Cape Perpetua, which is near the city Yachats, Oregon. Most tourists will visit the Cape Perpetua Visitors Center and park nearby at the Cook’s Chasm pull-out, then walk around the area to see the beautiful coastal views and of course, Thor’s Well itself. Here’s the exact address on Google maps, too.

    The best time to arrive is around an hour before the high tide that day to really take in the sights and watch the full process of this awesome force of nature.


    J.J. Pryor

    Head over here for more of my shenanigans.

  • Refactor! Another Look at the Arduino OBS Recording Sign

    Usually, when I start a new project I have dreams of polished, well-documented code tested for every edge case. But with a deadline quickly approaching, I often fall into the old familiar three-step routine. Google. Ctrl-c. Ctrl-v.

    That was the case with my recording sign for OBS from my recent article.

    As I was putting that article together, I notice that the Arduino code was heckin’ long. And when I was going through code for my Fakey Fakey, I realized that I could make a few small changes to the recording sign code to make it a lot shorter.

    In the end, I reduced the length of the sketch from 160 lines to a little over 50. I’m going to outline the changes, but if you just want the completed code, you can jump to here.

    code
    Photo by Markus Spiske on Unsplash

    Refactors

    First, I added an array and a string to store the IR codes and letter. Then, a variable to define the length of the array.

    const unsigned long REMOTE_IR_VALUES[] = {0XFFA25D, 0XFF629D, 0XFFE21D, 0XFF22DD, 0XFF02FD, 0XFFC23D, 0XFFE01F, 0XFFA857, 0XFF906F, 0XFF6897, 0XFF9867, 0XFFB04F, 0XFF30CF, 0XFF18E7, 0XFF7A85, 0XFF10EF, 0XFF38C7, 0XFF5AA5, 0XFF42BD, 0XFF4AB5, 0XFF52AD};
    const String KEY_VALUES = "QWERTYUIOPASDFGHJKLZX";
    const int NUMBER_OF_BUTTONS = 21;

    There is no length function for arrays in Arduino’s language and defining the length in a variable is common practice. Alternatively, the size can be calculated by getting the size (in bytes) of the array and then dividing that by the size of one of its values.

    const int NUMBER_OF_BUTTONS = sizeof(REMOTE_IR_VALUES)/sizeof(REMOTE_IR_VALUES[0]);

    The setup block goes unchanged. This next bit of code from the main loop is not a change, but I didn’t explain what it did in my original guide.

    if (results.value == 0XFFFFFFFF){
      results.value = key_value;
    }

    The remote controller sends the value FFFFFFFF whenever any button is held down. I’d much rather know which button is being held down, so the code above sets the result.value to the last button that was pressed.

    This line towards the end of the main loop keeps track of the last button pressed.

    key_value = results.value;

    OK, back to the refactoring. Now that all the IR codes nicely stored in an array, when the Arduino receives a signal, all it needs to do is loop through that array and find out which one matches.

    Once it finds a matching code, it can then send the corresponding keypress. But for the start and stop recording buttons, it also needs to interact with the light relay. So, let’s get those two cases out of the way first.

       if (results.value == REMOTE_IR_VALUES[0]) { 
          digitalWrite(RELAY_PIN, HIGH);
          Keyboard.press(KEY_VALUES[0]);
          delay(100);
          Keyboard.releaseAll();
        }
        else if (results.value == REMOTE_IR_VALUES[1]) { 
          Keyboard.press(KEY_VALUES[1]);
          delay(100);
          Keyboard.releaseAll();
          digitalWrite(RELAY_PIN, LOW);
        }

    Finally, if the button pressed wasn’t one of the first two, it’s time to loop through the rest of the buttons.

     else {
       for (int i = 2; i < NUMBER_OF_BUTTONS; i++) { 
         if (results.value == REMOTE_IR_VALUES[i]) {
           Keyboard.press(KEY_VALUES[i]);
           delay(100);
           Keyboard.releaseAll();
           break;
         }
       }
     }

    Notice that even though an array isn’t used to store the key values, the correct key can be snatched by passing an index to the variable.

    Keyboard.press(KEY_VALUES[i]);

    Conclusion

    The refactored code sure is a lot prettier, but is it any better? The short answer is no.

    Does it use less memory? Sure. But not enough to make a difference. Is it faster? Not really. It still has to do a conditional check against each IR code until it finds a match.

    But really… it looks so much better!

    Completed code

    #include <IRremote.h>
    #include <Keyboard.h>
    
    const int RECV_PIN = 11;
    const int RELAY_PIN = 10;
    unsigned long key_value = 0;
    IRrecv irrecv(RECV_PIN);
    decode_results results;
    
    const unsigned long REMOTE_IR_VALUES[] = {0XFFA25D, 0XFF629D, 0XFFE21D, 0XFF22DD, 0XFF02FD, 0XFFC23D, 0XFFE01F, 0XFFA857, 0XFF906F, 0XFF6897, 0XFF9867, 0XFFB04F, 0XFF30CF, 0XFF18E7, 0XFF7A85, 0XFF10EF, 0XFF38C7, 0XFF5AA5, 0XFF42BD, 0XFF4AB5, 0XFF52AD};
    const String KEY_VALUES = "QWERTYUIOPASDFGHJKLZX";
    const int NUMBER_OF_BUTTONS = 21;
    
    void setup() {
      irrecv.enableIRIn();
      pinMode(RELAY_PIN, OUTPUT);
    }
    
    void loop() {
      if (irrecv.decode(&results)){
        if (results.value == 0XFFFFFFFF){
          results.value = key_value;
        }
        // I like to use the first button as the start recording button
        if (results.value == REMOTE_IR_VALUES[0]) { 
          digitalWrite(RELAY_PIN, HIGH);
          Keyboard.press(KEY_VALUES[0]);
          delay(100);
          Keyboard.releaseAll();
        }
        // And the second button to stop recording
        else if (results.value == REMOTE_IR_VALUES[1]) { 
          Keyboard.press(KEY_VALUES[1]);
          delay(100);
          Keyboard.releaseAll();
          digitalWrite(RELAY_PIN, LOW);
        }
        // If it wasn't the first two buttons, check the rest
        else {
          for (int i = 2; i < NUMBER_OF_BUTTONS; i++) { 
            if (results.value == REMOTE_IR_VALUES[i]) {
    
              Keyboard.press(KEY_VALUES[i]);
              delay(100);
              Keyboard.releaseAll();
              break;
            }
          }
        }
    
        key_value = results.value;
    
        irrecv.resume(); 
      }
    }
  • Fakey Fakey! An Arduino-Based DIY Makey Makey

    When I was introduced to Makey Makey at a workshop hosted by a friend of mine last year, I saw great potential to spice up my English lessons. Then I saw the price tag.

    After a little research, I found this great instructable about how to make your own Makey Makey using an Arduino Leonardo. Besides the cost of the Leo, you can make a device that emulates most of the Makey Makey’s functions for a couple of bucks!

    I made one and it worked OK, but I found a way to make it better. Check out the materials and wiring diagram sections below to find out what I changed (OK, that sounds clickbaity; I just used different resistors).

    Note that I said my “Fakey Fakey” can do most of what the Makey Makey can do. This guide will not help you make the gamepad type board in the signature Makey Makey style. You will be able to use it for the famous Banana Piano though!

    Arduino DIY Makey Makey Fakey Fakey
    You can connect those to bananas!

    Table of Contents


    Materials Needed

    • 1 Arduino Leonardo
    • 1 Micro USB cable
    • 6 22MOhm 1/4 watt resistors
    • 1 Blank prototyping board at least 21 holes x 12 holes
    • 12 male header pins
    • 7 alligator clip jumper wires
    • Some scrap solid wire

    Notes for Materials:

    Board

    Arduino DIY Makey Makey Fakey Fakey
    This is also possibly a fake Leo…

    If you read my guide for making a recording sign for OBS, you might remember why we need to use a Leonardo instead of a regular Uno. The Leo is based on the ATmega32u4 chip which has a USB controller, thus letting it act as a keyboard. In fact, this is the same chip that the Makey Makey uses.

    Resistors

    10 mohm resistor
    10MOhm pictured

    In the guide that I mentioned above, they use 1 megaohm resistors. I followed that guide for my first Fakey Fakey. It worked OK when directly touching the leads But when grasped the ground wired in my left hand and touched a graphite drawing that was attached to a lead, it only worked sporadically. After some research, I bumped the resistors up to 10 megaohms, and everything worked fine. I’ll explain why in the wiring diagram section.

    Prototyping board

    Photo by Alexander Huayhua on Unsplash
    Like this but without the components

    I don’t know what to call these really. On Amazon, I see them sold as ‘blank PCB boards’ (printed circuit board boards?). But there is nothing printed on them. You could use a breadboard, but as I was planning on using my Fakey in a classroom setting with a bunch of rowdy third-graders, I needed something more rugged.

    Header pins

    Arduino DIY Makey Makey Fakey Fakey Header pins

    I had a bunch of these lying around because the Wemos boards that I love always come with a big selection of header pins. If you want the prototyping board to attach to the Arduino like a shield, you have to use male pins.

    Jumper wires

    Alligator Jumper Wire
    Soon it will be later ‘gator for one of those

    I’m not sure what gauge the wires I used are, but they are not much thicker than the jumper wires that come with Arduino kits. If you can find some with alligator clips on one end, and no connector on the other that would be ideal. Again, because of rambunctious kids, I opted to solder mine to the board.

    To keep things simple, I chose one color for the active leads, and green for the ground lead. Using multiple colors for the active leads might have confused the students.

    Scrap wire: Even with the lead wires soldered to the board, I was afraid that the little gremlins would rip the wires off the board. So I used some copper wire to secure everything to the board and take the stress off the solder points. Anything small enough to fit through the prototyping board holes will work.


    Wiring Diagram

    The wiring for this project is simple enough. The finished product will be six leads that when connected to ground will create a voltage divider.

    Fakey Fakey
    Image made with Fritzing
    Chabuduo

    So, as long as a lead is not connected to ground, there is no voltage division, and a relatively high signal is sent to the corresponding pin on the Leo. If you complete the circuit with ground, the voltage going to the pin will drop, and the Leo will see it as a low signal.

    Sparkfun has a great explanation of voltage dividers here. The tldr; version is that the two resistors of a voltage divider should have a difference in resistance by order of magnitudes.

    You may have noticed that each circuit in the wiring diagram above only has one resistor. That is because your body acts as the second resistor when you hold the ground wire in one hand, and the lead wire in the other.

    Schematic made with Fritzing
    The most natural of voltage dividers

    The resistance offered by the human body ranges from 10,000 ohms to 100,000 ohms depending on various factors. Using a 1 megaohm resistor should be perfect then, right? Even in bad conditions, it is still a magnitude larger.

    But the fun of Makey Makey is using random objects as buttons! However, once you add the resistance of the object to the resistance of your body, there is not enough difference in the two resistance values for the voltage divider to reduce the output to low.

    In writing this guide, I tested the resistance of one of the cards that I used in class. You can see the results below. This letter was colored in with a soft, 6B art pencil. I found that the resistance from the lower end of the C (marked 0) to the middle of the C was already 160 kiloohms!

    resistance impedance graphite
    Resistance to reading

    And that’s why Makey Makey uses 22 megaohm resistors, and you should too. I used 10 megaohm resistors and my Fakey Fakeys only worked about ninety percent of the time.


    Putting it together

    Did I mention that I used these Fakey Fakeys with classes of around 27 first through third graders? While I strongly encourage that you test your Fakey Fakey using a breadboard first, if I hadn’t made a more sturdy device, my class would have been over before it started.

    Before we get to any pictures, I just want to say, I know my soldering skills need some work! If you have any tips, leave them in the comments.

    Fakey Fakey DIY Makey Makey
    This thing again

    First, the headers should be added to the prototype board so you have a reference point when adding the rest of the components. Insert the long side of six of the header pins in the A0 – A5 female pins. Also, insert a ground and 5V on the same side of the board as the analog pins.

    On the opposite side of the board, insert two sets of two pins in any of the header pins on the Arduino. These pins are just to keep the Fakey Fakey in place, so you can use any pins you wish.

    Fakey Fakey
    Pins inserted

    After you’ve inserted all the pins, place the prototyping board on top of the Leo so that the short end of the new header pins go through the holes on the blank board. If your blank board is wider than the Arduino, let it hang over on the analog pin side. Also, if you are not using a double-sided board, make sure you have the solderable side facing up.

    After you solder the pins to the prototyping board, remove it from the Leonardo. It’s time to start adding resistors.

    How you lay out your components is up to you. The goal is to connect each of the analog pins to the 5V pin via a resistor. Along with the ground pin, each of the analog pins is also connected to a jumper wire.

    DIY Makey Makey
    Underside of my Fakey Fakey

    After you have finished soldering everything, secure the jumper wires to the board with some wire. This helps keep those overexcited students from ripping the wires off your Fakey Fakey.

    Your new Fakey Fakey can now be attached to the Leonardo!

    DIY Makey Makey
    Hope it fits!

    Fakey code

    The code from Simone Ferrecchia’s instructable can be downloaded here. The only change I made was to assign A – F to the pins instead of the generic Makey Makey keymap. I will briefly highlight the key components below.

    We’ll only need to import a few libraries. Keyboard will let the Leo send keypresses to the attached PC. MovingAvg smooths out the signal being received by the analog pins.

    #include <Keyboard.h>
    #include <movingAvg.h>

    Later, we’ll be using the analogRead function which reads the voltage on the analog pins and converts it to a value between 0 and 1023. The declarations below set the high and low threshold for the value read.

    const int PressedMaxThreshold = 200;
    const int ReleasedMinThreshold = 300;

    Also, we will need to know how many pins we are using later.

    const byte PinCount = 6;

    These two arrays map keys to the analog pins. Here, the original keymap, d-s-w-a-z-e to my own, A-B-C-D-E.

    const byte InputPins[PinCount] = {A0, A1, A2, A3, A4, A5};
    const char KeyCodes[PinCount] = {'A', 'B', 'C', 'D', 'E', 'F'};

    The next bit is really interesting. Arduino has a data object aptly named STRUCT. The following will define a data structure called TouchInput that holds the pin number, key letter, average input value, and state of each pin.

    struct TouchInput
    {
      byte analogPin;
      char keycode;
      movingAvg filter = movingAvg(20);
      boolean wasPressed = false;
    };

    Passing the integer 20 to movingAvg means that it will use the last 20 data points to create an average. Next, we create a TouchInput object called Pins.

    TouchInput Pins[PinCount];

    In the setup loop, besides starting serial data transmission, all we need to do is use the two arrays we created earlier to populate the Pins structure with some data. We also initiate the moving average for each pin.

    for (int i = 0; i < PinCount; i++)
      {
        Pins[i].analogPin = InputPins[i];
        Pins[i].keycode = KeyCodes[i];
        Pins[i].filter.begin();
      }

    Because of the beautiful way everything is stored in the Pins object, the main loop is very straightforward. If you haven’t already, you can download it with the link at the top of this section.

    We simply loop through Pins and check to see if it’s receiving a value below the low threshold. If it is, we know the voltage divider we created earlier is in action, meaning someone has completed the circuit. A keypress is then sent.

    You can upload the sketch to your Leo with Atom or VS Code with the PlatformIO plugin. Of course, you could use the official Arduino IDE, but I’d wait until Arduino Pro IDE has been released.


    My simple English lesson using my Fakey Fakeys is coming soon!

    DIY Makey Makey Raspberry Pi
    Fakey Fakey in action
  • A DIY Remote Control Arduino Recording Sign for OBS!

    A little rough around the edges, but it gets the job done!

    In the lesson I teach this semester, I have 50 minutes to take a group of around 14 fourth-graders that I haven’t seen since last semester and produce a video of them making a weather forecast. Due to the layout of the room, last year when I taught this lesson I was constantly running from the control section of our green screen studio to the recording area. So, this year I decided to make a remote controller.

    In the beginning, I wanted to make a display that would show which scene was being recorded. I spent many frustrating hours trying to get that done with a Pi Zero and a P10 LED panel. But, as the time for classes to start was creeping up I decided to ditch that and just make a simple recording sign with an Arduino Leonardo.

    Check it out in this video. If you want to try it yourself, detailed instructions are below!

    Table of Contents

    Materials needed

    • 1 Arduino Leonardo
    • 1 IR sensor
    • 1 remote control
    • 1 relay module
    • 1 small USB desk lamp
    • 3 – 6 male to female jumper wires
    • 3 – 6 male to male jumper wires
    • 1 micro-USB cable
    • 1 20K Ohm 1/4 watt resistor
    • 1 small (170 holes or smaller) breadboard (optional)
    • 1 cardboard box or other enclosure
    • Various arts and crafts supplies

    Notes for materials:

    Arduino Leonardo IR relay
    A rare pic of an Arduino Leo in the wild

    Board: Any development board using the ATmega32u4 microcontroller can be used for this project. That particular chip has a USB transceiver that can emulate keyboard keypress signals. The Arduino Leonardo is a common board that uses this controller. Other boards with the same chip include the Yun and the Micro. An Arduino Uno will not work for this project!

    TSOP382838 Arduino
    Many parts of this project follow the ancient Taiwanese philosophy of Cha-bu-duo

    IR sensor: I used a TSOP38238 for this project, but any similar IR receiver will do. If you use a sensor that is part of a module, you may or may not still need the 20K resistor.

    ELEGOO remote IR
    Easily fits into a breast pocket for the teacher on the go

    Remote control: Theoretically you could use any remote control you have lying around. I recommend using one of the little guys that come with most Arduino starter kits for two reasons. One, the Arduino will be able to decode its signal without having to mess around the IR library options. Two, you probably won’t have to make any changes to the button values in the code I supplied below.

    5V relay made in China will probably set your house on fire
    This relay was made in a country other than Taiwan

    Relay module: There are many packages similar to Keyes SRly on the market and they are all pretty much the same. You do need to pay attention to whether or not the relay closes when it receives a high signal or low signal. Also, make sure the relay control is made for 5 volts (there are a few 3.3-volt models out there).

    I regret using this relay a little because they are LOUD. But, in the end, the click the relay makes when it is powered on never made it into the video recordings. If you are concerned about the noise, you can opt to use a transistor instead.

    LED lamp recording sign Arduino
    Pay attention to wire placement BEFORE you hot glue everything in

    Desk lamp: I chose a USB lamp because I knew I could use a computer as a power supply. A development board will not supply enough juice for the amount of light needed for this project. You can use any kind of low DC voltage lamp.

    Do NOT use a lamp that uses AC power from your household mains unless you know what you are doing! The type of relay I used is generally not considered safe enough for mains voltage even though they are rated for it.

    Also, consider how much heat the light you choose will generate and whether or not the enclosure you are using can handle it.

    breadboard Arduino recording sign where's the butter
    You can use a wooden one if you want to keep it old school

    Breadboard: If you are not comfortable using a soldering iron, you can use a breadboard to connect everything.

    Nobody will ever see this side

    Enclosure and art supplies: I used the cardboard box an old router I of mine came in. For the front of the box, I used some really thick card stock with a red-tinted plastic document folder taped behind the letters. Use your imagination!

    Back to top

    Wiring diagram

    You”ll need a bigger light! Diagram thanks to Fritzing

    Wiring everything up is pretty straight-forward, but there are a few things to note. First, you should confirm the pinouts for the particular relay and IR sensor you use. Every component is a little bit different.

    Second, your set-up may vary a little based on the type of lamp you use. If you are using a USB lamp, then you just need to carefully skin off a little bit of the outer insulation on the cord. I would do this on a spot on the wire about 10 inches away from the lamp. You should see a red wire and a black wire, and possibly a green and white one too. Red is 5 volts and black is negative. Cut either of these two and attach them to the relay as shown.

    Third, you can use a breadboard to insert the resistor between the signal wire and the voltage wire going to the IR receiver. This resistor may not be necessary at all, depending on the IR sensor that you use. However I find that these sensors will often work fine for a minute or two, but then start sending faulty data once they heat up a little. Adding a connection to the voltage wire via a resistor smooths out the signal somehow (magic?).

    Back to top

    Installation

    OBS LED remote control recording sign
    Looks good from my house!

    Installation is as easy and wiring everything up was. Again, though, a few points.

    • The IR receiver needs to be exposed! It won’t work inside the box.
    • Just cut a slit to slide the sensor into, no need to do anything fancy.
    • Some say hot glue can damage PCBs. I used it anyway
    • Before you secure the Leo, make sure you will be able to plug the USB line in
    • I probably should have thrown some tape or nail polish on those resistor leads

    Back to top

    Remote settings

    **** If while uploading you’re getting an error about the programmer not responding, kick your Leo in the butt.

    If you are using a remote similar to the one I recommended you can probably just go ahead and skip to the main code. But if you trying to repurpose a clicker or if the recording sign isn’t responding to your remote, the code below will help you see what the IR sensor is receiving.

    If this is your first time uploading a sketch to an Arduino board, this official how-to can help you out. If you’ve been playing around with Arduino for a while but still using the official Arduino IDE, may I ask you, have you tried PlatformIO with Atom or VS Code? It’s dreamy. Anyways, upload this to your board:

    #include <Arduino.h>
    #include <IRremote.h>
    
    const int RECV_PIN = 11;
    IRrecv irrecv(RECV_PIN);
    decode_results results;
    
    void setup(){
      Serial.begin(9600);
      irrecv.enableIRIn();
      irrecv.blink13(true);
    }
    
    void loop(){
      if (irrecv.decode(&results)){
            Serial.println(results.value, HEX);
            irrecv.resume();
      }
    }

    After uploading the sketch, keep your board connected and open the serial monitor with Ctrl+Shift+M. If everything is going according to plan, you should see a code every time you hit a button on your remote. If that’s the case, record each button’s value and move on to the next section.

    Back to top

    Code

    There’s nothing complex in the code or logic for this project, so I’ll just briefly explain a few things. First, we need to include a library for the IR sensor and one to send keypresses over USB with:

    #include <IRremote.h>
    #include <Keyboard.h>

    Next, some variables to hold pin numbers for the IR receiver and relay, and one variable to hold the value of the last IR signal received:

    int RECV_PIN = 11;
    int RELAY_PIN = 10;
    unsigned long key_value = 0;

    Finally, for the last declarations before we get to the setup block, we initialize a couple of objects from the IRremote library, a receiver and a decoder:

    IRrecv irrecv(RECV_PIN);
    decode_results results;

    There are only two things to set up. Using enableIRIn function from the IRremote library to set up the IR sensor, and the basic function Arduino function pinMode, set the relay pin to output mode:

    irrecv.enableIRIn();
    pinMode(RELAY_PIN, OUTPUT);

    On the main loop. Use a conditional to listen for a signal coming from the IR receiver:

    if (irrecv.decode(&results))

    If there is a hex value coming from the receiver, the loop will then use a series of conditionals to figure out which button is being pushed, and then send a corresponding keypress over USB. For example, the value transmitted by my remote control is FA857. To mark this as a hex value, we add the prefix ‘0XF’. If the signal being received matches 0XFFA857, then we can assume that somebody’s sausage finger is pressing the volume down button, and can carry out an action. I chose to simulate someone pressing the I key on the PC’s keyboard with the following code:

    if (results.value == 0XFFA857){
              Keyboard.press('I');
              delay(100);
              Keyboard.releaseAll();
    }

    Note that if you don’t include some kind of key press release function, the Leonardo will send the keypress indefinitely. Keyboard.releaseAll() will stop all keypress simulations.

    For the buttons that will start and stop the recording, we also need to send a high or low signal to the relay control. A high signal will close the circuit and keep it closed until the signal is switched to low. This is what my code looks like:

    if (results.value == 0XFFA25D){
        digitalWrite(RELAY_PIN, HIGH);
        Keyboard.press('Q');
        delay(100);
        Keyboard.releaseAll();
    }
    
    else if (results.value == 0XFF629D){
        Keyboard.press('W');
        delay(100);
        Keyboard.releaseAll();
        digitalWrite(RELAY_PIN, LOW);
    }

    You can, and should, assign a keypress for every button on the remote. I used a very long chain of if-else statements. There may be some slick way to make the code shorter by using an array and converting an integer to a character, but I didn’t see the need.

    You can find the complete sketch below. From here you can either jump to how to set up OBS or go back to the top of the page to review.

    #include <IRremote.h>
    #include <Keyboard.h>
    
    int RECV_PIN = 11;
    int RELAY_PIN = 10;
    unsigned long key_value = 0;
    IRrecv irrecv(RECV_PIN);
    decode_results results;
    
    void setup() {
      irrecv.enableIRIn();
      pinMode(RELAY_PIN, OUTPUT);
    }
    
    void loop() {
      if (irrecv.decode(&results)){
            if (results.value == 0XFFFFFFFF){
              results.value = key_value;
            }
    
            else if (results.value == 0XFFA25D){
              Keyboard.press('Q');
              delay(100);
              Keyboard.releaseAll();
              digitalWrite(RELAY_PIN, HIGH);
            }
    
            else if (results.value == 0XFF629D){
              Keyboard.press('W');
              delay(100);
              Keyboard.releaseAll();
              digitalWrite(RELAY_PIN, LOW);
            }
    
            else if (results.value == 0XFFE21D){
              Keyboard.press('E');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF22DD){
              Keyboard.press('R');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF02FD){
              Keyboard.press('T');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFFC23D){
              Keyboard.press('Y');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFFE01F){
              Keyboard.press('U');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFFA857){
              Keyboard.press('I');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF906F){
              Keyboard.press('O');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF6897){
              Keyboard.press('P');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF9867){
              Keyboard.press('A');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFFB04F){
              Keyboard.press('S');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF30CF){
              Keyboard.press('D');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF18E7){
              Keyboard.press('F');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF7A85){
              Keyboard.press('G');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF10EF){
              Keyboard.press('H');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF38C7){
              Keyboard.press('J');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF5AA5){
              Keyboard.press('K');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF42BD){
              Keyboard.press('L');
              delay(100);
              Keyboard.releaseAll();
            }
    
            else if (results.value == 0XFF4AB5){
              Keyboard.press('Z');
              delay(100);
              Keyboard.releaseAll();
            }
            
            else if (results.value == 0XFF52AD){
              Keyboard.press('X');
              delay(100);
              Keyboard.releaseAll();
            }
    
            key_value = results.value;
            
            irrecv.resume(); 
        }
    }

    Back to top

    Configuring OBS

    OK, I have a confession to make. You can get 90% of the functionality of this recording by plugging a wireless keyboard to your PC. But you wouldn’t get the recording light and that is lame.

    In OBS settings, there is a section for setting up hotkeys. That’s what we will use to turn the keypresses coming from our Leo into an action in OBS. So, first, click on settings (in the cluster of buttons in the lower-left corner).

    OBS settings button
    Click it. I dare you.

    Then, find the ‘Hotkeys’ tab on the left and give that a click.

    OBS Hotkeys tab
    Spicy.

    Finally, click in the text field next to the action you want to assign a hotkey to, and press the desired key. I like to use the power button on my remote to start recording, and the key next to it to stop recording. The key values assigned to those buttons per our Arduino sketch are Q and W respectively. You could use the same letter to start and stop recording and it will act as a toggle, but I prefer to use two separate buttons.

    OBS start stop hotkey
    Assign some hot keys

    Besides start and stop, you have nineteen other buttons to play with. I used mine to change scenes, but you can also use them to hide elements within a scene.

    Assign some more hot keys. What are you waiting for?

    Back to top

    A few (very) minor quirks

    I have been using this setup for four classes a day, four days a week for a few months now, and it has performed almost flawlessly. I’ve only noticed one unexpected behavior and another separate glitch with OBS or the remote that I cannot figure out.

    First, the power button on the remote control for the studio’s air conditioner is the same as one of the values on the recording sign remote. I would have thought there were so many available codes that overlap would be unlikely, but I guess not?

    Second, for some reason when I push the stop recording button, OBS will change the scene to the second one in my queue. It doesn’t show up in the recording, so it’s not a big deal. I’ve tried changing the button and hotkey for the second scene, but it still jumps there regardless of which scene I started from. The only way to squash the behavior is to make sure OBS is not the window in focus.

    Back to top

    Kick your Leo in the butt (programmer not responding)

    Leonardo is a great board but sometimes it just refuses to be programmed. It’s gotten to the point where I thought that I had a defective board. I did a little research, and it turns out that sometimes the last sketch you uploaded does some to block the on-board programmer.

    To fix this, you have to tap the reset button on the board near when you see the “Uploading” in the console window at the bottom of whichever IDE you are using. I’ve seen a few descriptions of the correct timing of the button press on various forums, but all I can say is it takes some practice.

    That’s it! If you have any problems, please post them in the comment section.

  • Adding a PCIe SSD to a Non-Fusion Late 2014 Mac Mini — A Guide

    Recently, I found out some of my web pages didn’t look so great on Apple devices, so I picked up a late 2014 Mac Mini so I could have a device running Safari natively. Unfortunately, after upgrading it to the latest version of macOS, it was almost too sluggish to use. So started a long, misguided journey to carry out one of the few upgrades available for this particular model of the Mini — adding a PCIe SSD.

    Note that had I done a little more research before starting this adventure, I could have avoided a bunch of headaches. If your planning on doing a project like this but haven’t bought any hardware yet, please skip to my rant at the end of the article for some shopping advice. But if you have found yourself in possession of some components that aren’t playing nice together, read on.

    Table of Contents

    Hardware

    The first sign that I was going to run into trouble was how smooth the hardware installation went. One of the reasons I chose to add an M.2 drive instead of replacing the spinny 2.5″ disk with an SSD, is that doing the latter seemed like a real pain on this model of Mini. Adding an M.2, on the other hand, was accomplished in just a few steps. I started out with this adapter that I found on my local version of eBay:

    The SV agreement doesn't bug me so much, it's the effing space after the colon...

    Things were looking good. The model number of my Mini is A1347, and it say[s] right there “this adapter only fit 2014 A1347…”. This is what it looks like after I installed the hard drive that I chose based on its read/write speed vs price ratio (Crucial P1):

    The fastest Mac Mini NVMe PCIe SSD on the ROC

    Installing the SSD assembly in the Mini is as simple as popping off the round black plastic bottom and removing six Torx screws. Carefully lift the edge of the metal cover that is furthest away from the WIFI antennae away from the case (the antennae wire is still attached).

    You caught me. This is actually an after photo. You can see the SSD under the antennae there.

    Inside the case, you will see the PCIe plug as pointed out in the photo below by red arrows. Looking at the blue arrows, there is a hole to screw the SSD adapter to the case, but the hole in my adapter didn’t line up with it without some bending. Luckily, the foam on the bottom of my adapter included adhesive (green arrow).

    With the new hard drive firmly stuck in place, it’s time to put the covers back on and take it for a test drive! This was so easy!

    Disk Utility doesn’t see the new drive

    To see the new hard drive, click the Launchpad icon in your dock, and look for Disk Utility in the Other folder. Then click the view button in the upper-left-hand corner, and click devices.

    If you see your new drive, congratulations! Your hard drive was probably one of the ones that are fully compatible with macOS. If you don’t see the drive, as it was in my case, don’t give up hope! Formatting the drive might get your Mini to see the new disk. All you need is a PC with a real operating system and an empty M.2 slot or an external M.2 enclosure. After you have found a way to connect the drive to a PC, you can follow the Windows instructions below or jump to instructions for Linux.

    Windows

    I followed a guide that I found here.

    There are several Windows apps out there that can format drives to an Apple file system, but I prefer to use the all-powerful and automatically installed Diskpart. To open Diskpart, first, open a command prompt by pressing Windows+R. Then open the command prompt with:

    cmd.exe

    Press Enter. In the command prompt enter:

    diskpart

    A dialog will pop up asking if you want this program to make changes to your computer. Say yes, then click yes and Diskpart will open in a new window. First, you need to find out the disk number of the new drive. This can be accomplished by entering:

    list disk

    You should see something like the following.

    You caught me again. This wasn't actually the same drive as above. That one was a 400GB drive.

    This next step is very important. If you don’t choose wisely, you might accidentally format the wrong disk, and lose your data, your OS, or maybe your will to live. If you aren’t sure which drive to use, you can eject the new drive, and run the previous command again to see which one disappeared. Once you are are sure you know which disk is the one you want to format it, select it with the following command. REPLACE THE # WITH THE CORRECT DISK NUMBER!

    select disk #

    Sorry for yelling, but I can’t count the number of times I’ve blindly cut and paste commands from a tutorial and wondering why I get errors when I was supposed to replace bits of the command with my own info. Anyways, moving on. Now clean the disk and make a new partition with the following commands.

    clean
    create partition primary id=af

    I’ve used Diskpart more than a few times in the past, but this was the first time I had seen the ‘id=af’ parameter. I Googled around a bit and didn’t find a clear explanation, except that whatever follows the id tag lays out block sizes, etc. I assume ‘af’ stands for Apple filesystem. Moving on. If there isn’t any smoke coming out of the back of your computer, check to see that the partition appears with the following.

    list partition

    And the partition should be listed.

    Finally, select the partition, mark it active, and exit Diskpart.

    select partition 1
    active
    exit

    At this point, you can eject the SSD or power off your PC and remove the drive, and move on to the next step.

    Linux

    Usually, we carry out tasks with a shiny GUI in Windows, and terminal commands in Linux. However, for this project, I recommend the opposite. But don’t worry, the program of choice for making an Apple filesystem disk, GParted, isn’t included with every distro of Linux, so you might still be able to fire up that terminal! I’m running Ubuntu, and thus use the APT package manager. If you are using a different package manager, please change the following to match your system. First, if you haven’t checked for updates lately:

    sudo apt-get update

    Then install GParted and the package that allows it to use the HFS+ format. HFS+ is not the latest Apple file system, but your Mini will recognize it and it is the most recent one that can be used by GParted. Run the following:

    sudo apt-get install -y gparted hfsprogs

    Then launch GParted from your application launcher or with the following command:

    sudo gparted

    Yes, you must run it as sudo. That’s because if you make a wrong move you can really mess your system up. As long as you get the next step right, though, you won’t have any problems. In the drop-down menu at the top-right corner of GParted, select the disk you want to format.

    Hopefully, you will be able to judge which one it is by its capacity and since this a new drive, it will show up as unallocated. It usually is the last one, but if you are not sure, eject the new drive and unplug it to make sure. Next, from the Device menu, select Create Partition Table.

    Select mac as the partition table type. This is the point where you won’t be able to recover your system if you selected the incorrect hard drive, so cross your fingers and click Apply.

    The thing about accidentally deleting your system partition is that your OS is loaded in RAM, and you can keep going for a while thinking everything is OK. I’m sure that didn’t happen to you. The next step is to create a new partition:

    Select HFS+:

    And apply changes by clicking:

    And that’s it! After a short wait, your SSD is ready to be re-installed in your Mini.

    What now?

    Hopefully, your new disk now shows up in Disk Utility. However, as indicated by the warning message about your Mini not being able to read the disk you may or may not have gotten, the disk is not ready to go yet.

    I do have screenshots for this part, but I decided that because macOS is such an intuitive, user-friendly OS, I would save everyone’s bandwidth. All you need to do is open Disk Utility, right-click on the new hard drive (it should be a long string of numbers and letters, anything but ‘APPLE HDD’), and select Erase. Give the volume a new name and click Erase. After that finishes, you can right-click on the new volume and select Covert to APFS.

    From here, the drive is ready to use as you see fit. If you want it to speed up your user experience though, you will have to move macOS to the new drive. The way to accomplish this will vary from user to user. In my case, I had just started using this machine, so a fresh install via USB installer was convenient. If you want to migrate an established install, there a restore option might be more suitable.

    How can an i5 processor run at 1.4ghz?

    This Mac Mini was definitely an impulse buy for me. As I said at the top of the page, I needed something that could run Safari to view some web pages I had been working on, and not wanting to trust a simulator, I ran straight to Shopee (an Asian version of eBay) and bought the cheapest Mini I could find. All of the listings showed the CPU as being an i5, but none of them said which model of i5, but I said to myself, “Self, it’s an i5! It will have plenty of horsepower for what I want it for! Two cores minimum, running somewhere between 2.5ghz and 3.5ghz.”

    The other thing I noticed is that the 2012 model of the Mini was selling for a slightly higher price. I didn’t notice it too hard to save thirty bucks though, and went ahead and ordered the 2014 model. After I upgraded it to Catalina (the latest version of Xcode wouldn’t run on High Sierra) and started a simulation of an iPhone, I noticed that everything was a little sluggish.

    So, I opened a terminal checked the system resources with the following command:

    top

    This is what I saw:

    The first thing I saw was all the stuck processes. I had never seen that before, not even on a Pi Zero. The second thing was the high load average. This i5 is a dual-core, so ideally I’d like to see a load of 2 or less. Weirdly, the CPU was barely doing work. It seems the bottleneck was being caused by a shortage of memory. Unfortunately, this RAM is not user-upgradable on this model of Mini. So, my only hope was to make the virtual memory faster. That’s when I decided to try and put an SSD in this cute little beast.

    The 2012 model is fully upgradable! And it has a 2.5ghz i5! Sure, it is a third-generation chip instead of the 2014 model’s fourth-generation, but that is a pretty big difference in clock speed. But the ability as a user to add more RAM is the reason the 2012 model is still the winner for a cheap, second-hand Mac Mini.