Linux Guides, Tips and Tutorials | LinuxScrew https://www.linuxscrew.com LinuxScrew is a Linux resource that contains many useful guides on Linux, programming, networking, and more. Fri, 09 Apr 2021 15:24:08 +0000 en-US hourly 1 https://wordpress.org/?v=5.5.3 https://www.linuxscrew.com/wp-content/uploads/2020/10/favicon-7-48x48.png Linux Guides, Tips and Tutorials | LinuxScrew https://www.linuxscrew.com 32 32 Raspberry Pi & Python Powered Tank Part III: More Tank https://www.linuxscrew.com/raspberry-pi-python-powered-tank-part-iii https://www.linuxscrew.com/raspberry-pi-python-powered-tank-part-iii#respond Sun, 11 Apr 2021 14:45:22 +0000 https://www.linuxscrew.com/?p=6735 This was only meant to be a two-parter, but I couldn’t leave well enough alone. Raspberry Pi & Python Powered Tank Part III is an update with better Python code and an improved design. I’ve gone back to fix the biggest issues that were left after Part II. I’ve omitted a bit of the mucking… Read More »Raspberry Pi & Python Powered Tank Part III: More Tank

View the original article from LinuxScrew here: Raspberry Pi & Python Powered Tank Part III: More Tank

]]>
This was only meant to be a two-parter, but I couldn’t leave well enough alone.

Raspberry Pi & Python Powered Tank Part III is an update with better Python code and an improved design.

I’ve gone back to fix the biggest issues that were left after Part II. I’ve omitted a bit of the mucking about in this article to keep it focused on what worked.

How Did We Get Here?

If you haven’t yet checked out the full build process, here are the previous entries on LinuxScrew.com:

More projects:

The Gun

The completed tank in all its glory!

 

Tank BBs

The Tank’s BBs – they get everywhere!

First things first, the gun now works!

It shoots these little guys. They get everywhere.

Circuitry

Tank control circuit diagram

Tank control circuit diagram. You can see how the stripboard is cut, inset

Tank wired into Raspberry Pi GPIO

Tank wired into Raspberry Pi GPIO

Here’s an improved look at what controls the motors: The pin numbers are listed in the code below.

Camera

Now using a piCamera!

The Pi Camera is lighter, so the tank can move better

The Pi Camera is lighter, so the tank can move better

Enabling the PiCamera

The PiCamera needs to be enabled before it can be used. Done so by running:

sudo raspi-config

…and navigating to:

Interface Options -> Camera

…and enabling it.

Testing the PiCamera

To avoid much frustration, here’s how to test the camera BEFORE trying to write code to access it:

raspistill -o test.jpg

If you get a test.jpg file, your camera is working. If not, the ribbon cable probably isn’t attached correctly.

Note, if you set up your Raspberry Pi OS system on a Pi without a PiCamera slot and then move to one that has one, it won’t be detected.

Code Update

The L293D library I was using was kind of not great.

  • It would throw errors when telling the motors to stop if already stopped
  • It would only sometimes be able to run both motors simultaneously, even when threaded

So, I’ve rewritten the code to control the GPIO pins directly. It turns out it’s almost less code to do it this way than using the L293D library. Who knew.

New Dependencies

There are some new dependencies for the following code, installed below:

sudo apt install python3 python3-pip python3-opencv  python3-rpi.gpio python3-flask python3-picamera

This includes the libraries for using either the PiCamera or a USB web camera with OpenCV.

The Code

# This script tested for use with Python 3
# This script starts a web server, so needs to be run as root or using sudo
# This application will be available by accessing http://<your raspberry pi ip address>:5000

from flask import Flask, Response, render_template
import cv2
import sys
import RPi.GPIO as GPIO
import io
import time
import picamera
import logging 
import os

# Set up logging
logPath = os.path.dirname(os.path.abspath(__file__)) + '/pitank.log' # Makes sure the log goes in the directory the script is located in
print('Logging to ' + logPath)
logging.basicConfig(filename=logPath, level=logging.DEBUG)

# Initialise Flask, the Python web server package which serves all of this up to a browser
app = Flask(__name__)

# Define global variables for future use
# Some are initialized to None so that we can confirm they have been set up later

usbCamera = None
piCam = None

# GPIO Pins connected to LD293D chips
en1 = 22 # GPIO 25
in1 = 18 # GPIO 24
in2 = 16 # GPIO 23
# out1 to left motor
# out2 to left motor

en2 = 23 # GPIO 11
in3 = 21 # GPIO 9
in4 = 19 # GPIO 10
#out3 to right motor
#out4 to right motor

#Second l293d for GUN
g_en1 = 33 # GPIO 13
g_in1 = 35 # GPIO 19
g_in2 = 37 # GPIO 26
# out1 to gun motor
# out2 to gun motor

# Use BOARD pin numbering - ie the position of the pin, not the GPIO number. This is a matter of preference.
GPIO.setmode(GPIO.BOARD)

# Link to Pi Zero pinout
# https://i.stack.imgur.com/yHddo.png

# Setup to set up camera, motors

GPIO.setup(in1, GPIO.OUT)  
GPIO.setup(in2, GPIO.OUT)
GPIO.setup(en1, GPIO.OUT)

GPIO.setup(in3, GPIO.OUT)  
GPIO.setup(in4, GPIO.OUT)
GPIO.setup(en2, GPIO.OUT)

GPIO.setup(g_en1, GPIO.OUT)  
GPIO.setup(g_in1, GPIO.OUT)
GPIO.setup(g_in2, GPIO.OUT)

# Initialise CV2 with the first available USB camera, if one is being used
# No need to comment out if not in use, it doesn't throw an error if no camera is present
usbCamera = cv2.VideoCapture(0)

# PiCamera configuration
# Values chosen to maximise refresh rate on the network
framerate = 10
res = (1024, 568)
rotation = 180
quality = 80

# Initialise PiCamera
# Be sure to comment this line out if no PiCamera is present or an error is thrown
piCam = picamera.PiCamera(framerate=framerate, resolution=res)

time.sleep(2)  # Let the camera warm up

# If camera is mounted upside down or sideways, rotate the video feed
if piCam:
    piCam.rotation = rotation

# Cleanup function to release the camera and GPIO pins ready for the next time
def cleanup():

    print('Done, cleaning up video and GPIO')
    GPIO.cleanup()
    usbCamera.release()
    cv2.destroyAllWindows()
    sys.exit("Cleaned up") # Make sure the application exits after a cleanup in case it was called due to an error

# Function to generate frames from the USB camera using the cv2 library
def generateUsbCameraFrames():  # generate frame by frame from camera

    # Ensure the global camera variable is used in this scope
    global usbCamera

    # Only try to generate frames if the camera variable has been populated
    if usbCamera:
        while True:
            # Capture frame
            success, frame = usbCamera.read()  # Reads the camera frame
            if not success:
                break
            else:
                ret, buffer = cv2.imencode('.jpg', frame)
                frame = buffer.tobytes()
                yield (b'--frame\r\n'
                    b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')  # Concat frame one by one and show result

# Function to generate frames from an attached PiCamera
def generatePiCameraFrames():

    global piCam
    global quality

    if piCam:
        while True:
            try:
                image = io.BytesIO()
                piCam.capture(image, 'jpeg', quality=quality, use_video_port=True)
                frame = image.getvalue()
            
                yield (b'--frame\r\n'
                    b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')  # Concat frame one by one and show result
            except:
                 break

# @app.route decorators tell flask which functions return HTML pages

# Video streaming route - should output the live video stream
@app.route('/video_feed')
def video_feed():
    # Comment out the video capture method you're not using
    #return Response(generateUsbCameraFrames(), mimetype='multipart/x-mixed-replace; boundary=frame')
    return Response(generatePiCameraFrames(), mimetype='multipart/x-mixed-replace; boundary=frame')

# Main/index route - the page that loads when you access the app via a browser
@app.route('/')
def index():
    return render_template('index.html') # Ensure that index.html exists in the templates subdirectory for this to work

# Control routes - these routes will be hit when the related link is pressed from index.html

@app.route('/control_stop')
def control_stop():

    # Ensure the global GPIO variables are used in this scope
    global en1
    global in1
    global in2
    global en2
    global in3
    global in4

    # Stop
    GPIO.output(en1,GPIO.LOW)
    GPIO.output(en2,GPIO.LOW)
    
    # Return an empty (successful) response regardless of what happened above
    return Response("", mimetype='text/plain')

@app.route('/control_forward')
def control_forward():

    # Ensure the global GPIO variables are used in this scope
    global en1
    global in1
    global in2
    global en2
    global in3
    global in4

    # Left Motor Forward
    GPIO.output(in1, GPIO.HIGH)
    GPIO.output(in2, GPIO.LOW)
    GPIO.output(en1, GPIO.HIGH)

    # Right Motor Forward
    GPIO.output(in3, GPIO.HIGH)
    GPIO.output(in4, GPIO.LOW)
    GPIO.output(en2, GPIO.HIGH)

    return Response("", mimetype='text/plain')

@app.route('/control_back')
def control_back():

    # Ensure the global GPIO variables are used in this scope
    global en1
    global in1
    global in2
    global en2
    global in3
    global in4

    # Left Motor Back
    GPIO.output(in1,GPIO.LOW)
    GPIO.output(in2,GPIO.HIGH)
    GPIO.output(en1,GPIO.HIGH)

    # Right Motor Back
    GPIO.output(in3,GPIO.LOW)
    GPIO.output(in4,GPIO.HIGH)
    GPIO.output(en2,GPIO.HIGH)

    return Response("", mimetype='text/plain')

@app.route('/control_turn_right')
def control_turn_right():

    # Ensure the global GPIO variables are used in this scope
    global en1
    global in1
    global in2
    global en2
    global in3
    global in4
    
    # Left Motor Forward
    GPIO.output(in1, GPIO.HIGH)
    GPIO.output(in2, GPIO.LOW)
    GPIO.output(en1, GPIO.HIGH)
    
    # Right Motor Back
    GPIO.output(in3,GPIO.LOW)
    GPIO.output(in4,GPIO.HIGH)
    GPIO.output(en2,GPIO.HIGH)

    return Response("", mimetype='text/plain')

@app.route('/control_turn_left')
def control_turn_left():

    # Ensure the global GPIO variables are used in this scope
    global en1
    global in1
    global in2
    global en2
    global in3
    global in4
    
    # Left Motor Back
    GPIO.output(in1,GPIO.LOW)
    GPIO.output(in2,GPIO.HIGH)
    GPIO.output(en1,GPIO.HIGH)
    
    # Right Motor Forward
    GPIO.output(in3, GPIO.HIGH)
    GPIO.output(in4, GPIO.LOW)
    GPIO.output(en2, GPIO.HIGH)

    return Response("", mimetype='text/plain')

@app.route('/control_fire')
def control_fire():

    global g_en1
    global g_in1
    global g_in2

    # Gun Motor Forward
    GPIO.output(g_in1, GPIO.HIGH)
    GPIO.output(g_in2, GPIO.LOW)
    GPIO.output(g_en1, GPIO.HIGH)

    # Fire the gun for 2 seconds to get some shots off
    time.sleep(2)

    # Stop
    GPIO.output(g_en1,GPIO.LOW)

    return Response("", mimetype='text/plain')

# Launch the Flask web server when this script is executed
# Catch KeyboardInterrupt so that if the application is quitting, cleanup can be run
try:

    if __name__ == '__main__': # If the script is being run directly rather than called by another script

        # Start the flask app!
        app.run(host='0.0.0.0', port=5000, debug=True, use_reloader=False)
        # App is run WITHOUT threading to reduce the chance of camera/GPIO conflict - only one concurrent user is expected, so this is fine
        # Debug is enabled so we can see what's happening in the console
        # However, the app automatically reloads when debug=True, which causes camera/GPIO conflicts, so this is disabled with use_reloader=false
        # This application will be available by accessing http://<your raspberry pi ip address>:5000

except KeyboardInterrupt:

    pass

finally:

    # Ensure cleanup on exit
    cleanup()

# End of file!

 

Starting the Remote Control Server on Boot

To start up the remote control interface on boot rather than having to SSH into the pi, add it to the root crontab by running:

sudo crontab -e

And adding this line to the end:

@reboot python3 /home/pi/pitank/tank_remote.py

This will start the server every time the Pi boots – just make sure the path at the end of the line points to your python file.

WiFi

I was going to set up the Pi as a wifi hot spot so that a tablet or phone could connect to it for remote control, but it would cause more trouble than it’s worth as it means the Pi would no longer have internet access for updating the software (Or running an IRC server from a tank because why not).

Instead, I’ve enabled the WiFi hotspot on my phone and set up the Pi to connect to that. Road warrior!

New Interface

Here’s what the new interface looks like:

The new remote control interface

The new remote control interface – Yes, it’s a mockup, I forgot to screenshot it, but it does look like this.

In the Field

Trophy skull

Trophy skull

Misc Notes

  • The PiCamera is laggier than using a webcam but much lighter. Choose one based on your preference (or if you have a better RC vehicle that can tow more weight)
  • Due to this and the general slowness of the PiZero (encoding and transmitting the image takes time), video responsiveness wasn’t a priority – code simplicity was.
    • If you were building something really fancy, you could send the video feed over its own radio channel with faster hardware or something.
  • With this in mind, you could always modify the code so the tank only moves a set distance on each command so it can’t run away.
  • If you have a better, faster (but still simple! no big includes – people need to understand what they’re coding when following this!) way to stream the vid, let me know!
  • This tank is still the ULTIMATE DESTROYER OF AA BATTERIES.
  • If another client visits the remote control web page, the feed on the first stops. This is OK; I only expect one user at a time.
  • I’ve left the Flask server running with the video feed open in a browser for a while, and it doesn’t seem to crash, which is nice.
Tank close up

Tank close up

More

Here are some of my previous projects for LinuxScrew:

I’ve got more junk, more ideas (Roboduck…), and lots more solder to burn through, so follow LinuxScrew and myself on Twitter if you’re keen for more attempts to put computers inside of things.

Probably the last you'll see of the Python/Pi tank

Probably the last you’ll see of the Python/Pi tank.

View the original article from LinuxScrew here: Raspberry Pi & Python Powered Tank Part III: More Tank

]]>
https://www.linuxscrew.com/raspberry-pi-python-powered-tank-part-iii/feed 0
DIY Arduino Powered Soldering Extraction Fan https://www.linuxscrew.com/diy-arduino-powered-soldering-extraction-fan https://www.linuxscrew.com/diy-arduino-powered-soldering-extraction-fan#respond Sat, 10 Apr 2021 14:44:37 +0000 https://www.linuxscrew.com/?p=6721 This article will show you to build a DIY soldering extraction fan (to ventilate fumes) using an Arduino and a temperature sensor to activate the fan when the soldering iron is hot. Before you read this, why not open up my other projects in some new tabs for further reading when you’re done? Smart Mirror… Read More »DIY Arduino Powered Soldering Extraction Fan

View the original article from LinuxScrew here: DIY Arduino Powered Soldering Extraction Fan

]]>
This article will show you to build a DIY soldering extraction fan (to ventilate fumes) using an Arduino and a temperature sensor to activate the fan when the soldering iron is hot.

Before you read this, why not open up my other projects in some new tabs for further reading when you’re done?

The Project

A thoughtful commenter on a previous article suggested I get an extraction fan to use while I solder.

Bringing to attention the fact that:

  • I suck at soldering and burn more solder than is probably safe
  • I don’t have a proper work area

They were probably unaware of the fact that:

  • I’m too lazy and/or forgetful to actually turn on any extraction fan that’s present.
  • I didn’t inhale

Anyway, a technical solution is in order. After setting up a new work area with a soldering mat, I got to work on building an automatic extraction fan.

The aim is to have a fan that will switch itself on when I start soldering by reading a temperature change.

The Parts

Electronic Parts

Electronic parts – some are a bit worse for wear, but they work!

  • 10mm Plastic tube
  • 10mm 5v USB powered fan
    • 5v USB bus-powered fan because I want to power it from the Arduino if possible
    • A standard 12v PC fan wouldn’t suit unless I added a 12v power supply
  • Arduino (mine’s some kind of clone, but it says ‘Pro Micro’ on it and identifies itself as an Arduino Leonardo)
    • As you can see, this Arduino has seen better days – it’s been wrenched off of various things and re-used, but it still works
  • DHT11 Sensor – it senses temperature and humidity, and it’s cheap
  • MOSFET – this allows you to turn the power to the fan on/off from an Arduino pin – it’s basically a switch you can toggle by changing the voltage on the control pin
  • Resistor
  • I had originally planned to use an LED but instead replaced it with a push button to allow for manually turning the fan on
    • There was limited room on the breadboard I was using to have both, and the Arduino has an LED on it anyway.
  • Arduino IDE for Linux was used to write the code and load it onto the Arduino.
  • Fritzing was used to make the circuit diagram shown below

All in all, there’s maybe 25 bucks worth of stuff here, so it makes for a good weekend project.

Work area

Cluttered work area

The Build

There’s not a lot to be said for the design of this – Various cylindrical scrap objects from my flat were tried in series until I found some that could be taped together to secure the fan to the plastic tube.

See the video at the end of the article for me fumbling about with various food packaging to get the job done.

Eventually, once everything fit, I secured it with tape to test.

Snapped fan blade

Snapped fan blade

One of the first things I did was accidentally snap off one of the fan blades. It was later fixed with glue.

On testing – there was airflow at both ends! So that works.  That nasty masking tape will be replaced later on with something less temporary.

The USB plug on the fan was lopped off and wired into the Arduino with button and sensor as follows:

Circuit diagram

Circuit diagram

Fritzing is a great tool for visualizing your circuitry. Yeah, it’s not a real circuit diagram, but I haven’t made one of those since high school, and also, I don’t care – this illustrates what’s happening better anyway.

In the diagram above, you can see:

  • Top row:
    • Arduino
    • Push button
  • Bottom Row
    • MOSFET
    • DHT11
    • Resistor
    • Motor

      Prototyping

      Prototyping with Fritzing

 

The fan is wired to the RAW power pin on the Arduino rather than VCC – the RAW pin isn’t present on all boards, but it’s a direct link to the board’s power feed.

If the fan is connected to VCC, which means it’s getting its power via the board and not directly from the PSU, it quickly causes issues as it draws too much current for the Arduino to handle.

The Code

I ran into a small issue using the Arduino IDE where I’d receive a ‘permission denied’ error whenever I tried to load my code to the Arduino device.

This is because Arduino IDE, when installed from some Linux app stores, is missing a dependency – simply run:

sudo apt install avrdude

… to resolve this.

Loading the code onto the Arduino

Loading the code onto the Arduino

The upcoming code requires the DHT Sensor Library in Arduino IDE. It can be found by simply searching the available libraries and installing it from there. ‘Install All’ to install it with the required dependencies. See the video further down for carnage.

Installing DHT sensor library

Installing DHT sensor library

And without further fuss, here’s the Arduino code to run the automatic fan, with commentary:

// DHT sensor library can be found at https://github.com/adafruit/DHT-sensor-library
// It's available in the Arduino IDE libraries - just search for it

#include "DHT.h"

// Hardware configuration

int fanPin = 4; // Digital pin to control the fan via the MOSFET
long fanTime = 5000; // Number of milliseconds the fan will run for when triggered (5 Minutes)
int buttonPin = 2; // Digital pin the button is wired to
int dhtPin = 3; // Digital pin the DHT sensor is wired to
#define dhtType DHT11 // Define constant variable : Sensor type I have is DHT11, could also be DHT22 or DHT21

// Trigger conditions

int buttonState = HIGH; // The current button status - default unpressed (HIGH, as it's grounded on press!)
int triggerTemp = 25;// Temperature to trigger the fan at (Celsius)

// Initialize DHT sensor

DHT dht(dhtPin, dhtType);

// Setup function, runs once when the Arduino boots

void setup() {
  
  pinMode(fanPin, OUTPUT); // Set fan control pin as an output
  pinMode(buttonPin, INPUT); // Set button read pin as an input
  
  // Start outputting data on the Arduionos USB serial interface so that activity can be monitored from Arduino IDE
  
  Serial.begin(9600);
  Serial.println(F("DHT11 fan contoller ready."));
  
  // Start reading data from DHT module
  
  dht.begin();

  delay(3000); // Wait 3 seconds before continuing to the loop to give the hardware time to be ready
  
}

// Main code loop runs repeatedly while Arduino is powered

void loop() {
  
  buttonState = digitalRead(buttonPin);  // See if the button is currently pressed
  
  // Reading temperature/humidity from sensor takes about 250ms and values may be up to 2 seconds old as they are slow sensors
  
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature(); // Read temperature as Celsius - default
  float farenheit = dht.readTemperature(true); // isFahrenheit = true : Read temperature as Fahrenheit - not used in this script, but there for Yankees and... Burma?
  
  // Abort if any readings fail
  
  if (isnan(humidity) || isnan(temperature) || isnan(farenheit)) { // Read temperature as Fahrenheit - not used in this script, but there for Yankees and... Burma?
  
    Serial.println(F("Failed to take reading from DHT sensor."));
    return; // Exits, aborting this read attempt
    
  }

  // The DHT libarary can calculate the heat index if you want to use that as the trigger instead.

  float heatIndexFahrenheit = dht.computeHeatIndex(farenheit, humidity); // Heat index in Fahrenheit - default
  float heatIndexCelsius = dht.computeHeatIndex(temperature, humidity, false); // isFahreheit = false : Heat index in Celsius

  // Print out some data to the serial console so we know everything is working

  Serial.print(F(" Humidity: "));
  Serial.print(humidity);
  Serial.print(F("%  Temperature: "));
  Serial.print(temperature);
  Serial.print(F("C "));
  Serial.print(farenheit);
  Serial.print(F("F  Heat index: "));
  Serial.print(heatIndexCelsius);
  Serial.print(F("C "));
  Serial.print(heatIndexFahrenheit);
  Serial.println(F("F"));

  // Print the button state

  if (buttonState == LOW) {

    Serial.println(F("Button pressed."));
    
  } else {
    
    Serial.println(F("Button not pressed."));
    
  }

  // If the fan is triggered

  if (buttonState == LOW || temperature >= triggerTemp) { 
    
    digitalWrite(fanPin, HIGH);  // Turn Fan ON
    Serial.println(F("Fan On!"));

    // Keep the fan on for the specified time
    delay(fanTime);

  } 

  //When the delay after the fan is triggered is over, or the trigger condition hasn't been met, just make sure the fan is OFF

  digitalWrite(fanPin, LOW);  // turn Fan OFF
  Serial.println(F("Fan Off!"));
  
}

Temperature and humidity readings are output on the serial interface of the Arduino so that I can test things are working correctly.

Reading the temperature over the serial interface

Reading the temperature over the serial interface

It Works!

It works!

It works! I’ll add a case for it later on and probably mount it to the end of the soldering iron holster or something.

And just like that, it works. I really like the simplicity of making things on the Arduino platform.

When the soldering iron is on, it raises the temperature near the sensor, which then triggers the fan, which will turn off after 5 minutes after the temperature drops again.

Tidied up with better tape

Tidied up with better tape

I also replaced the nasty masking tape from earlier with something a bit nicer. Looks good!

Improvements / Other Ideas

There are a few things I will probably do to improve this system:

  • Investigate using an MQ135 gas sensor to trigger the fan when fumes are present instead of temperature
  • Or, set it up so that it enables the fan when the temperature rapidly changes – it’s going to be hot in my flat in summer (the British apparently don’t believe in air conditioning) so having a simple temperature threshold probably won’t work
  • A case!
    • I could even mount it inside the soldering iron holster for convenience

Other Uses

The DHT11 sensor also handles humidity, so there are a few other things that could be done with a (tidied up) version of this:

  • Automatic kitchen extraction fan, triggered by the heat of cooking
  • Automatic bathroom extraction fan, triggered by the humidity of the shower running
  • That’s about it, I guess

Video!

Seeing as I was setting up a new workspace, I decided to include a camera. I’m still getting used to positioning things, so they are in the shot, and there’s a lot of room for improvement, but let me know what you think!

If you want to see more of this stuff, follow LinuxScrew and myself on Twitter and let us know!

View the original article from LinuxScrew here: DIY Arduino Powered Soldering Extraction Fan

]]>
https://www.linuxscrew.com/diy-arduino-powered-soldering-extraction-fan/feed 0
Installing OpenWrt on a BT HomeHub 5 (or Plusnet Hub One), Full Instructions https://www.linuxscrew.com/openwrt-bt-homehub-5-instructions https://www.linuxscrew.com/openwrt-bt-homehub-5-instructions#respond Fri, 09 Apr 2021 14:54:16 +0000 https://www.linuxscrew.com/?p=6841 This article documents my success in getting the OpenWrt operating system up and running on a BT HomeHub 5A (also sold as the Plusnet Hub One). This was checked with the latest version of OpenWRT as of early 2021, so it’s up to date and ready to do some networking. Once you’ve got an OpenWrt device… Read More »Installing OpenWrt on a BT HomeHub 5 (or Plusnet Hub One), Full Instructions

View the original article from LinuxScrew here: Installing OpenWrt on a BT HomeHub 5 (or Plusnet Hub One), Full Instructions

]]>
This article documents my success in getting the OpenWrt operating system up and running on a BT HomeHub 5A (also sold as the Plusnet Hub One). This was checked with the latest version of OpenWRT as of early 2021, so it’s up to date and ready to do some networking.

Once you’ve got an OpenWrt device set up, you can start mucking around with a bunch of useful computer networking concepts and tools like ad blockers and segregated networks – I’ll explore some common usage in future articles.

This tutorial is specific to the BT HomeHub 5 Type A – but if you can find a device on the OpenWrt wiki (and recent releases of OpenWrt are supported on it) – you can follow the steps listed there to flash your device.

This tutorial aims to show how cracking devices and loading OpenWrt onto them isn’t nearly as daunting as the OpenWrt wiki makes it seem.

You can also skip the hacking and use a Raspberry Pi! I cover this later in the article; scroll past the HomeHub stuff.

Follow this guide at your own risk – you could brick your router – so make sure you use a spare.

All credit for this stuff really goes to the people at the OpenWrt project. They cracked the devices, wrote the code, and figured it out – I’m just relaying the steps they created with some minor tweaking and explanation.

What is OpenWRT?

OpenWRT is a Linux-based operating system designed to be run on routers and other embedded devices. It’s a full computer OS so you can do whatever you want with it, but its primary use (and the purpose of most of the tools and interfaces that ship with it by default) is for networking.

OpenWrt can be set up as a router, WiFi hotspot, managed switch, file sharing server – it has many tools for managing these services that go above and beyond what your generic off-the-shelf router can do.

In this article and in the OpenWrt documentation, you’ll see the occasional reference to LEDE – this is just the OpenWrt Project’s former name.

Some OpenWrt highlights:

  • Create multiple WiFi networks
  • Segregate networks, keeping untrusted devices separate from your own
  • Captive portal – run a WiFi hotspot that requires login using a web form (good for cafes!)
  • Firewall and ad blocker – keep your network secure
  • Much, much more – I’ll start covering the above and more in future articles, using this project as a jumping-off point to building the ultimate home lab setup

What You’ll Need:

  • BT HomeHub 5 Type A (and only Type A!) can be found on eBay for about 10 quid. Please don’t buy a pre-flashed one; it’s no fun, and you don’t know what the seller could have loaded on there.
  • USB serial adapter PL2302HX – these are cheap, and Linux compatible
  • A Linux computer to do all this stuff from
    • I used Pop!_OS, but steps should work on any modern Linux OS like Ubuntu or Arch.
  • Jumper wires (aka breadboard wires) – these wires have male and female connectors, making them good for adding a serial port to the router, which can be disconnected when the job is done
  • Soldering iron
  • Electrical tape
  • USB Stick
  • Ethernet Cable

Why a BT HomeHub 5A?

  • For a start, it’s compatible with OpenWrt.
  • Ubiquitous and cheap – you can get them on eBay for under a tenner
  • Lots of network ports
  • A USB port for extra storage
  • WiFi too
  • Built-in ADSL modem if you want to use it to replace your home router completely
  • Breaking into it will be F U N

Overview of the Steps Ahead

Here’s an overview of what all of the stuff below is actually doing:

  • Adding a serial port to the router so that custom software can be loaded to it
  • Sending a custom bootloader over to the router via the serial connection
  • Using that bootloader to load the OpenWrt operating system installer to the router over an ethernet connection using TFTP
  • Installing OpenWrt

Hacking The Hardware

First, a quick tour of the router:

BT Homehub 5 Front

This is the front.

BT HomeHub 5 Back

This is the back.

BT HomeHub 5 top

This is the top.

Moving on.

Opening the Router

Opening the router is not fun. Use a credit card or prying tool to work around the edges until it comes loose.

Trying to pry the case open

Trying to pry the case open.

Starting at the corners is easiest. You’ll eventually find a spot that gives way.

You might break some clips while doing it. As far as I’m concerned, this isn’t a bad thing as it’ll make it easier to open next time.

Opened HomeHub 5

Here’s the opened router and the front of the circuit board inside

HomeHub 5 circuit board

Circuit board removed, showing the ports at the back of the router

Adding a Serial Port

Four jumper wires need to be soldered to add the serial port to the router.

These are all soldered to the front of the circuit board – the side without the ethernet ports.

Serial port wires soldered to circuit board

Serial port wires soldered to the circuit board.

Each jumper wire has had the male end lopped off and been soldered to a solder pad on the board.

The photo above shows the locations for these solder pads, and the below table gives more detailed info on exactly what to solder to.

Resistor Number (Labelled onboard) Wire in Photo Function Info
R77 Yellow Serial TX Below NAND flash. Use solder pad right of it.
R78 Green Serial RX Directly below R77, to the left. Use solder pad to the right of it.
R45 Purple boot_sel2 Below R78 to the right. Use solder pad above it.
GND Brown Use solder pad for WPS switch closest to NAND flash.

Additional photos of the solder pads can be found on the OpenWrt wiki, here.

Here's a close up of the soldered wires showing the solder point locations

Here’s a close up of the soldered wires showing the solder point locations

Wire labels

Be sure to label things before you close the case!

Taped wires

I’ve taped the wires down so that the solder joints can’t be tugged on and break.

Putting it Back Together

I’ve drilled a hole to pass the wires through to make them available after closing the case.

Just in case I have to access them later, it’ll save trying to pry that case open again.

Case hole for wires

Tidy!

HomeHub 5 reassembled

Re-assembled and ready to go.

With the serial port added, it’s now possible to load OpenWrt onto the HomeHub.

Connecting the Serial Port

Serial adapter

Here’s the USB-Serial adapter I’ll be using.

Serial adapter wires

Here’s each wire on the serial end of the USB adapter.

This adapter comes with the same kind of jumper wire pins I’ve soldered to the board, so they’ll just slot together.

Connected to laptop

Serial and ethernet connected from router to laptop.

Connect the serial adapter to the router using the newly soldered port.

  • Ground (GND) connects to ground.
  • TX on the router connects to RX on USB serial adapter
  • RX on the router connects to TX on USB serial adapter
  • Leave boot_sel2 disconnected

Connect an ethernet cable from your computer to the first yellow port on the router as well.

Accessing the Router over the Serial Port

Now that everything’s connected, we can use picocom – a terminal application for Linux to connect to the router.

Install picocom using apt:

sudo apt update

sudo apt install picocom

The lsusb command can then be used to make sure that the USB to serial adapter has been detected:

lsusb
Identifying serial port

You can see it there – Pl2303 Serial Port.

Find out where the USB adapter is located on your system using demesg, which outputs recent messages from the system, and tail, which restricts output to the last few lines:

sudo dmesg | tail
Identifying serial port

Above, you can see that the serial adapter was attached to ttyUSB0.

Connect to the serial device using picocom and the location given by dmsg:

picocom -b 115200 /dev/ttyUSB0

When the connection is ready to use, you’ll see:

Terminal ready
Terminal Ready

Terminal Ready

To skip normal boot and give us the ability to boot our own firmware, we need to ground the boot_sel2 wire we added above.

Finding GROUND

The outer edge of the USB socket is connected to GROUND

To easily ground boot_sel2, touch the outside of the USB plug as shown.

Short boot_sel wire to ground and power up the router, and you should see the following output in picocom:

ROM VER: 1.1.4
CFG 04
UART
UART

Ready to load the bootloader

If you see something like:

ROM VER: 1.1.4
CFG 06
NAND
NAND Read OK

DDR autotuning Rev 0.3d
DDR size from 0xa0000000 - 0xa7ffffff
DDR check ok... start booting...

You’ve failed to ground the boot_sel pin properly.

Once you’ve seen the UART message, you can un-ground the boot_sel pin.

Loading the Bootloader onto the Router

Head on over to the OpenWrt page for the HomeHub 5A and scroll to the download section:

https://openwrt.org/toh/bt/homehub_v5a#downloads

Download the following file:

lede-lantiq-bthomehubv5a_ram-u-boot.asc

Note that file names may have changed on the OpenWrt site, but they will be the ones with the closest names to what I’ve written.

Now, stream the downloaded file to the HomeHub over your serial connection using the cat program:

cat lede-lantiq-bthomehubv5a_ram-u-boot.asc > /dev/ttyUSB0

You can do this in a new terminal with picocom still running in the other.

Bootloader loading...

Bootloader loading…

Head back to your picocom window, and you’ll see a bunch of * as the file is streamed across the serial connection.

When it’s done, you should see something like this in the picocom window – don’t worry about those errors for now. The bootloader is ready.

Bootloader loaded

Bootloader loaded

Serving the OpenWrt Install Image to the HomeHub using TFTP

TFTP (Trivial File Transfer Protocol) is commonly used by small devices to load software. It’s a dumb file transfer program that doesn’t require a lot of resources.

Install the tftpd TFTP service by running the following on your Linux machine (not the router):

sudo apt install tftpd-hpa

You can confirm that it is running using:

sudo systemctl status tftpd-hpa
TFTPD running

TFTPD running

You’ll see it running per the below screenshot.

The configuration file for tftpd is located at

/etc/default/tftpd-hpa

hh5 wrt 25 tftpd conf

There’s no need to modify it, but take note of the contents – particularly the directory in use:

/srv/tftp

This is where we need to put files that will be served over tftpd.

Next, the OpenWrt install image needs to be downloaded, again from

https://openwrt.org/toh/bt/homehub_v5a#downloads

Grab the file named

lede-lantiq-xrx200-BTHOMEHUBV5A-installimage.bin

and copy it to /srv/tftp:

sudo cp lede-lantiq-xrx200-BTHOMEHUBV5A-installimage.bin /srv/tftp/

The tftpd server and install image are now ready.

The HomeHub will assume an IP address of:

192.168.1.1

…and will assume the TFTP server it needs to connect to will have an IP address of

192.168.1.2

So, let’s give my laptop the latter IP address (this will be applicable until the next reboot – the ip address add command is not permanent)

sudo ip address add 192.168.1.2/24 dev enp0s25

enp0s25 is the name of my ethernet adapter – find yours by running:

ip link show

you can confirm with:

ip addr show

Or, you can use the control/settings panel on your system.

Now that both your computer and the router will be able to see each other over the ethernet connection, you can run the following on the serial terminal to the HomeHub to start loading the install image via TFTP:

tftpboot lede-lantiq-xrx200-BTHOMEHUBV5A-installimage.bin; bootm

And watch it go!

OpenWrt Installing

OpenWrt Installing

It’ll pause while decompressing for a bit.

When it’s ready, you’ll see

Please press Enter to activate this console.
Activating the console

Activating the console

So, press enter! You’ll get the following generated installation guide:

Generating install instructions

Generating install instructions

*******************************************************************************
*    Guide for installing LEDE onto a BT Home Hub 5 Type A/Plusnet Hub One    *
*******************************************************************************

You need an USB flash drive with at least 512MB free capacity, formatted as
FAT32 or exFAT, attached to the Hub. The attached flash drive will be
accessible at /tmp/mounts/<directory>.

To install LEDE, the USB flash drive must have a copy of the file:
lede-lantiq-xrx200-BTHOMEHUBV5A-squashfs-sysupgrade.bin

To restore the stock Firmware, the USB flash drive must have a have a copy
of the nanddump backup file (hh5a.nanddump) containing the stock Firmware.

LIST OF COMMANDS

To create a backup of the installed firmware (Take care to not overwrite
any previously made backup):
    nanddump --file /tmp/mounts/<directory>/hh5a.nanddump /dev/mtd4

To install LEDE:
    prepare

To restore stock firmware:
    restore

TO UPGRADE FROM OpenWrt OR LEDE PRIOR TO XXX:

Restore the stock firmware and do a fresh install of LEDE.

If you don't have a backup of the stock firmware from your Hub, use the
experimental 'migrate' script. It is offered with no guarantee of success. 
You are strongly advised to restore the stock firmware.

Please scroll back to view ALL of the above instructions.

Don’t start following it – this article does things a bit differently.

Downloading The OpenWrt Operating System

Download the latest release of the OpenWrt OS itself from the page at:

https://openwrt.org/toh/bt/homehub_v5a#downloads

As of writing, this is

openwrt-19.07.7-lantiq-xrx200-bt_homehub-v5a-squashfs-sysupgrade.bin

This may not appear in the main downloads area on that page – scroll down!

Copy this file to a FAT32 formatted USB stick – don’t change the filename!

Backing up the Existing Router Firmware (Important)

Attach the USB stick to the HomeHub.

Once plugged in, you’ll see the following in your picocom window:

USB stick attached

USB stick attached

As the generated guide above mentioned, you need to run:

nanddump --file /tmp/mounts/<directory>/hh5a.nanddump /dev/mtd4

…to backup the stock firmware (note /dev/mtd4 may be different on your HomeHub – check the instructions generated for your device!).

<directory> will also differ for your device and USB stick – run:

ls /tmp/mounts

…to find out what it should be.

Once the above command is run, you might be waiting around for 10 minutes for things to complete.

Backing up firmware

Backing up firmware

After the dump is done, we need to make sure it is at least 128MB in size – if it isn’t something went wrong, try again:

ls -la /tmp/mounts/<directory>/
Firmware backed up

Firmware backed up

Installing the New OpenWrt Firmware

To prepare for the installation, run the following in the picocom window:

prepare

You will receive a warning – heed it!

Backup warning

Pay attention to warnings!

…then type:

YESIHAVEABACKUP
Router selection

Router selection

…then select which ISP supplied the router:

The install script will supply you with a command to run, which will look something like this:

sysupgrade /tmp/mounts/<directory>/lede-lantiq-xrx200-BTHOMEHUBV5A-squashfs-sysupgrade.bin

Ignore it! We’ve downloaded a file with a different name to make sure we have OpenWrt’s latest release.

Instead, run:

sysupgrade /tmp/mounts/USB-B1/openwrt-19.07.7-lantiq-xrx200-bt_homehub-v5a-squashfs-sysupgrade.bin
OpenWrt Installed

OpenWrt Installed

You may get the UART prompt again, or you’ll get a success message, and the device will reboot – either way, OpenWrt is installed!

Power off the device using the power button, remove the USB stick and serial cable and turn it back on.

If at any point during the installation it fails with the message:

Invalid sysupgrade file.
Image check 'platform_check_image' failed.

…try again with a different USB stick, re-download the OpenWrt release file, and try again – the verification process is very sensitive.

Restoring

If something goes really wrong (or just refuses to work), you can restore the stock firmware that was backed up by running the following command in the picocom window:

restore

Before doing this, ensure that the file

hh5a.nanddump

…which was backed up is present on the USB stick.

A warning, though – restoring takes a really long time.

Then, you can try it all again.

Accessing OpenWrt After Installation

Navigate to

http://192.168.1.1

…in your web browser, and you should see the login screen.

If you don’t, something went wrong. Check that you are connected to the router via ethernet, and if that doesn’t work, restore the stock firmware and try again.

OpenWrt Running

OpenWrt Running

First thing you should do: Go go System -> Administration from the top menu and set a password!

Cleaning up – Purging tftpd from your System

Given that TFTP lacks authentication and is just running in the background on your Linux computer, you probably want to remove all traces of it before you go connecting to any public wifi network.

Remove tftpd and all configuration by running:

sudo apt purge tftpd-hpa

Then, remove the directory it was using to host files:

sudo rm -r /srv/tftp

You can now remove your static IP assignment if you did it through a GUI or reboot if you used the command line to assign one temporarily.

Using a Raspberry Pi Instead

If you’re not in the UK with access to a HomeHub and aren’t confident in finding a compatible router to upgrade, a Raspberry Pi is an easy (though less fun) alternative that doesn’t require soldering or unlocking.

To install OpenWrt on a Raspberry Pi, simply download the right install image for your Pi model from:

https://openwrt.org/toh/raspberry_pi_foundation/raspberry_pi#installation

…and use the Raspberry Pi Imager available at:

https://www.raspberrypi.org/software/

…to write the OpenWrt image to an SD card. Put the card in your Pi, and OpenWrt is ready to go!

So Far, So Good, Now What?!

Now you want to build a kickass network to kick start your home lab and learn some networking fundamentals. I’ll follow this article up with a bunch of things you can do with OpenWrt to make your network more useful and more secure.

Want to get a head start? Check out our article on IP addresses and calculating subnets.

BT HomeHub 5 Running OpenWrt

BT HomeHub 5 Running OpenWrt

View the original article from LinuxScrew here: Installing OpenWrt on a BT HomeHub 5 (or Plusnet Hub One), Full Instructions

]]>
https://www.linuxscrew.com/openwrt-bt-homehub-5-instructions/feed 0
DIY Arduino Powered Electronic Morning Checklist https://www.linuxscrew.com/diy-arduino-powered-checklist https://www.linuxscrew.com/diy-arduino-powered-checklist#respond Fri, 09 Apr 2021 13:11:34 +0000 https://www.linuxscrew.com/?p=6774 If you’re the sort of person who has to check the stove is off before leaving the house, then recheck it because you can’t remember if you checked it (and maybe even turn around at the front gate, unlock the door, go back inside and check a final time) – this might be the project… Read More »DIY Arduino Powered Electronic Morning Checklist

View the original article from LinuxScrew here: DIY Arduino Powered Electronic Morning Checklist

]]>
If you’re the sort of person who has to check the stove is off before leaving the house, then recheck it because you can’t remember if you checked it (and maybe even turn around at the front gate, unlock the door, go back inside and check a final time) – this might be the project for you.

Before I get into it, if you haven’t already, check out the other projects I’ve built for LinuxScrew:

What is it?

arduino box simpsons

The purpose of this box is to let you know you’ve done everything you need to do before leaving the house.

As you complete each task, flick the associated switch, and the LED beneath it will be illuminated. When they’re all illuminated, they’ll stay on – letting you know you’re ready to go.

After 5 minutes, the box will reset itself, turning off the LEDs ready for the next day.

This project illustrates how a simple circuit and some basic Arduino coding can be used to make a useful tool for around the house – it should make for a good Saturday afternoon project, especially if you’re learning how to build things and want to make something simple but useful to get started.

The Parts

Project parts

Project parts

Here’s a list of the parts involved – total cost less than about $20.

  • Arduino
  • Bread/prototyping board
  • Momentary toggle switches (or any momentary switch, push-button, etc.)
  • LEDs
  • 100 Ohm resistors
  • A box
  • Assorted lengths of wire
  • Assorted glue/tape
  • Hooks/bulldog clips
Various cutting attachments

Various cutting attachments

The “Arduino” I’m using is a generic brand knock-off that identifies as an Arduino Leonardo – it is called a “Pro Micro” – found online for a few dollars each.

I’ll use the following tools:

  • Soldering Iron
  • Dremel/cutting tool
  • Pencils/ruler
  • Arduino IDE to write the code and load it onto the Arduino board
Worlds cheapest rotary tool

Worlds cheapest rotary tool

I’ve upgraded my workspace from the kitchen to a better workbench with a work mat and some extra tools – but I’m still avoiding things like 3D printers and laser cutters to keep these projects accessible. This project uses the world’s cheapest Dremel clone, which was about $20 – and it actually did the job pretty tidily, to my surprise.

The Build

Holes cut in box

Holes cut in box

First, I’ve cut away at the box to make holes for the switches, LEDs, and hooks.

The LEDs will poke through the drilled holes so you can see them from the outside.

Hooks added to box

Hooks added to the box.

Hooks are added so that tasks/things to remember can be easily added and removed.

Switches, LED holes and hooks added

Switches, LED holes, and hooks added.

Everything fits! This is actually coming together pretty well.

Hot glue gun makes an appearance

Hot glue gun makes an appearance.

The inevitable appearance of a hot glue gun – used to reinforce the switches. They make a satisfying ‘thunk’ when flicked, but it takes a bit for force, so I don’t want them coming loose.

LEDs taped behind holes inside box

LEDs taped behind holes inside the box

Messy wiring

Messy wiring

The LEDs are taped behind each drilled hole so they can shine through when lit up.

And here’s the standard messy wiring job – each switch and LED is wired to one of the Arduino digital pins.

All LEDs, switches, and the Arduino have their ground pins wired together – this is important! Everything must share a common ground.

The short pin on LEDs should always be connected to ground – they are not reversible.

A 100 Ohm resistor is placed between each LED and the Arduino – otherwise, they will suck up as much current as possible, burning out quickly (and robbing other components of power).

See the circuit diagram below for what’s going on:

Circuit diagram

Circuit diagram

This could probably be achieved with a more complicated electronic circuit and no code/micro-controller, but that’s the great thing with the Arduino platform – it can replace a bunch of other hardware (and the know-how on how to use that hardware) with a single board and some simple code.

The Code

The code is pretty simple – basic reading of the switch states, turning on and off LEDs, and some If statements to perform actions conditionally. The code is commented so you can see what’s doing what.

// This is the code for the DIY Arduino Powered Morning Reminder/Checklist Gadget... Thing
// Project build details at LinuxScrew.com

// Variables defining which arduino pins are attached to the momentary buttons/switches (henceforth just referred to as the switches)
int switch1Pin = 2;
int switch2Pin = 3;
int switch3Pin = 4;
int switch4Pin = 5;
int switch5Pin = 6;

// Variables defining which arduino pins are attached to LEDs
int led1Pin = 7;
int led2Pin = 8;
int led3Pin = 9;
int led4Pin = 10;
int led5Pin = 16; 

// Variables to store the current state of the switches in the loop
// There are two states - HIGH and LOW voltage levels, low being when the pin is grounded
// The Arduino will be able to read whether a pin is HIGH or LOW and we can use that to determine whether the switch was pressed
// They will be defaulted to HIGH, so that when the switch is pressed, it is grounded and will read LOW
int switch1State = HIGH;
int switch2State = HIGH;
int switch3State = HIGH;
int switch4State = HIGH;
int switch5State = HIGH;

// Variables to store whether the switches have been pulled since the last reset in the loop
// The switches are only active momentarily, so we need variables to remember if they have been previously pressed
bool switch1Pulled = 0;
bool switch2Pulled = 0;
bool switch3Pulled = 0;
bool switch4Pulled = 0;
bool switch5Pulled = 0;

// Time to delay before resetting the system after all switches pulled
long delayTime = 300000; //ms, 5 minutes

void setup() {
    // The setup() function is run once when the Arduino is finished booting
    // It's used to set up the system

    // Set the switch pins to INPUT mode - as we will be reading their state
    pinMode(switch1Pin, INPUT); 
    pinMode(switch2Pin, INPUT); 
    pinMode(switch3Pin, INPUT); 
    pinMode(switch4Pin, INPUT); 
    pinMode(switch5Pin, INPUT); 

    // Set the LED pins to OUTPUT mode, as we will be changing their output to turn the LEDs on and off
    pinMode(led1Pin, OUTPUT);
    pinMode(led2Pin, OUTPUT);
    pinMode(led3Pin, OUTPUT);
    pinMode(led4Pin, OUTPUT);
    pinMode(led5Pin, OUTPUT);

    // Set the LED pins to LOW - turning them off to start with
    digitalWrite(led1Pin, LOW);
    digitalWrite(led2Pin, LOW);
    digitalWrite(led3Pin, LOW);
    digitalWrite(led4Pin, LOW);
    digitalWrite(led5Pin, LOW);

    // Set the switch pins to HIGH - this way, when they are pressed, they will read LOW (as they've been grounded) and we can use that to check if they have been pressed
    digitalWrite(switch1Pin, HIGH);
    digitalWrite(switch2Pin, HIGH);
    digitalWrite(switch3Pin, HIGH);
    digitalWrite(switch4Pin, HIGH);
    digitalWrite(switch5Pin, HIGH);

    // Enable serial output so we can see what the Arduino is doing from the Arduino IDE serial console - good for debugging
    Serial.begin(9600);
    Serial.println(F("Toggle-switch todo thing ready to go."));

    // Wait a second for everything to get ready before continuing
    delay(1000);// ms

}

void loop() {
    // The code in the loop() function will run repeatedly every few moments - it's where the main logic for this gadget goes.
    // When it's finished executing, it executes again, non stop.

    // Read the state of each button right now
    switch1State = digitalRead(switch1Pin); 
    switch2State = digitalRead(switch2Pin); 
    switch3State = digitalRead(switch3Pin); 
    switch4State = digitalRead(switch4Pin); 
    switch5State = digitalRead(switch5Pin); 

    // If a button is being pressed, it will read LOW
    // If a button is pressed, output a message to the serial console so we can see it while testing
    // Then, the variable tracking whether the switch has been pressed is updated, and the respective LED is illuminated by setting it to HIGH
    if (switch1State == LOW) {
        Serial.println(F("Switch 1 flicked."));
        switch1Pulled = 1;
        digitalWrite(led1Pin, HIGH);
    }

    // This is then done for all switches/LEDs
    if (switch2State == LOW) {
        Serial.println(F("Switch 2 flicked."));
        switch2Pulled = 1;
        digitalWrite(led2Pin, HIGH);
    }

    if (switch3State == LOW) {
        Serial.println(F("Switch 3 flicked."));
        switch3Pulled = 1;
        digitalWrite(led3Pin, HIGH);
    }

    if (switch4State == LOW) {
        Serial.println(F("Switch 4 flicked."));
        switch4Pulled = 1;
        digitalWrite(led4Pin, HIGH);
    }

    if (switch5State == LOW) {
        Serial.println(F("Switch 5 flicked."));
        switch5Pulled = 1;
        digitalWrite(led5Pin, HIGH);
    }

    // If all switches have been pressed, wait 5 minutes and then reset the system by turning off all of the LEDs and reseting the pulled variable for each switch
    if (switch1Pulled == 1 && switch2Pulled == 1 && switch3Pulled == 1 && switch4Pulled == 1 && switch5Pulled == 1) {
        Serial.println(F("All switches flicked."));

        delay(delayTime);

        digitalWrite(led1Pin, LOW);
        digitalWrite(led2Pin, LOW);
        digitalWrite(led3Pin, LOW);
        digitalWrite(led4Pin, LOW);
        digitalWrite(led5Pin, LOW);

        switch1Pulled = 0;
        switch2Pulled = 0;
        switch3Pulled = 0;
        switch4Pulled = 0;
        switch5Pulled = 0;

    }

}

// End of code!
It works!

It works!

The code works! I’ll tidy up the appearance and put it to use.

The Finished Product

Finished product

Finished product

This has actually turned out pretty well. It’s got an old switchboard/circuit breaker aesthetic – I’m pretty happy with it.

Closeup - Looking alright!

Closeup – Looking alright!

Of course, I have several improvements I’d like to make in a future revision:

  • A buzzer to buzz when all tasks are complete – an audio queue that, yes, you can leave the house, you haven’t forgotten anything.
  • Maybe a built-in night light on a timer with a button on the side for coming home late in the dark.
  • Running a USB cable to the Arduino for power is a bit of an eyesore – I wonder if solar panels on the top would provide enough power to keep a battery charged for running it.

Build Video

I’ve started putting together some short videos of the build process for these projects because why not. Check it out below:

And that was the Arduino Powered Morning Reminder/Checklist Gadget… Thing.

If you want to see more of this stuff, follow LinuxScrew and myself on Twitter.

arduino box guy

View the original article from LinuxScrew here: DIY Arduino Powered Electronic Morning Checklist

]]>
https://www.linuxscrew.com/diy-arduino-powered-checklist/feed 0
Converting Bytes To String In Python [Guide] https://www.linuxscrew.com/python-bytes-to-string https://www.linuxscrew.com/python-bytes-to-string#respond Tue, 09 Mar 2021 14:29:39 +0000 https://www.linuxscrew.com/?p=6670 This tutorial covers converting Bytes to a string variable (and a string to Bytes) in Python versions 2 and 3. What is the difference between a string and a byte string? A string is a series of characters. When stored on disk, the characters are converted to a series of bytes. What series of bytes represents what character is defined by the character… Read More »Converting Bytes To String In Python [Guide]

View the original article from LinuxScrew here: Converting Bytes To String In Python [Guide]

]]>
This tutorial covers converting Bytes to a string variable (and a string to Bytes) in Python versions 2 and 3.

What is the difference between a string and a byte string?

string is a series of characters.

When stored on disk, the characters are converted to a series of bytes.

What series of bytes represents what character is defined by the character encoding.

When a string is stored to disk, it is converted to bytes (encoding). When it is read back into a program, the bytes must be converted back to a usable and displayable string (decoding).

To convert the bytes back into a string correctly, you must know what character encoding was used when they were written to disk.

There are many different character encodings. The most popular are Unicode (UTF-8), the default in Python 3, and ASCII, which is (usually) the default in Python 2.

Unicode rules the roost these days – it offers portability (meaning code written to use Unicode will work the same way on different systems), rather than relying on the operating system or locale to provide information on how characters are displayed.

Turning a Byte String into a Character String (Decoding)

Python 3

Python 3 has a variable typed called a byte literal – it’s used by prefixing a string variable with b. It means the variable is instanced as a byte type rather than a string type.

Below, a byte string is decoded to a UTF-8 string:

encoding = 'utf-8'
b'This is my string'.decode(encoding) # Notice the b! It means the string is a Byte Literal

The str() function can be used similarly:

encoding = 'utf-8'
str(b'This is my string', encoding)

In Python 3, the default encoding is utf-8, so you can omit the encoding if that’s the encoding in use:

b'This is my string'.decode()

Which is the same as:

b'This is my string'.decode(encoding="utf-8")

Handling Errors in Python 3

Errors may be encountered when decoding a string if unexpected characters are present. The Python 3 str() function allows you to pass a third parameter telling it how to handle these errors:

str(b'This is my invalid string \x80abc', 'utf-8', 'ignore')

Above, we’ve told the str() function to ignore any errors. Running this without ‘ignore’ as the third parameter would result in :

Traceback (most recent call last):
File "<string>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 26: invalid start byte

You can use the following options in the third parameter to determine how errors are handled (table pulled directly from the Python docs):

Value Meaning
ignore Ignore the malformed data and continue without further notice.
strict Raise UnicodeError (or a subclass); this is the default.
replace Replace with a suitable replacement marker; Python will use the official U+FFFD REPLACEMENT CHARACTER for the built-in codecs on decoding and ‘?’ on encoding.
backslashreplace Replace with backslashed escape sequences

Additional error handlers for less common scenarios are detailed in the Python docs at:

https://docs.python.org/3/library/codecs.html

Using the error handler like so:

str(b'This is my invalid string \x80abc', 'utf-8', 'replace')

Will, therefore, produce the value:

This is my invalid string ?abc

Python 2

In Python 2, the decode() method of a string can be used to decode it using a given character encoding:

encoding = 'utf-8'
'This is my string'.decode(encoding)

or, if you’re using Unicode, the unicode() function can be used:

encoding = 'utf-8'
unicode('This is my string', encoding)

Strings in Python 2 do not have their encoding defined by default. This means that they may be:

  • Whatever your system locale’s default encoding is
  • Whatever MIME type was defined by the file when it was downloaded
  • It could also be just binary data with no meaningful encoding

Essentially – a string in Python 2 is a direct representation of the characters as read by Python, so your environment will affect it (this is why Python 3 encodes everything in Unicode – so that it’s portable and works the same way regardless of your system).

Thus, if you’re writing data in Python 2, it’s useful to define what encoding is being used when writing data and making sure it is used when converting bytes back into a string if you are doing it manually.

Converting Strings into Bytes (Encoding)

The rationale and definitions above all apply below when converting Strings into Byte Strings – it’s just doing the reverse.

Python 3

The decode method can be used in Python 3, with an optional second parameter with the character encoding:

encoding = 'utf-8'
str.encode('This is my string', encoding) # returns the value as bytes

A third parameter can also be passed with one of the error handlers outlined above.

Python 2

The following will return a bytes variable for the given string:

encoding = 'utf-8'
bytes('This is my string', encoding)

If the third parameter is not supplied, the default encoding will be used – see above for how Python 2 handles default encoding.

Checking a Variable’s Type

After converting a variable to another variable type, you may wish to verify the type of the new variable. Click here to see our article on how to do this in Python.

View the original article from LinuxScrew here: Converting Bytes To String In Python [Guide]

]]>
https://www.linuxscrew.com/python-bytes-to-string/feed 0
Checking Type in Python [Guide] https://www.linuxscrew.com/python-check-variable-type https://www.linuxscrew.com/python-check-variable-type#respond Mon, 08 Mar 2021 14:31:25 +0000 https://www.linuxscrew.com/?p=6672 This article explains how to check the type of a variable in the Python programming language. What are Types? The type of a variable determines what it can or can’t do. It determines what value the variable may take and what can be done with that value. Using the type() Function Pass a variable or value to the type() function, and… Read More »Checking Type in Python [Guide]

View the original article from LinuxScrew here: Checking Type in Python [Guide]

]]>
This article explains how to check the type of a variable in the Python programming language.

What are Types?

The type of a variable determines what it can or can’t do.

It determines what value the variable may take and what can be done with that value.

Using the type() Function

Pass a variable or value to the type() function, and the variable’s type will be returned:

myNumber = 4
type(myNumber) # int

myString = 'foo'
type(myString) # str

Using the isinstance() Function

For more examples, check out the similar isinstance() function:

To compare the type of a variable to a known type, you can use the isinstance() function.

View the original article from LinuxScrew here: Checking Type in Python [Guide]

]]>
https://www.linuxscrew.com/python-check-variable-type/feed 0
The MySQL LIMIT and OFFSET Clauses [with Examples] https://www.linuxscrew.com/mysql-limit-offset https://www.linuxscrew.com/mysql-limit-offset#respond Fri, 05 Mar 2021 14:26:57 +0000 https://www.linuxscrew.com/?p=6666 This tutorial covers limiting the number of results from a MySQL database query using the LIMIT clause and skipping results using the OFFSET clause. This is especially useful when wanting to paginate results in web applications – spreading them out over several pages by retrieving a subset of the database results. Examples Examples will use the following table and data: table_people… Read More »The MySQL LIMIT and OFFSET Clauses [with Examples]

View the original article from LinuxScrew here: The MySQL LIMIT and OFFSET Clauses [with Examples]

]]>
This tutorial covers limiting the number of results from a MySQL database query using the LIMIT clause and skipping results using the OFFSET clause.

This is especially useful when wanting to paginate results in web applications – spreading them out over several pages by retrieving a subset of the database results.

Examples

Examples will use the following table and data:

table_people

id name
1 Bob
2 Ted
3 Jim
4 Pip
5 Zak
6 Tim

LIMIT

Here’s how to limit the results to 2 records using LIMIT:

SELECT * FROM table_people LIMIT 2;

Which will return:

| id | name |
|----|------|
|  1 |  Bob |
|  2 |  Ted |

OFFSET

You can also start the results at a specific position – skipping a set number of records before including records in the results.

This example skips the first three results and returns 2 results total:

SELECT * FROM table_people LIMIT 2 OFFSET 3;

Which returns:

| id | name |
|----|------|
|  4 |  Pip |
|  5 |  Zak |

The OFFSET clause cannot be used independently of the LIMIT clause.

Combined LIMIT and OFFSET

The above query using both LIMIT and OFFSET can also be written as:

SELECT * FROM table_people LIMIT 3, 2;

This removes the need to type out OFFSET by including it in the LIMIT clause as the first parameter.

I prefer not to combine them, as having them displayed with both the words LIMIT and OFFSET makes it clearer what’s going on when skimming over your code.

LIMIT, OFFSET & Pagination

The most common usage for LIMIT and OFFSET is providing pagination.

This is pretty simple and works in any programming language:

  • LIMIT in this scenario is the number of records you want to display per page
  • OFFSET is where the records on the page start at
    • So, if you are on page 3, OFFSET should be 3 * LIMIT.

View the original article from LinuxScrew here: The MySQL LIMIT and OFFSET Clauses [with Examples]

]]>
https://www.linuxscrew.com/mysql-limit-offset/feed 0
MySQL CASE Statement and CASE Operator – Difference and Examples https://www.linuxscrew.com/mysql-case https://www.linuxscrew.com/mysql-case#respond Thu, 04 Mar 2021 14:30:49 +0000 https://www.linuxscrew.com/?p=6664 There are two things called CASE in MySQL (and, by extension, the compatible MariaDB). The CASE Operator, for use in queries. The CASE Statement, for use in stored programs and procedures. Both have similar syntax and the same purpose. Both MySQL CASE Operator and Statement provide control over the results based on a set of conditions.… Read More »MySQL CASE Statement and CASE Operator – Difference and Examples

View the original article from LinuxScrew here: MySQL CASE Statement and CASE Operator – Difference and Examples

]]>
There are two things called CASE in MySQL (and, by extension, the compatible MariaDB).

  • The CASE Operator, for use in queries.
  • The CASE Statement, for use in stored programs and procedures.

Both have similar syntax and the same purpose.

Both MySQL CASE Operator and Statement provide control over the results based on a set of conditions.

Examples in this article will use the following test data:

table_people

id name
1 Bob
2 Ted
3 Jim
4 Pip
5 Zak
6 Tim

CASE Operator

The CASE operator is used in MySQL queries – it is the most commonly used of the CASE operator/statement (and is probably the one you’ve just googled for).

The syntax is as follows:

CASE case_value WHEN compare_value THEN result [WHEN compare_value THEN result ...] [ELSE result] END

Or:

CASE WHEN condition THEN result [WHEN condition THEN result ...] [ELSE result] END 

Note that:

  • There are two variations in the syntax depending on what you are trying to achieve.
  • You can provide as many WHEN statements as required.
  • A final ELSE statement can be provided as a catch-all for any records which do not meet one of the CASE condition.

CASE Operator Examples

SELECT *,
CASE name
    WHEN "Bob" THEN "Robert"
    WHEN "Ted" THEN "Theodore"
    WHEN "Jim" THEN "James"
    WHEN "Tim" THEN "Timothy"
    ELSE "It's not short for anything"
END
AS fullname
FROM table_people;

If you are building case statements using more than a single column, you can omit the column name from the CASE statement and use the column names in each WHEN statement:

SELECT *,
CASE
    WHEN name = "Bob" THEN "Robert"
    WHEN name = "Ted" THEN "Theodore"
    WHEN name = "Jim" THEN "James"
    WHEN name = "Tim" THEN "Timothy"
    ELSE "It's not short for anything"
END
AS fullname
FROM table_people;

Note that in both of the above examples, the AS statement is used to name the column generated by the CASE operator. Otherwise, the column name would default to the query text, which looks very messy and is functionally useless.

Both of the above queries will return:

| id | name |                    fullname |
|----|------|-----------------------------|
|  1 |  Bob |                      Robert |
|  2 |  Ted |                    Theodore |
|  3 |  Jim |                       James |
|  4 |  Pip | It's not short for anything |
|  5 |  Zak | It's not short for anything |

CASE Statement

The CASE statement is best used for stored programs and has a slightly different syntax. Instead of providing a result for each WHEN clause to set a column value, you can provide a list of statements to be executed.

CASE case_value
    WHEN when_value THEN statement_list
    [WHEN when_value THEN statement_list] ...
    [ELSE statement_list]
END CASE

Or:

CASE
    WHEN search_condition THEN statement_list
    [WHEN search_condition THEN statement_list] ...
    [ELSE statement_list]
END CASE

CASE Statement Example

The following example would replace the name column rather than add a new column for the full name if used in a stored procedure:

CASE name
    WHEN  'Bob' THEN
        SET name = 'Robert';
    WHEN 'Ted' THEN
        SET name = 'Theodore';
    ELSE
        SET name = 'Not Bob or Ted';
END CASE;

As with the case operator, case_value can be omitted when referring to the columns by name in each WHEN statement.

CASE 
    WHEN  name = 'Bob' THEN
        SET name = 'Robert';
    WHEN name = 'Ted' THEN
        SET name = 'Theodore';
    ELSE
        SET name = 'Not Bob or Ted';
END CASE;

View the original article from LinuxScrew here: MySQL CASE Statement and CASE Operator – Difference and Examples

]]>
https://www.linuxscrew.com/mysql-case/feed 0
Showing Errors in PHP [Tutorial] https://www.linuxscrew.com/php-show-errors https://www.linuxscrew.com/php-show-errors#respond Wed, 03 Mar 2021 16:32:03 +0000 https://www.linuxscrew.com/?p=6668 This tutorial explains how to use several methods for displaying errors in a PHP application for debugging purposes. Be Careful There are security considerations when displaying errors in any application. Be careful when and where errors are displayed – especially when building products for the web – as the errors may contain sensitive information. For… Read More »Showing Errors in PHP [Tutorial]

View the original article from LinuxScrew here: Showing Errors in PHP [Tutorial]

]]>
This tutorial explains how to use several methods for displaying errors in a PHP application for debugging purposes.

Be Careful

There are security considerations when displaying errors in any application.

Be careful when and where errors are displayed – especially when building products for the web – as the errors may contain sensitive information.

For example, if an application connects to a database and the database crashes, an error will be produced. That error may contain the username and password used to connect to the database.

If these errors are displayed publicly, you’ve just given away access to anyone who wants it!

This is an easy mistake to make and is a common way for bad actors to get ahold of database credentials or user login details – or other data that the application may have processed.

Globally via php.ini

php.ini is the PHP configuration file for an environment. There may be several php.ini files on your system – one for each environment.

Run the following to find out where they are :

php --ini

It is common for there to be a configuration file for both the CLI (Command Line Interface) and a separate configuration file used by the webserver serving PHP applications (e.g., Apache).

Add (or change!) the following values in php.ini:

display_errors = on
display_startup_errors = on
error_reporting = E_ALL

The first line tells PHP to display any errors reported. If display_errors is not enabled, PHP will not display any errors regardless of which errors are reported.

The second line tells PHP to display errors generated not by your code but during the PHP startup process. It should really only be enabled as a last resort, so leave it out if you haven’t exhausted all other options.

The third line tells PHP to report all errors.

Looking to set up PHP and Apache? Check out our article.

Displaying Errors for a Specific Script

It is also possible to display PHP errors only for a specific script or file by adding the following to the beginning of the code:

ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

This temporarily sets PHP configuration options to be applied only for the execution of a specific script.

Checking for Syntax Errors

To check if a PHP file is valid before running it, test it from the command line using:

php -l index.php

Displaying Errors via .htaccess

If you are hosting multiple applications configured using .htaccess files, you can set PHP configuration for them separately by adding the following lines to the .htaccess file for each application:

php_flag display_startup_errors on
php_flag display_errors on
php_value error_reporting -1

Logging Errors

By default, PHP errors will be logged by your web server. The location will vary depending on your operating system and web server choice, but usually, they’ll be located at:

/var/log

…in a subdirectory for your web server or for the PHP CLI.

Changing Log File Location via .htaccess

To change the log file location using .htaccess configuration files on your web server, add the following lines:

php_value error_log path/to/logs/all_php_errors.log

If you use vhosts instead of .htaccess files to configure your web application, you can add these lines to those instead.

Using try/catch Statements to Show Specific Errors

To show the errors for a specific line or block of code, you can wrap it in a try/catch statement and print the errors on exception:

try {
    // Code with errors
} 
catch (Throwable $e) {
    print_r($e);
}

Here’s our full article on using the try/catch statement in PHP.

View the original article from LinuxScrew here: Showing Errors in PHP [Tutorial]

]]>
https://www.linuxscrew.com/php-show-errors/feed 0
Why You Should Wrap File Paths in Strings in Your Shell Scripts https://www.linuxscrew.com/why-you-should-wrap-file-paths https://www.linuxscrew.com/why-you-should-wrap-file-paths#respond Wed, 03 Mar 2021 14:01:41 +0000 https://www.linuxscrew.com/?p=6676 I stumbled across a thread about terrible programming mistakes and found this: https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/commit/a047be85247755cdbe0acce6f1dafc8beb84f2ac Which contains the line: rm -rf /foo-bar-usr /lib/nvidia-current/xorg/xorg Note – I’ve added foo-bar- to the string so that if you try to run it, it won’t do the thing I’m warning about –even having that on my clipboard makes me nervous!. What happens when… Read More »Why You Should Wrap File Paths in Strings in Your Shell Scripts

View the original article from LinuxScrew here: Why You Should Wrap File Paths in Strings in Your Shell Scripts

]]>
I stumbled across a thread about terrible programming mistakes and found this:

https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/commit/a047be85247755cdbe0acce6f1dafc8beb84f2ac

Which contains the line:

rm -rf /foo-bar-usr /lib/nvidia-current/xorg/xorg

Note – I’ve added foo-bar- to the string so that if you try to run it, it won’t do the thing I’m warning about –even having that on my clipboard makes me nervous!.

What happens when it’s run? It wipes out the entire /usr/ directory – essentially bricking your computer! All because of a single space.

Due to the space after /usr, the rm command interprets it as two separate directories to remove, not one mistyped path.

If that path had been wrapped in quotes so that it was passed as a single parameter, this wouldn’t have happened.

Most command-line examples won’t do this as it can obfuscate the meaning of a script or make it more difficult to read – but if you’re writing scripts rather than executing statements directly on the command line and especially if you’re accepting user input where they might accidentally tap space (never trust users), it’s worth wrapping your file paths as a precaution.

You don’t want to lose data, and you definitely don’t want to lose someone else’s data.

And as always – back up your files and your system regularly! You never know when a mistake like this might slip through – it might not even be your mistake.

View the original article from LinuxScrew here: Why You Should Wrap File Paths in Strings in Your Shell Scripts

]]>
https://www.linuxscrew.com/why-you-should-wrap-file-paths/feed 0