Home Blog Page 19

5 Ways To Use Exposed Brick As An Effective Feature

Exposed brick is rapidly becoming a popular home and business design trend and it’s easy to see why. In the right quantity and setting, it looks fantastic and it requires little to no maintenance. Fortunately, you don’t have to renovate a 19th century home or apartment to get the look either, there are loads of modern panels, veneers and even stick-ons which can be used to create the perfect feature wall.

Here are some of our favourite ways to incorporate exposed brick into your next home renovation project.

Also, have a look at these 12 home design trends making their mark on home improvement in 2020.

Create A Small Statement Wall

A statement wall in a hallway or room is one of the easiest and cheapest ways to incorporate exposed brick into your home. The colours in the brick generally bring enough colour into a room to work well with whites, greys and beiges.

Bedroom Statement Wall
Source: The Spruce

Bright green indoor plants also work well with the earthly feel that exposed brick brings.

Statement Wall In Living Room
Source: The Nord Room

Rustic hanging pendant lights and planters add an element of depth to the room and compliment the horizontal lines in the brick wall.

Statement Wall In Living Room 2
Source: Rock My Style

DIY wall hangings are a great way to decorate a feature wall on a budget.

Exposed Brick Floor

An exposed brick floor is perfect for a homestead or cottage, although you’ll need to go with real brick for this one.

Exposed Brick Floor Of Cottage
Source: Edward Deegan Architects

It’s also a durable and easy to keep clean flooring option for your home’s entrance.

Exposed Brick Hall Entrance
Source: Hammers n Hugs

Kitchen Splashback

A splashback which doubles up as a feature wall in your kitchen is easy to keep clean and compliments your neutral countertops. Make sure that you get it properly sealed so that it’s easy to clean any cooking splashes and spills.

Exposed Brick Splashback
Source: Herlong Architects

This splashback is not even real brick, or a veneer, it’s an easy to install and clean printout and it still looks great.

Printed Kitchen Splashback
Source: Opticolour

Showcasing Shelves

Installing shelves over an exposed brick feature wall is perfect for a home office.

Shelving On A Brick Feature Wall
Source: Life On Virginia Street

Or to display your favourite wines in your home’s minibar or kitchen.

Home Wines Shelves Exposed Brick
Source: The Green Spring Home

Line An Archway

If you’ve already got an archway built into your home as a room or hallway divider, adding some brick veneers in the correct places make for an amazing feature.

Exposed Brick Archway Over Kitchen
Source: The Cottage Market

A smaller archway in a hallway or to replace a door is a cheaper alternative, but equally effective.

Exposed Brick Archway
Source: Evolution of Style

Have you used an exposed brick wall as a feature or incorporated one into your home decor? Let us know in the comments section below.

DIY Arc Reactor CPU Performance Monitor, Powered By An Arduino

In this project, I’m going to be showing you how to turn one of these popular DIY arc reactor kits into a useful Arc Reactor CPU performance monitor for your computer. It plugs into one of your computers USB ports and displays your CPU performance on the OLED display and the arc reactor pulses according to your CPU usage, increasing the pulse frequency with an increased CPU load.

I started out by assembling an Arc Reactor kit, which has a built-in USB powered LED light and that got me thinking of a way to turn it into something useful for my desk. I initially thought about using an Arduino to make the LED pulse as some sort of readout or indication. This lead me to my computers CPU performance and I then decided to add the OLED display for a more accurate readout as well.

The arc reactor LED and OLED display are powered by an Arduino Uno in the base, which receives updates on the computers CPU usage every two and a half seconds and adjusts the pulse duration accordingly.

On the computer’s side, a python script reads the CPU performance data from a Hardware Monitoring application and posts this data to the Arduino through a serial communication port.

The device doesn’t need any external power, it is powered by the USB port as well.

Here’s a video of the build and the Arc Reactor in use, read on for the full step by step instructions as well as the code and print file downloads.

What You Need To Build Your Performance Monitor

You’ll also need to 3D print the components for the stand. If you don’t have a 3D printer and you enjoy DIY projects and building things, you should really consider getting one. They’ve become a lot more affordable and really expand your workshop capabilities.

How To Build Your Own Arc Reactor CPU Performance Monitor

I’ve split the build process up into a couple of steps to make it easier to follow. We’ll start by assembling the Arc Reactor (with is done according to the enclosed instructions), then solder the electronics, then assemble the base and finally upload the sketch and run the Python script.

Assemble The Arc Reactor

Theses Arc Reactor kits have been around for a couple of years and most of them are reasonably good quality, so you shouldn’t have any trouble following the enclosed instructions and assembling your reactor. There isn’t anything you need to modify or change during the build, you can even use one which is preassembled if you’ve got one you’ve already built.

Arc Reactor Kit Components

Arc Reactor Kit Assembly

Depending on your kit, it should take around half an hour to an hour to assemble. Some kits do require you to wind your own coils or trim off a lot of excess plastic which can be time-consuming.

Assembled DIY Arc Reactor Kit

Solder The Electronic Components

Before we assemble the base components, we need to make up the electrical circuit. The circuit for this project is quite simple and consists of an I2C connection between the Arduino Uno and the OLED display and then an NPN transistor connected to pin 5 of the Arduino to drive the Arc Reactor. The LED array on the Arc Reactor draws about 500 milliamps, which is too much to drive directly off of the Arduino. I added a 510Ω current limiting resistor onto the base leg of the transistor as well. I soldered this resistor directly onto the transistor and covered it with heat shrink tubing.

Arc Reactor CPU Performance Monitor Schematic

I connected the components using some coloured ribbon cable and header pins to plug into the Arduino and covered the exposed wiring on the transistor and plug with some heat shrink tubing.

Wiring Hardness

Important Note – There seem to be two variants of these OLED displays available online and they look very similar. One version has the GND pin first and the other the VCC pin first. They do not have any reverse polarity protection, so if you connect them the wrong way around they instantly “pop” and have to be thrown out.  Make sure that you check this before soldering the pin connector.

Print & Assemble The Arc Reactor Stand

I designed four components which stack together with some hex head screws to make up the stand. There is a base compartment to house the Arduino. A cover to go over the base and connect to the display housing and then a post on top of the display housing to support the arc reactor.

3D Print Design

Download The 3D Print Files – CPU Monitor Stand Files

I 3D printed the components using black PLA with a 15% infill. You could print the base out of a different colour as well, it would probably look great in silver or grey too. I usually try to hide the screws underneath the housing, but in this design I wanted the screws to be visible to add a bit of detail to the base.

3D Printing The Housing

3D Printed Arduino Uno Housing

I’ve used a single M5 x 8mm hex head screw to secure the reactor post to the display housing. Make sure that the top surface of the post is angled to point the Arc Reactor slightly upwards, ie the highest point is in the front.

M5 Screw To Hold Vertical Post

Then glued the OLED display into the housing using a glue gun.

Gluing OLED Display Into Place

I then glued the Arduino Uno into the base, with the USB port lined up with the cutout.

Gluing Arduino Uno Into Place

I then plugged the connector into the OLED display and attached the Arduino cover plate using four M3 x 8mm screws.

Installing The Electrical Assembly

Securing The M3 Screws

The cover then goes onto the base with four M4 x 8mm screws. I used M4 screws so that the heads are a bit larger and more visible on the base. You can switch these out for small M3 screws if you’d like, you’ll just need to modify the holes in the Arduino case to be slightly smaller.

Securing The M4 Screws

I attached the arc reactor to the base using the glue gun again. There isn’t really a nice flat surface on the bottom of the arc reactor, so you need to build up the glue around the supports until it’s held securely in place.

Gluing The Arc Reactor Into Place On The Stand

Lastly, glue the arc reactor plug into the back of the stand. This is just a dummy plug but it looks better if it’s actually plugged into something. If you’d like to neaten up the wiring even further, you could remove the existing plug on the LED and solder this plug’s leads onto the LED instead and then buy a real socket to plug the arc reactor into.

Glue The Back Plug In

Remember to also connect the power lead to the LED.

Plug The LED Socket In

And that’s the Arc Reactor CPU Performance Monitor’s hardware complete, now we just have to program it.

Programming The Arc Reactor CPU Performance Monitor

There are three things you need to set up to get your CPU data to be sent to your Arduino, a source of the data on your computer, a script to read the source on your computer and send the data through a serial communication port to your Arduino and the code on the Arduino to read the data, recompile it and then update the display and reactor LED.

We’ll start off with the Arduino’s code.

Programming The Arduino Uno

The Arduino’s code is fairly simple thanks to the OLED displays libraries. Most of the code is actually dedicated to turning the data received through the serial communication port back into usable data.

Let’s have a look at the code:

//Michael Klements
//The DIY Life
//Arc Reactor CPU Monitor
//25 May 2020

#include <SPI.h>                //Include the libraries for the display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128        //OLED display width, in pixels
#define SCREEN_HEIGHT 32        //OLED display height, in pixels

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);   //Create the display object

int pinReactor = 5;             //Pin to reactor
int brightness = 25;            //How bright the reactor LED is at minimum. Its distracting if it turns off entirely.
int fadeAmount = 1;             //How many increments to fade the reactor LED by in each loop cycle

char receivedChars[5];          //Variables to track the incoming Serial data
boolean newData = false;

int cpuLoad = 0;                //Variable for the CPU load

void setup() 
{ 
  Serial.begin(9600);                                     //Begin Serial communication with the PC
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))          // Address 0x3C for 128x32 display
  {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);                                              //Don't proceed if it can't communicate with the display
  }
  display.clearDisplay();                                 //Clear the display
  display.setTextSize(1);                                 //Set the display text size
  display.setTextColor(SSD1306_WHITE);                    //Draw white text
  updateDisplay();                                        //Update the displayed CPU load, 0% initially
}

void loop() 
{ 
  analogWrite(pinReactor, brightness);                    //Set the reactor LED brightness
  brightness = brightness + fadeAmount;                   //Increment the brightness by the fade amount
  if (brightness <= 25 || brightness >= 255)              //Reverse the direction of the fade increment once fully bright or dim
  {
    fadeAmount = -fadeAmount;
  }
  if (Serial.available())                                 //If an updated CPU load is received
  {
    delay(10);
    static byte ndx = 0;                                  //Index to keep track of received char position
    char endMarker = '%';                                 //Symbol indicating that the full CPU load value has been recieved
    char rc;                                              //Temporary variable to store the received char
    cpuLoad = 0;                                          //Reset CPU load variable to 0
    while (Serial.available() > 0 && newData == false)    //While data is being recieved
    {
      rc = Serial.read();                                 //Save data to rc
      if (rc != endMarker)                                //Check that the end of the data symbol hasn't been received
      {
            receivedChars[ndx] = rc;                      //Save char received to receivedChars array
            int temp = (int)rc - 48;                      //Convert ASCII char to int
            if(temp >= 0)                                 //Check that the converted char was not a space (ASCII 32)
            {
              if(ndx == 0)                                //Update CPU load based on 100s, 10s or units position
                cpuLoad = cpuLoad + temp*100;
              else if(ndx == 1)
                cpuLoad = cpuLoad + temp*10;
              else
                cpuLoad = cpuLoad + temp;
            }
            ndx++;                                        //Increment the index
        }
        else 
        {
            receivedChars[ndx] = '\0';                    //Terminate the string
            ndx = 0;                                      //Reset the index
            newData = true;                               //Terminate the loop
        }
    }
    if (newData == true)                                  //Reset new data
    {
        newData = false;
    }
    updateDisplay();                                      //Update the displayed CPU load
  }
  int delayDuration = map(cpuLoad, 0, 100, 10000, 500);   //Maps CPU Load to a delay duration, higher load is a shorter fade time
  delayMicroseconds(delayDuration);                       //Wait the calculated delay duration
}

void updateDisplay ()                                     //Update the displayed text
{
  display.clearDisplay();
  display.setCursor(40,5);
  display.println(F("CPU Load"));
  display.setCursor(55,15);
  display.print(cpuLoad);
  display.print(F("%"));
  display.display();
}

Download the Sketch – ArcReactorCPUMonitor

We start off by importing the libraries necessary to control the OLED display, then set up the display parameters and create a display object.

We then define the Arc reactor LED control pin and set parameters for the initial brightness and the fade amount or difference in light intensity for each cycle. I created the initial brightness variable as a non-zero value so that the LED doesn’t turn off entirely between cycles, I found this “flashing” to be too distracting on my desk.

We then create a character array to receive the serial communication data and create a variable to store the CPU load.

In the setup function, we start the Serial communication, then establish a connection with the display and update it to display the initial CPU load, which will be zero. If the Arduino is not able to communicate with the display, the code will hang here and won’t continue into the loop. This is good for troubleshooting and makes sure that your Arduino is able to communicate with the OLED display

In the loop function, we start by updating the arc reactors brightness and then increment the brightness variable by the fade amount for the next loop cycle. We then have a check to see if the brightness has reached a maximum or minimum and if so, reverse the fade direction.

We then check if any Serial communication data is available. If there is data available then it is read into the character array until the % sign is received, indicating that the data is complete.

Data is transferred to the Arduino letter by letter in ASCII format, not as an integer. So each digit is received individually and the Arduino needs to know when the number is complete and then convert it into an integer. The rest of the code in this section does just that, converting each digit received into either hundreds, tens or units and then adding them together to form a complete integer which the Arduino can then quantify.

We then update the display to reflect the new CPU load and then map the CPU load to a delay duration between cycles, which corresponds to a faster or slower arc reactor pulse. The higher the CPU load, the faster the arc reactor will pulse.

Lastly, we have a function to update the display which just clears the previous display and displays the new CPU load.

While you’ve got your Arduino IDE open, also make a note of which com port is assigned to your Arduino Uno, as you’ll need to update this in the python script.

Getting Your CPU Load Data

In order to send the CPU load to your Arduino, we first need to be able to access it. One of the easiest ways to do this is using a freeware application called Open Hardware Monitor.

Open Hardware Monitor Dashboard

The Open Hardware Monitor app runs in the background on your PC and minimises to the system tray. It collects information on a number of different hardware items on your computer and makes it available to be read using a Python script.

You’ll need to make a note of your processor name displayed in the monitor as you’ll need to update this in the python script in order to access your CPU load.

You’ll also need to set the Open Hardware Monitor to run on startup so that you don’t need to open it every time you restart your PC. To do this, go to options and make sure that “Start Minimized”, “Minimize To Tray” and “Run on Windows Startup” are all checked. Then minimise the app.

Posting The Data To Your Arduino Using A Python Script

Lastly, we have a python script which transmits the CPU load through the serial com port to your Arduino. The script reads the data posted by the Hardware monitor app, finds the CPU load and then posts the value to the Arduino.

I didn’t write this code. I’ve modified the code from Leonidas Tsekouras’s Arduino PC Monitor example in which he sends a range of data to be displayed on an external LCD display.

Here is the code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
import os
import time
from urllib.error import URLError, HTTPError
from urllib.request import Request, urlopen

import serial
import serial.tools.list_ports

serial_debug = False

def space_pad(number, length):
    """
    Return a number as a string, padded with spaces to make it the given length
    :param number: the number to pad with spaces (can be int or float)
    :param length: the specified length
    :returns: the number padded with spaces as a string
    """

    number_length = len(str(number))
    spaces_to_add = length - number_length
    return (' ' * spaces_to_add) + str(number)

def get_json_contents(json_url):
    """
    Return the contents of a (remote) JSON file
    :param json_url: the url (as a string) of the remote JSON file
    :returns: the data of the JSON file
    """

    data = None

    req = Request(json_url)
    try:
        response = urlopen(req).read()
    except HTTPError as e:
        print('HTTPError ' + str(e.code))
    except URLError as e:
        print('URLError ' + str(e.reason))
    else:
        try:
            data = json.loads(response.decode('utf-8'))
        except ValueError:
            print('Invalid JSON contents')

    return data


def find_in_data(ohw_data, name):
    """
    Search in the OpenHardwareMonitor data for a specific node, recursively
    :param ohw_data:    OpenHardwareMonitor data object
    :param name:        Name of node to search for
    :returns:           The found node, or -1 if no node was found
    """
    if ohw_data == -1:
        raise Exception('Couldn\'t find value ' + name + '!')
    if ohw_data['Text'] == name:
        # The node we are looking for is this one
        return ohw_data
    elif len(ohw_data['Children']) > 0:
        # Look at the node's children
        for child in ohw_data['Children']:
            if child['Text'] == name:
                # This child is the one we're looking for
                return child
            else:
                # Look at this children's children
                result = find_in_data(child, name)
                if result != -1:
                    # Node with specified name was found
                    return result
    # When this point is reached, nothing was found in any children
    return -1

def get_hardware_info():
    """
    Get hardware info from OpenHardwareMonitor's web server and format it
    """
    global serial_debug

    # Init arrays
    my_info = {}

    # Get data
    if serial_debug:
        # Read data from response.json file (for debugging)
        data_json = get_local_json_contents('response.json')
    else:
        # Get actual OHW data (Modify to your Open Hardware Monitor's IP Address)
        ohw_json_url = 'http://192.168.20.10:8085/data.json'
        data_json = get_json_contents(ohw_json_url)

    # Get info for CPU (Modify to your CPU name shown in Open Hardware Monitor)
    cpu_data = find_in_data(data_json, 'Intel Core i5-7200U')
    cpu_load = find_in_data(cpu_data, 'CPU Total')

    # Get CPU total load, and remove ".0 %" from the end
    cpu_load_value = cpu_load['Value'][:-4]

    my_info['cpu_load'] = cpu_load_value

    return my_info


def main():
    global serial_debug

    # Connect to the specified serial port (Modify to your Arduino's COM port)
    serial_port = 'COM3'
    if serial_port == 'TEST':
        serial_debug = True
    else:
        ser = serial.Serial(serial_port)

    while True:
        # Get current info
        my_info = get_hardware_info()

        # Prepare CPU string
        cpu = space_pad(int(my_info['cpu_load']), 3) + '%'

        # Send the strings via serial to the Arduino
        if serial_debug:
            print(arduino_str)
        else:
            ser.write(cpu.encode())

        # Wait until refreshing Arduino again
        time.sleep(2.5)


if __name__ == '__main__':
    main()

Download The Code – ArduinoPCMonitor

You’ll need to update the IP address that your Open Hardware Monitor is posting the data too. This can be found in the Open Hardware Monitor app by going to Options -> Remote Web Server -> Port.

You’ll also need to change your processor name and check that the Arduino’s com port is correct.

Both the python script and then hardware monitor can be set to automatically startup with your computer and run in the background, so you don’t have to keep running them every time you turn your computer off. The python script can be automatically started using the windows “Start a Program” utility.

Using The Arc Reactor CPU Monitor

Upload the code your Arduino and make sure that the Open Hardware Monitor app is running on your computer and then run the Python script to start posting the data to your Arduino. You should then start seeing updates to your CPU performance on the OLED display and the reactor pulsing accordingly.

Arc Reactor CPU Performance Monitor

On start-up, with no data being transmitted, the OLED display should still display CPU Load 0% and the reactor should be pulsing slowly.

CPU Load Displayed

If you open up a benchmarking application to increase the CPU load to 100%, you will see it pulse really quickly.

You can make your own adjustments to the pulse durations and brightness levels in the code. I found it too distracting to have the pulse turn off completely each cycle, so I limited the minimum brightness and also set the low CPU usage pulse duration to be quite long. You can edit these by making changes to the mapped delay, to the brightness variable and to the fadeAmount variable.

Let me know if you’re going to be building your own arc reactor CPU performance monitor or what you’ve done with an arc reactor you’ve built.

Share This Guide

Arc Reactor PC Monitor Pinterest

Make Your Own Desktop Tensegrity Tables

These tables look like they’re just floating in the air, but they’re actually a clever demonstration of the principle of tensegrity. The principle of tensegrity originated in the nineteen fifties and is still used in the design of modern buildings and structures. The worlds largest tensegrity structure is currently the Kurilpa Bridge in Brisbane, Australia. In this guide, I’ll be showing you how to build your own tensegrity tables step by step.

Have a look at my video summary of the build and the final product, otherwise, read on for the step by step instructions.

At first glance, it appears that the top surface is being supported by the three outside pieces of fishing line, which doesn’t make sense since we know that fishing line or string can’t support compressive loads. But taking a closer look, you will see that the line doing all of the work is actually the one in the centre.

Tensegrity Tables

The piece of fishing line in the centre of the structure is in tension and is supporting the load of the surface of the table and whatever is placed onto it. The three pieces of line on the outside are simply holding the top surface in place so that it remains directly overhead the centre line and doesn’t fall over.

Centre String Support on Tensegrity Table

If any of these four lines are cut, the table will collapse under its own weight.

To better understand tensegrity tables and how they work, let’s try building our own ones.

What You Need To Build Your Own

These tables were cut on a laser cutter, but you can still cut out the outlines of the components and build your own using MDF, acrylic or plywood if you don’t have access to a laser cutter, or make use of an online laser cutting service.

How To Make Your Own Desktop Tensegrity Tables

I designed the basic components in Inkscape, to be laser cut from 3mm MDF. You could also cut them from plywood or acrylic and you can cut the outlines out by hand if you don’t have access to a laser cutter.

Inkscape Tensegrity Table Design

Download The Cutting Template Files – Tensegrity Table Files

The file above includes the Inkscape svg files as well as dxf files and an A4 printable pdf template, so you should be able to find an application to open and print or cut them out.

I’ve added a few engraving marks as guides for the holes to be drilled for the fishing line as well.

I designed two versions of these tables, one which is supported by a strand of fishing line in the centre and one which is supported by two agents. The flat sections are the same, I’ve just adjusted the height of the vertical section to accommodate the magnets in the centre instead of the fishing line. The magnets add an interesting dynamic to the table, as there is now effectively no physical component holding the table up, it’s purely supported by the invisible magnetic force between the two magnets.

Cut your components from your chosen material. You’ll need two table ends and two vertical supports for each table.

Laser Cutting MDF

Laser Cutting The Tables

The Tables Once Cut

Once you’ve cut out your components, you need to drill the holes through them for the fishing line or thread. I drilled these using a 1mm drill bit on engraved markings. You can adjust the drill bit size to suit the diameter of your fishing line or thread.

Drilling The Holes For The Fishing Line

You’ll then need to glue the vertical pieces in place using PVA wood glue and wait for the glue to dry. There is a slot designed into the table ends to position and align the vertical support.

Gluing In The Vertical Supports

Once that’s done, you can add your fishing line or thread. Fishing line is a bit easier to use because it doesn’t fray and it’s more rigid, so its easier to thread through the holes. Cut four lengths a lot longer than you need, you can then trim them when you’re done. I found that lengths of around 200mm (8″) worked well.

Measuring The Fishing Line

Glue one end of each line into the bottom side of the table first. You can either tie a knot at the end of the lines or simply glue them into place with some super glue. I found it easier to position them accurately and glue them into place rather than trying to get the knot tied in the correct place. A single drop of super glue on each side was sufficient to hold the line in place and dried quite quickly.

Then glue the tension line in the middle into place, leaving around a 50-60mm (2″) gap between the two centre pieces. Glue or tie both ends of the tension line into place.

Attaching The Fishing Line

Then fed the three outer lines through their holes. The last step takes a bit of patience to get right. You need to glue or tie the three outer lines into place so that there is a little bit of tension on the centre line but also keep the top and bottom surfaces as close to parallel as possible. So all three lines need to be as close to the same length as possible. It helps to use a ruler to get this part right. Depending on the length of your centre strand, you’re looking for these lines to be around 130mm (5″) long. Also, use a small amount of glue to temporarily hold them in place until you’re sure that they’re correct so that you can undo a joint if you need to.

Measuring Before Gluing In Place

Both Sides Glued

Once you’re happy with your table, trim off the excess fishing line and make sure that the glued joints are secure and dried.

Trimming The Excess Line

If you’re using magnets, glue the outside three lines into place at the correct and even length, around 130mm (5″) and then add the magnets to the middle afterwards, with opposite poles facing each other so that they’re attracted to each other.

Magnetic Tensegrity Table

The magnetic table looks cool, but can’t really hold much weight. You could get more by positioning the magnets closer together as the magnetic force of attraction between two magnets is proportional to the square of the distance between them. There is a bit of a tradeoff here though because if they’re too close together then you can see the gap between them well and then it just looks like the magnets are rigidly holding up the table.

Completed Tensegrity Table

I tested the fishing line table to see if it could hold up my phone. It held up just under 200 grams but the outside lines did start flexing, so it probably couldn’t take too much more than this.

Load Testing The Tensegrity Table

These tensegrity tables are surprisingly rigid, you can even pick them up and hold them sideways.

It Can Be Help Up Sideways

The magnetic one will also hold itself up sideways but it collapses if you put too much weight on it. You can just pull the magnets back up towards each other to reset the table.

Magnetic Table Held Up Sideways

Magnetic Table Collapsing

Enjoy making your own tensegrity tables! Let me know how it goes for you and what your designs look like in the comments section below.

Both Tensegrity Tables Supprting Weight

Community Builds

Michael Marletto built a larger version of the table from wood and suspended the surfaces using chains. It really came out well and can probably take quite a lot more load than the smaller MDF ones:

Desktop Tensegrity Table

Share This Guide

Make Your Own Desktop Tensegrity Tables Social

How to Save Water While You’re Away From Home

Water scarcity is a real issue throughout the world, so paying attention to your water usage and being mindful of ways you can save water can help future generations.

Saving water at your own home is easy to do. You install a toilet bladder, avoid watering your flowers and yard in the midday sun, and harvest rainwater and you’ve already made a great dent in your usage.

Saving water when you’re away from home can be a little trickier, though. You’re not going to carry around a toilet bladder to use at a restaurant or friend’s house. These tips will help you feel good about your water-saving efforts, no matter where you are.

Use a Refillable Water Bottle

Use a Refillable Water Bottle

Conserving water can be as simple as considering which container you’ll drink it out of. To use pick the best container, buy a refillable water bottle or flask that you can carry with you in your car, bike, or while walking.

Disposable water bottles take water to produce. So do refillable bottles, but you’ll be reusing those hundreds or thousands of times compared to a single-use. It’s smarter and better for the environment if you stop using disposable water bottles entirely.

Reuse Towels at a Hotel

Reuse Towels at a Hotel

Chances are, you reuse your bath towels multiple times before washing them at home. Our to-do lists are long enough without having to wash and fold extra laundry unnecessarily. Who wants to do that?

But at hotels, many of us feel more indulgent. We don’t treat our hotel rooms like we’d treat our house, and we leave those soggy towels on the bathroom floor, secure in the knowledge new ones will be brought to our rooms the next morning.

But with laundry making up as much as 16 percent of the water usage at a hotel, those reused towels can add up quickly in terms of saved water and reduced bills. It just takes a second to hang up your towels, and there’s no reason you can’t use them for the full week of your vacation.

Depending upon the length of your vacation and how many towels are in your room, you could save one load of laundry during your trip just from reusing towels. That can save at least 14 gallons of water or more if the hotel doesn’t use an energy-efficient appliance.

Eat Locally When on Vacation

When you’re travelling, look for roadside fruit stands and restaurants that advertise using locally-grown foods. Transportation of food from one country to another or one state to another can add a lot to the water footprint of the food you eat. It takes resources and energy to get that food moved.

The fruit you buy at stands hasn’t been washed as much as packaged foods in your grocery store has, and it generally isn’t packaged in plastic that takes water to produce. If you don’t see any packaging on your food, you’re saving water usage just from that.

Pick Foods Low on the Food Chain

Foods higher on the food chain take more water to produce. The bigger the animal you’re about to consume, the more water it took to get that food on your plate.

Beef takes about 1,847 gallons for every pound of meat, compared to pork at 718 gallons per pound. Chicken takes less water to produce than either of those meats.

Even better, opt for a few meatless meals every week while eating out in restaurants or on the road.

Drink Water and Skip the Other Beverages

Drink Water and Skip the Other Beverages

Plain old water is your best bet for quenching your thirst and saving water. Other popular beverages use far more water to produce.

  • It takes 1,056 gallons of water to make one gallon of brewed coffee.
  • A gallon of beer requires 296 gallons of water to produce.
  • A gallon of wine takes 872 gallons of water to make.

If you’re tired of water, reach for tea. It only takes 108 gallons of water to produce one gallon of tea.

Carpool or Ride Your Bike

Producing gasoline takes water, so the less fuel you burn on driving, the less water you’ll be using. To conserve water, see if you can start an office carpool. It will save you and your coworkers money while also cutting down on water usage.

A better bet if you can do it is riding your bike or walking to wherever you’re going.

It All Adds Up

Sometimes the steps we take to save water can seem so small in the grand scheme of things. But every little bit truly does add up. And if enough people make a few changes to conserve water when they’re away from home, it will make a big difference in the long run.

What are some of the ways you save water outside of your home? Let us know in the comments section below.

How To Get Rid Of Mould On Caulk or Silicone Joints & Corners

Once mould starts growing on caulk or silicone joints and corners in your bathroom, it can be quite difficult to clean. You can scrub away for hours using bathroom cleaners and not really make much progress. We’ve found a solution that works well to remove mould on caulk and doesn’t require any scrubbing, you probably even have most of what you need in your home already.

If you’ve got hard water stains in your basin, bath or toilet, you can make an Epsom salt scrub to remove it.

What You Need To Clean Your Caulk Joints

  • 1/4 Cup of Household Bleach – Buy Here
  • 1/2 Cup of Baking Soda – Buy Here
  • An Old Toothbrush
  • A Disposable Cup or Container – Buy Here

How To Remove The Mould On Caulk Joints

You’ll need to mix the baking soda together with the bleach in your container. Start by adding a small amount of each and work until you run out of one of the ingredients. The mixture should form a paste which is quite thick and not drip off of the toothbrush, similar to toothpaste. The quantities are approximate and may need to be adjusted a little depending on how thick your bleach is.

Once you’ve got your thick bleach and baking soda paste, head into your bathroom and open up a window or turn on the extractor fan to get some fresh air flowing through the room. The mixture has a strong smell when you start spreading it out. You might also want to wear rubber gloves so that you don’t get it on your hands, undiluted bleach can be quite harsh on your skin and may cause burns or an allergic reaction.

Use the toothbrush to gently brush the mixture onto the mouldy spots, completely covering them. Make sure that the paste is thick enough to be held in place for an hour or so, it shouldn’t run down or drip off of the joint but should also completely cover the mould and shouldn’t be crumbly.

Note: You might want to test the mixture in a small hidden area to make sure that it doesn’t cause discolouration of your tiles.

Once you’ve brushed the mixture onto all of the mouldy spots, you just need to wait for the bleach to do the work. This usually takes about an hour, depending on how bad the mould is. It’s a good idea to check on the paste halfway through to make sure that it’s still in place.

After an hour, use some warm water and the toothbrush to wash the paste away. Make sure that it doesn’t drip onto your floor or bathmat or towels in the process as it may discolour them.

Once you’re done, the mould should be gone, or at least significantly lighter. Sometimes stubborn mould requires a second treatment.

Have you tried this method to clean the mould from your caulk or silicone joints and corners? Let us know what worked for you in the comments section below.

Crack The Code Game, Built Into A DIY Safe Puzzle Box

In this project, I’m going to be showing you how to build your own Arduino based crack the code game. You use a dial to guess the randomly generated code to the safe and 8 LEDs on the front of the safe then tell you how many of the digits you’ve guessed are correct and how many are in the right place as well. Using this feedback you can work your way towards cracking the code to the safe, and once you do, the game will then tell you how many attempts it took you to crack.

Here’s a video of the build. Read on for the full step by step instructions.

This project assumes you know the basics of Arduino programming, otherwise read our article on getting started with Arduino.

There have been a number of different forms of this game over the years, from physical pegboards in the 70s to video games, and more recently you may have seen these types of puzzles as single images on social media platforms.

Crack The Code Lock Code Puzzle

The safe is initially open, allowing you to put something into the inside compartment. You then push the dial to lock the safe using a servo on the inside of the door and a random code is generated.

The Box Opens To Put Something Inside

You then need to input the code by turning the dial to select the digits and pushing the dial to confirm each digit.

Entering A Code

After your fourth digit is chosen, the safe displays how many of your digits are correct and how many of them are in the correct place using the red and green LEDs on the door. A red LED indicates a correct digit and a green LED indicate that it’s also in the correct place. So you’re looking to light up all four red and green LEDs in order to crack the code and open the safe.

The LEDs Light Up To Show Correct Digits And Positions

The safe keeps track of how many guesses you’ve made in order to crack the code. It may sound complicated at first but it’s actually not that difficult. You just need to remember and build upon your previous guesses. Most of the time you should be able to crack the code in 5 to 10 guesses, depending on how lucky your initial guesses are.

What You Need To Build Your Own Crack The Code Puzzle Box

In addition to these, you’ll also need some basic tools, a soldering iron, glue gun and some wood glue. You’ll also need to have access to a laser cutter to cut the box. If you don’t have access to one, consider using an online laser cutting service. There are quite a few options available and they’re quite affordable, they’ll even deliver to your door.

If you’re interested in a laser cutter, this is the one I’ve used for this project.

K40 Laser Cutter – Buy Here

How To Build Your Own Crack The Code Safe Box

Cutting & Assembling The Safe Box

I started off by designing the safe box in Inkscape. It had to be large enough to house an Arduino Uno, have a compartment to put something into and look like a traditional safe. The pieces are designed to be cut from 3mm MDF, you could also cut the parts from 3mm acrylic or plywood. If you use a different thickness material then you’ll need to modify the edges of the box pieces so that the slots fit together.

Box Components Were Designed In Inkscape

Download The Laser Cutting File – Box Cutting Files

The download above includes svg, dxf and A3 printable pdf files to give you a couple of options for cutting or printing.

There are 6 main panels which make up the box. The front panel has a cutout in it for the door and the back has one for the Arduino and battery compartment cover. The 6 main panels are labelled in the cutting file so that you’re able to identify them.

The dial is also made up from laser-cut pieces which are then glued together.

There are three decorative panels which are stuck onto the top and two sides of the box to make it look more like a safe. There are also two panels which make up the door and a divider panel which goes into the middle of the box to separate the safe compartment from the electronics compartment.

The pieces fit onto a single piece of MDF 400 x 500mm. They can be divided up into smaller pieces, as I’ve done, if your laser cutter isn’t big enough to cut them all in one go. I cut out two sections at a time.

Let’s cut out the pieces.

Box Components Were Laser Cut

I used some masking tape to prevent the smoke from the laser from marking the wood. I had to take this off before gluing the box together, which is a little tedious but produces a much better end product.

Laser Cut Components

I started out gluing the decorative panels onto the top and sides first using some PVA wood glue and small plastic clamps. Make sure that you’ve got the pieces in the correct order so that you know which are which. There are three different pieces; the top and bottom are the same, the sides are the same and the front and back are the same. I’ve put text labels under each in the laser cutting file. Then glue the two door panels together.

Glue The Box Components Together

Once the panels are dry, you can assemble the box.

Gluing The Box Sides Together

Make sure the cutouts for the centre divider are on the sides. These are to run any wires from the front of the box to the back of the box where the Arduino and battery sit.

The hinges are also laser cut and just glue into place once you’ve lined up the door. Make sure that they’re parallel to the door or you’ll have difficulty opening it. Also, make sure that the door is positioned over the centre of the cutout in the box so that there are no gaps visible.

Box Hinges Glued Into Place

You may also need to sand a little bit off of the inside hinged edge of the door once the hinges are dry so that it doesn’t rub on the inside edge of the box cutout as it moves past.

Sand Some Of The Inside Edge Of The Box Hinge

Glue the four square pieces into the corners behind the back panel to hold the screws for the back cover in place.

Pieces To Hold The Back Screws In Place

Then drill holes for the screws. I used a couple of spare screws I had lying around which came with the micro servos.

Drill Holes For The Back Screws

You can then mount the screen onto the door with two screws, the Arduino into the back compartment and lastly the encoder through the front door. The nut for the encoder is hidden by the recess provided by the front panel.

Install The Electronic Components

Connecting The Electronics

Now that the box is complete, we can start connecting the electronic components together.

Here is the schematic:

Crack The Code Schematic

We’ve got 8 LEDs connected to the digital IO pins 6 to 13. The locking servo connected to pin 5. The encoder connected to pins 2, 3 and 4 and the OLED display connected to the Arduino’s I2C interface.

I used a 220 ohm resistor for each LED, which I soldered directly onto the cathode (negative pin) of the LED before gluing them into place with a glue gun.

Soldering Components Together

Glue Components To The Box

I connected the components together using coloured ribbon cable to keep the wiring neat and to help keep track of which wire needed to go to each Arduino pin.

Wiring Once Complete

I pushed the ribbon cables through to the back compartment and the soldered sections of header strips onto the ends to plug directly into the Arduino.

Pin Headers To Arduino Uno

I also mounted a power switch onto the back cover and connected this to a battery plug to connect to a rechargeable battery to power the game. I used a 3 cell, 800mAh LiPo battery which I had from some RC aircraft. You could also use a 9V battery or a battery holder which houses 4 AA batteries.

Lastly, you’ll need to mount the locking servo on the door. Position it towards the edge of the door so that it passes over the lip in the box and the arm is able to push up against the inside of the lip to lock the box. This isn’t the strongest locking mechanism but it is really simple and it works for the game’s purpose.

Servo Door Lock

The safe box is now complete.

Crack The Code Safe Puzzle Box

Safe Dial

Coding The Game

The code for this game isn’t that complicated but is quite lengthy. I’ve tried to make the code as readable as possible by splitting it up into functions and putting in a lot of comments.

The code makes use of an interrupt routine to take the inputs from the rotary encoder, this is based on Simon Merrett’s example for a rotary encoder input which I found online. It was the most simple I could find and didn’t require any additional libraries to function.

Here is the code:

//Code Breaker
//Michael Klements
//The DIY Life
//15 May 2020

//Encoder interrupt routine adapted from Simon Merrett's example code

#include <SPI.h>                          //Import libraries to control the OLED display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Servo.h>                        //Import library to control the servo

Servo lockServo;                          //Create a servo object for the lock servo

#define SCREEN_WIDTH 128                  // OLED display width, in pixels
#define SCREEN_HEIGHT 32                  // OLED display height, in pixels

#define OLED_RESET -1                     // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);   // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)

static int pinA = 2;                      //Hardware interrupt digital pin 2
static int pinB = 3;                      //Hardware interrupt digital pin 3
volatile byte aFlag = 0;                  //Rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0;                  //Rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0;             //Current value of encoder position, digit being input form 0 to 9
volatile byte prevEncoderPos = 0;         //To track whether the encoder has been turned and the display needs to update
volatile byte reading = 0;                //Stores direct value from interrupt pin

const byte buttonPin = 4;                 //Pin number for encoder push button
byte oldButtonState = HIGH;               //First button state is open because of pull-up resistor
const unsigned long debounceTime = 10;    //Debounce delay time
unsigned long buttonPressTime;            //Time button has been pressed for debounce

byte correctNumLEDs[4] = {9,12,7,11};      //Pin numbers for correct number LEDs (Indicate a correct digit)
byte correctPlaceLEDs[4] = {6,10,8,13};    //Pin numbers for correct place LEDs (Indicate a correct digit in the correct place)

byte code[4] = {0,0,0,0};                  //Create an array to store the code digits
byte codeGuess[4] = {0,0,0,0};             //Create an array to store the guessed code digits
byte guessingDigit = 0;                    //Tracks the current digit being guessed
byte numGuesses = 0;                       //Tracks how many guesses it takes to crack the code
boolean correctGuess = true;               //Variable to check whether the code has been guessed correctly, true initially to generate a new code on startup

void setup()
{
  Serial.begin(9600);                                 //Starts the Serial monitor for debugging
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))      //Connect to the OLED display
  {
    Serial.println(F("SSD1306 allocation failed"));   //If connection fails
    for(;;);                                          //Don't proceed, loop forever
  }
  display.clearDisplay();                             //Clear display
  lockServo.attach(5);                                //Assign the lock servo to pin 5
  for(int i=0 ; i<=3 ; i++)                           //Define pin modes for the LEDs
  {
    pinMode(correctNumLEDs[i], OUTPUT);
    pinMode(correctPlaceLEDs[i], OUTPUT);
  }
  pinMode(pinA, INPUT_PULLUP);                        //Set pinA as an input, pulled HIGH to the logic voltage
  pinMode(pinB, INPUT_PULLUP);                        //Set pinB as an input, pulled HIGH to the logic voltage
  attachInterrupt(0,PinA,RISING);                     //Set an interrupt on PinA
  attachInterrupt(1,PinB,RISING);                     //Set an interrupt on PinB
  pinMode (buttonPin, INPUT_PULLUP);                  //Set the encoder button as an input, pulled HIGH to the logic voltage
  randomSeed(analogRead(0));                          //Randomly choose a starting point for the random function, otherwise code pattern is predictable
  display.setTextColor(SSD1306_WHITE);                //Set the text colour to white
  startupAni();                                       //Display the startup animation
}

void loop() 
{
  if(correctGuess)                                            //Code between games to reset if the guess is correct, initially true to open safe and then generate new code
  {
    lockServo.write(140);                                     //Unlock the safe
    delay(300);
    updateLEDs (0,4);                                         //Flashing LED sequence
    delay(300);
    updateLEDs (4,0);
    delay(300);
    updateLEDs (0,4);
    delay(300);
    updateLEDs (4,0);
    delay(300);
    updateLEDs (4,4);                                         //Turn all LEDs on
    if(numGuesses >= 1)                                       //Check that its not the start of the game
    {
      display.clearDisplay();                                 //Clear the display
      display.setTextSize(1);                                 //Set the display text size to small
      display.setCursor(35,10);                               //Set the display cursor position
      display.print(F("In "));                                //Set the display text
      display.print(numGuesses);                              //Set the display text
      display.setCursor(35,20);                               //Set the display cursor position
      display.print(F("Attempts"));                           //Set the display text
      display.display();                                      //Output the display text
      delay(5000);
    }
    display.clearDisplay();                                   //Clear the display
    display.setTextSize(1);                                   //Set the display text size to small
    display.setCursor(35,10);                                 //Set the display cursor position
    display.print(F("Push To"));                              //Set the display text
    display.setCursor(35,20);                                 //Set the display cursor position
    display.print(F("Lock Safe"));                            //Set the display text
    display.display();                                        //Output the display text
    display.setTextSize(2);                                   //Set the display text size back to large
    boolean lock = false;                                     //Safe is initially not locked
    boolean pressed = false;                                  //Keeps track of button press
    while(!lock)                                              //While button is not pressed, wait for it to be pressed
    {
      byte buttonState = digitalRead (buttonPin); 
      if (buttonState != oldButtonState)
      {
        if (millis () - buttonPressTime >= debounceTime)      //Debounce button
        {
          buttonPressTime = millis ();                        //Time when button is pressed
          oldButtonState =  buttonState;                      //Remember button state
          if (buttonState == LOW)
          {
            pressed = true;                                   //Records button has been pressed
          }
          else 
          {
            if (pressed == true)                              //Makes sure that button is pressed and then released before continuing in the code
            {
              lockServo.write(45);                            //Lock the safe
              display.clearDisplay();                         //Clear the display
              display.setCursor(30,10);                       //Set the display cursor position
              display.print(F("Locked"));                     //Set the display text
              display.display();                              //Output the display text
              lock = true;
            }
          }  
        }
      }
    }
    generateNewCode();                                        //Calls function to generate a new random code
    updateLEDs (0,0);
    correctGuess = false;                                     //The code guess is initially set to incorrect
    numGuesses = 0;                                           //Reset the number of guesses counter
  }
  inputCodeGuess();                                           //Calls function to allow the user to input a guess
  numGuesses++;                                               //Increment the guess counter
  checkCodeGuess();                                           //Calls function to check the input guess
  encoderPos = 0;                                             //Reset the encoder position
  guessingDigit = 0;                                          //Reset the digit being guessed
  codeGuess[0] = 0;                                           //Reset the first digit of the code
  updateDisplayCode();                                        //Update the displayed code
}

void updateDisplayCode()                                      //Function to update the display with the input code
{
  String temp = "";                                           //Temporary variable to concatenate the code string
  if(!correctGuess)                                           //If the guess is not correct then update the display
  {
    for (int i=0 ; i<guessingDigit ; i++)                     //Loops through the four digits to display them
    {
      temp = temp + codeGuess[i];
    }
    temp = temp + encoderPos;
    for (int i=guessingDigit+1 ; i<=3 ; i++)
    {
      temp = temp + "0";
    }
    Serial.println(temp);                                     //Output to Serial monitor for debugging
    display.setTextSize(2);                                   //Set the display text size
    display.clearDisplay();                                   //Clear the display
    display.setCursor(40,10);                                 //Set the display cursor position
    display.println(temp);                                    //Set the display text
    display.display();                                        //Update the display
  }
}

void generateNewCode()                                        //Function to generate a new random code
{
  Serial.print("Code: ");
  for (int i=0 ; i<= 3 ; i++)                                 //Loops through the four digits and assigns a random number to each
  {
    code[i] = random(0,9);                                    //Generate a random number for each digit
    Serial.print(code[i]);                                    //Display the code on Serial monitor for debugging
  }
  Serial.println();
}

void inputCodeGuess()                                         //Function to allow the user to input a guess
{
  for(int i=0 ; i<=3 ; i++)                                   //User must guess all four digits
  {
    guessingDigit = i;
    boolean confirmed = false;                                //Both used to confirm button push to assign a digit to the guess code
    boolean pressed = false;
    encoderPos = 0;                                           //Encoder starts from 0 for each digit
    while(!confirmed)                                         //While the user has not confirmed the digit input
    {
      byte buttonState = digitalRead (buttonPin); 
      if (buttonState != oldButtonState)
      {
        if (millis () - buttonPressTime >= debounceTime)      //Debounce button
        {
          buttonPressTime = millis ();                        //Time when button was pushed
          oldButtonState =  buttonState;                      //Remember button state for next time
          if (buttonState == LOW)
          {
            codeGuess[i] = encoderPos;                        //If the button is pressed, accept the current digit into the guessed code
            pressed = true;
          }
          else 
          {
            if (pressed == true)                              //Confirm the input once the button is released again
            {
              updateDisplayCode();                            //Update the code being displayed
              confirmed = true;
            }
          }  
        }
      }
      if(encoderPos!=prevEncoderPos)                          //Update the displayed code if the encoder position has changed
      {
        updateDisplayCode();
        prevEncoderPos=encoderPos;
      }
    }
  }
}

void checkCodeGuess()                                         //Function to check the users guess against the generated code
{
  int correctNum = 0;                                         //Variable for the number of correct digits in the wrong place
  int correctPlace = 0;                                       //Variable for the number of correct digits in the correct place
  int usedDigits[4] = {0,0,0,0};                              //Mark off digits which have been already identified in the wrong place, avoids counting repeated digits twice
  for (int i=0 ; i<= 3 ; i++)                                 //Loop through the four digits in the guessed code
  {
    for (int j=0 ; j<=3 ; j++)                                //Loop through the four digits in the generated code
    {
      if (codeGuess[i]==code[j])                              //If a number is found to match
      {
        if(usedDigits[j]!=1)                                  //Check that it hasn't been previously identified
        {
          correctNum++;                                       //Increment the correct digits in the wrong place counter
          usedDigits[j] = 1;                                  //Mark off the digit as been identified
          break;                                              //Stop looking once the digit is found
        }
      }
    }
  }
  for (int i=0 ; i<= 3 ; i++)                                 //Compares the guess digits to the code digits for correct digits in correct place
  {
    if (codeGuess[i]==code[i])                                //If a correct digit in the correct place is found
      correctPlace++;                                         //Increment the correct place counter
  }
  updateLEDs(correctNum, correctPlace);                        //Calls a function to update the LEDs to reflect the guess
  if(correctPlace==4)                                          //If all 4 digits are correct then the code has been cracked
  {
    display.clearDisplay();                                    //Clear the display
    display.setCursor(20,10);                                  //Set the display cursor position
    display.print(F("Cracked"));                               //Set the display text
    display.display();                                         //Output the display text
    correctGuess = true;
  }
  else
    correctGuess = false;
}

void updateLEDs (int corNum, int corPla)                        //Function to update the LEDs to reflect the guess
{
  for(int i=0 ; i<=3 ; i++)                                     //First turn all LEDs off
  {
    digitalWrite(correctNumLEDs[i], LOW);
    digitalWrite(correctPlaceLEDs[i], LOW);
  }
  for(int j=0 ; j<=corNum-1 ; j++)                              //Turn on the number of correct digits in wrong place LEDs
  {
    digitalWrite(correctNumLEDs[j], HIGH);
  }
  for(int k=0 ; k<=corPla-1 ; k++)                              //Turn on the number of correct digits in the correct place LEDs
  {
    digitalWrite(correctPlaceLEDs[k], HIGH);
  }
}

void startupAni ()
{
  display.setTextSize(2);                     //Set the display text size
  display.setCursor(35,10);                   //Set the display cursor position
  display.println(F("Crack"));                //Set the display text
  display.display();                          //Output the display text
  delay(500);
  display.clearDisplay();                     //Clear the display
  display.setCursor(45,10);
  display.println(F("The"));
  display.display();
  delay(500);
  display.clearDisplay();
  display.setCursor(40,10);
  display.println(F("Code"));
  display.display();
  delay(500);
  display.clearDisplay();
}

void PinA()                               //Rotary encoder interrupt service routine for one encoder pin
{
  cli();                                  //Stop interrupts happening before we read pin values
  reading = PIND & 0xC;                   //Read all eight pin values then strip away all but pinA and pinB's values
  if(reading == B00001100 && aFlag)       //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  {     
    if(encoderPos>0)
      encoderPos --;                      //Decrement the encoder's position count
    else
      encoderPos = 9;                     //Go back to 9 after 0
    bFlag = 0;                            //Reset flags for the next turn
    aFlag = 0;                            //Reset flags for the next turn
  }
  else if (reading == B00000100)          //Signal that we're expecting pinB to signal the transition to detent from free rotation
    bFlag = 1;
  sei();                                  //Restart interrupts
}

void PinB()                               //Rotary encoder interrupt service routine for the other encoder pin
{
  cli();                                  //Stop interrupts happening before we read pin values
  reading = PIND & 0xC;                   //Read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag)      //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  {
    if(encoderPos<9)
      encoderPos ++;                      //Increment the encoder's position count
    else
      encoderPos = 0;                     //Go back to 0 after 9
    bFlag = 0;                            //Reset flags for the next turn
    aFlag = 0;                            //Reset flags for the next turn
  }
  else if (reading == B00001000)          //Signal that we're expecting pinA to signal the transition to detent from free rotation
    aFlag = 1;
  sei();                                  //Restart interrupts
}

Download The Code – CrackTheCode

We start by importing libraries to control the OLED display and the servo. The libraries are used to drive the OLED display, you can find more information on driving these OLED displays on Adafruit’s page on using I2C OLED displays.

We then set the parameters for the display and create all of our variables.

I’ve separated the variables into sections and given each a comment to identify their function. There are quite a few variables dedicated to tracking the encoder turns as these are done through rising edge interrupts on pins 2 and 3. The LED pins are assigned through to arrays which make updating them a bit easier. There are two code arrays created, on to store the randomly generated code and one to store the user’s current guess. There are also a few variables to track whether the uses code is correct, which digit is currently being guessed and how many guesses the user has made so far.

In the setup function, we start the display, which won’t progress unless the connection to the display is successful. Then attach the servo, set the LED and encoder IO pin modes. We then make use of the randomSeed function which reads in from an open analog input in order to randomise the starting point for the random function. If we don’t do this then the randomly generated codes will follow a predictable pattern which we’ll soon be able to identify. Lastly, we display the Crack The Code text animation on the display.

The loop function flashes the LEDs and displays the message “push to lock safe” which then waits until the user pushes the dial to start the game. The same code is run at the end of a game which then displays the number of attempts and waits for a dial press to start a new game.

There is some debouncing code on the encoder pushbutton and once pushed, the servo locks the safe and a random code is generated. The code then calls a function to ask the user to input their guess and then another to check the guess, this is repeated until the user guesses the code correctly. After each code guess, we increment the number of guesses counter and reset the input code so that the user can select a code from 0000 again. You could also change this to carry on from the previous code again if you’d prefer.

There is a function to update the code being displayed, which is called every time the encoder is turned and the displayed code needs to change. This just loops through the code guess array and displays the four digits.

The function to generate a new code simply assigns a random digit to each of the four elements in the code array.

The function to input a code guess allows the user to select a digit using the encoder and then confirm each digit input by pushing the encoder down. The displayed code is updated after each button push and any time that the encoder position has been changed. So the encoder updates the position variable using the interrupt function and this portion of the code checks if the variable has been changed and then updates the code accordingly. The interrupt doesn’t directly drive any portions of the code, it only updates the encoder position variable.

The check code guess function then looks through the guessed code and decides how many digits are correct and how many are in the correct place. There is more code to the logic behind the digits in the incorrect place so that duplicates in the user’s input and in the generated code are managed. So if the user guesses 5555 and the code contains one 5 then only one of the red LEDs will light up. The same goes for the user guessing a single 5 in their code while the generated code contains two 5s, only one red LED will light up. The correct digit in the correct place check is a simple comparison of the digits in each position of the two arrays.

The update LEDs function switches the correct number of red and green LEDs on based on the output from the check code guess function.

The startup ani function displays the three-word “Crack The Code” animation on startup.

Lastly, two interrupt functions manage the input from the encoder, one incrementing the digit upwards when turned clockwise and one downwards when turned anticlockwise. These are both rising edge interrupts available on pins 2 and 3 of an Arduino Uno. These could also be set as falling edge or low level interrupts. You can read more on the Arduino’s available interrupts if they interest you.

Now let’s upload the code and try it out.

Edit: Modify Code To Start With Locked Box

I’ve had quite a few people ask about how to start the box off locked rather than with the current push to start the game and lock the box. This is quite easy to change, you just need to modify three lines:

Add lockServo.write(45) into the setup function (at the end)  to lock the safe when the box is turned on.

Move line 73 – lockServo.write(140) into the if statement below it (at the beginning). The statement starting on line 84, so insert it into line 86. This will then only unlock the box again once the guess is correct.

Change the displayed text in line 101 to read display.print(F(“Start”)). As the box will already be locked on startup.

This will then start the box off locked. You’ll still need to press the encoder to start the game but this won’t do anything to the lock in the first round as it will already be locked. In subsequent games, starting the game will lock the box again.

Playing The Game and Trying To Crack The Code

The guessed code is input using the dial to increment the digit and a push on the dial to go to the next digit or to confirm the code once all four digits are selected.

Crack The Code Safe Puzzle Box

The LEDs on the front then light up to tell us what was correct in our guess. The red LEDs indicate that you’ve got a correct digit but not necessarily in the correct place and the green LEDs indicate that the digit is also in the correct place. The position or order of the LEDs is meaningless, they don’t give you any further information. Remember that you’ll need to be careful with double digits as the LEDs don’t light up multiples times for repeated digits unless you’ve repeated the digit in your guessed code.

The LEDs Light Up To Show Correct Digits And Positions

The safe is initially unlocked, allowing you to put something inside it.

The Box Opens To Put Something Inside

We then push the dial to lock the safe and generate a new code. You then have to try and figure out the code in order to open the safe again.

Box Locked

Once you’ve cracked the code, the box will unlock and display the number of attempts it took you to unlock it.

Code Cracked

Showing Number Of Attempts

The easiest way to see how the game is played is to watch the video at the beginning which has two demonstrations near the end.

Enjoy building your own crack the code safe box. Let me know how it goes for you or what changes you’ve made to it in the comments section.

Community Builds

Asaf Matan has made some fantastic modifications to the puzzle box to make the storage area a little larger, separate the electronics and to run on an ESP32 instead of an Arduino. Take a look at his blog post for the code and some additional build images:

Asaf Matan Build 1

Asaf Matan Build 2

Share This Guide

Crack The Code Social

How To Change A Euro Cylinder Profile Lock On A UPVC And Composite Door

The term “cylinder lock” could mean a large number of different locks, but in today’s guide, we will focus on one type of cylinder – the Euro cylinder. The Euro cylinder was first patented in 1805 and was an attempt by European locksmiths to standardise locks, which, in Europe and other parts of the world has been largely successful. Euro cylinders are now a common sight on the external doors of many homes. In the United States, Euro cylinders are used primarily on sliding glass doors and internal doors which divide two rooms.

Euro cylinders are easy to change or upgrade, and here at The DIY Life, we love to save you both time and money. There is no need to call a locksmith or handyman as these are one of the easiest locks to change, due to the cylinder being self-contained, their standardisation and wide availability. In fact, the most important part is the preparation.

To Change A Euro Cylinder You Will Need

  • 1 Phillips Screwdriver OR 1 Torx Screwdriver (depending on the screw type)
  • 1 Euro Cylinder Lock (Do NOT BUY before you have extracted the lock as you need the correct size)
  • Your existing Euro cylinder key

How To Change The Lock

1. Firstly open the door so you can comfortably stand by the side of it.

2. Next, locate the Euro cylinder on the door, then locate the central screw on the side of the door. This is the screw that keeps the Euro cylinder in place.

3. Identify which screwdriver is appropriate to use, then remove the screw. Normally this will be a Phillips, but may also be a Torx screw and very rarely may be a flat head.

How To Change A Euro Cylinder Lock

4. Once the screw is removed, insert your key into the lock and turn it about an 8th of the way or to approximately to 2 O’clock, this is to align the cam (middle of the lock). Once aligned, pull the key and lock towards you until you feel a release. You sometimes need to loosen the screws to the door handle to make this easier, but will not usually be necessary. This can be a bit tricky, but just remember, all it is is an alignment issue. Loosen the door handle, turn the key to align the cam and wiggle the lock out.

5. Now the Euro cylinder should be in your hand, giving you the opportunity to measure it. You can measure the size of a Euro cylinder by finding the central screw hole on the lock. Grab a tape measure and measure from the central screw hole to the end of the cylinder, measure both ways. Your measurement should be something like 35mm by 35 mm (As seen below) or 45mm by 50mm etc. However, this size is only correct if your lock is the correct size for the door in the first place. The cylinder should be flush to the door with none of the cylinder being exposed. If the cylinder is exposed then measure from the central screw hole to where the lock would fit flush to the door, this should be identifiable with a weathered line on the lock, where some of the lock has been exposed to the elements. This exposure of the lock is a serious security risk and makes bypassing the lock much easier with a technique called lock snapping (more on that later).

6. There are approximately 25 different sizes, so ensure you have measured the size correctly. Alternatively, if possible, you could take the lock with you to ensure you purchase the exact size needed.

Euro Cam

7. Now travel to your local hardware store and purchase the correct size replacement cylinder.

8. Once you have the new cylinder, insert your new key into it and turn it an 8th of the way, so the cam (the middle) is aligned, allowing you to insert the lock into your door. The exact same way you took it out but in reverse. This bit can be as tricky as getting the lock out, remember it’s just an alignment issue so just fiddle around and it will eventually come right.

9. Once the lock is fitted nicely and is flush with the door, use the old screw you took out of your previous lock and insert it into the central screw hole and tighten it. We recommend using the old screw as it will be the correct length, sometimes a new screw will be too long and will have to be cut to size. Alternatively, if the new screw is the same size and doesn’t require cutting,  then use the new screw. Don’t forget to keep your cylinder measurement for future maintenance.

10. To test that it has all been done correctly, lift the handle and turn the key to lock the lock with the door open. Lock the cylinder a few times to test it. Once you have seen this working it properly, it should be safe to shut the door and lock it.

Euro cylinders are great locks and are renowned for being difficult to pick, however, they do possess some natural weaknesses. one big vulnerability to the security of your door is called snapping. Lock snapping is exactly what the name suggests, it literally means snapping the lock in two. The scariest thing is that this technique can be successfully done in as little as 15 seconds, and the tools required to snap a lock can be found almost anywhere.  This can be overcome by purchasing anti-snap locks with a sacrificial section. Instead of the lock snapping in half, the sacrificial section will break off leaving the rest of the lock within the door, safe from another snapping attempt.

Another vulnerability of the euro cylinder is drilling. Like most locks, the Euro cylinder can be drilled within minutes. While this cannot be overcome, you can purchase euro cylinders with anti-drill pins within them. This does not stop drilling, but it does make drilling a harder and longer process.

Generally higher quality Euro cylinders are made out of stronger and sturdier materials, they also contain more anti-drill pins, in addition to other security feature,s such as anti-bump pins, sacrificial sections and magnetic operating locks, making picking extremely difficult. If your Euro cylinder is for an external door you should consider purchasing a high-security lock, as this will be much more resistant to any bypassing techniques used by criminals, making it harder for them to enter your property. More on this and other helpful security tips and advice can be found on ITCC Locksmith’s blog section.

Have you tried changing your own Euro cylinder lock? Let us know in the comments section below.

Share This Guide

Change A Euro Cylinder Lock Yourself - Step by Step

How to Make Your Own Inexpensive Chandelier

Few home decor accessories look as regal as a chandelier. The problem is that chandeliers usually cost a pretty penny. Whether you’re on a budget or just looking for a new home improvement DIY project, making your own chandelier is fun, easy, and surprisingly affordable.

As you read this guide on chandelier making, remember that these are just suggestions and not requirements. You can use the basic design as-is, or feel free to get creative and modify it to fit your unique style preferences. The possibilities are endless!

1. Gather Materials

If you do a lot of DIY projects, there’s a good chance you’ll already have a lot of these materials lying around the house. If not, you should be able to obtain them fairly easily—check hardware, junk stores, and other retailers that sell lamp and light fixture parts.

If you’re struggling to find a particular item, try to think outside the box. You should be able to come up with a solution, even if it’s not exactly what you initially had in mind.

Here’s a general idea of what types of materials you’re going to need:

  • A top base to hold the suspended items (repurposed wood, a bicycle wheel, or a wire cooking rack are all great choices)
  • At least 10 feet of lightweight wire lamp chain or other lamp cord
  • 14-gauge (1.5mm^2) wire
  • At least six small Mason jars (or exposed light bulbs of your choice)
  • A one-inch circular metal ring
  • A swag hook or other hardware for hanging the chandelier
  • Pliers
  • Wire cutters

Hanging Wooden DIY Chandelier

2. Attach the Chains or Cord to Your Top Base

The metal ring is going to be your point of contact to the ceiling. When you’re finished with your chandelier, you can use the ring to hang it from a hook. Start by using your wire cutters to cut the lamp chain or cord into four equal-sized pieces. The length depends on the size of your base, but around 16  (400mm) to 24 (600mm) inches should do the trick.

If you’re using chains, take your pliers and open the top link of each chain. Attach the chain to the metal ring, and then use your pliers to squeeze the chainlink back into place. Repeat this step for all four chains. You should end up with four equal lengths of chain hanging from the ring.

If you’re using a flexible cloth-covered cord, consider wrapping the material around your base in a way that matches the aesthetic you’re going for.

3. Attach the Cord to the Base

Now you’re going to repeat the same process to attach the bottom of the chains to your wheel, wire cooking rack, or other base. If your base is thicker than your chain links, you might need to get creative. You could use wire to wrap the bottom chain link to the perimeters of your base.

When you’re done attaching all four chains, you should be able to hold the top ring, and the base should hang evenly. If it’s crooked, you might need to trim the chains to make sure they’re all the exact same size.

4. Create Wire Handles on the Jars

Hanging Mason Jars To Make A DIY Chandelier
Credit: Color Cord Company

Next, you’re going to create wire ‘handles’ so you can hang each jar from the base. Cut your wires so they’re 24 inches (600mm) long. Wrap the wire securely around the lip of your Mason jars, leaving at least 8 inches (200mm) of slack like a tail. You’ll attach the other end of the wire ‘tail’ to the jar eventually, but not yet.

Pro tip: You could also leave the lids on, drill a hole in the tops of the lids, and attach the jars directly to the base using rope or twine for a rustic look. Simply thread it through and tie a knot in the bottom to prevent it from slipping out.

5. Thread the Wires Through the Bottom Chain Links

Cut six (or more) pieces of lamp chain to hang the jars. You can cut the chains into random lengths, or measure them if you want a symmetrical design. Now it’s time to thread the ‘tail’ of the wire handles on your jar through the bottom chain links. Secure the end of the wire to the other side of the jar, creating a U-shaped handle. Repeat these for all of the jars.

6. Attach the Jars to the Base

Now you have six or more jars with chains attached. You might need a friend for this next step. Hang your base from the ceiling, and attach the top of the chains to the base, one-by-one. Be sure to position them in areas that keep the weight of the structure balanced. It’s important to attach the jars while the chandelier is hanging, otherwise, you risk it being lopsided.

7. Add More Flair

You can leave the chandelier as-is if you want a classic, bucolic look. Minimalism is a popular architecture trend, so feel free to keep it simple. For a stronger visual effect, you could use your leftover light cord or chain to hang beads, crystals, shells, ornaments, antique bottles, or anything else that catches your eye. Feel free to get creative with it.

If you really want to make your new chandelier pop, consider painting it. A quick coat or two of spray paint will help to transform an old wheel or baking rack into a more realistic base. A coat of white paint is a safe and elegant option, but don’t be afraid to match it to your home’s unique colour scheme.

8. Light It Up

DIY Chandelier

Congratulations, your chandelier is finished! The last step is to light it up and enjoy. If you’re using candles, simply light them and drop them into the jars. Just make sure you follow candle safety guidelines to prevent fires.

If you prefer electricity, you could also consider using lamp parts and an extension cord or even wireless LEDs to add bulbs to the jars. Or, you could consider hanging your chandelier underneath an existing ceiling light fixture so the light can radiate off the glass naturally. Be sure to follow electrical safety guidelines if you’re doing any wiring on your own, or hire a contractor to help you with wiring your new light fixture.

Note: Make sure that you check your local regulations before doing any electrical work, some area do not permit unlicensed electrical work to be carried out.

Keep Adding To Your Chandelier

The great thing about this project is that you can keep modifying it over time. Just keep some spare chain and wire on hand, and you’ll be able to attach new pieces whenever the mood strikes. If your first chandelier doesn’t turn out as well as you were hoping, learn from your mistakes and try again! The materials are cheap, so why not experiment with your design?

Have you tried making your own chandelier? Let us know what you made in the comments section below.

An Arduino Playing The Chrome Dino Game On Another Arduino

You may have seen people using an Arduino to automate playing the Chrome Dino Game in their browser, I thought we could take it a bit further and see if we could get one Arduino to play the Chrome Dino Game on another Arduino.

I’ve previously set up an Arduino to run a simple version of the game, which produces a continuous stream of randomly spaced cactuses at an increasing speed, and closer together. The game is really easy to get running and only requires an Arduino Uno and an LCD keypad shield. A single button on the keypad shield is then used to make the dinosaur jump over each cactus until the player messes up and hits one. The score is kept based on how long you’re able to avoid running into a cactus.

Once I had the game running on one Arduino, I set up a second one, which uses an LDR to sense each cactus and then moves a servo to press the button on the other Arduino to make the dinosaur jump over each cactus.

Here’s a video of the build and the one Arduino playing the Chrome Dino Game on the other:

We’ll first have a look at running the dino game on an Arduino and then set up a second to play the game on the first. The code would be the same to set the second Arduino up to play on your computer’s browser as well, you’d just need to stick the LDR to the display.

What You Need For This Project

For The Dino Game

For The Dino Game Player

How To Load The Chrome Dino Game

Let me start out by saying – I didn’t code the dino game. It’s a version I found on Hackster.io which was the least complicated layout and ran the smoothest. One downside is that it’s written in AVR C code, so it looks a little different to the generic Arduino language but you should still be able to figure a lot of it out, I’ve also expanded on the description of the game and explained how you can change a few things around in another post – How to Play the Chrome Dino Game onto an Arduino.

Here’s a general overview of the game:

  • You push a button on the keypad shield to make the dinosaur jump over each cactus.
  • The game speed up the longer you play it.
  • Cactuses are initially separated by a minimum of 5 spaces and this goes down to 4 as the game progresses.
  • The current score is displayed throughout the game and is joined by the high score at the end of the game.
  • Cheating by holding down the button or continuously pressing the button is prevented.

I have made a couple of changes to the original version of the code for the timing, cactus spacing and the text layouts to better accommodate the sensor.

Here is the code:

//From Hackster.io
//By BRZI

#include <avr/io.h>
#define  F_CPU 16000000UL //Our CPU speed (16MHz)
#include <util/delay.h> //Libraries for delay and interrupt utilities
#include <avr/interrupt.h>
#define command 0 //explained in dispSend() function
#define write 1

uint8_t upperBuff[16] , downerBuff[16], overMsgUpper[] = "Score:  ", overMsgDowner[] = "Best:   ", scoremsg[] = "Score:" , din[] = {0x0E, 0x17, 0x1E, 0x1F, 0x18, 0x1F, 0x1A, 0x12}, cact[] = {0x04, 0x05, 0x15, 0x15, 0x16, 0x0C, 0x04, 0x04};
        //Buffers for line one and two. Message to display after lost game.                     //Score text during game. //Dinosaur and cactus bitmaps
uint8_t canup = 1, longhold = 0, distance = 6, speed = 200, isup = 0, dontprint = 0; //All of these are explained further
uint16_t aVal = 0, score = 1, bestscore = 0;
int i;

void dispInit();
void dispWrite(uint8_t bits);
void dispSend(uint8_t bits, uint8_t act);
void dispSetLine(uint8_t line);
void dispClear();
void dispHome();
void dispPrintChar(uint8_t chr[], uint8_t size);
uint16_t aRead();

int main(void)
{
  for(i = 0; i < 17; i++) downerBuff[i] = ' '; //Initialize upper and downward buffer
  for(i = 0; i < 17; i++) upperBuff[i] = ' ';
  
  dispInit(); //Initialize the display
  
  TCCR1B |= (1 << WGM12) | (1 << CS11); //Set Timer1 to compare to OCR1A and prescaler of 8
  OCR1AH = (500 >> 8); //This equals to 2000Hz or 500us timing, look for TIMER1_COMPA_vect down below
  OCR1AL = 500;
  TIMSK1 |= (1 << OCIE1A); //Enable Timer1 CompA ISR
  sei(); //Enable global interrupt

  
  ADMUX = (1 << REFS0); //Set AREF to VCC
  ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADEN); //set ADC prescaler to 128 and enable ADC (defaulted to free running mode)
  
  while (1) {   
    
    ADMUX |= (1 << MUX2) | (1 << MUX0); //Set pin from ADMUX to ADC5 (floating)
    srand(aRead()); //Use it as a random seed
    ADMUX &= ~(1 << MUX2) & ~(1 << MUX0); //Revert back to ADC0 to read the button value

    if(aRead() > 900) longhold = 0; //Reads if Up button has been released to prevent cheating. The value is so low because if you hold your fingers beneath one of the buttons the voltage would drop, this prevents the dinosaur from locking up

    for(i = 0; i < 16; i++) downerBuff[i] = downerBuff[i + 1]; //Shifts everything in downward buffer by one place to the left
     if((rand() % 100) > (rand() % 100) && !dontprint){ //This portion decides if it should put a cactus or a blank spot, dontprint is used to prevent cactus grouping
      downerBuff[15] = 0x01; //0x01 represents the cactus (we added cactus and dinosaur to CGRAM when we initialized the display)
      dontprint = 1; //This part acts both as a boolean and a counter to ensure cactus separation
     }
     else downerBuff[15] = ' ';
     char lastchar = downerBuff[3]; //We remember the whats initially added to the downward buffer before replacing it with the dinosaur
     if(!isup){ //If din should be placed down
      downerBuff[3] = 0x00; //Place it down
      dispSetLine(2);
      dispPrintChar(downerBuff, sizeof(downerBuff)); //Draw it
      downerBuff[3] = lastchar; //Place back previous thing to the buffer
      canup = 1;  //This flag is used to disable dinosaur from getting up before it was drawn down, in this case he can go up
    } else { //If din should be placed up
      upperBuff[3] = 0x00; //Place it up in upper buff
      dispSetLine(1);
      dispPrintChar(upperBuff, sizeof(upperBuff));
      dispSetLine(2);
      dispPrintChar(downerBuff, sizeof(downerBuff)); //Draw it
      canup = 0; //In this case he wont go up until rendered on line 2
    }

    if(dontprint) dontprint++;
    if(dontprint > distance) dontprint = 0; //This is the part that ensures cactus separation, it will keep the cactus 3-5 spaces apart minimally (depends on the game progress)
    
    if(isup) isup++; //This part makes sure din is on upper side for 3 loops after he was initially drawn there
    if(isup > 4){
     upperBuff[3] = ' ';
     dispSetLine(1);
     dispPrintChar(upperBuff, sizeof(upperBuff));
     isup = 0;
    }
    for(i = 0; i < sizeof(scoremsg); i++) upperBuff[i + 5] = scoremsg[i]; //This part prints the current score during the game
    uint8_t cnt = 11;
    for(i = 10000; i > 0; i /= 10){
      upperBuff[cnt] = ((score / i) % 10) + '0';
      cnt++;
      dispSetLine(1);
      dispPrintChar(upperBuff, sizeof(upperBuff));
    }

    score++; //Increment the score once on loop
    if(score > bestscore) bestscore = score; //Remember best score
    
    if(lastchar == 0x01 && !isup){ //Check if the dinosaur is downward and hit a cactus
      dispClear(); //Clear the display and buffers
      for(i = 0; i < 17; i++) downerBuff[i] = ' ';
      for(i = 0; i < 17; i++) upperBuff[i] = ' ';
      uint8_t cnt;
      
      dispSetLine(1);
      for(i = 0; i < sizeof(overMsgUpper); i++) upperBuff[i] = overMsgUpper[i]; //Display worst and best score
      cnt = sizeof(overMsgUpper) - 1;
      for(i = 10000; i > 0; i /= 10){
        upperBuff[cnt] = ((score / i) % 10) + '0';
        cnt++;
      }
      dispPrintChar(upperBuff, sizeof(upperBuff));
      
      dispSetLine(2);
      for(i = 0; i < sizeof(overMsgDowner); i++) downerBuff[i] = overMsgDowner[i];
      cnt = sizeof(overMsgDowner) - 1;
      for(i = 10000; i > 0; i /= 10){
        downerBuff[cnt] = ((bestscore / i) % 10) + '0';
        cnt++;
      }
      dispPrintChar(downerBuff, sizeof(downerBuff));
      
      while(1){ //Wait for select button to be pressed
        aVal = aRead();
        if(aVal > 635 && aVal < 645){ //After that clear all the variables
          for(i = 0; i < 17; i++) downerBuff[i] = ' ';
          dispSetLine(1);
          dispPrintChar(downerBuff, sizeof(downerBuff));
          for(i = 0; i < 17; i++) upperBuff[i] = ' ';
          dispSetLine(2);
          dispPrintChar(upperBuff, sizeof(upperBuff));
          dontprint = 0;
          isup = 0;
          score = 1;
          speed = 200;
          longhold = 0;
          distance = 6;
          canup = 1;
          break;
             }
      }
      
    }
    if(score % 5 == 0) speed -=1; //If score is divisible by 5 make game faster by -2ms
    if(speed < 120) speed = 120; //Minimal time in ms (+ ~2ms) that the loop will be halted for (limited by display refreshing, in my testing 11.8Hz was readable enough to be playable)
    if(score % 175 == 0) distance--; //Every time you score a number divisible by 175 minimal cactus distance gets smaller
    if(distance < 4) distance = 4;
    for(i = 0; i < speed; i++) _delay_ms(1); //This is the only way as the compiler expects a const number here
  }
}

void dispInit(){
  _delay_ms(50); //Just in case
  DDRD = 0b11110000; //Set these pins to output. PD4 - PD7 correspond to D4 - D7 on display, we need to configure it to run in 4 bit mode
  DDRB = 0b00000011; //PB0 is tied to RS and PB1 to EN
  dispWrite(0x30);//*This part here is explained in Hitachi HD44780 datasheet on how to initialize the display in 4bit mode
  _delay_us(4500);//*Essentially you send the reset signal 3 times, and then set it to 4 bit mode
  dispWrite(0x30);//*
  _delay_us(4500);//*
  dispWrite(0x30);//*
  _delay_us(4500);//*
  dispWrite(0x28);//*
  dispSend(0x28, command); //Send 4bit mode function set
  dispSend(0x08, command); //Turn the display off
  dispSend(0x01, command); //Clear its RAM (if MCU resets that doesn't mean the display was reset, so we clear everything)
    _delay_ms(50);
  dispSend(0x0C, command); //Turn the display on
  _delay_ms(5);
  dispSend(0x40, command); //Tell the display we want to enter a custom character to its CGRAM (on address 0x00)
  for(i=0; i<8; i++) dispSend(din[i], write);
  dispSend(0x80, command); //Transaction end
  dispSend(0x48, command); //Same thing, but for 0x01
  for(i=0; i<8; i++) dispSend(cact[i], write);
  dispSend(0x80, command);
}

void dispPrintChar(uint8_t chr[], uint8_t size){
  for(uint8_t i = 0; i < size; i++) dispSend(chr[i], write); //Self explanatory 
}

void dispSetLine(uint8_t line){
  if(line == 2) dispSend(0xC0, command); //Sets the line where 0xC0 is line 2 and 0x80 is line 1
  else dispSend(0x80, command);
}

void dispClear(){
  dispSend(0x01, command); //Self explanatory 
  _delay_ms(2); //This command takes longer for the IC to process, this delay is necessary
}

void dispHome(){ //This function isn't used in this application but its there for expandability, it places the cursor on the line 1 column 1
  dispSend(0x02, command); //Self explanatory 
  _delay_ms(2);
}

void dispSend(uint8_t bits, uint8_t act){
  if(act) PORTB |= (1 << DDB0); //Set PB0 if we are writing a character, else pull it low
  else PORTB &= ~(1<<DDB0);
  dispWrite(bits); //Send the bit then shift them 4 bit to the left to work in displays 4bit mode
  dispWrite(bits << 4);
  _delay_us(80);
}

void dispWrite(uint8_t bits){
  PORTD = bits; //This is a dirty way to write it but it's perfect for this application as it's not bulky and PORTD isn't used for anything else anyway
  PORTB |= (1<<DDB1); //Pulse the PB1 to signal the IC to read the data
  _delay_us(1);
  PORTB &= ~(1<<DDB1);
  _delay_us(1);
}

uint16_t aRead(){
  ADCSRA |= (1 << ADSC); //This signal the avr to read the ADC value
  while  (ADCSRA & (1 << ADSC)); //Wait until it's finished
  return ADCL | (ADCH << 8); //Send it back stitched together
}

ISR (TIMER1_COMPA_vect){ //Timer ISR we set up earlier
  if(!longhold){ //Return if the Up button was still held
    aVal = aRead(); //Read from ADC0
    if(aVal > 450 && aVal < 600 && canup){ //Check if Up is pressed and that din was rendered down
     isup = 1;
     longhold++;
     }
    }
}

Download The Code – DinoGame

The game is quite easy to play on the LCD keypad shield, although the buttons are not the best for quick presses, and the LCD is quite slow, so it starts suffering from ghosting and brightness issues once the cactuses start moving quickly.

Playing The Chrome Dino Game On An Arduino

Setting Up An Arduino To Play The Chrome Dino Game

Now that we’ve got the game running on our first Arduino, let’s try and get the second one playing the Chrome Dino Game on the first.

The components and wiring are quite simple, they involve basic starter circuits for the LDR, LED and servo. I initially included the LED to light up when each cactus was detected but the light started interfering with the LDR, so I turned it off. You can leave this LED out if you want to or cover it up slightly with tape so that it doesn’t interfere with the surrounding components.

Schematic - Arduino Chrome Dino Game Player

I connected the components together using a few strips of ribbon cable and some header pins to make it easier to plug into the Arduino and the servo.

Soldering Components

Ribbon Cable Connector

You’ll need to glue the servo onto a box or stand just above the left button on the keypad shield so that the servo arm pushes the button down when it rotates. You’ll then need to glue the LDR onto the edge of the LCD screen as close to the screen as possible. It needs to be positioned over the 7th character from the left of the display in the second row.

Arduino Playing The Chrome Dino Game On Another Arduino - Side

Make sure that the LED is covered, turned off or pointed away from the display so that the light from the LED doesn’t interfere with the LDR sensing the cactuses. The first few runs I had were interrupted by the LED causing false cactus readings by the LDR, so it turned it off.

Servo Pressing Jump Button

Now let’s have a look at the player code.

//Michael Klements
//The DIY Life
//8 May 2020

#include <Servo.h>

Servo player;         //Create a servo object to control the button servo
int cactus = 0;       //Variable to read in LDR value
int cactusVal = 770;  //LDR Sensor value when detecting a cactus

void setup()
{
  //Serial.begin(9600);
  player.attach(6);     //Set the player servo pin to pin 6
  player.write(75);     //Set the servo to an initial position just above the button
}

void loop()
{
  cactus = analogRead(A0);          //Read the output from the LDR to detect a cactus
  //Serial.println(cactus);         //Used for calibration
  if (cactus > cactusVal)           //If a cactus is detected
  {
    player.write(60);               //Move the servo to push the jump button
    delay(300);                     //Wait 300 milliseconds
    player.write(75);               //Move the servo back to the initial position
    delay(100);
  }
}

Download The Code – DinoGamePlayer

We start by including the servo library to control the servo.

We then create a servo object called player to control the servo and then create two variables, one to store the value read from the LDR and a second to store the light level set point when a cactus passes in front of the LDR.

In the setup function we set the servo pin number and set the servo position slightly above the jump button. You’ll also need the Serial monitor for calibration, this is detailed further on.

In the loop function, we read in the LDR sensor level, then compare it to the cactus set point. If the measured level is greater than the cactus set point, indicating a cactus is passing the sensor, then we move the servo downwards to push the button, wait 300 milliseconds for the servo to move and for the game to register the input and then move the servo back up for the next push. The second delay is to avoid repeated servo movements, this could be reduced to 50ms.

I’ve also included a serial monitor printout of the sensor value which is used initially to set the cactus value set point. You’ll need to run the game and display the sensor values and then see what value is measured when a cactus runs past the sensor and update this value in the code accordingly. This step is detailed in the video at the beginning of the project.

One Arduino Playing The Chrome Dino Game On Another

Upload the code and calibrate the sensor and you’re ready to try it out. Startup the player Arduino first and then start the game on the other. Press the reset button on the LCD shield to start a new game.

Arduino Playing The Chrome Dino Game On Another Arduino

It initially looks like the sensor is making the dinosaur jump a little too early, but it doesn’t hit the cactuses and you need to it respond quickly later on in the game. It starts to look better as the game progresses.

LDR Detecting Cactuses

You may need to make a few adjustments to the servo travel limits and to the cactus detection set points in your code to get it to work correctly.

Arduino Playing The Chrome Dino Game On Another Arduino - Side

Also, position the LDR as close to the LCD as possible. These LDRs are really sensitive, I noticed a significant fluctuation in the measured values with me moving around the room or at different times of the day, so It would be a good addition to add a pot to adjust the set point at any time. This way you could get it to work in any light conditions and use it on the Arduino game or your computer without having to change the code.

Let me know in the comments if you’ve tried playing the Chrome Dino Game on an Arduino or tried getting an Arduino to play the Chrome Dino Game.

Share This Guide

An Arduino Playing The Chrome Dino Game On Another Arduino Social

Play The Chrome Dino Game On An Arduino

If you’ve been kept busy playing the Chrome Dino Game in your browser, how about loading it onto an Arduino to leave on your desk? It’s a really simple game which runs quite well on an Arduino Uno using a simple LCD keypad shield without any other hardware.

I didn’t code this game, it’s a version I found on hackster.io which seems to run the best. It’s written in AVR C code, so it looks a bit different to the generic Arduino language but you should be able to figure a lot of it out and you can still open, edit and upload it using the Arduino IDE.

Here’s a video of the Chrome Dino Game being played on an Arduino:

What You Need To Run The Chrome Dino Game

How To Load The Game Onto Your Arduino

First, start off by plugging your LCD keypad shield onto your Arduino Uno. Make sure that all of the pins are lined up correctly so that you don’t bend any of them.

Next, plug the Arduino into your computer and start up the Arduino IDE. Make sure that the correct board is selected and that you’re working on the correct com port. Then upload the below sketch. You can also change a couple of things in the sketch if you like, I’ve outlined these after the sketch.

Here’s a brief overview of the game:

  • The game speeds up the longer you play it.
  • Cactuses are initially separated by a minimum of 5 spaces and this goes down to a minimum of 3 as the game progresses.
  • Cheating by holding down the button or continuously pressing the button is prevented.
  • The current score is displayed during the game and the high score at the end.
  • The high score is not saved and is lost when power to the Arduino is removed.

Let’s have a look at the code:

//From Hackster.io
//By BRZI

#include <avr/io.h>
#define  F_CPU 16000000UL //Our CPU speed (16MHz)
#include <util/delay.h> //Libraries for delay and interrupt utilities
#include <avr/interrupt.h>
#define command 0 //explained in dispSend() function
#define write 1

uint8_t upperBuff[16] , downerBuff[16], overMsgUpper[] = "Score:  ", overMsgDowner[] = "Best:   ", scoremsg[] = "Score:" , din[] = {0x0E, 0x17, 0x1E, 0x1F, 0x18, 0x1F, 0x1A, 0x12}, cact[] = {0x04, 0x05, 0x15, 0x15, 0x16, 0x0C, 0x04, 0x04};
        //Buffers for line one and two. Message to display after lost game.                     //Score text during game. //Dinosaur and cactus bitmaps
uint8_t canup = 1, longhold = 0, distance = 6, speed = 200, isup = 0, dontprint = 0; //All of these are explained further
uint16_t aVal = 0, score = 1, bestscore = 0;
int i;

void dispInit();
void dispWrite(uint8_t bits);
void dispSend(uint8_t bits, uint8_t act);
void dispSetLine(uint8_t line);
void dispClear();
void dispHome();
void dispPrintChar(uint8_t chr[], uint8_t size);
uint16_t aRead();

int main(void)
{
  for(i = 0; i < 17; i++) downerBuff[i] = ' '; //Initialize upper and downward buffer
  for(i = 0; i < 17; i++) upperBuff[i] = ' ';
  
  dispInit(); //Initialize the display
  
  TCCR1B |= (1 << WGM12) | (1 << CS11); //Set Timer1 to compare to OCR1A and prescaler of 8
  OCR1AH = (500 >> 8); //This equals to 2000Hz or 500us timing, look for TIMER1_COMPA_vect down below
  OCR1AL = 500;
  TIMSK1 |= (1 << OCIE1A); //Enable Timer1 CompA ISR
  sei(); //Enable global interrupt

  
  ADMUX = (1 << REFS0); //Set AREF to VCC
  ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADEN); //set ADC prescaler to 128 and enable ADC (defaulted to free running mode)
  
  while (1) {   
    
    ADMUX |= (1 << MUX2) | (1 << MUX0); //Set pin from ADMUX to ADC5 (floating)
    srand(aRead()); //Use it as a random seed
    ADMUX &= ~(1 << MUX2) & ~(1 << MUX0); //Revert back to ADC0 to read the button value

    if(aRead() > 900) longhold = 0; //Reads if Up button has been released to prevent cheating. The value is so low because if you hold your fingers beneath one of the buttons the voltage would drop, this prevents the dinosaur from locking up

    for(i = 0; i < 16; i++) downerBuff[i] = downerBuff[i + 1]; //Shifts everything in downward buffer by one place to the left
     if((rand() % 100) > (rand() % 100) && !dontprint){ //This portion decides if it should put a cactus or a blank spot, dontprint is used to prevent cactus grouping
      downerBuff[15] = 0x01; //0x01 represents the cactus (we added cactus and dinosaur to CGRAM when we initialized the display)
      dontprint = 1; //This part acts both as a boolean and a counter to ensure cactus separation
     }
     else downerBuff[15] = ' ';
     char lastchar = downerBuff[3]; //We remember the whats initially added to the downward buffer before replacing it with the dinosaur
     if(!isup){ //If din should be placed down
      downerBuff[3] = 0x00; //Place it down
      dispSetLine(2);
      dispPrintChar(downerBuff, sizeof(downerBuff)); //Draw it
      downerBuff[3] = lastchar; //Place back previous thing to the buffer
      canup = 1;  //This flag is used to disable dinosaur from getting up before it was drawn down, in this case he can go up
    } else { //If din should be placed up
      upperBuff[3] = 0x00; //Place it up in upper buff
      dispSetLine(1);
      dispPrintChar(upperBuff, sizeof(upperBuff));
      dispSetLine(2);
      dispPrintChar(downerBuff, sizeof(downerBuff)); //Draw it
      canup = 0; //In this case he wont go up until rendered on line 2
    }

    if(dontprint) dontprint++;
    if(dontprint > distance) dontprint = 0; //This is the part that ensures cactus separation, it will keep the cactus 3-5 spaces apart minimally (depends on the game progress)
    
    if(isup) isup++; //This part makes sure din is on upper side for 3 loops after he was initially drawn there
    if(isup > 4){
     upperBuff[3] = ' ';
     dispSetLine(1);
     dispPrintChar(upperBuff, sizeof(upperBuff));
     isup = 0;
    }
    for(i = 0; i < sizeof(scoremsg); i++) upperBuff[i + 5] = scoremsg[i]; //This part prints the current score during the game
    uint8_t cnt = 11;
    for(i = 10000; i > 0; i /= 10){
      upperBuff[cnt] = ((score / i) % 10) + '0';
      cnt++;
      dispSetLine(1);
      dispPrintChar(upperBuff, sizeof(upperBuff));
    }

    score++; //Increment the score once on loop
    if(score > bestscore) bestscore = score; //Remember best score
    
    if(lastchar == 0x01 && !isup){ //Check if the dinosaur is downward and hit a cactus
      dispClear(); //Clear the display and buffers
      for(i = 0; i < 17; i++) downerBuff[i] = ' ';
      for(i = 0; i < 17; i++) upperBuff[i] = ' ';
      uint8_t cnt;
      
      dispSetLine(1);
      for(i = 0; i < sizeof(overMsgUpper); i++) upperBuff[i] = overMsgUpper[i]; //Display worst and best score
      cnt = sizeof(overMsgUpper) - 1;
      for(i = 10000; i > 0; i /= 10){
        upperBuff[cnt] = ((score / i) % 10) + '0';
        cnt++;
      }
      dispPrintChar(upperBuff, sizeof(upperBuff));
      
      dispSetLine(2);
      for(i = 0; i < sizeof(overMsgDowner); i++) downerBuff[i] = overMsgDowner[i];
      cnt = sizeof(overMsgDowner) - 1;
      for(i = 10000; i > 0; i /= 10){
        downerBuff[cnt] = ((bestscore / i) % 10) + '0';
        cnt++;
      }
      dispPrintChar(downerBuff, sizeof(downerBuff));
      
      while(1){ //Wait for select button to be pressed
        aVal = aRead();
        if(aVal > 635 && aVal < 645){ //After that clear all the variables
          for(i = 0; i < 17; i++) downerBuff[i] = ' ';
          dispSetLine(1);
          dispPrintChar(downerBuff, sizeof(downerBuff));
          for(i = 0; i < 17; i++) upperBuff[i] = ' ';
          dispSetLine(2);
          dispPrintChar(upperBuff, sizeof(upperBuff));
          dontprint = 0;
          isup = 0;
          score = 1;
          speed = 200;
          longhold = 0;
          distance = 6;
          canup = 1;
          break;
             }
      }
      
    }
        if(score % 5 == 0) speed -=2; //If score is divisible by 5 make game faster by -2ms
    if(speed < 120) speed = 120; //Minimal time in ms (+ ~2ms) that the loop will be halted for (limited by display refreshing, in my testing 11.8Hz was readable enough to be playable)
    if(score % 175 == 0) distance--; //Every time you score a number divisible by 175 minimal cactus distance gets smaller
    if(distance < 3) distance = 3;
    for(i = 0; i < speed; i++) _delay_ms(1); //This is the only way as the compiler expects a const number here
  }
}

void dispInit(){
  _delay_ms(50); //Just in case
  DDRD = 0b11110000; //Set these pins to output. PD4 - PD7 correspond to D4 - D7 on display, we need to configure it to run in 4 bit mode
  DDRB = 0b00000011; //PB0 is tied to RS and PB1 to EN
  dispWrite(0x30);//*This part here is explained in Hitachi HD44780 datasheet on how to initialize the display in 4bit mode
  _delay_us(4500);//*Essentially you send the reset signal 3 times, and then set it to 4 bit mode
  dispWrite(0x30);//*
  _delay_us(4500);//*
  dispWrite(0x30);//*
  _delay_us(4500);//*
  dispWrite(0x28);//*
  dispSend(0x28, command); //Send 4bit mode function set
  dispSend(0x08, command); //Turn the display off
  dispSend(0x01, command); //Clear its RAM (if MCU resets that doesn't mean the display was reset, so we clear everything)
    _delay_ms(50);
  dispSend(0x0C, command); //Turn the display on
  _delay_ms(5);
  dispSend(0x40, command); //Tell the display we want to enter a custom character to its CGRAM (on address 0x00)
  for(i=0; i<8; i++) dispSend(din[i], write);
  dispSend(0x80, command); //Transaction end
  dispSend(0x48, command); //Same thing, but for 0x01
  for(i=0; i<8; i++) dispSend(cact[i], write);
  dispSend(0x80, command);
}

void dispPrintChar(uint8_t chr[], uint8_t size){
  for(uint8_t i = 0; i < size; i++) dispSend(chr[i], write); //Self explanatory 
}

void dispSetLine(uint8_t line){
  if(line == 2) dispSend(0xC0, command); //Sets the line where 0xC0 is line 2 and 0x80 is line 1
  else dispSend(0x80, command);
}

void dispClear(){
  dispSend(0x01, command); //Self explanatory 
  _delay_ms(2); //This command takes longer for the IC to process, this delay is necessary
}

void dispHome(){ //This function isn't used in this application but its there for expandability, it places the cursor on the line 1 column 1
  dispSend(0x02, command); //Self explanatory 
  _delay_ms(2);
}

void dispSend(uint8_t bits, uint8_t act){
  if(act) PORTB |= (1 << DDB0); //Set PB0 if we are writing a character, else pull it low
  else PORTB &= ~(1<<DDB0);
  dispWrite(bits); //Send the bit then shift them 4 bit to the left to work in displays 4bit mode
  dispWrite(bits << 4);
  _delay_us(80);
}

void dispWrite(uint8_t bits){
  PORTD = bits; //This is a dirty way to write it but it's perfect for this application as it's not bulky and PORTD isn't used for anything else anyway
  PORTB |= (1<<DDB1); //Pulse the PB1 to signal the IC to read the data
  _delay_us(1);
  PORTB &= ~(1<<DDB1);
  _delay_us(1);
}

uint16_t aRead(){
  ADCSRA |= (1 << ADSC); //This signal the avr to read the ADC value
  while  (ADCSRA & (1 << ADSC)); //Wait until it's finished
  return ADCL | (ADCH << 8); //Send it back stitched together
}

ISR (TIMER1_COMPA_vect){ //Timer ISR we set up earlier
  if(!longhold){ //Return if the Up button was still held
    aVal = aRead(); //Read from ADC0
    if(aVal > 450 && aVal < 600 && canup){ //Check if Up is pressed and that din was rendered down
     isup = 1;
     longhold++;
     }
    }
}

Download The Sketch – ChromeDinoGame

Playing The Chrome Dino Game On An Arduino

Things You Can Change In The Game

There are a couple of things you can edit to customise the game without knowing AVC C code, here are some of them:

  • Line 11 – Change the score/high score screen text.
  • Line 140 – Change by how much the speed increases every score increment of 5, or change the increment.
  • Line 141 – Change the maximum speed limit. The original coder suggests nothing less than 120ms a cycle or it becomes unplayable on the LCD.
  • Line 142 – Change how often the cactus spacing gets closer together.
  • Line 143 – Change the minimum cactus distance.

The game is quite easy to play on the LCD keypad shield although the buttons are not the best for quick presses and the LCD is quite slow, so it starts suffering from ghosting and brightness issues once the cactuses start moving quickly.

Let me know if you’ve loaded this game onto your Arduino Uno and what you’ve changed on it. Enjoy playing it!