How To Set Up A TFT Screen On Arduino With The RA8875

In a previous video, we looked at how to attach a TFT LCD screen to a Raspberry Pi to build a small-form-factor project. But what if your project doesn’t need quite as much horsepower? What if you just need to display shapes and colors on a screen with no operating system and nearly instant boot times? This is where a TFT LCD screen on Arduino comes in handy. In this video, we’ll look at how to set up a TFT LCD screen on Arduino with the RA8875 breakout board from Adafruit then walk through how to display various shapes and colors in the sketch.

PARTS/TOOLS:

(Most of these can be found in the Arduino Starter Kit available here)

Arduino UNO

Solderless breadboard

Jumper (Dupont) Wire

TFT LCD Display

Adafruit RA8875 Breakout Board

RESOURCES:

RA8875 Datasheet

Adafruit RA8875 Arduino Library

THE CIRCUIT:

It’s a fairly simple wiring job here using the hardware SPI interface on the Arduino. CLK, MISO, and MOSI connect to 13, 12, 11 respectively. CS, RST, and INT are defaulted to 10, 9, and 3, but can be reassigned in the sketch. Of course, the display is not illustrated, but it is connected to the 40-pin ribbon connector on the left.

THE SKETCH:

/******************************************************************
 This is an example for the Adafruit RA8875 Driver board for TFT displays
 ---------------> http://www.adafruit.com/products/1590
 The RA8875 is a TFT driver for up to 800x480 dotclock'd displays
 It is tested to work with displays in the Adafruit shop. Other displays
 may need timing adjustments and are not guanteed to work.

 Adafruit invests time and resources providing this open
 source code, please support Adafruit and open-source hardware
 by purchasing products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries.
 BSD license, check license.txt for more information.
 All text above must be included in any redistribution.
 ******************************************************************/

#include 
#include "Adafruit_GFX.h"
#include "Adafruit_RA8875.h"


// Library only supports hardware SPI at this time
// Connect SCLK to UNO Digital #13 (Hardware SPI clock)
// Connect MISO to UNO Digital #12 (Hardware SPI MISO)
// Connect MOSI to UNO Digital #11 (Hardware SPI MOSI)
#define RA8875_INT 3
#define RA8875_CS 10
#define RA8875_RESET 9

Adafruit_RA8875 tft = Adafruit_RA8875(RA8875_CS, RA8875_RESET);
uint16_t tx, ty;

void setup()
{
  Serial.begin(9600);
  Serial.println("RA8875 start");

  /* Initialize the display using 'RA8875_480x80', 'RA8875_480x128', 'RA8875_480x272' or 'RA8875_800x480' */
  if (!tft.begin(RA8875_480x272)) {
    Serial.println("RA8875 Not Found!");
    while (1);
  }

  Serial.println("Found RA8875");

  tft.displayOn(true);
  tft.GPIOX(true);      // Enable TFT - display enable tied to GPIOX
  tft.PWM1config(true, RA8875_PWM_CLK_DIV1024); // PWM output for backlight
  tft.PWM1out(255);

  // With hardware accelleration this is instant
  tft.fillScreen(RA8875_WHITE);

  // Play with PWM
  for (uint8_t i=255; i!=0; i-=5 )
  {
    tft.PWM1out(i);
    delay(10);
  }
  for (uint8_t i=0; i!=255; i+=5 )
  {
    tft.PWM1out(i);
    delay(10);
  }
  tft.PWM1out(255);

  tft.fillScreen(RA8875_RED);
  delay(500);
  tft.fillScreen(RA8875_YELLOW);
  delay(500);
  tft.fillScreen(RA8875_GREEN);
  delay(500);
  tft.fillScreen(RA8875_CYAN);
  delay(500);
  tft.fillScreen(RA8875_MAGENTA);
  delay(500);
  tft.fillScreen(RA8875_BLACK);

  // Try some GFX acceleration!
  tft.drawCircle(100, 100, 50, RA8875_BLACK);
  tft.fillCircle(100, 100, 49, RA8875_GREEN);

  tft.fillRect(11, 11, 398, 198, RA8875_BLUE);
  tft.drawRect(10, 10, 400, 200, RA8875_GREEN);
  tft.fillRoundRect(200, 10, 200, 100, 10, RA8875_RED);
  tft.drawPixel(10,10,RA8875_BLACK);
  tft.drawPixel(11,11,RA8875_BLACK);
  tft.drawLine(10, 10, 200, 100, RA8875_RED);
  tft.drawTriangle(200, 15, 250, 100, 150, 125, RA8875_BLACK);
  tft.fillTriangle(200, 16, 249, 99, 151, 124, RA8875_YELLOW);
  tft.drawEllipse(300, 100, 100, 40, RA8875_BLACK);
  tft.fillEllipse(300, 100, 98, 38, RA8875_GREEN);
  // Argument 5 (curvePart) is a 2-bit value to control each corner (select 0, 1, 2, or 3)
  tft.drawCurve(50, 100, 80, 40, 2, RA8875_BLACK);
  tft.fillCurve(50, 100, 78, 38, 2, RA8875_WHITE);

  pinMode(RA8875_INT, INPUT);
  digitalWrite(RA8875_INT, HIGH);

  tft.touchEnable(true);

  Serial.print("Status: "); Serial.println(tft.readStatus(), HEX);
  Serial.println("Waiting for touch events ...");
}

void loop()
{
  float xScale = 1024.0F/tft.width();
  float yScale = 1024.0F/tft.height();

  /* Wait around for touch events */
  if (! digitalRead(RA8875_INT))
  {
    if (tft.touched())
    {
      Serial.print("Touch: ");
      tft.touchRead(&tx, &ty);
      Serial.print(tx); Serial.print(", "); Serial.println(ty);
      /* Draw a circle */
      tft.fillCircle((uint16_t)(tx/xScale), (uint16_t)(ty/yScale), 4, RA8875_WHITE);
    }
  }
}

 

How To Solve Rubik’s Cube Every Time

As a child of the 1980s, I’ve been fascinated by the apparent complexity of Rubik’s Cube–the world-famous puzzle toy designed by Hungarian architect Emö Rubik–but I could never solve one past peeling the stickers and placing them in the correct position (a trick my uncle taught me that could be used to “mess with the Cube nerds”). I’ve had a newer model sitting around for a few years, and as one of my “COVID Side Projects”, I finally learned the proper way to solve the Cube.

Get the Original Rubik’s Cube on Amazon.com

The Algorithms:

Building the yellow cross:

If you have no yellow edge pieces on the top face, then F U R U’ R’ F’

If you have a line of yellow pieces through the center, then reorient the entire cube so that the line is vertical to your perspective and F U R U’ R’ F’

If you have two yellow edge pieces making an “L” shape, then turn the top layer until the edge pieces are in the 12:00 and 9:00 positions and F U R U’ R’ F’

Solving the yellow face:

If there are 0 or 2 yellow corners showing, then R U R’ U R U2 R’

If there is 1 yellow corner showing (“the goldfish”), then turn the top layer until the yellow corner piece is in the lower left of the face (the “goldfish” will be diving to the left) and R U R’ U R U2 R’

Set up the top layer:

If there are no matching corner pieces on any faces of the top layer, then L’ U R U’ L U R’ R U R’ U R U2 R’

If there is a set of matching corner pieces on a face, then reorient the cube so that face is on the left and L’ U R U’ L U R’ R U R’ U R U2 R’

Continue until there are matching corner pieces on all 4 faces

Solve the top layer:

F2 U’ R’ L F2 L’ R U’ F2

Continue until the top layer is solved, then rotate the top layer to solve faces as necessary.

How To Automatically Select Random Google Wear Android Watch Faces

Say you’re an aficionado of novelty like I am, but you also don’t like being overwhelmed by the paradox of choice when it comes to the myriad Google Wear watch faces. I have a huge array of Android watch faces installed, and I would like to have a way to randomly select a new watch face every day. To automatically select a random Google Wear Android watch face, you just need to have the Tasker app installed on your primary Android device with the AutoInput plugin also installed.

Once those prerequisites are met, you can create a task in Tasker called “Switch Watchface” and populate it with the following code:

A1: Array Push [ Variable Array:%watchfaces Position:9999 Value:/*ENTER THE NAME OF THE WATCH FACE HERE*/ Fill Spaces:Off ]
A2: Array Push [ Variable Array:%watchfaces Position:9999 Value:/*USE AS MANY 'ARRAY PUSH' LINES AS YOU HAVE WATCH FACES YOU WANT TO SELECT FROM*/ Fill Spaces:Off ]
A3: Variable Set [ Name:%count To:%watchfaces(#) Recurse Variables:Off Do Maths:Off Append:Off Max Rounding Digits:0 ]
A4: Variable Randomize [ Name:%random Min:1 Max:%count ]
A5: Variable Set [ Name:%watchface To:%watchfaces(%random) Recurse Variables:Off Do Maths:Off Append:Off Max Rounding Digits:0 ]
A6: Popup [ Title: Text:Switching watch face to %watchface Background Image: Layout:Popup Timeout (Seconds):1 Show Over Keyguard:On ]
A7: Launch App [ Package/App Name:Android Wear Data: Exclude From Recent Apps:Off Always Start New Copy:On ]
A8: Wait [ MS:0 Seconds:1 Minutes:0 Hours:0 Days:0 ]
A9: AutoInput Action [ Configuration:Type: Text
Value: More
Action : Click Timeout (Seconds):23 ]
A10: Wait [ MS:0 Seconds:1 Minutes:0 Hours:0 Days:0 ]
<check if on screen>
A11: AutoInput UI Query [ Configuration:Variables: faces() Timeout (Seconds):20 ]
A12: If [ %faces(#?%watchface) eq 0 ]
A13: Flash [ Text:Didn't find %watchface. Scrolling... Long:Off ]
A14: AutoInput Action [ Configuration:Type: Id
Value: com.google.android.wearable.app:id/watch_face_inner_frame
Action : Scroll Forward Timeout (Seconds):20 ]
A15: Wait [ MS:0 Seconds:1 Minutes:0 Hours:0 Days:0 ]
A16: Goto [ Type:Action Label Number:1 Label:check if on screen ]
A17: Else
A18: Flash [ Text:Found %watchface. Switching to it! Long:Off ]
A19: End If
A20: AutoInput Action [ Configuration:Type: Text
Value: %watchface
Action : Click Timeout (Seconds):20 ]
A21: Wait [ MS:0 Seconds:1 Minutes:0 Hours:0 Days:0 ]
A22: AutoInput Action [ Configuration:Type: Text
Value: Set on watch
Action : Click
Is Tasker Action: false
Check Screen State: false Timeout (Seconds):1 Continue Task After Error:On ]
A23: Wait [ MS:0 Seconds:1 Minutes:0 Hours:0 Days:0 ]
A24: Go Home [ Page:0 ]

What this task does is it creates an array populated by the names of each watch face to be selected from. Then it creates a variable %count whose value is the number of items in the array. Then it creates a %random variable with a value of a randomized number between 1 and the value of %count.  With as the random number selected, it assigns the %watchface variable the name of whichever watch face is located at that position in the array.

With a %watchface defined, Tasker launches the Android Wear app, looks for the word “More” on the screen, taps it to bring up the list of watch faces, looks for the name of the watch face (it’s important that the %watchfaces array is populated by exact matches, otherwise Tasker will not find them!), taps the menu icon for the watch face, then taps “Set on watch”, setting the watch face.

If Tasker doesn’t find the name of the watch face, it will automatically scroll, looking for the name of the face.

Once the watch face has been set, Tasker will exit the app back to the home screen. The whole process takes about 5 seconds or so, depending on how many times Tasker has to scroll.

For your convenience, I have saved this task as an XML file that can be directly imported into Tasker. You can download that file here.

Installing Mojave on a 12-year-old iMac

Let’s take a trip, shall we? I used to use this 2010 iMac at my office before it became hopelessly outdated. It’s spent close to the last half decade in storage at the shop (just off the left side of the screen in videos, actually). I’m gonna try to repurpose it

I forgot the account password, so I’m going to reinstall the OS

Silly me! Despite the wallpaper, it already has Mavericks on it

I wiped the hard drive to perform a fresh install of Mavericks. For some reason, I *really* hate entering my Apple ID. Probably because there’s always about 8 hoops I have to jump through (yay multi-layered security?) because I refuse to carry the Fruit Fone.

I actually have 2 of these lumbering beasts. Maybe I’ll put some flavor of Linux on the other for funsies.

Actually, thinking about it. It’ll probably be more useful running a newer version of Ubuntu than trying to force a newer version of OS X.

Still, I’M GONNA HAVE FUN TRYING!!!

Well, THAT took forever….

Well, there’s no big 🚫 over the icon. Let’s see if we can just run the app

Poop. That would’ve been TOO easy.

Gonna try DOSDude’s patcher and see what happens. dosdude1.com

Basically, this application patches the installers for newer versions of OS X so they’ll work on older Mac hardware. I’ll have to do this for every incremental update through Mojave.

This is promising….

Thinking aloud: If I do end up putting Linux on one of these, will it still have the chime? I don’t think it will, but I don’t recall ever trying to find out.

I came to really enjoy the chime. I was *mad* when Apple silenced it with the High Sierra “upgrade”.

Pretty sure the chime, then, is part of the OS and not, say, a bootloader? That would take some research.

MUAHAHAHAHA!!!

Seriously, though: I relish when something that *shouldn’t* work does. I feel like Alan Cumming in GoldenEye (even if I am just using a publicly available tool written by someone else)

Another half an hour waiting for the OS to install. I’m going to put this aside for the night and get some sleep.

Got up this morning and went to check on the Sierra install, but instead I got a big 🚫. Something obviously went wrong, now to see if I can recover.

“Success! Success! They’ve done it! They’ve done it!”

The trick is that you have to use a very specific version of the installer app with the patch–otherwise it will not install correctly. I managed to find a copy of 12.6.06 on archive.org and it worked a charm!

Let’s see how far we can ride this train. Hold on to your butts!

Aww yiss

LET IT RIDE!!!

It is done! I present to you, in sheer defiance of Cupertino, a 12-year-old iMac running the last version of OSX to support 32-bit apps! As much as I’d like to try, I’m going to hold off on upgrading to Catalina–this will serve my needs just fine.

How To Make An Aviation Cocktail

A few weeks ago, my wife and I started cleaning out her mother’s kitchen of the ~40 years of detritus she’s been holding on to. In the fray, I managed to save some really beautiful glassware such as these coupes.

My mother-in-law works for a law firm and one of the attorneys gave her these phenomenal sets of crystalware at Christmas. She didn’t want to part with them out of guilt, but she also *never* used them.

“Take them!” she yelled with her thick Cuban accent. “If you’ll use them, take them!”

Now, I’ve never really been one for fancy glassware (excepting the various branded pint glasses I collected for years), but I’ve always secretly wanted to have a really cool bar setup for entertaining.

So, under the guise of acquiring some really nice crystal wine glasses that Barbie has always wanted to use, I brought home my favorites.

Tonight I’m going to (forgive the expression) break them in.

Now, over the last 18 months, I’ve become a bit of a cocktail aficionado. I mean, I was already into the tiki scene since, like, 2008-ish, but I never really thought about learning to make these drinks myself. All that changed when COVID decided that I couldn’t go out anymore.

At the same time, I rekindled my appreciation for Alton Brown by watching Quarantine Qitchen on YouTube. Brown and his wife Elizabeth live in my hometown and I’ve always considered him kind of a “TV uncle” along with LeVar Burton and Adam Savage.

Seeing Brown’s eclectic collection of glassware inspired me to look around for some interesting pieces on my own, but eBay prices are way too high and thrifting really wasn’t a thing for a while (I still want to pick up a couple of those vintage Delta in-flight service glasses, though!)

So, tonight, after an extremely difficult month, I’m finally going to sit down and enjoy one of my favorite beverages. It’s a classic gin cocktail from the dawn of an era:

The Aviation

First, though, I need to decide on a glass: Do I go with the classic “Marie Antoinette” round coupe or the more stylistic, angled “Martini” coupe?

For tonight, I’m going with Marie just because she’s about a half an ounce larger (also so I can pretend I’m Leonardo DiCaprio in that Great Gatsby GIF)

via GIPHY

Okay, got a clean glass. First thing we’re gonna do is fill it with ice and set aside to chill.

First, let’s talk about hardware. Unlike many modern bartenders who prefer a fancy Boston shaker, I’m very partial to the Parisian shaker.

It’s got a nice period silhouette, but it’s only 2 pieces–unlike the cobbler variety most used by home gamers.

I like it over the Boston because (A) it just looks better, (B) doesn’t require a glass, and (C) seals better.

The Boston is for showing off, and I never strain with it (but many bartenders will tout that benefit anyway)

I can’t recommend a graduated jigger enough! Measurements get much faster and easier than eyeballing. Japanese or hourglass, it doesn’t matter (I just happen to like the look of the Japanese style)

Last thing is a good spring strainer. A Boston shaker can be used as a makeshift strainer, but if I’m going to strain a drink, I’m going to strain it!

This guy will get all those tiny ice flakes out so they don’t mess with the experience of a neat drink.

I’m going with the classic Savoy recipe here, but I’ve been playing around with the recipe some. Biggest change is that I’m going to be using violet liqueur instead of creme de Violette–because that’s what they had at Bevmo!

It’s really such a small amount only for coloring that it doesn’t appreciably affect the flavor. Despite what some purists will tell you!

Okay, let’s start with the good stuff! I am a fan of Sapphire going way back to my early 20s, but I haven’t bought it in over a decade. I switched to beer for a long time, and I picked up Beefeater for my tiki stuff since it was going to be so heavily mixed.

Not that there’s anything wrong with Beefeater! But if I’m going to be doing mostly gin, I want to taste my favorite.

That’s 1.5oz into the tin!

Maraschino liqueur fell by the wayside for a long time, but it is absolutely delicious and adds a nice, round cherry flavor to boozy cocktails like the Aviation, Manhattan, and Hemingway. We’re going to use 0.75oz for our concoction.

I don’t have any fresh lemons, but this will do for tonight. I need to get a couple of citrus trees for the apartment…. 😉 Throw in 0.5oz of lemon.

This is *not* the good stuff, but it’s what they had. Again, it’s really mostly for color and just a slight perfume of violet. Use 0.25oz at most.

Throw the ice from your glass in the tin and shake! (Yes, shake!) You don’t have to get violent here, just enough to mix and get the tin cold.

via GIPHY

Strain it into that chilled glass. Don’t that look something!

Now for the garnish! Most plebes use some of those neon red cherries that come in Dole fruit cocktail, but I’m gonna fancy it up a little bit for my new glass.

My step-sister and I discovered these at Trader Sam’s when she was out here for a conference a couple years ago. They’re stupid expensive, but so goddamned delicious!

And there we have it! Of course, keeping with the aviation theme, I had to use one of my Southwest Airlines swizzle sticks!

Delicious! And, as promised, my best Gatsby selfie 🍸

Now, there is very likely a contingent of you who reel at the thought of shaking an aviation (“You’ll bruise the gin!”). I get it, especially with a top shelf gin like Sapphire, you don’t want to lose those nice top notes from the juniper and pine.

However, we have to take into consideration the complexity of the drink. By adding Maraschino, lemon, and Violette, we’re replacing much of the top notes with these new flavors. And when I shake a drink like this, I go just long enough to get the tin cold. On top of that, the ice dilutes the drink a little bit to give it a smoother texture. The shake also creates a tiny bit of foam, reminiscent of cirrus clouds, which help give the drink it’s distinct appearance (the drink is supposed to evoke the idea of an open sky, hence the name).

On the other hand, if this were a martini, you’d better believe I’d be stirring!

At the end of the day, it’s your drink! Make what you want how you want, and don’t let anyone shame you for being unorthodox. Just don’t be that asshole who drives drunk or ends the night praying to the porcelain gods!

Have fun, y’all. Hug your loved ones if you can.

A Homebrew Geiger Counter Circuit

As discussed previously, a Geiger counter is a fairly simple circuit that takes a high voltage and runs it through the switch-like Geiger-Müller tube and into the meter mechanism. To build a Geiger counter, we need to look at three basic parts: a high voltage source, the GM tube itself, and the counting mechanism.

3 Parts of a Gegier Counter
3 Main Parts of a Geiger Counter

DIY Geiger Counter Circuit

After a bit of research, I decided to base my own circuit on this design by markusb on RobotShop.com. In this circuit, built around a Röhre ZP-1320 GM tube, the high voltage source is provided by a 40:1 transformer and charge pump that supplies the requisite 500V. Of course, a transformer requires an AC input, so a 555 timer in astable mode feeds an alternating 5V to the transformer. The metering side of the circuit uses a 555 timer to generate an electrical pulse that can be fed into a microcontroller or analog counter.

555-based Geiger counter by markusb (RobotShop.com)
555-based Geiger counter by markusb (RobotShop.com)
SBM-20 Geiger-Müller Tube
SBM-20

For Project Pripyat, I’m using a Soviet-era SBM-20 (СБМ-20) GM tube that we had lying around the shop, and I’ll need to adjust the circuit somewhat to power it. I like the oscillator-transformer concept, and I’ll keep that in tact, but I think I can simplify the charge pump somewhat and still provide a reliable 400V to power my tube.

I also want to run my Geiger counter off a rechargeable LiPo battery, so I’ll add a 3.7V pack and an Adafruit Powerboost 500 module to provide a stable 5V (and handle battery charging). On the counter end, I’d like to be able to have extended functionality such as data logging or triggering various outputs, so I’m going to send the GM tube pulse to an Arduino Nano (after passing through a voltage divider, of course).

After quite a bit of trial and error, I’ve come up with this:

Project Pripyat Breadboard LayoutProject Pripyat Schematic

As you can see from the schematic, I’m using a different transformer and a simpler rectifier circuit than the model’s diode ladder. I also tweaked the oscillator slightly, using only an N-channel MOSFET instead of the NPN-MOSFET combination in the original design. The NPN transistor, though, serves a new purpose as the pulse generator that drives the digital input for the Arduino. From this configuration, I can add a piezo buzzer, LED, analog meter, or any other output as well as save data to memory or pipe it to a computer via serial connection.

There are a couple of important things to note with this circuit: First, I have to reiterate that it is a high voltage circuit and you will likely get popped pretty hard if you’re not paying attention. I accidentally touched one of the capacitor terminals on the charge pump during testing and received quite an unpleasant surprise! It’s unlikely that you will suffer any lasting damage, though, but caution is the order of the day whenever high potentials are involved. Second, if you choose to use a different GM tube for this circuit, you will need to adjust the resistor and capacitor values in the charge pump. The film capacitors that I used are rated for up to 700V and the first version I assembled (using 5 capacitors) built a potential in excess of 600V (and literally screamed at me). Third, and it should go without saying, this is a device used to measure ionizing radiation and ionizing radiation is a hazardous phenomenon. Please take all precautions to limit your exposure to beta and gamma particles by using alpha sources for testing and storing your radioactive samples in appropriately shielded containers.

Using An Arduino To Drive A Geiger Counter

It's a moving coil-style analog meter
Moving coil-style analog meter from a CDV-700 (jonshobbies.com)

Traditionally, a Geiger counter like the CDV-700 series is a completely analog device–the device output is driven entirely by the analog electronics. Electrical pulses passing through the Geiger-Müller tube during ionizing events are run through a speaker cone, generating the characteristic clicks of a Geiger counter. That voltage is also directed into an electromechanical meter that displays the average clicks over a given amount of time. For Project Pripyat, I wanted to have the option to drive several different kinds of output with minimal rewiring and I want to be able to save data gathered and send it to a computer. The easiest way to accomplish all of these objectives was to pipe the GM tube output into an Arduino, and I just so happened to have a bunch of Nanos in my parts bin!

Arduino code for a Geiger counter

The Nano has a pretty low tolerance for excessive voltage on its digital pins, so I had to incorporate a pretty hefty voltage divider into my circuit design (from 400V down to <5V) to prevent frying the thing. The code itself was a work of trial and error, mostly playing around with various ways to drive the analog meter. Since the Arduino does not have true analog output (only pulse-width modulation), I decided to let digital pulses “kick” the needle into the appropriate position on the meter. The more frequent the pulses, the more the needle will be displaced. It’s basically PWM, but there’s no averaging being done in software. The v1.0 code, therefore, is rock-basic simple. It is almost entirely pin definitions, based on the “Blink” example sketch, but it’s snappy and serves its purpose as a “minimum viable” solution.

const byte interruptPin = 2;
attachInterrupt(digitalPinToInterrupt(interruptPin), blink, LOW);

In the sketch, we define several pins on the Arduino and how they’ll be used. Pin 2 is going to serve as an interrupt and is attached to the pulse generator in the circuit. In my Geiger counter circuit, the pulse generator is actually reverse-biased to become an interrupt generator. Every ionizing event detected by the GM tube causes the 2N2222 transistor to ground the signal line connected to the Arduino which will be picked up as the interrupt signal. Upon detecting the interrupt, the Arduino will jump to the blink interrupt service routine which changes quickly changes pin states for the LED, meter, and piezo buzzer. The counter sketch has a resolution of about 3 milliseconds which is significantly lower than the theoretical dead time on the SBM-20 tube, but this project is more of a concept demonstration and exploratory toy than anything else, so I’m not worried about it.

One other item to note: When simply given a high/low signal on one pin, the piezo buzzer’s clicks are extremely soft. Connecting the ground terminal of the buzzer to another digital pin held low and swapping the high/low pins during the ISR effectively doubles the deflection of the buzzer and results in a much more satisfying click!

/* Project Pripyat v1.0
 *  by Matthew Eargle https://airbornesurfer.com
 *  for element14 Presents
 *  CC-BY-SA 2018 AirborneSurfer
 */
 
const byte ledPin = 13;
const byte interruptPin = 2;
const byte speakerPin = 5;
volatile byte state = LOW;
volatile byte speaker = LOW;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(speakerPin, OUTPUT);
  pinMode(interruptPin, INPUT);
  pinMode(6, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(9, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, LOW);
}

void loop() {
  digitalWrite(ledPin, LOW);
  digitalWrite(speakerPin, LOW);
  digitalWrite(6, HIGH);
  digitalWrite(12, LOW);
  delay(3);
}

void blink() {
  digitalWrite(ledPin, HIGH);
  digitalWrite(speakerPin, HIGH);
  digitalWrite(6, LOW);
  digitalWrite(12, HIGH);
}

How To Replace A Failing Disk in FreeNAS (or Increase Storage Space)

My initial design for Project Gibson was a single vdev (FreeNAS’s term for a group of drives acting as a single device) with 6 drives in a single pool, and I would extend as I go. When planning vdevs, they should consist of drives of similar size, as the RAIDZ2 protocol will treat all the disks in the vdev as having the same capacity as the smallest drive in the vdev. For example: A vdev containing drives that are 4T, 3T, and 2T will treat them all as being 2T; replace the 2T drive with another 4T drive and the array will still only be seen as 3T each. When I set up FreeNAS, I consolidated all of my data on the smallest drives I had and created my first vdev with the 6 largest drives available.

A Word About ZFS RAID Protocols

I chose RAIDZ2 as it is a nice balance between space availability, read-write times, redundancy, and longevity. The RAIDZ2 is a double-parity system that distributes data in such a way that any two drives within a vdev could fail and the dataset would still survive. After having suffered data loss from accidental disk drops, and knowing that I would be storing more critical backups on this array, I opted for a little more protection at the cost of less storage space.

Once the initial vdev was set up and data was transferred from the smaller drives, I began to assemble a second vdev of the smaller drives. My plan is to, over time, replace failed drives within the vdevs with larger capacity drives until the entire system is uniform. As storage will eventually come down in price, I can begin replacing drives with even larger capacity units as my storage needs grow.

Extending a ZFS Pool

Once my second set of 6 drives was ready, I installed them as normal, then fired the system back up. Under the Storage > Pools dialog, click the cog icon (Settings) to open the “Pool Actions” dialog, then click “Add Vdevs”. In the Pool Manager dialog, you’ll be presented with two lists: Available Disks on the left and Data Vdevs on the right. The newly installed drives will be listed under Available Disks, select each of them and click the right arrow to add them to a new vdev.

FreeNAS Pool Manager
FreeNAS Pool Manager

With the new drives added to a new vdev, you can click “Add Vdevs” to add the new vdev to the existing pool. Once back on the Pools dialog, click the cog again and click “Extend Pool” to stripe the available space across the two vdevs, adding the second vdev’s available space to the total. This procedure is covered in more detail in the FreeNAS User Guide, section 9.2, but this will serve as a general guide to the process.

Replacing (Or Upgrading) Disks In A ZFS Pool

I have 2 use cases for replacing disks in a pool: The first is obviously replacing a failed drive and the second is replacing a smaller drive with a larger capacity one. Both of these involve the same offline-replace-online procedure, so I’ll cover them at the same time. This is where my decision to use RAIDZ2 really becomes apparent, as the procedure puts the parity of the vdevs in a weakened state by removing a drive instead of simply connecting another drive to the motherboard (I only have 12 drive connections available). With RAIDZ1, any mishaps during the process would render the data on the vdev unrecoverable, so with RAIDZ2 I have some parity during the replacement process–just in case.

To replace a disk within a pool, first navigate to the Storage > Pools dialog in the web interface, click the cog in the right corner, then click “Status”. You will be presented with a list of physical drives within the pool.

FreeNAS Pool Status dialog
FreeNAS Pool Status dialog. Yes, my pool is named “balabushka” after George Balabushka and is probably the only thing with his name that I will ever own.

Locate the disk you need to replace from the list, then click the stacked dots (Options) to the right of the line item and then “Offline”. This will take the drive offline for replacement. I usually cross-reference the drive label with the Storage > Disks dialog to get the disk’s serial number (which I have printed on the exposed side of the physical disks in the case) to make identifying the physical disk easier. Once you have offlined the drive and noted its serial, shut down the system and replace the physical drive. Boot the system back up, reconnect to the web interface, and return to the Storage > Pools > Status dialog (under the settings menu). Click the options menu for the offline drive and click “Replace”. Select the serial number of the new disk and click “Replace Disk”. The vdev will remain in a compromised state until the resilvering process completes and the data is restored in its original form on the new disk.

The smaller vdev has experienced more failed disks than the larger array, so my upgrade plan has served well here. As disks wear out, I am replacing them with new 4T disks (although FreeeNAS only sees them as 1T for the moment). Eventually, the smaller vdev will consist of 6x 4T disks to match the first, and I will finally have doubled my available storage. No telling how long that will take, though, but I still have plenty of space to spare for now, and I’m weighing the option of adding my stack of 2.5″ USB drives in a third vdev. More on that if and when it comes.

Using Pi-Hole For Whole-Network Ad Blocking

Internet advertising was once a fairly benign minor annoyance that spiraled into the oft-lampooned dark world of pop-ups on top of pop-ups. In these early years, simple ad-blocking plugins for popular browsers like Netscape Navigator (and its successor, Mozilla Firefox) were enough to keep these nuisances at bay, but as advertising technology got more sophisticated, Web 2.0 became more commercialized, and surveillance capitalism became the business model du jour, ad-blockers have moved from convenience to absolute necessity while simultaneously become more difficult to implement at the browser level.

Most commercial websites now can detect ad-blocker software and refuse to serve content in response. In these cases, it becomes necessary to allow some level of ad servicing–usually through whitelisting specific sites–but this also comes at the extended (and immeasurable) cost of privacy. Advertising networks track users’ movements across the internet and serve consistent ads based on that user’s specific browsing history. In this Brave New World, a user’s very identity is a commodity that must be exchanged in order to participate in society. One must sell their soul in an asymmetrical exchange to merely experience the world outside while the buyer resells the soul indefinitely and reaps exponential profits.

Pi-Hole is an application that adjusts the balance of power back into the hands of the user by allowing ads to be served, but intercepting and dumping them into a “black hole” before being displayed. Additionally, Pi-Hole blocks trackers from “phoning home” by directing their calls into the same virtual black hole, thus allowing the user to retain control over their identity. The result is a cleaner, safer, and more pleasant user experience with faster page load times and less noise in the browsing experience. Granted, Pi-Hole does have a few flaws that are more difficult to work around (such as Google’s first-party tracking), but by-and-large, the application is well-worth the few minutes that it takes to set up.

In my current network arrangement, I have Pi-Hole installed on a Raspberry Pi Zero W plugged into a 5V wall wart and connected to the WiFi. It’s not the fastest arrangement, of course, but it has a very low power consumption and serves my needs at the moment. I have also tried using Pi-Hole installed on an Ubuntu virtual machine in my FreeNAS server, but I noticed that it resulted in a noticeable increase in system resources (and noise, considering the case sits behind my sofa) so I migrated to the Pi. If you have the hardware to spare, I would probably recommend a Pi3B+ or better as the right nexus of speed and power consumption.

Installation on the Pi is fairly straightforward, following the directions of the Pi-Hole website. The most difficult part seems to be arranging the DNS settings on your router (which isn’t altogether difficult, but it doesn’t enjoy the virtue of an automatic installation script). I will put together a setup guide for the FreeNAS instance in a future number, for those who may be inclined (or whenever I upgrade my server and stuff it in an air-conditioned closet).

Pi-Hole is not a silver bullet to stop advertising and privacy-invading browser trackers wholesale, but I do recommend it as another tool in the ever-growing arsenal that users can employ to reclaim some of their own power on the internet. I’m still playing around with the idea of obfuscation, and seeing if it is even worth considering (it probably isn’t, but it may just be for fun), and I have been implementing other changes that have made my life–both online and especially off–better and less stressful than it used to be.

How To Install Pi-Hole on FreeNAS

I’ve been playing around a lot with my FreeNAS installation since assembling it last year as my “Pandemic Project” (which, of course, would become the first of many), and I’m constantly looking for new things to implement. Advertising has been a thorn in my side since the early days of the internet, so it seemed only logical that I should see what all the fuss with Pi-Hole was about!

Pi-Hole is most readily installed on a Raspberry Pi, but I’m trying to consolidate as much of my infrastructure as possible, so I thought I might have a go getting it working on the server. Unfortunately, FreeNAS is based on BSD while Pi-Hole is written for Linux (so there’s no plugin available), so we’ll have to install it on a virtual machine.

Installing Ubuntu Server on a Virtual Machine

The first thing we’ll need, of course, is the installation media. There’s a flavor of Pi-Hole written specifically for Ubuntu, so that seems to be the logical choice! My recommendation is to install the most compact version available, and the netboot installer image allows you to pick Ubuntu Server with minimal options. It’s a little difficult to find the correct download, so just grab the URL below:

http://archive.ubuntu.com/ubuntu/dists/bionic/main/installer-amd64/current/images/netboot/mini.iso

Of course, if Bionic Beaver is outdated, just change the /bionic directory to the current version!

Back in FreeNAS, go to the Virtual Machines menu and add a new Linux VM. Give it a name that you’ll remember (“pihole” is a solid choice) and set the virtual CPU count to 1 and the memory size to 512MiB. On the Disks page, create a new AHCI disk and set its Zvol location to /data/pihole and size to 4GiB. When you get to the options for installation media, select “Upload an installer image file” and choose the mini.iso file you downloaded earlier. Once all your settings are configured, you can boot the virtual machine and install Ubuntu. The VNC option opens a virtual terminal that will allow you to connect to and interact with the virtual machine through the installation process.

If you are prompted for DNS servers, use Google’s (8.8.8.8 and 8.8.4.4) as a default for now.

When the install completes, Ubuntu will prompt you to remove installation media and reboot. Once you are disconnected from the VNC, stop the virtual machine and remove the installation media by deleting the CDROM from the “Devices” list under the virtual machine options.

Setting up Pi-Hole

Restart the virtual machine and connect to the VNC. Log into Ubuntu and invoke the following commands:

sudo apt update
sudo apt upgrade
sudo apt install net-tools
sudo apt install wget

The first thing we need to do is set up a static IP address for the virtual machine. Use ifconfig to find the local IP address.

In this example, the device is called ‘enp0s4’.

We will now need to change the settings for this device by editing the netplan config. Invoke the following command:
sudo nano /etc/netplan/01-netcfg.yaml

You will need to change edit the file so that it look like the image below. Pay special attention to the number of spaces for each indentation.

Once this is complete, reboot the VM.

After rebooting and logging back into Ubuntu, install Pi-Hole using the automatic installation script, just like you normally would.

wget -O basic-install.sh https://install.pi-hole.net

sudo bash basic-install.sh

Once the script finishes, you can access the web UI by navigating to [PIHOLEIPADDRESS]/admin. Make sure to change your password!

The last thing you’ll need to do is set up your router’s DHCP settings, but that’s best explained by Pi-Hole’s own documentation.