Setting up Pi-Hole with Raspberry 3B+

I originally both RaspberryPi to set up my own VPN, but it turned out that my internet provider doesn’t let me mess with my router settings – so it’s probably the first thing You should check if you’re planning on doing that.

So intead I started my journey with Pi by setting up DNS black hole which stop adds from being displayed.

What is Pi-Hole ?

It is DNS server which stop ads from being displayed on all your devices. While You’re visiting a site Pi-hole download its contents, but it will detect the domains used to serve advertisements on that website and instead of looking up the real address of those sites, it will send a fake address which prevents ads from being downloaded. It is way better than browser AddOns like AdBlocker, because those addons still download content and then hide it on website. So it actually might speed up Your browsing experience in general.

1. Set up your RaspberryPi

If You don’t have Your Pi up and running follow this beautiful tutorial from https://www.raspberrypi.org/ which will guide You through all necessary steps to make Your Pi alive.

2. Give Your Pi Static IP Address and enable SSH (optional)

If You don’t want to access Your Pi remotely then You can skip this step (Pi-hole installer will add eth0 interface for You), but it’s generally a good idea to understand how it is done

This was actually troublesome. There are many tutorials out there but this one works like a charm.

Basically You need to run netstat –nr to find out your default gateway:

In my case its 192.168.0.1

Then You need to check Your DNS server addresses with

cat /etc/resolv.conf

Write down address (there could be also two ip adressess) then run:

sudo nano /etc/dhcpcd.conf

Scroll down to the bottom of the file. Eth0 and wlan0 are usually default interface names, to makes sure you can run ifconfig. I am assigning 192.168.0.100 for ethernet and 192.168.0.99 for wlan. That means every time that my router sees Pi it will reserve those IPs for me.

Default gateway goes to static routers value and DNS addressess from /etc/resolv.conf go under, separated with space.

To save file hit: ctrl+x -> y -> enter and we’re ready to reboot !

Run: sudo reboot

Now, when i run ifconfig we can see that static addresses for wlan and ethernet are just what we set them to

Bonus

Enable SSH connection to access Pi terminal remotely (for example by PuTTY on windows or app on android) by typing those commands into terminal

cd /boot

touch ssh

sudo reboot

3. Run Pi-hole installer

It is super important that Your Pi is connected with ethernet cable to Your router !

Open terminal, paste link and enjoy beautiful installer from Pi-hole®

curl -SSL https://install.pi-hole.net | bash

Basically hit enter through all steps, IP address for Your server will pop up somewher (if you followed step 2 it will be whatever You’ve put in dhcpcd.conf !) and make sure to install admin panel and write down password for Your admin panel.

For more info:

https://github.com/pi-hole/pi-hole/#one-step-automated-install

4. Configure Your router/devices

If You followed whole tutorial Your admin panel should be available at http://192.168.0.100/admin/

Now You have to configure Your router to use Pi-hole IP as the only DNS entry. In my case it’s impossible, cause I cannot control my router settings in any way (god bless my ISP). Here’s a quick tutorial from Pi-hole about how to do it https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245

In case you are in same situation You have to change DNS manually on all of Your devices

At home I’m using my windows machine and android phone to browse web, let’s take a look at my WiFi connection on android

Android

Go to WiFi details and edit DNS 1 field so it matches Pi-hole IP

When I go to admin panel we can see routed android device

Android is routed through Pi-hole. Success !

We can see also my router and PC, which is connected to network, but does not use Pi-hole. Let’s change it

Windows 10

On Windows 10: Open Control Panel -> Network and Internet -> Network and Sharing Center -> Change Adapter Settings (from side menu)

or simply go to

Control Panel\Network and Internet\Network Connections

I’m connecting with WiFi. Right click on WiFi icon, choose properties and then in WiFi properties window select Internet Protocol Version 4 (TCP/IPv4) and hit Properties

Now You can set Your DNS address to Pi-hole.

Leave alternative DNS server to 8.8.8.8 (Google public DNS) in case Pi-hole doesn’t respond

Now let’s take a look at admin panel once again

Works like a charm !

In case Your devices didn’t turn green reset internet connection, reconnect to WiFi, reboot You devices.

That’s it. Now You can enjoy Ad-free internet. I checked how much would this pleasure cost and it turns out that my RaspberryPi consumes 2-3W, so it gives us around 25kWh per Year

1kWh in Poland costs around 0,15 € , which gives us about 25000 * 0,15 € = 3,75 €

So I’ll be paying around 4 € / year to enjoy ad-free internet, that’s just amazing, isn’t it ?

OpenCV Data Types

There are few basic data types You will find handy using OpenCV.

data types

Let’s see, how we generally instantiate variables in OpenCV.

Starting with fixed vector, which is data type intended for small (up to 9 elements) vectors.

cv::Vec<int, 2> vector = { 5,10 };
int firstVectorElement = integerVector[0];

Here we specified, that we want a vector of 2 elements which are integers and we assigned to it two values. Then we accessed it’s first value, which is 5.

Yet, it can be done in more elegant fashion using aliases

cv::Vec2i integerVector {5, 10};

Aliases

Generally, we can use templates to instantiate variables, but OpenCV provides more elegant way to instantiate its variables:

cv::(Name of data type){NumberOfMembers}{UnderylingDataType}

Where name of data types are:
cv::Vec – fixed vector
cv::Matx – fixed matrix
cv::Point – point
cv::Rect – rectangle

Underyling data type:
b = unsigned char,
w = unsigned short,
s = short,
i = int,
= float,
d = double.

Some examples:

cv::Vec2i  2-element fixed integer vector
cv::Vec3i  3-element fixed integer vector
cv::Vec4d  4-element fixed double vector

cv::Matx22f floatMatx; // 2x2 float fixed matrix
cv::Matx41d doubleMatx; // 4x1 double fixed matrix

cv::Point2i integerPoint; // 2-element integer point
cv::Point3d doublePoint; // 3-element double point

cv::Point

Point represents a geometrical point.
We access it’s members by named variables :

cv::Point2i integerPoint {2,3};
int pointXValue = integerPoint.x;
int pointYValue = integerPoint.y;

cv::Matx

Fixed matrixes represent small matrixes (eg. 2×2,3×3) which are used in image processing a lot. Usually they are faster to work with than dense arrays (cv::Mat, they’ll get seperate post on this blog)

//Instantiated with template
cv::Matx<double, 2, 2> fixedMatrix(1, 5, 8, 20);

//Instantiated with alias
cv::Matx22f fixedMatrix {1, 5, 8, 20};

cv::Scalar

Special type of vector. Represents 4-element double vector.

cv::Scalar scalar = { 23.50,20.25,17,64 };

cv::Size

Meant to represent size of something. It’s similar to Point, but rather than members x and y, you use width and height to access it’s members.

cv::Size2i size{ 25,50 };
int sizeHeight = size.height;
int sizeWidth = size.width;

cv::Rect

It’s more than point and size. It has all four members – x,y,width and height. Apart from that it supports useful functions for geometric computation

//cv::Rect(x,y,width,height)
cv::Rect rectangle{ 5,3,10,20 };

//You can also use point and size to instantiate rectangle
cv::Point upperLeftCornerOfRectangle = { 5,3 };

cv::Size sizeOfRectangle;
sizeOfRectangle.width = 10;
sizeOfRectangle.height = 20;

cv::Rect rectangle(upperLeftCornerOfRectangle, sizeOfRectangle);

//Computing area of rectangle
auto areaOfRectangle = rect.area();

cv::RotatedRect

Underneath it’s not a template, but a container.

It consists of:
center – cv::Point2f
size – cv::Size2f
angle – float

cv::Point2f center{ 0,0 };
cv::Size2f size{ 5,10 };
float angle = 90.5;

cv::RotatedRect rotatedRectangle(center, size, angle);

Conversions between OpenCV data types

Point seems to be the building block of all primitives. It can be either converted or used to instantiate other types. Let’s take a look at some standard conversions

//Point can be casted to point of different type 
cv::Point2f floatingPoint;
cv::Point integerPoint = (cv::Point) floatingPoint;
floatingPoint = (cv::Point2f) integerPoint;

//Point can be casted to vector
cv::Point2i integerPoint;
auto integerVector = (cv::Vec2i) integerPoint;

//Point can be casted to fixed matrix cv::Point2d doublePoint;
auto integerFixedMatrix = (cv::Matx21d) doublePoint;

//Point can be casted to size cv::Point2i integerPoint;
auto size = (cv::Size) integerPoint;

//Size can be casted to point
integerPoint = (cv::Point2i) size;

//Fixed vector can be casted to scalar
cv::Vec4d doubleVector;
auto convertedScalar = (cv::Scalar) doubleVector;

Capture and save video from camera using OpenCV

This code snippet allows You to caputre video using OpenCV VideoWriter and save it to a file.

  1. Open camera using VideoCapture object
  2. Set up VideoWriter object
  3. Enter loop, so every frame is saved to writer
#include "opencv2/highgui.hpp"
 
#include <iostream>

using std::string;
using std::cout;
 
int main(int argc, char* argv[]) {
 
    cv::VideoCapture capture;

    //Gets default camera 
    cv::CommandLineParser parser(argc, argv, "{@input|0|}");
    string input = parser.get<string>("@input");
 
    if (input.size() == 1 && isdigit(input[0]))
        capture.open(input[0] - '0');
    else
        capture.open(input);
 
    if (!capture.isOpened())
    {
        cout << "Could not initialize capturing...\n";
        return 0;
    }
 
    //Gets fps and size of camera 
    double fps = capture.get(cv::CAP_PROP_FPS);
    cv::Size size(
        (int)capture.get(cv::CAP_PROP_FRAME_WIDTH),
        (int)capture.get(cv::CAP_PROP_FRAME_HEIGHT)
    );

 
    cv::VideoWriter writer;

    //Sets fourCC code. Set to -1 to get list of available codecs in console
    int fourCC = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');

    //Opens Video Writer
    //Filename need valid file type extension,otherwise it won't save properly
    //Size of image to save has to fit exactly Your target size
    //use cv::resize before feeding the writer if you've manipulatated Your image height and width
    writer.open("capture.avi", fourCC, fps, size);
 
    cv::Mat inputMat, outputMat;

    //Loop that captures video
    for (;;) {
        capture >> inputMat;
 
        cv::imshow("inputMat", inputMat);
 
        ////Can't use - resizes window !
        //cv::pyrDown(inputMat, outputMat);
        //cv::imshow("output", outputMat);

        ////If still doesn't work try to conver to BGR (default OpenCV color scheme)
        //cv::cvtColor(inputMat, inputMat, cv::COLOR_RGB2BGR);
 
        writer << inputMat;
 
        char c = cv::waitKey(10);
        if (c == 27) break; // hit ESC to stop program
    }
    capture.release();
}
  

Hope You liked it.