Setup CoreDNS as local DNS resolver

System setup

I am using ubuntu 20.10 as reference system with docker and docker-compose installed.

Ubuntu and other OSes comes with systemd-resolved installed and listening on port :53, preventing to listen on that port. Thanks to linuxuprising.com tutorial for the steps.

To check which port is in use in your system run sudo lsof -i :53. The response looks like

1
2
3
COMMAND   PID            USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
systemd-r 972 systemd-resolve 12u IPv4 32039 0t0 UDP 127.0.0.53:domain
systemd-r 972 systemd-resolve 13u IPv4 32040 0t0 TCP 127.0.0.53:domain (LISTEN)

Disable DNS stub listener option and set a default DNS

1
2
3
4
echo 'DNSStubListener=no' | sudo tee -a /etc/systemd/resolved.conf
echo 'DNS=1.1.1.1' | sudo tee -a /etc/systemd/resolved.conf
# check it is ok
cat /etc/systemd/resolved.conf

Replace the default resolv.conf file

1
2
sudo cp /etc/resolv.conf /etc/resolv.conf.bak
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

Finally reload the systemd-resolved service

1
sudo service systemd-resolved restart

Create a docker-compose file for coredns

Now create a docker-compose.yaml file with the following content. We expose port :53 tcp and udp and use the coredns/coredns docker image. It also mount the local folder ./config to /etc/coredns in the container, where we will store our configurations.

1
2
3
4
5
6
7
8
9
10
11
version: "3.1"
services:
coredns:
image: coredns/coredns
command: -conf /etc/coredns/Corefile
volumes:
- ./config:/etc/coredns
restart: unless-stopped
ports:
- '53:53'
- '53:53/udp'

Create a ./config folder and add in it a Corefile file

1
2
3
4
5
6
7
.:53 {
log
errors
ready
file /etc/coredns/lan.zone lan
forward . 1.1.1.1
}

See the Coredns configuration docs for more information

Add a zone file (named /etc/coredns/lan.zone from the previous example) with the references to the IPs in the network. It is based on a Wikipedia example but there are many more examples online as the format is standardized.

In the example the machine IP hosting the DNS server is 192.168.1.2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@  3600  IN  SOA  ns1.lan.  root.lan.  (
2019041900
3600
600
604800
600 )
3600 IN NS ns1.lan.
3600 IN NS ns2.lan.

ns1 IN A 192.168.1.2
ns2 IN A 192.168.1.2

;Example for IPv4, will provide ip for raspberrypi.lan
raspberrypi IN A 192.168.1.99

Now start the DNS server with docker-compose up.

You can test the resolution with nslookup

1
2
3
4
5
6
7
8
# replace the ip with your dns server ip
nslookup -norec raspberrypi.lan 192.168.1.2
# response
Server: 192.168.1.2
Address: 192.168.1.2#53

Name: raspberrypi.lan
Address: 192.168.1.99

Usig hexo for blogging

I decided to move to a markdown based, static, website generator and opted to use hexo.

Setup

To start writing install the hexo cli and initialize the work folder

1
2
3
4
npm install hexo-cli -g
hexo init blog
cd blog
npm install

Run the preview server with hexo server and visit localhost:4000

Basic configuration

General configuration reside in _config.yml most noticeable blog title and meta information.

Create a post

Create a new post with hexo new "My post". It will be placed in sources/_posts

More details about the metadata of the post can be found at https://hexo.io/docs/front-matter.html

Installing a theme

Themes can be choosen from the themes gallery and installed under theme folder. Click on the theme name to get to the repository.

For example to use minima add it to the theme folder

1
2
cd themes
git clone https://github.com/adisaktijrs/hexo-theme-minima.git minima

Creating a small robot with nodemcu

I was looking to have an acronym for a project, but I found it difficult to find a decent service to generate a reasonable one.

I found the repo github.com/bacook17/acronym which has a nice implementation.
The library, also very well curated, allow to lookup to a corpus of words which letters are contained in the proposed phrase.

I integrate the H2020 project acronym list to ensure I do not get duplicates, a reasonable amount of ~10k records.

The library own implementations suffered from an out of memory while comparing the existing acronym so I just checked for duplicates before returning the output.

Next step is to setup a simple HTTP service to expose the service and a minimal interface. Luckly python has an HTTP service implementation that came handy to expose a JSON endpoint to the generator and serve the web frontend.

It is also available as docker image

Give it a try with

1
docker run --rm -p 8081:8081 -v `pwd`/data:/app/data opny/acronym-generator

and then visit localhost:8081 to start a search.

The source is available at gitlab.com/opny/acronym-generator

Install Tasmota on a Sonoff Mini

The Sonoff Mini firmware can be performed over the air (OTA).

Tasmota project offer a well detailed guide on how to flash the device
https://github.com/arendst/Tasmota/wiki/Sonoff-DIY#flash-procedure

First open the case and install the jumper in the package to enable the DIY mode.

Create the wifi hotspost

Create an hotpost with SSID sonoffDiy and WPA2 password 20170618sn.

On ubuntu you can add a new wifi connection and select hotspot instead of client

ubuntu wifi hotspot

Connect back the power to the device (taking care of not being electrocuted) and await for it to be connected. Use arp to see connected devices.

Setup a local web server

It will serve the firmware. An example made with docker follows

Create a Caddyfile with content

1
2
3
0.0.0.0:80
browse /
root /data

in the same directory put the tasmota-wifiman.bin

wget http://thehackbox.org/tasmota/tasmota-wifiman.bin

then launch the web server

1
2
3
4
5
6
7
docker run -d \
--rm --name caddy_sonoff \
-v $(pwd)/Caddyfile:/etc/Caddyfile \
-e ACME_AGREE=false \
-p 80:80 \
-v $(pwd):/data \
abiosoft/caddy

Get the web server ip

1
WEBSERVER_IP=...

Manual Flash instructions (Linux)

I used the manual flash procedure.

Note Set the GLOBAL_VARS to copy paste the command

Retrieve the device ID and IP (in bold)

avahi-browse -t _ewelink._tcp --resolve

1
2
3
4
+ wlp2s0 IPv4 eWeLink_1010a02ed5                            _ewelink._tcp        local
...
address = [10.42.0.160]
...
1
2
DEVICE_ID=...
DEVICE_IP=...

Get the shasum of the firmware

shasum -a 256 tasmota-wifiman.bin

1
SHASUM=...

Retrieve device info

curl http://$DEVICE_IP:8081/zeroconf/info -XPOST --data '{"deviceid":"$DEVICE_ID","data":{} }'

Enable OTA updates

curl http://$DEVICE_IP:8081/zeroconf/ota_unlock -XPOST --data '{"deviceid":"$DEVICE_ID","data":{} }'

Flash the firmware

curl http://$DEVICE_IP:8081/zeroconf/ota_flash -XPOST --data '{"deviceid":"$DEVICE_ID,"data":{"downloadUrl": "http://$WEBSERVER_IP/tasmota-wifiman.bin", "sha256sum": "$SHASUM"} }'

The process takes 30 seconds

Setup the device

Connect to a wifi network named tasmota-* and browse http://192.168.4.1. Setup your AP and enjoy!

Configure the template for Sonoff MINI

config

image source

Debugging GO test function in Visual Studio Code

I want to write test in vscode and run them by hitting F5.

  • To get this to work I had to move to the Run tab

  • Open the configuration file with the cog button

  • Add a configuration

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     {
    "name": "Launch test function",
    "type": "go",
    "request": "launch",
    "mode": "test",
    "program": "${workspaceFolder}",
    "args": [
    "-test.v",
    "-test.run",
    "^TestHTTPServerGetID$",
    ],
    "showLog": false
    }
  • Set a breakpoint and hit F5 at will!

References:

Notes:

  • The "^TestHTTPServerGetID$", part indicates the test. It allows for a regular expression.
  • Ensure to set the subfolder eg. "program": "${workspaceFolder}/subpackge",

Scripting the Raspberry PI configuration

raspi-config

The raspi-config CLI can be used also headless with a lot of utilities for bash scripting.

This thread explain in more details.

Is a PI4 ? Run raspi-config nonint is_pifour to know

Working with Picamera

1
2
3
4
# enable picamera
sudo raspi-config nonint do_camera 1
# get picamera status
sudo raspi-config nonint get_camera

In general values are 1 to set on or 0 to off. Follow a list of possible commands

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
raspi-config nonint get_can_expand
raspi-config nonint do_expand_rootfs
raspi-config nonint get_hostname
raspi-config nonint do_hostname "yourhostname"
raspi-config nonint get_boot_cli
raspi-config nonint get_autologin
raspi-config nonint do_boot_behaviour B1
raspi-config nonint do_boot_behaviour B2
raspi-config nonint do_boot_behaviour B3
raspi-config nonint do_boot_behaviour B4
raspi-config nonint get_boot_wait
raspi-config nonint do_boot_wait 1|0
raspi-config nonint get_boot_splash
raspi-config nonint do_boot_splash 1|0
raspi-config nonint get_overscan
raspi-config nonint do_overscan 1|0
raspi-config nonint get_pixdub
raspi-config nonint do_pixdub 1|0
raspi-config nonint get_camera
raspi-config nonint do_camera 1|0
raspi-config nonint get_ssh
raspi-config nonint do_ssh 1|0
raspi-config nonint get_vnc
raspi-config nonint do_vnc 1|0
raspi-config nonint get_spi
raspi-config nonint do_spi 1|0
raspi-config nonint get_i2c
raspi-config nonint do_i2c 1|0
raspi-config nonint get_serial
raspi-config nonint get_serial_hw
raspi-config nonint do_serial 1|0
raspi-config nonint get_onewire
raspi-config nonint do_onewire 1|0
raspi-config nonint get_rgpio
raspi-config nonint do_rgpio 1|0
raspi-config nonint get_blanking
raspi-config nonint do_blanking 1|0
raspi-config nonint get_pi_type
raspi-config nonint is_pi
raspi-config nonint is_pifour
raspi-config nonint is_fkms
raspi-config nonint get_config_var arm_freq /boot/config.txt
raspi-config nonint do_overclock None|Modest|Medium|High|Turbo
raspi-config nonint get_config_var gpu_mem /boot/config.txt
raspi-config nonint get_config_var gpu_mem_256 /boot/config.txt
raspi-config nonint get_config_var gpu_mem_512 /boot/config.txt
raspi-config nonint get_config_var gpu_mem_1024 /boot/config.txt
raspi-config nonint do_memory_split 16|32|64|128|256
raspi-config nonint get_config_var hdmi_group /boot/config.txt
raspi-config nonint get_config_var hdmi_mode /boot/config.txt
raspi-config nonint get_wifi_country
# find wifi countries here /usr/share/zoneinfo/iso3166.tab
raspi-config nonint do_wifi_country "yourcountry"
raspi-config nonint do_pi4video V1
raspi-config nonint do_pi4video V2
raspi-config nonint do_pi4video V3
raspi-config nonint get_pi4video
raspi-config nonint get_overlay_now
raspi-config nonint get_overlay_conf
raspi-config nonint get_bootro_conf
raspi-config nonint enable_overlayfs
raspi-config nonint disable_overlayfs
raspi-config nonint enable_bootro
raspi-config nonint disable_bootro
raspi-config nonint is_uname_current
raspi-config nonint list_wlan_interfaces
raspi-config nonint is_installed realvnc-vnc-server
raspi-config nonint is_installed xscreensaver

vcgencmd

Also the vcgencmd CLI tool is good to knonw

vcgencmd commands list the available commands

Get the board temperature with vcgencmd measure_temp

To get the allocated GPU use vcgencmd get_mem gpu | cut -d = -f 2 | cut -d M -f 1

Streaming from picamera on PI4

First let’s install some dependencies

sudo apt-get install gstreamer-tools gstreamer1.0-plugins-bad gstreamer1.0-plugins-good

Server will open the raspivid cli tool and pipe on stdout, streamer will take the input and parse h264 and stream as rtp

1
raspivid -t 0 -h 720 -w 1080 -fps 25 -hf -b 2000000 -o - | gst-launch-1.0 -v fdsrc ! h264parse ! rtph264pay config-interval=1 pt=96 ! gdppay ! tcpserversink host=0.0.0.0 port=5000

Client will open a window displaying the video feedback

1
gst-launch-1.0 -v tcpclientsrc host=<pi4-ip> port=5000 ! gdpdepay ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false

Credits

Based on the article from raspberry-projects.com

Door opener project

I have an old door which is in urge of some smartness.

Materials

  1. nodemcu
  2. a relay
  3. a door lock
  4. an AV to 12v converter
  5. an pin keypad [coming soon]

Sources

button

The code is on muka/door-opener

It contains some scripts to get started and the codebase for the nodemcu

Hardware setup

Based on this post most pins are high at boot and should not be set to low to avoid boot issues.

Considering our door should not open if there is a power outage the control ping will be GPIO5. Power and ground can be a 3.3v and GND pin

Connection pf the nodeMcU to the relay
Relay

To connect the relay to the 12v adapter

  • NO goes to 12v +
  • CTRL goes to 12v -